Skip to content

Commit f4f7fd6

Browse files
authored
Use separate grammar rules for null-aware elements. (#3812)
Stuffing the `?` directly inside `expressionElement` and `mapEntryElement` lets us reuse some existing specification around leaf elements, but is otherwise confusing because it makes the case analysis for the different kinds of elements non-disjoint. This fixes that by defining entirely separate rules for null-aware elements.
1 parent 482288a commit f4f7fd6

File tree

1 file changed

+82
-36
lines changed

1 file changed

+82
-36
lines changed

working/0323-null-aware-elements/feature-specification.md

Lines changed: 82 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Author: Bob Nystrom
44

55
Status: In-progress
66

7-
Version 0.1
7+
Version 0.2 (see [CHANGELOG](#CHANGELOG) at end)
88

99
Experiment flag: null-aware-elements
1010

@@ -301,21 +301,25 @@ More formally, here is the proposal:
301301

302302
## Syntax
303303

304-
We redefine two rules in the grammar, `expressionElement` and `mapElement` in
305-
terms of a new `nullAwareExpression` rule:
304+
We add two new rules in the grammar and add two new clauses to `element`:
306305

307306
```
308-
expressionElement ::= nullAwareExpression
307+
element ::=
308+
| nullAwareExpressionElement
309+
| nullAwareMapElement
310+
| // Existing productions...
309311
310-
mapElement ::= nullAwareExpression ':' nullAwareExpression
312+
nullAwareExpressionElement ::= '?' expression
311313
312-
nullAwareExpression ::= '?'? expression
314+
nullAwareMapElement ::=
315+
| '?' expression ':' '?'? expression // Null-aware key or both.
316+
| expression ':' '?' expression // Null-aware value.
313317
```
314318

315-
*Note that the inner production of `nullAwareExpression` is `expression` and not
319+
*Note that the productions after `?` in these new rules are `expression` and not
316320
`element`. As with spread elements, null-aware elements can't nest and contain
317-
other elements. These immediately exit the element grammar and bottom out in an
318-
expression. There's no `????foo` or `?if (c) nullableThing else
321+
other elements. These new elements immediately exit the element grammar and
322+
bottom out in an expression. There's no `????foo` or `?if (c) nullableThing else
319323
otherNullableThing`.*
320324

321325
*The `?` character is already overloaded in Dart for nullable types, conditional
@@ -328,12 +332,43 @@ ambiguous.*
328332

329333
## Static semantics
330334

331-
Here and below, we use "null-aware `expressionElement`" to mean an
332-
`expressionElement` beginning with `?`. A "null-aware `mapElement` is a
333-
`mapElement` whose key and/or value parts begin with `?`. A "key null-aware
334-
`mapElement` is a `mapElement` that begins with `?` *(and whose value part may
335-
or may not be null-aware)*. A "value null-aware `mapElement` is a `mapElement`
336-
with a `?` after the `:` *(and whose key part may or may not be null-aware)*.
335+
Here and below, we say a `nullAwareMapElement` "has a null-aware key" if the
336+
`nullAwareMapElement` begins with `?` and "has a null-aware value" if there is a
337+
`?` after the `:`.
338+
339+
### Leaf elements
340+
341+
The existing specification uses *leaf elements* as part of disambiguating map
342+
and set literals. We extend the rules by saying the leaf elements of `element`
343+
are:
344+
345+
* Else, if element is an `nullAwareExpressionElement` or `nullAwareMapEntry`,
346+
then the *leaf element* is `element` itself.
347+
348+
*In other words, just like their non-null-aware forms, null-aware expressions
349+
and map entries are leaf elements.*
350+
351+
When disambiguating map and set literals, we replace the existing "If *leaf
352+
elements* is not empty" step with:
353+
354+
1. Else, if *leaf elements* is not empty, then:
355+
356+
* If *leaf elements* has at least one `expressionElement` or
357+
`nullAwareExpressionElement` and no `mapEntry` or `nullAwareMapEntry`
358+
elements, then *e* is a set literal with unknown static type. The static
359+
type will be filled in by type inference, defined below.
360+
361+
* If *leaf elements* has at least one `mapEntry` or `nullAwareMapEntry`
362+
and no `expressionElement` or `nullAwareExpressionElement` elements,
363+
then *e* is a map literal with unknown static type. The static type will
364+
be filled in by type inference, defined below.
365+
366+
* If leaf elements has at least one `mapEntry` or `nullAwareMapEntry` and
367+
at least one `expressionElement` or `nullAwareExpressionElement`, report
368+
a compile-time error.
369+
370+
*In other words, for map/set disambiguation, null-aware elements behave exactly
371+
like their non-null-aware siblings.*
337372

338373
### Type inference
339374

@@ -346,15 +381,13 @@ flow in and out of the element.
346381
When type inference is flowing through a brace-delimited collection literal, it
347382
is applied to each element. The [existing type inference behavior][type
348383
inference] is mostly unchanged by this proposal. We add two new clauses to
349-
handle null-aware elements. *The existing clauses for `expressionElement` and
350-
`mapElement` are interpreted to only apply to *non*-null-aware elements.* The
351-
new clauses are:
384+
handle null-aware elements:
352385

353386
[type inference]: https://github.com/dart-lang/language/blob/main/accepted/2.3/unified-collections/feature-specification.md#type-inference
354387

355388
To infer the type of `element` in context `P`:
356389

357-
* If `element` is a null-aware `expressionElement` with expression `e1`:
390+
* If `element` is a `nullAwareExpressionElement` with expression `e1`:
358391

359392
* If `P` is `_` (the unknown context):
360393

@@ -370,32 +403,32 @@ To infer the type of `element` in context `P`:
370403
* The inferred set element type is **NonNull**(`U`). *The value added to
371404
the set will never be `null`.*
372405

373-
* If `element` is a null-aware `mapElement` with entry `ek: ev`:
406+
* If `element` is a `nullAwareMapElement` with entry `ek: ev`:
374407

375408
* If `P` is `_` then the inferred key and value types of `element` are:
376409

377410
* Let `Uk` be the inferred type of `ek` in context `_`.
378411

379-
* If `element` is a key null-aware element then the inferred key
380-
element type is **NonNull**(`Uk`). *The entry added to the map will
381-
never have a `null` key.*
412+
* If `element` has a null-aware key then the inferred key element type
413+
is **NonNull**(`Uk`). *The entry added to the map will never have a
414+
`null` key.*
382415

383416
* Else the inferred key element type is `Uk`. *The whole element is
384417
null-aware, but the key part is not, so it is inferred as normal.*
385418

386419
* Let `Uv` be the inferred type of `ev` in context `_`.
387420

388-
* If `element` is a value null-aware element then the inferred value
389-
element type is **NonNull**(`Uv`). *The entry added to the map will
390-
never have a `null` value.*
421+
* If `element` has a null-aware value then the inferred value element
422+
type is **NonNull**(`Uv`). *The entry added to the map will never
423+
have a `null` value.*
391424

392425
* Else the inferred value element type is `Uv`. *The whole element is
393426
null-aware, but the value part is not, so it is inferred as normal.*
394427

395428
* If `P` is `Map<Pk, Pv>` then the inferred key and value types of
396429
`element` are:
397430

398-
* If `element` is a key null-aware element then:
431+
* If `element` has a null-aware key then:
399432

400433
* Let `Uk` be the inferred type of `ek` in context `Pk?`. *The key
401434
expression has a nullable context type because it may safely
@@ -409,7 +442,7 @@ To infer the type of `element` in context `P`:
409442
context `Pk`. *The whole element is null-aware, but the key part is
410443
not, so it is inferred as normal.*
411444

412-
* If `element` is a value null-aware element then:
445+
* If `element` has a null-aware value then:
413446

414447
* Let `Uv` be the inferred type of `ev` in context `Pv?`. *The
415448
value expression has a nullable context type because it may
@@ -435,7 +468,7 @@ Likewise, with list literals, we add a clause to handle a null-aware expression.
435468

436469
To infer the type of `element` in context `P`:
437470

438-
* If `element` is null-aware `expressionElement` with expression `e1`:
471+
* If `element` is a `nullAwareExpressionElement` with expression `e1`:
439472

440473
* If `P` is `_`:
441474

@@ -453,7 +486,7 @@ To infer the type of `element` in context `P`:
453486

454487
### Constants
455488

456-
A null-aware `expressionElement` or `mapElement` is constant if its inner
489+
A `nullAwareExpressionElement` or `nullAwareMapElement` is constant if its inner
457490
expression or map entry is constant.
458491

459492
## Runtime semantics
@@ -466,24 +499,24 @@ add two new cases to that procedure:
466499

467500
[unified-dynamic-element]: https://github.com/dart-lang/language/blob/main/accepted/2.3/unified-collections/feature-specification.md#to-evaluate-a-collection-element
468501

469-
* If `element` is a null-aware `expressionElement` with expression `e`:
502+
* If `element` is a `nullAwareExpressionElement` with expression `e`:
470503

471504
* Evaluate `e` to `v`.
472505

473506
* If `v` is not `null` then append it to *result*. *Else the `null` is
474507
discarded.*
475508

476-
* Else, if `element` is a null-aware `mapElement` with entry `k: v`:
509+
* Else, if `element` is a `nullAwareMapElement` with entry `k: v`:
477510

478511
* Evaluate `k` to a value `kv`.
479512

480-
* If `element` is a key null-aware element and `kv` is `null`, then stop.
481-
Else continue...
513+
* If `element` has a null-aware key and `kv` is `null`, then stop. Else
514+
continue...
482515

483516
* Evaluate `v` to a value `vv`.
484517

485-
* If `element` is a value null-aware element and `vv` is `null`, then
486-
stop. Else continue...
518+
* If `element` has a null-aware value and `vv` is `null`, then stop. Else
519+
continue...
487520

488521
* Append an entry `kv: vv` to *result*.
489522

@@ -539,3 +572,16 @@ use instead:
539572
If any of these patterns can be reliably detected through static analysis, then
540573
quick fixes could be added to automatically convert these to use null-aware
541574
elements instead.
575+
576+
## Changelog
577+
578+
### 0.2
579+
580+
- Use separate grammar rules for null-aware elements instead of allowing
581+
optional `?` inside `expressionElement` and `mapEntryElement`. This only
582+
affects the wording of the specification but not the behavior of the
583+
feature.
584+
585+
### 0.1
586+
587+
- Initial draft.

0 commit comments

Comments
 (0)