|
|
@@ -70,19 +70,19 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
- [Type compatible with another type](#type-compatible-with-another-type)
|
|
|
- [Same implementation restriction](#same-implementation-restriction)
|
|
|
- [Example: Multiple implementations of the same interface](#example-multiple-implementations-of-the-same-interface)
|
|
|
- - [Example: Creating an impl out of other impls](#example-creating-an-impl-out-of-other-impls)
|
|
|
+ - [Example: Creating an impl out of other implementations](#example-creating-an-impl-out-of-other-implementations)
|
|
|
- [Sized types and type-of-types](#sized-types-and-type-of-types)
|
|
|
- [Implementation model](#implementation-model-2)
|
|
|
- [`TypeId`](#typeid)
|
|
|
- [Destructor constraints](#destructor-constraints)
|
|
|
- [Generic `let`](#generic-let)
|
|
|
-- [Parameterized impls](#parameterized-impls)
|
|
|
+- [Parameterized impl declarations](#parameterized-impl-declarations)
|
|
|
- [Impl for a parameterized type](#impl-for-a-parameterized-type)
|
|
|
- [Conditional conformance](#conditional-conformance)
|
|
|
- [Conditional methods](#conditional-methods)
|
|
|
- - [Blanket impls](#blanket-impls)
|
|
|
- - [Difference between blanket impls and named constraints](#difference-between-blanket-impls-and-named-constraints)
|
|
|
- - [Wildcard impls](#wildcard-impls)
|
|
|
+ - [Blanket impl declarations](#blanket-impl-declarations)
|
|
|
+ - [Difference between a blanket impl and a named constraint](#difference-between-a-blanket-impl-and-a-named-constraint)
|
|
|
+ - [Wildcard impl declarations](#wildcard-impl-declarations)
|
|
|
- [Combinations](#combinations)
|
|
|
- [Lookup resolution and specialization](#lookup-resolution-and-specialization)
|
|
|
- [Type structure of an impl declaration](#type-structure-of-an-impl-declaration)
|
|
|
@@ -91,8 +91,8 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
- [Prioritization rule](#prioritization-rule)
|
|
|
- [Acyclic rule](#acyclic-rule)
|
|
|
- [Termination rule](#termination-rule)
|
|
|
- - [`final` impls](#final-impls)
|
|
|
- - [Libraries that can contain `final` impls](#libraries-that-can-contain-final-impls)
|
|
|
+ - [`final` impl declarations](#final-impl-declarations)
|
|
|
+ - [Libraries that can contain a `final` impl](#libraries-that-can-contain-a-final-impl)
|
|
|
- [Comparison to Rust](#comparison-to-rust)
|
|
|
- [Forward declarations and cyclic references](#forward-declarations-and-cyclic-references)
|
|
|
- [Declaring interfaces and named constraints](#declaring-interfaces-and-named-constraints)
|
|
|
@@ -108,7 +108,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
- [Requirements with `where` constraints](#requirements-with-where-constraints)
|
|
|
- [Observing a type implements an interface](#observing-a-type-implements-an-interface)
|
|
|
- [Observing interface requirements](#observing-interface-requirements)
|
|
|
- - [Observing blanket impls](#observing-blanket-impls)
|
|
|
+ - [Observing blanket impl declarations](#observing-blanket-impl-declarations)
|
|
|
- [Operator overloading](#operator-overloading)
|
|
|
- [Binary operators](#binary-operators)
|
|
|
- [`like` operator for implicit conversions](#like-operator-for-implicit-conversions)
|
|
|
@@ -121,7 +121,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
- [Abstract return types](#abstract-return-types)
|
|
|
- [Evolution](#evolution)
|
|
|
- [Testing](#testing)
|
|
|
- - [Impls with state](#impls-with-state)
|
|
|
+ - [Impl with state](#impl-with-state)
|
|
|
- [Generic associated types and higher-ranked types](#generic-associated-types-and-higher-ranked-types)
|
|
|
- [Generic associated types](#generic-associated-types)
|
|
|
- [Higher-ranked types](#higher-ranked-types)
|
|
|
@@ -245,14 +245,14 @@ definitions for all the functions (and other members) declared in the interface.
|
|
|
|
|
|
Carbon interfaces are ["nominal"](terminology.md#nominal-interfaces), which
|
|
|
means that types explicitly describe how they implement interfaces. An
|
|
|
-["impl"](terminology.md#impls-implementations-of-interfaces) defines how one
|
|
|
+["impl"](terminology.md#impl-implementation-of-an-interface) defines how one
|
|
|
interface is implemented for a type. Every associated entity is given a
|
|
|
definition. Different types satisfying `Vector` can have different definitions
|
|
|
for `Add` and `Scale`, so we say their definitions are _associated_ with what
|
|
|
type is implementing `Vector`. The `impl` defines what is associated with the
|
|
|
type for that interface.
|
|
|
|
|
|
-Impls may be defined inline inside the type definition:
|
|
|
+An impl may be defined inline inside the type definition:
|
|
|
|
|
|
```
|
|
|
class Point {
|
|
|
@@ -421,8 +421,8 @@ interface (`Vector` in this case) in addition to the library that defines the
|
|
|
type (`Point3` here). This (at least partially) addresses
|
|
|
[the expression problem](https://eli.thegreenplace.net/2016/the-expression-problem-and-its-solutions).
|
|
|
|
|
|
-Carbon requires `impl`s defined in a different library to be `external` so that
|
|
|
-the API of `Point3` doesn't change based on what is imported. It would be
|
|
|
+Carbon requires `impl` declarations in a different library to be `external` so
|
|
|
+that the API of `Point3` doesn't change based on what is imported. It would be
|
|
|
particularly bad if two different libraries implemented interfaces with
|
|
|
conflicting names that both affected the API of a single type. As a consequence
|
|
|
of this restriction, you can find all the names of direct members (those
|
|
|
@@ -731,7 +731,7 @@ A possible model for generating code for a generic function is to use a
|
|
|
implements an interface:
|
|
|
|
|
|
- [Interfaces](#interfaces) are types of witness tables.
|
|
|
-- [Impls](#implementing-interfaces) are witness table values.
|
|
|
+- An [impl](#implementing-interfaces) is a witness table value.
|
|
|
|
|
|
Type checking is done with just the interface. The impl is used during code
|
|
|
generation time, possibly using
|
|
|
@@ -2358,7 +2358,8 @@ class Bijection(FromType:! type, ToType:! type) {
|
|
|
impl as Map(FromType, ToType) { ... }
|
|
|
impl as Map(ToType, FromType) { ... }
|
|
|
}
|
|
|
-// ❌ Error: Bijection has two impls of interface Map(String, String)
|
|
|
+// ❌ Error: Bijection has twodifferent impl definitions of
|
|
|
+// interface Map(String, String)
|
|
|
var oops: Bijection(String, String) = ...;
|
|
|
```
|
|
|
|
|
|
@@ -2682,7 +2683,7 @@ since it can be applied to associated type members as well.
|
|
|
In the following example, normally the `ElementType` of a `Container` can be any
|
|
|
type. The `SortContainer` function, however, takes a pointer to a type
|
|
|
satisfying `Container` with the additional constraint that its `ElementType`
|
|
|
-must satisfy the `Comparable` interface.
|
|
|
+must satisfy the `Comparable` interface, using an `impls` constraint:
|
|
|
|
|
|
```
|
|
|
interface Container {
|
|
|
@@ -2691,7 +2692,7 @@ interface Container {
|
|
|
}
|
|
|
|
|
|
fn SortContainer
|
|
|
- [ContainerType:! Container where .ElementType is Comparable]
|
|
|
+ [ContainerType:! Container where .ElementType impls Comparable]
|
|
|
(container_to_sort: ContainerType*);
|
|
|
```
|
|
|
|
|
|
@@ -2699,13 +2700,9 @@ In contrast to [a same type constraint](#same-type-constraints), this does not
|
|
|
say what type `ElementType` exactly is, just that it must satisfy some
|
|
|
type-of-type.
|
|
|
|
|
|
-**Open question:** How do you spell that? Provisionally we are writing `is`,
|
|
|
-following Swift, but maybe we should have another operator that more clearly
|
|
|
-returns a boolean like `has_type`?
|
|
|
-
|
|
|
**Note:** `Container` defines `ElementType` as having type `type`, but
|
|
|
`ContainerType.ElementType` has type `Comparable`. This is because
|
|
|
-`ContainerType` has type `Container where .ElementType is Comparable`, not
|
|
|
+`ContainerType` has type `Container where .ElementType impls Comparable`, not
|
|
|
`Container`. This means we need to be a bit careful when talking about the type
|
|
|
of `ContainerType` when there is a `where` clause modifying it.
|
|
|
|
|
|
@@ -2731,7 +2728,7 @@ We can then define a function that only accepts types that implement
|
|
|
|
|
|
```
|
|
|
fn F[ContainerType:! ContainerInterface
|
|
|
- where .IteratorType is RandomAccessIterator]
|
|
|
+ where .IteratorType impls RandomAccessIterator]
|
|
|
(c: ContainerType);
|
|
|
```
|
|
|
|
|
|
@@ -2741,18 +2738,18 @@ We would like to be able to name this constraint, defining a
|
|
|
|
|
|
```
|
|
|
let RandomAccessContainer:! auto =
|
|
|
- ContainerInterface where .IteratorType is RandomAccessIterator;
|
|
|
+ ContainerInterface where .IteratorType impls RandomAccessIterator;
|
|
|
// or
|
|
|
constraint RandomAccessContainer {
|
|
|
extends ContainerInterface
|
|
|
- where .IteratorType is RandomAccessIterator;
|
|
|
+ where .IteratorType impls RandomAccessIterator;
|
|
|
}
|
|
|
|
|
|
// With the above definition:
|
|
|
fn F[ContainerType:! RandomAccessContainer](c: ContainerType);
|
|
|
// is equivalent to:
|
|
|
fn F[ContainerType:! ContainerInterface
|
|
|
- where .IteratorType is RandomAccessIterator]
|
|
|
+ where .IteratorType impls RandomAccessIterator]
|
|
|
(c: ContainerType);
|
|
|
```
|
|
|
|
|
|
@@ -2765,7 +2762,7 @@ and satisfy an interface:
|
|
|
```
|
|
|
fn EqualContainers
|
|
|
[CT1:! Container,
|
|
|
- CT2:! Container where .ElementType is HasEquality
|
|
|
+ CT2:! Container where .ElementType impls HasEquality
|
|
|
and .ElementType == CT1.ElementType]
|
|
|
(c1: CT1*, c2: CT2*) -> bool;
|
|
|
```
|
|
|
@@ -2870,7 +2867,7 @@ it is an error if `.Self` could mean two different things, as in:
|
|
|
|
|
|
```
|
|
|
// ❌ Illegal: `.Self` could mean `T` or `T.A`.
|
|
|
-fn F[T:! InterfaceA where .A is
|
|
|
+fn F[T:! InterfaceA where .A impls
|
|
|
(InterfaceB where .B == .Self)](x: T);
|
|
|
```
|
|
|
|
|
|
@@ -2889,7 +2886,7 @@ external impl Vector(String) as Printable { ... }
|
|
|
|
|
|
// Constraint: `T` such that `Vector(T)` implements `Printable`
|
|
|
fn PrintThree
|
|
|
- [T:! type where Vector(.Self) is Printable]
|
|
|
+ [T:! type where Vector(.Self) impls Printable]
|
|
|
(a: T, b: T, c: T) {
|
|
|
var v: Vector(T) = (a, b, c);
|
|
|
Print(v);
|
|
|
@@ -2903,16 +2900,16 @@ fn PrintThree
|
|
|
|
|
|
In this case, we need some other type to implement an interface parameterized by
|
|
|
a generic type parameter. The syntax for this case follows the previous case,
|
|
|
-except now the `.Self` parameter is on the interface to the right of the `is`.
|
|
|
-For example, we might need a type parameter `T` to support explicit conversion
|
|
|
-from an integer type like `i32`:
|
|
|
+except now the `.Self` parameter is on the interface to the right of the
|
|
|
+`impls`. For example, we might need a type parameter `T` to support explicit
|
|
|
+conversion from an integer type like `i32`:
|
|
|
|
|
|
```
|
|
|
interface As(T:! type) {
|
|
|
fn Convert[self: Self]() -> T;
|
|
|
}
|
|
|
|
|
|
-fn Double[T:! Mul where i32 is As(.Self)](x: T) -> T {
|
|
|
+fn Double[T:! Mul where i32 impls As(.Self)](x: T) -> T {
|
|
|
return x * (2 as T);
|
|
|
}
|
|
|
```
|
|
|
@@ -2925,8 +2922,8 @@ current type. This means referring to some
|
|
|
`.MemberName`, or [`.Self`](#recursive-constraints). Examples:
|
|
|
|
|
|
- `Container where .ElementType = i32`
|
|
|
-- `type where Vector(.Self) is Sortable`
|
|
|
-- `Addable where i32 is AddableWith(.Result)`
|
|
|
+- `type where Vector(.Self) impls Sortable`
|
|
|
+- `Addable where i32 impls AddableWith(.Result)`
|
|
|
|
|
|
Constraints that only refer to other types should be moved to the type that is
|
|
|
declared last. So:
|
|
|
@@ -2946,10 +2943,10 @@ fn F[A:! type, B:! type where A == .Self, C:! type](a: A, b: B, c: C);
|
|
|
This includes `where` clauses used in an `impl` declaration:
|
|
|
|
|
|
```
|
|
|
-// ❌ Error: `where T is B` does not use `.Self` or a designator
|
|
|
-external impl forall [T:! type] T as A where T is B {}
|
|
|
+// ❌ Error: `where T impls B` does not use `.Self` or a designator
|
|
|
+external impl forall [T:! type] T as A where T impls B {}
|
|
|
// ✅ Allowed
|
|
|
-external impl forall [T:! type where .Self is B] T as A {}
|
|
|
+external impl forall [T:! type where .Self impls B] T as A {}
|
|
|
// ✅ Allowed
|
|
|
external impl forall [T:! B] T as A {}
|
|
|
```
|
|
|
@@ -2991,13 +2988,13 @@ Effectively that means that these functions are automatically rewritten to add a
|
|
|
```
|
|
|
fn LookUp[KeyType:! type]
|
|
|
(hm: HashMap(KeyType, i32)*
|
|
|
- where KeyType is Hashable & EqualityComparable & Movable,
|
|
|
+ where KeyType impls Hashable & EqualityComparable & Movable,
|
|
|
k: KeyType) -> i32;
|
|
|
|
|
|
fn PrintValueOrDefault[KeyType:! Printable,
|
|
|
ValueT:! Printable & HasDefault]
|
|
|
(map: HashMap(KeyType, ValueT)
|
|
|
- where KeyType is Hashable & EqualityComparable & Movable,
|
|
|
+ where KeyType impls Hashable & EqualityComparable & Movable,
|
|
|
key: KeyT);
|
|
|
```
|
|
|
|
|
|
@@ -3064,7 +3061,7 @@ them, needs them to be `Hashable` and so on. To say "`T` is a type where
|
|
|
`HashSet(T)` is legal," we can write:
|
|
|
|
|
|
```
|
|
|
-fn NumDistinct[T:! type where HashSet(.Self) is type]
|
|
|
+fn NumDistinct[T:! type where HashSet(.Self) impls type]
|
|
|
(a: T, b: T, c: T) -> i32 {
|
|
|
var set: HashSet(T);
|
|
|
set.Add(a);
|
|
|
@@ -3326,7 +3323,7 @@ interfaces to generic types, they may be added without breaking existing code.
|
|
|
|
|
|
There are some constraints that we will naturally represent as named
|
|
|
type-of-types. These can either be used directly to constrain a generic type
|
|
|
-parameter, or in a `where ... is ...` clause to constrain an associated type.
|
|
|
+parameter, or in a `where ... impls ...` clause to constrain an associated type.
|
|
|
|
|
|
The compiler determines which types implement these interfaces, developers can
|
|
|
not explicitly implement these interfaces for their own types.
|
|
|
@@ -3341,7 +3338,7 @@ subtypes of `T`.
|
|
|
|
|
|
```
|
|
|
fn F[T:! Extends(BaseType)](p: T*);
|
|
|
-fn UpCast[T:! type](p: T*, U:! type where T is Extends(.Self)) -> U*;
|
|
|
+fn UpCast[T:! type](p: T*, U:! type where T impls Extends(.Self)) -> U*;
|
|
|
fn DownCast[T:! type](p: T*, U:! Extends(T)) -> U*;
|
|
|
```
|
|
|
|
|
|
@@ -3370,7 +3367,8 @@ have to include a "data representation requirement" option.
|
|
|
|
|
|
`CompatibleWith` determines an equivalence relationship between types.
|
|
|
Specifically, given two types `T1` and `T2`, they are equivalent if
|
|
|
-`T1 is CompatibleWith(T2)`. That is, if `T1` has the type `CompatibleWith(T2)`.
|
|
|
+`T1 impls CompatibleWith(T2)`. That is, if `T1` has the type
|
|
|
+`CompatibleWith(T2)`.
|
|
|
|
|
|
**Note:** Just like interface parameters, we require the user to supply `U`,
|
|
|
they may not be deduced. Specifically, this code would be illegal:
|
|
|
@@ -3402,7 +3400,7 @@ class HashSet(T:! Hashable) { ... }
|
|
|
```
|
|
|
|
|
|
Then `HashSet(T)` may be cast to `HashSet(U)` if
|
|
|
-`T is CompatibleWith(U, Hashable)`. The one-parameter interpretation of
|
|
|
+`T impls CompatibleWith(U, Hashable)`. The one-parameter interpretation of
|
|
|
`CompatibleWith(U)` is recovered by letting the default for the second `TT`
|
|
|
parameter be `type`.
|
|
|
|
|
|
@@ -3463,7 +3461,7 @@ assert(CombinedCompare(Song(...), Song(...), (SongByArtist, SongByTitle)) ==
|
|
|
through? They will also be needed for
|
|
|
[variadic argument support](#variadic-arguments).
|
|
|
|
|
|
-#### Example: Creating an impl out of other impls
|
|
|
+#### Example: Creating an impl out of other implementations
|
|
|
|
|
|
And then to package this functionality as an implementation of `Comparable`, we
|
|
|
combine `CompatibleWith` with [type adaptation](#adapting-types):
|
|
|
@@ -3679,7 +3677,7 @@ can be used to switch to the API of `C` when it is external, as an alternative
|
|
|
to [using an adapter](#use-case-accessing-external-names), or to simplify
|
|
|
inlining of a generic function while preserving semantics.
|
|
|
|
|
|
-## Parameterized impls
|
|
|
+## Parameterized impl declarations
|
|
|
|
|
|
There are cases where an impl definition should apply to more than a single type
|
|
|
and interface combination. The solution is to parameterize the impl definition,
|
|
|
@@ -3690,11 +3688,11 @@ so it applies to a family of types, interfaces, or both. This includes:
|
|
|
- "Conditional conformance" where a parameterized type implements some
|
|
|
interface if the parameter to the type satisfies some criteria, like
|
|
|
implementing the same interface.
|
|
|
-- "Blanket" impls where an interface is implemented for all types that
|
|
|
- implement another interface, or some other criteria beyond being a specific
|
|
|
- type.
|
|
|
-- "Wildcard" impls where a family of interfaces are implemented for single
|
|
|
- type.
|
|
|
+- "Blanket" impl declarations where an interface is implemented for all types
|
|
|
+ that implement another interface, or some other criteria beyond being a
|
|
|
+ specific type.
|
|
|
+- "Wildcard" impl declarations where a family of interfaces are implemented
|
|
|
+ for single type.
|
|
|
|
|
|
### Impl for a parameterized type
|
|
|
|
|
|
@@ -3720,8 +3718,8 @@ class Vector(T:! type) {
|
|
|
```
|
|
|
|
|
|
An impl may be declared [external](#external-impl) by adding an `external`
|
|
|
-keyword before `impl`. External impls may also be declared out-of-line, but all
|
|
|
-parameters must be declared in a `forall` clause:
|
|
|
+keyword before `impl`. External impl declarations may also be out-of-line, but
|
|
|
+all parameters must be declared in a `forall` clause:
|
|
|
|
|
|
```
|
|
|
external impl forall [T:! type] Vector(T) as Iterable
|
|
|
@@ -3787,8 +3785,8 @@ external impl forall [T:! Printable] Vector(T) as Printable {
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-To define these `impl`s inline in a `class` definition, include a `forall`
|
|
|
-clause with a more-specific type between the `impl` and `as` keywords.
|
|
|
+To include these `impl` definitions inline in a `class` definition, include a
|
|
|
+`forall` clause with a more-specific type between the `impl` and `as` keywords.
|
|
|
|
|
|
```
|
|
|
class Array(T:! type, template N:! i64) {
|
|
|
@@ -3880,7 +3878,7 @@ a vector type that only has a `Sort` method if its elements implement the
|
|
|
|
|
|
```
|
|
|
class Vector(T:! type) {
|
|
|
- // `Vector(T)` has a `Sort()` method if `T` is `Comparable`.
|
|
|
+ // `Vector(T)` has a `Sort()` method if `T` impls `Comparable`.
|
|
|
fn Sort[C:! Comparable, addr self: Vector(C)*]();
|
|
|
}
|
|
|
```
|
|
|
@@ -3893,11 +3891,11 @@ methods using
|
|
|
or
|
|
|
[contextual where clauses](https://docs.swift.org/swift-book/LanguageGuide/Generics.html#ID628).
|
|
|
|
|
|
-### Blanket impls
|
|
|
+### Blanket impl declarations
|
|
|
|
|
|
-A _blanket impl_ is an `impl` that could apply to more than one root type, so
|
|
|
-the `impl` will use a type variable for the `Self` type. Here are some examples
|
|
|
-where blanket impls arise:
|
|
|
+A _blanket impl declaration_ is an `impl` declaration that could apply to more
|
|
|
+than one root type, so the `impl` declaration will use a type variable for the
|
|
|
+`Self` type. Here are some examples where blanket impl declarations arise:
|
|
|
|
|
|
- Any type implementing `Ordered` should get an implementation of
|
|
|
`PartiallyOrdered`.
|
|
|
@@ -3915,15 +3913,15 @@ where blanket impls arise:
|
|
|
|
|
|
This means that every type is the common type with itself.
|
|
|
|
|
|
-Blanket impls must always be [external](#external-impl) and defined lexically
|
|
|
-out-of-line.
|
|
|
+Blanket impl declarations must always be [external](#external-impl) and defined
|
|
|
+lexically out-of-line.
|
|
|
|
|
|
-#### Difference between blanket impls and named constraints
|
|
|
+#### Difference between a blanket impl and a named constraint
|
|
|
|
|
|
-A blanket interface can be used to say "any type implementing `interface I` also
|
|
|
-implements `interface B`." Compare this with defining a `constraint C` that
|
|
|
-requires `I`. In that case, `C` will also be implemented any time `I` is. There
|
|
|
-are differences though:
|
|
|
+A blanket impl declaration can be used to say "any type implementing
|
|
|
+`interface I` also implements `interface B`." Compare this with defining a
|
|
|
+`constraint C` that requires `I`. In that case, `C` will also be implemented any
|
|
|
+time `I` is. There are differences though:
|
|
|
|
|
|
- There can be other implementations of `interface B` without a corresponding
|
|
|
implementation of `I`, unless `B` has a requirement on `I`. However, the
|
|
|
@@ -3931,12 +3929,13 @@ are differences though:
|
|
|
- More specialized implementations of `B` can override the blanket
|
|
|
implementation.
|
|
|
|
|
|
-### Wildcard impls
|
|
|
+### Wildcard impl declarations
|
|
|
|
|
|
-A _wildcard impl_ is an impl that defines a family of interfaces for a single
|
|
|
-`Self` type. For example, the `BigInt` type might implement `AddTo(T)` for all
|
|
|
-`T` that implement `ImplicitAs(i32)`. The implementation would first convert `T`
|
|
|
-to `i32` and then add the `i32` to the `BigInt` value.
|
|
|
+A _wildcard impl declaration_ is an `impl` declaration that defines how a family
|
|
|
+of interfaces are implemented for a single `Self` type. For example, the
|
|
|
+`BigInt` type might implement `AddTo(T)` for all `T` that implement
|
|
|
+`ImplicitAs(i32)`. The implementation would first convert `T` to `i32` and then
|
|
|
+add the `i32` to the `BigInt` value.
|
|
|
|
|
|
```
|
|
|
class BigInt {
|
|
|
@@ -3946,13 +3945,14 @@ class BigInt {
|
|
|
external impl forall [T:! ImplicitAs(i32)] BigInt as AddTo(T) { ... }
|
|
|
```
|
|
|
|
|
|
-Wildcard impls must always be [external](#external-impl), to avoid having the
|
|
|
-names in the interface defined for the type multiple times.
|
|
|
+Wildcard impl declarations must always be [external](#external-impl), to avoid
|
|
|
+having the names in the interface defined for the type multiple times.
|
|
|
|
|
|
### Combinations
|
|
|
|
|
|
-The different kinds of parameters to impls may be combined. For example, if `T`
|
|
|
-implements `As(U)`, then this implements `As(Optional(U))` for `Optional(T)`:
|
|
|
+The different kinds of parameters to an `impl` declarations may be combined. For
|
|
|
+example, if `T` implements `As(U)`, then this implements `As(Optional(U))` for
|
|
|
+`Optional(T)`:
|
|
|
|
|
|
```
|
|
|
external impl forall [U:! type, T:! As(U)]
|
|
|
@@ -3999,8 +3999,8 @@ impl Foo(?, i32) as Bar(String, ?)
|
|
|
To get a uniform representation across different `impl` definitions, before type
|
|
|
parameters are replaced the declarations are normalized as follows:
|
|
|
|
|
|
-- For impls declared lexically inline in a class definition, the type is added
|
|
|
- between the `impl` and `as` keywords if the type is left out.
|
|
|
+- For impl declarations lexically inline in a class definition, the type is
|
|
|
+ added between the `impl` and `as` keywords if the type is left out.
|
|
|
- Pointer types `T*` are replaced with `Ptr(T)`.
|
|
|
- The `external` keyword is removed, if present.
|
|
|
- The `forall` clause introducing type parameters is removed, if present.
|
|
|
@@ -4018,9 +4018,9 @@ library depends on.
|
|
|
|
|
|
To achieve coherence, we need to ensure that any given impl can only be defined
|
|
|
in a library that must be imported for it to apply. Specifically, given a
|
|
|
-specific type and specific interface, impls that can match can only be in
|
|
|
-libraries that must have been imported to name that type or interface. This is
|
|
|
-achieved with the _orphan rule_.
|
|
|
+specific type and specific interface, `impl` declarations that can match can
|
|
|
+only be in libraries that must have been imported to name that type or
|
|
|
+interface. This is achieved with the _orphan rule_.
|
|
|
|
|
|
**Orphan rule:** Some name from the type structure of an `impl` declaration must
|
|
|
be defined in the same library as the `impl`, that is some name must be _local_.
|
|
|
@@ -4031,17 +4031,18 @@ sufficient since it
|
|
|
[need not be imported](/proposals/p0920.md#orphan-rule-could-consider-interface-requirements-in-blanket-impls).
|
|
|
|
|
|
Since Carbon in addition requires there be no cyclic library dependencies, we
|
|
|
-conclude that there is at most one library that can define impls with a
|
|
|
-particular type structure.
|
|
|
+conclude that there is at most one library that can contain `impl` definitions
|
|
|
+with a particular type structure.
|
|
|
|
|
|
#### Overlap rule
|
|
|
|
|
|
Given a specific concrete type, say `Foo(bool, i32)`, and an interface, say
|
|
|
-`Bar(String, f32)`, the overlap rule picks, among all the matching impls, which
|
|
|
-type structure is considered "most specific" to use as the implementation of
|
|
|
-that type for that interface.
|
|
|
+`Bar(String, f32)`, the overlap rule picks, among all the matching `impl`
|
|
|
+declarations, which type structure is considered "most specific" to use as the
|
|
|
+implementation of that type for that interface.
|
|
|
|
|
|
-Given two different type structures of impls matching a query, for example:
|
|
|
+Given two different type structures of impl declarations matching a query, for
|
|
|
+example:
|
|
|
|
|
|
```
|
|
|
impl Foo(?, i32) as Bar(String, ?)
|
|
|
@@ -4059,14 +4060,15 @@ difference.
|
|
|
|
|
|
#### Prioritization rule
|
|
|
|
|
|
-Since at most one library can define impls with a given type structure, all
|
|
|
-impls with a given type structure must be in the same library. Furthermore by
|
|
|
-the [impl declaration access rules](#access), they will be defined in the API
|
|
|
-file for the library if they could match any query from outside the library. If
|
|
|
-there is more than one impl with that type structure, they must be
|
|
|
-[defined](#implementing-interfaces) or [declared](#declaring-implementations)
|
|
|
-together in a prioritization block. Once a type structure is selected for a
|
|
|
-query, the first impl in the prioritization block that matches is selected.
|
|
|
+Since at most one library can contain `impl` definitions with a given type
|
|
|
+structure, all `impl` definitions with a given type structure must be in the
|
|
|
+same library. Furthermore by the [impl declaration access rules](#access), they
|
|
|
+will be defined in the API file for the library if they could match any query
|
|
|
+from outside the library. If there is more than one impl with that type
|
|
|
+structure, they must be [defined](#implementing-interfaces) or
|
|
|
+[declared](#declaring-implementations) together in a prioritization block. Once
|
|
|
+a type structure is selected for a query, the first impl in the prioritization
|
|
|
+block that matches is selected.
|
|
|
|
|
|
**Open question:** How are prioritization blocks written? A block starts with a
|
|
|
keyword like `match_first` or `impl_priority` and then a sequence of impl
|
|
|
@@ -4084,12 +4086,13 @@ match_first {
|
|
|
when they contain a mixture of type structures? There are three options:
|
|
|
|
|
|
- Prioritization blocks implicitly define all non-empty intersections of
|
|
|
- contained impls, which are then selected by their type structure.
|
|
|
+ contained `impl` declarations, which are then selected by their type
|
|
|
+ structure.
|
|
|
- The compiler first picks the impl with the type pattern most favored for the
|
|
|
query, and then picks the definition of the highest priority matching impl
|
|
|
in the same prioritization block.
|
|
|
-- All the impls in a prioritization block are required to have the same type
|
|
|
- structure, at a cost in expressivity.
|
|
|
+- All the `impl` declarations in a prioritization block are required to have
|
|
|
+ the same type structure, at a cost in expressivity.
|
|
|
|
|
|
To see the difference between the first two options, consider two libraries with
|
|
|
type structures as follows:
|
|
|
@@ -4115,8 +4118,8 @@ interface) pairs where there is an edge from pair A to pair B if whether type A
|
|
|
implements interface A determines whether type B implements interface B.
|
|
|
|
|
|
The test for whether something forms a cycle needs to be precise enough, and not
|
|
|
-erase too much information when considering this graph, that these impls are not
|
|
|
-considered to form cycles with themselves:
|
|
|
+erase too much information when considering this graph, that these `impl`
|
|
|
+declarations are not considered to form cycles with themselves:
|
|
|
|
|
|
```
|
|
|
impl forall [T:! Printable] Optional(T) as Printable;
|
|
|
@@ -4135,8 +4138,8 @@ This is a cycle where which types implement `ComparableWith` determines which
|
|
|
types implement the same interface.
|
|
|
|
|
|
**Example:** Cycles can create situations where there are multiple ways of
|
|
|
-selecting impls that are inconsistent with each other. Consider an interface
|
|
|
-with two blanket `impl` declarations:
|
|
|
+selecting `impl` declarations that are inconsistent with each other. Consider an
|
|
|
+interface with two blanket `impl` declarations:
|
|
|
|
|
|
```
|
|
|
class Y {}
|
|
|
@@ -4145,18 +4148,18 @@ interface True {}
|
|
|
impl Y as True {}
|
|
|
interface Z(T:! type) { let Cond:! type; }
|
|
|
match_first {
|
|
|
- impl forall [T:! type, U:! Z(T) where .Cond is True] T as Z(U)
|
|
|
+ impl forall [T:! type, U:! Z(T) where .Cond impls True] T as Z(U)
|
|
|
where .Cond = N { }
|
|
|
impl forall [T:! type, U:! type] T as Z(U)
|
|
|
where .Cond = Y { }
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-What is `i8.(Z(i16).Cond)`? It depends on which of the two blanket impls are
|
|
|
-selected.
|
|
|
+What is `i8.(Z(i16).Cond)`? It depends on which of the two blanket impl
|
|
|
+declarations are selected.
|
|
|
|
|
|
- An implementation of `Z(i16)` for `i8` could come from the first blanket
|
|
|
- impl with `T == i8` and `U == i16` if `i16 is Z(i8)` and
|
|
|
+ impl with `T == i8` and `U == i16` if `i16 impls Z(i8)` and
|
|
|
`i16.(Z(i8).Cond) == Y`. This condition is satisfied if `i16` implements
|
|
|
`Z(i8)` using the second blanket impl. In this case,
|
|
|
`i8.(Z(i16).Cond) == N`.
|
|
|
@@ -4209,8 +4212,8 @@ only detected by its users.
|
|
|
**Open question:** Should Carbon reject cycles in the absence of a query? The
|
|
|
two options here are:
|
|
|
|
|
|
-- Combining impls gives you an immediate error if there exists queries using
|
|
|
- those impls that have cycles.
|
|
|
+- Combining `impl` declarations gives you an immediate error if there exists
|
|
|
+ queries using them that have cycles.
|
|
|
- Only when a query reveals a cyclic dependency is an error reported.
|
|
|
|
|
|
**Open question:** In the second case, should we ignore cycles if they don't
|
|
|
@@ -4219,20 +4222,21 @@ implementations that are lower priority.
|
|
|
|
|
|
#### Termination rule
|
|
|
|
|
|
-It is possible to define a set of impls where there isn't a cycle, but the graph
|
|
|
-is infinite. Without some rule to prevent exhaustive exploration of the graph,
|
|
|
-determining whether a type implements an interface could run forever.
|
|
|
+It is possible to have a set of `impl` declarations where there isn't a cycle,
|
|
|
+but the graph is infinite. Without some rule to prevent exhaustive exploration
|
|
|
+of the graph, determining whether a type implements an interface could run
|
|
|
+forever.
|
|
|
|
|
|
-**Example:** It could be that `A` implements `B`, so `A is B` if
|
|
|
-`Optional(A) is B`, if `Optional(Optional(A)) is B`, and so on. This could be
|
|
|
-the result of a single impl:
|
|
|
+**Example:** It could be that `A` implements `B`, so `A impls B` if
|
|
|
+`Optional(A) impls B`, if `Optional(Optional(A)) impls B`, and so on. This could
|
|
|
+be the result of a single impl:
|
|
|
|
|
|
```
|
|
|
-impl forall [A:! type where Optional(.Self) is B] A as B { ... }
|
|
|
+impl forall [A:! type where Optional(.Self) impls B] A as B { ... }
|
|
|
```
|
|
|
|
|
|
-This problem can also result from a chain of impls, as in `A is B` if `A* is C`,
|
|
|
-if `Optional(A) is B`, and so on.
|
|
|
+This problem can also result from a chain of `impl` declarations, as in
|
|
|
+`A impls B` if `A* impls C`, if `Optional(A) impls B`, and so on.
|
|
|
|
|
|
Rust solves this problem by imposing a recursion limit, much like C++ compilers
|
|
|
use to terminate template recursion. This goes against
|
|
|
@@ -4250,7 +4254,7 @@ allow our desired use cases, but allow the compiler to detect non-terminating
|
|
|
cases? Perhaps there is some sort of complexity measure Carbon can require
|
|
|
doesn't increase when recursing?
|
|
|
|
|
|
-### `final` impls
|
|
|
+### `final` impl declarations
|
|
|
|
|
|
There are cases where knowing that a parameterized impl won't be specialized is
|
|
|
particularly valuable. This could let the compiler know the return type of a
|
|
|
@@ -4332,10 +4336,11 @@ fn F[T:! type](x: T) {
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-#### Libraries that can contain `final` impls
|
|
|
+#### Libraries that can contain a `final` impl
|
|
|
|
|
|
-To prevent the possibility of two unrelated libraries defining conflicting
|
|
|
-impls, Carbon restricts which libraries may declare an impl as `final` to only:
|
|
|
+To prevent the possibility of two unrelated libraries defining conflicting impl
|
|
|
+declarations, Carbon restricts which libraries may declare an impl as `final` to
|
|
|
+only:
|
|
|
|
|
|
- the library declaring the impl's interface and
|
|
|
- the library declaring the root of the `Self` type.
|
|
|
@@ -4352,19 +4357,19 @@ higher-priority impl is defined superseding a `final` impl.
|
|
|
|
|
|
- An impl with type structure `impl MyType(...) as MyInterface(...)` defined
|
|
|
in the library with `MyType` must import the library defining `MyInterface`,
|
|
|
- and so will be able to see any final blanket impls.
|
|
|
+ and so will be able to see any final blanket impl declarations.
|
|
|
- A blanket impl with type structure
|
|
|
`impl ? as MyInterface(...ParameterType(...)...)` may be defined in the
|
|
|
library with `ParameterType`, but that library must import the library
|
|
|
- defining `MyInterface`, and so will be able to see any `final` blanket impls
|
|
|
- that might overlap. A final impl with type structure
|
|
|
+ defining `MyInterface`, and so will be able to see any `final` blanket impl
|
|
|
+ declarations that might overlap. A final impl with type structure
|
|
|
`impl MyType(...) as MyInterface(...)` would be given priority over any
|
|
|
overlapping blanket impl defined in the `ParameterType` library.
|
|
|
- An impl with type structure
|
|
|
`impl MyType(...ParameterType(...)...) as MyInterface(...)` may be defined
|
|
|
in the library with `ParameterType`, but that library must import the
|
|
|
libraries defining `MyType` and `MyInterface`, and so will be able to see
|
|
|
- any `final` impls that might overlap.
|
|
|
+ any `final` `impl` declarations that might overlap.
|
|
|
|
|
|
### Comparison to Rust
|
|
|
|
|
|
@@ -4378,13 +4383,14 @@ differences between the Carbon and Rust plans:
|
|
|
|
|
|
- A Rust impl defaults to not being able to be specialized, with a `default`
|
|
|
keyword used to opt-in to allowing specialization, reflecting the existing
|
|
|
- code base developed without specialization. Carbon impls default to allowing
|
|
|
- specialization, with restrictions on which may be declared `final`.
|
|
|
-- Since Rust impls are not specializable by default, generic functions can
|
|
|
- assume that if a matching blanket impl is found, the associated types from
|
|
|
- that impl will be used. In Carbon, if a generic function requires an
|
|
|
- associated type to have a particular value, the function commonly will need
|
|
|
- to state that using an explicit constraint.
|
|
|
+ code base developed without specialization. Carbon `impl` declarations
|
|
|
+ default to allowing specialization, with restrictions on which may be
|
|
|
+ declared `final`.
|
|
|
+- Since a Rust impl is not specializable by default, generic functions can
|
|
|
+ assume that if a matching blanket impl declaration is found, the associated
|
|
|
+ types from that impl will be used. In Carbon, if a generic function requires
|
|
|
+ an associated type to have a particular value, the function commonly will
|
|
|
+ need to state that using an explicit constraint.
|
|
|
- Carbon will not have the "fundamental" attribute used by Rust on types or
|
|
|
traits, as described in
|
|
|
[Rust RFC 1023: "Rebalancing Coherence"](https://rust-lang.github.io/rfcs/1023-rebalancing-coherence.html).
|
|
|
@@ -4399,8 +4405,8 @@ differences between the Carbon and Rust plans:
|
|
|
[Little Orphan Impls: The ordered rule](http://smallcultfollowing.com/babysteps/blog/2015/01/14/little-orphan-impls/#the-ordered-rule),
|
|
|
but the specifics are different.
|
|
|
- Carbon is not planning to support any inheritance of implementation between
|
|
|
- impls. This is more important to Rust since Rust does not support class
|
|
|
- inheritance for implementation reuse. Rust has considered multiple
|
|
|
+ impl definitions. This is more important to Rust since Rust does not support
|
|
|
+ class inheritance for implementation reuse. Rust has considered multiple
|
|
|
approaches here, see
|
|
|
[Aaron Turon: "Specialize to Reuse"](http://aturon.github.io/tech/2015/09/18/reuse/)
|
|
|
and
|
|
|
@@ -4481,9 +4487,9 @@ be used in the following contexts:
|
|
|
`as C; }`
|
|
|
- Nothing implied by implementing `C` will be visible until `C` is
|
|
|
complete.
|
|
|
-- ✅ `T:! C` ... `T is C`
|
|
|
-- ✅ `T:! A & C` ... `T is C`
|
|
|
- - This includes constructs requiring `T is C` such as `T as C` or
|
|
|
+- ✅ `T:! C` ... `T impls C`
|
|
|
+- ✅ `T:! A & C` ... `T impls C`
|
|
|
+ - This includes constructs requiring `T impls C` such as `T as C` or
|
|
|
`U:! C = T`.
|
|
|
- ✅ `external impl `...` as C;`
|
|
|
- Checking that all associated constants of `C` are correctly assigned
|
|
|
@@ -4496,7 +4502,7 @@ An incomplete `C` cannot be used in the following contexts:
|
|
|
- ❌ `class `...` { impl as C; }`
|
|
|
- The names of `C` are added to the class, and so those names need to be
|
|
|
known.
|
|
|
-- ❌ `T:! C` ... `T is A` where `A` is an interface or named constraint
|
|
|
+- ❌ `T:! C` ... `T impls A` where `A` is an interface or named constraint
|
|
|
different from `C`
|
|
|
- Need to see the definition of `C` to see if it implies `A`.
|
|
|
- ❌ `external impl` ... `as C {` ... `}`
|
|
|
@@ -4540,9 +4546,9 @@ these rules:
|
|
|
|
|
|
- The definition must be in the same library as the declaration. They must
|
|
|
either be in the same file, or the declaration can be in the API file and
|
|
|
- the definition in an impl file. **Future work:** Carbon may require the
|
|
|
- definition of [parameterized impls](#parameterized-impls) to be in the API
|
|
|
- file, to support separate compilation.
|
|
|
+ the definition in an impl file. **Future work:** Carbon may require
|
|
|
+ [parameterized impl definitions](#parameterized-impl-declarations) to be in
|
|
|
+ the API file, to support separate compilation.
|
|
|
- If there is both a forward declaration and a definition, only the first
|
|
|
declaration must specify the assignment of associated constants with a
|
|
|
`where` clause. Later declarations may omit the `where` clause by writing
|
|
|
@@ -4684,11 +4690,13 @@ class MyClass {
|
|
|
// from the API file to the implementation file for
|
|
|
// this library.
|
|
|
|
|
|
-// Definition of previously declared external impls.
|
|
|
+// Definition of implementations previously declared
|
|
|
+// external.
|
|
|
external impl MyClass as Interface2 where _ { }
|
|
|
external impl MyClass as Interface5 where _ { }
|
|
|
|
|
|
-// Definition of previously declared internal impls.
|
|
|
+// Definition of implementations previously declared
|
|
|
+// internal.
|
|
|
impl MyClass as Interface4 where _ { }
|
|
|
impl MyClass as Interface6 where _ { }
|
|
|
```
|
|
|
@@ -4886,8 +4894,8 @@ interface TotalOrder {
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-The workaround for this restriction is to use a [blanket impl](#blanket-impls)
|
|
|
-instead:
|
|
|
+The workaround for this restriction is to use a
|
|
|
+[blanket impl declaration](#blanket-impl-declarations) instead:
|
|
|
|
|
|
```
|
|
|
interface TotalOrder {
|
|
|
@@ -4916,7 +4924,7 @@ Rust has found them valuable.
|
|
|
|
|
|
As an alternative to providing a definition of an interface member as a default,
|
|
|
members marked with the `final` keyword will not allow that definition to be
|
|
|
-overridden in impls.
|
|
|
+overridden in `impl` definitions.
|
|
|
|
|
|
```
|
|
|
interface TotalOrder {
|
|
|
@@ -5060,7 +5068,7 @@ fn ProcessVector(v: Vector(i32)) {
|
|
|
}
|
|
|
|
|
|
// Satisfies the requirement that `Vector(i32)` must
|
|
|
-// implement `Equatable` since `i32` is `Equatable`.
|
|
|
+// implement `Equatable` since `i32` impls `Equatable`.
|
|
|
external impl forall [T:! Equatable] Vector(T) as Equatable { ... }
|
|
|
```
|
|
|
|
|
|
@@ -5080,7 +5088,7 @@ class Foo(T:! type) {}
|
|
|
// This is allowed because we know that an `impl Foo(T) as Equatable`
|
|
|
// will exist for all types `T` for which this impl is used, even
|
|
|
// though there's neither an imported impl nor an impl in this file.
|
|
|
-external impl forall [T:! type where Foo(T) is Equatable]
|
|
|
+external impl forall [T:! type where Foo(T) impls Equatable]
|
|
|
Foo(T) as Iterable {}
|
|
|
```
|
|
|
|
|
|
@@ -5090,7 +5098,7 @@ already satisfy the requirement of implementing `Iterable`:
|
|
|
```
|
|
|
class Bar {}
|
|
|
external impl Foo(Bar) as Equatable {}
|
|
|
-// Gives `Foo(Bar) is Iterable` using the blanket impl of
|
|
|
+// Gives `Foo(Bar) impls Iterable` using the blanket impl of
|
|
|
// `Iterable` for `Foo(T)`.
|
|
|
```
|
|
|
|
|
|
@@ -5114,10 +5122,10 @@ visible implementation of `A` with the same `T` parameter for those types with
|
|
|
the `.Result` associated type set to `i32`. That is
|
|
|
[not sufficient](/proposals/p1088.md#less-strict-about-requirements-with-where-clauses),
|
|
|
though, unless the implementation of `A` can't be specialized, either because it
|
|
|
-is [marked `final`](#final-impls) or is not
|
|
|
-[parameterized](#parameterized-impls). Implementations in other libraries can't
|
|
|
-make `A` be implemented for fewer types, but can cause `.Result` to have a
|
|
|
-different assignment.
|
|
|
+is [marked `final`](#final-impl-declarations) or is not
|
|
|
+[parameterized](#parameterized-impl-declarations). Implementations in other
|
|
|
+libraries can't make `A` be implemented for fewer types, but can cause `.Result`
|
|
|
+to have a different assignment.
|
|
|
|
|
|
## Observing a type implements an interface
|
|
|
|
|
|
@@ -5133,7 +5141,7 @@ One situation where this occurs is when there is a chain of
|
|
|
[interfaces requiring other interfaces](#interface-requiring-other-interfaces-revisited).
|
|
|
During the `impl` validation done during type checking, Carbon will only
|
|
|
consider the interfaces that are direct requirements of the interfaces the type
|
|
|
-is known to implement. An `observe...is` declaration can be used to add an
|
|
|
+is known to implement. An `observe...impls` declaration can be used to add an
|
|
|
interface that is a direct requirement to the set of interfaces whose direct
|
|
|
requirements will be considered for that type. This allows a developer to
|
|
|
provide a proof that there is a sequence of requirements that demonstrate that a
|
|
|
@@ -5154,32 +5162,33 @@ fn RequiresD[T:! D](x: T) {
|
|
|
// ❌ Illegal: No direct connection between `D` and `A`.
|
|
|
// RequiresA(x);
|
|
|
|
|
|
- // `T` is `D` and `D` directly requires `C` to be
|
|
|
+ // `T` impls `D` and `D` directly requires `C` to be
|
|
|
// implemented.
|
|
|
- observe T is C;
|
|
|
+ observe T impls C;
|
|
|
|
|
|
- // `T` is `C` and `C` directly requires `B` to be
|
|
|
+ // `T` impls `C` and `C` directly requires `B` to be
|
|
|
// implemented.
|
|
|
- observe T is B;
|
|
|
+ observe T impls B;
|
|
|
|
|
|
- // ✅ Allowed: `T` is `B` and `B` directly requires
|
|
|
+ // ✅ Allowed: `T` impls `B` and `B` directly requires
|
|
|
// `A` to be implemented.
|
|
|
RequiresA(x);
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-Note that `observe` statements do not affect the selection of impls during code
|
|
|
+Note that `observe` statements do not affect which impl is selected during code
|
|
|
generation. For coherence, the impl used for a (type, interface) pair must
|
|
|
always be the same, independent of context. The
|
|
|
[termination rule](#termination-rule) governs when compilation may fail when the
|
|
|
compiler can't determine the impl to select.
|
|
|
|
|
|
-### Observing blanket impls
|
|
|
+### Observing blanket impl declarations
|
|
|
|
|
|
-An `observe...is` declaration can also be used to observe that a type implements
|
|
|
-an interface because there is a [blanket impl](#blanket-impls) in terms of
|
|
|
-requirements a type is already known to satisfy. Without an `observe`
|
|
|
-declaration, Carbon will only use blanket impls that are directly satisfied.
|
|
|
+An `observe...impls` declaration can also be used to observe that a type
|
|
|
+implements an interface because there is a
|
|
|
+[blanket impl declaration](#blanket-impl-declarations) in terms of requirements
|
|
|
+a type is already known to satisfy. Without an `observe` declaration, Carbon
|
|
|
+will only use blanket impl declarations that are directly satisfied.
|
|
|
|
|
|
```
|
|
|
interface A { }
|
|
|
@@ -5205,11 +5214,11 @@ fn RequiresA(T:! A)(x: T) {
|
|
|
|
|
|
// There is a blanket implementation of `B` for
|
|
|
// types implementing `A`.
|
|
|
- observe T is B;
|
|
|
+ observe T impls B;
|
|
|
|
|
|
// There is a blanket implementation of `C` for
|
|
|
// types implementing `B`.
|
|
|
- observe T is C;
|
|
|
+ observe T impls C;
|
|
|
|
|
|
// ✅ Allowed: There is a blanket implementation
|
|
|
// of `D` for types implementing `C`.
|
|
|
@@ -5218,8 +5227,8 @@ fn RequiresA(T:! A)(x: T) {
|
|
|
```
|
|
|
|
|
|
In the case of an error, a quality Carbon implementation will do a deeper search
|
|
|
-for chains of requirements and blanket impls and suggest `observe` declarations
|
|
|
-that would make the code compile if any solution is found.
|
|
|
+for chains of requirements and blanket impl declarations and suggest `observe`
|
|
|
+declarations that would make the code compile if any solution is found.
|
|
|
|
|
|
## Operator overloading
|
|
|
|
|
|
@@ -5401,7 +5410,7 @@ external impl forall [T:! ImplicitAs(f64)]
|
|
|
}
|
|
|
}
|
|
|
// ✅ Allowed: uses `Meters as MultipliableWith(T)` impl
|
|
|
-// with `T == f32` since `f32 is ImplicitAs(f64)`.
|
|
|
+// with `T == f32` since `f32 impls ImplicitAs(f64)`.
|
|
|
var now_allowed: Meters = height * scale;
|
|
|
```
|
|
|
|
|
|
@@ -5430,11 +5439,12 @@ equivalent to "implementation one". The second implementation replaces the
|
|
|
`like f64` with a parameter that ranges over types that can be implicitly
|
|
|
converted to `f64`, equivalent to "implementation two".
|
|
|
|
|
|
-In general, each `like` adds one additional impl. There is always the impl with
|
|
|
-all of the `like` expressions replaced by their arguments with the definition
|
|
|
-supplied in the source code. In addition, for each `like` expression, there is
|
|
|
-an impl with it replaced by a new parameter. These additional impls will
|
|
|
-delegate to the main impl, which will trigger implicit conversions according to
|
|
|
+In general, each `like` adds one additional parameterized implementation. There
|
|
|
+is always the impl defined with all of the `like` expressions replaced by their
|
|
|
+arguments with the definition supplied in the source code. In addition, for each
|
|
|
+`like` expression, there is an automatic `impl` definition with it replaced by a
|
|
|
+new parameter. These additional automatic implementations will delegate to the
|
|
|
+main impl, which will trigger implicit conversions according to
|
|
|
[Carbon's ordinary implicit conversion rules](/docs/design/expressions/implicit_conversions.md).
|
|
|
In this example, there are two uses of `like`, producing three implementations
|
|
|
|
|
|
@@ -5512,12 +5522,12 @@ external impl forall [T:! ImplicitAs(String)] Vector(T) as Printable;
|
|
|
```
|
|
|
|
|
|
The generated implementations must be legal or the `like` is illegal. For
|
|
|
-example, it must be legal to define those impls in this library by the
|
|
|
-[orphan rule](#orphan-rule). In addition, the generated `impl` definitions must
|
|
|
-only require implicit conversions that are guaranteed to exist. For example,
|
|
|
-there existing an implicit conversion from `T` to `String` does not imply that
|
|
|
-there is one from `Vector(T)` to `Vector(String)`, so the following use of
|
|
|
-`like` is illegal:
|
|
|
+example, it must be legal to have those `impl` definitions in this library by
|
|
|
+the [orphan rule](#orphan-rule). In addition, the generated `impl` definitions
|
|
|
+must only require implicit conversions that are guaranteed to exist. For
|
|
|
+example, there existing an implicit conversion from `T` to `String` does not
|
|
|
+imply that there is one from `Vector(T)` to `Vector(String)`, so the following
|
|
|
+use of `like` is illegal:
|
|
|
|
|
|
```
|
|
|
// ❌ Illegal: Can't convert a value with type
|
|
|
@@ -5648,7 +5658,7 @@ interface OptionalStorage {
|
|
|
```
|
|
|
|
|
|
The default implementation of this interface is provided by a
|
|
|
-[blanket implementation](#blanket-impls):
|
|
|
+[blanket implementation](#blanket-impl-declarations):
|
|
|
|
|
|
```
|
|
|
// Default blanket implementation
|
|
|
@@ -5776,7 +5786,7 @@ supported and made safe.
|
|
|
The idea is that you would write tests alongside an interface that validate the
|
|
|
expected behavior of any type implementing that interface.
|
|
|
|
|
|
-### Impls with state
|
|
|
+### Impl with state
|
|
|
|
|
|
A feature we might consider where an `impl` itself can have state.
|
|
|
|
|
|
@@ -5850,3 +5860,4 @@ parameter, as opposed to an associated type, as in `N:! u32 where ___ >= 2`.
|
|
|
- [#2107: Clarify rules around `Self` and `.Self`](https://github.com/carbon-language/carbon-lang/pull/2107)
|
|
|
- [#2347: What can be done with an incomplete interface](https://github.com/carbon-language/carbon-lang/pull/2347)
|
|
|
- [#2376: Constraints must use `Self`](https://github.com/carbon-language/carbon-lang/pull/2376)
|
|
|
+- [#2483: Replace keyword `is` with `impls`](https://github.com/carbon-language/carbon-lang/pull/2483)
|