|
@@ -13,10 +13,14 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
- [Overview](#overview)
|
|
- [Overview](#overview)
|
|
|
- [Member resolution](#member-resolution)
|
|
- [Member resolution](#member-resolution)
|
|
|
- [Package and namespace members](#package-and-namespace-members)
|
|
- [Package and namespace members](#package-and-namespace-members)
|
|
|
- - [Lookup within values](#lookup-within-values)
|
|
|
|
|
- - [Templates and generics](#templates-and-generics)
|
|
|
|
|
- - [Lookup ambiguity](#lookup-ambiguity)
|
|
|
|
|
|
|
+ - [Types and facets](#types-and-facets)
|
|
|
|
|
+ - [Values](#values)
|
|
|
|
|
+ - [Facet binding](#facet-binding)
|
|
|
|
|
+ - [Compile-time bindings](#compile-time-bindings)
|
|
|
|
|
+ - [Lookup ambiguity](#lookup-ambiguity)
|
|
|
- [`impl` lookup](#impl-lookup)
|
|
- [`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)
|
|
|
- [Instance binding](#instance-binding)
|
|
- [Instance binding](#instance-binding)
|
|
|
- [Non-instance members](#non-instance-members)
|
|
- [Non-instance members](#non-instance-members)
|
|
|
- [Non-vacuous member access restriction](#non-vacuous-member-access-restriction)
|
|
- [Non-vacuous member access restriction](#non-vacuous-member-access-restriction)
|
|
@@ -58,17 +62,19 @@ Compound member accesses allow specifying a qualified member name.
|
|
|
For example:
|
|
For example:
|
|
|
|
|
|
|
|
```carbon
|
|
```carbon
|
|
|
-package Widgets api;
|
|
|
|
|
-interface Widget {
|
|
|
|
|
|
|
+namespace Widgets;
|
|
|
|
|
+
|
|
|
|
|
+interface Widgets.Widget {
|
|
|
fn Grow[addr self: Self*](factor: f64);
|
|
fn Grow[addr self: Self*](factor: f64);
|
|
|
}
|
|
}
|
|
|
-class Cog {
|
|
|
|
|
|
|
+
|
|
|
|
|
+class Widgets.Cog {
|
|
|
var size: i32;
|
|
var size: i32;
|
|
|
fn Make(size: i32) -> Self;
|
|
fn Make(size: i32) -> Self;
|
|
|
extend impl as Widgets.Widget;
|
|
extend impl as Widgets.Widget;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-fn GrowSomeCogs() {
|
|
|
|
|
|
|
+fn Widgets.GrowSomeCogs() {
|
|
|
var cog1: Cog = Cog.Make(1);
|
|
var cog1: Cog = Cog.Make(1);
|
|
|
var cog2: Cog = cog1.Make(2);
|
|
var cog2: Cog = cog1.Make(2);
|
|
|
var cog_pointer: Cog* = &cog2;
|
|
var cog_pointer: Cog* = &cog2;
|
|
@@ -110,6 +116,19 @@ A member access expression is processed using the following steps:
|
|
|
The process of _member resolution_ determines which member `M` a member access
|
|
The process of _member resolution_ determines which member `M` a member access
|
|
|
expression is referring to.
|
|
expression is referring to.
|
|
|
|
|
|
|
|
|
|
+For a simple member access, the second operand is a word. If the first operand
|
|
|
|
|
+is a type, facet, package, or namespace, a search for the word is performed in
|
|
|
|
|
+the first operand. Otherwise, a search for the word is performed in the type of
|
|
|
|
|
+the first operand. In either case, the search must succeed. In the latter case,
|
|
|
|
|
+if the result is an instance member, then [instance binding](#instance-binding)
|
|
|
|
|
+is performed on the first operand.
|
|
|
|
|
+
|
|
|
|
|
+For a compound member access, the second operand is evaluated as a compile-time
|
|
|
|
|
+constant to determine the member being accessed. The evaluation is required to
|
|
|
|
|
+succeed and to result in a member of a type, interface, or non-type facet. If
|
|
|
|
|
+the result is an instance member, then [instance binding](#instance-binding) is
|
|
|
|
|
+always performed on the first operand.
|
|
|
|
|
+
|
|
|
### Package and namespace members
|
|
### Package and namespace members
|
|
|
|
|
|
|
|
If the first operand is a package or namespace name, the expression must be a
|
|
If the first operand is a package or namespace name, the expression must be a
|
|
@@ -136,57 +155,84 @@ fn CallMyFunction2() {
|
|
|
}
|
|
}
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-### Lookup within values
|
|
|
|
|
-
|
|
|
|
|
-When the first operand is not a package or namespace name, there are three
|
|
|
|
|
-remaining cases we wish to support:
|
|
|
|
|
|
|
+The first operand may also be the keyword `package`, as in `package.Foo`, to
|
|
|
|
|
+name the `Foo` member of the current package. This can be used to disambiguate
|
|
|
|
|
+between different `Foo` definitions, as in:
|
|
|
|
|
|
|
|
-- The first operand is a value, and lookup should consider members of the
|
|
|
|
|
- value's type.
|
|
|
|
|
-- The first operand is a type, and lookup should consider members of that
|
|
|
|
|
- type. For example, `i32.Least` should find the member constant `Least` of
|
|
|
|
|
- the type `i32`.
|
|
|
|
|
-- The first operand is a type-of-type, and lookup should consider members of
|
|
|
|
|
- that type-of-type. For example, `Addable.Add` should find the member
|
|
|
|
|
- function `Add` of the interface `Addable`. Because a type-of-type is a type,
|
|
|
|
|
- this is a special case of the previous bullet.
|
|
|
|
|
|
|
+```carbon
|
|
|
|
|
+// This defines `package.Foo`
|
|
|
|
|
+class Foo {}
|
|
|
|
|
+class Bar {
|
|
|
|
|
+ // This defines `Bar.Foo`, or equivalently `package.Bar.Foo`.
|
|
|
|
|
+ class Foo {}
|
|
|
|
|
+ fn F() {
|
|
|
|
|
+ // ✅ OK, `x` has type `Foo` from the outer scope.
|
|
|
|
|
+ var x: package.Foo = {};
|
|
|
|
|
+
|
|
|
|
|
+ // ❌ Error: ambiguous;
|
|
|
|
|
+ // `Foo` could mean `package.Foo` or `Bar.Foo`.
|
|
|
|
|
+ var y: Foo = {};
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
|
|
|
-Note that because a type is a value, and a type-of-type is a type, these cases
|
|
|
|
|
-are overlapping and not entirely separable.
|
|
|
|
|
|
|
+### Types and facets
|
|
|
|
|
|
|
|
-If any of the above lookups ever looks for members of a type parameter, it
|
|
|
|
|
-should consider members of the type-of-type, treating the type parameter as an
|
|
|
|
|
-archetype.
|
|
|
|
|
|
|
+If the first operand is a type or facet, it must be a compile-time constant.
|
|
|
|
|
+This disallows member access into a type except during compile-time, see leads
|
|
|
|
|
+issue [#1293](https://github.com/carbon-language/carbon-lang/issues/1293).
|
|
|
|
|
|
|
|
-**Note:** If lookup is performed into a type that involves a template parameter,
|
|
|
|
|
-the lookup will be performed both in the context of the template definition and
|
|
|
|
|
-in the context of the template instantiation, as described in
|
|
|
|
|
-[templates and generics](#templates-and-generics).
|
|
|
|
|
|
|
+Like the previous case, types (including
|
|
|
|
|
+[facet types](/docs/design/generics/terminology.md#facet-type)) have member
|
|
|
|
|
+names, and lookup searches those names. For example:
|
|
|
|
|
|
|
|
-For a simple member access, the word is looked up in the following types:
|
|
|
|
|
|
|
+- `i32.Least` finds the member constant `Least` of the type `i32`.
|
|
|
|
|
+- `Add.Op` finds the member function `Op` of the interface `Add`. Because a
|
|
|
|
|
+ facet type is a type, this is a special case of the previous bullet.
|
|
|
|
|
|
|
|
-- If the first operand can be evaluated and evaluates to a type, that type.
|
|
|
|
|
-- If the type of the first operand can be evaluated, that type.
|
|
|
|
|
-- If the type of the first operand is a generic type parameter, and the type
|
|
|
|
|
- of that generic type parameter can be evaluated, that type-of-type.
|
|
|
|
|
|
|
+Unlike the previous case, both simple and compound member access is allowed.
|
|
|
|
|
|
|
|
-The results of these lookups are [combined](#lookup-ambiguity).
|
|
|
|
|
|
|
+Non-type facets, such as `T as Cowboy`, also have members. Specifically, the
|
|
|
|
|
+members of the `impl` or `impl`s that form the implementation of `T as Cowboy`.
|
|
|
|
|
+Being part of the `impl` rather than the interface, no further
|
|
|
|
|
+[`impl` lookup](#impl-lookup) is needed.
|
|
|
|
|
|
|
|
-For a compound member access, the second operand is evaluated as a constant to
|
|
|
|
|
-determine the member being accessed. The evaluation is required to succeed and
|
|
|
|
|
-to result in a member of a type or interface.
|
|
|
|
|
|
|
+```carbon
|
|
|
|
|
+interface Cowboy {
|
|
|
|
|
+ fn Draw[self: Self]();
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
-For example:
|
|
|
|
|
|
|
+interface Renderable {
|
|
|
|
|
+ fn Draw[self: Self]();
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
|
|
+class Avatar {
|
|
|
|
|
+ extend impl Avatar as Cowboy;
|
|
|
|
|
+ extend impl Avatar as Renderable;
|
|
|
|
|
+}
|
|
|
```
|
|
```
|
|
|
|
|
+
|
|
|
|
|
+Simple member access `(Avatar as Cowboy).Draw` finds the `Cowboy.Draw`
|
|
|
|
|
+implementation for `Avatar`, ignoring `Renderable.Draw`.
|
|
|
|
|
+
|
|
|
|
|
+### Values
|
|
|
|
|
+
|
|
|
|
|
+If the first operand is not a type, package, namespace, or facet it does not
|
|
|
|
|
+have member names, and a search is performed into the type of the first operand
|
|
|
|
|
+instead.
|
|
|
|
|
+
|
|
|
|
|
+```carbon
|
|
|
interface Printable {
|
|
interface Printable {
|
|
|
fn Print[self: Self]();
|
|
fn Print[self: Self]();
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
impl i32 as Printable;
|
|
impl i32 as Printable;
|
|
|
|
|
+
|
|
|
class Point {
|
|
class Point {
|
|
|
var x: i32;
|
|
var x: i32;
|
|
|
var y: i32;
|
|
var y: i32;
|
|
|
- // Internal impl injects the name `Print` into class `Point`.
|
|
|
|
|
|
|
+ // Extending impl injects the name `Print` into
|
|
|
|
|
+ // class `Point`.
|
|
|
extend impl as Printable;
|
|
extend impl as Printable;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -200,27 +246,49 @@ fn PrintPointTwice() {
|
|
|
|
|
|
|
|
// ✅ OK, `Print` found in type of `p`, namely `Point`.
|
|
// ✅ OK, `Print` found in type of `p`, namely `Point`.
|
|
|
p.Print();
|
|
p.Print();
|
|
|
- // ✅ OK, `Print` found in the type `Printable`.
|
|
|
|
|
|
|
+ // ✅ OK, `Print` found in the type `Printable`, and
|
|
|
|
|
+ // `Printable.Print` found in the type of `p`.
|
|
|
p.(Printable.Print)();
|
|
p.(Printable.Print)();
|
|
|
}
|
|
}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 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).
|
|
|
|
|
+
|
|
|
|
|
+For example:
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+interface Printable {
|
|
|
|
|
+ fn Print[self: Self]();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
fn GenericPrint[T:! Printable](a: T) {
|
|
fn GenericPrint[T:! Printable](a: T) {
|
|
|
- // ✅ OK, type of `a` is the type parameter `T`;
|
|
|
|
|
- // `Print` found in the type of `T`, namely `Printable`.
|
|
|
|
|
|
|
+ // ✅ OK, type of `a` is the facet binding `T`;
|
|
|
|
|
+ // `Print` found in the facet `T as Printable`.
|
|
|
a.Print();
|
|
a.Print();
|
|
|
}
|
|
}
|
|
|
-fn CallGenericPrint(p: Point) {
|
|
|
|
|
- GenericPrint(p);
|
|
|
|
|
-}
|
|
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-#### Templates and generics
|
|
|
|
|
|
|
+**Note:** If lookup is performed into a type that involves a template binding,
|
|
|
|
|
+the lookup will be performed both in the context of the template definition and
|
|
|
|
|
+in the context of the template instantiation, as described in
|
|
|
|
|
+[the "compile-time bindings" section](#compile-time-bindings). The results of
|
|
|
|
|
+these lookups are [combined](#lookup-ambiguity).
|
|
|
|
|
|
|
|
-If the value or type of the first operand depends on a template or generic
|
|
|
|
|
-parameter, the lookup is performed from a context where the value of that
|
|
|
|
|
-parameter is unknown. Evaluation of an expression involving the parameter may
|
|
|
|
|
-still succeed, but will result in a symbolic value involving that parameter.
|
|
|
|
|
|
|
+#### Compile-time bindings
|
|
|
|
|
|
|
|
-```
|
|
|
|
|
|
|
+If the value or type of the first operand depends on a checked or template
|
|
|
|
|
+generic parameter, or in fact any
|
|
|
|
|
+[compile-time binding](/docs/design/generics/terminology.md#bindings), the
|
|
|
|
|
+lookup is performed from a context where the value of that binding is unknown.
|
|
|
|
|
+Evaluation of an expression involving the binding may still succeed, but will
|
|
|
|
|
+result in a symbolic value involving that binding.
|
|
|
|
|
+
|
|
|
|
|
+```carbon
|
|
|
class GenericWrapper(T:! type) {
|
|
class GenericWrapper(T:! type) {
|
|
|
var field: T;
|
|
var field: T;
|
|
|
}
|
|
}
|
|
@@ -229,48 +297,98 @@ fn F[T:! type](x: GenericWrapper(T)) -> T {
|
|
|
return x.field;
|
|
return x.field;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-class TemplateWrapper(template T:! type) {
|
|
|
|
|
- var field: T;
|
|
|
|
|
|
|
+interface Renderable {
|
|
|
|
|
+ fn Draw[self: Self]();
|
|
|
}
|
|
}
|
|
|
-fn G[template T:! type](x: TemplateWrapper(T)) -> T {
|
|
|
|
|
- // 🤷 Not yet decided.
|
|
|
|
|
- return x.field;
|
|
|
|
|
|
|
+fn DrawChecked[T:! Renderable](c: T) {
|
|
|
|
|
+ // `Draw` resolves to `Renderable.Draw`.
|
|
|
|
|
+ c.Draw();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+class Cowboy { fn Draw[self: Self](); }
|
|
|
|
|
+impl Cowboy as Renderable { fn Draw[self: Self](); }
|
|
|
|
|
+
|
|
|
|
|
+fn CallsDrawChecked(c: Cowboy) {
|
|
|
|
|
+ // ✅ Calls member of `impl Cowboy as Renderable`.
|
|
|
|
|
+ DrawChecked(c);
|
|
|
|
|
+ // In contrast to this which calls member of `Cowboy`:
|
|
|
|
|
+ c.Draw();
|
|
|
}
|
|
}
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-> **TODO:** The behavior of `G` above is not yet fully decided. If class
|
|
|
|
|
-> templates can be specialized, then we cannot know the members of
|
|
|
|
|
-> `TemplateWrapper(T)` without knowing `T`, so this first lookup will find
|
|
|
|
|
-> nothing. In any case, as described below, the lookup will be performed again
|
|
|
|
|
-> when `T` is known.
|
|
|
|
|
|
|
+If the value or type depends on any template bindings, the lookup is redone from
|
|
|
|
|
+a context where the values of those bindings are known, but where the values of
|
|
|
|
|
+any symbolic bindings are still unknown. The lookup results from these two
|
|
|
|
|
+contexts are [combined](#lookup-ambiguity).
|
|
|
|
|
+
|
|
|
|
|
+```carbon
|
|
|
|
|
+fn DrawTemplate[template T:! type](c: T) {
|
|
|
|
|
+ // `Draw` not found in `type`, looked up in the
|
|
|
|
|
+ // actual deduced value of `T`.
|
|
|
|
|
+ c.Draw();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+fn CallsDrawTemplate(c: Cowboy) {
|
|
|
|
|
+ // ✅ Calls member of `Cowboy`:
|
|
|
|
|
+ DrawTemplate(c);
|
|
|
|
|
+ // Same behavior as:
|
|
|
|
|
+ c.Draw();
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
|
|
|
-If the value or type depends on any template parameters, the lookup is redone
|
|
|
|
|
-from a context where the values of those parameters are known, but where the
|
|
|
|
|
-values of any generic parameters are still unknown. The lookup results from
|
|
|
|
|
-these two contexts are [combined](#lookup-ambiguity).
|
|
|
|
|
|
|
+> **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.
|
|
|
|
|
|
|
|
-**Note:** All lookups are done from a context where the values of any generic
|
|
|
|
|
-parameters that are in scope are unknown. Unlike for a template parameter, the
|
|
|
|
|
-actual value of a generic parameter never affects the result of member
|
|
|
|
|
|
|
+**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.
|
|
resolution.
|
|
|
|
|
|
|
|
|
|
+##### 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
|
|
|
|
|
+when looking in multiple interfaces that are
|
|
|
|
|
+[combined with `&`](/docs/design/generics/details.md#combining-interfaces-by-anding-type-of-types):
|
|
|
|
|
+
|
|
|
|
|
+- If more than one distinct member is found, after performing
|
|
|
|
|
+ [`impl` lookup](#impl-lookup) if necessary, the lookup is ambiguous, and the
|
|
|
|
|
+ program is invalid.
|
|
|
|
|
+- If no members are found, the program is invalid.
|
|
|
|
|
+- Otherwise, the result of combining the lookup results is the unique member
|
|
|
|
|
+ that was found.
|
|
|
|
|
+
|
|
|
```carbon
|
|
```carbon
|
|
|
-class Cowboy { fn Draw[self: Self](); }
|
|
|
|
|
interface Renderable {
|
|
interface Renderable {
|
|
|
fn Draw[self: Self]();
|
|
fn Draw[self: Self]();
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+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.
|
|
|
|
|
+ c.Draw();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+class Cowboy { fn Draw[self: Self](); }
|
|
|
impl Cowboy as Renderable { fn Draw[self: Self](); }
|
|
impl Cowboy as Renderable { fn Draw[self: Self](); }
|
|
|
-fn DrawDirect(c: Cowboy) { c.Draw(); }
|
|
|
|
|
-fn DrawGeneric[T:! Renderable](c: T) { c.Draw(); }
|
|
|
|
|
-fn DrawTemplate[template T:! Renderable](c: T) { c.Draw(); }
|
|
|
|
|
|
|
|
|
|
-fn Draw(c: Cowboy) {
|
|
|
|
|
- // ✅ Calls member of `Cowboy`.
|
|
|
|
|
- DrawDirect(c);
|
|
|
|
|
- // ✅ Calls member of `impl Cowboy as Renderable`.
|
|
|
|
|
- DrawGeneric(c);
|
|
|
|
|
- // ❌ Error: ambiguous.
|
|
|
|
|
- DrawTemplate(c);
|
|
|
|
|
|
|
+class Pig { }
|
|
|
|
|
+impl Pig as Renderable {
|
|
|
|
|
+ fn Draw[self: Self]();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
class RoundWidget {
|
|
class RoundWidget {
|
|
@@ -287,35 +405,68 @@ class SquareWidget {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-fn DrawWidget(r: RoundWidget, s: SquareWidget) {
|
|
|
|
|
- // ✅ OK, lookup in type and lookup in type-of-type find the same entity.
|
|
|
|
|
- DrawTemplate(r);
|
|
|
|
|
|
|
+fn FlyTemplate[template T:! type](c: T) {
|
|
|
|
|
+ c.Fly();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+fn Draw(c: Cowboy, p: Pig, r: RoundWidget, s: SquareWidget) {
|
|
|
|
|
+ // ❌ Error: ambiguous. `Cowboy.Draw` and
|
|
|
|
|
+ // `(Cowboy as Renderable).Draw` are different.
|
|
|
|
|
+ DrawTemplate2(c);
|
|
|
|
|
+
|
|
|
|
|
+ // ✅ OK, lookup in type `Pig` finds nothing, so uses
|
|
|
|
|
+ // lookup in facet type `Pig as Renderable`.
|
|
|
|
|
+ DrawTemplate2(p);
|
|
|
|
|
+
|
|
|
|
|
+ // ✅ OK, lookup in type `RoundWidget` and lookup in facet
|
|
|
|
|
+ // type `RoundWidget as Renderable` find the same entity.
|
|
|
|
|
+ DrawTemplate2(r);
|
|
|
|
|
|
|
|
- // ✅ OK, lookup in type and lookup in type-of-type find the same entity.
|
|
|
|
|
- DrawTemplate(s);
|
|
|
|
|
|
|
+ // ✅ OK, lookup in type `SquareWidget` and lookup in facet
|
|
|
|
|
+ // type `SquareWidget as Renderable` find the same entity.
|
|
|
|
|
+ DrawTemplate2(s);
|
|
|
|
|
|
|
|
- // ✅ OK, found in type.
|
|
|
|
|
- r.Draw();
|
|
|
|
|
- s.Draw();
|
|
|
|
|
|
|
+ // ❌ Error: `Fly` method not found in `Pig` or
|
|
|
|
|
+ // `Pig as type`.
|
|
|
|
|
+ FlyTemplate(p);
|
|
|
}
|
|
}
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-#### Lookup ambiguity
|
|
|
|
|
|
|
+## `impl` lookup
|
|
|
|
|
|
|
|
-Multiple lookups can be performed when resolving a member access expression. If
|
|
|
|
|
-more than one member is found, after performing [`impl` lookup](#impl-lookup) if
|
|
|
|
|
-necessary, the lookup is ambiguous, and the program is invalid. Similarly, if no
|
|
|
|
|
-members are found, the program is invalid. Otherwise, the result of combining
|
|
|
|
|
-the lookup results is the unique member that was found.
|
|
|
|
|
|
|
+`impl` lookup maps a member of an interface to the corresponding member of the
|
|
|
|
|
+relevant `impl`. It is performed when member access names an interface member,
|
|
|
|
|
+except when the member was found by a search of a facet type scope in a simple
|
|
|
|
|
+member access expression.
|
|
|
|
|
|
|
|
-## `impl` lookup
|
|
|
|
|
|
|
+### `impl` lookup for simple member access
|
|
|
|
|
+
|
|
|
|
|
+For a simple member access `a.b` where `b` names a member of an interface `I`:
|
|
|
|
|
|
|
|
-When the second operand of a member access expression resolves to a member of an
|
|
|
|
|
-interface `I`, and the first operand is a value other than a type-of-type,
|
|
|
|
|
-_`impl` lookup_ is performed to map the member of the interface to the
|
|
|
|
|
-corresponding member of the relevant `impl`. The member of the `impl` replaces
|
|
|
|
|
-the member of the interface in all further processing of the member access
|
|
|
|
|
-expression.
|
|
|
|
|
|
|
+- If the interface member was found by searching a
|
|
|
|
|
+ non-[facet-type](/docs/design/generics/terminology.md#facet-type) scope `T`,
|
|
|
|
|
+ for example a class or an adapter, then `impl` lookup is performed for
|
|
|
|
|
+ `T as I`.
|
|
|
|
|
+ - In the case where the member was found in a base class of the class that
|
|
|
|
|
+ was searched, `T` is the derived class that was searched, not the base
|
|
|
|
|
+ class in which the name was declared.
|
|
|
|
|
+ - More generally, if the member was found in something the type extends,
|
|
|
|
|
+ such as a facet type or mixin, `T` is the type that was initially
|
|
|
|
|
+ searched, not what it extended.
|
|
|
|
|
+- Otherwise, `impl` lookup is not performed.
|
|
|
|
|
+
|
|
|
|
|
+The appropriate `impl T as I` implementation is located. The program is invalid
|
|
|
|
|
+if no such `impl` exists. When `T` or `I` depends on a symbolic binding, a
|
|
|
|
|
+suitable constraint must be specified to ensure that such an `impl` will exist.
|
|
|
|
|
+When `T` or `I` depends on a template binding, this check is deferred until the
|
|
|
|
|
+value for the template binding is known.
|
|
|
|
|
+
|
|
|
|
|
+`M` is replaced by the member of the `impl` that corresponds to `M`.
|
|
|
|
|
+
|
|
|
|
|
+[Instance binding](#instance-binding) may also apply if the member is an
|
|
|
|
|
+instance member.
|
|
|
|
|
+
|
|
|
|
|
+For example:
|
|
|
|
|
|
|
|
```carbon
|
|
```carbon
|
|
|
interface Addable {
|
|
interface Addable {
|
|
@@ -325,6 +476,7 @@ interface Addable {
|
|
|
default fn Sum[Seq:! Iterable where .ValueType = Self](seq: Seq) -> Self {
|
|
default fn Sum[Seq:! Iterable where .ValueType = Self](seq: Seq) -> Self {
|
|
|
// ...
|
|
// ...
|
|
|
}
|
|
}
|
|
|
|
|
+ alias AliasForSum = Sum;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
class Integer {
|
|
class Integer {
|
|
@@ -334,94 +486,27 @@ class Integer {
|
|
|
// #4, generated from default implementation for #2.
|
|
// #4, generated from default implementation for #2.
|
|
|
// fn Sum[...](...);
|
|
// fn Sum[...](...);
|
|
|
}
|
|
}
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
-fn SumIntegers(v: Vector(Integer)) -> Integer {
|
|
|
|
|
- // Member resolution resolves the name `Sum` to #2.
|
|
|
|
|
- // `impl` lookup then locates the `impl Integer as Addable`,
|
|
|
|
|
- // and determines that the member access refers to #4,
|
|
|
|
|
- // which is then called.
|
|
|
|
|
- return Integer.Sum(v);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-fn AddTwoIntegers(a: Integer, b: Integer) -> Integer {
|
|
|
|
|
- // Member resolution resolves the name `Add` to #1.
|
|
|
|
|
- // `impl` lookup then locates the `impl Integer as Addable`,
|
|
|
|
|
- // and determines that the member access refers to #3.
|
|
|
|
|
- // Finally, instance binding will be performed as described later.
|
|
|
|
|
- // This can be written more verbosely and explicitly as any of:
|
|
|
|
|
- // - `return a.(Integer.Add)(b);`
|
|
|
|
|
- // - `return a.(Addable.Add)(b);`
|
|
|
|
|
- // - `return a.(Integer.(Addable.Add))(b);`
|
|
|
|
|
- return a.Add(b);
|
|
|
|
|
|
|
+ alias AliasForAdd = Addable.Add;
|
|
|
}
|
|
}
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-The type `T` that is expected to implement `I` depends on the first operand of
|
|
|
|
|
-the member access expression, `V`:
|
|
|
|
|
-
|
|
|
|
|
-- If `V` can be evaluated and evaluates to a type, then `T` is `V`.
|
|
|
|
|
- ```carbon
|
|
|
|
|
- // `V` is `Integer`. `T` is `V`, which is `Integer`.
|
|
|
|
|
- // Alias refers to #2.
|
|
|
|
|
- alias AddIntegers = Integer.Add;
|
|
|
|
|
- ```
|
|
|
|
|
-- Otherwise, `T` is the type of `V`.
|
|
|
|
|
- ```carbon
|
|
|
|
|
- let a: Integer = {};
|
|
|
|
|
- // `V` is `a`. `T` is the type of `V`, which is `Integer`.
|
|
|
|
|
- // `a.Add` refers to #2.
|
|
|
|
|
- let twice_a: Integer = a.Add(a);
|
|
|
|
|
- ```
|
|
|
|
|
-
|
|
|
|
|
-The appropriate `impl T as I` implementation is located. The program is invalid
|
|
|
|
|
-if no such `impl` exists. When `T` or `I` depends on a generic parameter, a
|
|
|
|
|
-suitable constraint must be specified to ensure that such an `impl` will exist.
|
|
|
|
|
-When `T` or `I` depends on a template parameter, this check is deferred until
|
|
|
|
|
-the argument for the template parameter is known.
|
|
|
|
|
-
|
|
|
|
|
-`M` is replaced by the member of the `impl` that corresponds to `M`.
|
|
|
|
|
-
|
|
|
|
|
-```carbon
|
|
|
|
|
-interface I {
|
|
|
|
|
- // #1
|
|
|
|
|
- default fn F[self: Self]() {}
|
|
|
|
|
- let N:! i32;
|
|
|
|
|
-}
|
|
|
|
|
-class C {
|
|
|
|
|
- extend impl as I where .N = 5 {
|
|
|
|
|
- // #2
|
|
|
|
|
- fn F[self: C]() {}
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// `V` is `I` and `M` is `I.F`. Because `V` is a type-of-type,
|
|
|
|
|
-// `impl` lookup is not performed, and the alias binds to #1.
|
|
|
|
|
-alias A1 = I.F;
|
|
|
|
|
-
|
|
|
|
|
-// `V` is `C` and `M` is `I.F`. Because `V` is a type, `impl`
|
|
|
|
|
-// lookup is performed with `T` being `C`, and the alias binds to #2.
|
|
|
|
|
-alias A2 = C.F;
|
|
|
|
|
-
|
|
|
|
|
-let c: C = {};
|
|
|
|
|
-
|
|
|
|
|
-// `V` is `c` and `M` is `I.N`. Because `V` is a non-type, `impl`
|
|
|
|
|
-// lookup is performed with `T` being the type of `c`, namely `C`, and
|
|
|
|
|
-// `M` becomes the associated constant from `impl C as I`.
|
|
|
|
|
-// The value of `Z` is 5.
|
|
|
|
|
-let Z: i32 = c.N;
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-[Instance binding](#instance-binding) may also apply if the member is an
|
|
|
|
|
-instance member.
|
|
|
|
|
-
|
|
|
|
|
-```carbon
|
|
|
|
|
-var c: C;
|
|
|
|
|
-// `V` is `c` and `M` is `I.F`. Because `V` is not a type, `T` is the
|
|
|
|
|
-// type of `c`, which is `C`. `impl` lookup is performed, and `M` is
|
|
|
|
|
-// replaced with #2. Then instance binding is performed.
|
|
|
|
|
-c.F();
|
|
|
|
|
-```
|
|
|
|
|
|
|
+- For `Integer.Sum`, member resolution resolves the name `Sum` to \#2, which
|
|
|
|
|
+ is not an instance member. `impl` lookup then locates the
|
|
|
|
|
+ `impl Integer as Addable`, and determines that the member access refers to
|
|
|
|
|
+ \#4.
|
|
|
|
|
+- For `i.Add(j)` where `i: Integer`, member resolution resolves the name `Add`
|
|
|
|
|
+ to \#1, which is an instance member. `impl` lookup then locates the
|
|
|
|
|
+ `impl Integer as Addable`, and determines that the member access refers to
|
|
|
|
|
+ \#3. Finally, instance binding will be performed as described later.
|
|
|
|
|
+- `Integer.AliasForAdd` finds \#3, the `Add` member of the facet type
|
|
|
|
|
+ `Integer as Addable`, not \#1, the interface member `Addable.Add`.
|
|
|
|
|
+- `i.AliasForAdd`, with `i: Integer`, finds \#3, the `Add` member of the facet
|
|
|
|
|
+ type `Integer as Addable`, and performs
|
|
|
|
|
+ [instance binding](#instance-binding) since the member is an instance
|
|
|
|
|
+ member.
|
|
|
|
|
+- `Addable.AliasForSum` finds \#2, the member in the interface `Addable`, and
|
|
|
|
|
+ does not perform `impl` lookup.
|
|
|
|
|
|
|
|
**Note:** When an interface member is added to a class by an alias, `impl`
|
|
**Note:** When an interface member is added to a class by an alias, `impl`
|
|
|
lookup is not performed as part of handling the alias, but will happen when
|
|
lookup is not performed as part of handling the alias, but will happen when
|
|
@@ -429,21 +514,21 @@ naming the interface member as a member of the class.
|
|
|
|
|
|
|
|
```carbon
|
|
```carbon
|
|
|
interface Renderable {
|
|
interface Renderable {
|
|
|
- // #1
|
|
|
|
|
|
|
+ // #5
|
|
|
fn Draw[self: Self]();
|
|
fn Draw[self: Self]();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
class RoundWidget {
|
|
class RoundWidget {
|
|
|
impl as Renderable {
|
|
impl as Renderable {
|
|
|
- // #2
|
|
|
|
|
|
|
+ // #6
|
|
|
fn Draw[self: Self]();
|
|
fn Draw[self: Self]();
|
|
|
}
|
|
}
|
|
|
- // `Draw` names the member of the `Renderable` interface.
|
|
|
|
|
|
|
+ // `Draw` names #5, the member of the `Renderable` interface.
|
|
|
alias Draw = Renderable.Draw;
|
|
alias Draw = Renderable.Draw;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
class SquareWidget {
|
|
class SquareWidget {
|
|
|
- // #3
|
|
|
|
|
|
|
+ // #7
|
|
|
fn Draw[self: Self]() {}
|
|
fn Draw[self: Self]() {}
|
|
|
impl as Renderable {
|
|
impl as Renderable {
|
|
|
alias Draw = Self.Draw;
|
|
alias Draw = Self.Draw;
|
|
@@ -452,35 +537,36 @@ class SquareWidget {
|
|
|
|
|
|
|
|
fn DrawWidget(r: RoundWidget, s: SquareWidget) {
|
|
fn DrawWidget(r: RoundWidget, s: SquareWidget) {
|
|
|
// ✅ OK: In the inner member access, the name `Draw` resolves to the
|
|
// ✅ OK: In the inner member access, the name `Draw` resolves to the
|
|
|
- // member `Draw` of `Renderable`, #1, which `impl` lookup replaces with
|
|
|
|
|
- // the member `Draw` of `impl RoundWidget as Renderable`, #2.
|
|
|
|
|
|
|
+ // member `Draw` of `Renderable`, #5, which `impl` lookup replaces with
|
|
|
|
|
+ // the member `Draw` of `impl RoundWidget as Renderable`, #6.
|
|
|
// The outer member access then forms a bound member function that
|
|
// The outer member access then forms a bound member function that
|
|
|
- // calls #2 on `r`, as described in "Instance binding".
|
|
|
|
|
|
|
+ // calls #6 on `r`, as described in "Instance binding".
|
|
|
r.(RoundWidget.Draw)();
|
|
r.(RoundWidget.Draw)();
|
|
|
|
|
|
|
|
// ✅ OK: In the inner member access, the name `Draw` resolves to the
|
|
// ✅ OK: In the inner member access, the name `Draw` resolves to the
|
|
|
- // member `Draw` of `SquareWidget`, #3.
|
|
|
|
|
|
|
+ // member `Draw` of `SquareWidget`, #7.
|
|
|
// The outer member access then forms a bound member function that
|
|
// The outer member access then forms a bound member function that
|
|
|
- // calls #3 on `s`.
|
|
|
|
|
|
|
+ // calls #7 on `s`.
|
|
|
s.(SquareWidget.Draw)();
|
|
s.(SquareWidget.Draw)();
|
|
|
|
|
|
|
|
// ❌ Error: In the inner member access, the name `Draw` resolves to the
|
|
// ❌ Error: In the inner member access, the name `Draw` resolves to the
|
|
|
- // member `Draw` of `SquareWidget`, #3.
|
|
|
|
|
|
|
+ // member `Draw` of `SquareWidget`, #7.
|
|
|
// The outer member access fails because we can't call
|
|
// The outer member access fails because we can't call
|
|
|
- // #3, `Draw[self: SquareWidget]()`, on a `RoundWidget` object `r`.
|
|
|
|
|
|
|
+ // #7, `Draw[self: SquareWidget]()`, on a `RoundWidget` object `r`.
|
|
|
r.(SquareWidget.Draw)();
|
|
r.(SquareWidget.Draw)();
|
|
|
|
|
|
|
|
// ❌ Error: In the inner member access, the name `Draw` resolves to the
|
|
// ❌ Error: In the inner member access, the name `Draw` resolves to the
|
|
|
- // member `Draw` of `Renderable`, #1, which `impl` lookup replaces with
|
|
|
|
|
- // the member `Draw` of `impl RoundWidget as Renderable`, #2.
|
|
|
|
|
|
|
+ // member `Draw` of `Renderable`, #5, which `impl` lookup replaces with
|
|
|
|
|
+ // the member `Draw` of `impl RoundWidget as Renderable`, #6.
|
|
|
// The outer member access fails because we can't call
|
|
// The outer member access fails because we can't call
|
|
|
- // #2, `Draw[self: RoundWidget]()`, on a `SquareWidget` object `s`.
|
|
|
|
|
|
|
+ // #6, `Draw[self: RoundWidget]()`, on a `SquareWidget` object `s`.
|
|
|
s.(RoundWidget.Draw)();
|
|
s.(RoundWidget.Draw)();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
base class WidgetBase {
|
|
base class WidgetBase {
|
|
|
// ✅ OK, even though `WidgetBase` does not implement `Renderable`.
|
|
// ✅ OK, even though `WidgetBase` does not implement `Renderable`.
|
|
|
alias Draw = Renderable.Draw;
|
|
alias Draw = Renderable.Draw;
|
|
|
|
|
+
|
|
|
fn DrawAll[T:! Renderable](v: Vector(T)) {
|
|
fn DrawAll[T:! Renderable](v: Vector(T)) {
|
|
|
for (var w: T in v) {
|
|
for (var w: T in v) {
|
|
|
// ✅ OK. Unqualified lookup for `Draw` finds alias `WidgetBase.Draw`
|
|
// ✅ OK. Unqualified lookup for `Draw` finds alias `WidgetBase.Draw`
|
|
@@ -490,6 +576,7 @@ base class WidgetBase {
|
|
|
// `Renderable`. Finally, the member function is bound to `w` as
|
|
// `Renderable`. Finally, the member function is bound to `w` as
|
|
|
// described in "Instance binding".
|
|
// described in "Instance binding".
|
|
|
w.(Draw)();
|
|
w.(Draw)();
|
|
|
|
|
+
|
|
|
// ❌ Error: `Self.Draw` performs `impl` lookup, which fails
|
|
// ❌ Error: `Self.Draw` performs `impl` lookup, which fails
|
|
|
// because `WidgetBase` does not implement `Renderable`.
|
|
// because `WidgetBase` does not implement `Renderable`.
|
|
|
w.(Self.Draw)();
|
|
w.(Self.Draw)();
|
|
@@ -509,22 +596,93 @@ fn DrawTriangle(t: TriangleWidget) {
|
|
|
}
|
|
}
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
|
|
+### `impl` lookup for compound member access
|
|
|
|
|
+
|
|
|
|
|
+For a compound member access `a.(b)` where `b` names a member of an interface
|
|
|
|
|
+`I`, `impl` lookup is performed for `T as I`, where:
|
|
|
|
|
+
|
|
|
|
|
+- If `b` is an instance member, `T` is the type of `a`. In this case,
|
|
|
|
|
+ [instance binding](#instance-binding) is always performed.
|
|
|
|
|
+- Otherwise, `a` is implicitly converted to `I`, and `T` is the result of
|
|
|
|
|
+ symbolically evaluating the converted expression. In this case,
|
|
|
|
|
+ [instance binding](#instance-binding) is never performed.
|
|
|
|
|
+
|
|
|
|
|
+For example:
|
|
|
|
|
+
|
|
|
|
|
+```carbon
|
|
|
|
|
+fn AddTwoIntegers(a: Integer, b: Integer) -> Integer {
|
|
|
|
|
+ // Since `Addable.Add` is an instance member of `Addable`, `T`
|
|
|
|
|
+ // is set to the type of `a`, and so uses `Integer as Addable`.
|
|
|
|
|
+ return a.(Addable.Add)(b);
|
|
|
|
|
+ // ^ impl lookup and instance binding here
|
|
|
|
|
+ // Impl lookup transforms this into #3:
|
|
|
|
|
+ // return a.((Integer as Addable).Add)(b);
|
|
|
|
|
+ // which no longer requires impl lookup.
|
|
|
|
|
+
|
|
|
|
|
+ // ❌ By the same logic, in this example, `T` is set to the
|
|
|
|
|
+ // type of `Integer`, and so uses `type as Addable`, which
|
|
|
|
|
+ // isn't implemented.
|
|
|
|
|
+ return Integer.(Addable.Add)(...);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+fn SumIntegers(v: Vector(Integer)) -> Integer {
|
|
|
|
|
+ // Since `Addable.Sum` is a non-instance member of `Addable`,
|
|
|
|
|
+ // `Integer` is implicitly converted to `Addable`, and so uses
|
|
|
|
|
+ // `Integer as Addable`.
|
|
|
|
|
+ Integer.(Addable.Sum)(v);
|
|
|
|
|
+ // ^ impl lookup but no instance binding here
|
|
|
|
|
+ // Impl lookup transforms this into #4:
|
|
|
|
|
+ // ((Integer as Addable).Sum)(v);
|
|
|
|
|
+ // which no longer requires impl lookup.
|
|
|
|
|
+
|
|
|
|
|
+ var a: Integer;
|
|
|
|
|
+ // ❌ This is an error since `a` does not implicitly convert to
|
|
|
|
|
+ // a type.
|
|
|
|
|
+ a.(Addable.Sum)(v);
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
## Instance binding
|
|
## Instance binding
|
|
|
|
|
|
|
|
-If member resolution and `impl` lookup produce a member `M` that is an instance
|
|
|
|
|
-member -- that is, a field or a method -- and the first operand `V` of `.` is a
|
|
|
|
|
-value other than a type, then _instance binding_ is performed, as follows:
|
|
|
|
|
|
|
+Next, _instance binding_ may be performed. This associates an expression with a
|
|
|
|
|
+particular object instance. For example, this is the value bound to `self` when
|
|
|
|
|
+calling a method.
|
|
|
|
|
+
|
|
|
|
|
+For the simple member access syntax `x.y`, if `x` is an entity that has member
|
|
|
|
|
+names, such as a namespace or a type, then `y` is looked up within `x`, and
|
|
|
|
|
+instance binding is not performed. Otherwise, `y` is looked up within the type
|
|
|
|
|
+of `x` and instance binding is performed if an instance member is found.
|
|
|
|
|
+
|
|
|
|
|
+If instance binding is performed:
|
|
|
|
|
|
|
|
-- For a field member in class `C`, `V` is required to be of type `C` or of a
|
|
|
|
|
- type derived from `C`. The result is the corresponding subobject within `V`.
|
|
|
|
|
- The result is an lvalue if `V` is an lvalue.
|
|
|
|
|
|
|
+- For a field member in class `C`, `x` is required to be of type `C` or of a
|
|
|
|
|
+ type derived from `C`. The result is the corresponding subobject within `x`.
|
|
|
|
|
+ If `x` is an
|
|
|
|
|
+ [initializing expression](/docs/design/values.md#initializing-expressions),
|
|
|
|
|
+ then a
|
|
|
|
|
+ [temporary is materialized](/docs/design/values.md#temporary-materialization)
|
|
|
|
|
+ for `x`. The result of `x.y` has the same
|
|
|
|
|
+ [expression category](/docs/design/values.md#expression-categories) as the
|
|
|
|
|
+ possibly materialized `x`.
|
|
|
|
|
|
|
|
```carbon
|
|
```carbon
|
|
|
- var dims: auto = {.width = 1, .height = 2};
|
|
|
|
|
|
|
+ class Size {
|
|
|
|
|
+ var width: i32;
|
|
|
|
|
+ var height: i32;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var dims: Size = {.width = 1, .height = 2};
|
|
|
// `dims.width` denotes the field `width` of the object `dims`.
|
|
// `dims.width` denotes the field `width` of the object `dims`.
|
|
|
Print(dims.width);
|
|
Print(dims.width);
|
|
|
- // `dims` is an lvalue, so `dims.height` is an lvalue.
|
|
|
|
|
|
|
+ // `dims` is a reference expression, so `dims.height` is a
|
|
|
|
|
+ // reference expression.
|
|
|
dims.height = 3;
|
|
dims.height = 3;
|
|
|
|
|
+
|
|
|
|
|
+ fn GetSize() -> Size;
|
|
|
|
|
+ // `GetSize()` returns an initializing expression, which is
|
|
|
|
|
+ // materialized as a temporary on member access, so
|
|
|
|
|
+ // `GetSize().width` is an ephemeral reference expression.
|
|
|
|
|
+ Print(GetSize().width);
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
- For a method, the result is a _bound method_, which is a value `F` such that
|
|
- For a method, the result is a _bound method_, which is a value `F` such that
|
|
@@ -532,8 +690,8 @@ value other than a type, then _instance binding_ is performed, as follows:
|
|
|
`self` parameter initialized by a corresponding recipient argument:
|
|
`self` parameter initialized by a corresponding recipient argument:
|
|
|
|
|
|
|
|
- If the method declares its `self` parameter with `addr`, the recipient
|
|
- If the method declares its `self` parameter with `addr`, the recipient
|
|
|
- argument is `&V`.
|
|
|
|
|
- - Otherwise, the recipient argument is `V`.
|
|
|
|
|
|
|
+ argument is `&x`.
|
|
|
|
|
+ - Otherwise, the recipient argument is `x`.
|
|
|
|
|
|
|
|
```carbon
|
|
```carbon
|
|
|
class Blob {
|
|
class Blob {
|
|
@@ -551,11 +709,41 @@ value other than a type, then _instance binding_ is performed, as follows:
|
|
|
}
|
|
}
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
|
|
+The compound member access syntax `x.(Y)`, where `Y` names an instance member,
|
|
|
|
|
+always performs instance binding. It is an error if `Y` is already bound to an
|
|
|
|
|
+instance member. For example:
|
|
|
|
|
+
|
|
|
|
|
+```carbon
|
|
|
|
|
+interface DebugPrint {
|
|
|
|
|
+ // instance member
|
|
|
|
|
+ fn Print[self:Self]();
|
|
|
|
|
+}
|
|
|
|
|
+impl i32 as DebugPrint;
|
|
|
|
|
+impl type as DebugPrint;
|
|
|
|
|
+
|
|
|
|
|
+fn Debug() {
|
|
|
|
|
+ var i: i32 = 1;
|
|
|
|
|
+
|
|
|
|
|
+ // Prints `1` using `(i32 as DebugPrint).Print` bound to `i`.
|
|
|
|
|
+ i.(DebugPrint.Print)();
|
|
|
|
|
+
|
|
|
|
|
+ // Prints `i32` using `(type as DebugPrint).Print` bound to `i32`.
|
|
|
|
|
+ i32.(DebugPrint.Print)();
|
|
|
|
|
+
|
|
|
|
|
+ // ❌ This is an error since `i32.(DebugPrint.Print)` is already
|
|
|
|
|
+ // bound, and may not be bound again to `i`.
|
|
|
|
|
+ i.(i32.(DebugPrint.Print))();
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+To get the `M` member of interface `I` for a type `T`, use `(T as I).M`, as this
|
|
|
|
|
+doesn't attempt to perform instance binding on `T`, in contrast to `T.(I.M)`.
|
|
|
|
|
+
|
|
|
## Non-instance members
|
|
## Non-instance members
|
|
|
|
|
|
|
|
If instance binding is not performed, the result is the member `M` determined by
|
|
If instance binding is not performed, the result is the member `M` determined by
|
|
|
member resolution and `impl` lookup. Evaluating the member access expression
|
|
member resolution and `impl` lookup. Evaluating the member access expression
|
|
|
-evaluates `V` and discards the result.
|
|
|
|
|
|
|
+evaluates the first argument and discards the result.
|
|
|
|
|
|
|
|
An expression that names an instance member, but for which instance binding is
|
|
An expression that names an instance member, but for which instance binding is
|
|
|
not performed, can only be used as the second operand of a compound member
|
|
not performed, can only be used as the second operand of a compound member
|
|
@@ -571,11 +759,12 @@ fn CallStaticMethod(c: C) {
|
|
|
// ✅ OK, calls `C.StaticMethod`.
|
|
// ✅ OK, calls `C.StaticMethod`.
|
|
|
C.StaticMethod();
|
|
C.StaticMethod();
|
|
|
|
|
|
|
|
- // ✅ OK, evaluates expression `c` then calls `C.StaticMethod`.
|
|
|
|
|
|
|
+ // ✅ OK, evaluates expression `c`, discards the result, then
|
|
|
|
|
+ // calls `C.StaticMethod`.
|
|
|
c.StaticMethod();
|
|
c.StaticMethod();
|
|
|
|
|
|
|
|
- // ❌ Error: name of instance member `C.field` can only be used in a
|
|
|
|
|
- // member access or alias.
|
|
|
|
|
|
|
+ // ❌ Error: name of instance member `C.field` can only be used in
|
|
|
|
|
+ // a member access or alias.
|
|
|
C.field = 1;
|
|
C.field = 1;
|
|
|
// ✅ OK, instance binding is performed by outer member access,
|
|
// ✅ OK, instance binding is performed by outer member access,
|
|
|
// same as `c.field = 1;`
|
|
// same as `c.field = 1;`
|
|
@@ -600,30 +789,34 @@ always used for lookup.
|
|
|
interface Printable {
|
|
interface Printable {
|
|
|
fn Print[self: Self]();
|
|
fn Print[self: Self]();
|
|
|
}
|
|
}
|
|
|
-impl i32 as Printable {
|
|
|
|
|
- fn Print[self: Self]();
|
|
|
|
|
-}
|
|
|
|
|
|
|
+impl i32 as Printable;
|
|
|
|
|
+
|
|
|
fn MemberAccess(n: i32) {
|
|
fn MemberAccess(n: i32) {
|
|
|
- // ✅ OK: `Printable.Print` is the interface member.
|
|
|
|
|
- // `i32.(Printable.Print)` is the corresponding member of the `impl`.
|
|
|
|
|
- // `n.(i32.(Printable.Print))` is a bound member function naming that member.
|
|
|
|
|
- n.(i32.(Printable.Print))();
|
|
|
|
|
-
|
|
|
|
|
- // ✅ Same as above, `n.(Printable.Print)` is effectively interpreted as
|
|
|
|
|
- // `n.(T.(Printable.Print))()`, where `T` is the type of `n`,
|
|
|
|
|
- // because `n` does not evaluate to a type. Performs impl lookup
|
|
|
|
|
- // and then instance binding.
|
|
|
|
|
|
|
+ // ✅ OK: `(i32 as Printable).Print` is the `Print` member of the
|
|
|
|
|
+ // `i32 as Printable` facet corresponding to the `Printable.Print`
|
|
|
|
|
+ // interface member.
|
|
|
|
|
+ // `n.((i32 as Printable).Print)` is that member function bound to `n`.
|
|
|
|
|
+ n.((i32 as Printable).Print)();
|
|
|
|
|
+
|
|
|
|
|
+ // ✅ Same as above, `n.(Printable.Print)` is effectively interpreted
|
|
|
|
|
+ // as `n.((T as Printable).Print)()`, where `T` is the type of `n`.
|
|
|
|
|
+ // Performs impl lookup and then instance binding.
|
|
|
n.(Printable.Print)();
|
|
n.(Printable.Print)();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// ✅ OK, member `Print` of interface `Printable`.
|
|
|
|
|
-alias X1 = Printable.Print;
|
|
|
|
|
-// ❌ Error, compound access doesn't perform impl lookup or instance binding.
|
|
|
|
|
-alias X2 = Printable.(Printable.Print);
|
|
|
|
|
-// ✅ OK, member `Print` of `impl i32 as Printable`.
|
|
|
|
|
-alias X3 = i32.(Printable.Print);
|
|
|
|
|
-// ❌ Error, compound access doesn't perform impl lookup or instance binding.
|
|
|
|
|
-alias X4 = i32.(i32.(Printable.Print));
|
|
|
|
|
|
|
+interface Factory {
|
|
|
|
|
+ fn Make() -> Self;
|
|
|
|
|
+}
|
|
|
|
|
+impl i32 as Factory;
|
|
|
|
|
+
|
|
|
|
|
+// ✅ OK, member `Make` of interface `Factory`.
|
|
|
|
|
+alias X1 = Factory.Make;
|
|
|
|
|
+// ❌ Error, compound access without impl lookup or instance binding.
|
|
|
|
|
+alias X2 = Factory.(Factory.Make);
|
|
|
|
|
+// ✅ OK, member `Make` of `impl i32 as Factory`.
|
|
|
|
|
+alias X3 = (i32 as Factory).Make;
|
|
|
|
|
+// ❌ Error, compound access without impl lookup or instance binding.
|
|
|
|
|
+alias X4 = i32.((i32 as Factory).Make);
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
## Precedence and associativity
|
|
## Precedence and associativity
|
|
@@ -672,3 +865,7 @@ var n: i32 = 1 + X.Y;
|
|
|
- Proposal
|
|
- Proposal
|
|
|
[#989: member access expressions](https://github.com/carbon-language/carbon-lang/pull/989)
|
|
[#989: member access expressions](https://github.com/carbon-language/carbon-lang/pull/989)
|
|
|
- [Question for leads: constrained template name lookup](https://github.com/carbon-language/carbon-lang/issues/949)
|
|
- [Question for leads: constrained template name lookup](https://github.com/carbon-language/carbon-lang/issues/949)
|
|
|
|
|
+- Proposal
|
|
|
|
|
+ [#2360: Types are values of type `type`](https://github.com/carbon-language/carbon-lang/pull/2360)
|
|
|
|
|
+- Proposal
|
|
|
|
|
+ [#2550: Simplified package declaration for the `Main` package](https://github.com/carbon-language/carbon-lang/pull/2550)
|