Skip to content

Commit 0baa74d

Browse files
danakjCarbonInfraBotzygoloid
authored
Type completeness in extend (#6395)
Define rules for `extend` declarations (`extend require`, `extend impl as`, `extend base`, `extend adapt`) that say the target scope they name must be complete at the point of the declaration. Define completeness for a facet type to include all interfaces and named constraints that provide unqualified name lookup through the facet type. --------- Co-authored-by: Carbon Infra Bot <[email protected]> Co-authored-by: Richard Smith <[email protected]>
1 parent ec8c999 commit 0baa74d

File tree

2 files changed

+630
-13
lines changed

2 files changed

+630
-13
lines changed

proposals/p6395.md

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
# Type completeness in extend
2+
3+
<!--
4+
Part of the Carbon Language project, under the Apache License v2.0 with LLVM
5+
Exceptions. See /LICENSE for license information.
6+
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
-->
8+
9+
[Pull request](https://github.com/carbon-language/carbon-lang/pull/6395)
10+
11+
<!-- toc -->
12+
13+
## Table of contents
14+
15+
- [Abstract](#abstract)
16+
- [Problem](#problem)
17+
- [Background](#background)
18+
- [Proposal](#proposal)
19+
- [Details](#details)
20+
- [Rationale](#rationale)
21+
- [Alternatives considered](#alternatives-considered)
22+
23+
<!-- tocstop -->
24+
25+
## Abstract
26+
27+
Require any target scopes in an `extend` declaration to be complete, since
28+
`extend` changes the scopes which are looked in for name lookup to include those
29+
named in the declaration.
30+
31+
## Problem
32+
33+
Proposal
34+
[#5168](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p5168.md)
35+
laid out rules for when a facet type needs to be identified or complete. When
36+
`require impls X` is written, then `X` must be identified. However it does not
37+
specify any rule for `extend require impls X`. And we lack completeness rules
38+
for other `extend` declarations, including `extend impl as X`, `extend base: X`,
39+
and `extend adapt X`.
40+
41+
An `extend` declaration always names one or more target entities which will be
42+
included in the search for names when looking in the enclosing scope of the
43+
`extend` declaration. In order to do name lookup into an entity, it must be
44+
complete to avoid poisoning names in the entity.
45+
46+
## Background
47+
48+
- Proposal
49+
[#5168](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p5168.md):
50+
Forward `impl` declaration of an incomplete interface
51+
- Proposal
52+
[#2760](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p2760.md):
53+
Consistent `class` and `interface` syntax
54+
- Proposal
55+
[#0777](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p0777.md):
56+
Inheritance
57+
58+
## Proposal
59+
60+
An `extend` declaration declares that the enclosing scope extends the scope
61+
associated with any target entity named in the declaration. That is to say name
62+
lookups into the enclosing scope will also look into the scopes which are
63+
nominated by the `extend` declaration. The `extend` declaration requires that
64+
the target scopes named in an `extend` declaration are complete, or if the
65+
target is a generic parameter, requires the type of the parameter to be
66+
complete.
67+
68+
The scope of a facet type formed by a `where` declaration extends the scope of
69+
its first operand. And the scope of a facet type formed by a `&` operator
70+
extends the scopes of both of its operands. In either case, the scope of the
71+
facet type is complete if every scope it extends is complete. The facet type
72+
`type` is associated with an empty scope and is complete.
73+
74+
| Facet type | Requirement |
75+
| ---------------------------- | ------------------------------------- |
76+
| `I` | Requires `I` is complete. |
77+
| `I & J` | Requires `I` and `J` are complete. |
78+
| `type where U impls J` | Requires `type` is complete. |
79+
| `I & (type where U impls J)` | Requires `I` and `type` are complete. |
80+
81+
## Details
82+
83+
To `extend` an entity `Y` with another `Z` means that name lookups into `Y` will
84+
also look into `Z`. Immediately after the `extend` operation, members of `Z`
85+
should also be found when doing name lookup into `Y`, both from outside and from
86+
inside the definition of `Y`. In order to be able to perform lookups into `Z`,
87+
we require that `extend` operations only target scopes that are complete.
88+
89+
This requirement functions recursively. Given an interface `B` that extends
90+
another interface `A`: By naming `A` in an extend declaration, we require `A` is
91+
complete. This provides that its entire definition is known, and thus its
92+
`extend` relationship to `B`. The `extend` relationship there also provides that
93+
`B` is complete.
94+
95+
If the target scope of an `extend` declaration is a generic parameter, its type
96+
must be complete where the `extend` declaration is written, as name lookups into
97+
the extended scope will look into the type of the generic parameter.
98+
99+
```carbon
100+
interface I {
101+
fn F();
102+
}
103+
104+
class C(T:! I) {
105+
extend base: T;
106+
// `F` names `T.F` here, found in `I`.
107+
fn G() { F(); }
108+
}
109+
```
110+
111+
As any generic parameter in the enclosing scope is replaced by a more specific
112+
value, extended scopes that depend on a generic parameter must remain complete.
113+
This includes forming a specific for the extended scope involving the parameter
114+
in order to surface any monomorphization errors in the resulting specific.
115+
116+
In the next example, the `extend` declaration in interface `A(N)` names a
117+
symbolic facet type which can produce monomorphization errors when a negative
118+
value is provided for `N`. When a more specific value for the target `B(N)` is
119+
provided, we require the specific value to be complete as well by forming the
120+
specific. A diagnostic error would be produced while checking `C(-1)` for
121+
completeness, as it requires `A(-1)` to be complete, which requires
122+
`B(array(i32, -1))` to be complete, and that contains an invalid type.
123+
124+
```carbon
125+
interface B(T:! type) {}
126+
127+
interface A(N:! i32) {
128+
// Requires `B(N)` to be complete.
129+
extend require impls B(array(i32, N)) {}
130+
}
131+
132+
class C(N! i32) {
133+
// Requires `A(N)` to be complete, which requires `B(N)` to be complete.
134+
extend impl as A(N);
135+
}
136+
137+
fn F() {
138+
// Requires `C(-1)` to be complete, which requires `A(-1)` to be complete, which requires `B(array(i32, -1))` to be complete.
139+
var c: C(-1);
140+
}
141+
```
142+
143+
These rules prohibit an `extend` declaration from naming its enclosing scope,
144+
since by being part of the definition of that scope, it is implied that the
145+
enclosing scope is not complete. This seems reasonable as all names available
146+
inside the enclosing interface or named constraint are already available or
147+
would conflict with the ones that are.
148+
149+
## Rationale
150+
151+
This is based on the principle of
152+
[Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write).
153+
For code to be easy to write, the rules need to be consistent. Once an `extend`
154+
declaration has been written, the names inside should become available through
155+
the enclosing scope immediately. If we allow an interface named in an `extend`
156+
declaration to be incomplete, then name lookup will fail ambiguously. Those same
157+
names may become valid later once the interface is defined.
158+
159+
## Alternatives considered
160+
161+
We considered not requiring that the scope named in an `extend` declaration be
162+
complete where it is written, but only when the enclosing scope is required to
163+
be complete. This is more flexible, allowing entities to be defined later in the
164+
program. However this does not allow the use of names from the target scope in
165+
the `extend` to be used from within the enclosing definition scope. They would
166+
not become available until the enclosing definition scope was closed and
167+
complete.
168+
169+
In particular, we want this to work:
170+
171+
```carbon
172+
interface Z {
173+
let X:! type;
174+
fn F() -> X;
175+
}
176+
class C {
177+
extend impl as Z where .X = () {
178+
// Names `X` directly.
179+
fn F() -> X;
180+
}
181+
182+
// Names `X` and `F` directly.
183+
fn G() -> X { return F(); }
184+
}
185+
186+
// Now `C` is complete, `C.X` and `C.F` are also available.
187+
fn H() -> C.X {
188+
return C.F();
189+
}
190+
```

0 commit comments

Comments
 (0)