Skip to content

Commit da126ec

Browse files
[flow analysis] Rework demotion and type of interest promotion. (#4370)
This formulation is consisent with the behavior of the implementation. Fixes dart-lang/sdk#60646.
1 parent 5481b56 commit da126ec

File tree

1 file changed

+103
-40
lines changed

1 file changed

+103
-40
lines changed

resources/type-system/flow-analysis.md

Lines changed: 103 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,14 @@ that assignment).
110110
`b`.
111111
- We use the notation `[...l, a]` where `l` is a list to denote a list
112112
beginning with all the elements of `l` and followed by `a`.
113+
- A list of types `p` is called a _promotion chain_ iff, for all `i < j`,
114+
`p[i] <: p[j]`. _Note that since the subtyping relation is transitive, in
115+
order to establish that `p` is a promotion chain, it is sufficient to check
116+
the `p[i] <: p[j]` relation for each adjacent pair of types._
117+
- A promotion chain `p` is said to be _valid for declared type `T`_ iff every
118+
type in `p` is a subtype of `T`. _Note that since the subtyping relation is
119+
transitive, in order to establish that `p` is valid for declared type `T`,
120+
it is sufficient to check that the first type in `p` is a subtype of `T`._
113121

114122
- Stacks
115123
- We use the notation `push(s, x)` to mean pushing `x` onto the top of the
@@ -123,18 +131,18 @@ that assignment).
123131

124132
### Models
125133

126-
A *variable model*, denoted `VariableModel(declaredType, promotedTypes,
134+
A *variable model*, denoted `VariableModel(declaredType, promotionChain,
127135
tested, assigned, unassigned, writeCaptured)`, represents what is statically
128136
known to the flow analysis about the state of a variable at a given point in the
129137
source code.
130138

131139
- `declaredType` is the type assigned to the variable at its declaration site
132140
(either explicitly or by type inference).
133141

134-
- `promotedTypes` is a list of types that the variable has been promoted to,
135-
with the final type in the list being the current promoted type of the
136-
variable. Note that each type in the list must be a subtype of all previous
137-
types, and of the declared type.
142+
- `promotionChain` is the variable's promotion chain. This is a list of types
143+
that the variable has been promoted to, with the final type in the list being
144+
the current promoted type of the variable. It must always be a valid promotion
145+
chain for declared type `declaredType`.
138146

139147
- `tested` is a set of types which are considered "of interest" for the purposes
140148
of promotion, generally because the variable in question has been tested
@@ -397,8 +405,8 @@ We also make use of the following auxiliary functions:
397405
Promotion policy is defined by the following operations on flow models.
398406

399407
We say that the **current type** of a variable `x` in variable model `VM` is `S` where:
400-
- `VM = VariableModel(declared, promoted, tested, assigned, unassigned, captured)`
401-
- `promoted = [...l, S]` or (`promoted = []` and `declared = S`)
408+
- `VM = VariableModel(declared, promotionChain, tested, assigned, unassigned, captured)`
409+
- `promotionChain = [...l, S]` or (`promotionChain = []` and `declared = S`)
402410

403411
Policy:
404412
- We say that at type `T` is a type of interest for a variable `x` in a set of
@@ -407,46 +415,91 @@ Policy:
407415

408416
- We say that a variable `x` is promotable via type test with type `T` given
409417
variable model `VM` if
410-
- `VM = VariableModel(declared, promoted, tested, assigned, unassigned, captured)`
418+
- `VM = VariableModel(declared, promotionChain, tested, assigned, unassigned, captured)`
411419
- and `captured` is false
412420
- and `S` is the current type of `x` in `VM`
413421
- and not `S <: T`
414422
- and `T <: S` or (`S` is `X extends R` and `T <: R`) or (`S` is `X & R` and
415423
`T <: R`)
416424

417-
- We say that a variable `x` is promotable via assignment of an expression of
418-
type `T` given variable model `VM` if
419-
- `VM = VariableModel(declared, promoted, tested, assigned, unassigned, captured)`
420-
- and `captured` is false
421-
- and `S` is the current type of `x` in `VM`
422-
- and `T <: S` and not `S <: T`
423-
- and `T` is a type of interest for `x` in `tested`
424-
425-
- We say that a variable `x` is demotable via assignment of an expression of
426-
type `T` given variable model `VM` if
427-
- `VM = VariableModel(declared, promoted, tested, assigned, unassigned, captured)`
428-
- and `captured` is false
429-
- and [...promoted, declared] contains a type `S` such that `T` is `S` or
430-
`T` is **NonNull(`S`)**.
431-
432425
Definitions:
433426

427+
- `demote(promotionChain, written)`, is a promotion chain obtained by deleting
428+
any elements from `promotionChain` that do not satisfy `written <: T`. _In
429+
effect, this removes any type promotions that are no longer valid after the
430+
assignment of a value of type `written`._
431+
- _Note that if `promotionChain` is valid for declared type `T`, it follows
432+
that `demote(promotionChain, written)` is also valid for declared type `T`._
433+
434+
- `toi_promote(declared, promotionChain, tested, written)`, where `declared` and
435+
`written` are types satisfying `written <: declared`, `promotionChain` is
436+
valid for declared type `declared`, and all types `T` in `promotionChain`
437+
satisfy `written <: T`, is the promotion chain `newPromotionChain`, defined as
438+
follows. _("toi" stands for "type of interest".)_
439+
- Let `provisionalType` be the last type in `promotionChain`, or `declared` if
440+
`promotionChain` is empty. _(This is the type of the variable after
441+
demotions, but before type of interest promotion.)_
442+
- _Since the last type in a promotion chain is a subtype of all the others,
443+
it follows that all types `T` in `promotionChain` satisfy `provisionalType
444+
<: T`._
445+
- If `written` and `provisionalType` are the same type, then
446+
`newPromotionChain` is `promotionChain`. _(No type of interest promotion is
447+
necessary in this case.)_
448+
- Otherwise _(when `written` is not `provisionalType`)_, let `p1` be a set
449+
containing the following types:
450+
- **NonNull**(`declared`), if it is not the same as `declared`.
451+
- For each type `T` in the `tested` list:
452+
- `T`
453+
- **NonNull**(`T`)
454+
455+
_The types in `p1` are known as the types of interest._
456+
- Let `p2` be the set `p1 \ { provisionalType }` _(where `\` denotes set
457+
difference)_.
458+
- If the `written` type is in `p2`, and `written <: provisionalType`, then
459+
`newPromotionChain` is `[...promotionChain, written]`. _Writing a value
460+
whose static type is a type of interest promotes to that type._
461+
- _By precondition, `written <: declared` and `written <: T` for all types
462+
in `promotionChain`. Therefore, `newPromotionChain` satisfies the
463+
definition of a promotion chain, and is valid for declared type
464+
`declared`._
465+
- Otherwise _(when `written` is not in `p2`)_:
466+
- Let `p3` be the set of all types `T` in `p2` such that `written <: T <:
467+
provisionalType`.
468+
- If `p3` contains exactly one type `T` that is a subtype of all the others,
469+
then `promoted` is `[...promotionChain, T]`. _Writing a value whose static
470+
type is a subtype of a type of interest promotes to that type of interest,
471+
provided there is a single "best" type of interest available to promote
472+
to._
473+
- _Since `T <: provisionalType <: declared`, and all types `U` in
474+
`promotionChain` satisfy `provisionalType <: U`, it follows that all
475+
types `U` in `promotionChain` satisfy `T <: U`. Therefore
476+
`newPromotionChain` satisfies the definition of a promotion chain, and
477+
is valid for declared type `declared`._
478+
- Otherwise, `newPromotionChain` is `promotionChain`. _If there is no single
479+
"best" type of interest to promote to, then no type of interest promotion
480+
is done._
481+
434482
- `assign(x, E, M)` where `x` is a local variable, `E` is an expression of
435-
inferred type `T`, and `M = FlowModel(r, VI)` is the flow model for `E` is
436-
defined to be `FlowModel(r, VI[x -> VM])` where:
437-
- `VI(x) = VariableModel(declared, promoted, tested, assigned, unassigned, captured)`
438-
- if `captured` is true then:
439-
- `VM = VariableModel(declared, promoted, tested, true, false, captured)`.
440-
- otherwise if `x` is promotable via assignment of `E` given `VM`
441-
- `VM = VariableModel(declared, [...promoted, T], tested, true, false,
442-
captured)`.
443-
- otherwise if `x` is demotable via assignment of `E` given `VM`
444-
- `VM = VariableModel(declared, demoted, tested, true, false, captured)`.
445-
- where `previous` is the prefix of `promoted` ending with the first type
446-
`S` such that `T <: S`, and:
447-
- if `S` is nullable and if `T <: Q` where `Q` is **NonNull(`S`)** then
448-
`demoted` is `[...previous, Q]`
449-
- otherwise `demoted` is `previous`
483+
inferred type `T` (which must be a subtype of `x`'s declared type), and `M =
484+
FlowModel(r, VI)` is the flow model for `E` is defined to be `FlowModel(r,
485+
VI[x -> VM])` where:
486+
- `VI(x) = VariableModel(declared, promoted, tested, assigned, unassigned,
487+
captured)`
488+
- If `captured` is true then:
489+
- `VM = VariableModel(declared, promotionChain, tested, true, false, captured)`.
490+
- Otherwise:
491+
- Let `written = T`.
492+
- Let `promotionChain' = demote(promotionChain, written)`.
493+
- Let `promotionChain'' = toi_promote(declared, promotionChain', tested,
494+
written)`.
495+
- _The preconditions for `toi_promote` are satisfied because:_
496+
- _`demote` deletes any elements from `promotionChain` that do not
497+
satisfy `written <: T`, therefore every element of `promotionChain'`
498+
satisfies `written <: T`._
499+
- _`written = T` and `T` is a subtype of `x`'s declared type, therefore
500+
`written <: declared`._
501+
- Then `VM = VariableModel(declared, promotionChain'', tested, true, false,
502+
captured)`.
450503

