Skip to content

Commit

Permalink
Clarify some points in Member Access design (#3177)
Browse files Browse the repository at this point in the history
Follow-on to #3160. Incorporates decision about template specialization
from #2200.
  • Loading branch information
josh11b committed Sep 6, 2023
1 parent 9ac92ad commit dc394b6
Showing 1 changed file with 47 additions and 25 deletions.
72 changes: 47 additions & 25 deletions docs/design/expressions/member_access.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- [Values](#values)
- [Facet binding](#facet-binding)
- [Compile-time bindings](#compile-time-bindings)
- [Lookup ambiguity](#lookup-ambiguity)
- [Lookup ambiguity](#lookup-ambiguity)
- [`impl` lookup](#impl-lookup)
- [`impl` lookup for simple member access](#impl-lookup-for-simple-member-access)
- [`impl` lookup for compound member access](#impl-lookup-for-compound-member-access)
Expand Down Expand Up @@ -254,10 +254,9 @@ fn PrintPointTwice() {

### Facet binding

If any of the above lookups would search for members of a
[facet binding](/docs/design/generics/terminology.md#facet-binding) `T:! C`, it
searches the facet `T as C` instead, treating the facet binding as an
[archetype](/docs/design/generics/terminology.md#archetype).
A search for members of a facet binding `T:! C` treats the facet binding as an
[archetype](/docs/design/generics/terminology.md#archetype), and finds members
of the facet `T` of facet type `C`.

For example:

Expand Down Expand Up @@ -301,7 +300,8 @@ interface Renderable {
fn Draw[self: Self]();
}
fn DrawChecked[T:! Renderable](c: T) {
// `Draw` resolves to `Renderable.Draw`.
// `Draw` resolves to `(T as Renderable).Draw` or
// `T.(Renderable.Draw)`.
c.Draw();
}
Expand Down Expand Up @@ -336,29 +336,51 @@ fn CallsDrawTemplate(c: Cowboy) {
}
```

> **TODO:** The behavior of this code depends on whether we decide to allow
> class templates to be specialized:
>
> ```carbon
> class TemplateWrapper(template T:! type) {
> var field: T;
> }
> fn G[template T:! type](x: TemplateWrapper(T)) -> T {
> // 🤷 Not yet decided.
> return x.field;
> }
> ```
>
> If class specialization is allowed, then we cannot know the members of
> `TemplateWrapper(T)` without knowing `T`, so this first lookup will find
> nothing. In any case, the lookup will be performed again when `T` is known.
Since we have decided to forbid specialization of class templates, see
[proposal #2200: Template generics](https://github.com/carbon-language/carbon-lang/pull/2200),
the compiler can assume the body of a templated class will be the same for all
argument values:

```carbon
class TemplateWrapper(template T:! type) {
var field: T;
}
fn G[template T:! type](x: TemplateWrapper(T)) -> T {
// ✅ Allowed, finds `TemplateWrapper(T).field`.
return x.field;
}
```

In addition, the lookup will be performed again when `T` is known. This allows
cases where the lookup only succeeds for specific values of `T`:

```carbon
class HasField {
var field: i32;
}
class DerivingWrapper(template T:! type) {
extend base: T;
}
fn H[template T:! type](x: DerivingWrapper(T)) -> i32 {
// ✅ Allowed, but no name `field` found in template
// definition of `DerivingWrapper`.
return x.field;
}
fn CallH(a: DerivingWrapper(HasField),
b: DerivingWrapper(i32)) {
// ✅ Member `field` in base class found in instantiation.
var x: i32 = H(a);
// ❌ Error, no member `field` in type of `b`.
var y: i32 = H(b);
}
```

**Note:** All lookups are done from a context where the values of any symbolic
bindings that are in scope are unknown. Unlike for a template binding, the
actual value of a symbolic binding never affects the result of member
resolution.

##### Lookup ambiguity
#### Lookup ambiguity

Multiple lookups can be performed when resolving a member access expression with
a [template binding](#compile-time-bindings). We resolve this the same way as
Expand All @@ -378,8 +400,8 @@ interface Renderable {
}
fn DrawTemplate2[template T:! Renderable](c: T) {
// Member lookup finds `Renderable.Draw` and the `Draw`
// member of the actual deduced value of `T`, if any.
// Member lookup finds `(T as Renderable).Draw` and the
// `Draw` member of the actual deduced value of `T`, if any.
c.Draw();
}
Expand Down

0 comments on commit dc394b6

Please sign in to comment.