Selaa lähdekoodia

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>
josh11b 1 vuosi sitten
vanhempi
sitoutus
8c4ff33cb1
1 muutettua tiedostoa jossa 327 lisäystä ja 0 poistoa
  1. 327 0
      proposals/p5366.md

+ 327 - 0
proposals/p5366.md

@@ -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.