Explain more on the difference of where constraints (#4551)

This adds language to explain where the types of constraints can or can
not appear (attached-to an impl-as vs in-a type expression). And
describes the impact of using a rewrite vs same-type constraint inside
the body of the affected code, and thus why a rewrite is preferable when
the constraint is of a single facet type.
This commit is contained in:
Dana Jansens
2024-11-21 18:33:17 +00:00
committed by GitHub
parent 79b9180eff
commit 4cf2c07f7d
+28 -10
View File
@@ -2326,7 +2326,7 @@ fn PeekAtTopOfStackParameterizedImpl
...
}
fn PeekAtTopOfStackParameterized[StackType:! type]
(s: StackType*, T:! type where StackType is StackParameterized(T)) -> T {
(s: StackType*, T:! type where StackType impls StackParameterized(T)) -> T {
return PeekAtTopOfStackParameterizedImpl(T, StackType, s);
}
@@ -2505,8 +2505,21 @@ binary operator:
- _Same-type constraints_: `where`...`==`...
- _Implements constraints_: `where`...`impls`...
And there are two positions that `where` can be written:
- At the end of an `impl as` declaration, before the body of the impl.
```carbon
impl Class as Interface where .A = i32 { ... }
```
- Inside a type expression.
```carbon
fn F[T: Interface where .A impls OtherInterface](t: T) { ... }
```
A rewrite constraint is written `where .A = B`, where `A` is the name of an
[associated constant](#associated-constants) which is rewritten to `B`.
[associated constant](#associated-constants) which is rewritten to `B`. Any use
of `.A` thereafter is the same as using `B`, including direct access to the API
of `B`.
The "dot followed by the name of a member" construct, like `.A`, is called a
_designator_. The name of the designator is looked up in the constraint, and
@@ -2518,15 +2531,20 @@ constraint.
> constraint is satisfied.
A same-type constraint is written `where X == Y`, where `X` and `Y` both name
facets. The constraint is that `X as type` must be the same as `Y as type`. In
cases where a constraint may be written in either form, prefer a rewrite
constraint over a same-type constraint. Note that switching between the two
forms does not change which types satisfies the constraint, and so is a
compatible change for callers.
facets. The constraint is that `X as type` must be the same as `Y as type`. It
would normally only be used in the type expression position.
A same-type constraint does not rewrite the type on the left-hand side to the
right-hand side, and they are still treated as distinct types. A value of type
`X` [would need to be cast](#satisfying-both-facet-types) to `Y` in order to use
the API of `Y`. So for constraint clauses that name a single facet type on the
right-hand side, using a rewrite constraint is preferred. Note that switching
between the two forms does not change which types satisfies the constraint, and
so is a compatible change for callers.
An implements constraint is written `where T impls C`, where `T` is a facet and
`C` is a facet type. The constraint is that `T` satisfies the requirements of
`C`.
`C`. It would normally only be used in the type expression position.
**References:** The definition of rewrite and same-type constraints were in
[proposal #2173](https://github.com/carbon-language/carbon-lang/pull/2173).
@@ -2600,7 +2618,7 @@ interface Container {
let IteratorType:! Iterator where .ElementType = ElementType;
// `.Self` means `SliceType`.
let SliceType:! Container where .Self is SliceConstraint(ElementType, .Self);
let SliceType:! Container where .Self impls SliceConstraint(ElementType, .Self);
// `Self` means the type implementing `Container`.
fn GetSlice[addr self: Self*]
@@ -2989,7 +3007,7 @@ notionally does much the same thing as
impl A as SameAs(C) { ... }
```
where the `impl` makes use of `A is SameAs(B)` and `B is SameAs(C)`.
where the `impl` makes use of `A impls SameAs(B)` and `B impls SameAs(C)`.
In general, an `observe` declaration lists a sequence of
[type expressions](terminology.md#type-expression) that are equal by some