Skip to content

Commit 9cb7966

Browse files
Simplify reasoning about await soundness using a lemma about flatten. (#3920)
The lemma is that for all types `T`, `T <: FutureOr<flatten(T)>`. The lemma was claimed (without proof) in `dartLangSpec.tex`, so I've added a (non-normative) proof of it to the spec, right after the claim. This made it possible to significantly simplify the reasoning around the soundness of `await` in `inference.md`. Thanks to @lrhn for the core idea behind this.
1 parent 69ecf0f commit 9cb7966

File tree

2 files changed

+100
-100
lines changed

2 files changed

+100
-100
lines changed

resources/type-system/inference.md

Lines changed: 27 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,22 +1046,33 @@ The elaboration process sometimes introduces new operations that are not easily
10461046
expressible using the syntax of Dart. To allow these operations to be specified
10471047
succintly, the syntax of Dart is extended to allow the following forms:
10481048

1049-
- `@AWAIT_WITH_TYPE_CHECK<T>(m_1)`, represents the following operation:
1049+
- `@AWAIT_WITH_TYPE_CHECK(m_1)` represents the following operation:
10501050

1051-
- Evaluate `m_1`, and let `v` denote the result.
1051+
- Let `T_1` be the static type of `m_1`.
10521052

1053-
- If `v` is an instance satisfying type `Future<T>`, then
1054-
`@AWAIT_WITH_TYPE_CHECK<T>(m_1)` evaluates to `v`.
1053+
- Let `T` be `flatten(T_1)`.
10551054

1056-
- Otherwise, let `u` be a future satisfying type `Future<T>` that will
1057-
complete to the value `v` at some later point. Then,
1058-
`@AWAIT_WITH_TYPE_CHECK<T>(m_1)` evaluates to `u`.
1055+
- Evaluate `m_1`, and let `v` denote the result. _Note that since `m_1` has
1056+
static type `T_1`, by soundness, `v` must be an instance satisfying
1057+
`T_1`. Since `T_1 <: FutureOr<flatten(T_1)>` for all `T_1`, it follows that
1058+
`v` must be an instance satisfying `FutureOr<flatten(T_1)>`, or
1059+
equivalently, satisfying `FutureOr<T>`. Therefore, `v` must either be an
1060+
instance satisfying either `Future<T>` or `T`. We consider the two cases
1061+
below._
10591062

1060-
- _TODO(paulberry): explain why such a future is guaranteed to (soundly)
1061-
exist, by stipulating that if `T_1` is the static type of `m_1`, then `T`
1062-
must be `flatten(T_1)`, and then proving a lemma that `T_1 <:
1063-
FutureOr<flatten(T_1)>`; therefore `T_1 <: FutureOr<T>`, so in this
1064-
"otherwise" case, `v` must be an instance satisfying `T`._
1063+
- If `v` is an instance satisfying type `Future<T>`, then let
1064+
`@AWAIT_WITH_TYPE_CHECK(m_1)` evaluate to `v`.
1065+
1066+
- Otherwise, let `@AWAIT_WITH_TYPE_CHECK(m_1)` evaluate to a future satisfying
1067+
type `Future<T>` that will complete to the value `v` at some later
1068+
point. _Such a future could, for instance, be created by executing
1069+
`Future<T>.value(v)` (this is sound because in this case `v` is an instance
1070+
satisfying `T`). It also could be created using a more efficient
1071+
implementation-specific mechanism._
1072+
1073+
- _Note that these two cases in the abstract correspond concretely to a type
1074+
check in the implementation; this where the name `@AWAIT_WITH_TYPE_CHECK`
1075+
comes from._
10651076

10661077
- `@CONCAT(m_1, m_2, ..., m_n)`, where each `m_i` is an elaborated expression
10671078
whose static type is a subtype of `String`, represents the operation of
@@ -1389,99 +1400,17 @@ are determined as follows:
13891400

13901401
- Let `T_2` be `flatten(T_1)`.
13911402

1392-
- Let `m_2` be `@AWAIT_WITH_TYPE_CHECK<T_2>(m_1)`, with static type
1393-
`Future<T_2>`.
1403+
- Let `m_2` be `@AWAIT_WITH_TYPE_CHECK(m_1)`, with static type
1404+
`Future<T_2>`. _This is sound because `@AWAIT_WITH_TYPE_CHECK(m_1)` always
1405+
evaluates to an instance satisfying type `Future<T_2>`._
13941406

13951407
- _Note that in many circumstances, it will be trivial for the compiler to
13961408
establish that `m_1` always evaluates to an instance `v` that satisfies type
13971409
`Future<T_2>`, in which case `m_2` can be optimized to
13981410
`@PROMOTED_TYPE<Future<T_2>>(m_1)`._
13991411

1400-
- _For soundness, we must prove that whenever
1401-
`@AWAIT_WITH_TYPE_CHECK<T_2>(m_1)` evaluates `m_1` to a value `v` that is
1402-
__not__ an instance satisfying type `Future<T_2>`, that `v` __is__ an
1403-
instance satisfying type `T_2`. This is necessary to ensure that the future
1404-
created by `@AWAIT_WITH_TYPE_CHECK` will complete to a value satisfying its
1405-
type signature. Note that `v` is guaranteed to be an instance satisfying
1406-
type `T_1` (because `T_1` is the static type of `m_1`). So we can establish
1407-
soundness by assuming that `v` is an instance satisfying type `T_1` and not
1408-
an instance satisfying type `Future<T_2>`, and then considering two cases:_
1409-
1410-
- _TODO(paulberry): it should be possible to simplify and clarify this
1411-
section once we have proven that `T <: FutureOr<flatten(T)>` for all types
1412-
`T`._
1413-
1414-
- _If the runtime value of `v` is `null`, then by soundness, `T_1` must be
1415-
of the form `Null`, `dynamic`, `S*`, or `S?`. Considering each of these:_
1416-
1417-
- _If `T_1` is of the form `Null` or `dynamic`, then by the definition of
1418-
`flatten`, `T_2` must be the same as `T_1`. Therefore, `v` is an
1419-
instance satisfying type `T_2`, so soundness is satisfied._
1420-
1421-
- _If `T_1` is of the form `S*` or `S?`, then by the definition of
1422-
`flatten`, `T_2` must be of the form `flatten(S)*` or `flatten(S)?`,
1423-
respectively. `null` is an instance satisfying all types ending in `*`
1424-
and `?`, so soudness is satisfied._
1425-
1426-
- _Otherwise, we need to show that if `v` is a non-null instance satisfying
1427-
type `T_1`, but not an instance satisfying type `Future<T_2>`, then `v` is
1428-
an instance satisfying type `T_2`._
1429-
1430-
- _Substituting in the definition of `T_2`, we need to show that if `v` is a
1431-
non-null instance satisfying type `T_1`, but not an instance satisfying
1432-
type `Future<flatten(T_1)>`, then `v` is an instance satisfying type
1433-
`flatten(T_1)`. We can prove this by induction on `T_1`:_
1434-
1435-
- _If `T_1` is `S?`, then `flatten(T_1)` is `flatten(S)?`. We need to show
1436-
that if `v` is a non-null instance satisfying type `S?`, but not an
1437-
instance satisfying type `Future<flatten(S)?>`, then `v` is an instance
1438-
satisfying type `flatten(S)?`. Assuming `v` is a non-null instance
1439-
satisfying type `S?`, it must be a non-null instance satisfying type
1440-
`S`. Assuming `v` is not an instance satisfying type
1441-
`Future<flatten(S)?>`, it follows that `v` is not an instance satisfying
1442-
type `Future<flatten(S)>`. So we have satisfied the premise of the
1443-
induction hypothesis using `T_1 = S`, and therefore by induction, `v` is
1444-
an instance satisfying type `flatten(S)`. This in turn implies that `v`
1445-
is an instance satisfying type `flatten(S)?`._
1446-
1447-
- _(Same argument but with `?` replaced by `*`): If `T_1` is `S*`, then
1448-
`flatten(T_1)` is `flatten(S)*`. We need to show that if `v` is a
1449-
non-null instance satisfying type `S*`, but not an instance satisfying
1450-
type `Future<flatten(S)*>`, then `v` is an instance satisfying type
1451-
`flatten(S)*`. Assuming `v` is a non-null instance satisfying type `S*`,
1452-
it must be a non-null instance satisfying type `S`. Assuming `v` is not
1453-
an instance satisfying type `Future<flatten(S)*>`, it follows that `v`
1454-
is not an instance satisfying type `Future<flatten(S)>`. So we have
1455-
satisfied the premise of the induction hypothesis using `T_1 = S`, and
1456-
therefore by induction, `v` is an instance satisfying type
1457-
`flatten(S)`. This in turn implies that `v` is an instance satisfying
1458-
type `flatten(S)*`._
1459-
1460-
- _If `T_1` is `FutureOr<S>`, then `flatten(T_1)` is `S`. We need to show
1461-
that if `v` is a non-null instance satisfying type `FutureOr<S>`, but
1462-
not an instance satisfying type `Future<S>`, then `v` is an instance
1463-
satisfying type `S`. This is trivially true, because `FutureOr<S>` is
1464-
the union of types `S` and `Future<S>`._
1465-
1466-
- _If `T_1 <: Future`, then `flatten(T_1)` is `S`, where `S` is a type
1467-
such that `T_1 <: Future<S>` and for all `R`, if `T_1 <: Future<R>` then
1468-
`S <: R`. We need to show that if `v` is a non-null instance satisfying
1469-
type `T_1`, but not an instance satisfying type `Future<S>`, then `v` is
1470-
an instance satisfying type `S`. Assuming `v` is a non-null instance
1471-
satisfying type `T_1`, it must also be a non-null instance satisfying
1472-
type `Future<S>` (because `T_1 <: Future<S>`). But this contradicts the
1473-
assumption that `v` is __not__ an instance satisfying type `Future<S>`,
1474-
so this case is impossible._
1475-
1476-
- _Finally, if none of the above cases are satisfied, then `flatten(T_1)`
1477-
is `T_1`. We need to show that if `v` is a non-null instance satisfying
1478-
type `T_1`, but not an instance satisfying type `Future<T_1>`, then `v`
1479-
is an instance satisfying type `T_1`. This is trivially true, since if
1480-
`v` is a non-null instance satisfying type `T_1`, it must be an instance
1481-
satisfying type `T_1`._
1482-
14831412
- Let `T` be `T_2`, and let `m` be `await m_2`. _Since `m_2` has static type
1484-
`Future<m_2>`, the value of `await m_2` must necessarily be an instance
1413+
`Future<T_2>`, the value of `await m_2` must necessarily be an instance
14851414
satisfying type `T_2`, so soundness is satisfied._
14861415

14871416
<!--

specification/dartLangSpec.tex

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11794,7 +11794,41 @@ \subsection{Function Expressions}
1179411794
\commentary{%
1179511795
Note that if $T$ derives a future type $F$ then \SubtypeNE{T}{F},
1179611796
and $F$ is always of the form \code{$G$<...>} or \code{$G$<...>?},
11797-
where $G$ is \code{Future} or \code{FutureOr}.
11797+
where $G$ is \code{Future} or \code{FutureOr}. The proof is by induction on the
11798+
structure of $T$:
11799+
11800+
\begin{itemize}
11801+
\item
11802+
%% TODO(eernst): Come mixin classes and extension types: add them.
11803+
If $T$ is a type which is introduced by
11804+
a class, mixin, or enum declaration,
11805+
and if $T$ or a direct or indirect superinterface
11806+
(\ref{interfaceSuperinterfaces})
11807+
of $T$ is \code{Future<$U$>} for some $U$, then, letting
11808+
\code{$G =$ Future} and \code{$F = G$<$U$>}, $T <: F$.
11809+
\item
11810+
If $T$ is the type \code{FutureOr<$U$>} for some $U$, then by reflexivity,
11811+
\code{$T <:$ FutureOr<$U$>}. Letting \code{$G =$ FutureOr} and
11812+
\code{$F = G$<$U$>}, it follows that $T <: F$.
11813+
\item
11814+
If $T$ is \code{$S$?} for some $S$, and
11815+
$S$ derives the future type $F'$,
11816+
then by the induction hypothesis, \code{$S <: F'$}, where $F'$ is of the form
11817+
\code{$G'$<$U$>} or \code{$G'$<$U$>?} and $G'$ is \code{Future} or
11818+
\code{FutureOr}. Therefore, \code{$S$? $<: F'$?}, and by substitution,
11819+
\code{$T <: F'$?}. Since \code{$T$?? $= T$?} for all $T$, it follows that
11820+
\code{$T <: G'$<$U$>?}. So, letting $G = G'$ and \code{$F = G$<$U$>?},
11821+
it follows that $T <: F$.
11822+
\item
11823+
If $T$ is a type variable with bound $B$, and
11824+
$B$ derives the future type $F$,
11825+
then by the induction hypothesis, \code{$B <: F'$}, where $F'$ is of the form
11826+
\code{$G'$<$U$>} or \code{$G'$<$U$>?} and $G'$ is \code{Future} or
11827+
\code{FutureOr}. Also, since $B$ is the bound of $T$, $T <: B$, so by
11828+
transitivity, $T <: F'$. Therefore, letting $G = G'$ and $F = F'$, it
11829+
follows that $T <: F$.
11830+
\end{itemize}
11831+
1179811832
Also note that 'derives' in this context refers to the computation
1179911833
where a type $T$ is given, the supertypes of $T$ are searched,
1180011834
and a type $F$ of one of those forms is selected.
@@ -11829,7 +11863,44 @@ \subsection{Function Expressions}
1182911863

1183011864
\rationale{%
1183111865
This definition guarantees that for any type $T$,
11832-
\code{$T <:$ FutureOr<$\flatten{T}$>}.%
11866+
\code{$T <:$ FutureOr<$\flatten{T}$>}. The proof is by induction on the
11867+
structure of $T$:
11868+
11869+
\begin{itemize}
11870+
\item If $T$ is \code{$X$\,\&\,$S$} then
11871+
\begin{itemize}
11872+
\item if $S$ derives a future type $U$,
11873+
then \code{$T <: S$} and \code{$S <: U$}, so \code{$T <: U$}.
11874+
By the induction hypothesis, \code{$U <:$ FutureOr<$\flatten{U}$>}.
11875+
Since \code{$\flatten{T} = \flatten{U}$} in this case, it follows that
11876+
\code{$U <:$ FutureOr<$\flatten{T}$>}, and so
11877+
\code{$T <:$ FutureOr<$\flatten{T}$>}.
11878+
\item otherwise, \code{$T <: X$}.
11879+
By the induction hypothesis, \code{$X <:$ FutureOr<$\flatten{X}$>}.
11880+
Since \code{$\flatten{T} = \flatten{X}$} in this case, it follows that
11881+
\code{$U <:$ FutureOr<$\flatten{T}$>}, and so
11882+
\code{$T <:$ FutureOr<$\flatten{T}$>}.
11883+
\end{itemize}
11884+
11885+
\item If $T$ derives a future type \code{Future<$S$>}
11886+
or \code{FutureOr<$S$>}, then, since \code{Future<$S$> $<:$ FutureOr<$S$>},
11887+
it follows that \code{$T <:$ FutureOr<$S$>}. Since \code{$\flatten{T} = S$}
11888+
in this case, it follows that \code{$T <:$ FutureOr<$\flatten{T}$>}.
11889+
11890+
\item If $T$ derives a future type \code{Future<$S$>?} or
11891+
\code{FutureOr<$S$>?}, then, since \code{Future<$S$>? $<:$ FutureOr<$S$>?},
11892+
it follows that \code{$T <:$ FutureOr<$S$>?}.
11893+
\code{FutureOr<$S$>? $<:$ FutureOr<$S$?>} for any type $S$ (this can be shown
11894+
using the union type subtype rules and from
11895+
\code{Future<$S$> $<:$ Future<$S$?>} by covariance), so by transivitity,
11896+
\code{$T <:$ FutureOr<$S$?>}. Since \code{$\flatten{T} = S$?} in this case,
11897+
it follows that \code{$T <:$ FutureOr<$\flatten{T}$>}.
11898+
11899+
\item Otherwise, \code{$\flatten{T} = T$}, so
11900+
\code{FutureOr<$\flatten{T}$> $=$ FutureOr<$T$>}. Since
11901+
\code{$T <:$ FutureOr<$T$>}, it follows that
11902+
\code{$T <:$ FutureOr<$\flatten{T}$>}.
11903+
\end{itemize}
1183311904
}
1183411905

1183511906
\LMHash{}%

0 commit comments

Comments
 (0)