Skip to content

Commit 2a3c615

Browse files
authored
Final definite assignment (#1091)
Specifies use of definite assignment to allow imperative initialization of final variables, and various errors around definitely or potentially unassigned variables. Also specifies small changes to promotion and inference.
1 parent b901765 commit 2a3c615

File tree

2 files changed

+123
-25
lines changed

2 files changed

+123
-25
lines changed

accepted/future-releases/nnbd/feature-specification.md

Lines changed: 120 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ Status: Draft
2222
2020.05.20
2323
- Turn new references to `CastError` into being dynamic type errors.
2424

25+
2020.07.21
26+
- **CHANGE** Changes to definite assignment for local variables.
27+
2528
2020.05.14
2629
- **CHANGE** Strong mode is auto-opted in when the "main" file is opted in.
2730
- **CHANGE** Specify weak mode/strong mode flag.
@@ -498,23 +501,23 @@ It is an error to call an expression whose type is potentially nullable and not
498501
`dynamic`.
499502

500503
It is an error if a top level variable or static variable with a non-nullable
501-
type has no initializer expression unless the variable is marked with a
502-
`late` or `external` modifier.
504+
type has no initializer expression unless the variable is marked with a `late`
505+
or `external` modifier.
503506

504507
It is an error if a class declaration declares an instance variable with a
505-
potentially non-nullable type and no initializer expression, and the class has
506-
a generative constructor where the variable is not initialized via an
507-
initializing formal or an initializer list entry, unless the variable is marked
508-
with a `late`, `abstract`, or `external` modifier.
508+
potentially non-nullable type and no initializer expression, and the class has a
509+
generative constructor where the variable is not initialized via an initializing
510+
formal or an initializer list entry, unless the variable is marked with a
511+
`late`, `abstract`, or `external` modifier.
509512

510513
It is an error if a mixin declaration or a class declaration with no generative
511514
constructors declares an instance variable without an initializing expression
512-
which is final or whose type is potentially non-nullable, unless the variable
513-
is marked with a `late`, `abstract`, or `external` modifier.
515+
which is final or whose type is potentially non-nullable, unless the variable is
516+
marked with a `late`, `abstract`, or `external` modifier.
514517

515-
It is an error if a potentially non-nullable local variable which has no
516-
initializer expression and is not marked `late` is used before it is definitely
517-
assigned (see Definite Assignment below).
518+
It is an error to derive a mixin from a class declaration which contains an
519+
instance variable with a potentially non-nullable type and no initializer
520+
expression unless the variable is marked with the `late` modifier.
518521

519522
It is an error if the body of a method, function, getter, or function expression
520523
with a potentially non-nullable return type **may complete normally**.
@@ -568,18 +571,9 @@ expression.
568571
It is an error for a class with a `const` constructor to have a `late final`
569572
instance variable.
570573

571-
It is not a compile time error to write to a `final` variable if that variable
572-
is declared `late` and does not have an initializer.
573-
574-
It is a compile time error to assign a value to a local variable marked `late`
575-
and `final` when the variable is **definitely assigned**. This includes all
576-
forms of assignments, including assignments via the composite assignment
577-
operators as well as pre and post-fix operators.
578-
579-
It is a compile time error to read a local variable marked `late` when the
580-
variable is **definitely unassigned**. This includes all forms of reads,
581-
including implicit reads via the composite assignment operators as well as pre
582-
and post-fix operators.
574+
It is not a compile time error to write to a `final` non-local or instance
575+
variable if that variable is declared `late` and does not have an initializer.
576+
For local variables, see the section below.
583577

584578
It is an error if the object being iterated over by a `for-in` loop has a static
585579
type which is not `dynamic`, and is not a subtype of `Iterable<dynamic>`.
@@ -628,6 +622,109 @@ It is a warning to use a null aware operator (`?.`, `?[]`, `?..`, `??`, `??=`, o
628622
It is a warning to use the null check operator (`!`) on an expression of type
629623
`T` if `T` is **strictly non-nullable** .
630624

625+
### Local variables and definite (un)assignment.
626+
627+
As part of the null safety release, errors for local variables are specified to
628+
take into account **definite assignment** and **definite unassignment** (see the
629+
section on Definite Assignment below). We say that a variable is **potentially
630+
assigned** if it is not **definitely unassigned**, and that a variable is
631+
**potentially unassigned** if it is not **definitely assigned**.
632+
633+
In all cases in this section, errors that are described as occurring on reads of
634+
a variable are intended to apply to all form of reads, including indirectly as
635+
part of compound assignment operators, as well as via pre and post-fix
636+
operators. Similarly, errors that are described as occurring on writes of a
637+
variable are intended to apply to all form of writes.
638+
639+
It is a compile time error to assign a value to a `final`, non-`late` local
640+
variable which is **potentially assigned**. Thus, it is *not* a compile time
641+
error to assign to a **definitely unassigned** `final` local variable.
642+
643+
It is a compile time error to assign a value to a `final`, `late` local variable
644+
if it is **definitely assigned**. Thus, it is *not* a compile time error to
645+
assign to a **potentially unassigned** `final`, `late` local variable.
646+
647+
*Note that a variable is always considered **definitely assigned** and not
648+
**definitely unassigned** if it has an explicit initializer, or an implicit
649+
initializer as part of a larger construct (e.g. the loop variable in a `for in`
650+
construct).*
651+
652+
It is a compile time error to read a local variable when the variable is
653+
**definitely unassigned** unless the variable is non-`final`, and non-`late`,
654+
and has nullable type.
655+
656+
It is a compile time error to read a local variable when the variable is
657+
**potentially unassigned** unless the variable is non-`final` and has nullable
658+
type, or is `late`.
659+
660+
The errors specified above are summarized in the following table, where `int` is
661+
used as an example of an arbitrary **potentially non-nullable** type, `int?` is
662+
used as an example of an arbitrary **nullable** type, and `T` is used to stand
663+
for a type of any nullability. A variable which has an initializer (explicit or
664+
implicit) is always considered definitely assigned, and is never considered
665+
definitely unassigned.
666+
667+
668+
Read Behavior:
669+
670+
| Declaration form | Def. Assigned | Neither | Def. Unassigned |
671+
| ----------------- | ------------- | ----------------- | --------------- |
672+
| var x; | Ok | Ok | Ok |
673+
| final x; | Ok | Error | Error |
674+
| int x; | Ok | Error | Error |
675+
| int? x; | Ok | Ok | Ok |
676+
| final T x; | Ok | Error | Error |
677+
| late var x; | Ok | Ok | Error |
678+
| late final x; | Ok | Ok | Error |
679+
| late T x; | Ok | Ok | Error |
680+
| late final T x; | Ok | Ok | Error |
681+
682+
Write Behavior:
683+
684+
| Declaration form | Def. Assigned | Neither | Def. Unassigned |
685+
| ----------------- | ------------- | ------------------- | --------------- |
686+
| var x; | Ok | Ok | Ok |
687+
| final x; | Error | Error | Ok |
688+
| int x; | Ok | Ok | Ok |
689+
| int? x; | Ok | Ok | Ok |
690+
| final T x; | Error | Error | Ok |
691+
| late var x; | Ok | Ok | Ok |
692+
| late final x; | Error | Ok | Ok |
693+
| late T x; | Ok | Ok | Ok |
694+
| late final T x; | Error | Ok | Ok |
695+
696+
### Local variables and inference
697+
698+
Local variables with explicitly written types are given the declared types as
699+
written. The declared type of the variable is considered a "type of interest"
700+
in the sense defined in the flow analysis specification. If the variable has an
701+
initializer (explicit or implicit) and is not `final`, then the declaration is
702+
treated as an assignment for the purposes of promotion.
703+
704+
*Treating the declared type of the variable as a "type of interest" implies that
705+
if the variable has a nullable type, then the non-nullable version of that type
706+
is also a type of interest. Treating the initialization as an assignment for
707+
the purposes of promotion means that initializing a mutable variable declared at
708+
type `T?` with a value of non-nullable type `T` immediately promotes the
709+
variable to the non-nullable type.*
710+
711+
```dart
712+
void test() {
713+
int? x = 3; // x is declared at `int?`
714+
x.isEven; // Valid, x has been promoted to `int`
715+
x = null; // Valid, demotes to the declared type.
716+
}
717+
```
718+
719+
Local variables with no explicitly written type but with an initializer are
720+
given an inferred type equal to the type of their initializer, unless that type
721+
is a subtype of `Null`, in which case the inferred type of the variable shall be
722+
`dynamic`. The inferred type of the variable is considered a "type of interest"
723+
in the sense defined in the flow analysis specification. In the case that the
724+
type of the initializer is a promoted type variable `X & T`, the inferred type
725+
of the variable shall be `X`. However, such a variable shall be treated as
726+
immediately promoted to `X & T`.
727+
631728
### Expression typing
632729

633730
It is permitted to invoke or tear-off a method, setter, getter, or operator that

resources/type-system/inference.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,9 @@ The general inference procedure is as follows.
149149
- Record the type of `x` and mark `x` as *available*.
150150
- Otherwise, if `D` has an initializing expression `e`:
151151
- Perform local type inference on `e`.
152-
- Record the type of `x` to be the inferred type of `e`, and mark `x` as
153-
*available*.
152+
- Let `T` be the inferred type of `e`, or `dynamic` if the inferred type
153+
of `e` is a subtype of `Null`. Record the type of `x` to be `T` and
154+
mark `x` as *available*.
154155
- Otherwise record the type of `x` to be `dynamic` and mark `x` as
155156
*available*.
156157
- If `D` is a constructor declaration `C(...)` for which one or more of the

0 commit comments

Comments
 (0)