mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-04 22:02:52 +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`
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
# Clarify rules around `Self` and `.Self`
|
||||
|
||||
<!--
|
||||
Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
Exceptions. See /LICENSE for license information.
|
||||
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
-->
|
||||
|
||||
[Pull request](https://github.com/carbon-language/carbon-lang/pull/2107)
|
||||
|
||||
<!-- toc -->
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Abstract](#abstract)
|
||||
- [Problem](#problem)
|
||||
- [Background](#background)
|
||||
- [Proposal](#proposal)
|
||||
- [Details](#details)
|
||||
- [Rationale](#rationale)
|
||||
- [Alternatives considered](#alternatives-considered)
|
||||
- [`Self` not a keyword](#self-not-a-keyword)
|
||||
- [Make `Self` a member of all types](#make-self-a-member-of-all-types)
|
||||
- [`where` operator could be associative](#where-operator-could-be-associative)
|
||||
|
||||
<!-- tocstop -->
|
||||
|
||||
## Abstract
|
||||
|
||||
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.
|
||||
|
||||
## Problem
|
||||
|
||||
There were a number of gaps found in the design when @zygoloid went to implement
|
||||
these features in [the explorer codebase](/explorer), for example
|
||||
[#1311: Basic support for `.Self` within `:!` bindings and `where` expressions](https://github.com/carbon-language/carbon-lang/pull/1311).
|
||||
|
||||
## Background
|
||||
|
||||
`Self` was introduced for interfaces and implementations in
|
||||
[#524: Generics overview](https://github.com/carbon-language/carbon-lang/pull/524)
|
||||
and
|
||||
[#553: Generics details part 1](https://github.com/carbon-language/carbon-lang/pull/553).
|
||||
`Self` was introduced for class types and methods in
|
||||
[#494: Method syntax](https://github.com/carbon-language/carbon-lang/issues/494)
|
||||
and
|
||||
[#722: Nominal classes and methods](https://github.com/carbon-language/carbon-lang/pull/722).
|
||||
|
||||
Constraints using `where` and `.Self` were introduced in
|
||||
[#818: Constraints for generics (generics details 3)](https://github.com/carbon-language/carbon-lang/pull/818).
|
||||
The use of `where` to set associated types was introduced in
|
||||
[#1013: Generics: Set associated constants using `where` constraints](https://github.com/carbon-language/carbon-lang/pull/1013).
|
||||
|
||||
The type of `.Self` and where it would be introduced grammatically was discussed
|
||||
[#generics channel on Discord on 2022-06-07](https://discord.com/channels/655572317891461132/708431657849585705/1013904969335836732).
|
||||
|
||||
## Proposal
|
||||
|
||||
This proposal implements a number of changes and clarifications about the use of
|
||||
`Self` and `.Self` with generics:
|
||||
|
||||
- `Self` is now a keyword, and may not be used as an identifier even in
|
||||
contexts where `Self` has no meaning. If `Self` is used in a C++ API, Carbon
|
||||
will use the same mechanism for interop as other Carbon keywords.
|
||||
- Clarify that `Self` always refers to the current type, even for virtual
|
||||
functions implemented in a derived class, not the type implementing the
|
||||
method.
|
||||
- `Self` in an `impl` declaration may be used after the `as` to refer to the
|
||||
type being implemented. This could be the type named before `as` when
|
||||
specified, otherwise it is the enclosing type.
|
||||
- Clarify that `.Self` is legal after `:!` and `where`, as long as it only
|
||||
refers to a single type variable.
|
||||
- Specify the type of `.Self` as `Type` after `:!`, or `MyConstraint` after
|
||||
`MyConstraint where`.
|
||||
|
||||
In addition, this proposal specifies that `where` is not an associative
|
||||
operator.
|
||||
|
||||
## Details
|
||||
|
||||
`Self` was added as a keyword to
|
||||
[`docs/design/lexical_conventions/words.md`](/docs/design/lexical_conventions/words.md).
|
||||
The other rules were added to
|
||||
[`docs/design/classes.md`](/docs/design/classes.md) and
|
||||
[`docs/design/generics/details.md`](/docs/design/generics/details.md).
|
||||
|
||||
## Rationale
|
||||
|
||||
This proposal fills in gaps with an aim to make things consistent and simplify
|
||||
by having fewer rules where possible. For example, the rule saying that it is an
|
||||
error if `.Self` could mean two different things is consistent with other name
|
||||
lookup rules, such as those from
|
||||
[#989: Member access expressions](https://github.com/carbon-language/carbon-lang/pull/989)
|
||||
and
|
||||
[#2070: Always `==` not `=` in `where` clauses](https://github.com/carbon-language/carbon-lang/pull/2070).
|
||||
Simplicity benefits Carbon's
|
||||
[language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem)
|
||||
and consistency leads to
|
||||
[code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write).
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
### `Self` not a keyword
|
||||
|
||||
An alternative considered was forbidding identifiers to be equal to `Self`. The
|
||||
big concern with that approach is that we would need some other way to
|
||||
interoperate with C++ code, particularly classes, that had a `Self` member. If
|
||||
we were adding `Self` to the language later as part of evolution, we would make
|
||||
it a keyword. That would allow us to use the same evolution strategy as other
|
||||
keywords -- we would automatically update the code to change existing uses of
|
||||
`Self` to the raw identifier syntax.
|
||||
|
||||
Note that at this time
|
||||
[no raw identifier syntax has been approved](https://github.com/carbon-language/carbon-lang/pull/93),
|
||||
but Rust uses a `r#` prefix. If Carbon used the same syntax, existing uses of
|
||||
`Self` would be changed to `r#Self`, and so `r#Self` should still be a legal
|
||||
identifier.
|
||||
|
||||
### Make `Self` a member of all types
|
||||
|
||||
We considered making `Self` a member of all types. From this uses of `Self` and
|
||||
`.Self` would follow naturally. It would have other consequences as well:
|
||||
|
||||
- `T.Self == T` for all types `T`.
|
||||
- `x.Self`, where `x` is a non-type value with type `T`, would be `T`. This is
|
||||
because under the normal member-access rules, since `Self` is not a member
|
||||
of `x`, it would look in `T` and find `T.Self`.
|
||||
|
||||
This raised the question of whether `Self` is a member of type-of-types like
|
||||
`Type`. That would seem to introduce an ambiguity for `i32.Self`. Furthermore,
|
||||
using `x.Self` to get the type of `x` seemed tricky, it would be better to have
|
||||
something that used the word "type" to do that.
|
||||
|
||||
Since [`Self` is a keyword](#self-not-a-keyword), we don't need to make it
|
||||
follow the normal member-access rules. So we instead only define what it means
|
||||
in places where we have a use case.
|
||||
|
||||
This was discussed
|
||||
[on 2022-08-29 in the #typesystem channel in Discord](https://discord.com/channels/655572317891461132/708431657849585705/1013904969335836732).
|
||||
|
||||
### `where` operator could be associative
|
||||
|
||||
We considered making the `where` operator associative, since an expression like
|
||||
|
||||
```
|
||||
Interface1 where .AssocType1 is Interface2 where .AssocType2 == i32
|
||||
```
|
||||
|
||||
would more usefully be interpreted as:
|
||||
|
||||
```
|
||||
Interface1 where .AssocType1 is (Interface2 where .AssocType2 == i32)
|
||||
```
|
||||
|
||||
than the alternative. However, this is expected to be a rare case and so it
|
||||
seemed much friendlier to humans to require parentheses or a separate named
|
||||
constraint declaration. This way they can easily visually disambiguate how it is
|
||||
interpreted without having to remember a rule that won't commonly be relevant.
|
||||
|
||||
This was discussed in
|
||||
[the #syntax channel on Discord on 2022-05-27](https://discord.com/channels/655572317891461132/709488742942900284/979869282903130153)
|
||||
and
|
||||
[the weekly sync meeting on 2022-06-01](https://docs.google.com/document/d/1dwS2sJ8tsN3LwxqmZSv9OvqutYhP71dK9Dmr1IXQFTs/edit?resourcekey=0-NxBWgL9h05yD2GOR3wUisg#heading=h.qarzfirrcrgf).
|
||||
Reference in New Issue
Block a user