Integrate description of tuple indexing into member access better. (#3679)

Improve the exposition of the design changes from #3646 to integrate
better into the overall description of member access design.

This fixes the incorrect description of the rules for `->` by instead
relying on the general rule that `->` is rewritten to use `*` and `.`
before any other processing is done, and generally makes
*integer-literal* names be less of a special case.
This commit is contained in:
Richard Smith
2024-02-03 08:40:03 +00:00
committed by GitHub
parent 7e06b703bf
commit cb849cf165
+64 -65
View File
@@ -14,6 +14,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- [Member resolution](#member-resolution)
- [Package and namespace members](#package-and-namespace-members)
- [Types and facets](#types-and-facets)
- [Tuple indexing](#tuple-indexing)
- [Values](#values)
- [Facet binding](#facet-binding)
- [Compile-time bindings](#compile-time-bindings)
@@ -24,7 +25,6 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- [Instance binding](#instance-binding)
- [Non-instance members](#non-instance-members)
- [Non-vacuous member access restriction](#non-vacuous-member-access-restriction)
- [Tuple indexing](#tuple-indexing)
- [Precedence and associativity](#precedence-and-associativity)
- [Alternatives considered](#alternatives-considered)
- [References](#references)
@@ -52,13 +52,17 @@ form:
- _member-access-expression_ ::= _expression_ `.` _word_
- _member-access-expression_ ::= _expression_ `->` _word_
- _member-access-expression_ ::= _expression_ `.` _integer-literal_
- _member-access-expression_ ::= _expression_ `->` _integer-literal_
or a _compound_ member access of the form:
- _member-access-expression_ ::= _expression_ `.` `(` _expression_ `)`
- _member-access-expression_ ::= _expression_ `->` `(` _expression_ `)`
Compound member accesses allow specifying a qualified member name.
The _member name_ is the _word_, _integer-literal_, or the constant value of the
parenthesized _expression_ in the member access expression. Compound member
accesses allow specifying a qualified member name.
For example:
@@ -100,7 +104,7 @@ simplicity.
A member access expression is processed using the following steps:
- First, the word or parenthesized expression to the right of the `.` is
- First, the member name to the right of the `.` is
[resolved](#member-resolution) to a specific member entity, called `M` in
this document.
- Then, if necessary, [`impl` lookup](#impl-lookup) is performed to map from a
@@ -117,24 +121,26 @@ A member access expression is processed using the following steps:
The process of _member resolution_ determines which member `M` a member access
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 simple member access, if the first operand is a type, facet, package, or
namespace, a search for the member name is performed in the first operand.
Otherwise, a search for the member name 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.
succeed and to result in a member of a type, interface, or non-type facet, or a
value of an integer or integer literal type. If the result is an instance
member, then [instance binding](#instance-binding) is always performed on the
first operand.
### Package and namespace members
If the first operand is a package or namespace name, the expression must be a
simple member access expression. The _word_ must name a member of that package
or namespace, and the result is the package or namespace member with that name.
simple member access expression. The member name must be a _word_ that names a
member of that package or namespace, and the result is the package or namespace
member with that name.
An expression that names a package or namespace can only be used as the first
operand of a member access or as the target of an `alias` declaration.
@@ -216,9 +222,52 @@ class Avatar {
Simple member access `(Avatar as Cowboy).Draw` finds the `Cowboy.Draw`
implementation for `Avatar`, ignoring `Renderable.Draw`.
### Tuple indexing
Tuple types have member names that are *integer-literal*s, not *word*s.
Each positional element of a tuple is considered to have a name that is the
corresponding decimal integer: `0`, `1`, and so on. The spelling of the
_integer-literal_ is required to exactly match one of those names, and the
result of member resolution is an instance member that refers to the
corresponding element of the tuple.
```
// ✅ `a == 42`.
let a: i32 = (41, 42, 43).1;
// ❌ Error: no tuple element named `0x1`.
let b: i32 = (1, 2, 3).0x1;
// ❌ Error: no tuple element named `2`.
let c: i32 = (1, 2).2;
var t: (i32, i32, i32) = (1, 2, 3);
let p: (i32, i32, i32)* = &t;
// ✅ `m == 3`.
let m: i32 = p->2;
```
In a compound member access whose second operand is of integer or integer
literal type, the first operand is required to be of tuple type or to extend a
tuple type, otherwise member resolution fails. The second operand is required to
be a non-negative template constant that is less than the number of tuple
elements, and the result is an instance member that refers to the corresponding
positional element of the tuple.
```
// ✅ `d == 43`.
let d: i32 = (41, 42, 43).(1 + 1);
// ✅ `e == 2`.
let template e:! i32 = (1, 2, 3).(0x1);
// ❌ Error: no tuple element with index 4.
let f: i32 = (1, 2).(2 * 2);
// ✅ `n == 3`.
let n: i32 = p->(e);
```
### Values
If the first operand is not a type, package, namespace, or facet it does not
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.
@@ -842,56 +891,6 @@ alias X3 = (i32 as Factory).Make;
alias X4 = i32.((i32 as Factory).Make);
```
## Tuple indexing
A tuple indexing expression is of the form:
- _expression_ `.` _integer-literal_
- _expression_ `->` _integer-literal_
The _expression_ is required to be of tuple type.
Each positional element of the tuple is considered to have a name that is the
corresponding decimal integer: `0`, `1`, and so on. The spelling of the
_integer-literal_ is required to exactly match one of those names, and the
result is the corresponding element of the tuple.
```
// ✅ `a == 42`.
let a: i32 = (41, 42, 43).1;
// ❌ Error: no tuple element named `0x1`.
let b: i32 = (1, 2, 3).0x1;
// ❌ Error: no tuple element named `2`.
let c: i32 = (1, 2).2;
var t: (i32, i32, i32) = (1, 2, 3);
let p: (i32, i32, i32)* = &t;
// ✅ `m == 3`.
let m: i32 = p->2;
```
In a compound member access of the form:
- _expression_ `.` `(` _expression_ `)`
- _expression_ `->` `(` _expression_ `)`
in which the first _expression_ is a tuple and the second _expression_ is of
integer or integer literal type, the second _expression_ is required to be a
non-negative template constant that is less than the number of tuple elements,
and the result is the corresponding positional element of the tuple.
```
// ✅ `d == 43`.
let d: i32 = (41, 42, 43).(1 + 1);
// ✅ `e == 2`.
let template e:! i32 = (1, 2, 3).(0x1);
// ❌ Error: no tuple element with index 4.
let f: i32 = (1, 2).(2 * 2);
// ✅ `n == 3`.
let n: i32 = p->(e);
```
## Precedence and associativity
Member access expressions associate left-to-right: