From 2103fd4fc713fc5e5d0f7502daaac1103cce2ab0 Mon Sep 17 00:00:00 2001 From: Josh L Date: Thu, 31 Aug 2023 19:49:51 +0000 Subject: [PATCH 1/4] Clarify some points in Member Access design --- docs/design/expressions/member_access.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/design/expressions/member_access.md b/docs/design/expressions/member_access.md index 6b4c7f85e9ac7..01e3fb0d595eb 100644 --- a/docs/design/expressions/member_access.md +++ b/docs/design/expressions/member_access.md @@ -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) @@ -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: @@ -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(); } @@ -358,7 +358,7 @@ 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 @@ -378,8 +378,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(); } From 36b979ed5a58a67d00cd84de91e9837fb4d040a7 Mon Sep 17 00:00:00 2001 From: Josh L Date: Thu, 31 Aug 2023 20:25:38 +0000 Subject: [PATCH 2/4] Update with decision from #2200. --- docs/design/expressions/member_access.md | 32 ++++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/design/expressions/member_access.md b/docs/design/expressions/member_access.md index 01e3fb0d595eb..3ded3731bf664 100644 --- a/docs/design/expressions/member_access.md +++ b/docs/design/expressions/member_access.md @@ -336,22 +336,22 @@ 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 { + // 🤷 Not yet decided. + return x.field; +} +``` + +In addition, the lookup will be performed again when `T` is known. **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 From 79797bd7ffce783bf36a0f767cb233df088882b9 Mon Sep 17 00:00:00 2001 From: Josh L Date: Thu, 31 Aug 2023 20:27:24 +0000 Subject: [PATCH 3/4] Checkpoint progress. --- docs/design/expressions/member_access.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/expressions/member_access.md b/docs/design/expressions/member_access.md index 3ded3731bf664..bd0c58a7bcf33 100644 --- a/docs/design/expressions/member_access.md +++ b/docs/design/expressions/member_access.md @@ -346,7 +346,7 @@ class TemplateWrapper(template T:! type) { var field: T; } fn G[template T:! type](x: TemplateWrapper(T)) -> T { - // 🤷 Not yet decided. + // ✅ Allowed, finds `TemplateWrapper(T).field`. return x.field; } ``` From 631cd7f938627ce7af5d3f650dbf703b4e90acf6 Mon Sep 17 00:00:00 2001 From: Josh L Date: Thu, 31 Aug 2023 21:55:19 +0000 Subject: [PATCH 4/4] Add example based on suggestion --- docs/design/expressions/member_access.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/docs/design/expressions/member_access.md b/docs/design/expressions/member_access.md index bd0c58a7bcf33..027d8b7726a8f 100644 --- a/docs/design/expressions/member_access.md +++ b/docs/design/expressions/member_access.md @@ -351,7 +351,29 @@ fn G[template T:! type](x: TemplateWrapper(T)) -> T { } ``` -In addition, the lookup will be performed again when `T` is known. +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