mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
The name of an impl in class scope (#5366)
```
class C {
impl as I;
}
```
is redeclared
```
impl C.(as I)
```
for purposes of `match_first`/`impl_priority` blocks and definitions.
---------
Co-authored-by: Josh L <josh11b@users.noreply.github.com>
Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
This commit is contained in:
co-authored by
Josh L
Chandler Carruth
parent
32e68cfb5b
commit
8c4ff33cb1
@@ -0,0 +1,327 @@
|
||||
# The name of an `impl` in `class` scope
|
||||
|
||||
<!--
|
||||
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/5366)
|
||||
|
||||
<!-- toc -->
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Abstract](#abstract)
|
||||
- [Problem](#problem)
|
||||
- [Background](#background)
|
||||
- [Proposal](#proposal)
|
||||
- [Details](#details)
|
||||
- [Optional `Self` before `as`](#optional-self-before-as)
|
||||
- [Rationale](#rationale)
|
||||
- [Alternatives considered](#alternatives-considered)
|
||||
- [Use semantic match for the scope](#use-semantic-match-for-the-scope)
|
||||
|
||||
<!-- tocstop -->
|
||||
|
||||
## Abstract
|
||||
|
||||
```
|
||||
class C {
|
||||
impl as I;
|
||||
}
|
||||
```
|
||||
|
||||
is redeclared
|
||||
|
||||
```
|
||||
impl C.(as I)
|
||||
```
|
||||
|
||||
for purposes of `match_first`/`impl_priority` blocks and definitions.
|
||||
|
||||
## Problem
|
||||
|
||||
An `impl` declaration can be declared in `class` scope:
|
||||
|
||||
```
|
||||
class C {
|
||||
alias T = bool;
|
||||
impl as As(T);
|
||||
}
|
||||
```
|
||||
|
||||
or in file scope:
|
||||
|
||||
```
|
||||
class C {
|
||||
alias T = bool;
|
||||
}
|
||||
alias T = i32;
|
||||
impl C as As(T);
|
||||
```
|
||||
|
||||
These `impl` declarations need to be named so they may be redeclared in a
|
||||
definition or
|
||||
[`match_first`/`impl_priority` block](/docs/design/generics/details.md#prioritization-rule).
|
||||
Under the current rules introduced in
|
||||
[proposal #1084](https://github.com/carbon-language/carbon-lang/pull/1084) and
|
||||
modified in [#3763](https://github.com/carbon-language/carbon-lang/pull/3763),
|
||||
an `impl` redeclaration must match syntactically, and that only works if the
|
||||
redeclaration enters the same scope as the original declaration.
|
||||
|
||||
This problem is demonstrated in the example above. We need some indication
|
||||
whether to lookup `T` in the file or the class scope, otherwise these both would
|
||||
be redeclared as `impl C as As(T)`.
|
||||
|
||||
```carbon
|
||||
class C(T:! type) {
|
||||
class E {}
|
||||
impl E as I(C(T), E);
|
||||
}
|
||||
|
||||
// No ability to do syntactic match
|
||||
// C(T) does not match C(T:! type)
|
||||
// C(T).E does not match E
|
||||
impl forall [T:! type] C(T).E as I(C(T), C(T).E);
|
||||
```
|
||||
|
||||
## Background
|
||||
|
||||
The need for forward declarations of entities comes from the
|
||||
[information accumulation principle](/docs/project/principles/information_accumulation.md).
|
||||
|
||||
[Leads issue #1132](https://github.com/carbon-language/carbon-lang/issues/1132)
|
||||
defined the initial rules for matching forward declarations to their
|
||||
definitions. Those rules were partially incorporated into the design by
|
||||
[proposal #1084, "Generics details 9: forward declarations"](/proposals/p1084.md).
|
||||
|
||||
A replacement approach was
|
||||
[discussed on 2024-03-11](https://docs.google.com/document/d/1s3mMCupmuSpWOFJGnvjoElcBIe2aoaysTIdyczvKX84/edit?resourcekey=0-G095Wc3sR6pW1hLJbGgE0g&tab=t.0#heading=h.p69b78lovqb7)
|
||||
and mentioned in [proposal #3763](/proposals/p3763.md#redeclarations). This is
|
||||
the approach of syntactic matching and re-entering the same scope. The syntax
|
||||
adopted by this proposal was first suggested in those.
|
||||
|
||||
[Proposal #3762: Merging forward declarations](https://github.com/carbon-language/carbon-lang/pull/3762)
|
||||
and
|
||||
[proposal #3980: Singular `extern` declarations](https://github.com/carbon-language/carbon-lang/pull/3980)
|
||||
refined the rules for forward declarations, including the rules for `extern`
|
||||
declarations.
|
||||
|
||||
[Leads issue #5251: `impl` declarations in a generic class context](https://github.com/carbon-language/carbon-lang/issues/5251)
|
||||
is (pending resolution) saying that an `impl` declaration in class scope must
|
||||
use `Self` in a deducible position.
|
||||
|
||||
## Proposal
|
||||
|
||||
An `impl` declaration is associated with the scope it is first declared in, and
|
||||
can only be redeclared in that scope, matching all other declarations. Consider
|
||||
how a function is redeclared outside the scope of a class in which it was
|
||||
originally defined:
|
||||
|
||||
```
|
||||
class Z(T:! type) {
|
||||
// Forward declaration of a function.
|
||||
fn F();
|
||||
}
|
||||
|
||||
// Definition of the function that was forward declared.
|
||||
fn Z(T:! type).F() { ... }
|
||||
```
|
||||
|
||||
To redeclare an `impl` after the end of the scope it was declared in, that scope
|
||||
may be re-entered as part of the `impl` redeclaration, in the same way, except
|
||||
with parentheses around the name of the `impl`, as in:
|
||||
|
||||
```carbon
|
||||
class X {
|
||||
// Forward declaration that `X impls Y`:
|
||||
impl as Y;
|
||||
}
|
||||
|
||||
// Definition of the `impl` that `X impls Y`
|
||||
// that was forward declared in `X`:
|
||||
impl X.(as Y) { ... }
|
||||
```
|
||||
|
||||
More generally, in a `class` scope
|
||||
|
||||
```carbon
|
||||
class __X__ {
|
||||
impl __Y__;
|
||||
}
|
||||
```
|
||||
|
||||
is redeclared `impl __X__.(__Y__)` outside of that `class` scope. Here `__X__`
|
||||
is whatever sequence of tokens appears in that position in the `class`
|
||||
declaration, and `__Y__` is the sequence of tokens in the `impl` declaration.
|
||||
These declarations are matched syntactically, and anything in `__Y__` is
|
||||
interpreted as if it appeared in the scope of `__X__` like it was first
|
||||
declared.
|
||||
|
||||
## Details
|
||||
|
||||
Here are some examples of this in practice:
|
||||
|
||||
```carbon
|
||||
class F {}
|
||||
|
||||
class A {
|
||||
impl as As(i32);
|
||||
impl Self as As(bool);
|
||||
impl A as As(f64);
|
||||
|
||||
impl F as As(A);
|
||||
|
||||
class G {}
|
||||
impl G as As(A);
|
||||
}
|
||||
|
||||
impl A.(as As(i32)) { ... }
|
||||
impl A.(Self as As(bool)) { ... }
|
||||
impl A.(A as As(f64)) { ... }
|
||||
impl A.(F as As(A)) { ... }
|
||||
impl A.(G as As(A)) { ... }
|
||||
```
|
||||
|
||||
Parameterized classes:
|
||||
|
||||
```carbon
|
||||
class B(T:! type) {
|
||||
impl B(i32) as AddWith(A(T));
|
||||
}
|
||||
|
||||
impl B(T:! type).(B(i32) as AddWith(A(T))) { ... }
|
||||
```
|
||||
|
||||
Parameterized impl:
|
||||
|
||||
```carbon
|
||||
class C {
|
||||
impl forall [T:! type] as I(T);
|
||||
}
|
||||
|
||||
impl C.(forall [T:! type] as I(T));
|
||||
```
|
||||
|
||||
Putting the `forall` inside the parens both simplifies the syntactic match and
|
||||
means that any mentions of names in that clause are in scope. For example:
|
||||
|
||||
```
|
||||
class D(T:! type) {
|
||||
class E {}
|
||||
impl forall [U:! J(E)] as I(U);
|
||||
}
|
||||
|
||||
impl D(T:! type).(forall [U:! J(E)] as I(U));
|
||||
```
|
||||
|
||||
Notice how the `E` in the constraint on `U` is found in the `D(T:! type)` scope.
|
||||
|
||||
Nested classes:
|
||||
|
||||
```
|
||||
class C1 {
|
||||
class C2 {
|
||||
class C3 {
|
||||
impl as I;
|
||||
class C4 {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Defining impl that was forward declared within the `C3` definition:
|
||||
impl C1.C2.C3.(as I) { ... }
|
||||
|
||||
// Defining a new impl:
|
||||
impl C1.C2.C3.C4 as I { ... }
|
||||
```
|
||||
|
||||
Notice that we don't know which form will be used until we see:
|
||||
|
||||
- the open paren (`(`) after a `.`,
|
||||
- a parameter pattern,
|
||||
- a non-parameter argument, or
|
||||
- the `as`.
|
||||
|
||||
### Optional `Self` before `as`
|
||||
|
||||
The normalization to add `Self` before `as` when that type is omitted before
|
||||
performing syntactic match is preserved from proposals
|
||||
[#1084](https://github.com/carbon-language/carbon-lang/pull/1084) and
|
||||
[#3763](https://github.com/carbon-language/carbon-lang/pull/3763). Note that the
|
||||
`Self` is inserted in the parentheses when the `as` appears there. So:
|
||||
|
||||
```
|
||||
class A {
|
||||
// First impl declaration is equivalent
|
||||
// to `impl Self as As(i32);`
|
||||
impl as As(i32);
|
||||
|
||||
// Second impl declaration.
|
||||
impl Self as As(bool);
|
||||
}
|
||||
|
||||
// Redeclaration of the first impl declaration.
|
||||
impl A.(Self as As(i32)) { ... }
|
||||
|
||||
// Since this is equivalent to
|
||||
// `impl A.(Self as As(bool)) { ... }`, is a
|
||||
// valid redeclaration of the second impl
|
||||
// declaration.
|
||||
impl A.(as As(bool)) { ... }
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
The need for this proposal comes from supporting forward declarations for the
|
||||
[information accumulation principle](/docs/project/principles/information_accumulation.md).
|
||||
The specifics of this proposal were chosen comply with these
|
||||
[Carbon goals](/docs/project/goals.md):
|
||||
|
||||
- Unambiguous rules that are simple to implement benefit
|
||||
[language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem).
|
||||
- [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write)
|
||||
benefits by having rules that are simple to state and validate. The
|
||||
syntactic match rule means redeclarations are close to a copy of the
|
||||
original declaration, which makes authoring straightforward and automatable.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
### Use semantic match for the scope
|
||||
|
||||
[Leads issue #5367: `impl` in `class` redeclaration syntax with parameterization](https://github.com/carbon-language/carbon-lang/issues/5367)
|
||||
considered an alternative where
|
||||
|
||||
```carbon
|
||||
// Alternative
|
||||
impl forall [T:! type] B(T).(as I) { ... }
|
||||
```
|
||||
|
||||
instead of:
|
||||
|
||||
```carbon
|
||||
// This proposal
|
||||
impl B(T:! type).(as I) { ... }
|
||||
```
|
||||
|
||||
It avoided putting a `forall` clause inside the parentheses, but that both
|
||||
greatly limited the syntactic matching that we could do, and meant examples
|
||||
like:
|
||||
|
||||
```carbon
|
||||
// This proposal
|
||||
impl D(T:! type).(forall [U:! J(E)] as I(U));
|
||||
```
|
||||
|
||||
had to instead be written with more qualfiers:
|
||||
|
||||
```carbon
|
||||
// Alternative
|
||||
impl forall [T:! type, U:! J(D(T).E)] D(T).(as I(U));
|
||||
```
|
||||
|
||||
It is not just helpful for the compiler: being able to make fewer and more
|
||||
mechanical changes after copy-pasting the `impl` declaration to make the
|
||||
redeclaration makes authoring the code easier, and simplifies tooling to
|
||||
automate the process.
|
||||
Reference in New Issue
Block a user