451504
- `stripParens(E1)`, where `E1` is an expression, is the result of stripping
452505
outer parentheses from the expression `E1`. It is defined to be the
@@ -551,7 +604,12 @@ then they are all assigned the same value as `after(N)`.
551604
- **Local-variable assignment**: If `N` is an expression of the form `x = E1`
552605
where `x` is a local variable, then:
553606
- Let `before(E1) = before(N)`.
554-
- Let `after(N) = assign(x, E1, after(E1))`.
607+
- Let `E1'` be the result of applying type coercion to `E1`, to coerce it to
608+
the declared type of `x`.
609+
- Let `after(N) = assign(x, E1', after(E1))`.
610+
- _Since type coercion to type `T` produces an expression whose static type
611+
is a subtype of `T`, the precondition of `assign` is satisfied, namely
612+
that the static type of `E1'` must be a subtype of `x`'s declared type._
555613

556614
- **operator==** If `N` is an expression of the form `E1 == E2`, where the
557615
static type of `E1` is `T1` and the static type of `E2` is `T2`, then:
@@ -617,7 +675,12 @@ then they are all assigned the same value as `after(N)`.
617675
- **Local variable conditional assignment**: If `N` is an expression of the form
618676
`x ??= E1` where `x` is a local variable, then:
619677
- Let `before(E1) = split(promote(x, Null, before(N)))`.
620-
- Let `M1 = assign(x, E1, after(E1))`
678+
- Let `E1'` be the result of applying type coercion to `E1`, to coerce it to
679+
the declared type of `x`.
680+
- Let `M1 = assign(x, E1', after(E1))`
681+
- _Since type coercion to type `T` produces an expression whose static type
682+
is a subtype of `T`, the precondition of `assign` is satisfied, namely
683+
that the static type of `E1'` must be a subtype of `x`'s declared type._
621684
- Let `M2 = split(promoteToNonNull(x, before(N)))`
622685
- Let `after(N) = merge(M1, M2)`
623686

0 commit comments

Comments
 (0)