Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clarify some points in Member Access design #3177

Merged
merged 4 commits into from
Sep 6, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading