mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 21:30:12 +01:00
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:
+56
-4
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -76,6 +76,7 @@ The following words are interpreted as keywords:
|
||||
- `protected`
|
||||
- `return`
|
||||
- `returned`
|
||||
- `Self`
|
||||
- `then`
|
||||
- `var`
|
||||
- `virtual`
|
||||
|
||||
Reference in New Issue
Block a user