Clarify rules around Self and .Self (#2107)

A number of smaller changes grouped together in one proposal:

-   Make `Self` a keyword.
-   Clarify that `Self` refers to the current type in a base class and in impl declarations.
-   Clarify when `.Self` is legal, and what type it has.
-   Also specify that `where` is not an associative operator.
This commit is contained in:
josh11b
2022-09-08 13:04:58 -07:00
committed by GitHub
parent 6a9326f9e7
commit e4595deb4b
4 changed files with 275 additions and 10 deletions
+56 -4
View File
@@ -31,7 +31,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- [Operations performed field-wise](#operations-performed-field-wise)
- [Nominal class types](#nominal-class-types)
- [Forward declaration](#forward-declaration)
- [Self](#self)
- [`Self`](#self)
- [Construction](#construction)
- [Assignment](#assignment)
- [Member functions](#member-functions)
@@ -46,6 +46,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- [Virtual methods](#virtual-methods)
- [Virtual override keywords](#virtual-override-keywords)
- [Subtyping](#subtyping)
- [`Self` refers to the current type](#self-refers-to-the-current-type)
- [Constructors](#constructors)
- [Partial facet](#partial-facet)
- [Usage](#usage)
@@ -730,7 +731,7 @@ class GraphNode {
**Open question:** What is specifically allowed and forbidden with an incomplete
type has not yet been decided.
### Self
### `Self`
A `class` definition may provisionally include references to its own name in
limited ways. These limitations arise from the type not being complete until the
@@ -743,8 +744,8 @@ class IntListNode {
}
```
An equivalent definition of `IntListNode`, since `Self` is an alias for the
current type, is:
An equivalent definition of `IntListNode`, since the `Self` keyword is an alias
for the current type, is:
```
class IntListNode {
@@ -759,6 +760,7 @@ class IntListNode {
class IntList {
class IntListNode {
var data: i32;
// `Self` is `IntListNode`, not `IntList`.
var next: Self*;
}
var first: IntListNode*;
@@ -1199,6 +1201,55 @@ abstract class ExtensibleBase { ... }
class ExactlyExtensible extends ExtensibleBase { ... }
```
#### `Self` refers to the current type
Note that `Self` in a class definition means "the current type being defined"
not "the type implementing this method." To implement a method in a derived
class that uses `Self` in the declaration in the base class, only the type of
`me` should change:
```
base class B1 {
virtual fn F[me: Self](x: Self) -> Self;
// Means exactly the same thing as:
// virtual fn F[me: B1](x: B1) -> B1;
}
class D1 extends B1 {
// ❌ Illegal:
// impl fn F[me: Self](x: Self) -> Self;
// since that would mean the same thing as:
// impl fn F[me: Self](x: D1) -> D1;
// and `D1` is a different type than `B1`.
// ✅ Allowed: Parameter and return types
// of `F` match declaration in `B1`.
impl fn F[me: Self](x: B1) -> B1;
// Or: impl fn F[me: D1](x: B1) -> B1;
}
```
The exception is when there is a [subtyping relationship](#subtyping) such that
it would be legal for a caller using the base classes signature to actually be
calling the derived implementation, as in:
```
base class B2 {
virtual fn Clone[me: Self]() -> Self*;
// Means exactly the same thing as:
// virtual fn Clone[me: B2]() -> B2*;
}
class D2 extends B2 {
// ✅ Allowed
impl fn Clone[me: Self]() -> Self*;
// Means the same thing as:
// impl fn Clone[me: D2]() -> D2*;
// which is allowed since `D2*` is a
// subtype of `B2*`.
}
```
#### Constructors
Like for classes without inheritance, constructors for a derived class are
@@ -2057,3 +2108,4 @@ the type of `U.x`."
- [#777: Inheritance](https://github.com/carbon-language/carbon-lang/pull/777)
- [#981: Implicit conversions for aggregates](https://github.com/carbon-language/carbon-lang/pull/981)
- [#1154: Destructors](https://github.com/carbon-language/carbon-lang/pull/1154)
- [#2107: Clarify rules around `Self` and `.Self`](https://github.com/carbon-language/carbon-lang/pull/2107)
+49 -6
View File
@@ -223,7 +223,8 @@ have two methods:
```
interface Vector {
// Here `Self` means "the type implementing this interface".
// Here the `Self` keyword means
// "the type implementing this interface".
fn Add[me: Self](b: Self) -> Self;
fn Scale[me: Self](v: f64) -> Self;
}
@@ -257,7 +258,8 @@ class Point {
var x: f64;
var y: f64;
impl as Vector {
// In this scope, "Self" is an alias for "Point".
// In this scope, the `Self` keyword is an
// alias for `Point`.
fn Add[me: Self](b: Self) -> Self {
return {.x = a.x + b.x, .y = a.y + b.y};
}
@@ -364,7 +366,8 @@ class Point2 {
var y: f64;
external impl as Vector {
// In this scope, `Self` is an alias for `Point2`.
// In this scope, the `Self` keyword is an
// alias for `Point2`.
fn Add[me: Self](b: Self) -> Self {
return {.x = a.x + b.x, .y = a.y + b.y};
}
@@ -389,7 +392,8 @@ class Point3 {
}
external impl Point3 as Vector {
// In this scope, `Self` is an alias for `Point3`.
// In this scope, the `Self` keyword is an
// alias for `Point3`.
fn Add[me: Self](b: Self) -> Self {
return {.x = a.x + b.x, .y = a.y + b.y};
}
@@ -2065,6 +2069,18 @@ class DynamicArray(T:! Type) {
}
```
The keyword `Self` can be used after the `as` in an `impl` declaration as a
shorthand for the type being implemented, including in the `where` clause
specifying the values of associated types, as in:
```
external impl VeryLongTypeName as Add
// `Self` here means `VeryLongTypeName`
where .Result == Self {
...
}
```
**Alternatives considered:** See
[other syntax options considered in #731 for specifying associated types](/proposals/p0731.md#syntax-for-associated-constants).
In particular, it was deemed that
@@ -2269,8 +2285,9 @@ class Complex {
var imag: f64;
// Can implement this interface more than once
// as long as it has different arguments.
impl as EquatableWith(Complex) { ... }
impl as EquatableWith(f64) { ... }
// Same as: impl as EquatableWith(Complex) { ... }
impl as EquatableWith(Self) { ... }
}
```
@@ -2416,7 +2433,9 @@ type-of-type. Note that this expands the kinds of requirements that
type-of-types can have from just interface requirements to also include the
various kinds of constraints discussed later in this section. In addition, it
can introduce relationships between different type variables, such as that a
member of one is equal to the member of another.
member of one is equal to the member of another. The `where` operator is not
associative, so a type expression using multiple must use round parens `(`...`)`
to specify grouping.
**Comparison with other languages:** Both Swift and Rust use `where` clauses on
declarations instead of in the expression syntax. These happen after the type
@@ -2794,6 +2813,29 @@ constraint ContainerIsSlice {
Note that using the `constraint` approach we can name these constraints using
`Self` instead of `.Self`, since they refer to the same type.
The `.Self` construct follows these rules:
- `X :!` introduces `.Self:! Type`, where references to `.Self` are resolved
to `X`. This allows you to use `.Self` as an interface parameter as in
`X:! I(.Self)`.
- `A where` introduces `.Self:! A` and `.Foo` for each member `Foo` of `A`
- It's an error to reference `.Self` if it refers to more than one different
thing or isn't a type.
- You get the innermost, most-specific type for `.Self` if it is introduced
twice in a scope. By the previous rule, it is only legal if they all refer
to the same generic parameter.
So in `X:! A where ...`, `.Self` is introduced twice, after the `:!` and the
`where`. This is allowed since both times it means `X`. After the `:!`, `.Self`
has the type `Type`, which gets refined to `A` after the `where`. In contrast,
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
(InterfaceB where .B == .Self)](x: T);
```
#### Parameterized type implements interface
There are times when a function will pass a generic type parameter of the
@@ -5643,3 +5685,4 @@ parameter, as opposed to an associated type, as in `N:! u32 where ___ >= 2`.
- [#1144: Generic details 11: operator overloading](https://github.com/carbon-language/carbon-lang/pull/1144)
- [#1146: Generic details 12: parameterized types](https://github.com/carbon-language/carbon-lang/pull/1146)
- [#1327: Generics: `impl forall`](https://github.com/carbon-language/carbon-lang/pull/1327)
- [#2107: Clarify rules around `Self` and `.Self`](https://github.com/carbon-language/carbon-lang/pull/2107)
+1
View File
@@ -76,6 +76,7 @@ The following words are interpreted as keywords:
- `protected`
- `return`
- `returned`
- `Self`
- `then`
- `var`
- `virtual`