Bläddra i källkod

Documentation update for `require` and `extend impl as` in interfaces and named constraints (#6240)

Proposal [p5337](https://docs.carbon-lang.dev/proposals/p5337.html)
renamed and introduced new syntax for extending an interface or named
constraint with another.
- The `require` keyword can now be modified by `extend`, instead of it
being a separate thing altogether.
- Interfaces can `extend impl as I` to gain the members of `I` and
implicitly use them to implement `I`. Named constraints can not.

The `Identity` example is meant to not know anything about the type of
the object its passing through, but it ends up making a copy of it. Fix
the example to not by using a pointer.
Dana Jansens 6 månader sedan
förälder
incheckning
820e997261
1 ändrade filer med 107 tillägg och 57 borttagningar
  1. 107 57
      docs/design/generics/details.md

+ 107 - 57
docs/design/generics/details.md

@@ -32,7 +32,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 -   [Combining interfaces by anding facet types](#combining-interfaces-by-anding-facet-types)
 -   [Interface requiring other interfaces](#interface-requiring-other-interfaces)
     -   [Interface extension](#interface-extension)
-        -   [`extend` and `impl` with named constraints](#extend-and-impl-with-named-constraints)
+        -   [`extend require` with named constraints](#extend-require-with-named-constraints)
         -   [Diamond dependency issue](#diamond-dependency-issue)
     -   [Use case: detecting unreachable matches](#use-case-detecting-unreachable-matches)
 -   [Adapting types](#adapting-types)
@@ -905,14 +905,14 @@ because types don't have to explicitly specify which named constraints they
 implement, types automatically implement any named constraints they can satisfy.
 
 A named constraint definition can contain interface requirements using
-`require Self impls` declarations and names using `alias` declarations. Note
-that this allows us to declare the aspects of a facet type directly.
+`require ... impls` declarations and names using `alias` declarations. Note that
+this allows us to declare the aspects of a facet type directly.
 
 ```carbon
 constraint VectorLegoFish {
   // Interface implementation requirements
-  require Self impls Vector;
-  require Self impls LegoFish;
+  require impls Vector;
+  require impls LegoFish;
   // Names
   alias Scale = Vector.Scale;
   alias VAdd = Vector.Add;
@@ -920,22 +920,19 @@ constraint VectorLegoFish {
 }
 ```
 
-A `require Self impls` requirement may alternatively be on a named constraint,
+A `require impls` requirement may alternatively be on a named constraint,
 instead of an interface, to add all the requirements of another named constraint
 without adding any of the names:
 
 ```carbon
 constraint DrawVectorLegoFish {
   // The same as requiring both `Vector` and `LegoFish`.
-  require Self impls VectorLegoFish;
+  require impls VectorLegoFish;
   // A regular interface requirement. No syntactic difference.
-  require Self impls Drawable;
+  require impls Drawable;
 }
 ```
 
-> **TODO:** Document that `Self` can be omitted, as adopted in
-> [P5337: Interface extension and `final impl` update](/proposals/p5337.md).
-
 In general, Carbon makes no syntactic distinction between the uses of named
 constraints and interfaces, so one may be replaced with the other without
 affecting users. To accomplish this, Carbon allows a named constraint to be used
@@ -944,7 +941,7 @@ whenever an interface may be. This includes all of these
 
 -   A type may `impl` a named constraint to say that it implements all of the
     requirements of the named constraint, as
-    [described below](#extend-and-impl-with-named-constraints).
+    [described below](#extend-require-with-named-constraints).
 -   A named constraint may be used as a namespace name in
     [a qualified name](#qualified-member-names-and-compound-member-access). For
     example, `VectorLegoFish.VAdd` refers to the same name as `Vector.Add`.
@@ -964,14 +961,16 @@ That is, `type` is the facet type with no requirements (so matches every type),
 and defines no names.
 
 ```carbon
-fn Identity[T:! type](x: T) -> T {
+fn Identity[T:! type](x: T*) -> T* {
   // Can accept values of any type. But, since we know nothing about the
   // type, we don't know about any operations on `x` inside this function.
   return x;
 }
 
-var i: i32 = Identity(3);
-var s: String = Identity("string");
+var i: i32 = 3;
+var p_i: i32* = Identity(&i);
+var s: String = "string";
+var p_s: String = Identity(&s);
 ```
 
 In general, the declarations in `constraint` definition match a subset of the
@@ -1048,11 +1047,11 @@ interface Printable { fn Print[self: Self](); }
 interface Renderable { fn Draw[self: Self](); }
 
 constraint PrintAndRender {
-  require Self impls Printable;
-  require Self impls Renderable;
+  require impls Printable;
+  require impls Renderable;
 }
 constraint JustPrint {
-  require Self impls Printable;
+  require impls Printable;
 }
 
 fn PrintIt[T2:! JustPrint](x2: T2) {
@@ -1086,8 +1085,8 @@ interface Renderable {
 
 // `Printable & Renderable` is syntactic sugar for this facet type:
 constraint {
-  require Self impls Printable;
-  require Self impls Renderable;
+  require impls Printable;
+  require impls Renderable;
   alias Print = Printable.Print;
   alias Center = Renderable.Center;
   alias Draw = Renderable.Draw;
@@ -1138,8 +1137,8 @@ or by defining a named constraint explicitly and renaming the methods:
 
 ```carbon
 constraint RenderableAndEndOfGame {
-  require Self impls Renderable;
-  require Self impls EndOfGame;
+  require impls Renderable;
+  require impls EndOfGame;
   alias Center = Renderable.Center;
   alias RenderableDraw = Renderable.Draw;
   alias TieGame = EndOfGame.Draw;
@@ -1170,8 +1169,8 @@ avoid the possibility of name conflicts, names, use a
 ```
 // `Printable where .Self impls Renderable` is equivalent to:
 constraint {
-  require Self impls Printable;
-  require Self impls Renderable;
+  require impls Printable;
+  require impls Renderable;
   alias Print = Printable.Print;
 }
 ```
@@ -1203,7 +1202,7 @@ For example, in C++,
 requires all containers to also satisfy the requirements of
 `DefaultConstructible`, `CopyConstructible`, `Eq`, and `Swappable`. This is
 already a capability for [facet types in general](#facet-types). For consistency
-we use the same semantics and `require Self impls` syntax as we do for
+we use the same semantics and `require ... impls` syntax as we do for
 [named constraints](#named-constraints):
 
 ```carbon
@@ -1211,7 +1210,7 @@ interface Equatable { fn Equals[self: Self](rhs: Self) -> bool; }
 
 interface Iterable {
   fn Advance[ref self: Self]() -> bool;
-  require Self impls Equatable;
+  require impls Equatable;
 }
 
 fn DoAdvanceAndEquals[T:! Iterable](x: T) {
@@ -1237,7 +1236,7 @@ declarations:
 ```carbon
 interface Hashable {
   fn Hash[self: Self]() -> u64;
-  require Self impls Equatable;
+  require impls Equatable;
   alias Equals = Equatable.Equals;
 }
 
@@ -1294,12 +1293,12 @@ syntax:
 interface Equatable { fn Equals[self: Self](rhs: Self) -> bool; }
 
 interface Hashable {
-  extend Equatable;
+  extend require impls Equatable;
   fn Hash[self: Self]() -> u64;
 }
 // is equivalent to the definition of Hashable from before:
 // interface Hashable {
-//   require Self impls Equatable;
+//   require impls Equatable;
 //   alias Equals = Equatable.Equals;
 //   fn Hash[self: Self]() -> u64;
 // }
@@ -1335,13 +1334,60 @@ Carbon:
 
 ```carbon
 interface SetAlgebra {
-  extend Equatable;
-  extend ExpressibleByArrayLiteral;
+  extend require impls Equatable;
+  extend require impls ExpressibleByArrayLiteral;
 }
 ```
 
-> **TODO:** Document `extend [final] impl as I`, as adopted in
-> [p5337: Interface extension and `final impl` update](/proposals/p5337.md).
+With `extend require impls I`, an interface requires an `impl` to exist for `I`
+when implementing the containing interface. This requires writing at least two
+`impl`s to implement the containing interface. When the two interfaces are
+tightly coupled, it's possible to have one interface gain the members of and
+_provide_ an implementation for the other interface instead, with
+`extend impl as`.
+
+Just as when used in a class, the implied `Self` between `extend impl` and `as`
+must be omitted.
+
+When an interface `A` contains `extend impl as B`, implementing `A` will require
+writing an implementation of all members of `B` inside the `impl` of `A`. And
+anything that implements `A` will implicitly also implement `B`, using the
+definitions from the `impl` of `A`. Here is an example:
+
+```carbon
+interface A {
+  let T:! type;
+  fn F();
+  fn G();
+}
+
+interface B {
+  extend impl as I;
+  fn H();
+}
+```
+
+This is equivalent to writing `B` as:
+
+```carbon
+interface B {
+  let T:! type;
+  fn F();
+  fn G();
+  fn H();
+}
+impl forall [U:! B] U as A {
+  let T:! type = U.(B.T);
+  fn F() = U.(B.F);
+  fn G() = U.(B.G);
+}
+```
+
+The implied `impl` definition can be made `final` by writing the reference to
+`B` as `extend final impl as B`.
+
+Note that this is supported only in an `interface` and not in a named
+[`constraint`](#named-constraints).
 
 **Alternative considered:** The `extend` declarations are in the body of the
 `interface` definition instead of the header so we can use
@@ -1356,17 +1402,18 @@ interface ConvertibleTo(T:! type) { ... }
 // A type can only implement `PreferredConversion` once.
 interface PreferredConversion {
   let AssociatedFacet:! type;
-  // `extend` is in the body of an `interface`
+  // `extend require impls` is in the body of an `interface`
   // definition. This allows extending an expression
   // that uses an associated facet.
-  extend ConvertibleTo(AssociatedFacet);
+  extend require impls ConvertibleTo(AssociatedFacet);
 }
 ```
 
-#### `extend` and `impl` with named constraints
+#### `extend require` with named constraints
 
-The `extend` declaration makes sense with the same meaning inside a
-[`constraint`](#named-constraints) definition, and so is also supported.
+The `extend` modifier on `require` makes sense with the same meaning inside a
+[`constraint`](#named-constraints) definition, and so is also supported (unlike
+`extend impl as`).
 
 ```carbon
 interface Media {
@@ -1377,8 +1424,8 @@ interface Job {
 }
 
 constraint Combined {
-  extend Media;
-  extend Job;
+  extend require impls Media;
+  extend require impls Job;
 }
 ```
 
@@ -1388,9 +1435,9 @@ This definition of `Combined` is equivalent to requiring both the `Media` and
 ```carbon
 // Equivalent
 constraint Combined {
-  require Self impls Media;
+  require impls Media;
   alias Play = Media.Play;
-  require Self impls Job;
+  require impls Job;
   alias Run = Job.Run;
 }
 ```
@@ -1429,7 +1476,7 @@ Conversely, an `interface` can extend a `constraint`:
 
 ```carbon
 interface MovieCodec {
-  extend Combined;
+  extend require impls Combined;
 
   fn Load[ref self: Self](filename: String);
 }
@@ -1440,9 +1487,9 @@ equivalent to:
 
 ```carbon
 interface MovieCodec {
-  require Self impls Media;
+  require impls Media;
   alias Play = Media.Play;
-  require Self impls Job;
+  require impls Job;
   alias Run = Job.Run;
 
   fn Load[ref self: Self](filename: String);
@@ -3905,7 +3952,7 @@ Example:
 // In the Carbon standard library
 interface DefaultConstructible {
   // Types must be sized to be default constructible.
-  require Self impls Sized;
+  require impls Sized;
   fn Default() -> Self;
 }
 
@@ -5079,9 +5126,9 @@ An interface or named constraint may be forward declared subject to these rules:
 -   Only the first declaration may have an access-control keyword.
 -   An incomplete interface or named constraint may be used as constraints in
     declarations of types, functions, interfaces, or named constraints. This
-    includes an `require` or `extend` declaration inside an interface or named
-    constraint, but excludes specifying the values for associated constants
-    because that would involve name lookup into the incomplete constraint.
+    includes a `require` declaration inside an interface or named constraint,
+    but excludes specifying the values for associated constants because that
+    would involve name lookup into the incomplete constraint.
 -   An attempt to define the body of a generic function using an incomplete
     interface or named constraint in its signature is illegal.
 -   An attempt to call a generic function using an incomplete interface or named
@@ -5553,7 +5600,7 @@ interface TotalOrder {
   fn TotalLess[self: Self](right: Self) -> bool;
   // ❌ Illegal: May not provide definition
   //             for required interface.
-  require Self impls PartialOrder {
+  require impls PartialOrder {
     fn PartialLess[self: Self](right: Self) -> bool {
       return self.TotalLess(right);
     }
@@ -5655,7 +5702,7 @@ as in:
 
 ```carbon
 interface Iterable {
-  require Self impls Equatable;
+  require impls Equatable;
   // ...
 }
 ```
@@ -5688,12 +5735,15 @@ says that if `Self` implements `CommonTypeWith(T)`, then `T` must implement
 
 A `require`...`impls` constraint in an `interface`, or `constraint`, definition
 must still use `Self` in some way. It can be an argument to either the
-[type](#parameterized-types) or [interface](#parameterized-interfaces). For
-example:
+[type](#parameterized-types) or [interface](#parameterized-interfaces). The
+`Self` can be omitted and will be implied as the type between `require impls`.
+For example:
 
+-   ✅ Allowed: `require impls Equatable`
 -   ✅ Allowed: `require Self impls Equatable`
 -   ✅ Allowed: `require Vector(Self) impls Equatable`
 -   ✅ Allowed: `require i32 impls CommonTypeWith(Self)`
+-   ✅ Allowed: `require impls CommonTypeWith(Self)`
 -   ✅ Allowed: `require Self impls CommonTypeWith(Self)`
 -   ❌ Error: `require i32 impls Equatable`
 -   ❌ Error: `require T impls Equatable` where `T` is some parameter to the
@@ -5772,7 +5822,7 @@ interface A(T:! type) {
   let Result:! type;
 }
 interface B(T:! type) {
-  require Self impls A(T) where .Result == i32;
+  require impls A(T) where .Result == i32;
 }
 ```
 
@@ -5809,9 +5859,9 @@ type implements an interface, as in this example:
 
 ```carbon
 interface A { }
-interface B { require Self impls A; }
-interface C { require Self impls B; }
-interface D { require Self impls C; }
+interface B { require impls A; }
+interface C { require impls B; }
+interface D { require impls C; }
 
 fn RequiresA[T:! A](x: T);
 fn RequiresC[T:! C](x: T);