mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 19:00:11 +01:00
Assisted-by: Claude and Antigravity with Gemini --------- Co-authored-by: Geoff Romer <gromer@google.com> Co-authored-by: josh11b <15258583+josh11b@users.noreply.github.com>
This commit is contained in:
co-authored by
Geoff Romer
josh11b
parent
88160496e1
commit
a2890716ba
+20
-20
@@ -2710,7 +2710,7 @@ has a type\* parameter `T` that can be any type that implements the `Ordered`
|
||||
interface.
|
||||
|
||||
```carbon
|
||||
fn Min[T:! Ordered](x: T, y: T) -> T {
|
||||
fn Min[T: Ordered](x: T, y: T) -> T {
|
||||
// Can compare `x` and `y` since they have
|
||||
// type `T` known to implement `Ordered`.
|
||||
return if x <= y then x else y;
|
||||
@@ -2768,7 +2768,7 @@ parameter by prefixing it with the `template` keyword. Keywords matching the
|
||||
contextual default are disallowed to ensure consistency.
|
||||
|
||||
```carbon
|
||||
fn Convert[template T:! type](source: T, template U:! type) -> U {
|
||||
fn Convert[template T: type](source: T, template U: type) -> U {
|
||||
var converted: U = source;
|
||||
return converted;
|
||||
}
|
||||
@@ -2784,7 +2784,7 @@ A template parameter can still use a constraint. The `Min` example could have
|
||||
been declared as:
|
||||
|
||||
```carbon
|
||||
fn TemplatedMin[template T:! Ordered](x: T, y: T) -> T {
|
||||
fn TemplatedMin[template T: Ordered](x: T, y: T) -> T {
|
||||
return if x <= y then x else y;
|
||||
}
|
||||
```
|
||||
@@ -2882,7 +2882,7 @@ In this case, `Print` is not a direct member of `Circle`, but:
|
||||
`Printable`.
|
||||
|
||||
```carbon
|
||||
fn GenericPrint[T:! Printable](x: T) {
|
||||
fn GenericPrint[T: Printable](x: T) {
|
||||
// Look up into `T` delegates to `Printable`, so this
|
||||
// finds `Printable.Print`:
|
||||
x.Print();
|
||||
@@ -2943,7 +2943,7 @@ A function can require type arguments to implement multiple interfaces (or other
|
||||
facet types) by combining them using an ampersand (`&`):
|
||||
|
||||
```carbon
|
||||
fn PrintMin[T:! Ordered & Printable](x: T, y: T) {
|
||||
fn PrintMin[T: Ordered & Printable](x: T, y: T) {
|
||||
// Can compare since type `T` implements `Ordered`.
|
||||
if (x <= y) {
|
||||
// Can call `Print` since type `T` implements `Printable`.
|
||||
@@ -2961,7 +2961,7 @@ syntax ([1](expressions/member_access.md),
|
||||
qualify the name of the member, as in:
|
||||
|
||||
```carbon
|
||||
fn DrawTies[T:! Renderable & GameResult](x: T) {
|
||||
fn DrawTies[T: Renderable & GameResult](x: T) {
|
||||
if (x.(GameResult.Draw)()) {
|
||||
x.(Renderable.Draw)();
|
||||
}
|
||||
@@ -2993,18 +2993,18 @@ class Game {
|
||||
}
|
||||
}
|
||||
|
||||
fn TemplateDraw[template T:! type](x: T) {
|
||||
fn TemplateDraw[template T: type](x: T) {
|
||||
// Calls `Game.Draw` when `T` is `Game`:
|
||||
x.Draw();
|
||||
}
|
||||
|
||||
fn ConstrainedTemplateDraw[template T:! Renderable](x: T) {
|
||||
fn ConstrainedTemplateDraw[template T: Renderable](x: T) {
|
||||
// ❌ Error when `T` is `Game`: Finds both `T.Draw` and
|
||||
// `Renderable.Draw`, and they are different.
|
||||
x.Draw();
|
||||
}
|
||||
|
||||
fn CheckedGenericDraw[T:! Renderable](x: T) {
|
||||
fn CheckedGenericDraw[T: Renderable](x: T) {
|
||||
// Always calls `Renderable.Draw`, even when `T` is `Game`:
|
||||
x.Draw();
|
||||
}
|
||||
@@ -3038,7 +3038,7 @@ stack.
|
||||
|
||||
```
|
||||
interface StackInterface {
|
||||
let ElementType:! Movable;
|
||||
let ElementType: Movable;
|
||||
fn Push(ref self, value: ElementType);
|
||||
fn Pop(ref self) -> ElementType;
|
||||
fn IsEmpty(self) -> bool;
|
||||
@@ -3085,7 +3085,7 @@ or can be marked with the `template` keyword. For example, to define a stack
|
||||
that can hold values of any type `T`:
|
||||
|
||||
```carbon
|
||||
class Stack(T:! type) {
|
||||
class Stack(T: type) {
|
||||
fn Push(ref self, value: T);
|
||||
fn Pop(ref self) -> T;
|
||||
|
||||
@@ -3107,7 +3107,7 @@ The values of type parameters are part of a type's value, and so may be deduced
|
||||
in a function call, as in this example:
|
||||
|
||||
```carbon
|
||||
fn PeekTopOfStack[T:! type](s: Stack(T)*) -> T {
|
||||
fn PeekTopOfStack[T: type](s: Stack(T)*) -> T {
|
||||
var top: T = s->Pop();
|
||||
s->Push(top);
|
||||
return top;
|
||||
@@ -3128,7 +3128,7 @@ PeekTopOfStack(&int_stack);
|
||||
[Choice types](#choice-types) may be parameterized similarly to classes:
|
||||
|
||||
```carbon
|
||||
choice Result(T:! type, Error:! type) {
|
||||
choice Result(T: type, Error: type) {
|
||||
Success(value: T),
|
||||
Failure(error: Error)
|
||||
}
|
||||
@@ -3140,7 +3140,7 @@ Interfaces are always parameterized by a `Self` type, but in some cases they
|
||||
will have additional parameters.
|
||||
|
||||
```carbon
|
||||
interface AddWith(U:! type);
|
||||
interface AddWith(U: type);
|
||||
```
|
||||
|
||||
Interfaces without parameters may only be implemented once for a given type, but
|
||||
@@ -3163,12 +3163,12 @@ An `impl` declaration may be parameterized by adding `forall [`_compile-time
|
||||
parameter list_`]` after the `impl` keyword introducer, as in:
|
||||
|
||||
```carbon
|
||||
impl forall [T:! Printable] Vector(T) as Printable;
|
||||
impl forall [Key:! Hashable, Value:! type]
|
||||
impl forall [T: Printable] Vector(T) as Printable;
|
||||
impl forall [Key: Hashable, Value: type]
|
||||
HashMap(Key, Value) as Has(Key);
|
||||
impl forall [T:! Ordered] T as PartiallyOrdered;
|
||||
impl forall [T:! ImplicitAs(i32)] BigInt as AddWith(T);
|
||||
impl forall [U:! type, T:! As(U)]
|
||||
impl forall [T: Ordered] T as PartiallyOrdered;
|
||||
impl forall [T: ImplicitAs(i32)] BigInt as AddWith(T);
|
||||
impl forall [U: type, T: As(U)]
|
||||
Optional(T) as As(Optional(U));
|
||||
```
|
||||
|
||||
@@ -3384,7 +3384,7 @@ There are some situations where the common type for two types is needed:
|
||||
will be set to the common type of the corresponding arguments, as in:
|
||||
|
||||
```carbon
|
||||
fn F[T:! type](x: T, y: T);
|
||||
fn F[T: type](x: T, y: T);
|
||||
|
||||
// Calls `F` with `T` set to the
|
||||
// common type of `G()` and `H()`:
|
||||
|
||||
+12
-12
@@ -173,7 +173,7 @@ provided for built-in types as necessary to give the semantics described above.
|
||||
|
||||
```
|
||||
// Simple `=`.
|
||||
interface AssignWith(U:! type) {
|
||||
interface AssignWith(U: type) {
|
||||
fn Op(ref self, other: U);
|
||||
}
|
||||
constraint Assign { extend AssignWith(Self); }
|
||||
@@ -187,7 +187,7 @@ Given `var x: T` and `y: U`:
|
||||
|
||||
```
|
||||
// Compound `+=`.
|
||||
interface AddAssignWith(U:! type) {
|
||||
interface AddAssignWith(U: type) {
|
||||
fn Op(ref self, other: U);
|
||||
}
|
||||
constraint AddAssign { extend AddAssignWith(Self); }
|
||||
@@ -195,7 +195,7 @@ constraint AddAssign { extend AddAssignWith(Self); }
|
||||
|
||||
```
|
||||
// Compound `-=`.
|
||||
interface SubAssignWith(U:! type) {
|
||||
interface SubAssignWith(U: type) {
|
||||
fn Op(ref self, other: U);
|
||||
}
|
||||
constraint SubAssign { extend SubAssignWith(Self); }
|
||||
@@ -203,7 +203,7 @@ constraint SubAssign { extend SubAssignWith(Self); }
|
||||
|
||||
```
|
||||
// Compound `*=`.
|
||||
interface MulAssignWith(U:! type) {
|
||||
interface MulAssignWith(U: type) {
|
||||
fn Op(ref self, other: U);
|
||||
}
|
||||
constraint MulAssign { extend MulAssignWith(Self); }
|
||||
@@ -211,7 +211,7 @@ constraint MulAssign { extend MulAssignWith(Self); }
|
||||
|
||||
```
|
||||
// Compound `/=`.
|
||||
interface DivAssignWith(U:! type) {
|
||||
interface DivAssignWith(U: type) {
|
||||
fn Op(ref self, other: U);
|
||||
}
|
||||
constraint DivAssign { extend DivAssignWith(Self); }
|
||||
@@ -219,7 +219,7 @@ constraint DivAssign { extend DivAssignWith(Self); }
|
||||
|
||||
```
|
||||
// Compound `%=`.
|
||||
interface ModAssignWith(U:! type) {
|
||||
interface ModAssignWith(U: type) {
|
||||
fn Op(ref self, other: U);
|
||||
}
|
||||
constraint ModAssign { extend ModAssignWith(Self); }
|
||||
@@ -246,7 +246,7 @@ Given `var x: T` and `y: U`:
|
||||
|
||||
```
|
||||
// Compound `&=`.
|
||||
interface BitAndAssignWith(U:! type) {
|
||||
interface BitAndAssignWith(U: type) {
|
||||
fn Op(ref self, other: U);
|
||||
}
|
||||
constraint BitAndAssign { extend BitAndAssignWith(Self); }
|
||||
@@ -254,7 +254,7 @@ constraint BitAndAssign { extend BitAndAssignWith(Self); }
|
||||
|
||||
```
|
||||
// Compound `|=`.
|
||||
interface BitOrAssignWith(U:! type) {
|
||||
interface BitOrAssignWith(U: type) {
|
||||
fn Op(ref self, other: U);
|
||||
}
|
||||
constraint BitOrAssign { extend BitOrAssignWith(Self); }
|
||||
@@ -262,7 +262,7 @@ constraint BitOrAssign { extend BitOrAssignWith(Self); }
|
||||
|
||||
```
|
||||
// Compound `^=`.
|
||||
interface BitXorAssignWith(U:! type) {
|
||||
interface BitXorAssignWith(U: type) {
|
||||
fn Op(ref self, other: U);
|
||||
}
|
||||
constraint BitXorAssign { extend BitXorAssignWith(Self); }
|
||||
@@ -270,7 +270,7 @@ constraint BitXorAssign { extend BitXorAssignWith(Self); }
|
||||
|
||||
```
|
||||
// Compound `<<=`.
|
||||
interface LeftShiftAssignWith(U:! type) {
|
||||
interface LeftShiftAssignWith(U: type) {
|
||||
fn Op(ref self, other: U);
|
||||
}
|
||||
constraint LeftShiftAssign { extend LeftShiftAssignWith(Self); }
|
||||
@@ -278,7 +278,7 @@ constraint LeftShiftAssign { extend LeftShiftAssignWith(Self); }
|
||||
|
||||
```
|
||||
// Compound `>>=`.
|
||||
interface RightShiftAssignWith(U:! type) {
|
||||
interface RightShiftAssignWith(U: type) {
|
||||
fn Op(ref self, other: U);
|
||||
}
|
||||
constraint RightShiftAssign { extend RightShiftAssignWith(Self); }
|
||||
@@ -307,7 +307,7 @@ This defaulting is accomplished by a parameterized implementation of
|
||||
`OpAssignWith(U)` defined in terms of `AssignWith` and `OpWith`:
|
||||
|
||||
```
|
||||
impl forall [U:! type, T:! OpWith(U) where .Self impls AssignWith(.Self.Result)]
|
||||
impl forall [U: type, T: OpWith(U) where .Self impls AssignWith(.Self.Result)]
|
||||
T as OpAssignWith(U) {
|
||||
fn Op(ref self, other: U) {
|
||||
// Here, `$` is the operator described by `OpWith`.
|
||||
|
||||
+13
-11
@@ -1001,8 +1001,8 @@ they appear in square brackets `[`...`]` as usual, while `self` remains the
|
||||
first parameter in the parens `(`...`)`:
|
||||
|
||||
```carbon
|
||||
class Wrapper(T:! type) {
|
||||
fn Print[U:! type](self, x: U);
|
||||
class Wrapper(T: type) {
|
||||
fn Print[U: type](self, x: U);
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1170,17 +1170,19 @@ class, but other kinds of type declarations, like choice types, are allowed.
|
||||
|
||||
### Let
|
||||
|
||||
Other type constants can be defined using a `let` declaration:
|
||||
Other type constants can be defined using a `let` declaration with a `template`
|
||||
phase modifier:
|
||||
|
||||
```
|
||||
class MyClass {
|
||||
let Pi:! f32 = 3.141592653589793;
|
||||
let IndexType:! type = i32;
|
||||
let template Pi: f32 = 3.141592653589793;
|
||||
let template IndexType: type = i32;
|
||||
}
|
||||
```
|
||||
|
||||
The `:!` indicates that this is defining a compile-time constant, and so does
|
||||
not affect the storage of instances of that class.
|
||||
> **TODO**: This use of `let` and `template` is one we want to replace with a
|
||||
> better construct. There is nothing "templated" about the code using these, and
|
||||
> so that modifier isn't a good one even though it is the one available.
|
||||
|
||||
### Alias
|
||||
|
||||
@@ -1756,8 +1758,8 @@ call the `UnsafeDelete` method instead. Note that you may not call
|
||||
```
|
||||
interface Allocator {
|
||||
// ...
|
||||
fn Delete[T:! Deletable](ref self, p: T*);
|
||||
fn UnsafeDelete[T:! Destructible](ref self, p: T*);
|
||||
fn Delete[T: Deletable](ref self, p: T*);
|
||||
fn UnsafeDelete[T: Destructible](ref self, p: T*);
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1767,13 +1769,13 @@ checked-generic function expecting a `Deletable` type, use the
|
||||
[type adapter](/docs/design/generics/details.md#adapting-types).
|
||||
|
||||
```
|
||||
class UnsafeAllowDelete(T:! Concrete) {
|
||||
class UnsafeAllowDelete(T: Concrete) {
|
||||
extend adapt T;
|
||||
impl as Deletable {}
|
||||
}
|
||||
|
||||
// Example usage:
|
||||
fn RequiresDeletable[T:! Deletable](p: T*);
|
||||
fn RequiresDeletable[T: Deletable](p: T*);
|
||||
var x: MyExtensible;
|
||||
RequiresDeletable(&x as UnsafeAllowDelete(MyExtensible)*);
|
||||
```
|
||||
|
||||
@@ -83,8 +83,8 @@ interface:
|
||||
|
||||
```carbon
|
||||
interface Iterate {
|
||||
let ElementType:! type;
|
||||
let CursorType:! type;
|
||||
let ElementType: type;
|
||||
let CursorType: type;
|
||||
fn NewCursor(self) -> CursorType;
|
||||
fn Next(self, ref cursor: CursorType) -> Optional(ElementType);
|
||||
}
|
||||
|
||||
@@ -219,15 +219,15 @@ following family of interfaces:
|
||||
```
|
||||
// Unary `-`.
|
||||
interface Negate {
|
||||
default let Result:! type = Self;
|
||||
default let Result: type = Self;
|
||||
fn Op(self) -> Result;
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
// Binary `+`.
|
||||
interface AddWith(U:! type) {
|
||||
default let Result:! type = Self;
|
||||
interface AddWith(U: type) {
|
||||
default let Result: type = Self;
|
||||
fn Op(self, other: U) -> Result;
|
||||
}
|
||||
constraint Add {
|
||||
@@ -237,8 +237,8 @@ constraint Add {
|
||||
|
||||
```
|
||||
// Binary `-`.
|
||||
interface SubWith(U:! type) {
|
||||
default let Result:! type = Self;
|
||||
interface SubWith(U: type) {
|
||||
default let Result: type = Self;
|
||||
fn Op(self, other: U) -> Result;
|
||||
}
|
||||
constraint Sub {
|
||||
@@ -248,8 +248,8 @@ constraint Sub {
|
||||
|
||||
```
|
||||
// Binary `*`.
|
||||
interface MulWith(U:! type) {
|
||||
default let Result:! type = Self;
|
||||
interface MulWith(U: type) {
|
||||
default let Result: type = Self;
|
||||
fn Op(self, other: U) -> Result;
|
||||
}
|
||||
constraint Mul {
|
||||
@@ -259,8 +259,8 @@ constraint Mul {
|
||||
|
||||
```
|
||||
// Binary `/`.
|
||||
interface DivWith(U:! type) {
|
||||
default let Result:! type = Self;
|
||||
interface DivWith(U: type) {
|
||||
default let Result: type = Self;
|
||||
fn Op(self, other: U) -> Result;
|
||||
}
|
||||
constraint Div {
|
||||
@@ -270,8 +270,8 @@ constraint Div {
|
||||
|
||||
```
|
||||
// Binary `%`.
|
||||
interface ModWith(U:! type) {
|
||||
default let Result:! type = Self;
|
||||
interface ModWith(U: type) {
|
||||
default let Result: type = Self;
|
||||
fn Op(self, other: U) -> Result;
|
||||
}
|
||||
constraint Mod {
|
||||
|
||||
@@ -165,7 +165,7 @@ Explicit casts can be defined for user-defined types such as
|
||||
[classes](../classes.md) by implementing the `As` interface:
|
||||
|
||||
```
|
||||
interface As(Dest:! type) {
|
||||
interface As(Dest: type) {
|
||||
fn Convert(self) -> Dest;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -197,15 +197,15 @@ implementing the following family of interfaces:
|
||||
```
|
||||
// Unary `^`.
|
||||
interface BitComplement {
|
||||
default let Result:! type = Self;
|
||||
default let Result: type = Self;
|
||||
fn Op(self) -> Result;
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
// Binary `&`.
|
||||
interface BitAndWith(U:! type) {
|
||||
default let Result:! type = Self;
|
||||
interface BitAndWith(U: type) {
|
||||
default let Result: type = Self;
|
||||
fn Op(self, other: U) -> Result;
|
||||
}
|
||||
constraint BitAnd {
|
||||
@@ -215,8 +215,8 @@ constraint BitAnd {
|
||||
|
||||
```
|
||||
// Binary `|`.
|
||||
interface BitOrWith(U:! type) {
|
||||
default let Result:! type = Self;
|
||||
interface BitOrWith(U: type) {
|
||||
default let Result: type = Self;
|
||||
fn Op(self, other: U) -> Result;
|
||||
}
|
||||
constraint BitOr {
|
||||
@@ -226,8 +226,8 @@ constraint BitOr {
|
||||
|
||||
```
|
||||
// Binary `^`.
|
||||
interface BitXorWith(U:! type) {
|
||||
default let Result:! type = Self;
|
||||
interface BitXorWith(U: type) {
|
||||
default let Result: type = Self;
|
||||
fn Op(self, other: U) -> Result;
|
||||
}
|
||||
constraint BitXor {
|
||||
@@ -237,8 +237,8 @@ constraint BitXor {
|
||||
|
||||
```
|
||||
// Binary `<<`.
|
||||
interface LeftShiftWith(U:! type) {
|
||||
default let Result:! type = Self;
|
||||
interface LeftShiftWith(U: type) {
|
||||
default let Result: type = Self;
|
||||
fn Op(self, other: U) -> Result;
|
||||
}
|
||||
constraint LeftShift {
|
||||
@@ -248,8 +248,8 @@ constraint LeftShift {
|
||||
|
||||
```
|
||||
// Binary `>>`.
|
||||
interface RightShiftWith(U:! type) {
|
||||
default let Result:! type = Self;
|
||||
interface RightShiftWith(U: type) {
|
||||
default let Result: type = Self;
|
||||
fn Op(self, other: U) -> Result;
|
||||
}
|
||||
constraint RightShift {
|
||||
|
||||
@@ -254,7 +254,7 @@ The `EqWith` interface is used to define the semantics of the `==` and `!=`
|
||||
operators for a given pair of types:
|
||||
|
||||
```
|
||||
interface EqWith(U:! type) {
|
||||
interface EqWith(U: type) {
|
||||
fn Equal(self, u: U) -> bool;
|
||||
default fn NotEqual(self, u: U) -> bool {
|
||||
return not (self == u);
|
||||
@@ -354,7 +354,7 @@ choice Ordering {
|
||||
Greater,
|
||||
Incomparable
|
||||
}
|
||||
interface OrderedWith(U:! type) {
|
||||
interface OrderedWith(U: type) {
|
||||
fn Compare(self, u: U) -> Ordering;
|
||||
default fn Less(self, u: U) -> bool {
|
||||
return self.Compare(u) == Ordering.Less;
|
||||
@@ -433,8 +433,8 @@ implemented. The behaviors of such overrides should follow those of the above
|
||||
default implementations, and the members of an `OrderedWith` implementation
|
||||
should have no observable side-effects.
|
||||
|
||||
`OrderedWith` implementations should be _transitive_. That is, given `V:! type`,
|
||||
`U:! OrderedWith(V)`, `T:! OrderedWith(U) & OrderedWith(V)`, `a: T`, `b: U`,
|
||||
`OrderedWith` implementations should be _transitive_. That is, given `V: type`,
|
||||
`U: OrderedWith(V)`, `T: OrderedWith(U) & OrderedWith(V)`, `a: T`, `b: U`,
|
||||
`c: V`, then:
|
||||
|
||||
- If `a <= b` and `b <= c` then `a <= c`, and moreover if either `a < b` or
|
||||
|
||||
@@ -75,7 +75,7 @@ The common type of two types `T` and `U` is `(T as CommonType(U)).Result`, where
|
||||
defined as follows:
|
||||
|
||||
```
|
||||
constraint CommonType(U:! CommonTypeWith(Self)) {
|
||||
constraint CommonType(U: CommonTypeWith(Self)) {
|
||||
extend CommonTypeWith(U) where .Result == U.Result;
|
||||
}
|
||||
```
|
||||
@@ -87,8 +87,8 @@ The interface `CommonTypeWith` is used to customize the behavior of
|
||||
`CommonType`:
|
||||
|
||||
```
|
||||
interface CommonTypeWith(U:! type) {
|
||||
let Result:! type
|
||||
interface CommonTypeWith(U: type) {
|
||||
let Result: type
|
||||
where Self impls ImplicitAs(.Self) and
|
||||
U impls ImplicitAs(.Self);
|
||||
}
|
||||
@@ -120,15 +120,15 @@ The interface `SymmetricCommonTypeWith` is an implementation detail of the
|
||||
`CommonType` constraint. It is defined and implemented as follows:
|
||||
|
||||
```
|
||||
interface SymmetricCommonTypeWith(U:! type) {
|
||||
let Result:! type
|
||||
interface SymmetricCommonTypeWith(U: type) {
|
||||
let Result: type
|
||||
where Self impls ImplicitAs(.Self) and
|
||||
U impls ImplicitAs(.Self);
|
||||
}
|
||||
match_first {
|
||||
impl forall [T:! type, U:! CommonTypeWith(T)]
|
||||
impl forall [T: type, U: CommonTypeWith(T)]
|
||||
T as SymmetricCommonTypeWith(U) where .Result = U.Result {}
|
||||
impl forall [U:! type, T:! CommonTypeWith(U)]
|
||||
impl forall [U: type, T: CommonTypeWith(U)]
|
||||
T as SymmetricCommonTypeWith(U) where .Result = T.Result {}
|
||||
}
|
||||
```
|
||||
@@ -139,7 +139,7 @@ declarations above are used. The `CommonType` constraint is then defined as
|
||||
follows:
|
||||
|
||||
```
|
||||
constraint CommonType(U:! SymmetricCommonTypeWith(Self)) {
|
||||
constraint CommonType(U: SymmetricCommonTypeWith(Self)) {
|
||||
extend SymmetricCommonTypeWith(U) where .Result == U.Result;
|
||||
}
|
||||
```
|
||||
@@ -153,10 +153,10 @@ the `CommonType` constraint is not met. For example, given:
|
||||
|
||||
```
|
||||
// Implementation #1
|
||||
impl forall [T:! type] MyX as CommonTypeWith(T) where .Result = MyX {}
|
||||
impl forall [T: type] MyX as CommonTypeWith(T) where .Result = MyX {}
|
||||
|
||||
// Implementation #2
|
||||
impl forall [T:! type] MyY as CommonTypeWith(T) where .Result = MyY {}
|
||||
impl forall [T: type] MyY as CommonTypeWith(T) where .Result = MyY {}
|
||||
```
|
||||
|
||||
`MyX as CommonTypeWith(MyY)` will select #1, and `MyY as CommonTypeWith(MyX)`
|
||||
@@ -168,7 +168,7 @@ because result types differ.
|
||||
If `T` is the same type as `U`, the result is that type:
|
||||
|
||||
```
|
||||
final impl forall [T:! type] T as CommonTypeWith(T) where .Result = T {}
|
||||
final impl forall [T: type] T as CommonTypeWith(T) where .Result = T {}
|
||||
```
|
||||
|
||||
_Note:_ This rule is intended to be considered more specialized than the other
|
||||
@@ -179,7 +179,7 @@ assumed to be `T`, even in contexts where `T` involves a symbolic binding and so
|
||||
the result would normally be an unknown type whose facet type is `type`.
|
||||
|
||||
```
|
||||
fn F[T:! Hashable](c: bool, x: T, y: T) -> HashCode {
|
||||
fn F[T: Hashable](c: bool, x: T, y: T) -> HashCode {
|
||||
// OK, type of `if` expression is `T`.
|
||||
return (if c then x else y).Hash();
|
||||
}
|
||||
@@ -190,7 +190,7 @@ fn F[T:! Hashable](c: bool, x: T, y: T) -> HashCode {
|
||||
If `T` implicitly converts to `U`, the common type is `U`:
|
||||
|
||||
```
|
||||
impl forall [T:! type, U:! ImplicitAs(T)]
|
||||
impl forall [T: type, U: ImplicitAs(T)]
|
||||
T as CommonTypeWith(U) where .Result = T {}
|
||||
```
|
||||
|
||||
|
||||
@@ -224,7 +224,7 @@ extends
|
||||
[the `As` interface used to implement `as` expressions](as_expressions.md#extensibility):
|
||||
|
||||
```
|
||||
interface ImplicitAs(Dest:! type) {
|
||||
interface ImplicitAs(Dest: type) {
|
||||
extend As(Dest);
|
||||
// Inherited from As(Dest):
|
||||
// fn Convert(self) -> Dest;
|
||||
|
||||
@@ -52,13 +52,13 @@ left-to-right with all of them.
|
||||
Its semantics are defined in terms of the following interfaces:
|
||||
|
||||
```
|
||||
interface IndexWith(SubscriptType:! type) {
|
||||
let ElementType:! type;
|
||||
interface IndexWith(SubscriptType: type) {
|
||||
let ElementType: type;
|
||||
fn At(bound self, subscript: SubscriptType) -> val ElementType;
|
||||
fn Ref(bound ref self, subscript: SubscriptType) -> ref ElementType;
|
||||
}
|
||||
|
||||
interface IndirectIndexWith(SubscriptType:! type) {
|
||||
interface IndirectIndexWith(SubscriptType: type) {
|
||||
require Self impls IndexWith(SubscriptType);
|
||||
fn Ref(bound self, subscript: SubscriptType) -> ref ElementType;
|
||||
}
|
||||
@@ -81,7 +81,7 @@ implement `IndirectIndexWith(I)`:
|
||||
|
||||
```
|
||||
final impl forall
|
||||
[SubscriptType:! type, T:! IndirectIndexWith(SubscriptType)]
|
||||
[SubscriptType: type, T: IndirectIndexWith(SubscriptType)]
|
||||
T as IndexWith(SubscriptType) {
|
||||
where ElementType = T.(IndirectIndexWith(SubscriptType).ElementType);
|
||||
fn At(bound self, subscript: SubscriptType) -> val ElementType {
|
||||
@@ -101,9 +101,9 @@ its own definitions of `IndexWith.At` and `IndexWith.Ref`.
|
||||
An array type could implement subscripting like so:
|
||||
|
||||
```
|
||||
class Array(template T:! type, template N:! i64) {
|
||||
class Array(template T: type, template N: i64) {
|
||||
impl as IndexWith(like i64) {
|
||||
let ElementType:! type = T;
|
||||
let ElementType: type = T;
|
||||
fn At(bound self, subscript: i64) -> val T;
|
||||
fn Ref(bound ref self, subscript: i64) -> ref T;
|
||||
}
|
||||
@@ -113,9 +113,9 @@ class Array(template T:! type, template N:! i64) {
|
||||
And a type such as `std::span` could look like this:
|
||||
|
||||
```
|
||||
class Span(T:! type) {
|
||||
class Span(T: type) {
|
||||
impl as IndirectIndexWith(like i64) {
|
||||
let ElementType:! type = T;
|
||||
let ElementType: type = T;
|
||||
fn Ref(bound ref self, subscript: i64) -> ref T;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,12 +77,11 @@ and binary integer literals, and decimal and hexadecimal real number literals.
|
||||
The following types are defined in the Carbon prelude:
|
||||
|
||||
- `Core.BigInt`, an arbitrary-precision integer type;
|
||||
- `Core.Rational(T:! type)`, a rational type, parameterized by a type used for
|
||||
- `Core.Rational(T: type)`, a rational type, parameterized by a type used for
|
||||
its numerator and denominator -- the exact constraints on `T` are not yet
|
||||
decided;
|
||||
- `Core.IntLiteral(N:! Core.BigInt)`, a type representing integer literals;
|
||||
and
|
||||
- `Core.FloatLiteral(X:! Core.Rational(Core.BigInt))`, a type representing
|
||||
- `Core.IntLiteral(N: Core.BigInt)`, a type representing integer literals; and
|
||||
- `Core.FloatLiteral(X: Core.Rational(Core.BigInt))`, a type representing
|
||||
floating-point literals.
|
||||
|
||||
All of these types are usable during compilation. `Core.BigInt` supports the
|
||||
@@ -100,13 +99,13 @@ these operations are typically heterogeneous: for example, an addition between
|
||||
`Core.IntLiteral(N)` converts to any sufficiently large integer type, as if by:
|
||||
|
||||
```
|
||||
impl forall [template N:! Core.BigInt, template M:! Core.BigInt]
|
||||
impl forall [template N: Core.BigInt, template M: Core.BigInt]
|
||||
Core.IntLiteral(N) as ImplicitAs(Core.Int(M))
|
||||
if N >= Core.Int(M).MinValue as Core.BigInt
|
||||
and N <= Core.Int(M).MaxValue as Core.BigInt {
|
||||
...
|
||||
}
|
||||
impl forall [template N:! Core.BigInt, template M:! Core.BigInt]
|
||||
impl forall [template N: Core.BigInt, template M: Core.BigInt]
|
||||
Core.IntLiteral(N) as ImplicitAs(Core.UInt(M))
|
||||
if N >= Core.UInt(M).MinValue as Core.BigInt
|
||||
and N <= Core.UInt(M).MaxValue as Core.BigInt {
|
||||
@@ -147,7 +146,7 @@ var z: f64 = 1.0 / 3.0;
|
||||
// This is an error: 300 cannot be represented in type `i8`.
|
||||
var c: i8 = 300;
|
||||
|
||||
fn F[template T:! type](v: T) {
|
||||
fn F[template T: type](v: T) {
|
||||
var x: i32 = v * 2;
|
||||
}
|
||||
|
||||
@@ -158,7 +157,7 @@ F(1_000_000_000);
|
||||
F(2_000_000_000);
|
||||
|
||||
// No storage required for the bound when it's of integer literal type.
|
||||
struct Span(template T:! type, template BoundT:! type) {
|
||||
struct Span(template T: type, template BoundT: type) {
|
||||
var begin: T*;
|
||||
var bound: BoundT;
|
||||
}
|
||||
@@ -176,13 +175,13 @@ fn G() -> i32 {
|
||||
fn PassMeZero(_: Core.IntLiteral(0));
|
||||
|
||||
// Can only be called with integer literals in the given range.
|
||||
fn ConvertToByte[template N:! Core.BigInt](_: Core.IntLiteral(N)) -> i8
|
||||
fn ConvertToByte[template N: Core.BigInt](_: Core.IntLiteral(N)) -> i8
|
||||
if N >= -128 and N <= 127 {
|
||||
return N as i8;
|
||||
}
|
||||
|
||||
// Given any int literal, produces a literal whose value is one higher.
|
||||
fn OneHigher(L: Core.IntLiteral(template _:! Core.BigInt)) -> auto {
|
||||
fn OneHigher(L: Core.IntLiteral(template _: Core.BigInt)) -> auto {
|
||||
return L + 1;
|
||||
}
|
||||
// Error: 256 can't be represented in type `i8`.
|
||||
|
||||
@@ -171,7 +171,7 @@ alias MyNS = MyNamespace;
|
||||
fn CallMyFunction() { MyNS.MyFunction(); }
|
||||
|
||||
// ❌ Error: a namespace is not a value.
|
||||
let MyNS2:! auto = MyNamespace;
|
||||
let MyNS2: auto = MyNamespace;
|
||||
|
||||
fn CallMyFunction2() {
|
||||
// ❌ Error: cannot perform compound member access into a namespace.
|
||||
@@ -274,7 +274,7 @@ interface I {
|
||||
fn F();
|
||||
}
|
||||
|
||||
class C(T:! I) {
|
||||
class C(T: I) {
|
||||
extend base: T;
|
||||
// `F` names `T.F` here, found in `I`.
|
||||
fn G() { F(); }
|
||||
@@ -295,14 +295,14 @@ completeness, as it requires `A(-1)` to be complete, which requires
|
||||
`B(array(i32, -1))` to be complete, and that contains an invalid type.
|
||||
|
||||
```carbon
|
||||
interface B(T:! type) {}
|
||||
interface B(T: type) {}
|
||||
|
||||
interface A(N:! i32) {
|
||||
interface A(N: i32) {
|
||||
// Requires `B(N)` to be complete.
|
||||
extend require impls B(array(i32, N)) {}
|
||||
}
|
||||
|
||||
class C(N! i32) {
|
||||
class C(N: i32) {
|
||||
// Requires `A(N)` to be complete, which requires `B(N)` to be complete.
|
||||
extend impl as A(N);
|
||||
}
|
||||
@@ -357,7 +357,7 @@ positional element of the tuple.
|
||||
// ✅ `d == 43`.
|
||||
let d: i32 = (41, 42, 43).(1 + 1);
|
||||
// ✅ `e == 2`.
|
||||
let template e:! i32 = (1, 2, 3).(0x1);
|
||||
let template e: i32 = (1, 2, 3).(0x1);
|
||||
// ❌ Error: no tuple element with index 4.
|
||||
let f: i32 = (1, 2).(2 * 2);
|
||||
|
||||
@@ -404,7 +404,7 @@ fn PrintPointTwice() {
|
||||
|
||||
### Facet binding
|
||||
|
||||
A search for members of a facet binding `T:! C` treats the facet binding as an
|
||||
A search for members of a facet binding `T: C` treats the facet binding as an
|
||||
[archetype](/docs/design/generics/terminology.md#archetype), and finds members
|
||||
of the facet `T` of facet type `C`.
|
||||
|
||||
@@ -415,7 +415,7 @@ interface Printable {
|
||||
fn Print(self);
|
||||
}
|
||||
|
||||
fn GenericPrint[T:! Printable](a: T) {
|
||||
fn GenericPrint[T: Printable](a: T) {
|
||||
// ✅ OK, type of `a` is the facet binding `T`;
|
||||
// `Print` found in the facet `T as Printable`.
|
||||
a.Print();
|
||||
@@ -438,10 +438,10 @@ Evaluation of an expression involving the binding may still succeed, but will
|
||||
result in a symbolic constant involving that binding.
|
||||
|
||||
```carbon
|
||||
class GenericWrapper(T:! type) {
|
||||
class GenericWrapper(T: type) {
|
||||
var field: T;
|
||||
}
|
||||
fn F[T:! type](x: GenericWrapper(T)) -> T {
|
||||
fn F[T: type](x: GenericWrapper(T)) -> T {
|
||||
// ✅ OK, finds `GenericWrapper(T).field`.
|
||||
return x.field;
|
||||
}
|
||||
@@ -449,7 +449,7 @@ fn F[T:! type](x: GenericWrapper(T)) -> T {
|
||||
interface Renderable {
|
||||
fn Draw(self);
|
||||
}
|
||||
fn DrawChecked[T:! Renderable](c: T) {
|
||||
fn DrawChecked[T: Renderable](c: T) {
|
||||
// `Draw` resolves to `(T as Renderable).Draw` or
|
||||
// `T.(Renderable.Draw)`.
|
||||
c.Draw();
|
||||
@@ -472,7 +472,7 @@ any symbolic bindings are still unknown. The lookup results from these two
|
||||
contexts are [combined](#lookup-ambiguity).
|
||||
|
||||
```carbon
|
||||
fn DrawTemplate[template T:! type](c: T) {
|
||||
fn DrawTemplate[template T: type](c: T) {
|
||||
// `Draw` not found in `type`, looked up in the
|
||||
// actual deduced value of `T`.
|
||||
c.Draw();
|
||||
@@ -492,10 +492,10 @@ the compiler can assume the body of a templated class will be the same for all
|
||||
argument values:
|
||||
|
||||
```carbon
|
||||
class TemplateWrapper(template T:! type) {
|
||||
class TemplateWrapper(template T: type) {
|
||||
var field: T;
|
||||
}
|
||||
fn G[template T:! type](x: TemplateWrapper(T)) -> T {
|
||||
fn G[template T: type](x: TemplateWrapper(T)) -> T {
|
||||
// ✅ Allowed, finds `TemplateWrapper(T).field`.
|
||||
return x.field;
|
||||
}
|
||||
@@ -508,10 +508,10 @@ cases where the lookup only succeeds for specific values of `T`:
|
||||
class HasField {
|
||||
var field: i32;
|
||||
}
|
||||
class DerivingWrapper(template T:! type) {
|
||||
class DerivingWrapper(template T: type) {
|
||||
extend base: T;
|
||||
}
|
||||
fn H[template T:! type](x: DerivingWrapper(T)) -> i32 {
|
||||
fn H[template T: type](x: DerivingWrapper(T)) -> i32 {
|
||||
// ✅ Allowed, but no name `field` found in template
|
||||
// definition of `DerivingWrapper`.
|
||||
return x.field;
|
||||
@@ -549,7 +549,7 @@ interface Renderable {
|
||||
fn Draw(self);
|
||||
}
|
||||
|
||||
fn DrawTemplate2[template T:! Renderable](c: T) {
|
||||
fn DrawTemplate2[template T: Renderable](c: T) {
|
||||
// Member lookup finds `(T as Renderable).Draw` and the
|
||||
// `Draw` member of the actual deduced value of `T`, if any.
|
||||
c.Draw();
|
||||
@@ -577,7 +577,7 @@ class SquareWidget {
|
||||
}
|
||||
}
|
||||
|
||||
fn FlyTemplate[template T:! type](c: T) {
|
||||
fn FlyTemplate[template T: type](c: T) {
|
||||
c.Fly();
|
||||
}
|
||||
|
||||
@@ -645,7 +645,7 @@ interface Addable {
|
||||
// #1
|
||||
fn Add(self, other: Self) -> Self;
|
||||
// #2
|
||||
default fn Sum[Seq:! Iterable where .ValueType = Self](seq: Seq) -> Self {
|
||||
default fn Sum[Seq: Iterable where .ValueType = Self](seq: Seq) -> Self {
|
||||
// ...
|
||||
}
|
||||
alias AliasForSum = Sum;
|
||||
@@ -739,7 +739,7 @@ base class WidgetBase {
|
||||
// ✅ OK, even though `WidgetBase` does not implement `Renderable`.
|
||||
alias Draw = Renderable.Draw;
|
||||
|
||||
fn DrawAll[T:! Renderable](v: Vector(T)) {
|
||||
fn DrawAll[T: Renderable](v: Vector(T)) {
|
||||
for (w: T in v) {
|
||||
// ✅ OK. Unqualified lookup for `Draw` finds alias `WidgetBase.Draw`
|
||||
// to `Renderable.Draw`, which does not perform `impl` lookup yet.
|
||||
@@ -945,11 +945,11 @@ fn CallStaticMethod(c: C) {
|
||||
// same as `c.field = 1;`
|
||||
c.(C.field) = 1;
|
||||
|
||||
// ✅ OK
|
||||
let T:! type = C.Nested;
|
||||
// ❌ Error: value of `:!` binding is not compile-time because it
|
||||
// ✅ OK (also OK with `template`)
|
||||
let generic G: type = C.Nested;
|
||||
// ❌ Error: value of `generic` binding is not compile-time because it
|
||||
// refers to local variable `c`.
|
||||
let U:! type = c.Nested;
|
||||
let generic U: type = c.Nested;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
+11
-11
@@ -236,8 +236,8 @@ When the return clause is provided, including when it is `-> ()`, the `return`
|
||||
statement must have an expression that is convertible to the return type, and a
|
||||
`return` statement must be used to end control flow of the function.
|
||||
|
||||
> **TODO:** Update this section to cover the requirements on the form of the
|
||||
> expression.
|
||||
> **TODO:** Update this section to cover the requirements on the extended type
|
||||
> of the expression.
|
||||
|
||||
## Positional parameters
|
||||
|
||||
@@ -642,10 +642,10 @@ function type other than asking for the type of the function value.
|
||||
fn F(x: i32) -> i32 { return x; }
|
||||
|
||||
// Compile-time function.
|
||||
musteval fn TypeOf[T:! type](x: T) -> type { return T; }
|
||||
musteval fn TypeOf[T: type](x: T) -> type { return T; }
|
||||
|
||||
// `F` is a first-class value with a first-class type.
|
||||
let template FType:! type = TypeOf(F);
|
||||
let template FType: type = TypeOf(F);
|
||||
var my_f: FType = F;
|
||||
```
|
||||
|
||||
@@ -755,15 +755,15 @@ parameters. This checking proceeds as follows:
|
||||
`ref`, and
|
||||
- An argument to a `ref` parameter must be prefixed with `ref`, except
|
||||
in a generic context where the parameter's `ref` status may vary.
|
||||
- If the parameter is a `template :!` binding, the argument expression is
|
||||
- If the parameter is a `template` binding, the argument expression is
|
||||
converted to have the same type as the binding and template constant
|
||||
expression phase.
|
||||
- If the parameter is a symbolic `:!` binding, the argument expression is
|
||||
- If the parameter is a checked generic binding, the argument expression is
|
||||
converted to have the same type as the binding and symbolic constant
|
||||
expression phase.
|
||||
- Otherwise, the parameter is pattern-matched against the argument.
|
||||
|
||||
If a parameter is a `:!` binding, its corresponding converted argument
|
||||
If a parameter is a compile-time binding, its corresponding converted argument
|
||||
expression is evaluated, and its value is added to the list of deduced
|
||||
argument values before any later parameters are processed.
|
||||
|
||||
@@ -786,7 +786,7 @@ interface:
|
||||
|
||||
```carbon
|
||||
interface Call(... each Arg: type) {
|
||||
let Result:! type;
|
||||
let Result: type;
|
||||
fn Op(self, ... each arg: each Arg) -> Result;
|
||||
}
|
||||
```
|
||||
@@ -799,7 +799,7 @@ translated into an invocation of `Call(Arg1, Arg2,` ... `ArgN).Op`, where
|
||||
For example, given:
|
||||
|
||||
```carbon
|
||||
fn Sort[T:! type, F:! Call(T, T) where .Result = Ordering]
|
||||
fn Sort[T: type, F: Call(T, T) where .Result = Ordering]
|
||||
(ref v: Vector(T), cmp: F) {
|
||||
// ...
|
||||
auto ord: auto = cmp(v[i], v[j]);
|
||||
@@ -823,7 +823,7 @@ deduced parameters. The intent is for the `impl` to support indirect calls in
|
||||
the same cases where the function supports direct calls, with the same meaning.
|
||||
|
||||
```carbon
|
||||
fn TakeI32Fn[F:! Call(i32)](f: F);
|
||||
fn TakeI32Fn[F: Call(i32)](f: F);
|
||||
fn I64Fn(n: i64);
|
||||
fn Run() {
|
||||
// ✅ `I64Fn` can be called with an `i32`, because
|
||||
@@ -841,7 +841,7 @@ The `Call` interface can be implemented to overload the meaning of the function
|
||||
call operator for a type.
|
||||
|
||||
```carbon
|
||||
class Func(Arg:! type) {
|
||||
class Func(Arg: type) {
|
||||
impl as Call((Arg,)) where .Result = () {
|
||||
fn Op(self, arg: (Arg,)) { Print("hello, world"); }
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ this:
|
||||
|
||||
```
|
||||
package Container;
|
||||
class HashSet(Key:! Hashable) { ... }
|
||||
class HashSet(Key: Hashable) { ... }
|
||||
```
|
||||
|
||||
- A `Song` type is defined in package `SongLib`.
|
||||
|
||||
@@ -31,9 +31,9 @@ This document explains the rationale for choosing to make
|
||||
## Rewrite constraints
|
||||
|
||||
Rewrite constraints are [`where` clauses](details.md#where-constraints) of the
|
||||
form `.AssociatedConstant = Value`. Given `T:! A where .B = C`, references to
|
||||
`T.(A.B)` are rewritten to `C`. This appendix describes the precise rules
|
||||
governing them.
|
||||
form `.AssociatedConstant = Value`. Given a checked generic binding `T: A where .B = C`,
|
||||
references to `T.(A.B)` are rewritten to `C`. This appendix describes the
|
||||
precise rules governing them.
|
||||
|
||||
## Combining constraints with `&`
|
||||
|
||||
@@ -41,10 +41,9 @@ Suppose we have `X = C where .R = A` and `Y = C where .R = B`. What should
|
||||
`C & X` produce? What should `X & Y` produce?
|
||||
|
||||
- Combining two rewrite rules with different rewrite targets results in a
|
||||
facet type where the associated constant is ambiguous. Given `T:! X & Y`,
|
||||
the type expression `T.R` is ambiguous between a rewrite to `A` and a
|
||||
rewrite to `B`. But given `T:! X & X`, `T.R` is unambiguously rewritten to
|
||||
`A`.
|
||||
facet type where the associated constant is ambiguous. Given `T: X & Y`, the
|
||||
type expression `T.R` is ambiguous between a rewrite to `A` and a rewrite to
|
||||
`B`. But given `T: X & X`, `T.R` is unambiguously rewritten to `A`.
|
||||
- Combining a constraint with a rewrite rule with a constraint with no rewrite
|
||||
rule preserves the rewrite rule, so `C & X` is the same as `X`. For example,
|
||||
supposing that `interface Container` extends `interface Iterable`, and
|
||||
@@ -72,18 +71,18 @@ happens, the facet type `C where A and B` is interpreted as
|
||||
|
||||
```carbon
|
||||
interface C {
|
||||
let T:! type;
|
||||
let U:! type;
|
||||
let V:! type;
|
||||
let T: type;
|
||||
let U: type;
|
||||
let V: type;
|
||||
}
|
||||
class M {
|
||||
alias Me = Self;
|
||||
}
|
||||
// ✅ Same as `C where .T = M and .U = M.Me`, which is
|
||||
// the same as `C where .T = M and .U = M`.
|
||||
fn F[A:! C where .T = M and .U = .T.Me]() {}
|
||||
// ❌ No member `Me` in `A.T:! type`.
|
||||
fn F[A:! C where .U = .T.Me and .T = M]() {}
|
||||
fn F[A: C where .T = M and .U = .T.Me]() {}
|
||||
// ❌ No member `Me` in `A.T: type`.
|
||||
fn F[A: C where .U = .T.Me and .T = M]() {}
|
||||
```
|
||||
|
||||
## Combining constraints with `extend`
|
||||
@@ -93,8 +92,8 @@ constraint that has rewrites.
|
||||
|
||||
```carbon
|
||||
interface A {
|
||||
let T:! type;
|
||||
let U:! type;
|
||||
let T: type;
|
||||
let U: type;
|
||||
}
|
||||
interface B {
|
||||
extend A where .T = .U and .U = i32;
|
||||
@@ -105,7 +104,7 @@ var n: i32;
|
||||
// ✅ Resolved constraint on `T` is
|
||||
// `B where .(A.T) = i32 and .(A.U) = i32`.
|
||||
// `T.(A.T)` is rewritten to `i32`.
|
||||
fn F(T:! B) -> T.(A.T) { return n; }
|
||||
fn F(generic T: B) -> T.(A.T) { return n; }
|
||||
```
|
||||
|
||||
## Combining constraints with `require` and `impls`
|
||||
@@ -118,8 +117,8 @@ are equivalent to `==` constraints:
|
||||
|
||||
```carbon
|
||||
interface A {
|
||||
let T:! type;
|
||||
let U:! type;
|
||||
let T: type;
|
||||
let U: type;
|
||||
}
|
||||
constraint C {
|
||||
extend A where .T = .U and .U = i32;
|
||||
@@ -141,7 +140,7 @@ var n: i32;
|
||||
// `T.(A.T)` is single-step equal to `T.(A.U)`, and
|
||||
// `T.(A.U)` is single-step equal to `i32`, but
|
||||
// `T.(A.T)` is not single-step equal to `i32`.
|
||||
fn F(T:! B) -> T.(A.T) { return n; }
|
||||
fn F(generic T: B) -> T.(A.T) { return n; }
|
||||
```
|
||||
|
||||
Because `=` constraints are effectively treated as `==` constraints in an
|
||||
@@ -158,7 +157,7 @@ For example:
|
||||
|
||||
```carbon
|
||||
// Compile-time identity function.
|
||||
fn Identity[T:! type](x:! T) -> T { return x; }
|
||||
fn Identity[T: type](generic x: T) -> T { return x; }
|
||||
|
||||
interface E {
|
||||
// ❌ Rewrite constraint specified directly.
|
||||
@@ -177,19 +176,19 @@ is rewritten to `.Self.T`, and `.Self` is ambiguous.
|
||||
|
||||
```carbon
|
||||
// ❌ Rewrite constraint specified directly in `impls`.
|
||||
fn F[T:! A where .U impls (A where .T = i32)]();
|
||||
fn F[T: A where .U impls (A where .T = i32)]();
|
||||
|
||||
// ❌ Reference to `.T` in same-type constraint is ambiguous:
|
||||
// does this mean the outer or inner `.Self.T`?
|
||||
fn G[T:! A where .U impls (A where .T == i32)]();
|
||||
fn G[T: A where .U impls (A where .T == i32)]();
|
||||
|
||||
// ✅ Not specified directly, but does not result
|
||||
// in any rewrites being performed. Return type
|
||||
// is not rewritten to `i32`.
|
||||
fn H[T:! type where .Self impls C]() -> T.(A.U);
|
||||
fn H[T: type where .Self impls C]() -> T.(A.U);
|
||||
|
||||
// ✅ Return type is rewritten to `i32`.
|
||||
fn I[T:! C]() -> T.(A.U);
|
||||
fn I[T: C]() -> T.(A.U);
|
||||
```
|
||||
|
||||
## Rewrite constraint resolution
|
||||
@@ -200,7 +199,7 @@ constraints that apply to `T`. This happens:
|
||||
|
||||
- When the constraint is used explicitly when declaring a symbolic binding,
|
||||
like a generic parameter or associated constant, of the form
|
||||
`T:! Constraint`.
|
||||
`T: Constraint`.
|
||||
- When declaring that a type implements a constraint with an `impl`
|
||||
declaration, such as `impl T as Constraint`. Note that this does not include
|
||||
`require` ... `impls` constraints appearing in `interface` or `constraint`
|
||||
@@ -224,22 +223,22 @@ abstract constraints into a set of constraints on `T`:
|
||||
|
||||
```carbon
|
||||
interface I {
|
||||
let X:! type;
|
||||
let Y:! type;
|
||||
let X: type;
|
||||
let Y: type;
|
||||
}
|
||||
// ✅ `.X` in `.Y = .X` is rewritten to `i32` when initially
|
||||
// forming the facet type.
|
||||
// Nothing to do during constraint resolution.
|
||||
fn InOrder[T:! I where .X = i32 and .Y = .X]() {}
|
||||
fn InOrder[T: I where .X = i32 and .Y = .X]() {}
|
||||
// ✅ Facet type has `.X = .Y` before constraint resolution.
|
||||
// That rewrite is resolved to `.X = i32`.
|
||||
fn Reordered[T:! I where .X = .Y and .Y = i32]() {}
|
||||
fn Reordered[T: I where .X = .Y and .Y = i32]() {}
|
||||
// ✅ Facet type has `.Y = .X` before constraint resolution.
|
||||
// That rewrite is resolved to `.Y = i32`.
|
||||
fn ReorderedIndirect[T:! (I where .X = i32) & (I where .Y = .X)]() {}
|
||||
fn ReorderedIndirect[T: (I where .X = i32) & (I where .Y = .X)]() {}
|
||||
// ❌ Constraint resolution fails because
|
||||
// no fixed point of rewrites exists.
|
||||
fn Cycle[T:! I where .X = .Y and .Y = .X]() {}
|
||||
fn Cycle[T: I where .X = .Y and .Y = .X]() {}
|
||||
```
|
||||
|
||||
To find a fixed point, we can perform rewrites on other rewrites, cycling
|
||||
@@ -258,7 +257,7 @@ condition:
|
||||
// `.X = .Y*`, then `.Y = .Y**`, then `.Z = .Y***`,
|
||||
// then `.X = .Y**`, then detect that the `.Y` rewrite
|
||||
// would apply to itself.
|
||||
fn IndirectCycle[T:! I where .X = .Y and .Y = .Z* and .Z = .Y*]();
|
||||
fn IndirectCycle[T: I where .X = .Y and .Y = .Z* and .Z = .Y*]();
|
||||
```
|
||||
|
||||
After constraint resolution, no references to rewritten associated constants
|
||||
@@ -269,11 +268,11 @@ The following examples each treat the two assignments of `.X` as being
|
||||
identical, though they are written differently:
|
||||
|
||||
```carbon
|
||||
fn Identical(T:! I where .X = () and .X = .Y and .Y = ()) {}
|
||||
fn Identical(generic T: I where .X = () and .X = .Y and .Y = ()) {}
|
||||
|
||||
fn IdenticalNoCycle(T:! I where .X = () and .X = .Y and .Y = .X) {}
|
||||
fn IdenticalNoCycle(generic T: I where .X = () and .X = .Y and .Y = .X) {}
|
||||
|
||||
fn IdenticalNested(T:! (I where .X = ()) where .X = .Y and .Y = ()) {}
|
||||
fn IdenticalNested(generic T: (I where .X = ()) where .X = .Y and .Y = ()) {}
|
||||
```
|
||||
|
||||
The rewrite constraints of the current facet type are all available, so both
|
||||
@@ -285,7 +284,7 @@ But the following does not have the rewrite of `.Y` available at the time of
|
||||
resolving the two rewrites of `.X`, so the rewrites are invalid:
|
||||
|
||||
```carbon
|
||||
fn NotIdentical(T:! (I where .X = () and .X = .Y) where .Y = ()) {}
|
||||
fn NotIdentical(generic T: (I where .X = () and .X = .Y) where .Y = ()) {}
|
||||
```
|
||||
|
||||
When combining two facet types together with `&`, the rewrite constraints are
|
||||
@@ -302,10 +301,10 @@ which constraint resolution would always fail. For example:
|
||||
package Broken;
|
||||
|
||||
interface I {
|
||||
let X:! type;
|
||||
let Y:! type;
|
||||
let X: type;
|
||||
let Y: type;
|
||||
}
|
||||
let Bad:! auto = (I where .X = .Y) & (I where .Y = .X);
|
||||
let generic Bad: auto = (I where .X = .Y) & (I where .Y = .X);
|
||||
// Bad is not used here.
|
||||
```
|
||||
|
||||
@@ -350,10 +349,10 @@ type.
|
||||
|
||||
```carbon
|
||||
interface C {
|
||||
let M:! i32;
|
||||
let U:! C;
|
||||
let M: i32;
|
||||
let U: C;
|
||||
}
|
||||
fn F[T:! C](x: T) {
|
||||
fn F[T: C](x: T) {
|
||||
// Value is C.M in all four of these
|
||||
let a: i32 = x.M;
|
||||
let b: i32 = T.M;
|
||||
@@ -378,7 +377,7 @@ declared type.
|
||||
interface SelfIface {
|
||||
fn Get(self) -> Self;
|
||||
}
|
||||
class UsesSelf(T:! type) {
|
||||
class UsesSelf(T: type) {
|
||||
// Equivalent to `fn Make() -> UsesSelf(T)*;`
|
||||
fn Make() -> Self*;
|
||||
impl as SelfIface;
|
||||
@@ -405,33 +404,33 @@ example in detail:
|
||||
|
||||
```carbon
|
||||
interface A {
|
||||
let T:! type;
|
||||
let T: type;
|
||||
}
|
||||
interface B {
|
||||
let U:! type;
|
||||
let U: type;
|
||||
// More explicitly, this is of type `A where .(A.T) = Self.(B.U)`
|
||||
let V:! A where .T = U;
|
||||
let V: A where .T = U;
|
||||
}
|
||||
// Type of W is B.
|
||||
fn F[W:! B](x: W) {
|
||||
fn F[W: B](x: W) {
|
||||
// The type of the expression `W` is `B`.
|
||||
// `W.V` finds `B.V` with type `A where .(A.T) = Self.(B.U)`.
|
||||
// We substitute `Self` = `W` giving the type of `u` as
|
||||
// `A where .(A.T) = W.(B.U)`.
|
||||
let u:! auto = W.V;
|
||||
let generic u: auto = W.V;
|
||||
// The type of `u` is `A where .(A.T) = W.(B.U)`.
|
||||
// Lookup for `u.T` resolves it to `u.(A.T)`.
|
||||
// So the result of the qualified member access is `W.(B.U)`,
|
||||
// and the type of `v` is the type of `W.(B.U)`, namely `type`.
|
||||
// No substitution is performed in this step.
|
||||
let v:! auto = u.T;
|
||||
let generic v: auto = u.T;
|
||||
}
|
||||
```
|
||||
|
||||
The more complex case of
|
||||
|
||||
```carbon
|
||||
fn F2[Z:! B where .U = i32](x: Z);
|
||||
fn F2[Z: B where .U = i32](x: Z);
|
||||
```
|
||||
|
||||
is discussed later.
|
||||
@@ -446,7 +445,7 @@ substitution of inferred parameter values into the type of a function when
|
||||
type-checking a function call:
|
||||
|
||||
```carbon
|
||||
fn F[T:! C](x: T) -> T;
|
||||
fn F[T: C](x: T) -> T;
|
||||
fn G(n: i32) -> i32 {
|
||||
// Deduces T = i32, which is substituted
|
||||
// into the type `fn (x: T) -> T` to produce
|
||||
@@ -465,19 +464,19 @@ expressions, and do not do it again:
|
||||
|
||||
```carbon
|
||||
interface IfaceHasX {
|
||||
let X:! type;
|
||||
let X: type;
|
||||
}
|
||||
class ClassHasX {
|
||||
class X {}
|
||||
}
|
||||
interface HasAssoc {
|
||||
let Assoc:! IfaceHasX;
|
||||
let Assoc: IfaceHasX;
|
||||
}
|
||||
|
||||
// Qualified name lookup finds `T.(HasAssoc.Assoc).(IfaceHasX.X)`.
|
||||
fn F(T:! HasAssoc) -> T.Assoc.X;
|
||||
fn F(generic T: HasAssoc) -> T.Assoc.X;
|
||||
|
||||
fn G(T:! HasAssoc where .Assoc = ClassHasX) {
|
||||
fn G(generic T: HasAssoc where .Assoc = ClassHasX) {
|
||||
// `T.Assoc` rewritten to `ClassHasX` by qualified name lookup.
|
||||
// Names `ClassHasX.X`.
|
||||
var a: T.Assoc.X = {};
|
||||
@@ -493,11 +492,11 @@ value. It’s important that we perform this resolution:
|
||||
|
||||
```carbon
|
||||
interface A {
|
||||
let T:! type;
|
||||
let T: type;
|
||||
}
|
||||
class K { fn Member(); }
|
||||
fn H[U:! A](x: U) -> U.T;
|
||||
fn J[V:! A where .T = K](y: V) {
|
||||
fn H[U: A](x: U) -> U.T;
|
||||
fn J[V: A where .T = K](y: V) {
|
||||
// We need the interface of `H(y)` to include
|
||||
// `K.Member` in order for this lookup to succeed.
|
||||
H(y).Member();
|
||||
@@ -536,28 +535,28 @@ Continuing an example from [qualified name lookup](#qualified-name-lookup):
|
||||
|
||||
```carbon
|
||||
interface A {
|
||||
let T:! type;
|
||||
let T: type;
|
||||
}
|
||||
interface B {
|
||||
let U:! type;
|
||||
let V:! A where .T = U;
|
||||
let U: type;
|
||||
let V: A where .T = U;
|
||||
}
|
||||
|
||||
// Type of the expression `Z` is `B where .(B.U) = i32`
|
||||
fn F2[Z:! B where .U = i32](x: Z) {
|
||||
fn F2[Z: B where .U = i32](x: Z) {
|
||||
// The type of the expression `Z` is `B where .U = i32`.
|
||||
// `Z.V` is looked up and finds the associated facet `(B.V)`.
|
||||
// The declared type is `A where .(A.T) = Self.U`.
|
||||
// We substitute `Self = Z` with rewrite `.U = i32`.
|
||||
// The resulting type is `A where .(A.T) = i32`.
|
||||
// So `u` is `Z.V` with type `A where .(A.T) = i32`.
|
||||
let u:! auto = Z.V;
|
||||
let generic u: auto = Z.V;
|
||||
// The type of `u` is `A where .(A.T) = i32`.
|
||||
// Lookup for `u.T` resolves it to `u.(A.T)`.
|
||||
// So the result of the qualified member access is `i32`,
|
||||
// and the type of `v` is the type of `i32`, namely `type`.
|
||||
// No substitution is performed in this step.
|
||||
let v:! auto = u.T;
|
||||
let generic v: auto = u.T;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -565,41 +564,41 @@ fn F2[Z:! B where .U = i32](x: Z) {
|
||||
|
||||
```carbon
|
||||
interface Container {
|
||||
let Element:! type;
|
||||
let Element: type;
|
||||
}
|
||||
interface SliceableContainer {
|
||||
extend Container;
|
||||
let Slice:! Container where .Element = Self.(Container.Element);
|
||||
let Slice: Container where .Element = Self.(Container.Element);
|
||||
}
|
||||
// ❌ Qualified name lookup rewrites this facet type to
|
||||
// `SliceableContainer where .(Container.Element) = .Self.(Container.Element)`.
|
||||
// Constraint resolution rejects this because this rewrite forms a cycle.
|
||||
fn Bad[T:! SliceableContainer where .Element = .Slice.Element](x: T.Element) {}
|
||||
fn Bad[T: SliceableContainer where .Element = .Slice.Element](x: T.Element) {}
|
||||
```
|
||||
|
||||
```carbon
|
||||
interface Helper {
|
||||
let D:! type;
|
||||
let D: type;
|
||||
}
|
||||
interface Example {
|
||||
let B:! type;
|
||||
let C:! Helper where .D = B;
|
||||
let B: type;
|
||||
let C: Helper where .D = B;
|
||||
}
|
||||
// ✅ `where .D = ...` by itself is fine.
|
||||
// `T.C.D` is rewritten to `T.B`.
|
||||
fn Allowed(T:! Example, x: T.C.D);
|
||||
fn Allowed(generic T: Example, x: T.C.D);
|
||||
// ❌ But combined with another rewrite, creates an infinite loop.
|
||||
// `.C.D` is rewritten to `.B`, resulting in `where .B = .B`,
|
||||
// which causes an error during constraint resolution.
|
||||
// Using `==` instead of `=` would make this constraint redundant,
|
||||
// rather than it being an error.
|
||||
fn Error(T:! Example where .B = .C.D, x: T.C.D);
|
||||
fn Error(generic T: Example where .B = .C.D, x: T.C.D);
|
||||
```
|
||||
|
||||
```carbon
|
||||
interface Allowed;
|
||||
interface AllowedBase {
|
||||
let A:! Allowed;
|
||||
let A: Allowed;
|
||||
}
|
||||
interface Allowed {
|
||||
extend AllowedBase where .A = .Self;
|
||||
@@ -608,18 +607,18 @@ interface Allowed {
|
||||
// In `((T.A).A).A`, the inner `T.A` is rewritten to `T`,
|
||||
// resulting in `((T).A).A`, which is then rewritten to
|
||||
// `(T).A`, which is then rewritten to `T`.
|
||||
fn F(T:! Allowed, x: ((T.A).A).A);
|
||||
fn F(generic T: Allowed, x: ((T.A).A).A);
|
||||
```
|
||||
|
||||
```carbon
|
||||
interface MoveYsRight;
|
||||
constraint ForwardDeclaredConstraint(X:! MoveYsRight);
|
||||
constraint ForwardDeclaredConstraint(X: MoveYsRight);
|
||||
interface MoveYsRight {
|
||||
let X:! MoveYsRight;
|
||||
// Means `Y:! MoveYsRight where .X = X.Y`
|
||||
let Y:! ForwardDeclaredConstraint(X);
|
||||
let X: MoveYsRight;
|
||||
// Means `Y: MoveYsRight where .X = X.Y`
|
||||
let Y: ForwardDeclaredConstraint(X);
|
||||
}
|
||||
constraint ForwardDeclaredConstraint(X:! MoveYsRight) {
|
||||
constraint ForwardDeclaredConstraint(X: MoveYsRight) {
|
||||
extend MoveYsRight where .X = X.Y;
|
||||
}
|
||||
// ✅ The final type of `x` is `T.X.Y.Y`. It is computed as follows:
|
||||
@@ -647,7 +646,7 @@ constraint ForwardDeclaredConstraint(X:! MoveYsRight) {
|
||||
// - Qualified name lookup finds `MoveYsRight.X`.
|
||||
// - The type of `T.Y.Y` says to rewrite that to `T.X.Y.Y`.
|
||||
// - The result is `T.X.Y.Y`, of type `MoveYsRight`.
|
||||
fn F4(T:! MoveYsRight, x: T.Y.Y.X);
|
||||
fn F4(generic T: MoveYsRight, x: T.Y.Y.X);
|
||||
```
|
||||
|
||||
### Termination
|
||||
|
||||
@@ -204,7 +204,7 @@ defining a witness table type like:
|
||||
class Vector {
|
||||
// `Self` is the representation type, which is only
|
||||
// known at compile time.
|
||||
var Self:! type;
|
||||
var Self: type;
|
||||
// `fnty` is placeholder syntax for a "function type",
|
||||
// so `Add` is a function that takes two `Self` parameters
|
||||
// and returns a value of type `Self`.
|
||||
@@ -230,9 +230,9 @@ var VectorForPoint_Inline: Vector = {
|
||||
};
|
||||
```
|
||||
|
||||
Since generic arguments (where the parameter is declared using `:!`) are passed
|
||||
at compile time, the actual value of `VectorForPoint_Inline` can be used to
|
||||
generate the code for functions using that impl.
|
||||
Since generic arguments are passed at compile time, the actual value of
|
||||
`VectorForPoint_Inline` can be used to generate the code for functions using
|
||||
that impl.
|
||||
|
||||
### Associated facets example
|
||||
|
||||
@@ -245,7 +245,7 @@ interface Iterator {
|
||||
}
|
||||
|
||||
interface Container {
|
||||
let IteratorType:! Iterator;
|
||||
let IteratorType: Iterator;
|
||||
fn Begin(ref self) -> IteratorType;
|
||||
}
|
||||
```
|
||||
@@ -254,15 +254,15 @@ could be represented by:
|
||||
|
||||
```
|
||||
class Iterator {
|
||||
var Self:! type;
|
||||
var Self: type;
|
||||
var Advance: fnty(this: Self*);
|
||||
...
|
||||
}
|
||||
class Container {
|
||||
var Self:! type;
|
||||
var Self: type;
|
||||
|
||||
// Witness that IteratorType implements Iterator.
|
||||
var IteratorType:! Iterator*;
|
||||
var IteratorType: Iterator*;
|
||||
|
||||
// Method
|
||||
var Begin: fnty (this: Self*) -> IteratorType->Self;
|
||||
|
||||
+380
-373
File diff suppressed because it is too large
Load Diff
@@ -656,7 +656,7 @@ when recursion creates an infinite collection of types, such as in
|
||||
or:
|
||||
|
||||
```carbon
|
||||
fn Sort[T:! Ordered](list: List(T)) -> List(T) {
|
||||
fn Sort[T: Ordered](list: List(T)) -> List(T) {
|
||||
if (list.size() == 1) return list;
|
||||
var chunks: List(List(T)) = FormChunks(list, sqrt(list.size()));
|
||||
chunks = chunks.ApplyToEach(Sort);
|
||||
|
||||
@@ -138,12 +138,12 @@ You might have one generic function that could sort any array with comparable
|
||||
elements:
|
||||
|
||||
```
|
||||
fn SortVector(T:! Comparable, a: Vector(T)*) { ... }
|
||||
fn SortVector(generic T: Comparable, a: Vector(T)*) { ... }
|
||||
```
|
||||
|
||||
The syntax above adds a `!` to indicate that the parameter named `T` is
|
||||
compile-time. By default compile-time parameters are _checked_, the `template`
|
||||
keyword may be added to make it a _template generic_.
|
||||
The syntax above uses the `generic` keyword to indicate that the parameter named
|
||||
`T` is a _checked generic_ parameter. The `template` keyword may be added instead to
|
||||
make it a _template generic_.
|
||||
|
||||
Given an `i32` vector `iv`, `SortVector(i32, &iv)` is equivalent to
|
||||
`SortInt32Vector(&iv)`. Similarly for a `String` vector `sv`,
|
||||
@@ -318,7 +318,7 @@ already included in the type of the second argument. To eliminate the argument
|
||||
at the call site, use a _deduced parameter_.
|
||||
|
||||
```
|
||||
fn SortVectorDeduced[T:! Comparable](a: Vector(T)*) { ... }
|
||||
fn SortVectorDeduced[T: Comparable](a: Vector(T)*) { ... }
|
||||
```
|
||||
|
||||
The `T` parameter is defined in square brackets before the explicit parameter
|
||||
@@ -341,7 +341,7 @@ call site.
|
||||
|
||||
```
|
||||
// ERROR: can't determine `U` from explicit parameters
|
||||
fn Illegal[T:! type, U:! type](x: T) -> U { ... }
|
||||
fn Illegal[T: type, U: type](x: T) -> U { ... }
|
||||
```
|
||||
|
||||
#### Facet parameters
|
||||
@@ -350,7 +350,7 @@ A function with a facet parameter can have the same function body as an
|
||||
unparameterized one.
|
||||
|
||||
```
|
||||
fn PrintIt[T:! Printable](p: T*) {
|
||||
fn PrintIt[T: Printable](p: T*) {
|
||||
p->Print();
|
||||
}
|
||||
|
||||
@@ -447,7 +447,7 @@ interface EndOfGame {
|
||||
fn Draw(ref self);
|
||||
}
|
||||
|
||||
fn F[T:! Renderable & EndOfGame](game_state: T*) -> (i32, i32) {
|
||||
fn F[T: Renderable & EndOfGame](game_state: T*) -> (i32, i32) {
|
||||
game_state->SetWinner(1);
|
||||
return game_state->Center();
|
||||
}
|
||||
@@ -457,7 +457,7 @@ Names with conflicts can be accessed using a
|
||||
[qualified member access expression](#accessing-members-of-interfaces).
|
||||
|
||||
```
|
||||
fn BothDraws[T:! Renderable & EndOfGame](game_state: T*) {
|
||||
fn BothDraws[T: Renderable & EndOfGame](game_state: T*) {
|
||||
game_state->(Renderable.Draw)();
|
||||
game_state->(GameState.Draw)();
|
||||
}
|
||||
@@ -480,7 +480,7 @@ constraint Combined {
|
||||
alias SetWinner = EndOfGame.SetWinner;
|
||||
}
|
||||
|
||||
fn CallItAll[T:! Combined](game_state: T*, int winner) {
|
||||
fn CallItAll[T: Combined](game_state: T*, int winner) {
|
||||
if (winner > 0) {
|
||||
game_state->SetWinner(winner);
|
||||
} else {
|
||||
@@ -514,7 +514,7 @@ class CDCover {
|
||||
it can be passed to this `PrintIt` function:
|
||||
|
||||
```
|
||||
fn PrintIt[T:! Printable](p: T*) {
|
||||
fn PrintIt[T: Printable](p: T*) {
|
||||
p->Print();
|
||||
}
|
||||
```
|
||||
@@ -567,7 +567,7 @@ convenient to use. Imagine a `Stack` interface. Different types implementing
|
||||
|
||||
```
|
||||
interface Stack {
|
||||
let ElementType:! Movable;
|
||||
let ElementType: Movable;
|
||||
fn Push(ref self, value: ElementType);
|
||||
fn Pop(ref self) -> ElementType;
|
||||
fn IsEmpty(ref self) -> bool;
|
||||
@@ -582,7 +582,7 @@ can deduce the `ElementType` from the stack type.
|
||||
```
|
||||
// ✅ This is allowed, since the type of the stack will determine
|
||||
// `ElementType`.
|
||||
fn PeekAtTopOfStack[StackType:! Stack](s: StackType*)
|
||||
fn PeekAtTopOfStack[StackType: Stack](s: StackType*)
|
||||
-> StackType.ElementType;
|
||||
```
|
||||
|
||||
@@ -595,7 +595,7 @@ those types to be different. An element in a hash map might have type
|
||||
`Equatable(Pair(String, i64))`.
|
||||
|
||||
```
|
||||
interface Equatable(T:! type) {
|
||||
interface Equatable(T: type) {
|
||||
fn IsEqual(self, compare_to: T) -> bool;
|
||||
}
|
||||
```
|
||||
@@ -609,14 +609,14 @@ general, unless some other parameter determines `T`.
|
||||
```
|
||||
// ✅ This is allowed, since the value of `T` is determined by the
|
||||
// `v` parameter.
|
||||
fn FindInVector[T:! type, U:! Equatable(T)](v: Vector(T), needle: U)
|
||||
fn FindInVector[T: type, U: Equatable(T)](v: Vector(T), needle: U)
|
||||
-> Optional(i32);
|
||||
|
||||
// ❌ This is forbidden. Since `U` could implement `Equatable`
|
||||
// multiple times, there is no way to determine the value for `T`.
|
||||
// Contrast with `PeekAtTopOfStack` in the associated constant
|
||||
// example.
|
||||
fn CompileError[T:! type, U:! Equatable(T)](x: U) -> T;
|
||||
fn CompileError[T: type, U: Equatable(T)](x: U) -> T;
|
||||
```
|
||||
|
||||
### Constraints
|
||||
@@ -624,13 +624,13 @@ fn CompileError[T:! type, U:! Equatable(T)](x: U) -> T;
|
||||
Facet types can be further constrained using a `where` clause:
|
||||
|
||||
```
|
||||
fn FindFirstPrime[T:! Container where .Element = i32]
|
||||
fn FindFirstPrime[T: Container where .Element = i32]
|
||||
(c: T, i: i32) -> Optional(i32) {
|
||||
// The elements of `c` have type `T.Element`, which is `i32`.
|
||||
...
|
||||
}
|
||||
|
||||
fn PrintContainer[T:! Container where .Element impls Printable](c: T) {
|
||||
fn PrintContainer[T: Container where .Element impls Printable](c: T) {
|
||||
// The type of the elements of `c` is not known, but we do know
|
||||
// that type satisfies the `Printable` interface.
|
||||
...
|
||||
@@ -645,7 +645,7 @@ Constraints are also used when implementing an interface to specify the values
|
||||
of associated constants.
|
||||
|
||||
```
|
||||
class Vector(T:! Movable) {
|
||||
class Vector(T: Movable) {
|
||||
extend impl as Stack where .ElementType = T { ... }
|
||||
}
|
||||
```
|
||||
|
||||
@@ -210,7 +210,7 @@ alone. For example, let's say we have some overloaded function called `F` that
|
||||
has two overloads:
|
||||
|
||||
```
|
||||
fn F[template T:! type](x: T*) -> T;
|
||||
fn F[template T: type](x: T*) -> T;
|
||||
fn F(x: Int) -> bool;
|
||||
```
|
||||
|
||||
@@ -309,9 +309,9 @@ The name being declared, which is the identifier to the left of the `:` is
|
||||
called a _binding_, or more specifically a _runtime binding_, _compile-time
|
||||
binding_, _symbolic binding_, or _template binding_. The expression to the right
|
||||
defining the type of the binding pattern is called the _binding type
|
||||
expression_, a kind of [type expression](#type-expression). For example, in
|
||||
`generic T: Hashable`, `T` is the binding (a symbolic binding in this case), and
|
||||
`Hashable` is the binding type expression.
|
||||
expression_, a kind of [type expression](#type-expression). For example, in a
|
||||
generic binding pattern `T: Hashable`, `T` is the binding (a symbolic binding in
|
||||
this case), and `Hashable` is the binding type expression.
|
||||
|
||||
## Types and `type`
|
||||
|
||||
@@ -365,9 +365,10 @@ cases, we are concerned with the type value after the implicit conversion.
|
||||
## Facet binding
|
||||
|
||||
We use the term _facet binding_ to refer to the name introduced by a
|
||||
[compile-time binding pattern](#bindings) where the declared type is a
|
||||
[facet type](#facet-type). In the binding pattern `generic T: Hashable`, `T` is
|
||||
a facet binding, and the value of `T` is a [facet](#facet).
|
||||
[compile-time binding pattern](#bindings) (indicated by context or keywords like
|
||||
`generic` or `template`) where the declared type is a [facet type](#facet-type).
|
||||
In a generic binding pattern `T: Hashable`, `T` is a facet binding, and the
|
||||
value of `T` is a [facet](#facet).
|
||||
|
||||
## Deduced parameter
|
||||
|
||||
@@ -755,14 +756,14 @@ associated constants.
|
||||
```
|
||||
// Stack using associated facets
|
||||
interface Stack {
|
||||
let ElementType:! type;
|
||||
let ElementType: type;
|
||||
fn Push(ref self, value: ElementType);
|
||||
fn Pop(ref self) -> ElementType;
|
||||
}
|
||||
|
||||
// Works on any type implementing `Stack`. Return type
|
||||
// is determined by the type's implementation of `Stack`.
|
||||
fn PeekAtTopOfStack[T:! Stack](s: T*) -> T.ElementType {
|
||||
fn PeekAtTopOfStack[T: Stack](s: T*) -> T.ElementType {
|
||||
let ret: T.ElementType = s->Pop();
|
||||
s->Push(ret);
|
||||
return ret;
|
||||
@@ -792,8 +793,8 @@ For example, we might have an interface that says how to perform addition with
|
||||
another type:
|
||||
|
||||
```
|
||||
interface AddWith(T:! type) {
|
||||
let ResultType:! type;
|
||||
interface AddWith(T: type) {
|
||||
let ResultType: type;
|
||||
fn Add(self, rhs: T) -> ResultType;
|
||||
}
|
||||
```
|
||||
@@ -812,12 +813,12 @@ to be some way to determine the type to add to:
|
||||
```
|
||||
// ✅ This is allowed, since the value of `T` is determined by the
|
||||
// `y` parameter.
|
||||
fn DoAdd[T:! type, U:! AddWith(T)](x: U, y: T) -> U.ResultType {
|
||||
fn DoAdd[T: type, U: AddWith(T)](x: U, y: T) -> U.ResultType {
|
||||
return x.Add(y);
|
||||
}
|
||||
|
||||
// ❌ This is forbidden, can't uniquely determine `T`.
|
||||
fn CompileError[T:! type, U:! AddWith(T)](x: U) -> T;
|
||||
fn CompileError[T: type, U: AddWith(T)](x: U) -> T;
|
||||
```
|
||||
|
||||
Once the interface parameters can be determined, that determines the values for
|
||||
|
||||
@@ -96,7 +96,6 @@ source file:
|
||||
| `,` | Separate tuple and struct elements |
|
||||
| `.` | Member access |
|
||||
| `:` | Name binding patterns |
|
||||
| `:!` | Compile-time binding patterns |
|
||||
| `;` | Statement separator |
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
@@ -69,6 +69,7 @@ The following words are interpreted as keywords:
|
||||
- `for`
|
||||
- `forall`
|
||||
- `friend`
|
||||
- `generic`
|
||||
- `if`
|
||||
- `impl`
|
||||
- `impls`
|
||||
@@ -92,6 +93,7 @@ The following words are interpreted as keywords:
|
||||
- `require`
|
||||
- `return`
|
||||
- `returned`
|
||||
- `runtime`
|
||||
- `Self`
|
||||
- `self`
|
||||
- `template`
|
||||
|
||||
@@ -295,7 +295,7 @@ the type of the scrutinee and deduced values are substituted back into the type
|
||||
before pattern matching is performed.
|
||||
|
||||
```carbon
|
||||
fn G[T:! Type](p: T*);
|
||||
fn G[T: Type](p: T*);
|
||||
class X { impl as ImplicitAs(i32*); }
|
||||
// ✅ Deduces `T = i32` then implicitly and
|
||||
// trivially converts `p` to `i32*`.
|
||||
@@ -493,7 +493,7 @@ alternative, and the arguments of the alternative match the given tuple pattern
|
||||
(if any).
|
||||
|
||||
```carbon
|
||||
choice Optional(T:! Type) {
|
||||
choice Optional(T: Type) {
|
||||
None,
|
||||
Some(T)
|
||||
}
|
||||
@@ -532,7 +532,7 @@ meaningful due to a type error are instead treated as not matching. This
|
||||
includes cases where an `==` fails because of a missing `EqWith` implementation.
|
||||
|
||||
```carbon
|
||||
fn TypeName[template T:! Type](x: T) -> String {
|
||||
fn TypeName[template T: Type](x: T) -> String {
|
||||
match (x) {
|
||||
// ✅ OK, the type of `x` is a template parameter.
|
||||
case _: i32 => { return "int"; }
|
||||
@@ -547,7 +547,7 @@ Cases where the match is invalid for reasons not involving the template
|
||||
parameter are rejected when type-checking the template:
|
||||
|
||||
```carbon
|
||||
fn MeaninglessMatch[template T:! Type](x: T*) {
|
||||
fn MeaninglessMatch[template T: Type](x: T*) {
|
||||
match (*x) {
|
||||
// ✅ OK, `T` could be a tuple.
|
||||
case (_: auto, _: auto) => {}
|
||||
@@ -626,7 +626,7 @@ We will diagnose the following situations:
|
||||
example:
|
||||
|
||||
```carbon
|
||||
choice Optional(T:! Type) {
|
||||
choice Optional(T: Type) {
|
||||
None,
|
||||
Some(T)
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ than documentation), and `None`, which is empty. Choice types can also be
|
||||
parameterized, [like class types](generics/details.md#parameterized-types):
|
||||
|
||||
```carbon
|
||||
choice Optional(T:! type) {
|
||||
choice Optional(T: type) {
|
||||
Some(value: T),
|
||||
None
|
||||
}
|
||||
@@ -107,11 +107,11 @@ It does so by implementing the `Match` interface, which is defined as follows:
|
||||
```carbon
|
||||
interface Match {
|
||||
interface BaseContinuation {
|
||||
let ReturnType:! type;
|
||||
let ReturnType: type;
|
||||
}
|
||||
|
||||
let template Continuation:! type;
|
||||
fn Op[C:! Continuation](self, continuation: C*)
|
||||
let template Continuation: type;
|
||||
fn Op[C: Continuation](self, continuation: C*)
|
||||
-> C.(BaseContinuation.ReturnType);
|
||||
}
|
||||
```
|
||||
@@ -135,10 +135,10 @@ require that `Match.Op` invoke the continuation as a tail call.
|
||||
For example, here's how `Optional` can be defined as a class:
|
||||
|
||||
```carbon
|
||||
class Optional(T:! type) {
|
||||
class Optional(T: type) {
|
||||
// Factory functions
|
||||
fn Some(value: T) -> Self;
|
||||
let None:! Self;
|
||||
let None: Self;
|
||||
|
||||
private var has_value: bool;
|
||||
private var value: T;
|
||||
@@ -150,7 +150,7 @@ class Optional(T:! type) {
|
||||
fn None(ref self) -> ReturnType;
|
||||
}
|
||||
|
||||
fn Op[C:! Continuation](self, continuation: C*) -> C.ReturnType {
|
||||
fn Op[C: Continuation](self, continuation: C*) -> C.ReturnType {
|
||||
if (self.has_value) {
|
||||
return continuation->Some(self.value);
|
||||
} else {
|
||||
|
||||
@@ -44,7 +44,7 @@ are subject to full instantiation -- other parameters will be type checked and
|
||||
bound early to the extent possible. For example:
|
||||
|
||||
```
|
||||
class Stack(template T:! type) {
|
||||
class Stack(template T: type) {
|
||||
var storage: buf(T);
|
||||
|
||||
fn Push(ref self, value: T);
|
||||
@@ -67,7 +67,7 @@ arguments. The runtime call then passes the remaining arguments to the resulting
|
||||
complete definition.
|
||||
|
||||
```
|
||||
fn Convert[template T:! type](source: T, template U:! type) -> U {
|
||||
fn Convert[template T: type](source: T, template U: type) -> U {
|
||||
var converted: U = source;
|
||||
return converted;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ fn Sum(x: i32, y: i32) -> i32 {
|
||||
A parenthesized template constant expression can also be used to index a tuple:
|
||||
|
||||
```
|
||||
fn Choose(template N:! i32) -> i32 {
|
||||
fn Choose(template N: i32) -> i32 {
|
||||
return (1, 2, 3).(N % 3);
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1217,7 +1217,7 @@ The interface might look like:
|
||||
|
||||
```carbon
|
||||
interface Pointer {
|
||||
let ValueT:! Type;
|
||||
let ValueT: Type;
|
||||
fn Dereference(self) -> ValueT*;
|
||||
}
|
||||
```
|
||||
@@ -1226,12 +1226,12 @@ Here is an example using a hypothetical `TaggedPtr` that carries some extra
|
||||
integer tag next to the pointer it emulates:
|
||||
|
||||
```carbon
|
||||
class TaggedPtr(T:! Type) {
|
||||
class TaggedPtr(T: Type) {
|
||||
var tag: Int32;
|
||||
var ptr: T*;
|
||||
}
|
||||
external impl [T:! Type] TaggedPtr(T) as Pointer {
|
||||
let ValueT:! T;
|
||||
external impl [T: Type] TaggedPtr(T) as Pointer {
|
||||
let ValueT: T;
|
||||
fn Dereference(self) -> T* { return self.ptr; }
|
||||
}
|
||||
|
||||
@@ -1248,8 +1248,8 @@ that formed by `var` declarations. This interface is implemented for normal
|
||||
pointers as a no-op:
|
||||
|
||||
```carbon
|
||||
impl [T:! Type] T* as Pointer {
|
||||
let ValueT:! Type = T;
|
||||
impl [T: Type] T* as Pointer {
|
||||
let ValueT: Type = T;
|
||||
fn Dereference(self) -> T* { return self; }
|
||||
}
|
||||
```
|
||||
@@ -1406,7 +1406,7 @@ will require that the type containing that specifier satisfies the constraint
|
||||
|
||||
```carbon
|
||||
interface ReferenceImplicitAs {
|
||||
let T:! type;
|
||||
let T: type;
|
||||
fn Convert(ref self: const Self) -> T;
|
||||
}
|
||||
```
|
||||
|
||||
+40
-40
@@ -67,7 +67,7 @@ This example illustrates many of the key concepts:
|
||||
// Takes an arbitrary number of vectors with arbitrary element types, and
|
||||
// returns a vector of tuples where the i'th element of the vector is
|
||||
// a tuple of the i'th elements of the input vectors.
|
||||
fn Zip[... each ElementType:! type]
|
||||
fn Zip[... each ElementType: type]
|
||||
(... each vector: Vector(each ElementType))
|
||||
-> Vector((... each ElementType)) {
|
||||
... var each iter: auto = each vector.Begin();
|
||||
@@ -91,7 +91,7 @@ the number of values in the sequence.
|
||||
An _each-name_ consists of the keyword `each` followed by the name of a pack,
|
||||
and can only occur inside a pack expansion. On the Nth iteration of the pack
|
||||
expansion, an each-name refers to the Nth element of the named pack. As a
|
||||
result, a binding pattern with an each-name, such as `each ElementType:! type`,
|
||||
result, a binding pattern with an each-name, such as `each ElementType: type`,
|
||||
acts as a declaration of all the elements of the named pack, and thereby
|
||||
implicitly acts as a declaration of the pack itself.
|
||||
|
||||
@@ -190,7 +190,7 @@ fragment:
|
||||
|
||||
```
|
||||
var result: bool = true;
|
||||
for (let i:! i32 in (0, 1, 2)) {
|
||||
for (let generic i: i32 in (0, 1, 2)) {
|
||||
result = result && F(x[:i:], y[:i:]);
|
||||
if (result == false) { break; }
|
||||
}
|
||||
@@ -248,7 +248,7 @@ fn SumInts(... each param: i64) -> i64 {
|
||||
|
||||
```carbon
|
||||
// Concatenates its arguments, which are all convertible to String
|
||||
fn StrCat[... each T:! ConvertibleToString](... each param: each T) -> String {
|
||||
fn StrCat[... each T: ConvertibleToString](... each param: each T) -> String {
|
||||
var len: i64 = 0;
|
||||
... len += each param.Length();
|
||||
var result: String = "";
|
||||
@@ -260,7 +260,7 @@ fn StrCat[... each T:! ConvertibleToString](... each param: each T) -> String {
|
||||
|
||||
```carbon
|
||||
// Returns the minimum of its arguments, which must all have the same type T.
|
||||
fn Min[T:! Comparable & Value](first: T, ... each next: T) -> T {
|
||||
fn Min[T: Comparable & Value](first: T, ... each next: T) -> T {
|
||||
var result: T = first;
|
||||
... if (each next < result) {
|
||||
result = each next;
|
||||
@@ -271,7 +271,7 @@ fn Min[T:! Comparable & Value](first: T, ... each next: T) -> T {
|
||||
|
||||
```carbon
|
||||
// Invokes f, with the tuple `args` as its arguments.
|
||||
fn Apply[... each T:! type, F:! Call(... each T)]
|
||||
fn Apply[... each T: type, F: Call(... each T)]
|
||||
(f: F, args: (... each T)) -> auto {
|
||||
return f(...expand args);
|
||||
}
|
||||
@@ -365,7 +365,7 @@ tuple literal is a tuple literal of the types of its segments. For example,
|
||||
suppose we are trying to find the type of `z` in this code:
|
||||
|
||||
```carbon
|
||||
fn F[... each T:! type]((... each x: Optional(each T)), (... each y: i32)) {
|
||||
fn F[... each T: type]((... each x: Optional(each T)), (... each y: i32)) {
|
||||
let z: auto = (0 as f32, ... each x, ... each y);
|
||||
}
|
||||
```
|
||||
@@ -396,7 +396,7 @@ type of `z` is `(f32, ... Optional(each T), ... «i32; ‖each y‖»)`.
|
||||
Now, consider a modified version of that example:
|
||||
|
||||
```carbon
|
||||
fn F[... each T:! type]((... each x: Optional(each T)), (... each y: i32)) {
|
||||
fn F[... each T: type]((... each x: Optional(each T)), (... each y: i32)) {
|
||||
let (... each z: auto) = (0 as f32, ... each x, ... each y);
|
||||
}
|
||||
```
|
||||
@@ -499,8 +499,8 @@ an each-name that is not a parameter of the enclosing
|
||||
pattern can have at most one segment with deduced arity. For example:
|
||||
|
||||
```carbon
|
||||
class C(... each T:! type) {
|
||||
fn F[... each U:! type](... each t: each T, ... each u: each U);
|
||||
class C(... each T: type) {
|
||||
fn F[... each U: type](... each t: each T, ... each u: each U);
|
||||
}
|
||||
```
|
||||
|
||||
@@ -514,14 +514,14 @@ possible, in order to simplify the subsequent pattern matching. For example,
|
||||
consider the following function declaration:
|
||||
|
||||
```carbon
|
||||
fn Min[T:! type](first: T, ... each next: T) -> T;
|
||||
fn Min[T: type](first: T, ... each next: T) -> T;
|
||||
```
|
||||
|
||||
During typechecking, we rewrite that function signature so that it only has one
|
||||
parameter:
|
||||
|
||||
```carbon
|
||||
fn Min[T:! type](... each args: «T; ‖each next‖+1») -> T;
|
||||
fn Min[T: type](... each args: «T; ‖each next‖+1») -> T;
|
||||
```
|
||||
|
||||
(We represent the arity as `‖each next‖+1` to capture the fact that `each args`
|
||||
@@ -531,7 +531,7 @@ When the pattern is heterogeneous, the merging process may be more complex. For
|
||||
example:
|
||||
|
||||
```carbon
|
||||
fn ZipAtLeastOne[First:! type, ... each Next:! type]
|
||||
fn ZipAtLeastOne[First: type, ... each Next: type]
|
||||
(first: Vector(First), ... each next: Vector(each Next))
|
||||
-> Vector((First, ... each Next));
|
||||
```
|
||||
@@ -539,7 +539,7 @@ fn ZipAtLeastOne[First:! type, ... each Next:! type]
|
||||
During typechecking, we transform that function signature to the following form:
|
||||
|
||||
```carbon
|
||||
fn ZipAtLeastOne[... ⟬First, each Next⟭:! «type; ‖each next‖+1»]
|
||||
fn ZipAtLeastOne[... ⟬First, each Next⟭: «type; ‖each next‖+1»]
|
||||
(... each __args: Vector(⟬First, each Next⟭))
|
||||
-> Vector((... ⟬First, each Next⟭));
|
||||
```
|
||||
@@ -549,7 +549,7 @@ with an invented name `each __Args`, so that the function has only one
|
||||
parameter:
|
||||
|
||||
```carbon
|
||||
fn ZipAtLeastOne[... each __Args:! «type; ‖each next‖+1»]
|
||||
fn ZipAtLeastOne[... each __Args: «type; ‖each next‖+1»]
|
||||
(... each __args: Vector(each __Args))
|
||||
-> Vector((... each __Args));
|
||||
```
|
||||
@@ -567,7 +567,7 @@ following conditions hold:
|
||||
declaration of `X`:
|
||||
|
||||
```carbon
|
||||
fn F[... ⟬X, each Y⟭:! «type; ‖each next‖+1»]
|
||||
fn F[... ⟬X, each Y⟭: «type; ‖each next‖+1»]
|
||||
(... each __args: each ⟬X, each Y⟭) -> X;
|
||||
```
|
||||
|
||||
@@ -577,7 +577,7 @@ following conditions hold:
|
||||
parameter list also contains the pack literal `⟬I, each type⟭`:
|
||||
|
||||
```carbon
|
||||
fn F[... ⟬X, each Y⟭:! ⟬I, each type⟭](... each __args: each ⟬X, each Y⟭);
|
||||
fn F[... ⟬X, each Y⟭: ⟬I, each type⟭](... each __args: each ⟬X, each Y⟭);
|
||||
```
|
||||
|
||||
Notice that as a corollary of this rule, all the names in the name pack must
|
||||
@@ -595,7 +595,7 @@ same arity. For example, consider this call to `ZipAtLeastOne` (as defined in
|
||||
the previous section):
|
||||
|
||||
```carbon
|
||||
fn F[... each T:! type](... each t: Vector(each T), u: Vector(i32)) {
|
||||
fn F[... each T: type](... each t: Vector(each T), u: Vector(i32)) {
|
||||
ZipAtLeastOne(... each t, u);
|
||||
}
|
||||
```
|
||||
@@ -654,7 +654,7 @@ expansion.
|
||||
In this formalism, deduced arities are explicit rather than implicit, so Carbon
|
||||
code must be desugared into this formalism as follows:
|
||||
|
||||
For each pack expansion pattern, we introduce a binding pattern `__N:! Arity` as
|
||||
For each pack expansion pattern, we introduce a binding pattern `__N: Arity` as
|
||||
a deduced parameter of the enclosing full pattern, where `__N` is a name chosen
|
||||
to avoid collisions. Then, for each binding pattern of the form `each X: T`
|
||||
within that expansion, if `T` does not contain an each-name, the binding pattern
|
||||
@@ -695,7 +695,7 @@ The type of an expression or pattern can be computed as follows:
|
||||
|
||||
- The type of `each x: auto` is `each __X`, a newly-invented deduced parameter
|
||||
of the enclosing full pattern, which behaves as if it was declared as
|
||||
`... each __X:! type`.
|
||||
`... each __X: type`.
|
||||
- The type of an each-name expression is the type expression of the binding
|
||||
pattern that declared it.
|
||||
- The type of an arity coercion `«E; S»` is `«T; S»`, where `T` is the type of
|
||||
@@ -808,15 +808,15 @@ _Shape equality:_ Let `(S1s)`, `(S2s)`, `(S3s)`, and `(S4s)` be shapes.
|
||||
|
||||
A full pattern is in _normal form_ if it contains no pack literals, and every
|
||||
arity coercion is fully expanded. For example,
|
||||
`[__N:! Arity](... each x: Vector(«i32; __N»))` is not in normal form, but
|
||||
`[__N:! Arity](... each x: «Vector(i32); __N»)` is. Note that all user-written
|
||||
`[__N: Arity](... each x: Vector(«i32; __N»))` is not in normal form, but
|
||||
`[__N: Arity](... each x: «Vector(i32); __N»)` is. Note that all user-written
|
||||
full patterns are in normal form. Note also that by construction, this means
|
||||
that the type of the body of every pack expansion has a single scalar component.
|
||||
The _canonical form_ of a full pattern is the unique normal form (if any) that
|
||||
is "maximally merged", meaning that every tuple pattern and tuple literal has
|
||||
the smallest number of segments. For example, the canonical form of
|
||||
`[__N:! Arity](... each x: «i32; __N», y: i32)` is
|
||||
`[__N:! Arity](... each __args: «i32; __N+1»)`.
|
||||
`[__N: Arity](... each x: «i32; __N», y: i32)` is
|
||||
`[__N: Arity](... each __args: «i32; __N+1»)`.
|
||||
|
||||
> **TODO:** Specify algorithm for converting a full pattern to canonical form,
|
||||
> or establishing that there is no such form. See next section for a start.
|
||||
@@ -848,7 +848,7 @@ parameter type.
|
||||
For example, consider the following function:
|
||||
|
||||
```carbon
|
||||
fn F[First:! type, Second:! type, ... each Next:! type]
|
||||
fn F[First: type, Second: type, ... each Next: type]
|
||||
(first: Vector(First), second: Vector(Second),
|
||||
... each next: Vector(each Next)) -> (First, Second, ... each Next);
|
||||
```
|
||||
@@ -856,7 +856,7 @@ fn F[First:! type, Second:! type, ... each Next:! type]
|
||||
First, we desugar the implicit arity:
|
||||
|
||||
```carbon
|
||||
fn F[__N:! Arity, First:! type, Second:! type, ... each Next:! «type; __N»]
|
||||
fn F[__N: Arity, First: type, Second: type, ... each Next: «type; __N»]
|
||||
(first: Vector(First), second: Vector(Second),
|
||||
... each next: Vector(each Next)) -> (First, Second, ... each Next);
|
||||
```
|
||||
@@ -867,32 +867,32 @@ reductions):
|
||||
|
||||
```carbon
|
||||
// Singular pack removal (in reverse)
|
||||
fn F[__N:! Arity, First:! type, Second:! type, ... ⟬each Next:! «type; __N»⟭]
|
||||
fn F[__N: Arity, First: type, Second: type, ... ⟬each Next: «type; __N»⟭]
|
||||
(first: Vector(First), second: Vector(Second),
|
||||
... each next: Vector(⟬each Next⟭)) -> (First, Second, ... ⟬each Next⟭);
|
||||
// Pack expanding
|
||||
fn F[__N:! Arity, First:! type, Second:! type, ... ⟬each Next:! «type; __N»⟭]
|
||||
fn F[__N: Arity, First: type, Second: type, ... ⟬each Next: «type; __N»⟭]
|
||||
(first: Vector(First), second: Vector(Second),
|
||||
... each next: ⟬Vector(each Next)⟭) -> (First, Second, ... ⟬each Next⟭);
|
||||
// Pack expanding
|
||||
fn F[__N:! Arity, First:! type, Second:! type, ... ⟬each Next:! «type; __N»⟭]
|
||||
fn F[__N: Arity, First: type, Second: type, ... ⟬each Next: «type; __N»⟭]
|
||||
(first: Vector(First), second: Vector(Second),
|
||||
... ⟬each next: Vector(each Next)⟭) -> (First, Second, ... ⟬each Next⟭);
|
||||
// Pack expansion splitting (in reverse)
|
||||
fn F[__N:! Arity, First:! type, ... ⟬Second:! type, each Next:! «type; __N»⟭]
|
||||
fn F[__N: Arity, First: type, ... ⟬Second: type, each Next: «type; __N»⟭]
|
||||
(first: Vector(First), ... ⟬second: Vector(Second),
|
||||
each next: Vector(each Next)⟭)
|
||||
-> (First, ... ⟬Second, each Next⟭);
|
||||
// Pack expanding (in reverse)
|
||||
fn F[__N:! Arity, First:! type, ... ⟬Second, each Next⟭:! «type; __N+1»]
|
||||
fn F[__N: Arity, First: type, ... ⟬Second, each Next⟭: «type; __N+1»]
|
||||
(first: Vector(First), ... ⟬second, each next⟭: ⟬Vector(Second), Vector(each Next)⟭)
|
||||
-> (First, ... ⟬Second, each Next⟭);
|
||||
// Pack expanding (in reverse)
|
||||
fn F[__N:! Arity, First:! type, ... ⟬Second, each Next⟭:! «type; __N+1»]
|
||||
fn F[__N: Arity, First: type, ... ⟬Second, each Next⟭: «type; __N+1»]
|
||||
(first: Vector(First), ... ⟬second, each next⟭: Vector(⟬Second, each Next⟭))
|
||||
-> (First, ... ⟬Second, each Next⟭);
|
||||
// Pack renaming
|
||||
fn F[__N:! Arity, First:! type, ... each __A:! «type; __N+1»]
|
||||
fn F[__N: Arity, First: type, ... each __A: «type; __N+1»]
|
||||
(first: Vector(First), ... each __a: Vector(each __A))
|
||||
-> (First, ... each __A);
|
||||
```
|
||||
@@ -901,31 +901,31 @@ This brings us back to a normal form, while reducing the number of tuple
|
||||
segments. We can now repeat that process to merge the remaining parameter type:
|
||||
|
||||
```carbon
|
||||
fn F[__N:! Arity, First:! type, ... ⟬each __A:! «type; __N+1»⟭]
|
||||
fn F[__N: Arity, First: type, ... ⟬each __A: «type; __N+1»⟭]
|
||||
(first: Vector(First), ... each __a: Vector(⟬each __A⟭))
|
||||
-> (First, ... ⟬each __A⟭);
|
||||
// Pack expanding
|
||||
fn F[__N:! Arity, First:! type, ... ⟬each __A:! «type; __N+1»⟭]
|
||||
fn F[__N: Arity, First: type, ... ⟬each __A: «type; __N+1»⟭]
|
||||
(first: Vector(First), ... each __a: ⟬Vector(each __A)⟭)
|
||||
-> (First, ... ⟬each __A⟭);
|
||||
// Pack expanding
|
||||
fn F[__N:! Arity, First:! type, ... ⟬each __A:! «type; __N+1»⟭]
|
||||
fn F[__N: Arity, First: type, ... ⟬each __A: «type; __N+1»⟭]
|
||||
(first: Vector(First), ... ⟬each __a: Vector(each __A)⟭)
|
||||
-> (First, ... ⟬each __A⟭);
|
||||
// Pack expansion splitting (in reverse)
|
||||
fn F[__N:! Arity, ... ⟬First:! type, each __A:! «type; __N+1»⟭]
|
||||
fn F[__N: Arity, ... ⟬First: type, each __A: «type; __N+1»⟭]
|
||||
(... ⟬first: Vector(First), each __a: Vector(each __A)⟭)
|
||||
-> (... ⟬First, each __A⟭);
|
||||
// Pack expanding (in reverse)
|
||||
fn F[__N:! Arity, ... ⟬First, each __A⟭:! «type; __N+2»⟭]
|
||||
fn F[__N: Arity, ... ⟬First, each __A⟭: «type; __N+2»⟭]
|
||||
(... ⟬first, each __a⟭: ⟬Vector(First), Vector(each __A)⟭)
|
||||
-> (... ⟬First, each __A⟭);
|
||||
// Pack expanding (in reverse)
|
||||
fn F[__N:! Arity, ... ⟬First, each __A⟭:! «type; __N+2»⟭]
|
||||
fn F[__N: Arity, ... ⟬First, each __A⟭: «type; __N+2»⟭]
|
||||
(... ⟬first, each __a⟭: Vector(⟬First, each __A⟭))
|
||||
-> (... ⟬First, each __A⟭);
|
||||
// Pack renaming
|
||||
fn F[__N:! Arity, ... __B:! «type; __N+2»⟭]
|
||||
fn F[__N: Arity, ... __B: «type; __N+2»⟭]
|
||||
(... __b: Vector(__B))
|
||||
-> (... __B);
|
||||
```
|
||||
|
||||
@@ -23,7 +23,7 @@ A sample of quicksort in Carbon.
|
||||
```cpp
|
||||
package Sorting;
|
||||
|
||||
fn Partition[T:! Comparable & Movable](s: slice(T))
|
||||
fn Partition[T: Comparable & Movable](s: slice(T))
|
||||
-> i64 {
|
||||
var i: i64 = -1;
|
||||
|
||||
@@ -36,7 +36,7 @@ fn Partition[T:! Comparable & Movable](s: slice(T))
|
||||
return i;
|
||||
}
|
||||
|
||||
fn QuickSort[T:! Comparable & Movable](s: slice(T)) {
|
||||
fn QuickSort[T: Comparable & Movable](s: slice(T)) {
|
||||
if (s.Size() <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user