1
1
# Dart static access shorthand
2
2
3
- Author:
[email protected] <
br >Version: 1.
1
3
+ Author:
[email protected] <
br >Version: 1.
2
4
4
5
5
You can write ` .foo ` instead of ` ContextType.foo ` when it makes sense. The rules
6
6
are fairly simple and easy to explain.
@@ -31,20 +31,25 @@ We also special-case the `==` and `!=` operators, but nothing else.
31
31
We introduce grammar productions of the form:
32
32
33
33
``` ebnf
34
- <primary > ::= ... -- all current productions
34
+ <postfixExpression > ::= ... -- all current productions
35
35
| <staticMemberShorthand>
36
36
37
37
<constantPattern> ::= ... -- all current productions
38
38
| <staticMemberShorthand>
39
39
40
- <staticMemberShorthand> ::=
40
+ <staticMemberShorthand> ::= <staticMemberShorthandHead> <selector*>
41
+
42
+ <staticMemberShorthandHead> ::=
41
43
'.' (<identifier> | 'new') -- shorthand qualified name
42
44
| 'const' '.' (<identifier> | 'new') <arguments> -- shorthand object creation
43
45
```
44
46
45
- We also add ` . ` to the tokens that an expression statement cannot start with.
47
+ We also add ` . ` to the tokens that an expression statement cannot start with. This doesn't
48
+ affect starting with a double literal like ` .42 ` , since that's a different token than a single ` . ` .
49
+ _ (Not sure this is * necessary* , but it will possibly make parser recovery easier._
50
+ _ So mainly disallow this as an abundance of caution.)_
46
51
47
- That means you can write things like the following (with the intended meaning as
52
+ That means you can write things like the following (with the intended meaning as
48
53
comments, specification to achieve that below):
49
54
50
55
``` dart
@@ -77,13 +82,20 @@ Future<String> futures = .wait([lazyString(), lazyString()]).then((list) => list
77
82
```
78
83
79
84
This is a simple grammatical change. It allows new constructs in any place where
80
- we currently allow primary expressions, which can be followed by selector chains
81
- through the ` <postfixExpression> ` production ` <primary> <selector>* ` .
85
+ we currently allow primary expressions followed by selector chains
86
+ through the ` <postfixExpression> ` production ` <primary> <selector>* ` ,
87
+ and now also ` <staticMemberShorthandHead> <selector*> ` .
88
+
89
+ The new grammar is added as a separate production, rather than making
90
+ ` <staticMemberShorthandHead> ` a ` <primary> ` , and sharing the ` <selector>* `
91
+ between all ` <primary> ` s, because the context type of the entire
92
+ ` <staticMemberShorthand> ` is relevant and will be captured when processing
93
+ that production.
82
94
83
95
#### Non-ambiguity
84
96
85
- A ` <primary > ` cannot immediately follow any other complete expression. We trust
86
- that because a primary expression already contains the production
97
+ A ` <postfixExpression > ` cannot immediately follow any other complete expression.
98
+ We trust that because a primary expression already contains the production
87
99
` '(' <expression> ')' ` which would cause an ambiguity for ` e1(e2) ` since ` (e2) `
88
100
can also be parsed as a ` <primary> ` . The existing places where a ` . ` token
89
101
occurs in the grammar are all in positions where they follow another expression
@@ -98,12 +110,13 @@ new rules are needed.
98
110
Therefore the new productions introduces no new grammatical ambiguities.
99
111
100
112
We prevent expression statements from starting with ` . ` mainly out of caution.
101
- _ (It’s very unlikely that an expression statement starting with static member
102
- shorthand can compile at all. If we ever allow metadata on statements, we don’t
103
- want ` @foo . bar(4) ; ` to be ambiguous. If we ever allow metadata on
104
- expressions, we have bigger issues.)_
113
+ _ (It's an unlikely expression that can start with a static member, it requires something
114
+ that adds a context type on the left, ` .parse(userInput) || (throw "Not true!") `
115
+ or similar, which isn't particularly * useful* ._
116
+ _ If we ever allow metadata on statements, we don’t want ` @foo . bar(4); `
117
+ to be ambiguous. If we ever allow metadata on expressions, we have bigger issues.)_
105
118
106
- A primary expression * can* follow a ` ? ` in a conditional expression, as in
119
+ A postfix expression expression * can* follow a ` ? ` in a conditional expression, as in
107
120
` {e1 ? . id : e2} ` . This is not ambiguous with ` e1?.id ` since we parse ` ?. ` as a
108
121
single token, and will keep doing so. It does mean that ` {e1?.id:e2} ` and
109
122
` {e1? .id:e2} ` will now both be valid and have different meanings, where the
@@ -133,41 +146,47 @@ which is not generic. We do not want this to be treated as
133
146
constructor.)_
134
147
135
148
The * general rule* is that any of the expression forms above, starting with
136
- <code >.id</code >, are treated exactly * as if* they were prefixed by a fresh
137
- identifier <code >* X* </code > which denotes an accessible type alias for the
138
- greatest closure of the context type scheme of the following primary and
139
- selector chain.
149
+ <code >.id</code >, are treated exactly * as if* they were preceded by a fresh
150
+ prefixed identifier <code >* _ p.C* </code > which denotes the declaration of the type of the
151
+ context type scheme of the entire ` <staticMemberShorthand> ` .
140
152
141
153
#### Type inference
142
154
143
155
First, when inferring types for a ` <postfixExpression> ` of the form
144
- ` <staticMemberShorthand> <selector>* ` with context type scheme * C* , then, if the
145
- ` <staticMemberShorthand> ` has not yet been assigned a * shorthand context* ,
146
- assign * C* as its shorthand context. Then continue as normal. _ This assigns the
156
+ ` <staticMemberShorthand> ` with context type scheme * C* , then assign * C* as
157
+ the shorthand context of the leading ` <staticMemberShorthandHead> ` .
158
+ Then continue inferring a type for the entire ` <staticMemberShorthand> `
159
+ recursively on the chain of selectors of the ` <selector*> ` ,
160
+ in the same way as for a ` <primary> <selector>* ` . _ This assigns the
147
161
context type scheme of the entire, maximal selector chain to the static member
148
- shorthand, and does not change that when recursing on shorter prefixes ._
162
+ shorthand head, moving it past any intermediate ` <selector> ` s ._
149
163
150
- _ The effect will be that ` .id… ` will behave exactly like ` T.id… ` where ` T `
164
+ _ The intended effect will be that ` .id… ` will behave exactly like ` T.id… ` where ` T `
151
165
denotes the declaration of the context type._
152
166
153
- ** Definition:** If a shorthand context type schema has the form ` C ` or ` C<...> ` ,
167
+ ** Definition (Declaration denoted by a context type scheme):**
168
+ If a shorthand context type schema has the form ` C ` or ` C<...> ` ,
154
169
and ` C ` is a type introduced by the type declaration * D* , then the shorthand
155
170
context * denotes the type declaration* * D* . If a shorthand context ` S ` denotes a
156
- type declaration * D* , then so does a shorthand context ` S? ` . Otherwise, a
157
- shorthand context does not denote any declaration.
171
+ type declaration * D* , then so does a shorthand context of ` S? ` and ` FutureOr<S> ` .
172
+ Otherwise, a shorthand context does not denote any declaration.
158
173
159
- _ This effectively derives a * declaration* from the context type scheme of the
174
+ _ This effectively derives a single declaration from the context type scheme of the
160
175
surrounding ` <postfixExpression> ` . It allows a nullable context type to denote
161
176
the same as its non-` Null ` type, so that you can use a static member shorthand
162
177
as an argument for optional parameters, or in other contexts where we change a
163
- type to nullable just to allow omitting things ._
178
+ type to nullable just to allow omitting things , and it allows a ` FutureOr<T> ` to_
179
+ _ denoted the same declarations as ` T ` mainly to allow shorthands in returns_
180
+ _ of ` async ` function_ ._
164
181
165
182
** Constant shorthand** : When inferring types for a ` const .id(arguments) ` or
166
183
` const .new(arguments) ` with context type schema * C* , let * D* be the declaration
167
184
denoted by the shorthand context assigned to the ` <staticMemberShorthand> ` . Then
168
- proceed with type inference as if ` .id ` /` .new ` was preceded by an identifier
185
+ proceed with type inference as if ` .id ` /` .new ` was preceded by an identifier ` D `
169
186
denoting the declaration * D* . It’s a compile-time error if the shorthand context
170
- does not denote a class, mixin, enum or extension type declaration.
187
+ does not denote a class, mixin, enum or extension type declaration, or if
188
+ ` D.id ` /` D.new ` does not denote a constant constructor. _ (And since ` mixin ` s cannot
189
+ have constructors, it won't be a ` mixin ` declaration.)_
171
190
172
191
** Non-constant shorthand** : When inferring types for constructs containing the
173
192
non-` const ` production, in every place where the current specification specifies
@@ -183,6 +202,11 @@ assigned to the leading `<staticMemberShorthand>`. It’s a compile-time error i
183
202
the shorthand context does not denote a class, mixin, enum or extension type
184
203
declaration.
185
204
205
+ _ If no selectors were recursed past getting to this point, or only ` ! ` selectors, then
206
+ this expression may have an actual context type. If it was followed by "real" selectors,_
207
+ _ like ` .parse(input).abs() ` , then the recognized expression, ` .parse(input) ` _
208
+ _ here, likely has no context type._
209
+
186
210
Expression forms ` .new<typeArgs> ` or ` .new<typeArgs>(args) ` will always be
187
211
compile-time errors. (The grammar allows them, because it allows any selector to
188
212
follow a static member shorthand, but that static member shorthand must denote a
@@ -205,8 +229,9 @@ be inferred as `List<int>`. In most normal use cases it doesn’t matter, becaus
205
229
the context type will fill in the missing type variables, but if the
206
230
construction is followed by more selectors, it loses that context type. _ It also
207
231
means that the meaning of ` .id ` /` .new ` is * always* the same, it doesn’t matter
208
- whether it’s a constructor or a static member, it’s always preceded by the name
209
- of the declaration denoted by the context.
232
+ whether it’s a constructor or a static member, it’s always implicitly preceded by
233
+ the raw name of the declaration denoted by the context, and any instantiation
234
+ in the context is ignored.
210
235
211
236
The following uses are * not* allowed because they have no shorthand context that
212
237
denotes an allowed type declaration:
@@ -219,33 +244,27 @@ dynamic v3 = .parse("42"); // Context `_`.
219
244
FutureOr<int> v4 = .parse("42"); // Context `FutureOr<int>` is structural type.
220
245
```
221
246
247
+ Since ` ! ` does propagate a context, ` int x = (.tryParse(input))!; ` does work,
248
+ with a context type scheme of ` int? ` , which is enough to allow ` .tryParse ` .
249
+
222
250
#### Special case for ` == `
223
251
224
- For ` == ` , we special-case when the right operand is a static member shorthand.
252
+ For ` == ` , we special-case when the right operand is (precisely!) a static
253
+ member shorthand.
225
254
226
255
If an expression has the form ` e1 == e2 ` or ` e1 != e2 ` , or a pattern has the
227
- form ` == e2 ` , where the static type of ` e1 ` is * S1* and the function signature
228
- of ` operator == ` of ` S1 ` is <code >* R* Function(* T* )</code >, * then* before doing
229
- type inference of ` e2 ` as part of that expression or pattern:
230
-
231
- * If ` e2 ` has the form ` <staticMemberShorthand> <selector>* ` and
232
- <code >* T* </code > is a supertype of ` Object ` ,
233
-
234
- * Then assign * T* as the shorthand context of ` e2 ` .
235
-
236
- _ If the parameter type of the ` == ` operator of the type of ` e1 ` is,
237
- unexpectedly, a proper subtype of ` Object ` (so it's declared ` covariant ` ), it's
238
- assumed that that is the kind of object it should be compared to. Otherwise we
239
- assume the right-hand side should have the same type as the left-hand side, most
240
- likely an enum value._
256
+ form ` == e2 ` , where the static type of ` e1 ` is * S1* , and * e2* is precisely a
257
+ ` <staticMemberShorthand> ` expression, then assign the type * S1* as the
258
+ shorthand context of the ` <staticMemberShorthandHead> ` of * e2* before inferring
259
+ its static type the same way as above.
241
260
242
261
This special-casing is only against an immediate static member shorthand.
243
262
It does not change the * context type* of the second operand, so it would not
244
263
work with, for example, ` Endian.host == wantBig ? .big : .little ` .
245
- Here the second operand is not a ` <staticMemberShorthand> <selector>* ` ,
246
- so it won't have a shorthand context set, and the parameter type of
247
- ` Endian.operator ==` is ` Object ` , so that is the context type of the
248
- second operand.
264
+ Here the second operand is not a ` <staticMemberShorthand> ` ,
265
+ so it won't have a shorthand context set, and the context type of the
266
+ second operand of ` == ` is the empty context. (It's neither the static type of
267
+ the first operand, or the parameter type of the first operand's ` operator== ` .)
249
268
250
269
Examples of allowed comparisons:
251
270
@@ -260,11 +279,19 @@ Not allowed:
260
279
// NOT ALLOWED, ALL `.id`S ARE ERRORS
261
280
if (.host == Endian.host) notOk!; // Dart `==` is not symmetric.
262
281
if (Endian.host == preferLittle ? .little : .big) notOk!; // RHS not shorthand.
263
- if ((Endian.host as Object) == .little) notOk!; // Context type `Object`.
282
+ if ((Endian.host as Object) == .little) notOk!; // Assigned shorthand context type `Object`.
264
283
```
265
284
_ We could consider generally changing the context type of the second operand to
266
285
the static type of the LHS, an aspirational context type, if the parameter type
267
- is not useful._
286
+ is not useful. For now, that's kept as a possible future improvement._
287
+
288
+ If a pattern has the form ` '==' <staticMemberShorthand> ` , then the matched value type
289
+ is assigned as the shorthand context of the leading ` <staticMemberShorthandHead> ` ,
290
+ and the type is inferred in the same way. It's also a compile-time error if the resulting
291
+ expression is not constant, which limits the possible forms.
292
+
293
+ If a ` <staticMemberShorthand> ` occurs in a constant context for any other reason,
294
+ it must also be a constant expression.
268
295
269
296
#### Runtime semantics
270
297
@@ -275,9 +302,8 @@ to constructors, and use those as runtime type arguments to the class, we infer
275
302
the entire target of the member access and use that at runtime._
276
303
277
304
In every case where we inserted a type inference clause, we resolved the
278
- reference to a static member in order to use its type for static type inference.
279
- The runtime semantics then say that it invokes the member found before, and it
280
- works for the ` .id… ` variant too.
305
+ reference to a static member or constructor in order to use its type for static type inference.
306
+ The runtime semantics then say that it invokes the member found before.
281
307
282
308
#### Patterns
283
309
@@ -310,7 +336,7 @@ constant getter or constant constructor invocation. _(There is no chance of a
310
336
method or constructor tear-off having the correct type for the context, but if
311
337
the context type is not enforced for some reason, like being lost in an ** Up**
312
338
computation, it’s technically possible to tear off a static method as a constant
313
- expression. It’s unlikely to succeed dynamic type tests at runtime.)_
339
+ expression. It’s unlikely to succeed at dynamic type tests at runtime.)_
314
340
315
341
An expression without a leading ` const ` is a potential constant and constant
316
342
expression if the corresponding explicit static access would be one. Being a
@@ -323,6 +349,10 @@ Symbol symbol = const .new("orange"); // => const Symbol.new("Orange")
323
349
Endian endian = .big; // => Endian.big.
324
350
```
325
351
352
+ The only selector which can follow a constant ` .id ` /` .new ` or
353
+ ` const .id(args) ` /` const .new(args) ` ` <staticMemberAccessHead> ` and
354
+ still be a constant expression is the ` ! ` selector.
355
+
326
356
## New complications and concerns
327
357
328
358
### Delayed resolution
@@ -508,9 +538,26 @@ probably to `FutureOr<Foo>` too. But it is annoying because of the implicit
508
538
while still allowing a ` FutureOr<F> ` to be returned, as an aspirational
509
539
context type.)
510
540
541
+ We've decided to allow members of ` X ` to be accessed on ` FutureOr<X> ` , but
542
+ not members of ` Future ` . Primarily to allow people to return values from
543
+ ` async ` functions, where we don't * want* to encourage returning ` Future ` s.
544
+
511
545
## Versions
512
546
547
+ 1.2: "Final" decisions:
548
+
549
+ * ` == ` only special-cases second operand, and only if it's precisely a
550
+ shorthand expression. There is no change to the actual context type,
551
+ and no recognition of nested shorthands. You can do ` e == .foo ` ,
552
+ and that's it. Does not depend on parameter type of LHS's ` operator== ` .
553
+ * The static namespace denoted by ` S ` is also the namespace denoted
554
+ by ` S? ` and ` FutureOr<S> ` , nothing more and nothing less.
555
+ * Made grammar be ` <postfixExpression> ` and not share ` <selector>* `
556
+ with ` <primary> ` s. Shouldn't change anything, but makes it clear at which
557
+ grammar production the context type is captured.
558
+
513
559
1.1: Makes ` == ` only special-case second operand.
560
+
514
561
* Keeps context type for second operand, set its shorthand context instead.
515
562
* Remember to mention ` == e ` pattern.
516
563
* Clean-up and reflow.
0 commit comments