Skip to content

Commit

Permalink
Add requires, namespaces, using, and namespace/type/function/object a…
Browse files Browse the repository at this point in the history
…liases
  • Loading branch information
hsutter committed Feb 28, 2024
1 parent b795f31 commit 74fb716
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 19 deletions.
86 changes: 82 additions & 4 deletions docs/cpp2/aliases.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,97 @@
# Aliases

Aliases are pronounced **"synonym for**, and written using the same **name `:` kind `=` value** [declaration syntax](../cpp2/declarations.md) as everything in Cpp2:

- **name** is declared to be a synonym for **value**.

- **kind** can be any of the kinds: `namespace`, `type`, a function signature, or a type.

- **`==`**, pronounced **"defined as a synonym for"**, always precedes the value. The `==` syntax stresses that during compilation every use of the name could be equivalently replaced with the value.

- **value** is the expression that the **name** is a synonym for.


## <a id="namespace-aliases"></a> Namespace aliases

TODO
A namespace alias is written the same way as a [namespace](namespaces.md), but using `==` and with the name of another namespace as its value. For example:

``` cpp title="Namespace aliases" hl_lines="1 2 4 5 8 12 16"
// 'chr' is a namespace defined as a synonym for 'std::chrono'
chr : namespace == std::chrono;

// 'chrlit' is a namespace defined as a synonym for 'std::chrono_literals'
chrlit : namespace == std::chrono_literals;

main: () = {
using namespace chrlit;

// The next two lines are equivalent
std::cout << "1s is (std::chrono::nanoseconds(1s).count())$ns\n";
std::cout << "1s is (chr::nanoseconds(1s).count())$ns\n";
}
// Prints:
// 1s is 1000000000ns
// 1s is 1000000000ns
```


## <a id="type-aliases"></a> Type aliases

TODO
A namespace alias is written the same way as a [type](types.md), but using `==` and with the name of another type as its value. For example:

``` cpp title="Type aliases" hl_lines="1 2 7 10"
// 'imap<T>' is a type defined as a synonym for 'std::map<i32, T>'
imap : <T> type == std::map<i32, T>;

main: () = {
// The next two lines declare two objects with identical type
map1: std::map<i32, std::string> = ();
map2: imap<std::string> = ();

// Assertion they are the same type, using the same_as concept
assert( std::same_as< decltype(map1), decltype(map2) > );
}
```


## <a id="function-aliases"></a> Function aliases

TODO
A function alias is written the same way as a [function](functions.md), but using `==` and with a side-effect-free body as its value; the body must always return the same value for the same input arguments. For example:

``` cpp title="Function aliases" hl_lines="1 2 6 9 12 15"
// 'square' is a function defined as a synonym for the value of 'i * i'
square: (i: i32) -> _ == i * i;

main: () = {
// It can be used at compile time, with compile time values
ints: std::array<i32, square(4)> = ();

// Assertion that the size is the square of 4
assert( ints.size() == 16 );

// And if can be used at run time, with run time values
std::cout << "the square of 4 is (square(4))$\n";
}
// Prints:
// the square of 4 is 16
```

> Note: A function alias is compiled to a Cpp1 `#!cpp constexpr` function.

## <a id="object-aliases"></a> Object aliases

TODO
An object alias is written the same way as an [object](objects.md), but using `==` and with a side-effect-free value. For example:

``` cpp title="Function aliases" hl_lines="1 2 5 6"
// 'BufferSize' is an object defined as a synonym for the value 1'000'000
BufferSize: i32 == 1'000'000;

main: () = {
buf: std::array<std::byte, BufferSize> = ();
assert( buf.size() == BufferSize );
}
```

> Note: An object alias is compiled to a Cpp1 `#!cpp constexpr` object.
40 changes: 29 additions & 11 deletions docs/cpp2/declarations.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,23 @@

All Cpp2 declarations are written as **"_name_ `:` _kind_ `=` _statement_"**.

- The _name_ must be a valid identifier (start with a letter, and consist of letters, digits, or `_`). The name can be variadic (be a name for a list of zero or more things) by writing a `...` suffix at the end of the name.

- The `:` is pronounced **"is a."**

- The _kind_ can start with [template parameters](#template-parameters).
- The _kind_ can start with [template parameters](#template-parameters) and end with [`#!cpp requires` constraints](#requires).

- The `=` is pronounced **"defined as."** For the definition of something that will always have the same value, write `==`.
- The `=` is pronounced **"defined as."** For the definition of something that will always have the same value, write `==`, pronounced **"defined as a synonym for"**.

- The _statement_ is typically an expression statement (e.g., `#!cpp a + b();`) or a compound statement (e.g., `#!cpp { /*...*/ return c(d) / e; }`).

- Various parts of the syntax allow a `_` "don't care" wildcard or can be omitted entirely to accept a default (e.g., `#!cpp x: int = 0;` can be equivalently written `#!cpp x: _ = 0;` or `#!cpp x := 0;` both of which deduce the type).
Various parts of the syntax allow a `_` "don't care" wildcard or can be omitted entirely to accept a default (e.g., `#!cpp x: int = 0;` can be equivalently written `#!cpp x: _ = 0;` or `#!cpp x := 0;` both of which deduce the type).

> Notes:
>
> - When the type is omitted, whitespace does not matter, and writing `#!cpp x: = 0;` or `#!cpp x : = 0;` or `#!cpp x := 0;` or other whitespace is just a stylistic choice. This documentation's style uses the last one, except when there are multiple adjacent declaration lines this style lines up their `:` and `=`.
>
> - `==` stresses that this name will always have the given value, to express [aliases](./aliases.md) and side-effect-free 'constexpr' functions (e.g., `#!cpp square: (i: int) == i * i;`).
> - `==` stresses that this name will always have the given value, to express [aliases](aliases.md) and side-effect-free 'constexpr' [function aliases](aliases.md/#function-aliases).

## <a id="template-parameters"></a> Template parameters
Expand All @@ -27,13 +29,35 @@ A template parameter list is enclosed by `<` `>` angle brackets, and the paramet

For example:

``` cpp title="Declaring template parameters" hl_lines="1-3"
``` cpp title="Declaring template parameters" hl_lines="1-3 8-9"
array: <T: type, size: i32> type
// parameter T is a type
// parameter size is a 32-bit int
= {
// ...
}

tuple: <Ts...: type> type
// parameter Ts is variadic list of zero or more types
= {
// ...
}
```


## <a id="requires"></a> `#!cpp requires` constraints

A `#!cpp requires` ***condition*** constraint appears at the end of the ***kind*** of a templated declaration. If the condition evaluates to `#!cpp false`, that specialization of the template is ignored as if not declared.

For example:

``` cpp title="A requires constraint on a variadic function" hl_lines="3"
print: <Args...: type>
(inout out: std::ostream, args...: Args)
requires sizeof...(Args) >= 1u
= {
(out << ... << args);
}
```


Expand Down Expand Up @@ -97,9 +121,3 @@ n: namespace
```

> Note: `@enum` is a metafunction, which provides an easy way to opt into a group of defaults, constraints, and generated functions. For details, see [`@enum`](metafunctions.md#enum).

## <a id="requires"></a> `requires` constraints

TODO

55 changes: 51 additions & 4 deletions docs/cpp2/namespaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,58 @@

## Overview

TODO
A namespace `N` can contain declarations that are then accessed by writing `N::` or [`using`](#using) the namespace or declaration. For example:

For details, see [Design note: Namespaces](https://github.com/hsutter/cppfront/wiki/Design-note%3A-Namespaces).
``` cpp title="Declaring some things in a namespace" hl_lines="2 8"
// A namespace to put all the names provided by a widget library
widgetlib: namespace = {
widget: type = { /*...*/ }
// ... more things ...
}

main: () = {
w: widgetlib::widget = /*...*/;
}
```


## <a id="using"></a> `using`

A `#!cpp using` statement brings names declared in another namespace into the current scope as if they had been declared in the current scope. It has two forms:

- `#!cpp using a_namespace::a_name;` brings the single name `a_name` into scope.

- `#!cpp using namespace a_namespace;` brings all the namespace's names into scope.

For example:

``` cpp title="using statements" hl_lines="13 14 20 21"
// A namespace to put all the names provided by a widget library
widgetlib: namespace = {
widget: type = { /*...*/ }
// ... more things ...
}

main: () = {
// Explicit name qualification
w: widgetlib::widget = /*...*/;

{
// Using the name, no qualification needed
using widgetlib::widget;
w2: widget = /*...*/;
// ...
}

{
// Using the whole namespace, no qualification needed
using namespace widgetlib;
w3: widget = /*...*/;
// ...
}

// ...
}
```

## `using`

TODO

0 comments on commit 74fb716

Please sign in to comment.