From 0b550b305bf2626f3783424ddf5cdd6f7d4949b9 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Thu, 7 Oct 2021 12:35:56 +0200 Subject: [PATCH 01/85] Rebasing --- specification/dartLangSpec.tex | 1940 ++++++++++++++++++++++++++++++++ 1 file changed, 1940 insertions(+) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 8615bc9f7..6840102a4 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -130,6 +130,7 @@ % members. % - Correct to include metadata. % - Clarify the section about assignable expressions. +% - Integrate the null safety specification into this document. % % 2.14 % - Add constraint on type of parameter which is covariant-by-declaration in @@ -23321,6 +23322,1945 @@ \subsection{Operator Precedence} } +\section{Null safety} %% !!!TODO!!! + + +\newcommand{\NnbdTopMergeName}{\metavar{topMerge}} +\newcommand{\NnbdTopMerge}[2]{\ensuremath{\NnbdTopMergeName(\code{{#1},\,\,{#2}})}} + +\newcommand{\NonNullTypeOfName}{\metavar{nonNullTypeOf}} +\newcommand{\NonNullTypeOf}[1]{\ensuremath{\NonNullTypeOfName(\code{#1})}} + +\newcommand{\IsTopTypeName}{\metavar{isTopType}} +\newcommand{\IsTopType}[1]{\ensuremath{\IsTopTypeName(\code{#1})}} + +\newcommand{\IsObjectTypeName}{\metavar{isObjectType}} +\newcommand{\IsObjectType}[1]{\ensuremath{\IsObjectTypeName(\code{#1})}} + +\newcommand{\IsBottomTypeName}{\metavar{isBottomType}} +\newcommand{\IsBottomType}[1]{\ensuremath{\IsObjectTypeName(\code{#1})}} + +\newcommand{\IsNullTypeName}{\metavar{isNullType}} +\newcommand{\IsNullType}[1]{\ensuremath{\IsObjectTypeName(\code{#1})}} + +\newcommand{\IsMoreTopTypeName}{\metavar{isMoreTopType}} +\newcommand{\IsMoreTopType}[2]{\ensuremath{\IsObjectTypeName(\code{{#1},\,\,{#2}})}} + +\newcommand{\IsMoreBottomTypeName}{\metavar{isMoreBottomType}} +\newcommand{\IsMoreBottomType}[2]{\ensuremath{\IsObjectTypeName(\code{{#1},\,\,{#2}})}} + +\newcommand{\NormalizedTypeOfName}{\metavar{normalizedTypeOf}} +\newcommand{\NormalizedTypeOf}[1]{\ensuremath{\NormalizedTypeOfName(\code{#1})}} + +\newcommand{\LegacyErasureTypeOfName}{\metavar{legacyErasureTypeOf}} +\newcommand{\LegacyErasureTypeOf}[1]{\ensuremath{\LegacyErasureTypeOfName(\code{#1})}} + +\newcommand{\ConstCanonicalTypeOfName}{\metavar{constCanonicalTypeOf}} +\newcommand{\ConstCanonicalTypeOf}[1]{\ensuremath{\ConstCanonicalTypeOfName(\code{#1})}} + +\subsection{Syntax} + +The precise changes to the syntax are given in an accompanying set of +modifications to the grammar in the formal specification. This section +summarizes in prose the grammar changes associated with this feature. + +The grammar of types is extended to allow any type to be suffixed with a \code{?} +(e.g. \code{int?}) indicating the nullable version of that type. + +A new primitive type \code{Never}. This type is denoted by the built-in type +declaration \code{Never} declared in \code{dart:core}. + +The grammar of expressions is extended to allow any expression to be suffixed +with a \code{!}. + +The modifier \LATE{} is added as a built-in identifier. The grammar of top level +variables, static fields, instance fields, and local variables is extended to +allow any declaration to include the modifer \LATE. + +The modifier \REQUIRED{} is added as a built-in identifier. The grammar of +function types is extended to allow any named parameter declaration to be +prefixed by the \REQUIRED{} modifier +(e.g. \code{int \FUNCTION(int, {int? y, \REQUIRED\ int z})}). + +The grammar of selectors is extended to allow null-aware subscripting using the +syntax \code{$e_1$?[$e_2$]} which evaluates to \code{null} if \code{$e_1$} evaluates to \code{null} and +otherwise evaluates as \code{$e_1$[$e_2$]}. + +The grammar of cascade sequences is extended to allow the first cascade of a +sequence to be written as \code{?..} indicating that the cascade is null-shorting. + +All of the syntax changes for this feature have been incorporated into +the +[formal grammar](https://github.com/dart-lang/language/blob/master/specification/dartLangSpec.tex), +which serves as the canonical reference for the grammatical changes. + +\subsubsection{Grammatical ambiguities and clarifications} + +\paragraph{Conditional expression ambiguities} + +Conditional expressions inside of braces are ambiguous between sets and maps. +That is, \code{{ a as bool ? - 3 : 3 }} can be parsed as a set literal \code{{ (a as bool) +? - 3 : 3 }} or as a map literal \code{{ (a as bool ?) - 3 : 3 }}. Parsers will +prefer the former parse over the latter. + +The same is true for \code{{ a is int ? - 3 : 3 }}. + +The same is true for \code{{ int ? - 3 : 3 }} if we allow this. + +\paragraph{Null aware subscript} + +Certain uses of null aware subscripts in conditional expressions are ambiguous. +For example, \code{{ a?[b]:c }} can be parsed either as a set literal or a map +literal, depending on whether the \code{?} is interpreted as part of a null aware +subscript or as part of a conditional expression. Whenever there is a sequence +of tokens which may be parsed either as a conditional expression or as two +expressions separated by a colon, the first of which is a null aware +subscript, parsers shall choose to parse as a conditional expression. + + +\subsection{Static semantics} + +\subsubsection{Legacy types} + +The internal representation of types is extended with a type \code{$T$*} for every type +$T$ to represent legacy pre-NNBD types. This is discussed further in the legacy +library section below. + +\subsubsection{Subtyping} + +We modify the subtyping rules to account for nullability and legacy types as +specified +[here](https://github.com/dart-lang/language/blob/master/resources/type-system/subtyping.md). +We write \code{S <: T} to mean that the type $S$ is a subtype of $T$ according to the +rules specified there. + + +We define \code{LEGACY_SUBTYPE(S, T)} to be true iff $S$ would be a subtype of $T$ +in a modification of the rules above in which all \code{?} on types were ignored, \code{*} +was added to each type, and \REQUIRED{} parameters were treated as optional. +This has the effect of treating \code{Never} as equivalent to \code{Null}, restoring +\code{Null} to the bottom of the type hierarchy, treating \code{Object} as nullable, and +ignoring \REQUIRED{} on named parameters. This is intended to provide the same +subtyping results as pre-nnbd Dart. + +Where potentially ambiguous, we sometimes write \code{NNBD_SUBTYPE(S, T)} to mean +the full subtyping relation without the legacy exceptions defined in the +previous paragraph. + +\subsubsection{Upper and lower bounds} + +We modify the upper and lower bound rules to account for nullability and legacy +types as +specified +[here](https://github.com/dart-lang/language/blob/master/resources/type-system/upper-lower-bounds.md). + +\subsubsection{Type normalization} + +We define a normalization procedure on types which defines a canonical +representation for otherwise equivalent +types +[here](https://github.com/dart-lang/language/blob/master/resources/type-system/normalization.md). +This defines a procedure \NormalizedTypeOfName{} such that \NormalizedTypeOf{$T$} is syntactically +equal to \NormalizedTypeOf{$S$} modulo replacement of primitive top types iff \code{$S$ <: $T$} +and \code{$T$ <: $S$}. + +\subsubsection{Future flattening} + +The **flatten** function is modified as follows: + +**flatten**($T$) is defined by cases on $T$: + - if $T$ is \code{S?} then **flatten**($T$) = **flatten**($S$)\code{?} + - otherwise if $T$ is \code{S*} then **flatten**($T$) = **flatten**($S$)\code{*} + - otherwise if $T$ is \code{FutureOr} then **flatten**($T$) = $S$ + - otherwise if \code{$T$ <: Future} then let $S$ be a type such that \code{$T$ <: Future<$S$>} +and for all $R$, if \code{$T$ <: Future<$R$>} then \code{$S$ <: $R$}; then **flatten**($T$) = $S$ + - otherwise **flatten**($T$) = $T$ + +\subsubsection{The future value type of an asynchronous non-generator function} + +_We specify a concept which corresponds to the static type of objects which may +be contained in the Future object returned by an async function with a given +declared return type._ + +Let _f_ be an asynchronous non-generator function with declared return type +$T$. Then the **future value type** of _f_ is **futureValueType**($T$). +The function **futureValueType** is defined as follows: + +- **futureValueType**(\code{S?}) = **futureValueType**($S$), for all $S$. +- **futureValueType**(\code{S*}) = **futureValueType**($S$), for all $S$. +- **futureValueType**(\code{Future}) = $S$, for all $S$. +- **futureValueType**(\code{FutureOr}) = $S$, for all $S$. +- **futureValueType**(\VOID) = \VOID. +- **futureValueType**(\DYNAMIC) = \DYNAMIC. +- Otherwise, for all $S$, **futureValueType**($S$) = \code{Object?}. + +_Note that it is a compile-time error unless the return type of an asynchronous +non-generator function is a supertype of \code{Future}, which means that +the last case will only be applied when $S$ is \code{Object} or a top type._ + +\subsubsection{Return statements} + +The static analysis of return statements is changed in the following +way, where $T$ is the declared return type and $S$ is the static type of +the expression $e$. + +At line 15477 about synchronous non-generator functions, the text is changed as follows: + +\Case{Spec change} +It is a compile-time error if $s$ is \code{\RETURN{} $e$;}, +$T$ is neither \VOID{} nor \DYNAMIC, +and $S$ is \VOID. +\EndCase + +_Comparing to Dart before null-safety, this means that it is no longer allowed +to return a void expression in a regular function if the return type is +\code{Null}._ + +At line 15507 about an asynchronous non-generator function with future value type $T_v$, +the text is changed as follows: + +\Case{Spec change} +It is a compile-time error if $s$ is \code{\RETURN;}, +unless $T_v$ +is \VOID, \DYNAMIC, or \code{Null}. +% +It is a compile-time error if $s$ is \code{\RETURN\,\,$e$;}, +$T_v$ is \VOID, +and \flatten{S} is neither \VOID, \DYNAMIC, \code{Null}. +% +It is a compile-time error if $s$ is \code{\RETURN\,\,$e$;}, +$T_v$ is neither \VOID{} nor \DYNAMIC, +and \flatten{S} is \VOID. +% +It is a compile-time error if $s$ is \code{\RETURN\,\,$e$;}, +\flatten{S} is not \VOID, +$S$ is not assignable to $T_v$, +and flatten{S} is not a subtype of $T_v$. +\EndCase + +_Comparing to Dart before null-safety, this means that it is no longer allowed +to return an expression whose flattened static type is \VOID{} in an \code{async} +function with future value type \code{Null}; nor is it allowed, in an \code{async} +function with future value type \VOID, to return an expression whose flattened +static type is not \VOID, \code{void*}, \DYNAMIC, or \code{Null}. Conversely, it is +allowed to return a future when the future value type is a suitable future; +for instance, we can have \code{return Future.value(42)} in an \code{async} function +with declared return type \code{Future>}. Finally, let $S$ be +\code{Future} or \code{FutureOr}; it is then no longer allowed to +return an expression with static type $S$, unless the future value type is a +supertype of $S$. This differs from Dart before null-safety in that it was +allowed to return an expression of these types with a declared return type +of the form \code{Future} for any $T$._ + +The dynamic semantics specified at line 15597 +is changed as follows, where $f$ is the enclosing function with declared +return type $T$, and $e$ is the returned expression: + +\Case{Spec change} +When $f$ is a synchronous non-generator, evaluation proceeds as follows: +The expression $e$ is evaluated to an object $o$. +A dynamic error occurs unless the dynamic type of $o$ is a subtype of +the actual return type of $f$ +(\ref{actualTypes}). +Then the return statement $s$ completes returning $o$ +(\ref{statementCompletion}). + +\commentary{% +The case where the evaluation of $e$ throws is covered by the general rule +which propagates the throwing completion from $e$ to $s$ to the function body.% +} + +When $f$ is an asynchronous non-generator with future value type $T_v$ +(\ref{functions}), evaluation proceeds as follows: +The expression $e$ is evaluated to an object $o$. +If the run-time type of $o$ is a subtype of \code{Future<$T_v$>}, +let \code{v} be a fresh variable bound to $o$ and +evaluate \code{\AWAIT{} v} to an object $r$; +otherwise let $r$ be $o$. +A dynamic error occurs unless the dynamic type of $r$ +is a subtype of the actual value of $T_v$ +(\ref{actualTypes}). +Then the return statement $s$ completes returning $r$ +(\ref{statementCompletion}). + +\commentary{% +The cases where $f$ is a generator cannot occur, +because in that case $s$ is a compile-time error.% +} +\EndCase + +\subsubsection{Static errors} +\paragraph{Nullability definitions} + +We say that a type $T$ is **nullable** if \code{Null <: $T$} and not \code{$T$ <: Object}. +This is equivalent to the syntactic criterion that $T$ is any of: + - \code{Null} + - \code{$S$?} for some $S$ + - \code{$S$*} for some $S$ where $S$ is nullable + - \code{FutureOr<$S$>} for some $S$ where $S$ is nullable + - \DYNAMIC{} + - \VOID{} + +Nullable types are types which are definitively known to be nullable, regardless +of instantiation of type variables, and regardless of any choice of replacement +for the \code{*} positions (with \code{?} or nothing). + +We say that a type $T$ is **non-nullable** if \code{$T$ <: Object}. +This is equivalent to the syntactic criterion that $T$ is any of: + - \code{Never} + - Any function type (including \FUNCTION) + - Any interface type except \code{Null}. + - \code{$S$*} for some $S$ where $S$ is non-nullable + - \code{FutureOr<$S$>} where $S$ is non-nullable + - \code{$X$ \EXTENDS\ $S$} where $S$ is non-nullable + - \code{$X$ \& $S$} where $S$ is non-nullable + +Non-nullable types are types which are either definitively known to be +non-nullable regardless of instantiation of type variables, or for which +replacing the \code{*} positions with nothing will result in a non-nullable type. + +Note that there are types which are neither nullable nor non-nullable. For +example \code{X \EXTENDS\ T} where $T$ is nullable is neither nullable nor +non-nullable. + +We say that a type $T$ is **strictly non-nullable** if \code{$T$ <: Object} and not +\code{Null <: $T$}. This is equivalent to the syntactic criterion that $T$ is any of: + - \code{Never} + - Any function type (including \code{Function}) + - Any interface type except \code{Null}. + - \code{FutureOr} where $S$ is strictly non-nullable + - \code{X \EXTENDS\ S} where $S$ is strictly non-nullable + - \code{X \& S} where $S$ is strictly non-nullable + +We say that a type $T$ is **potentially nullable** if $T$ is not non-nullable. +Note that this is different from saying that $T$ is nullable. For example, a +type variable \code{X \EXTENDS\ Object?} is a type which is potentially nullable but +not nullable. Note that \code{$T$*} is potentially nullable by this definition if $T$ +is potentially nullable - so \code{int*} is not potentially nullable, but \code{$X$*} where +\code{$X$ \EXTENDS\ int?} is. The potentially nullable types include all of the types +which are either definitely nullable, potentially instantiable to a nullable +type, or for which any migration results in a potentially nullable type. + +We say that a type $T$ is **potentially non-nullable** if $T$ is not nullable. +Note that this is different from saying that $T$ is non-nullable. For example, +a type variable \code{$X$ \EXTENDS\ Object?} is a type which is potentially non-nullable +but not non-nullable. Note that \code{$T$*} is potentially non-nullable by this +definition if $T$ is potentially non-nullable. + + +\paragraph{Reachability} + +A number of errors and warnings are updated to take reachability of statements +into account. Computation of code reachability +is +[specified separately](https://github.com/dart-lang/language/blob/master/resources/type-system/flow-analysis.md). + +We say that a statement **may complete normally** if the specified control flow +analysis determines that any control flow path may reach the end of the +statement without returning, throwing an exception not caught within the +statement, breaking to a location outside of the statement, or continuing to a +location outside of the statement. + +\paragraph{Errors and Warnings} + +It is an error to call a method, setter, getter or operator on an expression +whose type is potentially nullable and not \DYNAMIC, except for the methods, +setters, getters, and operators on \code{Object}. + +It is an error to read a field or tear off a method from an expression whose +type is potentially nullable and not \DYNAMIC, except for the methods and +fields on \code{Object}. + +It is an error to call an expression whose type is potentially nullable and not +\DYNAMIC. + +It is an error if a top level variable or static variable with a non-nullable +type has no initializer expression unless the variable is marked with a \LATE{} +or \EXTERNAL{} modifier. + +It is an error if a class declaration declares an instance variable with a +potentially non-nullable type and no initializer expression, and the class has a +generative constructor where the variable is not initialized via an initializing +formal or an initializer list entry, unless the variable is marked with a +\LATE, \ABSTRACT, or \EXTERNAL{} modifier. + +It is an error if a mixin declaration or a class declaration with no generative +constructors declares an instance variable without an initializing expression +which is final or whose type is potentially non-nullable, unless the variable is +marked with a \LATE, \ABSTRACT, or \EXTERNAL{} modifier. + +It is an error to derive a mixin from a class declaration which contains an +instance variable with a potentially non-nullable type and no initializer +expression unless the variable is marked with the \LATE{} modifier. + +It is an error if the body of a method, function, getter, or function expression +with a potentially non-nullable return type **may complete normally**. + +It is an error if an optional parameter (named or otherwise) with no default +value has a potentially non-nullable type **except** in the parameter list of an +abstract method declaration. + +It is an error if a required named parameter has a default value. + +It is an error if a required named parameter is not bound to an argument at a +call site. + +It is an error to call the default \code{List} constructor. + +For the purposes of errors and warnings, the null aware operators \code{?.}, \code{?..}, +and \code{?[]} are checked as if the receiver of the operator had non-nullable type. +More specifically, if the type of the receiver of a null aware operator is $T$, +then the operator is checked as if the receiver had type \NonNullTypeOf{$T$} (see +definition below). + +It is an error for a class to extend, implement, or mixin a type of the form +\code{$T$?} for any $T$. + +It is an error for a class to extend, implement, or mixin the type \code{Never}. + +It is not an error to call or tear-off a method, setter, or getter, or to read +or write a field, on a receiver of static type \code{Never}. Implementations that +provide feedback about dead or unreachable code are encouraged to indicate that +any arguments to the invocation are unreachable. + +It is not an error to apply an expression of type \code{Never} in the function +position of a function call. Implementations that provide feedback about dead or +unreachable code are encouraged to indicate that any arguments to the call are +unreachable. + +It is an error if the static type of $e$ in the expression \code{\THROW\,\,$e$} is not +assignable to \code{Object}. + +It is not an error for the body of a \LATE{} field to reference \THIS. + +It is an error for a variable to be declared as \LATE{} in any of the following +positions: in a formal parameter list of any kind; in a catch clause; in the +variable binding section of a c-style \FOR{} loop, a for-in loop, an \AWAIT-for +loop, or a \syntax{forElement} in a collection literal. + +It is an error for the initializer expression of a \LATE{} local variable to use +a prefix \AWAIT{} expression that is not nested inside of another function +expression. + +It is an error for a class with a generative \CONST{} constructor to have a +\code{\LATE\ \FINAL} instance variable. + +It is not a compile time error to write to a \FINAL{} non-local or instance +variable if that variable is declared \LATE{} and does not have an initializer. +For local variables, see the section below. + +It is an error if the object being iterated over by a \code{for-in} loop has a static +type which is not \DYNAMIC, and is not a subtype of \code{Iterable}. + +It is an error if the type of the value returned from a factory constructor is +not a subtype of the class type associated with the class in which it is defined +(specifically, it is an error to return a nullable type from a factory +constructor for any class other than \code{Null}). + +It is an error if any case of a switch statement except the last case (the +default case if present) **may complete normally**. The previous syntactic +restriction requiring the last statement of each case to be one of an enumerated +list of statements (break, continue, return, throw, or rethrow) is removed. + +Given a switch statement which switches over an expression $e$ of type $T$, +where the cases are dispatched based on expressions \List{e}{0}{k}: + - It is no longer required that the $e_i$ evaluate to instances of the same + class. + - It is an error if any of the $e_i$ evaluate to a value whose static type is + not a subtype of $T$. + - It is an error if any of the $e_i$ evaluate to constants for which equality + is not primitive. + - If $T$ is an enum type, it is a warning if the switch does not handle all + enum cases, either explicitly or via a default. + - If $T$ is \code{$Q$?} where $Q$ is an enum type, it is a warning if the switch does + not handle all enum cases and \code{null}, either explicitly or via a default. + +It is an error if a class has a setter and a getter with the same basename where +the return type of the getter is not a subtype of the argument type of the +setter. Note that this error specifically requires subtyping and not +assignability and hence makes no exception for \DYNAMIC. + +If the static type of $e$ is \VOID, the expression \code{\AWAIT\,\,$e$} is a compile-time +error. *This implies that the list item at line 18281 +will be removed from the language specification.* + +A compile-time error occurs if an expression has static type \code{\VOID{}*}, and it +does not occur in any of the ways specified in the list on line 18238. +*This implies that \code{\VOID{}*} is treated the same as \VOID.* + +Let $C$ be a type literal denoting a class, mixin, or extension. It is a warning +to use a null aware member access with receiver $C$. *E.g., \code{$C$?.staticMethod()} +is a warning.* + +It is a warning to use a null aware operator (\code{?.}, \code{?[]}, \code{?..}, \code{??}, \code{??=}, or +\code{...?}) on an expression of type $T$ if $T$ is **strictly non-nullable**. + +It is a warning to use the null check operator (\code{!}) on an expression of type +$T$ if $T$ is **strictly non-nullable** . + +It is no longer a warning to override a method which has a default value for a +parameter with a method with a different default value for the corresponding +parameter. + +\subsubsection{Local variables and definite (un)assignment} + +As part of the null safety release, errors for local variables are specified to +take into account **definite assignment** and **definite unassignment** (see the +section on Definite Assignment below). We say that a variable is **potentially +assigned** if it is not **definitely unassigned**, and that a variable is +**potentially unassigned** if it is not **definitely assigned**. + +In all cases in this section, errors that are described as occurring on reads of +a variable are intended to apply to all form of reads, including indirectly as +part of compound assignment operators, as well as via pre and post-fix +operators. Similarly, errors that are described as occurring on writes of a +variable are intended to apply to all form of writes. + +It is a compile time error to assign a value to a \FINAL, non-\LATE{} local +variable which is **potentially assigned**. Thus, it is *not* a compile time +error to assign to a **definitely unassigned** \FINAL{} local variable. + +It is a compile time error to assign a value to a \FINAL, \LATE{} local variable +if it is **definitely assigned**. Thus, it is *not* a compile time error to +assign to a **potentially unassigned** \FINAL, \LATE{} local variable. + +*Note that a variable is always considered **definitely assigned** and not +**definitely unassigned** if it has an explicit initializer, or an implicit +initializer as part of a larger construct (e.g. the loop variable in a for-in +construct).* + +It is a compile time error to read a local variable when the variable is +**definitely unassigned** unless the variable is non-\FINAL, and non-\LATE, +and has nullable type. + +It is a compile time error to read a local variable when the variable is +**potentially unassigned** unless the variable is non-\FINAL{} and has nullable +type, or is \LATE. + +The errors specified above are summarized in the following table, where \code{int} is +used as an example of an arbitrary **potentially non-nullable** type, \code{int?} is +used as an example of an arbitrary **nullable** type, and $T$ is used to stand +for a type of any nullability. A variable which has an initializer (explicit or +implicit) is always considered definitely assigned, and is never considered +definitely unassigned. + +Read Behavior: + +\begin{verbatim} +| Declaration form | Def. Assigned | Neither | Def. Unassigned | +| ----------------- | ------------- | ----------------- | --------------- | +| var x; | Ok | Ok | Ok | +| final x; | Ok | Error | Error | +| int x; | Ok | Error | Error | +| int? x; | Ok | Ok | Ok | +| final T x; | Ok | Error | Error | +| late var x; | Ok | Ok | Error | +| late final x; | Ok | Ok | Error | +| late T x; | Ok | Ok | Error | +| late final T x; | Ok | Ok | Error | +\end{verbatim} + +Write Behavior: + +\begin{verbatim} +| Declaration form | Def. Assigned | Neither | Def. Unassigned | +| ----------------- | ------------- | ------------------- | --------------- | +| var x; | Ok | Ok | Ok | +| final x; | Error | Error | Ok | +| int x; | Ok | Ok | Ok | +| int? x; | Ok | Ok | Ok | +| final T x; | Error | Error | Ok | +| late var x; | Ok | Ok | Ok | +| late final x; | Error | Ok | Ok | +| late T x; | Ok | Ok | Ok | +| late final T x; | Error | Ok | Ok | +\end{verbatim} + +\subsubsection{Local variables and inference} + +Local variables with explicitly written types are given the declared types as +written. The declared type of the variable is considered a "type of interest" +in the sense defined in the flow analysis specification. If the variable has an +initializer (explicit or implicit) and is not \FINAL, then the declaration is +treated as an assignment for the purposes of promotion. + +*Treating the declared type of the variable as a "type of interest" implies that +if the variable has a nullable type, then the non-nullable version of that type +is also a type of interest. Treating the initialization as an assignment for +the purposes of promotion means that initializing a mutable variable declared at +type \code{$T$?} with a value of non-nullable type $T$ immediately promotes the +variable to the non-nullable type.* + +\begin{dartCode} +\VOID test() \{ + int? x = 3; // x is declared at \code{int?} + x.isEven; // Valid, x has been promoted to \code{int} + x = null; // Valid, demotes to the declared type. +\} +\end{dartCode} + +Local variables with no explicitly written type but with an initializer are +given an inferred type equal to the type of their initializer, unless that type +is \code{Null}, in which case the inferred type of the variable shall be \DYNAMIC. +The inferred type of the variable is considered a "type of interest" in the +sense defined in the flow analysis specification. In the case that the type of +the initializer is a promoted type variable \code{X \& T}, the inferred type of the +variable shall be \code{X}, but \code{X \& T} shall be considered as a type of interest and +the initialization treated as an assignment for the purposes of promotion. +Consequently, such a variable shall be treated as immediately promoted to \code{X \& +T}. + +\subsubsection{Expression typing} + +It is permitted to invoke or tear-off a method, setter, getter, or operator that +is defined on \code{Object} on potentially nullable type. The type used for static +analysis of such an invocation or tear-off shall be the type declared on the +relevant member on \code{Object}. For example, given a receiver $o$ of type \code{$T$?}, +invoking an \code{Object} member on $o$ shall use the type of the member as declared +on \code{Object}, regardless of the type of the member as declared on $T$ (note that +the type as declared on $T$ must be a subtype of the type on \code{Object}, and so +choosing the \code{Object} type is a sound choice. The opposite choice is not +sound). + +\commentary{% +Note that evaluation of an expression $e$ of the form \code{$e_1$\,\,==\,\,$e_2$} is not an +invocation of \code{operator\,\,==}, it includes special treatment of null. The +precise rules are specified later in this section.% +} + +Calling a method (including an operator) or getter on a receiver of static type +\code{Never} is treated by static analysis as producing a result of type \code{Never}. +Tearing off a method from a receiver of static type \code{Never} produces a value of +type \code{Never}. Applying an expression of type \code{Never} in the function position +of a function call produces a result of type \code{Never}. + +The static type of a \code{\THROW\,\,$e$} expression is \code{Never}. + +Consider an expression $e$ of the form \code{$e_1$\,==\,$e_2$} where the static type of +$e_1$ is $T_1$ and the static type of $e_2$ is $T_2$. Let $S$ be the type of the +formal parameter of \code{operator\,\,==} in the interface of \NonNullTypeOf{$T_1$}. +It is a compile-time error unless $T_2$ is assignable to \code{$S$?}. + +Similarly, consider an expression $e$ of the form \code{\SUPER\,==\,$e_2$} that occurs in a +class whose superclass is $C$, where the static type of $e_2$ is $T_2$. Let $S$ be +the formal parameter type of the concrete declaration of \code{operator\,\,==} found by +method lookup in $C$ +\commentary{(if that search succeeds, otherwise it is a compile-time error)}. +It is a compile-time error unless $T_2$ is assignable to \code{$S$?}. + +\commentary{% +Even if the static type of $e_1$ is potentially nullable, the parameter type +of the \code{operator\,\,==} of the corresponding non-null type is taken into account, +because that instance method will not be invoked when $e_1$ is null. Similarly, +it is not a compile-time error for the static type of $e_2$ to be potentially +nullable, even when the parameter type of said \code{operator\,\,==} is non-nullable. +This is again safe, because the instance method will not be invoked when $e_2$ +is null.% +} + +In legacy mode, an override of \code{operator\,\,==} with no explicit parameter type +inherits the parameter type of the overridden method if any override of +\code{operator\,\,==} between the overriding method and \code{Object.==} has an explicit +parameter type. Otherwise, the parameter type of the overriding method is +\DYNAMIC. + +Top level variable and local function inference is performed +as specified separately, https://github.com/dart-lang/language/blob/master/resources/type-system/inference.md. +Method body inference is not yet specified. + +If no type is specified in a catch clause, then the default type of the error +variable is \code{Object}, instead of \DYNAMIC{} as was the case in pre-null safe +Dart. + +\paragraph{Spread element typing} + +In a collection literal in Dart before null-safety, the inferred element +type of a spread element of the form \code{...?$e$} where $e$ has static type +\code{Null} is \code{Null}, and so are the inferred key type and value type. + +With null-safety, when the static type of $e$ is \code{Null} or a potentially +nullable subtype thereof, the inferred element, key, and value type +of \code{...?$e$} is \code{Never}. + +Similarly, when the static type of $e$ is a subtype of \code{Never}, +the element, key, and value type of \code{...$e$} and \code{...?$e$} is \code{Never}. + +\commentary{% +When the static type $S$ of $e$ is strictly non-nullable, such as when $S$ +is \code{Never}, \code{...?$e$} is a warning, but it may still occur.% +} + +\subsubsection{Instantiation to bound} + +The computation of instantiation to bound is changed to substitute \code{Never} for +type variables appearing in contravariant positions instead of \code{Null}. + +\subsubsection{Super-bounded types} + +Null safety requires three changes to the section 'Super-Bounded Types' in +the language specification. + +The definition of a top type is changed: $T$ is a top type if and only if +\code{Object?} is a subtype of $T$. Note that the helper predicate \IsTopTypeName{} +provides a syntactic characterization of the same concept. + +The definition of a super-bounded type is changed such that occurrences of +\code{Null} are replaced by types involving \code{Never}, and \code{Object} is replaced by +\code{Object?}. Moreover, top types in invariant positions and in positions that +have no variance (*unused type parameters in a type alias*) are given the +same treatment as top types in covariant positions. This causes one +sentence to change, with the following result: + +Let _T'_ be the result of replacing every occurrence in _T_ of a type _S_ +in a contravariant position where _S <: Never_ by \code{Object?}, and every +occurrence in _T_ of a top type in a position which is not contravariant by +\code{Never}. + +\subsubsection{Least and greatest closure} + +The definitions of least and greatest closure are changed in null safe libraries +to substitute \code{Never} in positions where previously \code{Null} would have been +substituted, and \code{Object?} in positions where previously \code{Object} or \DYNAMIC{} +would have been substituted. + +\subsubsection{Const type variable elimination} + +If performing inference on a constant value of a generic class results in +inferred type arguments to the generic class which contain free type variables +from an enclosing generic class or method, the free type variables shall be +eliminated by taking the least closure of the inferred type with respect to the +free type variables. Note that free type variables which are explicitly used as +type arguments in constant generic instances are still considered erroneous. + +\begin{dartCode} +\CLASS\ G \{ + \VOID\ foo() \{ + \CONST\ List c = []; // Error + \CONST\ List d = []; // The list literal is inferred as [] + \} +\} +\end{dartCode} + +\subsubsection{Extension method resolution} + +For the purposes of extension method resolution, there is no special treatment +of nullable types with respect to what members are considered accessible. That +is, the only members of a nullable type that are considered accessible +(and hence which take precedence over extensions) are the members on \code{Object}. + +For the purposes of extension method resolution, the type \code{Never} is considered +to implement all members, and hence no extension may apply to an expression of +type \code{Never}. + +\subsubsection{Assignability} + +The definition of assignability is changed as follows. + +A type $T$ is **assignable** to a type $S$ if $T$ is \DYNAMIC, or if $T$ is a +subtype of $S$. + +\subsubsection{Generics} + +The default bound of generic type parameters is treated as \code{Object?}. + +\subsubsection{Combined member signatures} + +This section, line 4241 +in the language specification defines the notion of a _combined member +signature_. In Dart before null-safety it is based on the textually first +superinterface that has a most specific signature. With null-safety it +is changed such that the all the most specific signatures are merged. + +This is achieved by changing this paragraph, line 4373, +to the following: + +"Let _mall_ be the result of applying \code{\NnbdTopMergeName} to +the elements in _Mall_, ordered according to the interface +_I1 .. Ik_ that each signature came from." + +Moreover, the occurrence of _mi_ in the next paragraph is +changed to _mall_. + +\subsubsection{Implicit conversions} + +The implicit conversion of integer literals to double literals is performed when +the context type is \code{double} or \code{double?}. + +The implicit tear-off conversion which converts uses of instances of classes +with call methods to the tear-off of their \CALL{} method is performed when the +context type is a function type, or the nullable version of a function type. + +Implicit tear-off conversion is *not* performed on objects of nullable type, +regardless of the context type. For example: + +\begin{dartCode} +\CLASS\ C \{ + int call() \{\} +\} +\VOID\ main() \{ + int \FUNCTION()? c0 = \NEW\ C(); // Ok + int \FUNCTION()? c0 = (null \AS\ C?); // static error + int \FUNCTION() c1 = (null \AS\ C?); // static error +\} +\end{dartCode} + +\subsubsection{Constant Objects} + +The definition of potentially constant expressions is extended to include type +casts and instance checks on potentially constant types, as follows. + +We change the following specification text: + +\Case{Spec change} +An expression of the form \code{$e$\,\,\AS\,\,$T$} is potentially constant +if $e$ is a potentially constant expression +and $T$ is a constant type expression, +and it is further constant if $e$ is constant. +\EndCase + +to + +\Case{Spec change} +An expression of the form \code{$e$\,\,\AS\,\,$T$} or +\code{$e$\,\,\IS\,\,$T$} is potentially constant +if $e$ is a potentially constant expression +and $T$ is a potentially constant type expression, +and it is further constant if $e$ is constant. +\EndCase + +where the definition of a "potentially constant type expression" is the same as +the current definition for a "constant type expression" with the addition that a +type variable is allowed as a "potentially constant type expression". + +This is motivated by the requirement to make downcasts explicit as part of the +NNBD release. Current constant evaluation is permitted to evaluate implicit +downcasts involving type variables. Without this change, it is difficult to +change such implicit downcasts to an explicit form. For example this class is +currently valid Dart code, but is invalid after the NNBD restriction on implicit +downcasts because of the implied downcast on the initialization of \code{w}: + + +\begin{dartCode} +\CONST\ num three = 3; + +\CLASS{} ConstantClass \{ + \FINAL\ T w; + \CONST\ ConstantClass() : w = three /* as T */; +\} + +\VOID\ main() \{ + print(\CONST\ ConstantClass()); +\} +\end{dartCode} + +With this change, the following is a valid migration of this code: + +\begin{dartCode} +\CONST\ num three = 3; + +\CLASS\ ConstantClass \{ + \FINAL\ T w; + \CONST\ ConstantClass() : w = three \AS\ T; +\} + +\VOID{} main() \{ + print(\CONST\ ConstantClass()); +\} +\end{dartCode} + +\subsubsection{Null promotion} + +The machinery of type promotion is extended to promote the type of variables +based on nullability checks subject to the same set of restrictions as normal +promotion. The relevant checks and the types they are considered to promote to +are as follows. + +A check of the form \code{$e$ == null} or of the form \code{$e$\,\,\IS\,\,Null} where $e$ has static +type $T$ promotes the type of $e$ to \code{Null} in the \code{true} continuation, and to +\NonNullTypeOf{$T$} in the \code{false} continuation. + +A check of the form \code{$e$ != null} or of the form \code{$e$\,\,\IS\,\,$T$} where $e$ has static +type \code{$T$?} promotes the type of $e$ to $T$ in the \code{true} continuation, and to +\code{Null} in the \code{false} continuation. + +The static type of an expression \code{$e$!} is \NonNullTypeOf{$T$} where $T$ is the +static type of $e$. + +The **NonNull** function defines the null-promoted version of a type, and is +defined as follows. + +- \NonNullTypeOf{Null} = \code{Never} +- \NonNullTypeOf{$C$<$T_1$, \ldots, $T_n$>} = \code{$C$<$T_1$, \ldots, $T_n$>}, + for class $C$ other than \code{Null} (including \code{Object}). +- \NonNullTypeOf{FutureOr<$T$>} = \code{FutureOr<$T$>} +- \NonNullTypeOf{$T_0$ \FUNCTION(\ldots)} = \code{$T_0$ \FUNCTION(\ldots)} +- \NonNullTypeOf{\FUNCTION} = \FUNCTION{} +- \NonNullTypeOf{Never} = \code{Never} +- \NonNullTypeOf{\DYNAMIC} = \DYNAMIC{} +- \NonNullTypeOf{\VOID} = \VOID{} +- \NonNullTypeOf{$X$} = \code{$X$ \& \NonNullTypeOf{$B$}}, where $B$ is the bound of $X$. +- \NonNullTypeOf{$X$ \& $T$} = \code{$X$ \& \NonNullTypeOf{$T$}} +- \NonNullTypeOf{$T$?} = \NonNullTypeOf{$T$} +- \NonNullTypeOf{$T$*} = \NonNullTypeOf{$T$} + +\paragraph{Extended Type promotion, Definite Assignment, and Reachability} + +These are extended as per +[separate proposal](https://github.com/dart-lang/language/blob/master/resources/type-system/flow-analysis.md). + +\subsection{Helper predicates} + +The following helper predicates are used to classify types. They are syntactic +in nature such that termination is obvious. In particular, they do not rely on +subtyping. + +The \IsTopTypeName{} predicate is true for any type which is in the equivalence class of +top types. + +- \IsTopType{$T$?} is true iff \IsTopType{$T$} or \IsObjectType{$T$} +- \IsTopType{$T$*} is true iff \IsTopType{$T$} or \IsObjectType{$T$} +- \IsTopType{\DYNAMIC} is true +- \IsTopType{\VOID} is true +- \IsTopType{FutureOr<$T$>} is \IsTopType{$T$} +- \IsTopType{$T$} is false otherwise + +\IsTopType{$T$} is true if and only if $T$ is a supertype of \code{Object?}. + +The \IsObjectTypeName{} predicate is true for any type which is in the equivalence class +of \code{Object}. + +- \IsObjectType{Object} is true +- \IsObjectType{FutureOr<$T$>} is \IsObjectType{$T$} +- \IsObjectType{$T$} is false otherwise + +\IsObjectType{$T$} is true if and only if $T$ is a subtype and a supertype of +\code{Object}. + +The \IsBottomTypeName{} predicate is true for things in the equivalence class of \code{Never}. + +- \IsBottomType{Never} is true +- \IsBottomType{$X$ \& $T$} is true iff \IsBottomType{$T$} +- \IsBottomType{$X$ \EXTENDS\ $T$} is true iff \IsBottomType{$T$} +- \IsBottomType{$T$} is false otherwise + +\IsBottomType{$T$} is true if and only if $T$ is a subtype of \code{Never}. + +The \IsNullTypeName{} predicate is true for things in the equivalence class of \code{Null}. + +- \IsNullType{Null} is true +- \IsNullType{$T$?} is true iff \IsNullType{$T$} or \IsBottomType{$T$} +- \IsNullType{$T$*} is true iff \IsNullType{$T$} or \IsBottomType{$T$} +- \IsNullType{$T$} is false otherwise + +\IsNullType{$T$} is true if and only if $T$ is a subtype and a supertype of \code{Null}. + +The \IsMoreTopTypeName{} predicate defines a total order on top and \code{Object} types. + +- \IsMoreTopType{\VOID}{$T$} = \code{true} +- \IsMoreTopType{$T$}{\VOID} = \code{false} +- \IsMoreTopType{\DYNAMIC}{$T$} = \code{true} +- \IsMoreTopType{$T$}{\DYNAMIC} = \code{false} +- \IsMoreTopType{Object}{$T$} = \code{true} +- \IsMoreTopType{$T$}{Object} = \code{false} +- \IsMoreTopType{$T$*}{$S$*} = \IsMoreTopType{$T$}{$S$} +- \IsMoreTopType{$T$}{$S$*} = \code{true} +- \IsMoreTopType{$T$*}{$S$} = \code{false} +- \IsMoreTopType{$T$?}{$S$?} = \IsMoreTopType{$T$}{$S$} +- \IsMoreTopType{$T$}{$S$?} = \code{true} +- \IsMoreTopType{$T$?}{$S$} = \code{false} +- \IsMoreTopType{FutureOr<$T$>}{FutureOr<$S$>} = \IsMoreTopType{$T$}{$S$} + +The \IsMoreBottomTypeName{} predicate defines an (almost) total order on bottom and +\code{Null} types. This does not currently consistently order two different type +variables with the same bound. + +- \IsMoreBottomType{Never}{$T$} = \code{true} +- \IsMoreBottomType{$T$}{Never} = \code{false} +- \IsMoreBottomType{Null}{$T$} = \code{true} +- \IsMoreBottomType{$T$}{Null} = \code{false} +- \IsMoreBottomType{$T$?}{$S$?} = \IsMoreBottomType{$T$}{$S$} +- \IsMoreBottomType{$T$}{$S$?} = \code{true} +- \IsMoreBottomType{$T$?}{$S$} = \code{false} +- \IsMoreBottomType{$T$*}{$S$*} = \IsMoreBottomType{$T$}{$S$} +- \IsMoreBottomType{$T$}{$S$*} = \code{true} +- \IsMoreBottomType{$T$*}{$S$} = \code{false} +- \IsMoreBottomType{$X$ \& $T$}{$Y$ \& $S$} = \IsMoreBottomType{$T$}{$S$} +- \IsMoreBottomType{$X$ \& $T$}{$S$} = \code{true} +- \IsMoreBottomType{$S$, $X$ \& $T$} = \code{false} +- \IsMoreBottomType{$X$ \EXTENDS\ $T$}{$Y$ \EXTENDS\ $S$} = \IsMoreBottomType{$T$}{$S$} + +\subsubsection{The main function} + +The section 'Scripts' in the language specification is replaced by the +following: + +Let $L$ be a library that exports a declaration $D$ named \code{main}. It is a +compile-time error unless $D$ is a non-getter function declaration. It is a +compile-time error if $D$ declares more than two required positional +parameters, or if there are any required named parameters. It is a +compile-time error if $D$ declares at least one positional parameter, and +the first positional parameter has a type which is not a supertype of +\code{List}. + +Implementations are free to impose any additional restrictions on the +signature of \code{main}. + +A _script_ is a library that exports a declaration named \code{main}. +A script $L$ is executed as follows: + +First, $L$ is compiled as a library as specified above. +Then, the top-level function defined by \code{main} +in the exported namespace of $L$ is invoked as follows: + +If \code{main} can be called with two positional arguments, +it is invoked with the following two actual arguments: + +- An object whose run-time type implements \code{List}. +- An object specified when the current isolate $i$ was created, + for example through the invocation of \code{Isolate.spawnUri} that spawned $i$, + or the null object if no such object was supplied. + A dynamic error occurs if the run-time type of this object is not a + subtype of the declared type of the corresponding parameter of \code{main}. + +If \code{main} cannot be called with two positional arguments, but it can be +called with one positional argument, it is invoked with an object whose +run-time type implements \code{List} as the only argument. + +If \code{main} cannot be called with one or two positional arguments, it is +invoked with no arguments. + +In each of the above three cases, an implementation is free to provide +additional arguments allowed by the signature of \code{main} (*the above rules +ensure that the corresponding parameters are optional*). But the +implementation must ensure that a dynamic error occurs if an actual +argument does not have a run-time type which is a subtype of the declared +type of the parameter. + +A Dart program will typically be executed by executing a script. The +procedure whereby this script is chosen is implementation specific. + +\subsection{Runtime semantics} + +\subsubsection{Unsound and sound semantics} + +To allow the null safety feature to be rolled out incrementally, we define two +modes of compilation and execution. + +**Unsound null checking** mode largely ignores the nullability of types at +runtime, as defined below. Unmigrated programs or programs consisting of a +mix of migrated and unmigrated code are expected to run without encountering +new nullability related errors at runtime. **This mode is unsound** in the +sense that variables marked as non-nullable may still be null at runtime. + +**Sound null checking** mode respects the nullability of types at runtime in +casts and instance checks, as defined below. Unmigrated programs or programs +consisting of a mix of migrated and unmigrated code may not be compiled or run +with sound null checking, and it is a compile time error if unmigrated +code is attempted to be compiled with sound null checking enabled. + +Unsound vs sound null checking can be controlled at runtime via the +\code{--[no-]sound-null-safety} flag, where the negated version of the flag implies +unsound null checking and the unnegated version implies sound null checking. + +In the absence of an explicit value for the flag, the mode of execution depends +on migrated status of the program entry point. If the entry point of the +program (\code{main}) is in an opted-in library, then the program is compiled and run +as if \code{--sound-null-safety} were specified on the command line. Otherwise, +the program is run as if \code{--no-sound-null-safety} were specified on the +command line. + +Compilers may (and are encouraged to) print a warning indicating that sound null +checking has been disabled when compiling a program that contains migrated +libraries with unsound null checking. + +\subsubsection{Runtime type equality operator} + +Two objects $T_1$ and $T_2$ which are instances of \code{Type} (that is, runtime type +objects) are considered equal if and only if the runtime type objects $T_1$ and +$T_2$ corresponds to the types $S_1$ and $S_2$ respectively, and the normal forms +\NormalizedTypeOf{$S_1$} and \NormalizedTypeOf{$S_2$} are syntactically equal up to equivalence of +bound variables and **ignoring \code{*} modifiers on types**. So for example, the +run-time type objects corresponding to \code{List} and \code{List} are +considered equal. Note that we do not equate primitive top types. \code{List<\VOID>} +and \code{List<\DYNAMIC>} are still considered distinct runtime type objects. Note +that we also do not equate \code{Never} and \code{Null}, and we do not equate function +types which differ in the placement of \REQUIRED{} on parameter types. Because +of this, the equality described here is not equivalent to syntactic equality on +the \LegacyErasureTypeOfName{} of the types. + + +\subsubsection{Constant evaluation and canonicalization} + +Constant evaluation is modified so that both type literals and legacy and opted-in +instances canonicalize more consistently as defined below. + +\paragraph{Type literals} + +Two constant type literals $T_1$ and $T_2$ compare as identical if they +are equal using the definition of runtime type equality specified above. + +The effect of this definition is to ensure that constant type literals which +look identical in the source syntax but which may differ by the presence of +legacy type modifiers are canonicalized consistently in the sense that any two +type literals which would compare equal via the definition of runtime type +equality given above will compare as identical. For runtime implementations +which implement identity by choosing a canonical representative for the +equivalence class of equal instances, the choice of what type object to +canonicalize to is arbitrary in that placement of legacy modifiers in type +literals is not otherwise observable in the language. + +Note that the choice of canonicalization for type literals does not depend +directly on whether sound or unsound null checking is in use. + +\paragraph{Constant instances} + +In both sound and unsound null checking, and in both opted in and opted out +code, comparison of constant instances for identity is defined such that any two +instances which are otherwise identical except for their generic type arguments +shall be considered identical if those generic type arguments compare equal +using the definition of runtime type object equality defined above. That is, +comparison (or canonicalization) of constant instances of generic classes is +performed relative to the normal forms of their generic type arguments, and +ignoring legacy type annotations as described above. Hence, an instance of +\code{$C$<$T_0$>} compares identical to \code{C<$T_1$>} if $T_0$ and $T_1$ have the same normal form +(up to the identity of bound variables), and the objects are otherwise +identical. + +Implementations of the Dart runtime semantics rely on canonicalization of +constant objects to allow the identity semantics specified above to be +implemented as fast pointer equality checks on the reference to the canonical +form. The definition above defines equivalence classes of constant objects for +which we must choose the canonical representative. The choice of this +representative is observable in mixed mode programs, since instances with +different degrees of "legacy-ness" in their type arguments are considered +identical, but may contain operations which perform casts and instance checks +which will evaluate differently depending on whether a legacy type or a +non-legacy type is used in the canonical representative. For example: + +\begin{dartCode} +// null safe code. +\CLASS{} C \{ + \FINAL\ T x; + \VOID\ test(Object? o) \{ + o \AS\ T; + \} + \CONST\ C(Object? o) : x = o \AS\ T; +\} + +// If the canonical instance uses `int`, this is a compile time error +// If the canonical instance uses `int*`, this is not a compile time error +\CONST\ c1 = C(null); + +// If the canonical instance uses `int`, this throws +// If the canonical instance uses `int*`, this does not throw +\VOID\ test1() => c1.test(null); + +// Opted out code + +// If the canonical instance uses `int`, this is a compile time error +// If the canonical instance uses `int*`, this is not a compile time error +\CONST\ c2 = C(null); + +// If the canonical instance uses `int`, this throws +// If the canonical instance uses `int*`, this does not throw +\VOID\ test1() => c2.test(null); +\end{dartCode} + +We therefore define the choice of the canonical instance representing an +equivalence class of constant objects as follows. + +With sound null checking, all generic constant constructors and generic constant +literals are evaluated using the type arguments provided, and canonicalization +is performed with respect to the normal form of the type arguments. This +ensures that with sound null checking, the final consistent semantics are +obeyed, since it is not observable which instance is chosen as the canonical +representative in sound mode. + +With unsound null checking, all generic constant object expressions are +additionally treated as if all type arguments passed to them were legacy types +regardless of whether the constructed class was defined in a legacy library or +not, and regardless of whether the constructor invocation or literal occured in +a legacy library or not. Specifically, a constant object expression with +generic type parameters $T_i$ is treated as if the parameters were +\ConstCanonicalTypeOf{$T_i$} as defined below. This ensures that constant +objects which appear identical in the syntax continue to canonicalize +consistently across legacy and opted-in libraries. + +The Dart static analysis tool does not distinguish between sound and unsound +checking mode, and hence it is expected that there will be some small level of +infidelity in the constant evaluation semantics in the analyzer. Identity +semantics for constant objects can be faithfully modeled in the analyzer using +the existing strategy of implementing identity directly, rather than via +choosing a canonical representative for each equivalence class. However, the +lack of a canonical representative is observable at compile time in rare cases, +such as the example shown above. We propose that the analyzer should choose to +evaluate those constants in opted in libraries using sound mode semantics, and +to evaluate those in opted out libraries using unsound mode semantics. Hence in +the example above, the definition of \code{c1} would be a compile time error, but the +definition of \code{c2} would not. + +The \ConstCanonicalTypeOfName{} erasure operation on types used above is +defined as follows. + +- \ConstCanonicalTypeOf{$T$} = $T$, if $T$ is \DYNAMIC, \VOID, or \code{Null} +- \ConstCanonicalTypeOf{$T$} = \code{$T$*} if $T$ is \code{Never} or \code{Object} +- \ConstCanonicalTypeOf{FutureOr<$T$>} = \code{FutureOr<$S$>*} + - where $S$ is \ConstCanonicalTypeOf{$T$} +- \ConstCanonicalTypeOf{$T$?} = + - let $S$ be \ConstCanonicalTypeOf{$T$} + - if $S$ is \code{$R$*} then \code{$R$?} + - else \code{$S$?} +- \ConstCanonicalTypeOf{$T$*} = \ConstCanonicalTypeOf{$T$} +- \ConstCanonicalTypeOf{$X$ \EXTENDS\ $T$} = \code{$X$*} +- \ConstCanonicalTypeOf{$X$ \& $T$} = + - This case should not occur, since intersection types are not permitted as + generic arguments. +- \ConstCanonicalTypeOf{$C$<$T_0$, \ldots, $T_n$>} = \code{$C$<$R_0$, \ldots, $R_n$>*} + - where $R_i$ is \ConstCanonicalTypeOf{$T_i$} + - Note this includes the case of an interface type with no generic parameters + (e.g \code{int}. +- \ConstCanonicalTypeOf{$R$ \FUNCTION<$X$ \EXTENDS\ $B$>($S$)} = \code{$F$*} + - where $F$ = \code{$R_1$ \FUNCTION<$X$ \EXTENDS\ $B_1$>($S_1$)} + - and $R_1$ = \ConstCanonicalTypeOf{$R$} + - and $B_1$ = \ConstCanonicalTypeOf{$B$} + - and $S_1$ = \ConstCanonicalTypeOf{$S$} + - Note, this generalizes to arbitrary number of type and term parameters. + +Note that if $T$ is a normal form type, then \ConstCanonicalTypeOf{$T$} is +also a normal form type. + + +\subsubsection{Null check operator} + +When evaluating an expression of the form \code{$e$!}, +where $e$ evaluates to a value $v$, +a dynamic type error occurs if $v$ is \code{null}, +and otherwise the expression evaluates to $v$. + +\subsubsection{Null aware operator} + +The semantics of the null aware operator \code{?.} are defined via a source to source +translation of expressions into Dart code extended with a let binding construct. +The translation is defined using meta-level functions over syntax. We use the +notation \code{fn[x : Exp] : Exp => E} to define a meta-level function of type \code{Exp +-> Exp} (that is, a function from expressions to expressions), and similarly +\code{fn[k : Exp -> Exp] : Exp => E} to define a meta-level function of type \code{Exp -> +Exp -> Exp}. Where obvious from context, we elide the parameter and return +types on the meta-level functions. The meta-variables \code{F} and \code{G} are used to +range over meta-level functions. Application of a meta-level function is +written as \code{F[p]} where \code{p} is the argument. + +The null-shorting translation of an expression \code{e} is meta-level function \code{F} of +type \code{(Exp -> Exp) -> Exp} which takes as an argument the continuation of \code{e} and +produces an expression semantically equivalent to \code{e} with all occurrences of +\code{?.} eliminated in favor of explicit sequencing using a \code{let} construct. + +Let \code{ID} be the identity function \code{fn[x : Exp] : Exp => x}. + +The expression translation of an expression \code{e} is the result of applying the +null-shorting translation of \code{e} to \code{ID}. That is, if \code{e} translates to \code{F}, +then \code{F[ID]} is the expression translation of \code{e}. + +We use \code{EXP(e)} as a shorthand for the expression translation of \code{e}. That is, +if the null-shorting translation of \code{e} is \code{F}, then \code{EXP(e)} is \code{F[ID]}. + +We extend the expression translation to argument lists in the obvious way, using +\code{ARGS(args)} to denote the result of applying the expression translation +pointwise to the arguments in the argument list \code{args}. + +We use three combinators to express the translation. + +The null-aware shorting combinator \code{SHORT} is defined as: +\begin{dartCode} + SHORT = fn[r : Exp, c : Exp -> Exp] => + fn[k : Exp -> Exp] : Exp => + let x = r in x == null ? null : k[c[x]] +\end{dartCode} + +where \code{x} is a fresh object level variable. The \code{SHORT} combinator is used to +give semantics to uses of the \code{?.} operator. It is parameterized over the +receiver of the conditional property access (\code{r}) and a meta-level function +(\code{c}) which given an object-level variable (\code{x}) bound to the result of +evaluating the receiver, produces the final expression. The result is +parameterized over the continuation of the expression being translated. The +continuation is only called in the case that the result of evaluating the +receiver is non-null. + +The shorting propagation combinator \code{PASSTHRU} is defined as: +\begin{dartCode} + PASSTHRU = fn[F : (Exp -> Exp) -> Exp, c : Exp -> Exp] => + fn[k : Exp -> Exp] : Exp => F[fn[x] => k[c[x]]] +\end{dartCode} + +The \code{PASSTHRU} combinator is used to give semantics to expression forms which +propagate null-shorting behavior. It is parameterized over the translation \code{F} +of the potentially null-shorting expression, and over a meta-level function \code{c} +which given an expression which denotes the value of the translated +null-shorting expression produces the final expression being translated. The +result is parameterized over the continuation of the expression being +translated, which is called unconditionally. + +The null-shorting termination combinator TERM is defined as: +\begin{dartCode} + TERM = fn[r : Exp] => fn[k : Exp -> Exp] : Exp => k[r] +\end{dartCode} + +The \code{TERM} combinator is used to give semantics to expressions which neither +short-circuit nor propagate null-shorting behavior. It is parameterized over +the translated expression, and simply passes on the expression to its +continuation. + +- A property access \code{$e$?.$f$} translates to: + - \code{SHORT[EXP($e$), fn[x] => x.$f$]} +- If $e$ translates to $F$ then \code{$e$.$f$} translates to: + - \code{PASSTHRU[$F$, fn[x] => x.$f$]} +- A null aware method call \code{$e$?.$m$(\metavar{args})} translates to: + - \code{SHORT[EXP($e$), fn[x] => x.$m$(ARGS(\metavar{args}))]} +- If $e$ translates to $F$ then \code{$e$.$m$(\metavar{args})} translates to: + - \code{PASSTHRU[$F$, fn[x] => x.$m$(ARGS(\metavar{args}))]} +- If $e$ translates to $F$ then \code{$e$(\metavar{args})} translates to: + - \code{PASSTHRU[$F$, fn[x] => x(ARGS(\metavar{args}))]} +- If $e_1$ translates to $F$ then \code{$e_1$?[$e_2$]} translates to: + - \code{SHORT[EXP($e_1$), fn[x] => x[EXP($e_2$)]]} +- If $e_1$ translates to $F$ then \code{$e_1$[$e_2$]} translates to: + - \code{PASSTHRU[$F$, fn[x] => x[EXP($e_2$)]]} +- If $e$ translates to $F$ then \code{$e$!} translates to: + - \code{PASSTHRU[$F$, fn[x] => x!]} +- The assignment \code{$e_1$?.$f$ = $e_2$} translates to: + - \code{SHORT[EXP($e_1$), fn[x] => x.f = EXP($e_2$)]} +- The other assignment operators are handled equivalently. +- If $e_1$ translates to $F$ then \code{$e_1$.$f$ = $e_2$} translates to: + - \code{PASSTHRU[$F$, fn[x] => x.$f$ = EXP($e_2$)]} +- The other assignment operators are handled equivalently. +- If $e_1$ translates to $F$ then \code{$e_1$?[$e_2$] = $e_3$} translates to: + - \code{SHORT[EXP($e_1$), fn[x] => x[EXP($e_2$)] = EXP($e_3$)]} +- The other assignment operators are handled equivalently. +- If $e_1$ translates to $F$ then \code{$e_1$[$e_2$] = $e_3$} translates to: + - \code{PASSTHRU[$F$, fn[x] => x[EXP($e_2$)] = EXP($e_3$)]} +- The other assignment operators are handled equivalently. +- A cascade expression \code{$e$..$s$} translates as follows, where $F$ is the + translation of $e$ and \code{x} and \code{y} are fresh object level variables: + \begin{dartCode} + fn[k : Exp -> Exp] : Exp => + $F$[fn[r : Exp] : Exp => let x = r in + let y = EXP(x.$s$) + in k[x] + ] + \end{dartCode} +- A null-shorting cascade expression \code{$e$?..$s$} translates as follows, where \code{x} + and \code{y} are fresh object level variables. + \begin{dartCode} + fn[k : Exp -> Exp] : Exp => + let x = EXP($e$) in x == null ? null : let y = EXP(x.$s$) in k(x) + \end{dartCode} +- All other expressions are translated compositionally using the \code{TERM} + combinator. Examples: + - An identifier \code{x} translates to \code{TERM[x]} + - A list literal \code{[$e_1$, \ldots, $e_n$]} translates to \code{TERM[ [EXP($e_1$), \ldots, EXP($e_n$)] ]} + - A parenthesized expression \code{($e$)} translates to \code{TERM[(EXP($e$))]} + +The language specification specifies that an invocation of any of several +operators is considered equivalent to a member access (this applies to +relational expressions, bitwise expressions, shift expressions, additive +expressions, multiplicative expressions, and unary expressions). + +*For example, \code{$a$ + $b$} is specified as equivalent to \code{$a$.plus($b$)}, +where \code{plus} is assumed to be a method with the same behavior as \code{+}. +Similarly, \code{-$e$} is equivalent to \code{$e$.unaryMinus()}.* + +This equivalence is not applicable in the above rules, so operators not +mentioned specifically in a rule are handled in the case for 'other' +expressions, not in the case for \code{$e$.m(\metavar{args})}. + +*This means that the null-shorting transformation stops at operators. For +instance, \code{$e$?.$f$ + $b$} is a compile-time error because \code{$e$?.$f$} can be null, it is +not an expression where both \code{.$f$} and \code{+ $b$} will be skipped if \code{$e$} is null. +Similarly, both \code{-$a$?.$f$} and \code{~$a$?.$f$} are errors, and do not null-short like +\code{$a$?.$f$.op()}.* + +\subsubsection{Late fields and variables} + +A non-local \LATE{} variable declaration $D$ implicitly induces a getter +into the enclosing scope. It also induces an implicit setter iff one of the +following conditions is satisfied: + + - $D$ is non-final. + - $D$ is late, final, and has no initializing expression. + +The late final variable declaration with no initializer is permitted, and +introduces a variable which may be assigned to so long as the variable is not +known to be definitely assigned. The property that the variable is never +mutated after initialization is enforced dynamically rather than statically. + +An instance variable declaration may be declared \COVARIANT{} iff it introduces +an implicit setter. + +A read of a field or variable which is marked as \LATE{} which has not yet been +written to causes the initializer expression of the variable to be evaluated to +a value, assigned to the variable or field, and returned as the value of the +read. + - If there is no initializer expression, the read causes a runtime error to be + thrown. + - Evaluating the initializer expression may validly cause a write to the field + or variable, assuming that the field or variable is not final. In this + case, the variable assumes the written value. The final value of the + initializer expression overwrites any intermediate written values. + - Evaluating the initializer expression may cause an exception to be thrown. + If the variable was written to before the exception was thrown, the value of + the variable on subsequent reads is the last written value. If the variable + was not written before the exception was thrown, then the next read attempts + to evaluate the initializer expression again. + - If a variable or field is read from during the process of evaluating its own + initializer expression, and no write to the variable has occurred, the read + is treated as a first read and the initializer expression is evaluated + again. + +Let $D$ be a \LATE{} and \FINAL{} non-local variable declaration named \code{v} +without an initializing expression. +It is a run-time error, to invoke the setter \code{v=} which is +implicitly induced by $D$ if a value has previously been assigned to \code{v} +(which could be due to an initializing formal or a constructor initializer +list, or due to an invocation of the setter). + +Let $D$ be a \LATE{} and \FINAL{} local variable declaration named \code{v}. It is a +run-time error, to assign a value to \code{v} if a value has previously +been assigned to \code{v}. + +Note that this includes the implicit initializing writes induced by +evaluating the initializer during a read. Hence, the following program +terminates with an error. + +\begin{dartCode} +int i = 0; +\LATE\ \FINAL\ int x = i++ == 0 ? x + 1 : 0; +\VOID\ main() \{ + print(x); +\} +\end{dartCode} + +A toplevel or static variable with an initializer is evaluated as if it +was marked \LATE. Note that this is a change from pre-NNBD semantics in that: + - Throwing an exception during initializer evaluation no longer sets the + variable to \code{null} + - Reading the variable during initializer evaluation is no longer checked for, + and does not cause an error. + +\subsubsection{Boolean conditional evaluation} + +The requirement that the condition in a boolean conditional control expression +(e.g. the a conditional statement, conditional element, \code{while} loop, etc) be +assignable to \code{bool} is unchanged from pre null-safe Dart. The change in +assignability means that the static type of the condition may only be \DYNAMIC, +\code{Never}, or \code{bool}. In full null-safe Dart, an expression of type \code{Never} will +always diverge and an expression of type \code{bool} will never evaluate to a value +other than \code{true} or \code{false}, and hence no conversion is required in these +cases. A conditional expression of type \DYNAMIC{} may evaluated to any value, +and hence must be implicitly downcast to \code{bool}, after which no further check is +required. + +During unsound null checking execution, values of type \code{Never} and \code{bool} may +evaluate to \code{null}, and so a boolean conversion check must be performed in +addition to any implicit downcasts implied. The full semantics then are given +as follows. + +Given a boolean conditional expression $e$ where $e$ has type $S$, it is a +static error if $S$ is not assignable to \code{bool}. Otherwise: + +With sound null checking, evaluation proceeds as follows: + - First $e$ is implicitly cast to \code{bool} if required. + - This cast may fail, and if so it is a dynamic error. + - If the cast does not fail, then the result is known to be a non-null + boolean, and evaluation of the enclosing conditional proceeds as usual. + +With unsound null checking, evaluation proceeds as follows: + - First $e$ is implicitly cast to \code{bool} if required (using + \code{LEGACY_SUBTYPE($e$.runtimeType, bool)}) + - This cast may fail, and if so it is a dynamic error. + - If the cast does not fail, then the result may still be \code{null}, and so the + result must be checked against \code{null}. + - If the \code{null} check fails, it is an \code{AssertionError}, otherwise evaluation + of the enclosing conditional proceeds as usual. + + +\subsection{Core library changes} + +Certain core libraries APIs will have a change in specified behavior only when +interacting with opted in code. These changes are as follows. + +Calling the \code{.length} setter on a \code{List} with element type $E$ with an argument +greater than the current length of the list is a runtime error unless +\code{Null <: $E$}. + +The \code{Iterator.current} getter is given an non-nullable return type, and is +changed such that the behavior if it is called before calling +\code{Iterator.moveNext} or after \code{Iterator.moveNext} has returned \code{false} is +unspecified and implementation defined. In most core library implementations, +the implemented behavior will to return \code{null} if the element type is +\code{nullable}, and otherwise to throw an error. + +\subsubsection{Legacy breaking changes} + +We will make a small set of minimally breaking changes to the core library APIs +that apply to legacy code as well. These changes are as follows. + +The \code{String.fromEnvironment} and \code{int.fromEnvironment} contructors have default +values for their optional parameters. + +\subsection{Migration features} + +For migration, we support incremental adoption of non-nullability as described +at a high level in +https://github.com/dart-lang/language/blob/master/accepted/2.12/nnbd/roadmap.md. + +\subsubsection{Opted in libraries} + +Libraries and packages must opt into the feature as described elsewhere. An +opted-in library may depend on un-opted-in libraries, and vice versa. + +\subsubsection{Errors as warnings} + +An earlier version of this proposal specified that null safety related static +errors in opted-in code should be demoted to warnings when running with +unsound null checking. This behavior has been eliminated based on early +feedback. Null safety related errors in opted-in code behave as usual +independently of the compilation mode, subject to differences in const +evaluation and the usual suppression of errors when interacting with legacy +(opted-out) code (see below). + +\subsubsection{Legacy libraries} + +Static checking for a library which has not opted into this feature (a *legacy* +library) is done using the semantics as of the last version of the language +before this feature ships (or the last version to which it has opted in, if that +is different). All opted-in libraries upstream from the legacy library are +viewed by the legacy library with nullability related features erased from their +APIs. In particular: + - All types of the form \code{$T$?} in the opted-in API are treated as $T$. + - All required named parameters are treated as optional named parameters. + - The type \code{Never} is treated as the type \code{Null}. + +In a legacy library, none of the new syntax introduced by this proposal is +available, and it is a static error if it is used. + +\subsubsection{Importing legacy libraries from opted-in libraries} + +The type system is extended with a notion of a legacy type operator. For every +type $T$, there is an additional type \code{$T$*} which is the legacy version of the +type. There is no surface syntax for legacy types, and implementations should +display the legacy type \code{$T$*} in the same way that they would display the type +$T$, except in so far as it is useful to communicate to programmers for the +purposes of error messages that the type originates in legacy code. + +When static checking is done in an opted-in library, types which are imported +from legacy libraries are seen as legacy types. However, type inference in +the opted-in library "erases" legacy types. That is, if a missing type +parameter, local variable type, or closure type is inferred to be a type $T$, +all occurrences of \code{$S$*} in $T$ shall be replaced with $S$. As a result, legacy +types will never appear as type annotations in opted-in libraries, nor will they +appear in reified positions. + +\subsubsection{Typedefs defined in legacy libraries used in opted-in libraries} + +A typedef which is define in a legacy library and used in an opted-in library is +treated as defining a function type, all of the components of which are +legacy. The function type itself is treated as non-nullable (and not legacy) at +the top level. Hence given the following program, it is an error to assign a +nullable value to a variable of type $F$ in an opted-in library, but any +function which is compatible with a legacy function of type +\code{int* Function(int*)} may be assigned to such a variable. + +\begin{dartCode} +// Opted-out library "opted\_out.dart". +\TYPEDEF\ F = int \FUNCTION(int); + +// Opted-in library "main.dart". +\IMPORT\ "opted\_out.dart"; + +int? f1(int x) => x; + +\VOID\ test() \{ + F f = null; // Static error + f = f1; // No error +\} +\end{dartCode} + +\subsubsection{Exports} + +If a legacy library re-exports an opted-in library, the re-exported symbols +retain their opted-in status (that is, downstream migrated libraries will see +their nnbd-aware types). + +It is an error for an opted-in library to re-export symbols which are defined in +a legacy library (note that a symbol which is defined in an opted-in library and +then exported from a legacy library is accepted for re-export from a third +opted-in library since the symbol is not **defined** in the legacy library which +first exports it). + +\subsubsection{Super-interface and member type computation with legacy types} + +A class defined in a legacy library may have in its set of super-interfaces both +legacy and opted-in interfaces, and hence may have members which are derived +from either, or both. Similarly, a class defined in an opted-in library may +have in its set of super-interfaces both legacy and opted-in interfaces, and +hence may have members which are derived from either, or both. We define the +super-interface and member signature computation for such classes as follows. + +\paragraph{Classes defined in legacy libraries} + +The legacy erasure of a type $T$ denoted \LegacyErasureTypeOf{$T$} is $T$ with all +occurrences of \code{?} removed, \code{Never} replaced with \code{Null}, \REQUIRED{} removed +from all parameters, and all types marked as legacy types. + +A direct super-interface of a class defined in a legacy library (that is, an +interface which is listed in the \EXTENDS, \code{implements} or \code{with} clauses of +the class) has all generic arguments (and all sub-components of the generic +arguments) marked as legacy types. + +If a class \code{C} in a legacy library implements the same generic class \code{I} more +than once, it is an error if the \LegacyErasureTypeOf{} all such super-interfaces +are not all syntactically equal. + +When \code{C} implements \code{I} once, and also when \code{C} implements \code{I} more than once +without error, \code{C} is considered to implement the canonical signature given by +\LegacyErasureTypeOf{} of the super-interfaces in question. This determines the +outcome of dynamic instance checks applied to instances of \code{C}, as well as +static subtype checks on expressions of type \code{C}. + +A member which is defined in a class in a legacy library (whether concrete or +abstract), is given a signature in which every type is a legacy type. It is an +error if the signature of a member is not a correct override of all members of +the same name in the direct super-interfaces of the class, using the legacy +subtyping rules. + +Using the legacy erasure for checking super-interfaces accounts for opted-out +classes which depend on both opted-in and opted-out versions of the same generic +interface. For example: + +\begin{dartCode} +//opted in +\CLASS\ I \{\} + +// opted in +\CLASS\ A \IMPLEMENTS\ I \{\} + +// opted out +\CLASS\ B \IMPLEMENTS\ I \{\} + +// opted out +\CLASS\ C \EXTENDS\ A \IMPLEMENTS\ B \{\} +\end{dartCode} + +The class \code{C} is not considered erroneous, despite implementing both \code{I} +and \code{I}, since legacy erasure makes both of those interfaces equal. The +interface which \code{C} is considered to implement is \code{I}. + + +\paragraph{Classes defined in legacy libraries as seen from opted-in libraries} + +Members inherited in a class in an opted-in library, which are inherited via a +class or mixin defined in a legacy library are viewed with their erased legacy +signature, even if they were original defined in an opted-in library. Note that +if a class which is defined in a legacy library inherits a member with the same +name from multiple super-interfaces, then error checking is done as usual using +the legacy typing rules which ignore nullability. This means that it is valid +for a legacy class to inherit the same member signature with contradictory +nullability information. For the purposes of member lookup within a legacy +library, nullability information is ignored, and so it is valid to simply erase +the nullability information within the legacy library. When referenced from an +opted-in library, the same erasure is performed, and the member is seen at its +legacy type. + +We use legacy subtyping when checking inherited member signature coherence in +classes because opted out libraries may bring together otherwise incompatible +member signatures without causing an error. + +\begin{dartCode} +// opted\_in.dart +\CLASS\ A \{ + int? foo(int? x) \{\} +\} +\CLASS\ B \{ + int foo(int x) \{\} +\} +\end{dartCode} + +\begin{dartCode} +// opted\_out.dart +// @dart = 2.6 +import 'opted\_in.dart'; + +\CLASS\ C \EXTENDS\ A \IMPLEMENTS\ B \{\} +\end{dartCode} + +The class \code{C} is accepted, since the versions of \code{foo} inherited from \code{A} and +\code{B} are compatible. + +If the class \code{C} is now used within an opted-in library, we must decide what +signature to ascribe to \code{foo}. The \LegacyErasureTypeOf{} function computes a legacy +signature for \code{foo} which drops the nullability information producing a single +signature, in this case \code{int* Function(int*)}. Consequently, the following code +is accepted: + +\begin{dartCode} +//opted in +\IMPORT\ 'opted\_out.dart'; +\VOID\ test() \{ + \NEW\ C().foo(null).isEven; +\} +\end{dartCode} + +\paragraph{Classes defined in opted-in libraries} + +The \NnbdTopMergeName{} of two types $T$ and $S$ is the unique type $R$ defined +as: + - \NnbdTopMerge{Object?}{Object?} = \code{Object?} + - \NnbdTopMerge{\DYNAMIC}{\DYNAMIC} = \code{\DYNAMIC} + - \NnbdTopMerge{\VOID}{\VOID} = \code{\VOID} + - \NnbdTopMerge{Object?}{\VOID} = \code{Object?} + - And the reverse + - \NnbdTopMerge{\DYNAMIC}{\VOID} = \code{Object?} + - And the reverse + - \NnbdTopMerge{Object?}{\DYNAMIC} = \code{Object?} + - And the reverse + - \NnbdTopMerge{Object*}{\VOID} = \code{Object?} + - And the reverse + - \NnbdTopMerge{Object*}{\DYNAMIC} = \code{Object?} + - And the reverse + - \NnbdTopMerge{Never*}{Null} = \code{Null} + - And the reverse + - \NnbdTopMerge{$T$?}{$S$?} = \code{\NnbdTopMerge{$T$}{$S$}?} + - \NnbdTopMerge{$T$?}{$S$*} = \code{\NnbdTopMerge{$T$}{$S$}?} + - \NnbdTopMerge{$T$*}{$S$?} = \code{\NnbdTopMerge{$T$}{$S$}?} + - \NnbdTopMerge{$T$*}{$S$*} = \code{\NnbdTopMerge{$T$}{$S$}*} + - \NnbdTopMerge{$T$*}{$S$} = \NnbdTopMerge{$T$}{$S$} + - \NnbdTopMerge{$T$}{$S$*} = \NnbdTopMerge{$T$}{$S$} + + - And for all other types, recursively applying the transformation over the + structure of the type + - e.g. \NnbdTopMerge{$C$<$T$>}{$C$<$S$>} = \code{$C$<\NnbdTopMerge{$T$}{$S$}>} + + - When computing the \NnbdTopMergeName{} of two method parameters at least one of + which is marked as covariant, the following algorithm is used to compute the + canonical parameter type. + - Given two corresponding parameters of type $T_1$ and $T_2$ where at least + one of the parameters has a \COVARIANT{} declaration: + - if \code{$T_1$ <: $T_2$} and \code{$T_2$ <: $T_1$} then the result is \NnbdTopMerge{$T_1$}{$T_2$}, + and it is covariant. + - otherwise, if \code{$T_1$ <: $T_2$} then the result is $T_2$ and it is covariant + - otherwise the result is $T_1$ and it is covariant + +In other words, \NnbdTopMergeName{} takes two types which are structurally equal +except for the placement \code{*} types, and the particular choice of top types, and +finds a single canonical type to represent them by replacing \code{?} with \code{*} or +adding \code{*} as required.. The \NnbdTopMergeName{} of two types is not defined for +types which are not otherwise structurally equal. + +The \NnbdTopMergeName{} of more than two types is defined by taking the +\NnbdTopMergeName{} of the first two, and then recursively taking the +\NnbdTopMergeName{} of the rest. + +A direct super-interface of a class defined in an opted-in library (that is, an +interface which is listed in the \EXTENDS, \IMPLEMENTS, or \WITH{} clauses of +the class) has all generic arguments (and all sub-components of the generic +arguments) marked as nullable or non-nullable as written. + +If a class $C$ in an opted-in library implements the same generic class $I$ more +than once as \code{$I_0$, \ldots, $I_n$}, and at least one of the $I_i$ is not syntactically +equal to the others, then it is an error if \NnbdTopMerge{$S_0$, \ldots}{$S_n$} is not +defined where $S_i$ is \NormalizedTypeOf{$I_i$}. Otherwise, $C$ is considered to +implement the canonical interface given by \NnbdTopMerge{$S_0$, \ldots}{$S_n$}. This +determines the outcome of dynamic instance checks applied to instances of $C$, +as well as static subtype checks on expressions of type $C$. + +If a class $C$ in an opted-in library overrides a member, it is an error if its +signature is not a subtype of the types of all overriden members from all +direct super-interfaces (whether legacy or opted-in). This implies that +override checks for a member $m$ may succeed due to a legacy member signature +for $m$ in a direct super-interface, even in the case where an indirect +super-interface has a member signature for $m$ where the override would be a +compile-time error. For example: + +\begin{dartCode} +// opted\_in.dart +\CLASS\ A \{ + int foo(int? x) \{\} +\} +\end{dartCode} + +\begin{dartCode} +// opted\_out.dart +// @dart = 2.6 +import 'opted\_in.dart'; + +\CLASS\ B \EXTENDS\ A \{\} +\end{dartCode} + +\begin{dartCode} +// opted\_in.dart +\CLASS\ C \EXTENDS\ B \{ + // Override checking is done against the legacy signature of B.foo. + int? foo(int x) \{\} +\} +\end{dartCode} + +It is difficult to predict the outcome of migrating \code{B} in such situations, but +lints or hints may be used by tools to communicate to developers that \code{C} may +need to be changed again when \code{B} is migrated. + +If a class $C$ in an opted-in library inherits a member $m$ with the same name +from multiple direct super-interfaces (whether legacy or opted-in), let +\code{$T_0$, \ldots, $T_n$} be the signatures of the inherited members. If there is exactly one +$T_i$ such that \code{NNBD_SUBTYPE($T_i$, $T_k$)} for all $k \in 0 .. n$ then the signature +of $m$ is considered to be $T_i$. If there is more than one such $T_i$, then it +is an error if the \code{\NnbdTopMergeName} of \code{$S_0$, \ldots, $S_n$} does not exist, where $S_i$ +is \NormalizedTypeOf{$T_i$}. Otherwise, the signature of $m$ for the purposes of member +lookup is the \code{\NnbdTopMergeName} of the $S_i$. + +Note that when a member $m$ is inherited from multiple indirect super-interfaces +**via** a single direct super-interface, override checking is only performed +against the signature of the direct super-interface which mediates the +inheritance as described above. Hence the following example is not an error, +since the direct super-interface $C$ of $D$ mediates the conflicting inherited +signatures of \code{foo} as \code{$C$.foo} with signature \code{int* Function(int*)}. + +\begin{dartCode} +// opted\_in.dart +\CLASS\ A \{ + int? foo(int? x) \{\} +\} +\CLASS\ B \{ + int foo(int x) \{\} +\} +\end{dartCode} + +\begin{dartCode} +// opted\_out.dart +// @dart = 2.6 +\IMPORT\ 'opted\_in.dart'; + +\CLASS\ C \EXTENDS\ A \IMPLEMENTS\ B \{\} + +\end{dartCode} + +\begin{dartCode} +//opted in +\IMPORT\ 'opted\_out.dart'; +\CLASS\ D \EXTENDS\ C \{\} +\VOID\ test() \{ + \NEW\ D().foo(null).isEven; +\} +\end{dartCode} + +\subsubsection{Type reification} + +All types reified in legacy libraries are reified as legacy types. Runtime +subtyping checks treat them according to the subtyping rules specified +separately. + +\subsubsection{Runtime checks and unsound null checking} + +With unsound null checking, runtime type tests (including explicit and +implicit casts) shall succeed whenever the runtime type test would have +succeeded if all \code{?} on types were ignored, \code{*} was added to each type, and +\REQUIRED{} parameters were treated as optional. This has the effect of treating +\code{Never} as equivalent to \code{Null}, restoring \code{Null} to the bottom of the type +hierarchy, treating \code{Object} as nullable, and ignoring \REQUIRED{} on named +parameters. This is intended to provide the same subtyping results as pre-nnbd +Dart. + +Instance checks (\code{e \IS\ T}) and casts (\code{e \AS\ T}) behave differently when run +with sound vs unsound null checking. + +We define the instance tests with unsound null checking and sound null +checking as follows: + +**With unsound null checking**: if $e$ evaluates to a value $v$ and $v$ has runtime +type $S$, an instance check \code{$e$ \IS\ $T$} occurring in a **legacy library** or an +**opted-in library** is evaluated as follows: + - If $v$ is \code{null} and $T$ is a legacy type, return \code{LEGACY_SUBTYPE($T$, Null) + || LEGACY_SUBTYPE(Object, $T$)} + - If $v$ is \code{null} and $T$ is not a legacy type, return \code{NNBD_SUBTYPE(Null, $T$)} + - Otherwise return \code{LEGACY_SUBTYPE($S$, $T$)} + +A type is a legacy type if it is of the form \code{$R$*} for some $R$ after normalizing +away nested nullability annotations - e.g. \code{int*} is a legacy type, but \code{int?*} +is not, since the normal form of the latter is \code{int?}. + +Note that except in the case that $T$ is of the form $X$ or \code{$X$*} for some type +variable $X$, it is statically decidable which of the first two clauses apply in +the case that $v$ is \code{null}. + +**With sound null checking**: if $e$ evaluates to a value $v$ and $v$ has +runtime type $S$, an instance check \code{$e$ \IS\ $T$} occurring in an **opted-in +library** is evaluated as follows: + - Return \code{NNBD_SUBTYPE($S$, $T$)} + +Note that it is an error to run a program containing legacy libraries with +sound null checking. + +Note that given the definitions above, the result of an instance check may vary +depending on whether it is run with sound or unsound null checking. However, +in the specific case that the value being checked is \code{null}, instance checks +will always return the same result regardless of mode, and regardless of +whether the check occurs in an opted in or opted out library. + +\begin{verbatim} +| T | Any mode | +| -------- | ------------- | +| Never | false | +| Never* | true | +| Never? | true | +| Null | true | +| int | false | +| int* | false | +| int? | true | +| Object | false | +| Object* | true | +| Object? | true | +| dynamic | true | +\end{verbatim} + +We define casts with unsound null checking and sound null checking as follows: + +**With unsound null checking**: if $e$ evaluates to a value $v$ and $v$ has +runtime type $S$, a cast \code{$e$ \AS\ $T$} **whether textually occurring in a legacy or +opted-in library** is evaluated as follows: + - if \code{LEGACY_SUBTYPE($S$, $T$)} then \code{$e$ \AS\ $T$} evaluates to $v$. Otherwise a + dynamic type error occurs. + +**With sound null checking**: if $e$ evaluates to a value $v$ and $v$ has +runtime type $S$, a cast \code{$e$ \AS\ $T$} **whether textually occurring in a legacy or +opted-in library** is evaluated as follows: + - if \code{NNBD_SUBTYPE($S$, $T$)} then \code{$e$ as $T$} evaluates to $v$. Otherwise a + dynamic type error occurs. + +With unsound null checking, we ensure that opted-in libraries do not break +downstream clients by continuing to evaluate instance checks and casts with +the same semantics as in pre-nnbd Dart. All runtime subtype checks are done +using the legacy subtyping, and instance checks maintain the pre-nnbd behavior +on \code{null} instances. With sound null checking, we use the specified nnbd +subtyping for all instance checks and casts. + +When developers enable sound null checking in their tests and applications, new +runtime cast failures may arise. The process of migrating libraries and +applications will require users to track down these changes in behavior. +Development platforms are encouraged to provide facilities to help users +understand these changes: for example, by providing a debugging option in which +instance checks or casts which would result in a different outcome if run with +sound null checking vs unsound null checking are flagged for the developer by +logging a warning or breaking to the debugger. + +\subsubsection{Automatic debug assertion insertion} + +When running with unsound null checking, implementations may insert code +equivalent to \code{\ASSERT(x != null)} in the prelude of every method or function +defined in an opted-in library for each parameter \code{x} which has a non-nullable +type. When compiling a program with sound null checking, these assertions will +never fire and should be elided, but during the migration when mixed mode code +is being executed it is possible for opted-out libraries to cause the +invariants of the null safety checking to be violated. +%% !!!END TODO!!! + \section*{Appendix: Algorithmic Subtyping} \LMLabel{algorithmicSubtyping} From ef17572ad3b26b04366162f2b5d4e218c9d5d89c Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 8 Oct 2021 11:50:33 +0200 Subject: [PATCH 02/85] Typos: Escaping braces meant to be code --- specification/dartLangSpec.tex | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 6840102a4..243698a5b 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -23399,18 +23399,18 @@ \subsubsection{Grammatical ambiguities and clarifications} \paragraph{Conditional expression ambiguities} Conditional expressions inside of braces are ambiguous between sets and maps. -That is, \code{{ a as bool ? - 3 : 3 }} can be parsed as a set literal \code{{ (a as bool) -? - 3 : 3 }} or as a map literal \code{{ (a as bool ?) - 3 : 3 }}. Parsers will +That is, \code{\{ a as bool ? - 3 : 3 \}} can be parsed as a set literal \code{\{ (a as bool) +? - 3 : 3 \}} or as a map literal \code{\{ (a as bool ?) - 3 : 3 \}}. Parsers will prefer the former parse over the latter. -The same is true for \code{{ a is int ? - 3 : 3 }}. +The same is true for \code{\{ a is int ? - 3 : 3 \}}. -The same is true for \code{{ int ? - 3 : 3 }} if we allow this. +The same is true for \code{\{ int ? - 3 : 3 \}} if we allow this. \paragraph{Null aware subscript} Certain uses of null aware subscripts in conditional expressions are ambiguous. -For example, \code{{ a?[b]:c }} can be parsed either as a set literal or a map +For example, \code{\{ a?[b]:c \}} can be parsed either as a set literal or a map literal, depending on whether the \code{?} is interpreted as part of a null aware subscript or as part of a conditional expression. Whenever there is a sequence of tokens which may be parsed either as a conditional expression or as two From f3faf0cd14d0f3781931e0b92ecbc131431a4060 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 8 Oct 2021 16:53:26 +0200 Subject: [PATCH 03/85] Eliminated text about migration features, adapted the remaining text to preserve the meaning for all null-safety features --- specification/dartLangSpec.tex | 1287 +++++++++----------------------- 1 file changed, 356 insertions(+), 931 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 243698a5b..fc9d3b178 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -23324,6 +23324,8 @@ \subsection{Operator Precedence} \section{Null safety} %% !!!TODO!!! +\newcommand{\FlattenName}{\metavar{flatten}} +\newcommand{\Flatten}[1]{\ensuremath{\FlattenName(\code{#1})}} \newcommand{\NnbdTopMergeName}{\metavar{topMerge}} \newcommand{\NnbdTopMerge}[2]{\ensuremath{\NnbdTopMergeName(\code{{#1},\,\,{#2}})}} @@ -23338,25 +23340,23 @@ \section{Null safety} %% !!!TODO!!! \newcommand{\IsObjectType}[1]{\ensuremath{\IsObjectTypeName(\code{#1})}} \newcommand{\IsBottomTypeName}{\metavar{isBottomType}} -\newcommand{\IsBottomType}[1]{\ensuremath{\IsObjectTypeName(\code{#1})}} +\newcommand{\IsBottomType}[1]{\ensuremath{\IsBottomTypeName(\code{#1})}} \newcommand{\IsNullTypeName}{\metavar{isNullType}} -\newcommand{\IsNullType}[1]{\ensuremath{\IsObjectTypeName(\code{#1})}} +\newcommand{\IsNullType}[1]{\ensuremath{\IsNullTypeName(\code{#1})}} \newcommand{\IsMoreTopTypeName}{\metavar{isMoreTopType}} -\newcommand{\IsMoreTopType}[2]{\ensuremath{\IsObjectTypeName(\code{{#1},\,\,{#2}})}} +\newcommand{\IsMoreTopType}[2]{\ensuremath{\IsMoreTopTypeName(\code{{#1},\,\,{#2}})}} \newcommand{\IsMoreBottomTypeName}{\metavar{isMoreBottomType}} -\newcommand{\IsMoreBottomType}[2]{\ensuremath{\IsObjectTypeName(\code{{#1},\,\,{#2}})}} +\newcommand{\IsMoreBottomType}[2]{\ensuremath{\IsMoreBottomTypeName(\code{{#1},\,\,{#2}})}} \newcommand{\NormalizedTypeOfName}{\metavar{normalizedTypeOf}} \newcommand{\NormalizedTypeOf}[1]{\ensuremath{\NormalizedTypeOfName(\code{#1})}} -\newcommand{\LegacyErasureTypeOfName}{\metavar{legacyErasureTypeOf}} -\newcommand{\LegacyErasureTypeOf}[1]{\ensuremath{\LegacyErasureTypeOfName(\code{#1})}} +\newcommand{\FutureValueTypeOfName}{\metavar{futureValueTypeOf}} +\newcommand{\FutureValueTypeOf}[1]{\ensuremath{\FutureValueTypeOfName(\code{#1})}} -\newcommand{\ConstCanonicalTypeOfName}{\metavar{constCanonicalTypeOf}} -\newcommand{\ConstCanonicalTypeOf}[1]{\ensuremath{\ConstCanonicalTypeOfName(\code{#1})}} \subsection{Syntax} @@ -23389,11 +23389,6 @@ \subsection{Syntax} The grammar of cascade sequences is extended to allow the first cascade of a sequence to be written as \code{?..} indicating that the cascade is null-shorting. -All of the syntax changes for this feature have been incorporated into -the -[formal grammar](https://github.com/dart-lang/language/blob/master/specification/dartLangSpec.tex), -which serves as the canonical reference for the grammatical changes. - \subsubsection{Grammatical ambiguities and clarifications} \paragraph{Conditional expression ambiguities} @@ -23420,83 +23415,55 @@ \subsubsection{Grammatical ambiguities and clarifications} \subsection{Static semantics} -\subsubsection{Legacy types} - -The internal representation of types is extended with a type \code{$T$*} for every type -$T$ to represent legacy pre-NNBD types. This is discussed further in the legacy -library section below. - -\subsubsection{Subtyping} - -We modify the subtyping rules to account for nullability and legacy types as -specified -[here](https://github.com/dart-lang/language/blob/master/resources/type-system/subtyping.md). -We write \code{S <: T} to mean that the type $S$ is a subtype of $T$ according to the -rules specified there. - - -We define \code{LEGACY_SUBTYPE(S, T)} to be true iff $S$ would be a subtype of $T$ -in a modification of the rules above in which all \code{?} on types were ignored, \code{*} -was added to each type, and \REQUIRED{} parameters were treated as optional. -This has the effect of treating \code{Never} as equivalent to \code{Null}, restoring -\code{Null} to the bottom of the type hierarchy, treating \code{Object} as nullable, and -ignoring \REQUIRED{} on named parameters. This is intended to provide the same -subtyping results as pre-nnbd Dart. - -Where potentially ambiguous, we sometimes write \code{NNBD_SUBTYPE(S, T)} to mean -the full subtyping relation without the legacy exceptions defined in the -previous paragraph. - -\subsubsection{Upper and lower bounds} - -We modify the upper and lower bound rules to account for nullability and legacy -types as -specified -[here](https://github.com/dart-lang/language/blob/master/resources/type-system/upper-lower-bounds.md). - \subsubsection{Type normalization} We define a normalization procedure on types which defines a canonical representation for otherwise equivalent -types -[here](https://github.com/dart-lang/language/blob/master/resources/type-system/normalization.md). +types here +(\ref{sec:typeNormalization}). This defines a procedure \NormalizedTypeOfName{} such that \NormalizedTypeOf{$T$} is syntactically equal to \NormalizedTypeOf{$S$} modulo replacement of primitive top types iff \code{$S$ <: $T$} and \code{$T$ <: $S$}. \subsubsection{Future flattening} -The **flatten** function is modified as follows: +The \FlattenName{} function is modified as follows: -**flatten**($T$) is defined by cases on $T$: - - if $T$ is \code{S?} then **flatten**($T$) = **flatten**($S$)\code{?} - - otherwise if $T$ is \code{S*} then **flatten**($T$) = **flatten**($S$)\code{*} - - otherwise if $T$ is \code{FutureOr} then **flatten**($T$) = $S$ - - otherwise if \code{$T$ <: Future} then let $S$ be a type such that \code{$T$ <: Future<$S$>} -and for all $R$, if \code{$T$ <: Future<$R$>} then \code{$S$ <: $R$}; then **flatten**($T$) = $S$ - - otherwise **flatten**($T$) = $T$ +\begin{itemize} +\item if $T$ is \code{S?} then \Flatten{$T$} = \code{\Flatten{$S$}?}. +\item otherwise if $T$ is \code{FutureOr<$S$>} then \Flatten{$T$} = $S$. +\item otherwise if \code{$T$ <: Future} then let $S$ be a type such that + \code{$T$ <: Future<$S$>} and for all $R$, + if \code{$T$ <: Future<$R$>} then \code{$S$ <: $R$}; then \Flatten{$T$} = $S$. +\item otherwise \Flatten{$T$} = $T$ +\end{itemize} \subsubsection{The future value type of an asynchronous non-generator function} -_We specify a concept which corresponds to the static type of objects which may +\commentary{% +We specify a concept which corresponds to the static type of objects which may be contained in the Future object returned by an async function with a given -declared return type._ +declared return type.% +} -Let _f_ be an asynchronous non-generator function with declared return type -$T$. Then the **future value type** of _f_ is **futureValueType**($T$). -The function **futureValueType** is defined as follows: +Let $f$ be an asynchronous non-generator function with declared return type +$T$. Then the \Index{future value type} of $f$ is \FutureValueTypeOf{$T$}. +The function \FutureValueTypeOfName{} is defined as follows: -- **futureValueType**(\code{S?}) = **futureValueType**($S$), for all $S$. -- **futureValueType**(\code{S*}) = **futureValueType**($S$), for all $S$. -- **futureValueType**(\code{Future}) = $S$, for all $S$. -- **futureValueType**(\code{FutureOr}) = $S$, for all $S$. -- **futureValueType**(\VOID) = \VOID. -- **futureValueType**(\DYNAMIC) = \DYNAMIC. -- Otherwise, for all $S$, **futureValueType**($S$) = \code{Object?}. +\begin{itemize} +\item \FutureValueTypeOf{$S$?} = \FutureValueTypeOf{$S$}, for all $S$. +\item \FutureValueTypeOf{Future} = $S$, for all $S$. +\item \FutureValueTypeOf{FutureOr} = $S$, for all $S$. +\item \FutureValueTypeOf{\VOID} = \VOID. +\item \FutureValueTypeOf{\DYNAMIC} = \DYNAMIC. +\item Otherwise, for all $S$, \FutureValueTypeOf{$S$} = \code{Object?}. +\end{itemize} -_Note that it is a compile-time error unless the return type of an asynchronous +\commentary{% +Note that it is a compile-time error unless the return type of an asynchronous non-generator function is a supertype of \code{Future}, which means that -the last case will only be applied when $S$ is \code{Object} or a top type._ +the last case will only be applied when $S$ is \code{Object} or a top type.% +} \subsubsection{Return statements} @@ -23512,9 +23479,11 @@ \subsubsection{Return statements} and $S$ is \VOID. \EndCase -_Comparing to Dart before null-safety, this means that it is no longer allowed +\commentary{% +Comparing to Dart before null-safety, this means that it is no longer allowed to return a void expression in a regular function if the return type is -\code{Null}._ +\code{Null}.% +} At line 15507 about an asynchronous non-generator function with future value type $T_v$, the text is changed as follows: @@ -23538,11 +23507,12 @@ \subsubsection{Return statements} and flatten{S} is not a subtype of $T_v$. \EndCase -_Comparing to Dart before null-safety, this means that it is no longer allowed +\commentary{% +Comparing to Dart before null-safety, this means that it is no longer allowed to return an expression whose flattened static type is \VOID{} in an \code{async} function with future value type \code{Null}; nor is it allowed, in an \code{async} function with future value type \VOID, to return an expression whose flattened -static type is not \VOID, \code{void*}, \DYNAMIC, or \code{Null}. Conversely, it is +static type is not \VOID, \DYNAMIC, or \code{Null}. Conversely, it is allowed to return a future when the future value type is a suitable future; for instance, we can have \code{return Future.value(42)} in an \code{async} function with declared return type \code{Future>}. Finally, let $S$ be @@ -23550,7 +23520,8 @@ \subsubsection{Return statements} return an expression with static type $S$, unless the future value type is a supertype of $S$. This differs from Dart before null-safety in that it was allowed to return an expression of these types with a declared return type -of the form \code{Future} for any $T$._ +of the form \code{Future} for any $T$.% +} The dynamic semantics specified at line 15597 is changed as follows, where $f$ is the enclosing function with declared @@ -23592,74 +23563,62 @@ \subsubsection{Return statements} \subsubsection{Static errors} \paragraph{Nullability definitions} -We say that a type $T$ is **nullable** if \code{Null <: $T$} and not \code{$T$ <: Object}. +We say that a type $T$ is \Index{nullable} if \code{Null <: $T$} and not \code{$T$ <: Object}. This is equivalent to the syntactic criterion that $T$ is any of: - - \code{Null} - - \code{$S$?} for some $S$ - - \code{$S$*} for some $S$ where $S$ is nullable - - \code{FutureOr<$S$>} for some $S$ where $S$ is nullable - - \DYNAMIC{} - - \VOID{} + +\begin{itemize} +\item \code{Null} +\item \code{$S$?} for some $S$ +\item \code{FutureOr<$S$>} for some $S$ where $S$ is nullable +\item \DYNAMIC{} +\item \VOID{} +\end{itemize} Nullable types are types which are definitively known to be nullable, regardless -of instantiation of type variables, and regardless of any choice of replacement -for the \code{*} positions (with \code{?} or nothing). +of instantiation of type variables. -We say that a type $T$ is **non-nullable** if \code{$T$ <: Object}. +We say that a type $T$ is \Index{non-nullable} if \code{$T$ <: Object}. This is equivalent to the syntactic criterion that $T$ is any of: - - \code{Never} - - Any function type (including \FUNCTION) - - Any interface type except \code{Null}. - - \code{$S$*} for some $S$ where $S$ is non-nullable - - \code{FutureOr<$S$>} where $S$ is non-nullable - - \code{$X$ \EXTENDS\ $S$} where $S$ is non-nullable - - \code{$X$ \& $S$} where $S$ is non-nullable + +\begin{itemize} +\item \code{Never} +\item Any function type (including \FUNCTION) +\item Any interface type except \code{Null}. +\item \code{FutureOr<$S$>} where $S$ is non-nullable +\item \code{$X$ \EXTENDS\ $S$} where $S$ is non-nullable +\item \code{$X$ \& $S$} where $S$ is non-nullable +\end{itemize} Non-nullable types are types which are either definitively known to be -non-nullable regardless of instantiation of type variables, or for which -replacing the \code{*} positions with nothing will result in a non-nullable type. +non-nullable regardless of instantiation of type variables. Note that there are types which are neither nullable nor non-nullable. For example \code{X \EXTENDS\ T} where $T$ is nullable is neither nullable nor non-nullable. -We say that a type $T$ is **strictly non-nullable** if \code{$T$ <: Object} and not +We say that a type $T$ is \Index{strictly non-nullable} if \code{$T$ <: Object} and not \code{Null <: $T$}. This is equivalent to the syntactic criterion that $T$ is any of: - - \code{Never} - - Any function type (including \code{Function}) - - Any interface type except \code{Null}. - - \code{FutureOr} where $S$ is strictly non-nullable - - \code{X \EXTENDS\ S} where $S$ is strictly non-nullable - - \code{X \& S} where $S$ is strictly non-nullable - -We say that a type $T$ is **potentially nullable** if $T$ is not non-nullable. + +\begin{itemize} +\item \code{Never} +\item Any function type (including \code{Function}) +\item Any interface type except \code{Null}. +\item \code{FutureOr} where $S$ is strictly non-nullable +\item \code{X \EXTENDS\ S} where $S$ is strictly non-nullable +\item \code{X \& S} where $S$ is strictly non-nullable +\end{itemize} + +We say that a type $T$ is \Index{potentially nullable} if $T$ is not non-nullable. Note that this is different from saying that $T$ is nullable. For example, a type variable \code{X \EXTENDS\ Object?} is a type which is potentially nullable but -not nullable. Note that \code{$T$*} is potentially nullable by this definition if $T$ -is potentially nullable - so \code{int*} is not potentially nullable, but \code{$X$*} where -\code{$X$ \EXTENDS\ int?} is. The potentially nullable types include all of the types +not nullable. The potentially nullable types include all of the types which are either definitely nullable, potentially instantiable to a nullable -type, or for which any migration results in a potentially nullable type. +type. -We say that a type $T$ is **potentially non-nullable** if $T$ is not nullable. +We say that a type $T$ is \Index{potentially non-nullable} if $T$ is not nullable. Note that this is different from saying that $T$ is non-nullable. For example, a type variable \code{$X$ \EXTENDS\ Object?} is a type which is potentially non-nullable -but not non-nullable. Note that \code{$T$*} is potentially non-nullable by this -definition if $T$ is potentially non-nullable. - - -\paragraph{Reachability} - -A number of errors and warnings are updated to take reachability of statements -into account. Computation of code reachability -is -[specified separately](https://github.com/dart-lang/language/blob/master/resources/type-system/flow-analysis.md). - -We say that a statement **may complete normally** if the specified control flow -analysis determines that any control flow path may reach the end of the -statement without returning, throwing an exception not caught within the -statement, breaking to a location outside of the statement, or continuing to a -location outside of the statement. +but not non-nullable. \paragraph{Errors and Warnings} @@ -23694,10 +23653,10 @@ \subsubsection{Static errors} expression unless the variable is marked with the \LATE{} modifier. It is an error if the body of a method, function, getter, or function expression -with a potentially non-nullable return type **may complete normally**. +with a potentially non-nullable return type may complete normally. It is an error if an optional parameter (named or otherwise) with no default -value has a potentially non-nullable type **except** in the parameter list of an +value has a potentially non-nullable type except in the parameter list of an abstract method declaration. It is an error if a required named parameter has a default value. @@ -23742,7 +23701,7 @@ \subsubsection{Static errors} a prefix \AWAIT{} expression that is not nested inside of another function expression. -It is an error for a class with a generative \CONST{} constructor to have a +It is an error for a class with a generative \CONST{} constructor to have a \code{\LATE\ \FINAL} instance variable. It is not a compile time error to write to a \FINAL{} non-local or instance @@ -23758,22 +23717,25 @@ \subsubsection{Static errors} constructor for any class other than \code{Null}). It is an error if any case of a switch statement except the last case (the -default case if present) **may complete normally**. The previous syntactic +default case if present) may complete normally. The previous syntactic restriction requiring the last statement of each case to be one of an enumerated list of statements (break, continue, return, throw, or rethrow) is removed. Given a switch statement which switches over an expression $e$ of type $T$, where the cases are dispatched based on expressions \List{e}{0}{k}: - - It is no longer required that the $e_i$ evaluate to instances of the same + +\begin{itemize} +\item It is no longer required that the $e_i$ evaluate to instances of the same class. - - It is an error if any of the $e_i$ evaluate to a value whose static type is +\item It is an error if any of the $e_i$ evaluate to a value whose static type is not a subtype of $T$. - - It is an error if any of the $e_i$ evaluate to constants for which equality +\item It is an error if any of the $e_i$ evaluate to constants for which equality is not primitive. - - If $T$ is an enum type, it is a warning if the switch does not handle all +\item If $T$ is an enum type, it is a warning if the switch does not handle all enum cases, either explicitly or via a default. - - If $T$ is \code{$Q$?} where $Q$ is an enum type, it is a warning if the switch does +\item If $T$ is \code{$Q$?} where $Q$ is an enum type, it is a warning if the switch does not handle all enum cases and \code{null}, either explicitly or via a default. +\end{itemize} It is an error if a class has a setter and a getter with the same basename where the return type of the getter is not a subtype of the argument type of the @@ -23781,22 +23743,18 @@ \subsubsection{Static errors} assignability and hence makes no exception for \DYNAMIC. If the static type of $e$ is \VOID, the expression \code{\AWAIT\,\,$e$} is a compile-time -error. *This implies that the list item at line 18281 -will be removed from the language specification.* - -A compile-time error occurs if an expression has static type \code{\VOID{}*}, and it -does not occur in any of the ways specified in the list on line 18238. -*This implies that \code{\VOID{}*} is treated the same as \VOID.* +error. This implies that the list item at line 18281 +will be removed from the language specification. Let $C$ be a type literal denoting a class, mixin, or extension. It is a warning -to use a null aware member access with receiver $C$. *E.g., \code{$C$?.staticMethod()} -is a warning.* +to use a null aware member access with receiver $C$. +\commentary{E.g., \code{$C$?.staticMethod()} is a warning.} It is a warning to use a null aware operator (\code{?.}, \code{?[]}, \code{?..}, \code{??}, \code{??=}, or -\code{...?}) on an expression of type $T$ if $T$ is **strictly non-nullable**. +\code{...?}) on an expression of type $T$ if $T$ is strictly non-nullable. It is a warning to use the null check operator (\code{!}) on an expression of type -$T$ if $T$ is **strictly non-nullable** . +$T$ if $T$ is strictly non-nullable. It is no longer a warning to override a method which has a default value for a parameter with a method with a different default value for the corresponding @@ -23805,10 +23763,11 @@ \subsubsection{Static errors} \subsubsection{Local variables and definite (un)assignment} As part of the null safety release, errors for local variables are specified to -take into account **definite assignment** and **definite unassignment** (see the -section on Definite Assignment below). We say that a variable is **potentially -assigned** if it is not **definitely unassigned**, and that a variable is -**potentially unassigned** if it is not **definitely assigned**. +take into account definite assignment and definite unassignment (see the +section on Definite Assignment below). We say that a variable is +\Index{potentially assigned} +if it is not definitely unassigned, and that a variable is +\Index{potentially unassigned} if it is not definitely assigned. In all cases in this section, errors that are described as occurring on reads of a variable are intended to apply to all form of reads, including indirectly as @@ -23817,29 +23776,31 @@ \subsubsection{Local variables and definite (un)assignment} variable are intended to apply to all form of writes. It is a compile time error to assign a value to a \FINAL, non-\LATE{} local -variable which is **potentially assigned**. Thus, it is *not* a compile time -error to assign to a **definitely unassigned** \FINAL{} local variable. +variable which is \Index{potentially assigned}. Thus, it is not a compile time +error to assign to a definitely unassigned \FINAL{} local variable. It is a compile time error to assign a value to a \FINAL, \LATE{} local variable -if it is **definitely assigned**. Thus, it is *not* a compile time error to -assign to a **potentially unassigned** \FINAL, \LATE{} local variable. +if it is definitely assigned. Thus, it is not a compile time error to +assign to a potentially unassigned \FINAL, \LATE{} local variable. -*Note that a variable is always considered **definitely assigned** and not -**definitely unassigned** if it has an explicit initializer, or an implicit +\commentary{% +Note that a variable is always considered definitely assigned and not +definitely unassigned if it has an explicit initializer, or an implicit initializer as part of a larger construct (e.g. the loop variable in a for-in -construct).* +construct).% +} It is a compile time error to read a local variable when the variable is -**definitely unassigned** unless the variable is non-\FINAL, and non-\LATE, +definitely unassigned, unless the variable is non-\FINAL, and non-\LATE, and has nullable type. It is a compile time error to read a local variable when the variable is -**potentially unassigned** unless the variable is non-\FINAL{} and has nullable +potentially unassigned, unless the variable is non-\FINAL{} and has nullable type, or is \LATE. The errors specified above are summarized in the following table, where \code{int} is -used as an example of an arbitrary **potentially non-nullable** type, \code{int?} is -used as an example of an arbitrary **nullable** type, and $T$ is used to stand +used as an example of an arbitrary potentially non-nullable type, \code{int?} is +used as an example of an arbitrary nullable type, and $T$ is used to stand for a type of any nullability. A variable which has an initializer (explicit or implicit) is always considered definitely assigned, and is never considered definitely unassigned. @@ -23884,12 +23845,14 @@ \subsubsection{Local variables and inference} initializer (explicit or implicit) and is not \FINAL, then the declaration is treated as an assignment for the purposes of promotion. -*Treating the declared type of the variable as a "type of interest" implies that +\commentary{% +Treating the declared type of the variable as a "type of interest" implies that if the variable has a nullable type, then the non-nullable version of that type is also a type of interest. Treating the initialization as an assignment for the purposes of promotion means that initializing a mutable variable declared at type \code{$T$?} with a value of non-nullable type $T$ immediately promotes the -variable to the non-nullable type.* +variable to the non-nullable type.% +} \begin{dartCode} \VOID test() \{ @@ -23917,10 +23880,14 @@ \subsubsection{Expression typing} analysis of such an invocation or tear-off shall be the type declared on the relevant member on \code{Object}. For example, given a receiver $o$ of type \code{$T$?}, invoking an \code{Object} member on $o$ shall use the type of the member as declared -on \code{Object}, regardless of the type of the member as declared on $T$ (note that +on \code{Object}, regardless of the type of the member as declared on $T$. + +\commentary{% +Note that the type as declared on $T$ must be a subtype of the type on \code{Object}, and so choosing the \code{Object} type is a sound choice. The opposite choice is not -sound). +sound.% +} \commentary{% Note that evaluation of an expression $e$ of the form \code{$e_1$\,\,==\,\,$e_2$} is not an @@ -23958,19 +23925,8 @@ \subsubsection{Expression typing} is null.% } -In legacy mode, an override of \code{operator\,\,==} with no explicit parameter type -inherits the parameter type of the overridden method if any override of -\code{operator\,\,==} between the overriding method and \code{Object.==} has an explicit -parameter type. Otherwise, the parameter type of the overriding method is -\DYNAMIC. - -Top level variable and local function inference is performed -as specified separately, https://github.com/dart-lang/language/blob/master/resources/type-system/inference.md. -Method body inference is not yet specified. - If no type is specified in a catch clause, then the default type of the error -variable is \code{Object}, instead of \DYNAMIC{} as was the case in pre-null safe -Dart. +variable is \code{Object}. \paragraph{Spread element typing} @@ -24007,13 +23963,13 @@ \subsubsection{Super-bounded types} The definition of a super-bounded type is changed such that occurrences of \code{Null} are replaced by types involving \code{Never}, and \code{Object} is replaced by \code{Object?}. Moreover, top types in invariant positions and in positions that -have no variance (*unused type parameters in a type alias*) are given the +have no variance (\commentary{unused type parameters in a type alias}) are given the same treatment as top types in covariant positions. This causes one sentence to change, with the following result: -Let _T'_ be the result of replacing every occurrence in _T_ of a type _S_ -in a contravariant position where _S <: Never_ by \code{Object?}, and every -occurrence in _T_ of a top type in a position which is not contravariant by +Let $T'$ be the result of replacing every occurrence in $T$ of a type $S$ +in a contravariant position where \code{$S$ <: Never} by \code{Object?}, and every +occurrence in $T$ of a top type in a position which is not contravariant by \code{Never}. \subsubsection{Least and greatest closure} @@ -24056,7 +24012,7 @@ \subsubsection{Assignability} The definition of assignability is changed as follows. -A type $T$ is **assignable** to a type $S$ if $T$ is \DYNAMIC, or if $T$ is a +A type $T$ is \Index{assignable} to a type $S$ if $T$ is \DYNAMIC, or if $T$ is a subtype of $S$. \subsubsection{Generics} @@ -24074,12 +24030,12 @@ \subsubsection{Combined member signatures} This is achieved by changing this paragraph, line 4373, to the following: -"Let _mall_ be the result of applying \code{\NnbdTopMergeName} to -the elements in _Mall_, ordered according to the interface -_I1 .. Ik_ that each signature came from." +"Let $m_{\metavar{all}}$ be the result of applying \NnbdTopMergeName{} to +the elements in $M_{\metavar{all}}$, ordered according to the interface +\List{I}{1}{k} that each signature came from." -Moreover, the occurrence of _mi_ in the next paragraph is -changed to _mall_. +Moreover, the occurrence of $m_i$ in the next paragraph is +changed to $m_{\metavar{all}}$. \subsubsection{Implicit conversions} @@ -24090,7 +24046,7 @@ \subsubsection{Implicit conversions} with call methods to the tear-off of their \CALL{} method is performed when the context type is a function type, or the nullable version of a function type. -Implicit tear-off conversion is *not* performed on objects of nullable type, +Implicit tear-off conversion is not performed on objects of nullable type, regardless of the context type. For example: \begin{dartCode} @@ -24186,22 +24142,23 @@ \subsubsection{Null promotion} The static type of an expression \code{$e$!} is \NonNullTypeOf{$T$} where $T$ is the static type of $e$. -The **NonNull** function defines the null-promoted version of a type, and is +The \NonNullTypeOfName{} function defines the null-promoted version of a type, and is defined as follows. -- \NonNullTypeOf{Null} = \code{Never} -- \NonNullTypeOf{$C$<$T_1$, \ldots, $T_n$>} = \code{$C$<$T_1$, \ldots, $T_n$>}, +\begin{itemize} +\item \NonNullTypeOf{Null} = \code{Never} +\item \NonNullTypeOf{$C$<$T_1$, \ldots, $T_n$>} = \code{$C$<$T_1$, \ldots, $T_n$>}, for class $C$ other than \code{Null} (including \code{Object}). -- \NonNullTypeOf{FutureOr<$T$>} = \code{FutureOr<$T$>} -- \NonNullTypeOf{$T_0$ \FUNCTION(\ldots)} = \code{$T_0$ \FUNCTION(\ldots)} -- \NonNullTypeOf{\FUNCTION} = \FUNCTION{} -- \NonNullTypeOf{Never} = \code{Never} -- \NonNullTypeOf{\DYNAMIC} = \DYNAMIC{} -- \NonNullTypeOf{\VOID} = \VOID{} -- \NonNullTypeOf{$X$} = \code{$X$ \& \NonNullTypeOf{$B$}}, where $B$ is the bound of $X$. -- \NonNullTypeOf{$X$ \& $T$} = \code{$X$ \& \NonNullTypeOf{$T$}} -- \NonNullTypeOf{$T$?} = \NonNullTypeOf{$T$} -- \NonNullTypeOf{$T$*} = \NonNullTypeOf{$T$} +\item \NonNullTypeOf{FutureOr<$T$>} = \code{FutureOr<$T$>} +\item \NonNullTypeOf{$T_0$ \FUNCTION(\ldots)} = \code{$T_0$ \FUNCTION(\ldots)} +\item \NonNullTypeOf{\FUNCTION} = \FUNCTION{} +\item \NonNullTypeOf{Never} = \code{Never} +\item \NonNullTypeOf{\DYNAMIC} = \DYNAMIC{} +\item \NonNullTypeOf{\VOID} = \VOID{} +\item \NonNullTypeOf{$X$} = \code{$X$ \& \NonNullTypeOf{$B$}}, where $B$ is the bound of $X$. +\item \NonNullTypeOf{$X$ \& $T$} = \code{$X$ \& \NonNullTypeOf{$T$}} +\item \NonNullTypeOf{$T$?} = \NonNullTypeOf{$T$} +\end{itemize} \paragraph{Extended Type promotion, Definite Assignment, and Reachability} @@ -24217,77 +24174,81 @@ \subsection{Helper predicates} The \IsTopTypeName{} predicate is true for any type which is in the equivalence class of top types. -- \IsTopType{$T$?} is true iff \IsTopType{$T$} or \IsObjectType{$T$} -- \IsTopType{$T$*} is true iff \IsTopType{$T$} or \IsObjectType{$T$} -- \IsTopType{\DYNAMIC} is true -- \IsTopType{\VOID} is true -- \IsTopType{FutureOr<$T$>} is \IsTopType{$T$} -- \IsTopType{$T$} is false otherwise +\begin{itemize} +\item \IsTopType{$T$?} is true iff \IsTopType{$T$} or \IsObjectType{$T$} +\item \IsTopType{\DYNAMIC} is true +\item \IsTopType{\VOID} is true +\item \IsTopType{FutureOr<$T$>} is \IsTopType{$T$} +\item \IsTopType{$T$} is false otherwise +\end{itemize} \IsTopType{$T$} is true if and only if $T$ is a supertype of \code{Object?}. The \IsObjectTypeName{} predicate is true for any type which is in the equivalence class of \code{Object}. -- \IsObjectType{Object} is true -- \IsObjectType{FutureOr<$T$>} is \IsObjectType{$T$} -- \IsObjectType{$T$} is false otherwise +\begin{itemize} +\item \IsObjectType{Object} is true +\item \IsObjectType{FutureOr<$T$>} is \IsObjectType{$T$} +\item \IsObjectType{$T$} is false otherwise +\end{itemize} \IsObjectType{$T$} is true if and only if $T$ is a subtype and a supertype of \code{Object}. The \IsBottomTypeName{} predicate is true for things in the equivalence class of \code{Never}. -- \IsBottomType{Never} is true -- \IsBottomType{$X$ \& $T$} is true iff \IsBottomType{$T$} -- \IsBottomType{$X$ \EXTENDS\ $T$} is true iff \IsBottomType{$T$} -- \IsBottomType{$T$} is false otherwise +\begin{itemize} +\item \IsBottomType{Never} is true +\item \IsBottomType{$X$ \& $T$} is true iff \IsBottomType{$T$} +\item \IsBottomType{$X$ \EXTENDS\ $T$} is true iff \IsBottomType{$T$} +\item \IsBottomType{$T$} is false otherwise +\end{itemize} \IsBottomType{$T$} is true if and only if $T$ is a subtype of \code{Never}. The \IsNullTypeName{} predicate is true for things in the equivalence class of \code{Null}. -- \IsNullType{Null} is true -- \IsNullType{$T$?} is true iff \IsNullType{$T$} or \IsBottomType{$T$} -- \IsNullType{$T$*} is true iff \IsNullType{$T$} or \IsBottomType{$T$} -- \IsNullType{$T$} is false otherwise +\begin{itemize} +\item \IsNullType{Null} is true +\item \IsNullType{$T$?} is true iff \IsNullType{$T$} or \IsBottomType{$T$} +\item \IsNullType{$T$} is false otherwise +\end{itemize} \IsNullType{$T$} is true if and only if $T$ is a subtype and a supertype of \code{Null}. The \IsMoreTopTypeName{} predicate defines a total order on top and \code{Object} types. -- \IsMoreTopType{\VOID}{$T$} = \code{true} -- \IsMoreTopType{$T$}{\VOID} = \code{false} -- \IsMoreTopType{\DYNAMIC}{$T$} = \code{true} -- \IsMoreTopType{$T$}{\DYNAMIC} = \code{false} -- \IsMoreTopType{Object}{$T$} = \code{true} -- \IsMoreTopType{$T$}{Object} = \code{false} -- \IsMoreTopType{$T$*}{$S$*} = \IsMoreTopType{$T$}{$S$} -- \IsMoreTopType{$T$}{$S$*} = \code{true} -- \IsMoreTopType{$T$*}{$S$} = \code{false} -- \IsMoreTopType{$T$?}{$S$?} = \IsMoreTopType{$T$}{$S$} -- \IsMoreTopType{$T$}{$S$?} = \code{true} -- \IsMoreTopType{$T$?}{$S$} = \code{false} -- \IsMoreTopType{FutureOr<$T$>}{FutureOr<$S$>} = \IsMoreTopType{$T$}{$S$} +\begin{itemize} +\item \IsMoreTopType{\VOID}{$T$} = \code{true} +\item \IsMoreTopType{$T$}{\VOID} = \code{false} +\item \IsMoreTopType{\DYNAMIC}{$T$} = \code{true} +\item \IsMoreTopType{$T$}{\DYNAMIC} = \code{false} +\item \IsMoreTopType{Object}{$T$} = \code{true} +\item \IsMoreTopType{$T$}{Object} = \code{false} +\item \IsMoreTopType{$T$?}{$S$?} = \IsMoreTopType{$T$}{$S$} +\item \IsMoreTopType{$T$}{$S$?} = \code{true} +\item \IsMoreTopType{$T$?}{$S$} = \code{false} +\item \IsMoreTopType{FutureOr<$T$>}{FutureOr<$S$>} = \IsMoreTopType{$T$}{$S$} +\end{itemize} The \IsMoreBottomTypeName{} predicate defines an (almost) total order on bottom and \code{Null} types. This does not currently consistently order two different type variables with the same bound. -- \IsMoreBottomType{Never}{$T$} = \code{true} -- \IsMoreBottomType{$T$}{Never} = \code{false} -- \IsMoreBottomType{Null}{$T$} = \code{true} -- \IsMoreBottomType{$T$}{Null} = \code{false} -- \IsMoreBottomType{$T$?}{$S$?} = \IsMoreBottomType{$T$}{$S$} -- \IsMoreBottomType{$T$}{$S$?} = \code{true} -- \IsMoreBottomType{$T$?}{$S$} = \code{false} -- \IsMoreBottomType{$T$*}{$S$*} = \IsMoreBottomType{$T$}{$S$} -- \IsMoreBottomType{$T$}{$S$*} = \code{true} -- \IsMoreBottomType{$T$*}{$S$} = \code{false} -- \IsMoreBottomType{$X$ \& $T$}{$Y$ \& $S$} = \IsMoreBottomType{$T$}{$S$} -- \IsMoreBottomType{$X$ \& $T$}{$S$} = \code{true} -- \IsMoreBottomType{$S$, $X$ \& $T$} = \code{false} -- \IsMoreBottomType{$X$ \EXTENDS\ $T$}{$Y$ \EXTENDS\ $S$} = \IsMoreBottomType{$T$}{$S$} +\begin{itemize} +\item \IsMoreBottomType{Never}{$T$} = \code{true} +\item \IsMoreBottomType{$T$}{Never} = \code{false} +\item \IsMoreBottomType{Null}{$T$} = \code{true} +\item \IsMoreBottomType{$T$}{Null} = \code{false} +\item \IsMoreBottomType{$T$?}{$S$?} = \IsMoreBottomType{$T$}{$S$} +\item \IsMoreBottomType{$T$}{$S$?} = \code{true} +\item \IsMoreBottomType{$T$?}{$S$} = \code{false} +\item \IsMoreBottomType{$X$ \& $T$}{$Y$ \& $S$} = \IsMoreBottomType{$T$}{$S$} +\item \IsMoreBottomType{$X$ \& $T$}{$S$} = \code{true} +\item \IsMoreBottomType{$S$, $X$ \& $T$} = \code{false} +\item \IsMoreBottomType{$X$ \EXTENDS\ $T$}{$Y$ \EXTENDS\ $S$} = \IsMoreBottomType{$T$}{$S$} +\end{itemize} \subsubsection{The main function} @@ -24315,12 +24276,14 @@ \subsubsection{The main function} If \code{main} can be called with two positional arguments, it is invoked with the following two actual arguments: -- An object whose run-time type implements \code{List}. -- An object specified when the current isolate $i$ was created, +\begin{itemize} +\item An object whose run-time type implements \code{List}. +\item An object specified when the current isolate $i$ was created, for example through the invocation of \code{Isolate.spawnUri} that spawned $i$, or the null object if no such object was supplied. A dynamic error occurs if the run-time type of this object is not a subtype of the declared type of the corresponding parameter of \code{main}. +\end{itemize} If \code{main} cannot be called with two positional arguments, but it can be called with one positional argument, it is invoked with an object whose @@ -24330,98 +24293,49 @@ \subsubsection{The main function} invoked with no arguments. In each of the above three cases, an implementation is free to provide -additional arguments allowed by the signature of \code{main} (*the above rules -ensure that the corresponding parameters are optional*). But the -implementation must ensure that a dynamic error occurs if an actual -argument does not have a run-time type which is a subtype of the declared -type of the parameter. +additional arguments allowed by the signature of \code{main} +(\commentary{the above rules ensure that the corresponding parameters are optional}). +But the implementation must ensure that a dynamic error occurs if an +actual argument does not have a run-time type which is a subtype of +the declared type of the parameter. A Dart program will typically be executed by executing a script. The procedure whereby this script is chosen is implementation specific. \subsection{Runtime semantics} -\subsubsection{Unsound and sound semantics} - -To allow the null safety feature to be rolled out incrementally, we define two -modes of compilation and execution. - -**Unsound null checking** mode largely ignores the nullability of types at -runtime, as defined below. Unmigrated programs or programs consisting of a -mix of migrated and unmigrated code are expected to run without encountering -new nullability related errors at runtime. **This mode is unsound** in the -sense that variables marked as non-nullable may still be null at runtime. - -**Sound null checking** mode respects the nullability of types at runtime in -casts and instance checks, as defined below. Unmigrated programs or programs -consisting of a mix of migrated and unmigrated code may not be compiled or run -with sound null checking, and it is a compile time error if unmigrated -code is attempted to be compiled with sound null checking enabled. - -Unsound vs sound null checking can be controlled at runtime via the -\code{--[no-]sound-null-safety} flag, where the negated version of the flag implies -unsound null checking and the unnegated version implies sound null checking. - -In the absence of an explicit value for the flag, the mode of execution depends -on migrated status of the program entry point. If the entry point of the -program (\code{main}) is in an opted-in library, then the program is compiled and run -as if \code{--sound-null-safety} were specified on the command line. Otherwise, -the program is run as if \code{--no-sound-null-safety} were specified on the -command line. - -Compilers may (and are encouraged to) print a warning indicating that sound null -checking has been disabled when compiling a program that contains migrated -libraries with unsound null checking. - \subsubsection{Runtime type equality operator} Two objects $T_1$ and $T_2$ which are instances of \code{Type} (that is, runtime type objects) are considered equal if and only if the runtime type objects $T_1$ and $T_2$ corresponds to the types $S_1$ and $S_2$ respectively, and the normal forms \NormalizedTypeOf{$S_1$} and \NormalizedTypeOf{$S_2$} are syntactically equal up to equivalence of -bound variables and **ignoring \code{*} modifiers on types**. So for example, the -run-time type objects corresponding to \code{List} and \code{List} are -considered equal. Note that we do not equate primitive top types. \code{List<\VOID>} +bound variables. Note that we do not equate primitive top types. \code{List<\VOID>} and \code{List<\DYNAMIC>} are still considered distinct runtime type objects. Note -that we also do not equate \code{Never} and \code{Null}, and we do not equate function -types which differ in the placement of \REQUIRED{} on parameter types. Because -of this, the equality described here is not equivalent to syntactic equality on -the \LegacyErasureTypeOfName{} of the types. - +that we also do not equate function +types which differ in the placement of \REQUIRED{} on parameter types. \subsubsection{Constant evaluation and canonicalization} -Constant evaluation is modified so that both type literals and legacy and opted-in -instances canonicalize more consistently as defined below. - \paragraph{Type literals} Two constant type literals $T_1$ and $T_2$ compare as identical if they are equal using the definition of runtime type equality specified above. -The effect of this definition is to ensure that constant type literals which -look identical in the source syntax but which may differ by the presence of -legacy type modifiers are canonicalized consistently in the sense that any two -type literals which would compare equal via the definition of runtime type -equality given above will compare as identical. For runtime implementations -which implement identity by choosing a canonical representative for the -equivalence class of equal instances, the choice of what type object to -canonicalize to is arbitrary in that placement of legacy modifiers in type -literals is not otherwise observable in the language. - -Note that the choice of canonicalization for type literals does not depend -directly on whether sound or unsound null checking is in use. +For runtime implementations which implement identity by choosing a +canonical representative for the equivalence class of equal instances, +the choice of what type object to canonicalize to is not +observable in the language. \paragraph{Constant instances} -In both sound and unsound null checking, and in both opted in and opted out -code, comparison of constant instances for identity is defined such that any two +Any two instances which are otherwise identical except for their generic type arguments shall be considered identical if those generic type arguments compare equal using the definition of runtime type object equality defined above. That is, comparison (or canonicalization) of constant instances of generic classes is -performed relative to the normal forms of their generic type arguments, and -ignoring legacy type annotations as described above. Hence, an instance of +performed relative to the normal forms of their generic type arguments. +Hence, an instance of \code{$C$<$T_0$>} compares identical to \code{C<$T_1$>} if $T_0$ and $T_1$ have the same normal form (up to the identity of bound variables), and the objects are otherwise identical. @@ -24430,105 +24344,16 @@ \subsubsection{Constant evaluation and canonicalization} constant objects to allow the identity semantics specified above to be implemented as fast pointer equality checks on the reference to the canonical form. The definition above defines equivalence classes of constant objects for -which we must choose the canonical representative. The choice of this -representative is observable in mixed mode programs, since instances with -different degrees of "legacy-ness" in their type arguments are considered -identical, but may contain operations which perform casts and instance checks -which will evaluate differently depending on whether a legacy type or a -non-legacy type is used in the canonical representative. For example: +which we must choose the canonical representative. -\begin{dartCode} -// null safe code. -\CLASS{} C \{ - \FINAL\ T x; - \VOID\ test(Object? o) \{ - o \AS\ T; - \} - \CONST\ C(Object? o) : x = o \AS\ T; -\} - -// If the canonical instance uses `int`, this is a compile time error -// If the canonical instance uses `int*`, this is not a compile time error -\CONST\ c1 = C(null); - -// If the canonical instance uses `int`, this throws -// If the canonical instance uses `int*`, this does not throw -\VOID\ test1() => c1.test(null); - -// Opted out code - -// If the canonical instance uses `int`, this is a compile time error -// If the canonical instance uses `int*`, this is not a compile time error -\CONST\ c2 = C(null); - -// If the canonical instance uses `int`, this throws -// If the canonical instance uses `int*`, this does not throw -\VOID\ test1() => c2.test(null); -\end{dartCode} - -We therefore define the choice of the canonical instance representing an +We define the choice of the canonical instance representing an equivalence class of constant objects as follows. -With sound null checking, all generic constant constructors and generic constant +All generic constant constructors and generic constant literals are evaluated using the type arguments provided, and canonicalization is performed with respect to the normal form of the type arguments. This -ensures that with sound null checking, the final consistent semantics are -obeyed, since it is not observable which instance is chosen as the canonical -representative in sound mode. - -With unsound null checking, all generic constant object expressions are -additionally treated as if all type arguments passed to them were legacy types -regardless of whether the constructed class was defined in a legacy library or -not, and regardless of whether the constructor invocation or literal occured in -a legacy library or not. Specifically, a constant object expression with -generic type parameters $T_i$ is treated as if the parameters were -\ConstCanonicalTypeOf{$T_i$} as defined below. This ensures that constant -objects which appear identical in the syntax continue to canonicalize -consistently across legacy and opted-in libraries. - -The Dart static analysis tool does not distinguish between sound and unsound -checking mode, and hence it is expected that there will be some small level of -infidelity in the constant evaluation semantics in the analyzer. Identity -semantics for constant objects can be faithfully modeled in the analyzer using -the existing strategy of implementing identity directly, rather than via -choosing a canonical representative for each equivalence class. However, the -lack of a canonical representative is observable at compile time in rare cases, -such as the example shown above. We propose that the analyzer should choose to -evaluate those constants in opted in libraries using sound mode semantics, and -to evaluate those in opted out libraries using unsound mode semantics. Hence in -the example above, the definition of \code{c1} would be a compile time error, but the -definition of \code{c2} would not. - -The \ConstCanonicalTypeOfName{} erasure operation on types used above is -defined as follows. - -- \ConstCanonicalTypeOf{$T$} = $T$, if $T$ is \DYNAMIC, \VOID, or \code{Null} -- \ConstCanonicalTypeOf{$T$} = \code{$T$*} if $T$ is \code{Never} or \code{Object} -- \ConstCanonicalTypeOf{FutureOr<$T$>} = \code{FutureOr<$S$>*} - - where $S$ is \ConstCanonicalTypeOf{$T$} -- \ConstCanonicalTypeOf{$T$?} = - - let $S$ be \ConstCanonicalTypeOf{$T$} - - if $S$ is \code{$R$*} then \code{$R$?} - - else \code{$S$?} -- \ConstCanonicalTypeOf{$T$*} = \ConstCanonicalTypeOf{$T$} -- \ConstCanonicalTypeOf{$X$ \EXTENDS\ $T$} = \code{$X$*} -- \ConstCanonicalTypeOf{$X$ \& $T$} = - - This case should not occur, since intersection types are not permitted as - generic arguments. -- \ConstCanonicalTypeOf{$C$<$T_0$, \ldots, $T_n$>} = \code{$C$<$R_0$, \ldots, $R_n$>*} - - where $R_i$ is \ConstCanonicalTypeOf{$T_i$} - - Note this includes the case of an interface type with no generic parameters - (e.g \code{int}. -- \ConstCanonicalTypeOf{$R$ \FUNCTION<$X$ \EXTENDS\ $B$>($S$)} = \code{$F$*} - - where $F$ = \code{$R_1$ \FUNCTION<$X$ \EXTENDS\ $B_1$>($S_1$)} - - and $R_1$ = \ConstCanonicalTypeOf{$R$} - - and $B_1$ = \ConstCanonicalTypeOf{$B$} - - and $S_1$ = \ConstCanonicalTypeOf{$S$} - - Note, this generalizes to arbitrary number of type and term parameters. - -Note that if $T$ is a normal form type, then \ConstCanonicalTypeOf{$T$} is -also a normal form type. - +ensures a consistent semantics, since it is not observable which instance is +chosen as the canonical representative. \subsubsection{Null check operator} @@ -24610,73 +24435,79 @@ \subsubsection{Null aware operator} the translated expression, and simply passes on the expression to its continuation. -- A property access \code{$e$?.$f$} translates to: - - \code{SHORT[EXP($e$), fn[x] => x.$f$]} -- If $e$ translates to $F$ then \code{$e$.$f$} translates to: - - \code{PASSTHRU[$F$, fn[x] => x.$f$]} -- A null aware method call \code{$e$?.$m$(\metavar{args})} translates to: - - \code{SHORT[EXP($e$), fn[x] => x.$m$(ARGS(\metavar{args}))]} -- If $e$ translates to $F$ then \code{$e$.$m$(\metavar{args})} translates to: - - \code{PASSTHRU[$F$, fn[x] => x.$m$(ARGS(\metavar{args}))]} -- If $e$ translates to $F$ then \code{$e$(\metavar{args})} translates to: - - \code{PASSTHRU[$F$, fn[x] => x(ARGS(\metavar{args}))]} -- If $e_1$ translates to $F$ then \code{$e_1$?[$e_2$]} translates to: - - \code{SHORT[EXP($e_1$), fn[x] => x[EXP($e_2$)]]} -- If $e_1$ translates to $F$ then \code{$e_1$[$e_2$]} translates to: - - \code{PASSTHRU[$F$, fn[x] => x[EXP($e_2$)]]} -- If $e$ translates to $F$ then \code{$e$!} translates to: - - \code{PASSTHRU[$F$, fn[x] => x!]} -- The assignment \code{$e_1$?.$f$ = $e_2$} translates to: - - \code{SHORT[EXP($e_1$), fn[x] => x.f = EXP($e_2$)]} -- The other assignment operators are handled equivalently. -- If $e_1$ translates to $F$ then \code{$e_1$.$f$ = $e_2$} translates to: - - \code{PASSTHRU[$F$, fn[x] => x.$f$ = EXP($e_2$)]} -- The other assignment operators are handled equivalently. -- If $e_1$ translates to $F$ then \code{$e_1$?[$e_2$] = $e_3$} translates to: - - \code{SHORT[EXP($e_1$), fn[x] => x[EXP($e_2$)] = EXP($e_3$)]} -- The other assignment operators are handled equivalently. -- If $e_1$ translates to $F$ then \code{$e_1$[$e_2$] = $e_3$} translates to: - - \code{PASSTHRU[$F$, fn[x] => x[EXP($e_2$)] = EXP($e_3$)]} -- The other assignment operators are handled equivalently. -- A cascade expression \code{$e$..$s$} translates as follows, where $F$ is the - translation of $e$ and \code{x} and \code{y} are fresh object level variables: - \begin{dartCode} - fn[k : Exp -> Exp] : Exp => - $F$[fn[r : Exp] : Exp => let x = r in - let y = EXP(x.$s$) - in k[x] - ] - \end{dartCode} -- A null-shorting cascade expression \code{$e$?..$s$} translates as follows, where \code{x} - and \code{y} are fresh object level variables. - \begin{dartCode} - fn[k : Exp -> Exp] : Exp => - let x = EXP($e$) in x == null ? null : let y = EXP(x.$s$) in k(x) - \end{dartCode} -- All other expressions are translated compositionally using the \code{TERM} +\begin{itemize} +\item A property access \code{$e$?.$f$} translates to: + \code{SHORT[EXP($e$), fn[x] => x.$f$]} +\item If $e$ translates to $F$ then \code{$e$.$f$} translates to: + \code{PASSTHRU[$F$, fn[x] => x.$f$]} +\item A null aware method call \code{$e$?.$m$(\metavar{args})} translates to: + \code{SHORT[EXP($e$), fn[x] => x.$m$(ARGS(\metavar{args}))]} +\item If $e$ translates to $F$ then \code{$e$.$m$(\metavar{args})} translates to: + \code{PASSTHRU[$F$, fn[x] => x.$m$(ARGS(\metavar{args}))]} +\item If $e$ translates to $F$ then \code{$e$(\metavar{args})} translates to: + \code{PASSTHRU[$F$, fn[x] => x(ARGS(\metavar{args}))]} +\item If $e_1$ translates to $F$ then \code{$e_1$?[$e_2$]} translates to: + \code{SHORT[EXP($e_1$), fn[x] => x[EXP($e_2$)]]} +\item If $e_1$ translates to $F$ then \code{$e_1$[$e_2$]} translates to: + \code{PASSTHRU[$F$, fn[x] => x[EXP($e_2$)]]} +\item If $e$ translates to $F$ then \code{$e$!} translates to: + \code{PASSTHRU[$F$, fn[x] => x!]} +\item The assignment \code{$e_1$?.$f$ = $e_2$} translates to: + \code{SHORT[EXP($e_1$), fn[x] => x.f = EXP($e_2$)]} +\item The other assignment operators are handled equivalently. +\item If $e_1$ translates to $F$ then \code{$e_1$.$f$ = $e_2$} translates to: + \code{PASSTHRU[$F$, fn[x] => x.$f$ = EXP($e_2$)]} +\item The other assignment operators are handled equivalently. +\item If $e_1$ translates to $F$ then \code{$e_1$?[$e_2$] = $e_3$} translates to: + \code{SHORT[EXP($e_1$), fn[x] => x[EXP($e_2$)] = EXP($e_3$)]} +\item The other assignment operators are handled equivalently. +\item If $e_1$ translates to $F$ then \code{$e_1$[$e_2$] = $e_3$} translates to: + \code{PASSTHRU[$F$, fn[x] => x[EXP($e_2$)] = EXP($e_3$)]} +\item The other assignment operators are handled equivalently. +\item A cascade expression \code{$e$..$s$} translates as follows, where $F$ is the + translation of $e$ and \code{x} and \code{y} are fresh object level variables: + \begin{dartCode} + fn[k : Exp -> Exp] : Exp => + $F$[fn[r : Exp] : Exp => let x = r in + let y = EXP(x.$s$) + in k[x] + ] + \end{dartCode} +\item A null-shorting cascade expression \code{$e$?..$s$} translates as follows, where \code{x} + and \code{y} are fresh object level variables. + \begin{dartCode} + fn[k : Exp -> Exp] : Exp => + let x = EXP($e$) in x == null ? null : let y = EXP(x.$s$) in k(x) + \end{dartCode} +\item All other expressions are translated compositionally using the \code{TERM} combinator. Examples: - - An identifier \code{x} translates to \code{TERM[x]} - - A list literal \code{[$e_1$, \ldots, $e_n$]} translates to \code{TERM[ [EXP($e_1$), \ldots, EXP($e_n$)] ]} - - A parenthesized expression \code{($e$)} translates to \code{TERM[(EXP($e$))]} + An identifier \code{x} translates to \code{TERM[x]} + A list literal \code{[$e_1$, \ldots, $e_n$]} translates to \code{TERM[ [EXP($e_1$), \ldots, EXP($e_n$)] ]} + A parenthesized expression \code{($e$)} translates to \code{TERM[(EXP($e$))]}. +\end{itemize} The language specification specifies that an invocation of any of several operators is considered equivalent to a member access (this applies to relational expressions, bitwise expressions, shift expressions, additive expressions, multiplicative expressions, and unary expressions). -*For example, \code{$a$ + $b$} is specified as equivalent to \code{$a$.plus($b$)}, +\commentary{% +For example, \code{$a$ + $b$} is specified as equivalent to \code{$a$.plus($b$)}, where \code{plus} is assumed to be a method with the same behavior as \code{+}. -Similarly, \code{-$e$} is equivalent to \code{$e$.unaryMinus()}.* +Similarly, \code{-$e$} is equivalent to \code{$e$.unaryMinus()}.% +} This equivalence is not applicable in the above rules, so operators not mentioned specifically in a rule are handled in the case for 'other' expressions, not in the case for \code{$e$.m(\metavar{args})}. -*This means that the null-shorting transformation stops at operators. For +\commentary{% +This means that the null-shorting transformation stops at operators. For instance, \code{$e$?.$f$ + $b$} is a compile-time error because \code{$e$?.$f$} can be null, it is not an expression where both \code{.$f$} and \code{+ $b$} will be skipped if \code{$e$} is null. Similarly, both \code{-$a$?.$f$} and \code{~$a$?.$f$} are errors, and do not null-short like -\code{$a$?.$f$.op()}.* +\code{$a$?.$f$.op()}.% +} \subsubsection{Late fields and variables} @@ -24699,24 +24530,27 @@ \subsubsection{Late fields and variables} written to causes the initializer expression of the variable to be evaluated to a value, assigned to the variable or field, and returned as the value of the read. - - If there is no initializer expression, the read causes a runtime error to be + +\begin{itemize} +\item If there is no initializer expression, the read causes a runtime error to be thrown. - - Evaluating the initializer expression may validly cause a write to the field +\item Evaluating the initializer expression may validly cause a write to the field or variable, assuming that the field or variable is not final. In this case, the variable assumes the written value. The final value of the initializer expression overwrites any intermediate written values. - - Evaluating the initializer expression may cause an exception to be thrown. +\item Evaluating the initializer expression may cause an exception to be thrown. If the variable was written to before the exception was thrown, the value of the variable on subsequent reads is the last written value. If the variable was not written before the exception was thrown, then the next read attempts to evaluate the initializer expression again. - - If a variable or field is read from during the process of evaluating its own +\item If a variable or field is read from during the process of evaluating its own initializer expression, and no write to the variable has occurred, the read is treated as a first read and the initializer expression is evaluated again. +\end{itemize} Let $D$ be a \LATE{} and \FINAL{} non-local variable declaration named \code{v} -without an initializing expression. +without an initializing expression. It is a run-time error, to invoke the setter \code{v=} which is implicitly induced by $D$ if a value has previously been assigned to \code{v} (which could be due to an initializing formal or a constructor initializer @@ -24740,10 +24574,13 @@ \subsubsection{Late fields and variables} A toplevel or static variable with an initializer is evaluated as if it was marked \LATE. Note that this is a change from pre-NNBD semantics in that: - - Throwing an exception during initializer evaluation no longer sets the - variable to \code{null} - - Reading the variable during initializer evaluation is no longer checked for, - and does not cause an error. + +\begin{itemize} +\item Throwing an exception during initializer evaluation no longer sets the + variable to \code{null} +\item Reading the variable during initializer evaluation is no longer checked for, + and does not cause an error. +\end{itemize} \subsubsection{Boolean conditional evaluation} @@ -24751,514 +24588,102 @@ \subsubsection{Boolean conditional evaluation} (e.g. the a conditional statement, conditional element, \code{while} loop, etc) be assignable to \code{bool} is unchanged from pre null-safe Dart. The change in assignability means that the static type of the condition may only be \DYNAMIC, -\code{Never}, or \code{bool}. In full null-safe Dart, an expression of type \code{Never} will +\code{Never}, or \code{bool}. An expression of type \code{Never} will always diverge and an expression of type \code{bool} will never evaluate to a value other than \code{true} or \code{false}, and hence no conversion is required in these cases. A conditional expression of type \DYNAMIC{} may evaluated to any value, and hence must be implicitly downcast to \code{bool}, after which no further check is required. -During unsound null checking execution, values of type \code{Never} and \code{bool} may -evaluate to \code{null}, and so a boolean conversion check must be performed in -addition to any implicit downcasts implied. The full semantics then are given -as follows. - Given a boolean conditional expression $e$ where $e$ has type $S$, it is a -static error if $S$ is not assignable to \code{bool}. Otherwise: +static error if $S$ is not assignable to \code{bool}. +Otherwise evaluation proceeds as follows: -With sound null checking, evaluation proceeds as follows: - - First $e$ is implicitly cast to \code{bool} if required. - - This cast may fail, and if so it is a dynamic error. - - If the cast does not fail, then the result is known to be a non-null +\begin{itemize} +\item First $e$ is implicitly cast to \code{bool} if required. + This cast may fail, and if so it is a dynamic error. +\item If the cast does not fail, then the result is known to be a non-null boolean, and evaluation of the enclosing conditional proceeds as usual. - -With unsound null checking, evaluation proceeds as follows: - - First $e$ is implicitly cast to \code{bool} if required (using - \code{LEGACY_SUBTYPE($e$.runtimeType, bool)}) - - This cast may fail, and if so it is a dynamic error. - - If the cast does not fail, then the result may still be \code{null}, and so the - result must be checked against \code{null}. - - If the \code{null} check fails, it is an \code{AssertionError}, otherwise evaluation - of the enclosing conditional proceeds as usual. - - -\subsection{Core library changes} - -Certain core libraries APIs will have a change in specified behavior only when -interacting with opted in code. These changes are as follows. - -Calling the \code{.length} setter on a \code{List} with element type $E$ with an argument -greater than the current length of the list is a runtime error unless -\code{Null <: $E$}. - -The \code{Iterator.current} getter is given an non-nullable return type, and is -changed such that the behavior if it is called before calling -\code{Iterator.moveNext} or after \code{Iterator.moveNext} has returned \code{false} is -unspecified and implementation defined. In most core library implementations, -the implemented behavior will to return \code{null} if the element type is -\code{nullable}, and otherwise to throw an error. - -\subsubsection{Legacy breaking changes} - -We will make a small set of minimally breaking changes to the core library APIs -that apply to legacy code as well. These changes are as follows. - -The \code{String.fromEnvironment} and \code{int.fromEnvironment} contructors have default -values for their optional parameters. +\end{itemize} \subsection{Migration features} -For migration, we support incremental adoption of non-nullability as described -at a high level in -https://github.com/dart-lang/language/blob/master/accepted/2.12/nnbd/roadmap.md. - -\subsubsection{Opted in libraries} - -Libraries and packages must opt into the feature as described elsewhere. An -opted-in library may depend on un-opted-in libraries, and vice versa. - -\subsubsection{Errors as warnings} - -An earlier version of this proposal specified that null safety related static -errors in opted-in code should be demoted to warnings when running with -unsound null checking. This behavior has been eliminated based on early -feedback. Null safety related errors in opted-in code behave as usual -independently of the compilation mode, subject to differences in const -evaluation and the usual suppression of errors when interacting with legacy -(opted-out) code (see below). - -\subsubsection{Legacy libraries} - -Static checking for a library which has not opted into this feature (a *legacy* -library) is done using the semantics as of the last version of the language -before this feature ships (or the last version to which it has opted in, if that -is different). All opted-in libraries upstream from the legacy library are -viewed by the legacy library with nullability related features erased from their -APIs. In particular: - - All types of the form \code{$T$?} in the opted-in API are treated as $T$. - - All required named parameters are treated as optional named parameters. - - The type \code{Never} is treated as the type \code{Null}. - -In a legacy library, none of the new syntax introduced by this proposal is -available, and it is a static error if it is used. - -\subsubsection{Importing legacy libraries from opted-in libraries} - -The type system is extended with a notion of a legacy type operator. For every -type $T$, there is an additional type \code{$T$*} which is the legacy version of the -type. There is no surface syntax for legacy types, and implementations should -display the legacy type \code{$T$*} in the same way that they would display the type -$T$, except in so far as it is useful to communicate to programmers for the -purposes of error messages that the type originates in legacy code. - -When static checking is done in an opted-in library, types which are imported -from legacy libraries are seen as legacy types. However, type inference in -the opted-in library "erases" legacy types. That is, if a missing type -parameter, local variable type, or closure type is inferred to be a type $T$, -all occurrences of \code{$S$*} in $T$ shall be replaced with $S$. As a result, legacy -types will never appear as type annotations in opted-in libraries, nor will they -appear in reified positions. - -\subsubsection{Typedefs defined in legacy libraries used in opted-in libraries} - -A typedef which is define in a legacy library and used in an opted-in library is -treated as defining a function type, all of the components of which are -legacy. The function type itself is treated as non-nullable (and not legacy) at -the top level. Hence given the following program, it is an error to assign a -nullable value to a variable of type $F$ in an opted-in library, but any -function which is compatible with a legacy function of type -\code{int* Function(int*)} may be assigned to such a variable. - -\begin{dartCode} -// Opted-out library "opted\_out.dart". -\TYPEDEF\ F = int \FUNCTION(int); - -// Opted-in library "main.dart". -\IMPORT\ "opted\_out.dart"; - -int? f1(int x) => x; - -\VOID\ test() \{ - F f = null; // Static error - f = f1; // No error -\} -\end{dartCode} - -\subsubsection{Exports} - -If a legacy library re-exports an opted-in library, the re-exported symbols -retain their opted-in status (that is, downstream migrated libraries will see -their nnbd-aware types). - -It is an error for an opted-in library to re-export symbols which are defined in -a legacy library (note that a symbol which is defined in an opted-in library and -then exported from a legacy library is accepted for re-export from a third -opted-in library since the symbol is not **defined** in the legacy library which -first exports it). - -\subsubsection{Super-interface and member type computation with legacy types} - -A class defined in a legacy library may have in its set of super-interfaces both -legacy and opted-in interfaces, and hence may have members which are derived -from either, or both. Similarly, a class defined in an opted-in library may -have in its set of super-interfaces both legacy and opted-in interfaces, and -hence may have members which are derived from either, or both. We define the -super-interface and member signature computation for such classes as follows. - -\paragraph{Classes defined in legacy libraries} - -The legacy erasure of a type $T$ denoted \LegacyErasureTypeOf{$T$} is $T$ with all -occurrences of \code{?} removed, \code{Never} replaced with \code{Null}, \REQUIRED{} removed -from all parameters, and all types marked as legacy types. - -A direct super-interface of a class defined in a legacy library (that is, an -interface which is listed in the \EXTENDS, \code{implements} or \code{with} clauses of -the class) has all generic arguments (and all sub-components of the generic -arguments) marked as legacy types. - -If a class \code{C} in a legacy library implements the same generic class \code{I} more -than once, it is an error if the \LegacyErasureTypeOf{} all such super-interfaces -are not all syntactically equal. - -When \code{C} implements \code{I} once, and also when \code{C} implements \code{I} more than once -without error, \code{C} is considered to implement the canonical signature given by -\LegacyErasureTypeOf{} of the super-interfaces in question. This determines the -outcome of dynamic instance checks applied to instances of \code{C}, as well as -static subtype checks on expressions of type \code{C}. - -A member which is defined in a class in a legacy library (whether concrete or -abstract), is given a signature in which every type is a legacy type. It is an -error if the signature of a member is not a correct override of all members of -the same name in the direct super-interfaces of the class, using the legacy -subtyping rules. - -Using the legacy erasure for checking super-interfaces accounts for opted-out -classes which depend on both opted-in and opted-out versions of the same generic -interface. For example: - -\begin{dartCode} -//opted in -\CLASS\ I \{\} - -// opted in -\CLASS\ A \IMPLEMENTS\ I \{\} - -// opted out -\CLASS\ B \IMPLEMENTS\ I \{\} - -// opted out -\CLASS\ C \EXTENDS\ A \IMPLEMENTS\ B \{\} -\end{dartCode} - -The class \code{C} is not considered erroneous, despite implementing both \code{I} -and \code{I}, since legacy erasure makes both of those interfaces equal. The -interface which \code{C} is considered to implement is \code{I}. - - -\paragraph{Classes defined in legacy libraries as seen from opted-in libraries} - -Members inherited in a class in an opted-in library, which are inherited via a -class or mixin defined in a legacy library are viewed with their erased legacy -signature, even if they were original defined in an opted-in library. Note that -if a class which is defined in a legacy library inherits a member with the same -name from multiple super-interfaces, then error checking is done as usual using -the legacy typing rules which ignore nullability. This means that it is valid -for a legacy class to inherit the same member signature with contradictory -nullability information. For the purposes of member lookup within a legacy -library, nullability information is ignored, and so it is valid to simply erase -the nullability information within the legacy library. When referenced from an -opted-in library, the same erasure is performed, and the member is seen at its -legacy type. - -We use legacy subtyping when checking inherited member signature coherence in -classes because opted out libraries may bring together otherwise incompatible -member signatures without causing an error. - -\begin{dartCode} -// opted\_in.dart -\CLASS\ A \{ - int? foo(int? x) \{\} -\} -\CLASS\ B \{ - int foo(int x) \{\} -\} -\end{dartCode} - -\begin{dartCode} -// opted\_out.dart -// @dart = 2.6 -import 'opted\_in.dart'; - -\CLASS\ C \EXTENDS\ A \IMPLEMENTS\ B \{\} -\end{dartCode} - -The class \code{C} is accepted, since the versions of \code{foo} inherited from \code{A} and -\code{B} are compatible. - -If the class \code{C} is now used within an opted-in library, we must decide what -signature to ascribe to \code{foo}. The \LegacyErasureTypeOf{} function computes a legacy -signature for \code{foo} which drops the nullability information producing a single -signature, in this case \code{int* Function(int*)}. Consequently, the following code -is accepted: - -\begin{dartCode} -//opted in -\IMPORT\ 'opted\_out.dart'; -\VOID\ test() \{ - \NEW\ C().foo(null).isEven; -\} -\end{dartCode} - -\paragraph{Classes defined in opted-in libraries} - The \NnbdTopMergeName{} of two types $T$ and $S$ is the unique type $R$ defined as: - - \NnbdTopMerge{Object?}{Object?} = \code{Object?} - - \NnbdTopMerge{\DYNAMIC}{\DYNAMIC} = \code{\DYNAMIC} - - \NnbdTopMerge{\VOID}{\VOID} = \code{\VOID} - - \NnbdTopMerge{Object?}{\VOID} = \code{Object?} - - And the reverse - - \NnbdTopMerge{\DYNAMIC}{\VOID} = \code{Object?} - - And the reverse - - \NnbdTopMerge{Object?}{\DYNAMIC} = \code{Object?} - - And the reverse - - \NnbdTopMerge{Object*}{\VOID} = \code{Object?} + +\begin{itemize} +\item \NnbdTopMerge{Object?}{Object?} = \code{Object?} +\item \NnbdTopMerge{\DYNAMIC}{\DYNAMIC} = \code{\DYNAMIC} +\item \NnbdTopMerge{\VOID}{\VOID} = \code{\VOID} +\item \NnbdTopMerge{Object?}{\VOID} = \code{Object?} - And the reverse - - \NnbdTopMerge{Object*}{\DYNAMIC} = \code{Object?} +\item \NnbdTopMerge{\DYNAMIC}{\VOID} = \code{Object?} - And the reverse - - \NnbdTopMerge{Never*}{Null} = \code{Null} +\item \NnbdTopMerge{Object?}{\DYNAMIC} = \code{Object?} - And the reverse - - \NnbdTopMerge{$T$?}{$S$?} = \code{\NnbdTopMerge{$T$}{$S$}?} - - \NnbdTopMerge{$T$?}{$S$*} = \code{\NnbdTopMerge{$T$}{$S$}?} - - \NnbdTopMerge{$T$*}{$S$?} = \code{\NnbdTopMerge{$T$}{$S$}?} - - \NnbdTopMerge{$T$*}{$S$*} = \code{\NnbdTopMerge{$T$}{$S$}*} - - \NnbdTopMerge{$T$*}{$S$} = \NnbdTopMerge{$T$}{$S$} - - \NnbdTopMerge{$T$}{$S$*} = \NnbdTopMerge{$T$}{$S$} - - - And for all other types, recursively applying the transformation over the - structure of the type - - e.g. \NnbdTopMerge{$C$<$T$>}{$C$<$S$>} = \code{$C$<\NnbdTopMerge{$T$}{$S$}>} - - - When computing the \NnbdTopMergeName{} of two method parameters at least one of - which is marked as covariant, the following algorithm is used to compute the - canonical parameter type. - - Given two corresponding parameters of type $T_1$ and $T_2$ where at least - one of the parameters has a \COVARIANT{} declaration: - - if \code{$T_1$ <: $T_2$} and \code{$T_2$ <: $T_1$} then the result is \NnbdTopMerge{$T_1$}{$T_2$}, +\item \NnbdTopMerge{$T$?}{$S$?} = \code{\NnbdTopMerge{$T$}{$S$}?} +\item And for all other types, recursively applying the transformation over the + structure of the type + - e.g. \NnbdTopMerge{$C$<$T$>}{$C$<$S$>} = \code{$C$<\NnbdTopMerge{$T$}{$S$}>} +\item When computing the \NnbdTopMergeName{} of two method parameters at least one of + which is marked as covariant, the following algorithm is used to compute the + canonical parameter type. + + Given two corresponding parameters of type $T_1$ and $T_2$ where at least + one of the parameters has a \COVARIANT{} declaration: + if \code{$T_1$ <: $T_2$} and \code{$T_2$ <: $T_1$} then the result is \NnbdTopMerge{$T_1$}{$T_2$}, and it is covariant. - - otherwise, if \code{$T_1$ <: $T_2$} then the result is $T_2$ and it is covariant - - otherwise the result is $T_1$ and it is covariant + Otherwise, if \code{$T_1$ <: $T_2$} then the result is $T_2$ and it is covariant. + Otherwise the result is $T_1$ and it is covariant. +\end{itemize} In other words, \NnbdTopMergeName{} takes two types which are structurally equal -except for the placement \code{*} types, and the particular choice of top types, and -finds a single canonical type to represent them by replacing \code{?} with \code{*} or -adding \code{*} as required.. The \NnbdTopMergeName{} of two types is not defined for +except for the particular choice of top types, and +finds a single canonical type to represent them. +The \NnbdTopMergeName{} of two types is not defined for types which are not otherwise structurally equal. The \NnbdTopMergeName{} of more than two types is defined by taking the \NnbdTopMergeName{} of the first two, and then recursively taking the \NnbdTopMergeName{} of the rest. -A direct super-interface of a class defined in an opted-in library (that is, an -interface which is listed in the \EXTENDS, \IMPLEMENTS, or \WITH{} clauses of -the class) has all generic arguments (and all sub-components of the generic -arguments) marked as nullable or non-nullable as written. - -If a class $C$ in an opted-in library implements the same generic class $I$ more +If a class $C$ library implements the same generic class $I$ more than once as \code{$I_0$, \ldots, $I_n$}, and at least one of the $I_i$ is not syntactically equal to the others, then it is an error if \NnbdTopMerge{$S_0$, \ldots}{$S_n$} is not defined where $S_i$ is \NormalizedTypeOf{$I_i$}. Otherwise, $C$ is considered to -implement the canonical interface given by \NnbdTopMerge{$S_0$, \ldots}{$S_n$}. This +implement the canonical interface given by \NnbdTopMerge{$S_0$, \ldots}{$S_n$}. This determines the outcome of dynamic instance checks applied to instances of $C$, as well as static subtype checks on expressions of type $C$. -If a class $C$ in an opted-in library overrides a member, it is an error if its -signature is not a subtype of the types of all overriden members from all -direct super-interfaces (whether legacy or opted-in). This implies that -override checks for a member $m$ may succeed due to a legacy member signature -for $m$ in a direct super-interface, even in the case where an indirect -super-interface has a member signature for $m$ where the override would be a -compile-time error. For example: - -\begin{dartCode} -// opted\_in.dart -\CLASS\ A \{ - int foo(int? x) \{\} -\} -\end{dartCode} - -\begin{dartCode} -// opted\_out.dart -// @dart = 2.6 -import 'opted\_in.dart'; - -\CLASS\ B \EXTENDS\ A \{\} -\end{dartCode} - -\begin{dartCode} -// opted\_in.dart -\CLASS\ C \EXTENDS\ B \{ - // Override checking is done against the legacy signature of B.foo. - int? foo(int x) \{\} -\} -\end{dartCode} - -It is difficult to predict the outcome of migrating \code{B} in such situations, but -lints or hints may be used by tools to communicate to developers that \code{C} may -need to be changed again when \code{B} is migrated. - If a class $C$ in an opted-in library inherits a member $m$ with the same name -from multiple direct super-interfaces (whether legacy or opted-in), let -\code{$T_0$, \ldots, $T_n$} be the signatures of the inherited members. If there is exactly one -$T_i$ such that \code{NNBD_SUBTYPE($T_i$, $T_k$)} for all $k \in 0 .. n$ then the signature +from multiple direct super-interfaces, let +\List{T}{0}{n} be the signatures of the inherited members. If there is exactly one +$T_i$ such that \code{$T_i$ <: $T_k$} for all $k \in 0 .. n$ then the signature of $m$ is considered to be $T_i$. If there is more than one such $T_i$, then it -is an error if the \code{\NnbdTopMergeName} of \code{$S_0$, \ldots, $S_n$} does not exist, where $S_i$ +is an error if the \NnbdTopMergeName{} of \List{S}{0}{n} does not exist, where $S_i$ is \NormalizedTypeOf{$T_i$}. Otherwise, the signature of $m$ for the purposes of member -lookup is the \code{\NnbdTopMergeName} of the $S_i$. +lookup is the \NnbdTopMergeName{} of the $S_i$. -Note that when a member $m$ is inherited from multiple indirect super-interfaces -**via** a single direct super-interface, override checking is only performed -against the signature of the direct super-interface which mediates the -inheritance as described above. Hence the following example is not an error, -since the direct super-interface $C$ of $D$ mediates the conflicting inherited -signatures of \code{foo} as \code{$C$.foo} with signature \code{int* Function(int*)}. +!!!TODO!!! -\begin{dartCode} -// opted\_in.dart -\CLASS\ A \{ - int? foo(int? x) \{\} -\} -\CLASS\ B \{ - int foo(int x) \{\} -\} -\end{dartCode} - -\begin{dartCode} -// opted\_out.dart -// @dart = 2.6 -\IMPORT\ 'opted\_in.dart'; - -\CLASS\ C \EXTENDS\ A \IMPLEMENTS\ B \{\} - -\end{dartCode} - -\begin{dartCode} -//opted in -\IMPORT\ 'opted\_out.dart'; -\CLASS\ D \EXTENDS\ C \{\} -\VOID\ test() \{ - \NEW\ D().foo(null).isEven; -\} -\end{dartCode} +Check that the semantics of \IS{} is as specified here: +if $e$ evaluates to a value $v$ and $v$ has +runtime type $S$, an instance check \code{$e$ \IS\ $T$} is evaluated as follows: +Return \code{$S$ <: $T$}. -\subsubsection{Type reification} +Check that the semantics of \AS{} is as specified here: +if $e$ evaluates to a value $v$ and $v$ has +runtime type $S$, a cast \code{$e$ \AS\ $T$} is evaluated as follows: +if \code{$S$ <: $T$} then \code{$e$ as $T$} evaluates to $v$. +Otherwise a dynamic type error occurs. -All types reified in legacy libraries are reified as legacy types. Runtime -subtyping checks treat them according to the subtyping rules specified -separately. - -\subsubsection{Runtime checks and unsound null checking} - -With unsound null checking, runtime type tests (including explicit and -implicit casts) shall succeed whenever the runtime type test would have -succeeded if all \code{?} on types were ignored, \code{*} was added to each type, and -\REQUIRED{} parameters were treated as optional. This has the effect of treating -\code{Never} as equivalent to \code{Null}, restoring \code{Null} to the bottom of the type -hierarchy, treating \code{Object} as nullable, and ignoring \REQUIRED{} on named -parameters. This is intended to provide the same subtyping results as pre-nnbd -Dart. - -Instance checks (\code{e \IS\ T}) and casts (\code{e \AS\ T}) behave differently when run -with sound vs unsound null checking. - -We define the instance tests with unsound null checking and sound null -checking as follows: - -**With unsound null checking**: if $e$ evaluates to a value $v$ and $v$ has runtime -type $S$, an instance check \code{$e$ \IS\ $T$} occurring in a **legacy library** or an -**opted-in library** is evaluated as follows: - - If $v$ is \code{null} and $T$ is a legacy type, return \code{LEGACY_SUBTYPE($T$, Null) - || LEGACY_SUBTYPE(Object, $T$)} - - If $v$ is \code{null} and $T$ is not a legacy type, return \code{NNBD_SUBTYPE(Null, $T$)} - - Otherwise return \code{LEGACY_SUBTYPE($S$, $T$)} - -A type is a legacy type if it is of the form \code{$R$*} for some $R$ after normalizing -away nested nullability annotations - e.g. \code{int*} is a legacy type, but \code{int?*} -is not, since the normal form of the latter is \code{int?}. - -Note that except in the case that $T$ is of the form $X$ or \code{$X$*} for some type -variable $X$, it is statically decidable which of the first two clauses apply in -the case that $v$ is \code{null}. - -**With sound null checking**: if $e$ evaluates to a value $v$ and $v$ has -runtime type $S$, an instance check \code{$e$ \IS\ $T$} occurring in an **opted-in -library** is evaluated as follows: - - Return \code{NNBD_SUBTYPE($S$, $T$)} - -Note that it is an error to run a program containing legacy libraries with -sound null checking. - -Note that given the definitions above, the result of an instance check may vary -depending on whether it is run with sound or unsound null checking. However, -in the specific case that the value being checked is \code{null}, instance checks -will always return the same result regardless of mode, and regardless of -whether the check occurs in an opted in or opted out library. - -\begin{verbatim} -| T | Any mode | -| -------- | ------------- | -| Never | false | -| Never* | true | -| Never? | true | -| Null | true | -| int | false | -| int* | false | -| int? | true | -| Object | false | -| Object* | true | -| Object? | true | -| dynamic | true | -\end{verbatim} +Other documents that need to be integrated, too: -We define casts with unsound null checking and sound null checking as follows: - -**With unsound null checking**: if $e$ evaluates to a value $v$ and $v$ has -runtime type $S$, a cast \code{$e$ \AS\ $T$} **whether textually occurring in a legacy or -opted-in library** is evaluated as follows: - - if \code{LEGACY_SUBTYPE($S$, $T$)} then \code{$e$ \AS\ $T$} evaluates to $v$. Otherwise a - dynamic type error occurs. - -**With sound null checking**: if $e$ evaluates to a value $v$ and $v$ has -runtime type $S$, a cast \code{$e$ \AS\ $T$} **whether textually occurring in a legacy or -opted-in library** is evaluated as follows: - - if \code{NNBD_SUBTYPE($S$, $T$)} then \code{$e$ as $T$} evaluates to $v$. Otherwise a - dynamic type error occurs. - -With unsound null checking, we ensure that opted-in libraries do not break -downstream clients by continuing to evaluate instance checks and casts with -the same semantics as in pre-nnbd Dart. All runtime subtype checks are done -using the legacy subtyping, and instance checks maintain the pre-nnbd behavior -on \code{null} instances. With sound null checking, we use the specified nnbd -subtyping for all instance checks and casts. - -When developers enable sound null checking in their tests and applications, new -runtime cast failures may arise. The process of migrating libraries and -applications will require users to track down these changes in behavior. -Development platforms are encouraged to provide facilities to help users -understand these changes: for example, by providing a debugging option in which -instance checks or casts which would result in a different outcome if run with -sound null checking vs unsound null checking are flagged for the developer by -logging a warning or breaking to the debugger. - -\subsubsection{Automatic debug assertion insertion} - -When running with unsound null checking, implementations may insert code -equivalent to \code{\ASSERT(x != null)} in the prelude of every method or function -defined in an opted-in library for each parameter \code{x} which has a non-nullable -type. When compiling a program with sound null checking, these assertions will -never fire and should be elided, but during the migration when mixed mode code -is being executed it is possible for opted-out libraries to cause the -invariants of the null safety checking to be violated. +https://github.com/dart-lang/language/blob/master/resources/type-system/subtyping.md +https://github.com/dart-lang/language/blob/master/resources/type-system/upper-lower-bounds.md +https://github.com/dart-lang/language/blob/master/resources/type-system/normalization.md +https://github.com/dart-lang/language/blob/master/resources/type-system/flow-analysis.md +https://github.com/dart-lang/language/blob/master/resources/type-system/inference.md %% !!!END TODO!!! \section*{Appendix: Algorithmic Subtyping} From 9ce0da15770fb1f0d010d1c79a8faf8d341e6139 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Wed, 13 Oct 2021 15:27:59 +0200 Subject: [PATCH 04/85] Rebasing --- specification/dart.sty | 8 +- specification/dartLangSpec.tex | 201 ++++++++++++++++++--------------- 2 files changed, 111 insertions(+), 98 deletions(-) diff --git a/specification/dart.sty b/specification/dart.sty index 6c856874b..804d51ce8 100644 --- a/specification/dart.sty +++ b/specification/dart.sty @@ -124,11 +124,11 @@ \newenvironment{commentary}[1]{{\color{commentaryColor}\sf{#1}}}{} % Auxiliary functions. -\newcommand{\flattenName}{\mbox{\it flatten}} +\newcommand{\flattenName}{\metavar{flatten}} \newcommand{\flatten}[1]{\ensuremath{\flattenName({#1})}} -\newcommand{\futureOrBase}[1]{\ensuremath{\mbox{\it futureOrBase}({#1})}} -\newcommand{\overrides}[1]{\ensuremath{\mbox{\it overrides}({#1})}} -\newcommand{\inherited}[1]{\ensuremath{\mbox{\it inherited}({#1})}} +\newcommand{\futureOrBase}[1]{\ensuremath{\metavar{futureOrBase}({#1})}} +\newcommand{\overrides}[1]{\ensuremath{\metavar{overrides}({#1})}} +\newcommand{\inherited}[1]{\ensuremath{\metavar{inherited}({#1})}} % Used as a mini-section marker, indicating visibly that a range of % text (usually just a couple of paragraphs) are concerned with one diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index fc9d3b178..8d2f0176d 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -2391,11 +2391,13 @@ \subsubsection{Required Formals} an instance method, instance setter, or instance operator. -\subsubsection{Optional Formals} +\subsubsection{Optional and Named Formals} \LMLabel{optionalFormals} \LMHash{}% -Optional parameters may be specified and provided with default values. +Optional formal parameters may be specified and provided with default values. +Named formal parameters can be optional, +but if the modifier \REQUIRED{} is present then it is required. \begin{grammar} ::= (`=' )? @@ -2416,7 +2418,10 @@ \subsubsection{Optional Formals} It is a compile-time error if the default value of an optional parameter is not a constant expression (\ref{constants}). If no default is explicitly specified for an optional parameter +whose declared type is nullable, an implicit default of \NULL{} is provided. +Otherwise, when the declared type is not nullable, +it is a compile-time error if an optional parameter has no default value. \LMHash{}% It is a compile-time error if the name of a named optional parameter @@ -10740,7 +10745,45 @@ \subsubsection{Set and Map Literal Disambiguation} \LMLabel{setAndMapLiteralDisambiguation} \LMHash{}% -Some terms like \code{\{\}} and \code{\{\,...\id\,\}} are ambiguous: +Some terms are syntactically ambiguous because they contain a \lit{?} +which may be considered part of a nullable type +or part of a \syntax{conditionalExpression}. +In all such cases, +the ambiguity is resolved in favor of the set literal +and not the map literal. + +\commentary{% +In other words, conditional expressions are given preference. +For example, \code{\{ a as bool ? - 3 : 3 \}} and +\code{\{ a is int ? - 3 : 3 \}} are syntactically ambiguous +in this manner. +The former can be parsed as a set literal or as a map literal, +depending on whether the \lit{?} is considered to make \code{bool} nullable, +or it is considered as part of a conditional expression. +The other expression has a similar ambiguity. +They are both resolved to be set literals +containing a conditional expression.% +} + +\LMHash{}% +Similarly, null aware subscripts in conditional expressions +can be syntactically ambiguous. +Whenever there is a sequence of tokens +which may be parsed either as a conditional expression +or as two expressions separated by a colon, +the first of which is a null aware subscript, +parsers shall choose to parse as a conditional expression. + +\commentary{% +For example, \code{\{ a?[b]:c \}} can be parsed either as a set +literal or a map literal, depending on whether the \lit{?} is +interpreted as part of a null aware subscript or as part of a +conditional expression. +It is resolved as the latter.% +} + +\LMHash{}% +Some terms like \code{\{\}} and \code{\{\,...\id\,\}} are still ambiguous: they may be either a set literal or a map literal. This ambiguity is eliminated in two steps. The first step uses only the syntax and context type, @@ -17133,8 +17176,7 @@ \subsection{Assignable Expressions} from the end of $e$. It is easy to see that only some \synt{assignableExpression}s have a receiver term. -For instance, a plain \synt{identifier} does not. -% +For instance, a plain \synt{identifier} does not.% } \LMHash{}% @@ -19973,7 +20015,7 @@ \subsection{Imports} \ref{exports}). \LMHash{}% -The dart core library \code{dart:core} +The Dart core library \code{dart:core} is implicitly imported into every dart library other than itself via an import clause of the form \code{\IMPORT{} 'dart:core';} @@ -22254,6 +22296,63 @@ \subsubsection{Additional Subtyping Concepts} } +\subsection{Type \code{Never}} +\LMLabel{typeNever} + +\LMHash{}% +The Dart core library \code{dart:core} exports a type named \code{Never}. +No object has dynamic type \code{Never}. + +\commentary{% +In other words, \code{Never} is the empty type. +\code{Never} is a subtype of every other type in Dart +(\ref{subtypeRules}).% +} + +\rationale{% +\code{Never} is a useful type for several reasons: +It can be used to indicate that +the evaluation of an expression will never complete normally, +e.g., because it calls a function that always throws. +This allows for a more precise control flow analysis. + +Also, the object \code{\CONST\,\,[]} can be used +in any situation where a \code{List<$T$>} is required, +for any $T$ whatsoever, +and the value of a variable of type \code{Object?\,\,\FUNCTION(Never)} +can be any function that accepts a single, positional argument.% +} + + +\subsection{Type \code{Null}} +\LMLabel{typeNull} + +\LMHash{}% +The Dart core library \code{dart:core} exports a type named \code{Null}, +There is exactly one object whose dynamic type is \code{Null}, +and that is the null object. + +\commentary{% +In other words, \code{Null} is a singleton type. +\code{Null} is a subtype of all other types +other than \code{Never} and any type which is +a subtype of \code{Never} +(\ref{subtypeRules}).% +} + +\rationale{% +\code{Null} is mainly used implicitly in +a type of the form \code{$T$?} for some $T$. +If $e$ is an expression of type \code{$T$?} that evaluates to an object $o$, +then $o$ can be an instance of a subtype of $T$, +or it could be the null object. +The latter situation is commonly interpreted to mean that +the computation did not yield a result. +In this sense, \code{Null} can be considered to model +the property of being optional.% +} + + \subsection{Function Types} \LMLabel{functionTypes} @@ -23324,94 +23423,8 @@ \subsection{Operator Precedence} \section{Null safety} %% !!!TODO!!! -\newcommand{\FlattenName}{\metavar{flatten}} -\newcommand{\Flatten}[1]{\ensuremath{\FlattenName(\code{#1})}} - -\newcommand{\NnbdTopMergeName}{\metavar{topMerge}} -\newcommand{\NnbdTopMerge}[2]{\ensuremath{\NnbdTopMergeName(\code{{#1},\,\,{#2}})}} - -\newcommand{\NonNullTypeOfName}{\metavar{nonNullTypeOf}} -\newcommand{\NonNullTypeOf}[1]{\ensuremath{\NonNullTypeOfName(\code{#1})}} - -\newcommand{\IsTopTypeName}{\metavar{isTopType}} -\newcommand{\IsTopType}[1]{\ensuremath{\IsTopTypeName(\code{#1})}} - -\newcommand{\IsObjectTypeName}{\metavar{isObjectType}} -\newcommand{\IsObjectType}[1]{\ensuremath{\IsObjectTypeName(\code{#1})}} - -\newcommand{\IsBottomTypeName}{\metavar{isBottomType}} -\newcommand{\IsBottomType}[1]{\ensuremath{\IsBottomTypeName(\code{#1})}} - -\newcommand{\IsNullTypeName}{\metavar{isNullType}} -\newcommand{\IsNullType}[1]{\ensuremath{\IsNullTypeName(\code{#1})}} - -\newcommand{\IsMoreTopTypeName}{\metavar{isMoreTopType}} -\newcommand{\IsMoreTopType}[2]{\ensuremath{\IsMoreTopTypeName(\code{{#1},\,\,{#2}})}} - -\newcommand{\IsMoreBottomTypeName}{\metavar{isMoreBottomType}} -\newcommand{\IsMoreBottomType}[2]{\ensuremath{\IsMoreBottomTypeName(\code{{#1},\,\,{#2}})}} - -\newcommand{\NormalizedTypeOfName}{\metavar{normalizedTypeOf}} -\newcommand{\NormalizedTypeOf}[1]{\ensuremath{\NormalizedTypeOfName(\code{#1})}} - -\newcommand{\FutureValueTypeOfName}{\metavar{futureValueTypeOf}} -\newcommand{\FutureValueTypeOf}[1]{\ensuremath{\FutureValueTypeOfName(\code{#1})}} - - -\subsection{Syntax} - -The precise changes to the syntax are given in an accompanying set of -modifications to the grammar in the formal specification. This section -summarizes in prose the grammar changes associated with this feature. - -The grammar of types is extended to allow any type to be suffixed with a \code{?} -(e.g. \code{int?}) indicating the nullable version of that type. - -A new primitive type \code{Never}. This type is denoted by the built-in type -declaration \code{Never} declared in \code{dart:core}. - -The grammar of expressions is extended to allow any expression to be suffixed -with a \code{!}. - -The modifier \LATE{} is added as a built-in identifier. The grammar of top level -variables, static fields, instance fields, and local variables is extended to -allow any declaration to include the modifer \LATE. - -The modifier \REQUIRED{} is added as a built-in identifier. The grammar of -function types is extended to allow any named parameter declaration to be -prefixed by the \REQUIRED{} modifier -(e.g. \code{int \FUNCTION(int, {int? y, \REQUIRED\ int z})}). - -The grammar of selectors is extended to allow null-aware subscripting using the -syntax \code{$e_1$?[$e_2$]} which evaluates to \code{null} if \code{$e_1$} evaluates to \code{null} and -otherwise evaluates as \code{$e_1$[$e_2$]}. - -The grammar of cascade sequences is extended to allow the first cascade of a -sequence to be written as \code{?..} indicating that the cascade is null-shorting. - -\subsubsection{Grammatical ambiguities and clarifications} - -\paragraph{Conditional expression ambiguities} - -Conditional expressions inside of braces are ambiguous between sets and maps. -That is, \code{\{ a as bool ? - 3 : 3 \}} can be parsed as a set literal \code{\{ (a as bool) -? - 3 : 3 \}} or as a map literal \code{\{ (a as bool ?) - 3 : 3 \}}. Parsers will -prefer the former parse over the latter. - -The same is true for \code{\{ a is int ? - 3 : 3 \}}. - -The same is true for \code{\{ int ? - 3 : 3 \}} if we allow this. - -\paragraph{Null aware subscript} - -Certain uses of null aware subscripts in conditional expressions are ambiguous. -For example, \code{\{ a?[b]:c \}} can be parsed either as a set literal or a map -literal, depending on whether the \code{?} is interpreted as part of a null aware -subscript or as part of a conditional expression. Whenever there is a sequence -of tokens which may be parsed either as a conditional expression or as two -expressions separated by a colon, the first of which is a null aware -subscript, parsers shall choose to parse as a conditional expression. - +%% !!!At the end: Search Null, change to Never where appropriate +%% !!!Search all `TODO`.*null \subsection{Static semantics} From 3cd77745161c9f7d02c28b983b8a8f91282c1985 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Thu, 14 Oct 2021 09:26:33 +0200 Subject: [PATCH 05/85] Reorganized material on null/Null --- specification/dartLangSpec.tex | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 8d2f0176d..97d2c04b1 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -22301,7 +22301,7 @@ \subsection{Type \code{Never}} \LMHash{}% The Dart core library \code{dart:core} exports a type named \code{Never}. -No object has dynamic type \code{Never}. +No object has a dynamic type which is \code{Never} or a subtype thereof. \commentary{% In other words, \code{Never} is the empty type. @@ -22328,24 +22328,40 @@ \subsection{Type \code{Null}} \LMLabel{typeNull} \LMHash{}% -The Dart core library \code{dart:core} exports a type named \code{Null}, +The Dart core library \code{dart:core} exports a type named \code{Null}. There is exactly one object whose dynamic type is \code{Null}, -and that is the null object. +and that is the null object +(\ref{null}). \commentary{% In other words, \code{Null} is a singleton type. -\code{Null} is a subtype of all other types -other than \code{Never} and any type which is -a subtype of \code{Never} +Apart from top types +(\ref{superBoundedTypes}), +\code{Null} is a subtype of all types of the form \code{$T$?}, +and of all types $S$ such that \futureOrBase{S} is one of the above. +The only non-trivial subtypes of \code{Null} are +\code{Never} and subtypes of \code{Never} (\ref{subtypeRules}).% } +\LMHash{}% +Attempting to instantiate \code{Null} causes a compile-time error. +It is a compile-time error for a class to extend, mix in or implement +\code{Null}. +The \code{Null} class declares exactly the same members +with the same signatures as the class \code{Object}. + +\commentary{% +The \code{Null} class has a primitive \lit{==} operator +(\ref{theOperatorEqualsEquals}).% +} + \rationale{% \code{Null} is mainly used implicitly in a type of the form \code{$T$?} for some $T$. If $e$ is an expression of type \code{$T$?} that evaluates to an object $o$, then $o$ can be an instance of a subtype of $T$, -or it could be the null object. +or it can be the null object. The latter situation is commonly interpreted to mean that the computation did not yield a result. In this sense, \code{Null} can be considered to model From 14855513a465a2e8c344c57c240f705c520629b6 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Mon, 18 Oct 2021 10:44:14 +0200 Subject: [PATCH 06/85] Rebase --- specification/dartLangSpec.tex | 189 ++++++++++++++++++++++++++++++--- 1 file changed, 173 insertions(+), 16 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 97d2c04b1..23b03db22 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -9174,6 +9174,10 @@ \subsection{Null} The static type of \NULL{} is the \code{Null} type. +\LMHash{}% +The dynamic type of the null object is \code{Null}. + + \subsection{Numbers} \LMLabel{numbers} @@ -22296,11 +22300,171 @@ \subsubsection{Additional Subtyping Concepts} } +\subsection{Type Normalization} +\LMLabel{typeNormalization} + +\LMHash{}% +Some Dart types are mutual subtypes, +but are not considered to be the same type. + +\commentary{% +For instance, \DYNAMIC{} and \code{Object?} are subtypes of each other, +but they are not considered to be the same type. +This is useful because it allows us to treat two expressions very differently +even though the set of objects that they could evaluate to is exactly the same. +In particular, a member access on a receiver of type \code{Object?} +can only call \code{hashCode} and a few other members, +but a member access on a receiver of type \DYNAMIC{} can invoke +any member as a method accepting any actual list of arguments, +and as a getter or as a setter, +and it will be checked at run time that said invocation is possible.% +} + +\LMHash{}% +This section defines a normalization procedure on types +which yields a canonical representation for any given type. + +\commentary{% +In other words, subtyping on Dart types is a preorder, +but subtyping on normalized Dart types is a total order.% +} + +\LMHash{}% +An \IndexCustom{atomic type}{type!atomic} +is a term derived from \synt{typeName} +that denotes a type. + +\commentary{% +For instance, \DYNAMIC{} is an atomic type, +and so is \code{prefix.MyClass}, +if it denotes a class.% +} + +\newlength{\FunCr} +\setlength{\FunCr}{1.5ex} + +\begin{figure}[t] + \begin{minipage}[c]{\textwidth} + \begin{displaymath} + \begin{array}{ll} + \mbox{\bf Unaliased argument} & \mbox{\bf Result}\\[\FunCr] + \hline + \mbox{If $T$ is atomic: }T & T\\[\FunCr] + % + \code{FutureOr<$T$>} & + \left\{ + \begin{array}{l} + S\mbox{, if $S$ is a top type or \code{Object}}\\ + \code{Future}\mbox{, if $S$ is \code{Never}}\\ + \code{Future?}\mbox{, if $S$ is \code{Null}}\\ + \code{FutureOr}\mbox{, otherwise} + \end{array} + \right.\\[1ex] + \mbox{where $S$ is \NormalizedTypeOf{$T$}}\\[\FunCr] + % + \code{$T$?} & + \left\{ + \begin{array}{l} + S\mbox{, if $S$ is a top type}\\ + \code{Null}\mbox{, if $S$ is \code{Never} or \code{Null}}\\ + S\mbox{, if $S$ is \code{FutureOr<$R$>} where $R$ is nullable}\\ + \code{$R$?}\mbox{, if $S$ is \code{$R$?} for some $R$}\\ + \code{$S$?}\mbox{, otherwise} + \end{array} + \right.\\ + \mbox{where $S$ is \NormalizedTypeOf{$T$}}\\[\FunCr] + % + \code{$X$\,\,\EXTENDS\,\,$T$} & + \left\{ + \begin{array}{l} + \code{Never}\mbox{,if $T$ is \code{Never}}\\ + \code{Never}\mbox{, + if $T$ is a type variable $Y$, + and \NormalizedTypeOf{$Y$} is \code{Never}}\\ + \code{$X$\,\,\EXTENDS\,\,$T$}\mbox{, otherwise} + \end{array} + \right.\\ + \mbox{}\\[\FunCr] + % + \code{$X$\,\,\&\,\,$T$} & + \left\{ + \begin{array}{l} + \code{Never}\mbox{, if $S$ is \code{Never}}\\ + X\mbox{, if $S$` is $X$ or a top type}\\ + X\mbox{, if \SubtypeStd{\NormalizedTypeOf{$B$}}{S}, where $B$ is the bound of $X$}\\ + \code{$X$\,\,\&\,\,$S$}\mbox{, otherwise} + \end{array} + \right.\\ + \mbox{where $S$ is \NormalizedTypeOf{$T$}}\\[\FunCr] + % + \code{$C$<\List{T}{1}{k}>} & \code{$C$<\List{R}{1}{k}>}\\ + \mbox{where $R_i$ is \NormalizedTypeOf{$T_i$},}\\ + \mbox{\quad{}for $i \in 1 .. k$}\\[\FunCr] + % + % !!!TODO: Generalize this to handle all parameter list shapes. + \code{$R$ \FUNCTION<$X$\,\,\EXTENDS,\,$B$>($S$)} & + \code{$R_1$ Function<$X$\,\,\EXTENDS\,\,$B_1$>($S_1$)}\\ + \mbox{where $R_1$ is \NormalizedTypeOf{$R$},}\\ + \mbox{\quad{}$B_1$ is \NormalizedTypeOf{$B$},}\\ + \mbox{\quad{}and $S_1$ is \NormalizedTypeOf{$S$}} + \end{array} + \end{displaymath} + \end{minipage} + \caption{Definition of the type function \NormalizedTypeOfName: + For a given type $T_0$, + let $T$ be the transitive alias expansion of $T_0$ + (\ref{typedef}), + then look up $T$ in the left column of the table in this figure + such that the associated condition is satisfied. + Then \NormalizedTypeOf{$T$} is as shown in the right hand column.} +\end{figure} + +---------------------------------------------------------------------- + + +!!!TODO!!! +Note that there is currently no place in the type system where normalization can +apply to intersection types (promoted types). The rule is included here for +completeness. + +We assume that type aliases are fully expanded, and that prefixed types are +resolved to a canonical name. + +The **NORM** relation defines the canonical representative of classes of +equivalent types. In the absence of legacy (*) types, it should be the case +that for any two types which are mutual subtypes, their normal forms are +syntactically identical up to identification of top types (\DYNAMIC, \VOID, +\code{Object?}). + +This is based on the following equations: +\begin{itemize} +\item \code{T?? == T?} +\item \code{Null? == Null} +\item \code{Never? == Null} +\item \code{\DYNAMIC? == \DYNAMIC} +\item \code{\VOID? == \VOID} +\item \code{FutureOr<$T$> == $T$}, if \SubtypeStd{\code{Future<$T$>}}{T} +\item \code{FutureOr<$T$> == Future<$T$>}, if \SubtypeStd{T}{\code{Future<$T$>}} +\item \code{$X$\,\,\EXTENDS\,\,Never == Never} +\item \code{$X$\,\,\&\,\,$T$ == $T$}, if \SubtypeStd{T}{X} +\item \code{$X$\,\,\&\,\,$T$ == $X$}, if \SubtypeStd{X}{T} +\end{itemize} + +---------------------------------------------------------------------- +\LMHash{}% +This defines a procedure \NormalizedTypeOfName{} +such that \NormalizedTypeOf{$T$} is syntactically +equal to \NormalizedTypeOf{$S$} modulo replacement of primitive top types iff +\SubtypeStd{S}{T} and \SubtypeStd{T}{S}. + + + + \subsection{Type \code{Never}} \LMLabel{typeNever} \LMHash{}% -The Dart core library \code{dart:core} exports a type named \code{Never}. +The system library \code{dart:core} exports a type named \code{Never}. No object has a dynamic type which is \code{Never} or a subtype thereof. \commentary{% @@ -22316,11 +22480,12 @@ \subsection{Type \code{Never}} e.g., because it calls a function that always throws. This allows for a more precise control flow analysis. -Also, the object \code{\CONST\,\,[]} can be used +Also, \code{Never} can be used to express some extreme types: +For instance, the object \code{\CONST\,\,[]} can be used in any situation where a \code{List<$T$>} is required, -for any $T$ whatsoever, -and the value of a variable of type \code{Object?\,\,\FUNCTION(Never)} -can be any function that accepts a single, positional argument.% +for any $T$ whatsoever. +Similarly, a variable of type \code{Object?\,\,\FUNCTION(Never)} +can be used to hold any function that accepts a single, positional argument.% } @@ -22328,7 +22493,7 @@ \subsection{Type \code{Null}} \LMLabel{typeNull} \LMHash{}% -The Dart core library \code{dart:core} exports a type named \code{Null}. +The system library \code{dart:core} exports a type named \code{Null}. There is exactly one object whose dynamic type is \code{Null}, and that is the null object (\ref{null}). @@ -22338,7 +22503,8 @@ \subsection{Type \code{Null}} Apart from top types (\ref{superBoundedTypes}), \code{Null} is a subtype of all types of the form \code{$T$?}, -and of all types $S$ such that \futureOrBase{S} is one of the above. +and of all types $S$ such that \futureOrBase{S} is +a top type or a type of the form \code{$T$?}. The only non-trivial subtypes of \code{Null} are \code{Never} and subtypes of \code{Never} (\ref{subtypeRules}).% @@ -23443,17 +23609,8 @@ \section{Null safety} %% !!!TODO!!! %% !!!Search all `TODO`.*null \subsection{Static semantics} - \subsubsection{Type normalization} -We define a normalization procedure on types which defines a canonical -representation for otherwise equivalent -types here -(\ref{sec:typeNormalization}). -This defines a procedure \NormalizedTypeOfName{} such that \NormalizedTypeOf{$T$} is syntactically -equal to \NormalizedTypeOf{$S$} modulo replacement of primitive top types iff \code{$S$ <: $T$} -and \code{$T$ <: $S$}. - \subsubsection{Future flattening} The \FlattenName{} function is modified as follows: From 86c5b2a85cf7fd5050be2caa68be1fa26185c268 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Wed, 27 Oct 2021 23:33:01 +0200 Subject: [PATCH 07/85] Rebasing --- specification/dartLangSpec.tex | 2100 +++++++++++++++++--------------- 1 file changed, 1145 insertions(+), 955 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 23b03db22..f261b7b2e 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -11,6 +11,7 @@ \usepackage{lmodern} \usepackage[T1]{fontenc} \usepackage{makeidx} +\usepackage{enumitem} \makeindex \title{Dart Programming Language Specification\\ {6th edition draft}\\ @@ -609,6 +610,14 @@ \section{Notation} or an immediate subterm of $t$, or a subterm of an immediate subterm of $t$. +\LMHash{}% +\BlindDefineSymbol{\stackrel{\triangle}{=}}% +We use a `$\triangle$' over an equals sign to indicate that +a mathematical function or value is being defined. +\commentary{% +For example, \DefEquals{\metavar{successor}(k)}{k + 1}.% +} + \LMHash{}% A list \DefineSymbol{x_1, \ldots, x_n} denotes any list of $n$ elements of the form $x_i, 1 \le i \le n$. @@ -1432,6 +1441,12 @@ \section{Variables} That is, any kind of variable which is not a local variable.% } +\LMHash{}% +A compile-time error occurs if a static variable has a +non-nullable type and no initializing expression, +unless the variable declaration has +the modifier \LATE{} or \EXTERNAL. + \LMHash{}% A \IndexCustom{constant variable}{variable!constant} is a variable whose declaration includes the modifier \CONST. @@ -1940,7 +1955,7 @@ \section{Functions} Functions abstract over executable actions. \begin{grammar} - ::= \gnewline{} + ::= \gnewline{} ? ::= ? @@ -1983,7 +1998,8 @@ \section{Functions} } \LMHash{}% -Each declaration that introduces a function has a signature that specifies +Each declaration that introduces a function has +a function header that specifies its return type, name, and formal parameter part, except that the return type may be omitted, and getters never have a formal parameter part. @@ -2043,7 +2059,7 @@ \section{Functions} \item The function is synchronous, $T$ is not \VOID, and it would have been a compile-time error to declare the function with the body - \code{\{ \RETURN{} $e$; \}} + \code{\{ \RETURN\,\,$e$; \}} rather than \code{=> $e$}. \commentary{% In particular, $e$ can have \emph{any} type when the return type is \VOID.% @@ -2052,16 +2068,16 @@ \section{Functions} This enables concise declarations of \VOID{} functions. It is reasonably easy to understand such a function, because the return type is textually near to the returned expression $e$. - In contrast, \code{\RETURN{} $e$;} in a block body is only allowed + In contrast, \code{\RETURN\,\,$e$;} in a block body is only allowed for an $e$ with one of a few specific static types, because it is less likely that the developer understands that the returned object will not be used (\ref{return}).% } - \item The function is asynchronous, \flatten{T} is not \VOID, + \item The function is asynchronous, \Flatten{$T$} is not \VOID, and it would have been a compile-time error to declare the function with the body - \code{\ASYNC{} \{ \RETURN{} $e$; \}} + \code{\ASYNC{} \{ \RETURN\,\,$e$; \}} rather than \code{\ASYNC{} => $e$}. \commentary{% In particular, $e$ can have \emph{any} type @@ -2172,8 +2188,8 @@ \subsection{Function Declarations} \LMHash{}% A function declaration consists of an identifier indicating the function's name, possibly prefaced by a return type. -The function name is followed by a signature and body. -For getters, the signature is empty. +The function name is followed by a function header and body. +For getters, the function header is empty. The body is empty for functions that are external. \LMHash{}% @@ -2207,11 +2223,40 @@ \subsection{Formal Parameters} \Index{formal parameter list}, which consists of a list of required positional parameters (\ref{requiredFormals}), -followed by any optional parameters (\ref{optionalFormals}). -The optional parameters may be specified either as -a set of named parameters or as a list of positional parameters, +followed by any optional and/or named parameters (\ref{optionalFormals}). +The latter may be specified +either as a list of optional positional parameters, +or as a set of named parameters, but not both. +\begin{grammar} + ::= `(' `)' + \alt `(' `,'? `)' + \alt `(' `,' `)' + \alt `(' `)' + + ::= \gnewline{} + (`,' )* + + ::= + \alt + + ::= \gnewline{} + `[' (`,' )* `,'? `]' + + ::= \gnewline{} + `{' (`,' )* `,'? `}' +\end{grammar} + +\LMHash{}% +Formal parameter lists allow an optional trailing comma +after the last parameter (\syntax{`,'?}). +A parameter list with such a trailing comma is +equivalent in all ways to the same parameter list without the trailing comma. +All parameter lists in this specification are shown without a trailing comma, +but the rules and semantics apply equally to +the corresponding parameter list with a trailing comma. + \LMHash{}% Some function declarations include a \Index{formal type parameter list} (\ref{functions}), @@ -2221,7 +2266,9 @@ \subsection{Formal Parameters} is a function which is not generic. \LMHash{}% -The \Index{formal parameter part} of a function declaration consists of +The \Index{formal parameter part} +(\ref{functions}) +of a function declaration consists of the formal type parameter list, if any, and the formal parameter list. \commentary{% @@ -2239,10 +2286,11 @@ \subsection{Formal Parameters} \LMHash{}% If it exists, the type parameter scope of a function $f$ is -the current scope for the signature of $f$, +the current scope for the function header of $f$ +(\ref{functions}), and for the formal type parameter list itself; otherwise the scope where $f$ is declared is -the current scope for the signature of $f$. +the current scope for the function header of $f$. \commentary{% This means that formal type parameters are in scope @@ -2259,7 +2307,7 @@ \subsection{Formal Parameters} } \LMHash{}% -The formal parameter list of a function declaration introduces +The \Index{formal parameter list} of a function declaration introduces a new scope known as the function's \IndexCustom{formal parameter scope}{scope!formal parameter}. The formal parameter scope of a non-generic function $f$ is enclosed in @@ -2268,14 +2316,14 @@ \subsection{Formal Parameters} the type parameter scope of $f$. Every formal parameter introduces a local variable into the formal parameter scope. -The current scope for the function's signature is +The current scope for the function's header is the scope that encloses the formal parameter scope. \commentary{% This means that in a generic function declaration, the return type and parameter type annotations can use the formal type parameters, -but the formal parameters are not in scope in the signature.% +but the formal parameters are not in scope in the function header.% } \LMHash{}% @@ -2288,52 +2336,40 @@ \subsection{Formal Parameters} \LMHash{}% It is a compile-time error if a formal parameter is declared as a constant variable (\ref{variables}). - -\begin{grammar} - ::= `(' `)' - \alt `(' `,'? `)' - \alt `(' `,' `)' - \alt `(' `)' - - ::= \gnewline{} - (`,' )* - - ::= - \alt - - ::= \gnewline{} - `[' (`,' )* `,'? `]' - - ::= \gnewline{} - `{' (`,' )* `,'? `}' -\end{grammar} - -\LMHash{}% -Formal parameter lists allow an optional trailing comma -after the last parameter (\syntax{`,'?}). -A parameter list with such a trailing comma is -equivalent in all ways to the same parameter list without the trailing comma. -All parameter lists in this specification are shown without a trailing comma, -but the rules and semantics apply equally to -the corresponding parameter list with a trailing comma. +% We put the following error here because it's shared among all kinds of +% functions except one. +It is a compile-time error if an optional formal parameter +(\commentary{named or positional}) +with no default value has a potentially non-nullable type +(\ref{typeNullability}), +except if it occurs in the parameter list of an abstract method declaration. \subsubsection{Required Formals} \LMLabel{requiredFormals} \LMHash{}% -A \Index{required formal parameter} may be specified in one of three ways: +This section describes some required formal parameters, +and it also introduces non-terminals +which are used to derive other formals as well. +A \Index{required positional formal parameter} is derived from +\synt{normalFormalParameter}, +and it may be specified in one of three ways: + \begin{itemize} \item - By means of a function signature that names the parameter and - describes its type as a function type (\ref{functionTypes}). - It is a compile-time error if any default values are specified - in the signature of such a function type. + Via a declaration which is similar to an ordinary variable declaration + (\ref{variables}), + derived from \synt{simpleFormalParameter}. \item - As an initializing formal, which is only valid as a parameter to - a generative constructor (\ref{generativeConstructors}). + As an initializing formal, derived from \synt{fieldFormalParameter}, + which is only valid as a parameter to a generative constructor + (\ref{generativeConstructors}). \item - Via an ordinary variable declaration (\ref{variables}). + By means of a declaration which is similar to a function type, + derived from \synt{functionFormalParameter}. + It is a compile-time error if any default values are specified + in the signature of such a function type. \end{itemize} \begin{grammar} @@ -2357,8 +2393,8 @@ \subsubsection{Required Formals} \end{grammar} \LMHash{}% -It is a compile-time error if a formal parameter has the modifier \CONST{} -or the modifier \LATE. +It is a compile-time error if a formal parameter has +the modifier \CONST{} or the modifier \LATE. It is a compile-time error if \VAR{} occurs as the first token of a \synt{fieldFormalParameter}. @@ -2396,7 +2432,7 @@ \subsubsection{Optional and Named Formals} \LMHash{}% Optional formal parameters may be specified and provided with default values. -Named formal parameters can be optional, +A named formal parameter can be optional, but if the modifier \REQUIRED{} is present then it is required. \begin{grammar} @@ -2407,6 +2443,7 @@ \subsubsection{Optional and Named Formals} \gnewline{} ((`=' | `:') )? \end{grammar} +\LMHash{}% The form \syntax{ `:' } is equivalent to the form \syntax{ `=' }. @@ -2415,13 +2452,17 @@ \subsubsection{Optional and Named Formals} a later version of the language specification. \LMHash{}% +It is a compile-time error if an optional or named formal parameter has +the modifier \CONST{} or the modifier \LATE. It is a compile-time error if the default value of an optional parameter is not a constant expression (\ref{constants}). -If no default is explicitly specified for an optional parameter -whose declared type is nullable, -an implicit default of \NULL{} is provided. -Otherwise, when the declared type is not nullable, +If no default is specified for an optional parameter +whose declared type is nullable +(\ref{typeNullability}), +a default value of \NULL{} is implicitly induced. +Otherwise, when the declared type is potentially non-nullable, it is a compile-time error if an optional parameter has no default value. +It is a compile-time error if a required named parameter has a default value. \LMHash{}% It is a compile-time error if the name of a named optional parameter @@ -2462,7 +2503,7 @@ \subsubsection{Covariant Parameters} \commentary{% As specified below, a parameter can also be covariant for other reasons. The overall effect of having a covariant parameter $p$ -in the signature of a given method $m$ +in the member signature of a given method $m$ is to allow the type of $p$ to be overridden covariantly, which means that the type required at run time for a given actual argument may be a proper subtype of the type which is known at compile time @@ -2617,7 +2658,8 @@ \subsection{Type of a Function} where $T_0$ is the return type, $X_j$ are the formal type parameters with bounds $B_j$, $j \in 1 .. s$, $T_j$ are the formal parameter types for $j \in 1 .. n + k$, -and $x_{n+j}$ are the names of named parameters for $j \in 1 .. k$. +$x_{n+j}$ are the names of named parameters for $j \in 1 .. k$, +and $r_{n+j}$ is \REQUIRED{} or empty for $j \in 1 .. k$. Non-generic function types are covered by the case $s = 0$, where the type parameter declaration list \code{<\ldots{}>} @@ -2689,8 +2731,9 @@ \subsection{Type of a Function} type parameters \TypeParametersStd, required formal parameter types \List{T}{1}{n}, return type $T_0$, -and named parameters \PairList{T}{x}{n+1}{n+k}, -where $x_{n+j}$, $j \in 1 .. k$ may or may not have a default value. +and named parameters \TripleList{r}{T}{x}{n+1}{n+k}, +where $r_{n+j}$ is \REQUIRED{} or empty for $j \in 1 .. k$, +and each of the named parameters may or may not have a default value. Then the static type of $F$ is \FunctionTypeNamedStd{T_0}. @@ -2752,7 +2795,7 @@ \subsection{External Functions} (\ref{generativeConstructors}, \ref{factories}). External functions are introduced via the built-in identifier \EXTERNAL{} (\ref{identifierReference}) -followed by the function signature. +followed by the function header. \rationale{% External functions allow us to introduce type information for code @@ -2809,22 +2852,22 @@ \section{Classes} ::= (`,' )* ::= `;' - \alt - - ::= ? - \alt - \alt \STATIC? - \alt \STATIC? - \alt \STATIC? - \alt - - ::= \EXTERNAL{} - \alt \EXTERNAL{} - \alt \EXTERNAL{} - \alt (\EXTERNAL{} \STATIC?)? - \alt (\EXTERNAL{} \STATIC?)? - \alt (\EXTERNAL{} \STATIC?)? - \alt \EXTERNAL? + \alt + + ::= ? + \alt + \alt \STATIC? + \alt \STATIC? + \alt \STATIC? + \alt + + ::= \EXTERNAL{} + \alt \EXTERNAL{} + \alt \EXTERNAL{} + \alt (\EXTERNAL{} \STATIC?)? + \alt (\EXTERNAL{} \STATIC?)? + \alt (\EXTERNAL{} \STATIC?)? + \alt \EXTERNAL? \alt \STATIC{} \CONST{} ? \alt \STATIC{} \FINAL{} ? \alt \STATIC{} \LATE{} \FINAL{} ? @@ -2833,9 +2876,9 @@ \section{Classes} \alt \COVARIANT{} \LATE? \alt \LATE? \FINAL{} ? \alt \LATE? - \alt - \alt ( | )? - \alt ( | )? + \alt + \alt ( | )? + \alt ( | )? ::= \gnewline{} (`,' )* @@ -3182,7 +3225,7 @@ \subsubsection{Operators} and operator \lit{[]=} which is an instance setter. \begin{grammar} - ::= \gnewline{} + ::= \gnewline{} ? \OPERATOR{} ::= `~' @@ -3704,7 +3747,7 @@ \subsection{Getters} to retrieve the values of object properties. \begin{grammar} - ::= ? \GET{} + ::= ? \GET{} \end{grammar} \LMHash{}% @@ -3743,7 +3786,7 @@ \subsection{Setters} the values of object properties. \begin{grammar} - ::= ? \SET{} + ::= ? \SET{} \end{grammar} \commentary{% @@ -3756,7 +3799,7 @@ \subsection{Setters} a static setter. Otherwise, it defines an instance setter. The name of a setter is obtained by appending the string `=' to -the identifier given in its signature. +the identifier given in its function header. \commentary{% Hence, a setter name can never conflict with, override or be overridden by @@ -3831,7 +3874,7 @@ \subsection{Abstract Instance Members} One way to think about this is that an abstract member declaration in a subclass does not override or shadow an inherited member implementation. -It only serves to specify the signature of the given member that +It only serves to specify the member signature of the given member that every concrete subtype must have an implementation of; that is, it contributes to the interface of the class, not to the class itself.% @@ -3904,6 +3947,21 @@ \subsection{Instance Variables} An instance getter for it can always be defined manually if desired.% } +% Note that the following rule is somewhat artificial: If $C$ does not have +% a generative constructor (it would then have a factory constructor) then +% we cannot create an instance and we cannot create a subclass, so there is +% no soundness issue. It's just "messy" that it _seems_ to be possible to +% have an instance with an uninitialized $v$. We don't even accept the default +% value `null` in the case where $v$ is final, again because it's unlikely +% to be intended (and that's of course artificial in the same sense). +\LMHash{}% +Assume that the member declaration $D$ in a class $C$ +does not have any of the modifiers \LATE, \ABSTRACT, or \EXTERNAL, +and that it declares an instance variable named $v$ +that does not have an initializing expression. +A compile-time error occurs if $C$ has no generative constructors, +and $v$ is final or the type of $v$ is potentially non-nullable. + \LMHash{}% It is possible for the declaration of an instance variable to include the modifier \COVARIANT{} @@ -3996,13 +4054,13 @@ \subsubsection{Generative Constructors} and either a redirect clause or an initializer list and an optional body. \begin{grammar} - ::= + ::= ::= (`.' )? \end{grammar} \commentary{% -See \synt{declaration} and \synt{methodSignature} for grammar rules +See \synt{declaration} and \synt{methodHeader} for grammar rules introducing a redirection or an initializer list and a body.% } @@ -4350,14 +4408,20 @@ \subsubsection{Generative Constructors} an initialization expression. \LMHash{}% -Let \DefineSymbol{f} be a final instance variable declared in -the immediately enclosing class or enum. -A compile-time error occurs unless $f$ is initialized -by one of the following means: +\BlindDefineSymbol{D, C, v}% +Assume that the member declaration $D$ in a class $C$ +does not have any of the modifiers \LATE, \ABSTRACT, or \EXTERNAL, +and that it declares an instance variable named $v$ +that does not have an initializing expression, +where $v$ is not initialized via an initializing formal of $k$, +and $v$ is not initialized via an initializer list entry of $k$. +In this situation, +a compile-time error occurs if any of the following criteria holds: + \begin{itemize} -\item $f$ is declared by an initializing variable declaration. -\item $f$ is initialized by means of an initializing formal of $k$. -\item $f$ has an initializer in $k$'s initializer list. +\item The type of $v$ is potentially non-nullable + (\ref{typeNullability}). +\item $v$ is final. \end{itemize} \LMHash{}% @@ -4536,14 +4600,14 @@ \subsubsection{Factories} \FACTORY. \begin{grammar} - ::= \gnewline{} + ::= \gnewline{} \CONST? \FACTORY{} \end{grammar} \LMHash{}% -The return type of a factory whose signature is of -the form \FACTORY{} $M$ or -the form \FACTORY{} \code{$M$.\id} +The return type of a factory whose header is of +the form \code{\FACTORY\,\,$M$} or +the form \code{\FACTORY\,\,$M$.\id} is $M$ if $M$ is not a generic type; otherwise the return type is \code{$M$<$T_1, \ldots,\ T_n$>} @@ -4584,7 +4648,7 @@ \subsubsection{Factories} whenever the redirecting constructor is called. \begin{grammar} - ::= \gnewline{} + ::= \gnewline{} \CONST? \FACTORY{} `=' \gnewline{} @@ -4769,7 +4833,7 @@ \subsubsection{Constant Constructors} A constant constructor is prefixed by the reserved word \CONST. \begin{grammar} - ::= \gnewline{} + ::= \gnewline{} \CONST{} \end{grammar} @@ -4923,20 +4987,10 @@ \subsection{Superclasses} \LMHash{}% It is a compile-time error if the type -in the \EXTENDS{} clause of a class $C$ is -a type variable (\ref{generics}), -a type alias that does not denote a class (\ref{typedef}), -an enumerated type (\ref{enums}), -a deferred type (\ref{staticTypes}), type \DYNAMIC{} (\ref{typeDynamic}), -or type \code{FutureOr<$T$>} for any $T$ (\ref{typeFutureOr}). - -\commentary{% -Note that \VOID{} is a reserved word, -which implies that the same restrictions apply for the type \VOID, -and similar restrictions are specified for other types like -\code{Null} (\ref{null}) and -\code{String} (\ref{strings}).% -} +in the \EXTENDS{} clause of a class +which is declared in a non-system library +is not a class building type +(\ref{classBuildingTypes}). \commentary{% The type parameters of a generic class are available in @@ -5130,15 +5184,13 @@ \subsection{Superinterfaces} \LMHash{}% It is a compile-time error if an element -in the type list of the \IMPLEMENTS{} clause of a class $C$ is -a type variable (\ref{generics}), -a type alias that does not denote a class (\ref{typedef}), -an enumerated type (\ref{enums}), -a deferred type (\ref{staticTypes}), type \DYNAMIC{} (\ref{typeDynamic}), -or type \code{FutureOr<$T$>} for any $T$ (\ref{typeFutureOr}). -It is a compile-time error if two elements in the type list of +in the type list of an \IMPLEMENTS{} clause +that occurs in a non-system library +is not a class building type +(\ref{classBuildingTypes}). +It is a compile-time error if two elements in said type list %% TODO(eernst): Refer to nnbd notion of 'same type'. -the \IMPLEMENTS{} clause of a class $C$ specifies the same type $T$. +specifies the same type. It is a compile-time error if the superclass of a class $C$ is one of the elements of the type list of the \IMPLEMENTS{} clause of $C$. @@ -5271,6 +5323,7 @@ \section{Interfaces} it contains the return type and parameter types even if they are implicit in $D$; it omits the names of positional parameters; +it omits the default values of optional parameters, if any; it omits the modifier \FINAL{} from each parameter, if any; it omits metadata (\ref{metadata}); @@ -5314,10 +5367,10 @@ \section{Interfaces} \code{$T_0$ \id<\TypeParametersStd>(} \noindent -\code{\qquad\qquad\List{\COVARIANT?\ T}{1}{n},} +\code{\qquad\List{\COVARIANT?\ T}{1}{n},} \noindent -\code{\qquad\qquad[\PairList{\COVARIANT?\ T}{= d}{n+1}{n+k}])}. +\code{\qquad[\List{\COVARIANT?\ T}{n+1}{n+k}])}. \noindent The \IndexCustom{function type of}{method signature!function type} @@ -5333,12 +5386,13 @@ \section{Interfaces} \code{$T_0$ \id<\TypeParametersStd>(} \noindent -\code{\qquad\qquad\List{\COVARIANT?\ T}{1}{n},} +\code{\qquad\List{\COVARIANT?\ T}{1}{n},} \noindent -\code{\qquad\qquad\{\TripleList{\COVARIANT?\ T}{x}{= d}{n+1}{n+k}\})}. +\code{\qquad\{\TripleList{\COVARIANT?\ r}{T}{x}{n+1}{n+k}\})} \noindent +where $r_{n+j}$ is \REQUIRED{} or empty for $j \in 1 .. k$. The \NoIndex{function type of} $m$ is then \noindent @@ -5350,10 +5404,6 @@ \section{Interfaces} The \NoIndex{function type of} $m$ is then \FunctionTypeSimple{\VOID}{$T$}. -\LMHash{}% -The function type of a member signature remains unchanged if -some or all default values are omitted. - \commentary{% We do not specify the function type of a getter signature. For such signatures we will instead directly refer to the return type.% @@ -5401,7 +5451,7 @@ \section{Interfaces} and property extractions (\ref{propertyExtraction}), any type $T$ which is $T_0$ bounded -(\ref{bindingActualsToFormals}), +(\ref{typesBoundedByTypes}), where $T_0$ is a class with interface $I$, is also considered to have interface $I$. Similarly, when $T$ is $T_0$ bounded where $T_0$ is a function type, @@ -5455,7 +5505,7 @@ \section{Interfaces} because those member signatures are then noSuchMethod forwarded (\ref{theMethodNoSuchMethod}), and an invocation of \code{\_foo} on an instance of $D$ in $L$ -must return an `int` according to the first member signature, +must return an \code{int} according to the first member signature, and it must return a function object according to the second one, and an invocation of \code{\_foo(42)} must return a \code{String} with the first member signature, and it must fail @@ -5471,7 +5521,7 @@ \section{Interfaces} But it should be noted that the conflicts can be detected locally in the library where the private declarations exist, because they only arise for private members with -the same name and incompatible signatures. +the same name and incompatible member signatures. Renaming that private member to anything not used in that library will eliminate the conflict and will not break any clients.% } @@ -5661,7 +5711,7 @@ \subsection{Superinterfaces} another type $T$, this means that $T$ is a superinterface of $S$, or $S$ is $S_0$ bounded for some type $S_0$ -(\ref{bindingActualsToFormals}), +(\ref{typesBoundedByTypes}), and $T$ is a superinterface of $S_0$. Assume that $G$ is a raw type (\ref{instantiationToBound}) @@ -5848,16 +5898,14 @@ \subsection{Mixin Classes} \LMHash{}% It is a compile-time error if an element in -the type list of the \WITH{} clause of a mixin application is -a type variable (\ref{generics}), -a function type (\ref{functionTypes}), -a type alias that does not denote a class (\ref{typedef}), -an enumerated type (\ref{enums}), -a deferred type (\ref{staticTypes}), -type \DYNAMIC{} (\ref{typeDynamic}), -type \VOID{} (\ref{typeVoid}), -or type \code{FutureOr<$T$>} for any $T$ (\ref{typeFutureOr}). -If $T$ is a type in a \WITH{} clause, \IndexCustom{the mixin of}{type!mixin of} +the type list of the \WITH{} clause of a mixin application +that occurs in a non-system library +is not a class building type +(\ref{classBuildingTypes}). + +\LMHash{}% +If $T$ is a type in a \WITH{} clause, the +\IndexCustom{mixin of}{type!mixin of} $T$ is either the mixin derived from $T$ if $T$ denotes a class, or the mixin introduced by $T$ if $T$ denotes a mixin declaration. @@ -5919,31 +5967,6 @@ \subsection{Mixin Classes} \subsection{Mixin Declaration} \LMLabel{mixinDeclaration} -\LMHash{}% -A mixin defines zero or more -\IndexCustom{mixin member declarations}{mixin!member declaration}, -zero or more -\IndexCustom{required superinterfaces}{mixin!required superinterface}, -one -\IndexCustom{combined superinterface}{mixin!combined superinterface}, -and zero or more -\IndexCustom{implemented interfaces}{mixin!implemented interface}. - -\LMHash{}% -The mixin derived from a class declaration: - -\begin{normativeDartCode} -\ABSTRACT? \CLASS{} $X$ \IMPLEMENTS{} $I_1$, \ldots, $I_k$ \{ - \metavar{members} -\} -\end{normativeDartCode} - -has \code{Object} as required superinterface -and combined superinterface, -$I_1$, \ldots, $I_k$ as implemented interfaces, -and the instance members of \metavar{members} as mixin member declarations. -If $X$ is generic, so is the mixin. - \LMHash{}% A mixin declaration introduces a mixin and provides a scope for static member declarations. @@ -5954,8 +5977,14 @@ \subsection{Mixin Declaration} \gnewline{} `\{' ( )* `\}' \end{grammar} +%% TODO(eernst): Change this paragraph if we allow factory constructors. \LMHash{}% -It is a compile-time error to declare a constructor in a mixin-declaration. +It is a compile-time error to declare a constructor in a mixin declaration. +It is a compile-time error if a mixin declaration declares +an instance variable without an initializing expression +which is final or whose type is potentially non-nullable +(\ref{typeNullability}), +unless the variable has a \LATE, \ABSTRACT, or \EXTERNAL{} modifier. \LMHash{}% A mixin declaration with no \code{\ON} clause is equivalent @@ -5972,43 +6001,44 @@ \subsection{Mixin Declaration} \end{normativeDartCode} \LMHash{}% -It is a compile-time error if any of the types $T_1$ through $T_n$ -or $I_1$ through $I_k$ is -a type variable (\ref{generics}), -a function type (\ref{functionTypes}), -a type alias not denoting a class (\ref{typedef}), -an enumerated type (\ref{enums}), -a deferred type (\ref{staticTypes}), -type \DYNAMIC{} (\ref{typeDynamic}), -type \VOID{} (\ref{typeVoid}), -or type \code{FutureOr<$T$>} for any $T$ (\ref{typeFutureOr}). +A compile-time error occurs if +the declaration of $M$ occurs in a non-system library, +and any of the types $T_1$ through $T_n$ +or $I_1$ through $I_k$ is not a class building type +(\ref{classBuildingTypes}). \LMHash{}% -Let $M_S$ be the interface declared by the class declaration +Let $M_S$ be the interface declared by the following class declaration: \begin{normativeDartCode} -\ABSTRACT{} \CLASS{} $M_{super}$<$P_1$, \ldots, $P_m$> \IMPLEMENTS{} $T_1$, $\dots{}$, $T_n$ \{\} +\ABSTRACT{} \CLASS{} $M_{\metavar{super}}$<\TypeParametersStd{}> + \IMPLEMENTS{} \List{T}{1}{n} \{\} \end{normativeDartCode} -\noindent -where $M_{super}$ is a fresh name. +\LMHash{}% +In this declaration, $M_{\metavar{super}}$ is a fresh name. It is a compile-time error for the mixin declaration if the $M_S$ -class declaration would cause a compile-time error, +class declaration would cause a compile-time error. + \commentary{% -that is, if any member is declared by more than one declared superinterface, -and there is not a most specific signature for that member among the super -interfaces% -}. +That is, if any member is declared by more than one declared superinterface, +and there is no most specific signature for that member among the super +interfaces.% +} + +\LMHash{}% The interface $M_S$ is called the -\Index{superinvocation interface} of the mixin declaration $M$. +\IndexCustom{superinvocation interface}{mixin!superinvocation interface} +of the mixin declaration $M$. + \commentary{% If the mixin declaration $M$ has only one declared superinterface, $T_1$, -then the superinvocation interface $M_{super}$ has exactly the same members -as the interface $T_1$.% +then the superinvocation interface is the interface of $T_1$.% } \LMHash{}% -Let $M_I$ be the interface that would be defined by the class declaration +Let $M_I$ be the interface that would be defined by +the following class declaration: \begin{normativeDartCode} \ABSTRACT{} \CLASS{} $N$<\TypeParametersStd> @@ -6017,24 +6047,65 @@ \subsection{Mixin Declaration} \} \end{normativeDartCode} -where $\metavar{members}'$ are the member declarations of -the mixin declaration $M$ except that all superinvocations are treated -as if \SUPER{} was a valid expression with static type $M_S$. -It is a compile-time error for the mixin $M$ if this $N$ class -declaration would cause a compile-time error, \commentary{that is, if the -required superinterfaces, the implemented interfaces and the declarations do not -define a consistent interface, if any member declaration contains a -compile-time error other than a super-invocation, or if a super-invocation -is not valid against the interface $M_S$}. +\LMHash{}% +In this declaration, \metavar{members'} are +the member declarations of the mixin declaration $M$, +except that all superinvocations are treated +as if \SUPER{} were a valid expression with static type $M_S$. + +\LMHash{}% +It is a compile-time error for the mixin $M$ if this class declaration $N$ +would cause a compile-time error. + +\commentary{% +That is, if the required superinterfaces, the implemented interfaces, +and the declarations do not define a consistent interface, +if any member declaration contains a compile-time error +other than a super-invocation, +or if a super-invocation is not valid against the interface $M_S$.% +} + +\LMHash{}% The interface introduced by the mixin declaration $M$ has the same member signatures and superinterfaces as $M_I$. \LMHash{}% -The mixin declaration $M$ introduces a mixin -with the \NoIndex{required superinterface}s $T_1$, \ldots, $T_n$, -the \NoIndex{combined superinterface} $M_S$, -\NoIndex{implemented interface}s $I_1$, \ldots, $I_k$ -and the instance members declared in $M$ as \Index{mixin member declarations}. +The mixin declaration $M$ introduces a mixin with +\IndexCustom{required superinterfaces}{mixin!required superinterface} +\List{T}{1}{n}, +\IndexCustom{combined superinterface}{mixin!combined superinterface} +$M_S$, +\IndexCustom{implemented interfaces}{mixin!implemented interface} +\List{I}{1}{k}, +and the instance members declared in $M$ as +\IndexCustom{mixin member declarations}{mixin!member declaration}. + +\LMHash{}% +It is possible to +\IndexCustom{derive a mixin from a class}{% + mixin!derived from a class}. +Consider a class declaration of the following form: + +\begin{normativeDartCode} +\ABSTRACT? \CLASS{} $C$<\TypeParametersStd> + \IMPLEMENTS{} \List{I}{1}{k} \{ + \metavar{members} +\} +\end{normativeDartCode} + +The mixin derived from $C$ has +\code{Object} as required superinterface and combined superinterface, +\List{I}{1}{k} as implemented interfaces, +and the instance members of \metavar{members} as mixin member declarations. + +\LMHash{}% +Assume that \metavar{members} includes the declaration $D$ +which does not have the modifier \LATE, +and that $D$ declares an instance variable $v$ +which does not have an initializing expression. +It is then a compile-time error if +the type of $v$ is potentially non-nullable +(\ref{typeNullability}). \subsection{Mixin Application} @@ -6045,15 +6116,15 @@ \subsection{Mixin Application} \LMHash{}% Let $S$ be a class, -$M$ be a mixin with \NoIndex{required superinterface}s $T_1$, \ldots, $T_n$, +$M$ be a mixin with \NoIndex{required superinterface}s \List{T}{1}{n}, \NoIndex{combined superinterface} $M_S$, -\NoIndex{implemented interfaces} $I_1$, \ldots, $I_k$ and +\NoIndex{implemented interfaces} \List{I}{1}{k} and \metavar{members} as \NoIndex{mixin member declarations}, and let $N$ be a name. \LMHash{}% It is a compile-time error to apply $M$ to $S$ if $S$ does not implement, -directly or indirectly, all of $T_1$, \ldots, $T_n$. +directly or indirectly, all of \List{T}{1}{n}. It is a compile-time error if any of \metavar{members} contains a super-invocation of a member $m$ \commentary{(for example \code{super.foo}, \code{super + 2}, or \code{super[1] = 2})}, and $S$ does not have a concrete @@ -7708,8 +7779,8 @@ \subsection{Super-Bounded Types} or a constant object expression (\ref{const}). \item $T$ is an immediate subterm of a redirecting factory constructor - signature - (\ref{redirectingFactoryConstructors}). + header + (\ref{redirectingFactoryConstructors}). \item $T$ is an immediate subterm of an \EXTENDS{} clause of a class (\ref{superclasses}), or it occurs as an element in the type list of an \IMPLEMENTS{} clause @@ -7882,7 +7953,7 @@ \subsection{Instantiation to Bound} } \commentary{% For example, assuming the declaration -\code{\CLASS{} C \{\ldots\}}, +\code{\CLASS{} C \{\ldots\}}, instantiation to bound on \code{C} yields \code{C}, and this means that \code{C x;} can be used to declare a variable \code{x} whose value can be a \code{C<$T$>} for \emph{all possible} values of $T$.% @@ -7931,7 +8002,7 @@ \subsubsection{Auxiliary Concepts for Instantiation to Bound} that is, if $S$ receives any type arguments. Also note that $S$ cannot be a type variable, - because then `$S$ is $T$' cannot hold. + because then \code{$S$\,\,\IS\,\,$T$} cannot hold. See the discussion below and the reference to~\ref{subtypeRules} for more details about why this is so.% } @@ -8255,6 +8326,8 @@ \subsubsection{The Instantiation to Bound Algorithm} \noindent and a function type \FunctionTypeNamedStd{T_0} + +\noindent it is applied to \List{T}{0}{n+k}. \commentary{% @@ -9910,6 +9983,14 @@ \subsection{Collection Literals} how to obtain zero or more entities, of the form \synt{ifElement} or \synt{forElement}. +\LMHash{}% +It is a compile-time error if +the \synt{forLoopParts} of a \synt{forElement} +declares an iteration variable that has +the modifier \CONST{} or the modifier \LATE, +using the same rules as with a \FOR{} statement +(\ref{for}). + \commentary{% Terms derived from \synt{element}, and the ability to build collections from them, @@ -11732,8 +11813,15 @@ \subsection{Throw} \end{grammar} \LMHash{}% -Evaluation of a throw expression of the form -\code{\THROW{} $e$;} +Consider an expression of the form \code{\THROW\,\,$e$} +where $e$ is an expression with static type $T$. +It is a compile-time error unless $T$ is assignable to \code{Object}. + +\LMHash{}% +The static type of a throw expression is \code{Never}. + +\LMHash{}% +Evaluation of a throw expression of the form \code{\THROW\,\,$e$;} proceeds as follows: \LMHash{}% @@ -11742,7 +11830,8 @@ \subsection{Throw} \commentary{% There is no requirement that the expression $e$ must evaluate to -any special kind of object.% +any special kind of object, +except that the null object is given a special treatment.% } \LMHash{}% @@ -11763,9 +11852,6 @@ \subsection{Throw} the \emph{first} time it was thrown.% } -\LMHash{}% -The static type of a throw expression is $\bot$. - \subsection{Function Expressions} \LMLabel{functionExpressions} @@ -11987,7 +12073,7 @@ \subsection{Function Expressions} \noindent is -\FunctionTypePositionalStdCr{\code{Future<\flatten{T_0}>}}, +\FunctionTypePositionalStdCr{\code{Future<\Flatten{$T_0$}>}}, \noindent where $T_0$ is the static type of $e$. @@ -12023,7 +12109,7 @@ \subsection{Function Expressions} \noindent is -\FunctionTypeNamedStdCr{\code{Future<\flatten{T_0}>}}, +\FunctionTypeNamedStd{\code{Future<\Flatten{$T_0$}>}}, \noindent where $T_0$ is the static type of $e$. @@ -12121,7 +12207,7 @@ \subsection{Function Expressions} \noindent is %% TODO(eernst): Adjust to take type inference into account. -\FunctionTypeNamedStdCr{\code{Future}}. +\FunctionTypeNamedStd{\code{Future}}. \EndCase \LMHash{}% @@ -12137,7 +12223,7 @@ \subsection{Function Expressions} \noindent is %% TODO(eernst): Adjust to take type inference into account. -\FunctionTypeNamedStdCr{\code{Stream}}. +\FunctionTypeNamedStd{\code{Stream}}. \EndCase \LMHash{}% @@ -12153,7 +12239,7 @@ \subsection{Function Expressions} \noindent is %% TODO(eernst): Adjust to take type inference into account. -\FunctionTypeNamedStdCr{\code{Iterable}}. +\FunctionTypeNamedStd{\code{Iterable}}. \EndCase \LMHash{}% @@ -12401,7 +12487,7 @@ \subsubsection{New} (\ref{factories}) of the form \code{\CONST? $T$($p_1, \ldots,\ p_{n+k}$) = $c$;} or of the form \code{\CONST? $T$.\id($p_1, \ldots,\ p_{n+k}$) = $c$;} -where \code{\CONST?} indicates that \CONST{} may be present or absent, +where \code{\CONST?}\ indicates that \CONST{} may be present or absent, the remaining evaluation of $e$ is equivalent to evaluating \code{\NEW{} $c$($v_1, \ldots,\ v_n,\ x_{n+1}$: $v_{n+1}, \ldots,\ x_{n+k}$: $v_{n+k}$)} @@ -12900,22 +12986,26 @@ \subsubsection{Actual Argument Lists} \LMHash{}% Let $L$ be an argument list of the form -\code{($e_1 \ldots,\ e_m,\ y_{m+1}$: $e_{m+1} \ldots,\ y_{m+p}$: $e_{m+p}$)} + +\noindent +\code{(\List{e}{1}{m},\ \PairList{y}{\!\!:\,\,e}{m+1}{m+p})} + +\noindent and assume that the static type of $e_i$ is $S_i$, $i \in 1 .. m+p$. The \Index{static argument list type} of $L$ is then -\code{($S_1 \ldots,\ S_m,\ S_{m+1}\ y_{m+1} \ldots,\ S_{m+p}\ y_{m+p}$)}. +\code{(\List{S}{1}{m},\ \PairList{S}{y}{m+1}{m+p})}. \LMHash{}% Let $\argumentList{S}$ be the static argument list type \noindent -\code{($S_1 \ldots,\ S_m,\ S_{m+1}\ y_{m+1} \ldots,\ S_{m+p}\ y_{m+p}$)} +\code{(\List{S}{1}{m},\ \PairList{S}{y}{m+1}{m+p})} \noindent and let $\parameterList{P}$ be the formal parameter list \noindent -\code{($T_1\ x_1 \ldots,\ T_n\ x_n,\ $[$T_{n+1}\ x_{n+1} = d_1, \ldots,\ T_{n+k}\ x_{n+k} = d_k$])} +\code{(\PairList{T}{x}{1}{n},\ [\TripleList{T}{x}{= d}{n+1}{n+k}])} \noindent where each parameter may be marked \COVARIANT{} @@ -12935,23 +13025,24 @@ \subsubsection{Actual Argument Lists} Let $\argumentList{S}$ be the static argument list type \noindent -\code{($S_1 \ldots,\ S_m,\ S_{m+1}\ y_{m+1} \ldots,\ S_{m+p}\ y_{m+p}$)} +\code{(\List{S}{1}{m},\ \PairList{S}{y}{m+1}{m+p})} \noindent and let $\parameterList{P}$ be the formal parameter list \noindent -\code{($T_1\ x_1 \ldots,\ T_n\ x_n,\ $\{$T_{n+1}\ x_{n+1} = d_1, \ldots,\ T_{n+k}\ x_{n+k} = d_k$\})} +\code{(\PairList{T}{x}{1}{n},\ \{\TripleList{T}{x}{d}{n+1}{n+k}\})} \noindent where each parameter may be marked \COVARIANT{} +and each named parameter may be marked \REQUIRED{} (\commentary{not shown, but allowed}). \LMHash{}% We say that $\argumentList{S}$ is a \Index{subtype match} for $\parameterList{P}$ if{}f $m = n$, -$\{y_{m+1}\ldots,\ y_{m+p}\} \subseteq \{x_{n+1}\ldots,\ x_{n+k}\}$, +$\{\List{y}{m+1}{m+p}\} \subseteq \{\List{x}{n+1}{n+k}\}$, $S_i$ is a subtype of $T_i$ for all $i \in 1 .. m$, and $S_i$ is a subtype of $T_j$ whenever $y_i = x_j$ and $j \in n + 1 .. n + k$, for all @@ -12959,7 +13050,7 @@ \subsubsection{Actual Argument Lists} We say that $\argumentList{S}$ is an \Index{assignable match} for $\parameterList{P}$ if{}f $m = n$, -$\{y_{m+1}\ldots,\ y_{m+p}\} \subseteq \{x_{n+1}\ldots,\ x_{n+k}\}$, +$\{\List{y}{m+1}{m+p}\} \subseteq \{\List{x}{n+1}{n+k}\}$, $S_i$ is assignable to $T_i$ for all $i \in 1 .. m$, and $S_i$ is assignable to $T_j$ whenever $y_i = x_j$ and $j \in n + 1 .. n + k$, for all @@ -13053,6 +13144,10 @@ \subsubsection{Binding Actuals to Formals} an actual argument part of the form \code{<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_m,\ q_1$: $a_{m+1}, \ldots,\ q_l$: $a_{m+l}$)}. +\LMHash{}% +% We cannot pass the same named parameter twice. +It is a compile-time error if $q_j = q_k$ for any $j \ne k$. + \commentary{% Note that $f$ denotes a function in a semantic sense, rather than a syntactic construct. @@ -13086,44 +13181,19 @@ \subsubsection{Binding Actuals to Formals} } \LMHash{}% -% We cannot pass the same named parameter twice. -It is a compile-time error if $q_j = q_k$ for any $j \ne k$. - -\LMHash{}% -For a given type $T_0$, we introduce the notion of a -\IndexCustom{$T_0$ bounded type}{type!T0 bounded}: -$T_0$ itself is $T_0$ bounded; -if $B$ is $T_0$ bounded and -$X$ is a type variable with bound $B$ -then $X$ is $T_0$ bounded; -finally, if $B$ is $T_0$ bounded and -$X$ is a type variable -then $X \& B$ is $T_0$ bounded. -In particular, a -\IndexCustom{\DYNAMIC{} bounded type}{type!dynamic bounded} -is either \DYNAMIC{} itself -or a type variable whose bound is \DYNAMIC{} bounded, -or an intersection whose second operand is \DYNAMIC{} bounded. -Similarly for a -\IndexCustom{\FUNCTION{} bounded type}{type!function bounded}. - -\LMHash{}% -A -\IndexCustom{function-type bounded type}{type!function-type bounded} -is a type $T$ which is $T_0$ bounded where $T_0$ is a function type -(\ref{functionTypes}). -A function-type bounded type $T$ has an -\Index{associated function type} -which is the unique function type $T_0$ such that $T$ is $T_0$ bounded. - -\LMHash{}% -If the static type of $f$ is \DYNAMIC{} bounded or \FUNCTION{} bounded, +If the static type of $f$ is \DYNAMIC{} bounded or \FUNCTION{} bounded +(\ref{typesBoundedByTypes}), no further static checks are performed on the invocation $i$ (\commentary{apart from separate static checks on subterms like arguments}), and the static type of $i$ is \DYNAMIC. Otherwise, it is a compile-time error if the static type of $f$ is not function-type bounded. +\LMHash{}% +A compile-time error occurs if the static type of $f$ is potentially nullable +and not \DYNAMIC{} +(\ref{typeNullability}). + \LMHash{}% If no error occurred and the static analysis of $i$ is not complete then the static type $T_f$ of $f$ is function-type bounded; @@ -13131,11 +13201,11 @@ \subsubsection{Binding Actuals to Formals} \LMHash{}% Let $S_0$ be the return type of $F$, -let $X_1\ \EXTENDS\ B_1, \ldots, X_s\ \EXTENDS\ B_s$ -be the formal type parameters, -let $h$ be the number of required parameters, +let \TypeParametersStd{} be the formal type parameters, +let $h$ be the number of required positional parameters, let $p_1, \ldots, p_n$ be the positional parameters, -and let $p_{h+1}, \ldots, p_{h+k}$ be the optional parameters of $F$. +and let $p_{h+1}, \ldots, p_{h+k}$ be +the optional positional or named parameters of $F$. Let $S_i$ be the static type of the formal parameters $p_i, i \in 1 .. h+k$, and for each $q$ let $S_q$ be the type of the parameter named $q$, where each parameter type is obtained by replacing $X_j$ by $A_j, j \in 1 .. s$, @@ -13146,7 +13216,8 @@ \subsubsection{Binding Actuals to Formals} We have an actual argument list consisting of $r$ type arguments, $m$ positional arguments, and $l$ named arguments. We have a function with $s$ type parameters, -$h$ required parameters, and $k$ optional parameters. +$h$ required positional parameters, +and $k$ optional positional or named parameters. Figure~\ref{fig:argumentsAndParameters} shows how this situation arises.% } @@ -13177,7 +13248,7 @@ \subsubsection{Binding Actuals to Formals} \end{array} \end{displaymath} % - \flushleft{\commentary{Declaration with named parameters: $n = h$}} + \flushleft{\commentary{Declaration with optional positional parameters: $n = h + k$}} \begin{displaymath} \begin{array}{rl} \left<\P{$s$ type}\right> @@ -13185,21 +13256,21 @@ \subsubsection{Binding Actuals to Formals} \begin{array}{r@{,\;}l} \P{$h$ required}&\P{$k$ optional}\\ \multicolumn{2}{c}{\mbox{\scriptsize\textit{which may also be viewed as}}}\\ - \P{$n$ positional}&\P{$k$ named}\\ + \multicolumn{2}{c}{\P{$n$ positional}} \end{array} \right) \end{array} \end{displaymath} % - \flushleft{\commentary{Declaration with optional positional parameters: $n = h + k$}} + \flushleft{\commentary{Declaration with named parameters: $n = h$}} \begin{displaymath} \begin{array}{rl} \left<\P{$s$ type}\right> \left( \begin{array}{r@{,\;}l} - \P{$h$ required}&\P{$k$ optional}\\ + \P{$h$ required positional}&\P{$k$ named}\\ \multicolumn{2}{c}{\mbox{\scriptsize\textit{which may also be viewed as}}}\\ - \multicolumn{2}{c}{\P{$n$ positional}} + \P{$n$ positional}&\P{$k$ named}\\ \end{array} \right) \end{array} @@ -13216,16 +13287,23 @@ \subsubsection{Binding Actuals to Formals} It is a compile-time error if $r = s$ and for some $j \in 1 .. s$, $A_j \not<: [A_1/X_1, \ldots, A_r/X_s]B_j$. It is a compile-time error unless $h \le m \le n$. -If $l > 0$, -it is a compile-time error unless $F$ has named parameters and -$q_j \in \{p_{h+1}, \ldots, p_{h+k}\}, j \in 1 .. l$. +It is a compile-time error unless both of the following are satisfied: + +\begin{itemize} +\item If $l > 0$ then $F$ has named parameters and + $q_j \in \{\List{p}{h+1}{h+k}\}$, for each $j \in 1 .. l$. +\item If $F$ has named parameters then for each $p_j$, $j \in h+1 .. h+k$, + if $p_j$ is declared with the modifier \REQUIRED{} + then $p_j \in \{\List{q}{1}{l}\}$. +\end{itemize} \commentary{% That is, the number of type arguments must match the number of type parameters, and the bounds must be respected. -We must receive at least the required number of positional arguments, +We must receive at least the required number of positional parameters, and not more than the total number of positional parameters. -For each named argument there must be a named parameter with the same name.% +For each named argument there must be a named parameter with the same name, +and there must be a named argument for each required named parameter.% } \LMHash{}% @@ -13477,7 +13555,8 @@ \subsubsection{Function Expression Invocation} \LMHash{}% Consider the situation where $e_f$ denotes a class $C$ that contains a declaration of a constructor named $C$, -or it is of the form \code{$e'_f$.\id} where +or it is of the form \code{$e'_f$.\id} +or \code{$e'_f$<\metavar{typeArguments}>.\id} where $e'_f$ denotes a class $C$ that contains a declaration of a constructor named \code{$C$.\id}. If $i$ occurs in a constant context @@ -13512,7 +13591,7 @@ \subsubsection{Function Expression Invocation} \LMHash{}% If $e_f$ is a property extraction expression (\ref{propertyExtraction}) -then $i$ treated as an ordinary method invocation +then $i$ is treated as an ordinary method invocation (\ref{ordinaryInvocation}). \commentary{% @@ -13530,7 +13609,7 @@ \subsubsection{Function Expression Invocation} \LMHash{}% Let $F$ be the static type of $e_f$. -If $F$ is an interface type that has a method named \CALL, +If the interface of $F$ has a method named \CALL, $i$ is treated as (\ref{notation}) the ordinary invocation @@ -13642,7 +13721,7 @@ \subsection{Function Closurization} which is an instance of a class $C$ whose interface is a subtype of the actual type $F$ (\ref{actualTypes}) -corresponding to the signature in the function declaration $f$, +corresponding to the header in the declaration of $f$, using the current bindings of type variables, if any. There does not exist a function type $F'$ which is a proper subtype of $F$ such that $C$ is a subtype of $F'$. @@ -14170,6 +14249,70 @@ \subsection{Member Invocations} \label{fig:desugarCompositeMemberInvocations} \end{figure} +\LMHash{}% +A \Index{member invocation} is an expression with a specific syntactic form +whose dynamic semantics involves invocation of +one or two instance members of a given receiver, +or invocation of extension members. +This section specifies which syntactic forms are member invocations, +and defines some terminology +which is needed in order to denote +specific parts of several syntactic forms collectively. + +\LMHash{}% +The static analysis and dynamic semantics of +each of the syntactic forms that are member invocations +is specified separately, +this section is only concerned with +the syntactic classification and terminology. + +\commentary{% +For example, one kind of member invocation is an ordinary method invocation +(\ref{ordinaryInvocation}).% +} + +\LMHash{}% +A +\IndexCustom{simple member invocation}{member invocation!simple} +respectively +\IndexCustom{composite member invocation}{member invocation!composite} +on a +\IndexCustom{syntactic receiver}{member invocation!syntactic receiver} +expression $r$ is an expression of +one of the forms shown in Fig.~\ref{fig:memberInvocations}. +Each member invocation has a +\IndexCustom{corresponding member name}{ + member invocation!corresponding member name} +as shown in the figure. + +\LMHash{}% +Each member invocation in Fig.~\ref{fig:memberInvocations} that contains +\lit{?} is a +\IndexCustom{conditional member invocation}{member invocation!conditional}. +An +\IndexCustom{unconditional member invocation}{member invocation!unconditional} +is a member invocation which is not conditional. + +\commentary{% +For a simple member invocation the corresponding member name is +the name of the member which is invoked +in the case where the member invocation invokes an instance member. +For a composite member invocation it is the name of the getter +and the basename of both the getter and the setter.% +} + +\rationale{% +Note that $r$ cannot be \SUPER{} +even though \code{\SUPER.m()} invokes an instance method. +This is because the semantics of a superinvocation is different from +that of other invocations. +Among the binary operators, \lit{==} is not included. +This is because evaluation of \code{$e_1$ == $e_2$} +involves more steps than an instance member invocation. +Similarly, \lit{\&\&}, and \lit{||} are not included +because their evaluation does not involve method invocation.% +} + \LMHash{}% A composite member invocation is an abbreviated form whose meaning is reduced to simple member invocations @@ -14228,6 +14371,8 @@ \subsubsection{Ordinary Invocation} \IndexCustom{conditional ordinary method invocation}{% method invocation!conditional ordinary} $i$ of the form + +\noindent \code{$e$?.$m$<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}. \commentary{% @@ -14238,36 +14383,20 @@ \subsubsection{Ordinary Invocation} } \LMHash{}% -The static type of $i$ is the same as the static type of +A warning occurs if the static type of $e$ is non-nullable. +\commentary{In this case, the \lit{?} is superfluous.} -\noindent -\code{$e$.$m$<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}. - -\noindent -Exactly the same compile-time errors that would be caused by +\LMHash{}% +Otherwise, $i$ is treated as \noindent -\code{$e$.$m$<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} +\Let{$v$}{$e$}{$v$\,\,==\,\,\NULL\ ?\ \NULL\ :\ $v$.$m$<$A_1, \ldots,\ A_r$>(} \noindent -are also generated in the case of $i$. - -\LMHash{}% -Evaluation of $i$ proceeds as follows: - -\LMHash{}% -If $e$ is a type literal or denotes an extension, $i$ is treated as +\code{\qquad{}$a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} \noindent -\code{$e$.$m$<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}. - -\LMHash{}% -Otherwise, evaluate $e$ to an object $o$. -If $o$ is the null object, $i$ evaluates to the null object (\ref{null}). -Otherwise let $v$ be a fresh variable bound to $o$ and evaluate -\code{$v$.$m$<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} -to an object $r$. -Then $e$ evaluates to $r$. +which determines the static analysis and dynamic semantics of $i$. \EndCase \LMHash{}% @@ -14275,7 +14404,8 @@ \subsubsection{Ordinary Invocation} A \IndexCustom{static member invocation}{member invocation!static} $i$ is an invocation of the form \code{$C$.$m$<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$:\ $a_{n+1}, \ldots,\ x_{n+k}$:\ $a_{n+k}$)}, -where $C$ is a type literal, or $C$ denotes an extension. +where $C$ is a term derived from \synt{typeName} +that denotes a type or an extension. \commentary{% Non-generic invocations arise as the special case @@ -14283,7 +14413,14 @@ \subsubsection{Ordinary Invocation} } \LMHash{}% -A compile-time error occurs +If $C$ denotes a type alias then $i$ is treated as described in +the section about type aliases +(\ref{typedef}). + +\LMHash{}% +Otherwise +(\commentary{when $C$ does not denote a type alias}), +a compile-time error occurs unless $C$ denotes a class, a mixin, or an extension that declares a static member named $m$, which we will call the @@ -14296,8 +14433,8 @@ \subsubsection{Ordinary Invocation} \LMHash{}% The static analysis of $i$ is then performed as specified in Section~\ref{bindingActualsToFormals}, -considering $F$ to be the static type of the function to call, -and the static type of $i$ is as specified there. +considering $F$ to be the static type of the function to call. +The static type of $i$ is as specified there. \LMHash{}% Evaluation of a static method invocation $i$ of the form @@ -14344,7 +14481,7 @@ \subsubsection{Ordinary Invocation} \LMHash{}% If $T$ is \DYNAMIC{} bounded -(\ref{bindingActualsToFormals}) +(\ref{typesBoundedByTypes}) and $m$ is one of \code{hashCode}, \code{noSuchMethod}, \code{runtimeType}, or \code{toString} then we say that $i$ is a @@ -14366,23 +14503,30 @@ \subsubsection{Ordinary Invocation} Or \item $T$ is \FUNCTION{} bounded - (\ref{bindingActualsToFormals}) + (\ref{typesBoundedByTypes}) and $m$ is \CALL; in this case no further static checks are performed on $i$ (\commentary{apart from separate static checks on subterms like arguments}) and the static type of $i$ is \DYNAMIC. -\rationale{% -This means that for invocations of an instance method named \CALL, -a receiver of type \FUNCTION{} is treated like a receiver of type \DYNAMIC. -The expectation is that any concrete subclass of \FUNCTION{} -will implement \CALL, -but there is no method signature -which can be assumed for \CALL{} in \FUNCTION{} -because every signature will conflict with -some potential overriding declarations.% -} + \rationale{% + This means that for invocations of an instance method named \CALL, + a receiver of type \FUNCTION{} is treated like a receiver of type \DYNAMIC. + The expectation is that any concrete subclass of \FUNCTION{} + will implement \CALL, + but there is no method signature + which can be assumed for \CALL{} in \FUNCTION{} + because every signature will conflict with + some potential overriding declarations.% + } \end{itemize} +\commentary{% +Note that if $T$ is potentially nullable then $m$ must be +a member of \code{Object}, +because those are the only members of the interface of $T$ +(\ref{typeNullability}).% +} + \LMHash{}% If $T$ did not have an accessible member named $m$ the static type of $i$ is \DYNAMIC, @@ -14673,7 +14817,14 @@ \subsubsection{Cascades} } \LMHash{}% -The static analysis and dynamic semantics of a cascaded member access +Consider an expression of the form \code{$e$?..$s$} where +$e$ is a \synt{conditionalExpression} and +$s$ is a \synt{cascadeSection}. +A warning occurs unless the static type of $e$ is potentially nullable. + +\LMHash{}% +Otherwise, the static analysis and dynamic semantics of +a cascaded member access is specified in terms of the following desugaring step. \LMHash{}% @@ -14688,6 +14839,19 @@ \subsubsection{Cascades} \noindent \LetThree{$v$}{$e_0$}{$v_1$}{$v$.$c_1$,\ $\cdots$}{$v_k$}{$v$.$c_k$}{$v$}. +\commentary{% +Note that if the static type of $e_0$ is a potentially nullable type $T_0$ +then the type of $v$ is also $T_0$, +and then if $T_0$ is not \DYNAMIC{}, +and, for some $j$, +the first selector in $c_j$ accesses a member $m$ which is not +a member of \code{Object}, +then a compile-time error occurs. +In other words, the member accesses at the beginning of each cascade section +give rise to compile-time errors for a potentially null receiver +just like an ordinary invocation or property extraction.% +} + \LMHash{}% Let $e$ be a cascaded member access which is initially conditional. This implies that there exist terms \List{c}{1}{k} @@ -14854,7 +15018,7 @@ \subsection{Property Extraction} (\ref{instanceMethodClosurization}). Or \item A getter invocation, which returns - the result of invoking of a getter method + the result of invoking of a getter (\ref{getterAccessAndMethodExtraction}). \end{enumerate} @@ -14873,31 +15037,18 @@ \subsection{Property Extraction} $i$ of the form \code{$e$?.\id}. \LMHash{}% +%% TODO(eernst): 'A compile-time error occurs if $e$ is a type literal.' +%% when we change the rules about type literals as receivers. If $e$ is a type literal, $i$ is treated as \code{$e$.\id}. \LMHash{}% -Otherwise, the static type of $i$ is the same as -the static type of \code{$e$.\id}. -Let $T$ be the static type of $e$, -and let $y$ be a fresh variable of type $T$. -Except for errors inside $e$ and references to the name $y$, -exactly the same compile-time errors that would be caused by \code{$y$.\id} -are also generated in the case of \code{$e$?.\id}. +A warning occurs if the static type of $e$ is non-nullable. +\commentary{In this case, the \lit{?} is superfluous.} \LMHash{}% -Evaluation of a conditional property extraction expression $i$ -of the form \code{$e$?.\id} proceeds as follows: - -\LMHash{}% -If $e$ is a type literal or $e$ denotes an extension, -evaluation of $i$ amounts to evaluation of \code{$e$.\id}. - -\LMHash{}% -Otherwise evaluate $e$ to an object $o$. -If $o$ is the null object, $i$ evaluates to the null object (\ref{null}). -Otherwise let $x$ be a fresh variable bound to $o$ -and evaluate \code{$x$.\id} to an object $r$. -Then $i$ evaluates to $r$. +Otherwise, $i$ is treated as +\Let{$v$}{$e$}{$v$\,\,==\,\,\NULL\ ?\ \NULL\ :\ $v$.\id}, +which determines the static analysis and dynamic semantics. \EndCase \LMHash{}% @@ -14906,7 +15057,13 @@ \subsection{Property Extraction} a \IndexCustom{static property extraction}{% property extraction!static} $i$ is an expression of the form \code{$C$.\id}, -where $C$ is a type literal or $C$ denotes an extension. +where $C$ is a term derived from \synt{typeName} that denotes +a type or an extension. + +\LMHash{}% +If $C$ denotes a type alias then $i$ is treated as described +in the section about type aliases +(\ref{typedef}). \LMHash{}% A compile-time error occurs @@ -14941,9 +15098,9 @@ \subsection{Property Extraction} is an expression of the form \code{$e$.\id} where $e$ is an expression that is not a type literal, and does not denote an extension -(\ref{getterAccessAndMethodExtraction}); +(specified in~\ref{getterAccessAndMethodExtraction}); or it is an expression of the form \code{\SUPER.\id} -(\ref{superGetterAccessAndMethodClosurization}). +(specified in~\ref{superGetterAccessAndMethodClosurization}). \EndCase \LMHash{}% @@ -14965,7 +15122,7 @@ \subsection{Property Extraction} \EndCase -\subsubsection{Getter Access and Method Extraction} +\subsubsection{Getter Invocation and Method Closurization} \LMLabel{getterAccessAndMethodExtraction} \LMHash{}% @@ -14998,7 +15155,7 @@ \subsubsection{Getter Access and Method Extraction} \LMHash{}% If $T$ is \DYNAMIC{} bounded -(\ref{bindingActualsToFormals}) +(\ref{typesBoundedByTypes}) and $m$ is one of \code{hashCode}, \code{noSuchMethod}, \code{runtimeType}, or \code{toString} then we say that $i$ is a @@ -16675,7 +16832,7 @@ \subsection{Multiplicative Expressions} the method invocation \code{\SUPER.$op$($e_2$)}. \LMHash{}% -The static type of an multiplicative expression is usually determined +The static type of a multiplicative expression is usually determined by the signature given in the declaration of the operator used. However, invocations of the operators \code{*} and \code{\%} of class \code{int}, \code{double} and \code{num} @@ -18231,7 +18388,7 @@ \subsection{Local Function Declaration} (\ref{functionDeclarations}). \begin{grammar} - ::= + ::= \end{grammar} \LMHash{}% @@ -18407,6 +18564,39 @@ \subsection{For} \subsubsection{For Loop} \LMLabel{forLoop} +\LMHash{}% +This section describes a traditional, C-style \FOR{} loop. + +\LMHash{}% +Consider a loop of the form +\code{\FOR\ ($D$;\,\,$c$;\,\,\List{e}{1}{n})\ $S$}, +where $D$ is a local variable declaration declaring a variable $v$, +$c$ is an expression or empty +(\commentary{used to decide whether to run the body one more time}), +and \List{e}{1}{n} is an expression list or empty +(\commentary{used to update the iteration state}). + +\LMHash{}% +A compile-time error occurs if $D$ has the modifier \LATE. +A compile-time error occurs if the static type of $c$ is not +assignable to \code{bool}. + +\LMHash{}% +Consider a loop of the form +\code{\FOR\ ($e$;\,\,$c$;\,\,\List{e}{1}{n})\ $S$}, +where $e$ is an expression or empty +(\commentary{used to perform initialization}), +$c$ is an expression or empty +(\commentary{used to decide whether to run the body one more time}), +and \List{e}{1}{n} is an expression list or empty +(\commentary{used to update the iteration state}). + +\LMHash{}% +A compile-time error occurs if the static type of $c$ is not +assignable to \code{bool}. + +%% TODO(eernst): The description of the dynamic semantics is highly +%% incomplete and example driven. Rewrite! \LMHash{}% Execution of a for statement of the form \code{\FOR{} (\VAR{} $v$ = $e_0$; $c$; $e$) $s$} proceeds as follows: @@ -18462,31 +18652,19 @@ \subsubsection{For Loop} and then modifies $v''$ as required for the next iteration.% } -\LMHash{}% -It is a compile-time error if the static type of $c$ -may not be assigned to \code{bool}. - -%A for statement of the form \code{\FOR{} ($d$ ; $c$; $e$) $s$} -%is equivalent to the following code: - -%\code{ -%\{$d$; -%\WHILE{} ($c$) \{ -% \{$s$\} -% $e$; -%\}\} -%} - -%If $c$ is empty, it is interpreted as \TRUE. - \subsubsection{For-in} \LMLabel{for-in} \LMHash{}% -Let $D$ be derived from \syntax{?}. -A for statement of the form \code{\FOR{} ($D$ \id{} \IN{} $e$)\,\,$S$} -is then treated as the following code, +Let $D$ be derived from \syntax{?}\ and +consider a \FOR{} statement $S_{\metavar{for}}$ of the form +\code{\FOR\ ($D$\,\,\id\,\,\IN\,\,$e$)\ $S$}. +A compile-time error occurs if $D$ contains +the modifier \CONST{} or the modifier \LATE. + +\LMHash{}% +Otherwise, $S_{\metavar{for}}$ is treated as the following code, where $\id_1$ and $\id_2$ are fresh identifiers: \begin{normativeDartCode} @@ -18499,12 +18677,11 @@ \subsubsection{For-in} \end{normativeDartCode} \noindent -If the static type of $e$ is a top type -(\ref{superBoundedTypes}) +If the static type of $e$ is \DYNAMIC{} then $T$ is \code{Iterable<\DYNAMIC>}, otherwise $T$ is the static type of $e$. It is a compile-time error if $T$ is not assignable to -\code{Iterable<\DYNAMIC>}. +\code{Iterable}. \commentary{% It follows that it is a compile-time error @@ -18513,7 +18690,7 @@ \subsubsection{For-in} if $D$ is empty and \id{} is a final variable. Also, it is a dynamic error if $e$ has type \DYNAMIC, but $e$ evaluates to an instance of a type -which is not a subtype of \code{Iterable<\DYNAMIC>}.% +which is not a subtype of \code{Iterable}.% } @@ -18527,10 +18704,26 @@ \subsubsection{Asynchronous For-in} the keyword \AWAIT{} immediately preceding the keyword \FOR. \LMHash{}% -Let $D$ be derived from \syntax{?}. -Execution of a for-in statement, $f$, of the form -\code{\AWAIT{} \FOR{} ($D$ \id{} \IN{} $e$) $s$} -proceeds as follows: +Let $D$ be derived from \syntax{?}\ and +consider the asynchronous for-in statement, $f$, of the form +\code{\AWAIT\,\,\FOR\ ($D$\,\,\id\,\,\IN\,\,$e$)\ $s$}. + +\LMHash{}% +A compile-time error occurs if $D$ contains +the modifier \CONST{} or the modifier \LATE. +It is a compile-time error if an asynchronous for-in statement appears +inside a synchronous function (\ref{functions}). +It is a compile-time error if a traditional for loop (\ref{forLoop}) is +prefixed by the \AWAIT{} keyword. + +\rationale{% +An asynchronous loop would make no sense within a synchronous function, +for the same reasons that an await expression makes no sense +in a synchronous function.% +} + +\LMHash{}% +Execution of $f$ proceeds as follows: \LMHash{}% The expression $e$ is evaluated to an object $o$. @@ -18617,18 +18810,6 @@ \subsubsection{Asynchronous For-in} \LMHash{}% When $u$ is done, execution of $f$ completes normally. -\LMHash{}% -It is a compile-time error if an asynchronous for-in statement appears -inside a synchronous function (\ref{functions}). -It is a compile-time error if a traditional for loop (\ref{forLoop}) is -prefixed by the \AWAIT{} keyword. - -\rationale{% -An asynchronous loop would make no sense within a synchronous function, -for the same reasons that an await expression makes no sense -in a synchronous function.% -} - \subsection{While} \LMLabel{while} @@ -19233,15 +19414,50 @@ \subsection{Return} \end{grammar} \LMHash{}% -Consider a return statement $s$ of the form \code{\RETURN{} $e$?;}. +With an asynchronous non-generator function, +the returned object will always be a \code{Future<$T$>} for some $T$. +We need to introduce an auxiliary function +in order to be able to specify rules about said $T$ +without going into the details of the actual declared return type. +That function, \FutureValueTypeOfName, is defined as follows: + +\begin{itemize} +\item \DefEquals{\FutureValueTypeOf{$S$?}}{\FutureValueTypeOf{$S$}}, + for any $S$. +\item \DefEquals{\FutureValueTypeOf{Future}}{S}, for any $S$. +\item \DefEquals{\FutureValueTypeOf{FutureOr}}{S}, for any $S$. +\item \DefEquals{\FutureValueTypeOf{\VOID}}{\VOID}. +\item \DefEquals{\FutureValueTypeOf{\DYNAMIC}}{\DYNAMIC}. +\item Otherwise, \DefEquals{\FutureValueTypeOf{$S$}}{\code{Object?}}, + for any $S$. +\end{itemize} + +\commentary{% +Note that it is a compile-time error unless +the return type of an asynchronous non-generator function is +a supertype of \code{Future}, +which means that the last case will only be applied +when $S$ is \code{Object} or a top type.% +} + +\LMHash{}% +We define the \Index{future value type} of +an asynchronous non-generator function with declared return type $T$ +to be \FutureValueTypeOf{$T$}. + +\LMHash{}% +\BlindDefineSymbol{s,e,S,f,T}% +Consider a return statement $s$ of the form \code{\RETURN\,\,$e$?;}. Let $S$ be the static type of $e$, if $e$ is present, let $f$ be the immediately enclosing function, and let $T$ be the declared return type of $f$. \LMHash{}% -\Case{Synchronous non-generator functions} +\Case{Synchronous non-generator functions and factory constructors} Consider the case where $f$ is a synchronous non-generator function -(\ref{functions}). +(\ref{functions}) +or a generative constructor +(\ref{generativeConstructors}). % % Returning without an object is only ok for "voidy" return types. It is a compile-time error if $s$ is \code{\RETURN;}, @@ -19249,23 +19465,23 @@ \subsection{Return} % % Returning with an object in a void function % is only ok when the return type "voidy". -It is a compile-time error if $s$ is \code{\RETURN{} $e$;}, +It is a compile-time error if $s$ is \code{\RETURN\,\,$e$;}, $T$ is \VOID, and $S$ is neither \VOID, \DYNAMIC, nor \code{Null}. % % Returning void in a "non-voidy" function is an error. -It is a compile-time error if $s$ is \code{\RETURN{} $e$;}, -$T$ is neither \VOID, \DYNAMIC, nor \code{Null}, +It is a compile-time error if $s$ is \code{\RETURN\,\,$e$;}, +$T$ is neither \VOID{} nor \DYNAMIC, and $S$ is \VOID. % % Otherwise, returning an unassignable value is an error. -It is a compile-time error if $s$ is \code{\RETURN{} $e$;}, +It is a compile-time error if $s$ is \code{\RETURN\,\,$e$;}, $S$ is not \VOID, and $S$ is not assignable to $T$. \commentary{% -Note that $T$ cannot be \VOID, \DYNAMIC, or \code{Null} in the last case, -because all types are assignable to those types. +Note that $T$ cannot be \VOID{} or \DYNAMIC{} in the last case, +because then every type is assignable to $T$. An error will not be raised if $f$ has no declared return type, since the return type would be \DYNAMIC, to which every type is assignable. However, a synchronous non-generator function @@ -19281,54 +19497,44 @@ \subsection{Return} \LMHash{}% \Case{Asynchronous non-generator functions} Consider the case where $f$ is an asynchronous non-generator function -(\ref{functions}). +(\ref{functions}) +with future value type \DefineSymbol{T_v}. % -% Returning without an object is only ok for async-"voidy" return types. +% Returning without an object is only ok with a "voidy" future value type. It is a compile-time error if $s$ is \code{\RETURN;}, -unless \flatten{T} -(\ref{functionExpressions}) +unless $T_v$ is \VOID, \DYNAMIC, or \code{Null}. -% + \rationale{% An asynchronous non-generator always returns a future of some sort. If no expression is given, the future will be completed with the null object (\ref{null}) which motivates this rule.% } -% Returning with an object in an void async function only ok -% when that value is async-"voidy". -It is a compile-time error if $s$ is \code{\RETURN{} $e$;}, -\flatten{T} is \VOID, -and \flatten{S} is neither \VOID, \DYNAMIC, nor \code{Null}. + +% Returning with an object in a void async function is only ok +% when the returned value has a "voidy" type. +It is a compile-time error if $s$ is \code{\RETURN\,\,$e$;}, +$T_v$ is \VOID, +and \Flatten{$S$} is neither \VOID, \DYNAMIC, nor \code{Null}. % % Returning async-void in a "non-async-voidy" function is an error. -It is a compile-time error if $s$ is \code{\RETURN{} $e$;}, -\flatten{T} is neither \VOID, \DYNAMIC, nor \code{Null}, -and \flatten{S} is \VOID. +It is a compile-time error if $s$ is \code{\RETURN\,\,$e$;}, +$T_v$ is neither \VOID{} nor \DYNAMIC, +and \Flatten{$S$} is \VOID. % % Otherwise, returning an un-deasync-assignable value is an error. -It is a compile-time error if $s$ is \code{\RETURN{} $e$;}, -\flatten{S} is not \VOID, -and \code{Future<\flatten{S}>} is not assignable to $T$. - -\commentary{% -Note that \flatten{T} cannot be \VOID, \DYNAMIC, or \code{Null} -in the last case, -because then \code{Future<$U$>} is assignable to $T$ for \emph{all} $U$. -In particular, when $T$ is \code{FutureOr} -(which is equivalent to \code{Future}), -\code{Future<\flatten{S}>} is assignable to $T$ for all $S$. -This means that no compile-time error is raised, -but \emph{only} the null object (\ref{null}) -or an instance of \code{Future} can successfully be returned at run time. -This is not an anomaly, -it corresponds to the treatment of a synchronous function -with return type \code{Null}; -but tools may choose to give a hint that a downcast is unlikely to succeed. +It is a compile-time error if $s$ is \code{\RETURN\,\,$e$;}, +\Flatten{$S$} is not \VOID, +$S$ is not assignable to $T_v$, +and \Flatten{$S$} is not a subtype of $T_v$. +\commentary{% +Note that $T_v$ cannot be \VOID{} or \DYNAMIC{} in the last case, +because then every type is assignable to $T_v$. An error will not be raised if $f$ has no declared return type, -since the return type would be \DYNAMIC, -and \code{Future<\flatten{S}>} is assignable to \DYNAMIC{} for all $S$. +since the return type and future value type would be \DYNAMIC, +and $S$ is assignable to \DYNAMIC{} for all $S$. However, an asynchronous non-generator function that declares a return type which is not ``voidy'' must return an expression explicitly.% @@ -19342,7 +19548,7 @@ \subsection{Return} \LMHash{}% \Case{Generator functions} It is a compile-time error if a return statement of -the form \code{\RETURN{} $e$;} appears in a generator function. +the form \code{\RETURN\,\,$e$;} appears in a generator function. \rationale{% In the case of a generator function, the object returned by the function is @@ -19355,7 +19561,7 @@ \subsection{Return} \LMHash{}% \Case{Generative constructors} It is a compile-time error if a return statement of -the form \code{\RETURN{} $e$;} appears in a generative constructor +the form \code{\RETURN\,\,$e$;} appears in a generative constructor (\ref{generativeConstructors}). \rationale{% @@ -19371,65 +19577,60 @@ \subsection{Return} \EndCase \LMHash{}% -Executing a return statement \code{\RETURN{} $e$;} proceeds as follows: +The execution of a return statement proceeds as follows. + +\LMHash{}% +\Case{Synchronous non-generator functions and factory constructors} +Let $s$ be a statement of the form \code{\RETURN\,\,$e$;}. +\BlindDefineSymbol{s,e,f}% +Let $f$ be the immediately enclosing function, +and consider the case where $f$ is a synchronous non-generator +or a factory constructor. +Execution of $s$ proceeds as follows. \LMHash{}% -First the expression $e$ is evaluated, producing an object $o$. -Let $S$ be the run-time type of $o$ and -let $T$ be the actual return type of $f$ +The expression $e$ is evaluated to an object $o$. +A dynamic error occurs unless the dynamic type of $o$ is a subtype of +the actual return type of $f$ (\ref{actualTypes}). -If the body of $f$ is marked \ASYNC{} (\ref{functions}) -and $S$ is a subtype of \code{Future<\flatten{T}>} -then let $r$ be the result of evaluating \code{await $v$} -where $v$ is a fresh variable bound to $o$. -Otherwise let $r$ be $o$. -Then the return statement returns the object $r$ +Then the return statement $s$ completes returning $o$ (\ref{statementCompletion}). -%% TODO(eernst): We have some special cases with the dynamic semantics -%% specified above that we may wish to consider: -%% -%% Future foo() async { -%% return new Future.value(42); -%% } -%% -%% Statically we will allow this because `Future)>`, -%% i.e., `Future`, is assignable to `Future`. But at run time -%% we get an error because `Future` is is not a subtype of -%% `Future)>` = `Future`. We could have awaited -%% the value just because we're about to return a `Future`, and it would -%% then have succeeded. Are we doing the right thing? Might be fine, but -%% it seems surprising that the compiler will sometimes insert `await`, -%% and here we have to do it explicitly in order to succeed. -%% -%% Object /* or dynamic, etc. */ foo() async { -%% Object o; -%% if (someCondition) o = 42; else o = new Future.value(42); -%% return o; -%% } -%% -%% Here we will interject an `await` on the returned value whenever -%% we return a future (for all `S`). This means that we will `await o` -%% in some situations and not in others. It may surprise developers -%% that we can have this dynamic variation at a location in the code -%% where there is no static justification for expecting a future. +\commentary{% +The case where the evaluation of $e$ throws is covered by the general rule +which propagates the throwing completion from $e$ to $s$ to the function body.% +} \LMHash{}% -Let $U$ be the run-time type of $r$. -\begin{itemize} -\item - If the body of $f$ is marked \ASYNC{} (\ref{functions}) - % This error can occur due to implicit casts. - it is a dynamic type error if \code{Future<$U$>} is not a subtype of $T$. -\item - % This error can occur due to implicit casts. - Otherwise, it is a dynamic type error if $U$ is not a subtype of $T$. -\end{itemize} +\Case{Asynchronous non-generator functions} +Let $s$ be a statement of the form \code{\RETURN\,\,$e$;}. +\BlindDefineSymbol{s,e,f,T_v}% +Let $f$ be the immediately enclosing function, +and consider the case where $f$ is an asynchronous non-generator +with future value type $T_v$. +Execution of $s$ proceeds as follows. + +\LMHash{}% +The expression $e$ is evaluated to an object $o$. +If the run-time type of $o$ is a subtype of \code{Future<$T_v$>}, +let \code{v} be a fresh variable bound to $o$ and +evaluate \code{\AWAIT{} v} to an object $r$; +otherwise let $r$ be $o$. +A dynamic error occurs unless the dynamic type of $r$ +is a subtype of the actual value of $T_v$ +(\ref{actualTypes}). +Then the return statement $s$ completes returning $r$ +(\ref{statementCompletion}). +\EndCase + +\commentary{% +The cases where $f$ is a generator or a generative constructor cannot occur, +because $s$ is then a compile-time error.% +} \LMHash{}% -Executing a return statement with no expression, -\code{\RETURN;} -returns without an object +Execution of a return statement of the form \code{\RETURN;} +causes no computations and then returns without an object (\ref{statementCompletion}). @@ -19834,12 +20035,12 @@ \section{Libraries and Scripts} \alt \alt \alt - \alt \EXTERNAL{} `;' - \alt \EXTERNAL{} `;' - \alt \EXTERNAL{} `;' - \alt - \alt - \alt + \alt \EXTERNAL{} `;' + \alt \EXTERNAL{} `;' + \alt \EXTERNAL{} `;' + \alt + \alt + \alt \alt (\FINAL{} | \CONST{}) ? `;' \alt \LATE{} \FINAL{} ? `;' \alt \LATE? `;' @@ -20296,7 +20497,7 @@ \subsubsection{Semantics of Imports} \item For every top level function $f$ named \id{} in \NamespaceName{\metavar{import},i}, - a corresponding function named \id{} with the same signature as $f$. + a corresponding function named \id{} with the same function header as $f$. % This error can occur because being-loaded is a dynamic property. Calling the function results in a dynamic error that occurs before any actual arguments are evaluated. @@ -20306,13 +20507,14 @@ \subsubsection{Semantics of Imports} \item For every top level getter $g$ named \id{} in \NamespaceName{\metavar{import},i}, - a corresponding getter named \id{} with the same signature as $g$. + a corresponding getter named \id{} with the same function header as $g$. % This error can occur because being-loaded is a dynamic property. Calling the getter results in a dynamic error. \item For every top level setter $s$ named \code{\id=} in \NamespaceName{\metavar{import},i}, - a corresponding setter named \code{\id=} with the same signature as $s$. + a corresponding setter named \code{\id=} with + the same function header as $s$. % This error can occur because being-loaded is a dynamic property. Calling the setter results in a dynamic error that occurs before the actual argument is evaluated. @@ -20338,7 +20540,8 @@ \subsubsection{Semantics of Imports} \NamespaceName{\metavar{loaded}}, with bindings as described below for immediate imports. In addition, \NamespaceName{\metavar{loaded}} -maps \code{loadLibrary} to a function with the same signature as before, +maps \code{loadLibrary} to a function with +the same function header as before, and so it is possible to invoke \code{$p$.loadLibrary()} again, which will always succeed. If a call fails, the library has not been loaded, @@ -21103,7 +21306,7 @@ \subsection{Static Types} and it does not denote a declaration of a type. \item $T$ denotes a type variable, - but it occurs in the signature or body of a static member. + but it occurs in the function header or body of a static member. \item $T$ is a parameterized type of the form \code{$G$<$S_1, \ldots,\ S_n$>}, and $G$ is malformed, @@ -21346,8 +21549,8 @@ \subsection{Type Aliases} \LMHash{}% For historic reasons, a type alias can have two more forms, derived using \synt{functionTypeAlias}. -Let \DefineSymbol{S?} be a term which is empty or derived from \synt{type}, -and similarly for \DefineSymbol{T_j?} for any $j$. +Let \DefineSymbol{S?}\ be a term which is empty or derived from \synt{type}, +and similarly for \DefineSymbol{T_j?}\ for any $j$. The two older forms are then defined as follows: \LMHash{}% @@ -21619,7 +21822,7 @@ \subsection{Type Aliases} \commentary{For example:} \begin{dartCode} -\CLASS{} C \{\} +\CLASS{} C \{\} \TYPEDEF{} F = C; \TYPEDEF{} G = Z; \\ @@ -22300,164 +22503,408 @@ \subsubsection{Additional Subtyping Concepts} } -\subsection{Type Normalization} -\LMLabel{typeNormalization} +\subsection{Type Nullability} +\LMLabel{typeNullability} \LMHash{}% -Some Dart types are mutual subtypes, -but are not considered to be the same type. +We say that a type $T$ is \IndexCustom{nullable}{type!nullable} +if{}f \SubtypeNE{\code{Null}}{T}, +and that $T$ is +\IndexCustom{potentially non-nullable}{type!potentially non-nullable} +if{}f $T$ is not nullable. \commentary{% -For instance, \DYNAMIC{} and \code{Object?} are subtypes of each other, -but they are not considered to be the same type. -This is useful because it allows us to treat two expressions very differently -even though the set of objects that they could evaluate to is exactly the same. -In particular, a member access on a receiver of type \code{Object?} -can only call \code{hashCode} and a few other members, -but a member access on a receiver of type \DYNAMIC{} can invoke -any member as a method accepting any actual list of arguments, -and as a getter or as a setter, -and it will be checked at run time that said invocation is possible.% +Nullable types are types which are +definitively known to include the null object, +regardless of the value of any type variables. +This is equivalent to the syntactic criterion that $T$ is any of: + +\begin{itemize}[itemsep=-0.5ex] +\item \VOID. +\item \DYNAMIC. +\item \code{Null}. +\item \code{$S$?}, for any type $S$. +\item \code{FutureOr<$S$>}, for any nullable type $S$. +\end{itemize}% +} + +\LMHash{}% +We say that a type $T$ is \Index{non-nullable} +if{}f \SubtypeNE{T}{\code{Object}}, +and that $T$ is +\IndexCustom{potentially nullable}{type!potentially nullable} +if{}f $T$ is not non-nullable. + +\commentary{% +Non-nullable types are types which are definitively known to +\emph{not} include the null object, +regardless of the value of any type variables. +This is equivalent to the syntactic criterion that $T$ is any of: + +\begin{itemize}[itemsep=-0.5ex] +\item \code{Never}. +\item Any function type. +\item The type \FUNCTION. +\item Any interface type except \code{Null}. +\item \code{FutureOr<$S$>}, for any non-nullable type $S$. +\item Any type variable $X$ whose bound is non-nullable. +\item Any type of the form \code{$X$\,\&\,$S$}, where + $X$ is a type variable and $S$ is non-nullable. +\end{itemize}% +} + +\commentary{% +Some types are neither nullable nor non-nullable. +For example, a type variable $X$ whose bound is nullable, say \code{int?}, +is neither nullable nor non-nullable: +If the value of $X$ is \code{int} then it does not include the null object, +and hence $X$ cannot be a nullable type. +Conversely, if the value of $X$ is \code{int?}\ or \code{Null} +then it \emph{does} include the null object, +so it cannot be non-nullable. + +It follows that all four concepts are distinct: +The same type $X$ is potentially nullable, but not nullable, +and $X$ is also potentially non-nullable, but not non-nullable.% +} + +\LMHash{}% +We define a function \NonNullTypeName, +which maps a type $T$ to the corresponding non-nullable type, +to the extent said non-nullable type is expressible. + +\commentary{% +For example, \code{int?}\ is mapped to \code{int}, +a non-nullable result, +but \DYNAMIC{} is mapped to \DYNAMIC, +even though that is still a nullable type. +If $X$ is a type variable whose bound is nullable, +\code{FutureOr<$X$>?}\ is mapped to \code{FutureOr<$X$>}, +which is still a potentially nullable type. +We cannot map it to \code{FutureOr<\NonNullType{$X$}>} +because that would be unsound: +An expression of type \code{FutureOr<$X$>?} +may evaluate to an instance of \code{Future<$X$>} (which is not \NULL), +but \code{Future<$X$>} might not be a subtype +of \code{FutureOr<\NonNullType{$X$}>}. +Also, this could introduce an intersection type which is a type argument, +and that violates the constraints on intersection types +(\ref{intersectionTypes}), +so it is lucky that we don't need it.% } +\begin{itemize} +\item + \DefEquals{\NonNullType{Null}}{\code{Never}}. +\item + \DefEquals{\NonNullType{$T$?}}{\NonNullType{$T$}}, + for any type $T$. +\item + \DefEquals{\NonNullType{$X$}}{\code{$X$\,\&\,\NonNullType{$B$}}}, + where $X$ is a type variable with bound $B$, + such that $\NonNullType{$B$} \not= B$. +\item + \DefEquals{\NonNullType{$X$\,\&\,$T$}}{\code{$X$\,\&\,\NonNullType{$T$}}}, + for any type $T$. +\item + Otherwise, \DefEquals{\NonNullType{$T$}}{T}, + for any type $T$. +\end{itemize} + \LMHash{}% -This section defines a normalization procedure on types -which yields a canonical representation for any given type. +Let $T$ be a potentially nullable type such that +\NormalizedTypeOf{$T$} is neither \DYNAMIC{} nor \VOID. +The \IndexCustom{interface}{interface!of potentially nullable type} +of $T$ is the interface of \code{Object}. \commentary{% -In other words, subtyping on Dart types is a preorder, -but subtyping on normalized Dart types is a total order.% +For example, even though \code{$e_1$.isEven} is allowed +when the type of $e_1$ is \code{int}, +\code{$e_2$.isEven} is a compile-time error +when the type of $e_2$ is \code{int?}\ or +a type variable $X$ whose bound is \code{int?}.% } + +\subsection{Type Normalization} +\LMLabel{typeNormalization} + +\LMHash{}% +This section specifies a function that normalizes Dart types. +In this section it is assumed that type inference and +instantiation to bound has taken place, +and that no type under consideration is a compile-time error. + \LMHash{}% +For the definitions below, we need the following concept: An \IndexCustom{atomic type}{type!atomic} is a term derived from \synt{typeName} -that denotes a type. +that denotes a type which is not a type alias. \commentary{% For instance, \DYNAMIC{} is an atomic type, and so is \code{prefix.MyClass}, -if it denotes a class.% +if it denotes a class, +but \code{List} and \code{\VOID\,\,\FUNCTION()} are not atomic.% } -\newlength{\FunCr} -\setlength{\FunCr}{1.5ex} +\LMHash{}% +Some Dart types are mutual subtypes, +but are not considered to be the same type. +Other Dart types are mutual subtypes, +even though they have a different syntactic structure. +Type normalization erases the latter distinction. -\begin{figure}[t] - \begin{minipage}[c]{\textwidth} - \begin{displaymath} - \begin{array}{ll} - \mbox{\bf Unaliased argument} & \mbox{\bf Result}\\[\FunCr] - \hline - \mbox{If $T$ is atomic: }T & T\\[\FunCr] - % - \code{FutureOr<$T$>} & - \left\{ - \begin{array}{l} - S\mbox{, if $S$ is a top type or \code{Object}}\\ - \code{Future}\mbox{, if $S$ is \code{Never}}\\ - \code{Future?}\mbox{, if $S$ is \code{Null}}\\ - \code{FutureOr}\mbox{, otherwise} - \end{array} - \right.\\[1ex] - \mbox{where $S$ is \NormalizedTypeOf{$T$}}\\[\FunCr] - % - \code{$T$?} & - \left\{ - \begin{array}{l} - S\mbox{, if $S$ is a top type}\\ - \code{Null}\mbox{, if $S$ is \code{Never} or \code{Null}}\\ - S\mbox{, if $S$ is \code{FutureOr<$R$>} where $R$ is nullable}\\ - \code{$R$?}\mbox{, if $S$ is \code{$R$?} for some $R$}\\ - \code{$S$?}\mbox{, otherwise} - \end{array} - \right.\\ - \mbox{where $S$ is \NormalizedTypeOf{$T$}}\\[\FunCr] - % - \code{$X$\,\,\EXTENDS\,\,$T$} & - \left\{ - \begin{array}{l} - \code{Never}\mbox{,if $T$ is \code{Never}}\\ - \code{Never}\mbox{, - if $T$ is a type variable $Y$, - and \NormalizedTypeOf{$Y$} is \code{Never}}\\ - \code{$X$\,\,\EXTENDS\,\,$T$}\mbox{, otherwise} - \end{array} - \right.\\ - \mbox{}\\[\FunCr] - % - \code{$X$\,\,\&\,\,$T$} & - \left\{ - \begin{array}{l} - \code{Never}\mbox{, if $S$ is \code{Never}}\\ - X\mbox{, if $S$` is $X$ or a top type}\\ - X\mbox{, if \SubtypeStd{\NormalizedTypeOf{$B$}}{S}, where $B$ is the bound of $X$}\\ - \code{$X$\,\,\&\,\,$S$}\mbox{, otherwise} - \end{array} - \right.\\ - \mbox{where $S$ is \NormalizedTypeOf{$T$}}\\[\FunCr] - % - \code{$C$<\List{T}{1}{k}>} & \code{$C$<\List{R}{1}{k}>}\\ - \mbox{where $R_i$ is \NormalizedTypeOf{$T_i$},}\\ - \mbox{\quad{}for $i \in 1 .. k$}\\[\FunCr] - % - % !!!TODO: Generalize this to handle all parameter list shapes. - \code{$R$ \FUNCTION<$X$\,\,\EXTENDS,\,$B$>($S$)} & - \code{$R_1$ Function<$X$\,\,\EXTENDS\,\,$B_1$>($S_1$)}\\ - \mbox{where $R_1$ is \NormalizedTypeOf{$R$},}\\ - \mbox{\quad{}$B_1$ is \NormalizedTypeOf{$B$},}\\ - \mbox{\quad{}and $S_1$ is \NormalizedTypeOf{$S$}} - \end{array} - \end{displaymath} - \end{minipage} - \caption{Definition of the type function \NormalizedTypeOfName: - For a given type $T_0$, - let $T$ be the transitive alias expansion of $T_0$ - (\ref{typedef}), - then look up $T$ in the left column of the table in this figure - such that the associated condition is satisfied. - Then \NormalizedTypeOf{$T$} is as shown in the right hand column.} -\end{figure} +\commentary{% +For instance, \DYNAMIC{} and \code{Object?}\ are subtypes of each other, +but they are not considered to be the same type. +This is useful because it allows us to treat two expressions very differently +even though the set of objects that they could evaluate to +is exactly the same. ----------------------------------------------------------------------- +Similarly, \code{FutureOr} and \code{Object} are mutual subtypes, +and so are \code{Never} and a type variable $X$ whose bound is \code{Never}. +The normalization which is described here will preserve +distinctions like the former (such as \DYNAMIC{} and \code{Object?}), +and it will eliminate distinctions like the latter +(such as \code{Never} and $X$). -!!!TODO!!! -Note that there is currently no place in the type system where normalization can -apply to intersection types (promoted types). The rule is included here for -completeness. +In particular, \SubtypeNE{S}{T} and \SubtypeNE{T}{S} holds if and only if +\NormalizedTypeOf{$T$} is syntactically equal to \NormalizedTypeOf{$S$}, +modulo replacement of atomic top types, +and modulo replacement of terms derived from \synt{typeName} +denoting the same type +(such as \code{List{}>} and \code{List{}>}).% +} -We assume that type aliases are fully expanded, and that prefixed types are -resolved to a canonical name. +\LMHash{}% +The function \Index{\NormalizedTypeOfName} is then defined as follows: +\BlindDefineSymbol{T_a,T_u,T_r}% +Let $T_a$ be a type +(\commentary{where `a' stands for `argument'}). +Let $T_u$ be the transitive alias expansion of $T_a$ +(\commentary{`u' for `unaliased'}, \ref{typedef}). +Then \DefEquals{\NormalizedTypeOf{$T_a$}}{T_r} +(\commentary{`r' for result}), +where $T_r$ is determined as follows: -The **NORM** relation defines the canonical representative of classes of -equivalent types. In the absence of legacy (*) types, it should be the case -that for any two types which are mutual subtypes, their normal forms are -syntactically identical up to identification of top types (\DYNAMIC, \VOID, -\code{Object?}). +\begin{itemize} +\item + If $T_u$ is atomic then $T_r$ is $T_u$. +\item If $T_u$ is of the form \code{FutureOr<$T$>} + then let $S$ be \NormalizedTypeOf{$T$} and then: + \begin{itemize} + \item If $S$ is a top type or \code{Object} then $T_r$ is $S$. + \item If $S$ is \code{Never} then $T_r$ is \code{Future}. + \item If $S$ is \code{Null} then $T_r$ is \code{Future?}. + \item Otherwise, $T_r$ is \code{FutureOr}. + \end{itemize} +\item If $T_u$ is of the form \code{$T$?}\ then + let $S$ be \NormalizedTypeOf{$T$} and then: + \begin{itemize} + \item If $S$ is a top type then $T_r$ is $S$. + \item If $S$ is \code{Never} or \code{Null} then + $T_r$ is \code{Null}. + \item If $S$ is \code{FutureOr<$R$>} where $R$ is nullable then + $T_r$ is $S$. + \item If $S$ is \code{$R$?}\ for some $R$ then $T_r$ is \code{$R$?}. + \item Otherwise, $T_r$ is \code{$S$?}. + \end{itemize} +\item If $T_u$ is a type variable $X$ with bound $B$ then: + \begin{itemize} + \item If $B$ is \code{Never} then $T_r$ is \code{Never}. + \item If $B$ is a type variable $Y$ + and \NormalizedTypeOf{$Y$} is \code{Never} + then $T_r$ is \code{Never}. + \item Otherwise, $T_r$ is $X$. + \end{itemize} +\item If $T_u$ is of the form \code{$X$\,\&\,$T$} + where $X$ is a type variable with bound $B$ + then let $S$ be \NormalizedTypeOf{$T$} and then: + \begin{itemize} + \item If $S$ is \code{Never} then $T_r$ is \code{Never}. + \item If $S$ is $X$ or a top type then $T_r$ is $X$. + \item If \SubtypeNE{\NormalizedTypeOf{$B$}}{S} then $T_r$ is $X$. + \item Otherwise, $T_r$ is \code{$X$\,\&\,$S$}. + \end{itemize} +\item If $T_u$ is of the form \code{$C$<\List{T}{1}{k}>} + then $T_r$ is \code{$C$<\List{R}{1}{k}>}, + where $R_i$ is \NormalizedTypeOf{$T_i$}, for $i \in 1 .. k$. +\item If $T_u$ is of the form + \FunctionTypePositionalStd{T_0} + + \noindent + then $T_r$ is + \FunctionTypePositional{R_0}{ }{X}{B}{s}{R}{n}{k} + + \noindent + where $R_i$ is \NormalizedTypeOf{$T_i$} for $i \in 0 .. n+k$. +\item If $T_u$ is of the form + \FunctionTypeNamedStd{T_0} + + \noindent + where $r_j$ is either \REQUIRED{} or empty + then $T_r$ is + \noindent + \FunctionTypeNamed{R_0}{ }{X}{B}{s}{R}{n}{x}{k} + + \noindent + where $R_i$ is \NormalizedTypeOf{$T_i$} for $i \in 0 .. n+k$. +\end{itemize} + +\commentary{% +There is currently no place in the type system +where normalization can apply to intersection types +(\code{$X$\,\&\,$T$}). +The rule is included here for completeness. +Normalization is based on the following reductions: + +\begin{displaymath} + \begin{array}{l@{\quad\mapsto\quad}l} + \code{T??} & \code{T?}\\ + \code{\VOID?} & \code{\VOID}\\ + \code{\DYNAMIC?} & \code{\DYNAMIC}\\ + \code{Null?} & \code{Null}\\ + \code{Never?} & \code{Null}\\ + \code{$X$\,\,\EXTENDS\,\,Never} & \code{Never}\\ + \code{FutureOr<$T$>} & \code{$T$}% + \mbox{, if \SubtypeNE{\code{Future<$T$>}}{T}}\\ + \code{FutureOr<$T$>} & \code{Future<$T$>}% + \mbox{, if \SubtypeNE{T}{\code{Future<$T$>}}}\\ + \code{$X$\,\,\&\,\,$T$} & \code{$T$}\mbox{, if \SubtypeNE{T}{X}}\\ + \code{$X$\,\,\&\,\,$T$} & \code{$X$}\mbox{, if \SubtypeNE{X}{T}} + \end{array} +\end{displaymath} +} + + +\subsection{Types Bounded by Types} +\LMLabel{typesBoundedByTypes} + +\LMHash{}% +For a given type $T_0$, we introduce the notion of a +\IndexCustom{$T_0$ bounded type}{type!T0 bounded}: +$T_0$ itself is $T_0$ bounded; +if $B$ is $T_0$ bounded and +$X$ is a type variable with bound $B$ +then $X$ is $T_0$ bounded; +finally, if $B$ is $T_0$ bounded and +$X$ is a type variable +then $X \& B$ is $T_0$ bounded. + +\LMHash{}% +In particular, a +\IndexCustom{\DYNAMIC{} bounded type}{type!dynamic bounded} +is either \DYNAMIC{} itself +or a type variable whose bound is \DYNAMIC{} bounded, +or an intersection whose second operand is \DYNAMIC{} bounded. +Similarly for a +\IndexCustom{\FUNCTION{} bounded type}{type!function bounded}. + +\LMHash{}% +A +\IndexCustom{function-type bounded type}{type!function-type bounded} +is a type $T$ which is $T_0$ bounded where $T_0$ is a function type +(\ref{functionTypes}). +A function-type bounded type $T$ has an +\Index{associated function type} +which is the unique function type $T_0$ such that $T$ is $T_0$ bounded. + + +\subsection{Class Building Types} +\LMLabel{classBuildingTypes} + +\LMHash{}% +Some types known as +\IndexCustom{class building types}{type!class building} +can be used in specific contexts where a class is required +(\commentary{e.g., when specifying a superinterface of a class}). +We say that a type $T$ is a class building type +if none of the following criteria is satisfied: -This is based on the following equations: \begin{itemize} -\item \code{T?? == T?} -\item \code{Null? == Null} -\item \code{Never? == Null} -\item \code{\DYNAMIC? == \DYNAMIC} -\item \code{\VOID? == \VOID} -\item \code{FutureOr<$T$> == $T$}, if \SubtypeStd{\code{Future<$T$>}}{T} -\item \code{FutureOr<$T$> == Future<$T$>}, if \SubtypeStd{T}{\code{Future<$T$>}} -\item \code{$X$\,\,\EXTENDS\,\,Never == Never} -\item \code{$X$\,\,\&\,\,$T$ == $T$}, if \SubtypeStd{T}{X} -\item \code{$X$\,\,\&\,\,$T$ == $X$}, if \SubtypeStd{X}{T} +\item $T$ is \VOID{} (\ref{typeVoid}). +\item $T$ is \DYNAMIC{} (\ref{typeDynamic}). +\item $T$ is \code{FutureOr<$S$>} for some type $S$ (\ref{typeFutureOr}). +\item + % TODO(eernst): If we introduce sealed classes we will have an item + % for those, and we may wish to rephrase this. + $T$ is \code{Never}, \code{Null}, \code{num}, \code{int}, \code{double}, + \code{bool}, or \code{String}. +\item $T$ is \code{$S$?} for some type $S$. +\item $T$ is a type variable (\ref{generics}). +\item $T$ is a function type (\ref{functionTypes}). +\item $T$ is a type alias whose transitive alias expansion + (\ref{typedef}) does not denote a class. +\item $T$ is an enumerated type (\ref{enums}). +\item $T$ is a deferred type (\ref{staticTypes}). \end{itemize} ----------------------------------------------------------------------- + +\subsection{Interface Types} +\LMLabel{interfaceTypes} + +\LMHash{}% +Interface types can be used to access +a specific set of instance members. +Let $T$ be a type. +We say that $T$ is an \Index{interface type} if{}f +$T$ is of the form \code{$C$<\List{T}{1}{k}>}, +where $C$ denotes a class different from \code{Never} or a mixin, +and \List{T}{1}{k} are arbitrary types. + +\commentary{% +Non-generic classes are included because we can have $k = 0$. + +In particular, the following types are \emph{not} interface types: +\VOID, \DYNAMIC, \FUNCTION, \code{FutureOr<$T$>} for any $T$, \code{Never}, +any function type, any type variable, any intersection type, +and any type of the form \code{$T$?}.% +} + + +\subsection{Intersection Types} +\LMLabel{intersectionTypes} + +\LMHash{}% +Dart supports a very limited notion of intersection types. +They only occur at compile-time, i.e., they are not reified at run time. +They arise during static analysis, +but cannot be specified directly in source code. +They only admit a left operand which is a type variable. +The right operand is required to be a subtype of +the bound of said type variable. + +\commentary{% +An intersection type will never occur as a nested type, that is, +it will never occurs as +an actual type argument in a parameterized type, +as a parameter type or a return type in a function type, +as the right operand of another intersection type, +or as the operand of the nullable type operator \lit{?}.% +} + \LMHash{}% -This defines a procedure \NormalizedTypeOfName{} -such that \NormalizedTypeOf{$T$} is syntactically -equal to \NormalizedTypeOf{$S$} modulo replacement of primitive top types iff -\SubtypeStd{S}{T} and \SubtypeStd{T}{S}. +Assume that $X$ is a type variable with bound $B$, +and $T$ is a type such that \SubtypeNE{T}{B}. +We then use the syntax \code{$X$\,\&\,$T$} to designate the intersection type +that intersects $X$ and $T$. +\commentary{% +Note again that \code{$X$\,\&\,$T$} cannot occur in source code, +but it can emerge as the type of an expression. +Intersection types in Dart are used to hold the information that +the object which is the value of a specific variable +has a type which is a type variable $X$ with bound $B$, +and it is also known to have some other type $T$ which is +a subtype of $B$.% +} +\LMHash{}% +Let $S$ be a type of the form \code{$X$\,\&\,$T$}. +The \IndexCustom{interface}{interface!of intersection type} +of $S$ is the interface of $T$. \subsection{Type \code{Never}} @@ -22488,6 +22935,25 @@ \subsection{Type \code{Never}} can be used to hold any function that accepts a single, positional argument.% } +\commentary{% +With a receiver whose static type is \code{Never}, +it is not an error to invoke any method, setter, or getter, or to +tear off any method. +In other words, \code{Never} is considered to have all members, +with all signatures. + +Implementations that provide feedback about dead or unreachable code +are encouraged to indicate +that arguments passed to any such invocation +will not be evaluated at run time. + +It is not an error to apply an expression of type \code{Never} +in the function position of a function call. +Implementations that provide feedback about dead or unreachable code +are encouraged to indicate that any arguments to the call +will not be evaluated at run time.% +} + \subsection{Type \code{Null}} \LMLabel{typeNull} @@ -22524,8 +22990,8 @@ \subsection{Type \code{Null}} \rationale{% \code{Null} is mainly used implicitly in -a type of the form \code{$T$?} for some $T$. -If $e$ is an expression of type \code{$T$?} that evaluates to an object $o$, +a type of the form \code{$T$?}\ for some $T$. +If $e$ is an expression of type \code{$T$?}\ that evaluates to an object $o$, then $o$ can be an instance of a subtype of $T$, or it can be the null object. The latter situation is commonly interpreted to mean that @@ -22580,7 +23046,7 @@ \subsection{Function Types} A function object is always an instance of some class $C$ that implements the class \FUNCTION{} (\ref{functionType}), and which has a method named \CALL, -whose signature is the function type $C$ itself. +whose header is such that its function type is $C$ itself. \commentary{% Consequently, all function types are subtypes of \FUNCTION{} (\ref{subtypes}).% @@ -23608,284 +24074,26 @@ \section{Null safety} %% !!!TODO!!! %% !!!At the end: Search Null, change to Never where appropriate %% !!!Search all `TODO`.*null -\subsection{Static semantics} -\subsubsection{Type normalization} - -\subsubsection{Future flattening} - -The \FlattenName{} function is modified as follows: - -\begin{itemize} -\item if $T$ is \code{S?} then \Flatten{$T$} = \code{\Flatten{$S$}?}. -\item otherwise if $T$ is \code{FutureOr<$S$>} then \Flatten{$T$} = $S$. -\item otherwise if \code{$T$ <: Future} then let $S$ be a type such that - \code{$T$ <: Future<$S$>} and for all $R$, - if \code{$T$ <: Future<$R$>} then \code{$S$ <: $R$}; then \Flatten{$T$} = $S$. -\item otherwise \Flatten{$T$} = $T$ -\end{itemize} - -\subsubsection{The future value type of an asynchronous non-generator function} - -\commentary{% -We specify a concept which corresponds to the static type of objects which may -be contained in the Future object returned by an async function with a given -declared return type.% -} - -Let $f$ be an asynchronous non-generator function with declared return type -$T$. Then the \Index{future value type} of $f$ is \FutureValueTypeOf{$T$}. -The function \FutureValueTypeOfName{} is defined as follows: - -\begin{itemize} -\item \FutureValueTypeOf{$S$?} = \FutureValueTypeOf{$S$}, for all $S$. -\item \FutureValueTypeOf{Future} = $S$, for all $S$. -\item \FutureValueTypeOf{FutureOr} = $S$, for all $S$. -\item \FutureValueTypeOf{\VOID} = \VOID. -\item \FutureValueTypeOf{\DYNAMIC} = \DYNAMIC. -\item Otherwise, for all $S$, \FutureValueTypeOf{$S$} = \code{Object?}. -\end{itemize} - -\commentary{% -Note that it is a compile-time error unless the return type of an asynchronous -non-generator function is a supertype of \code{Future}, which means that -the last case will only be applied when $S$ is \code{Object} or a top type.% -} - -\subsubsection{Return statements} - -The static analysis of return statements is changed in the following -way, where $T$ is the declared return type and $S$ is the static type of -the expression $e$. - -At line 15477 about synchronous non-generator functions, the text is changed as follows: - -\Case{Spec change} -It is a compile-time error if $s$ is \code{\RETURN{} $e$;}, -$T$ is neither \VOID{} nor \DYNAMIC, -and $S$ is \VOID. -\EndCase - -\commentary{% -Comparing to Dart before null-safety, this means that it is no longer allowed -to return a void expression in a regular function if the return type is -\code{Null}.% -} - -At line 15507 about an asynchronous non-generator function with future value type $T_v$, -the text is changed as follows: - -\Case{Spec change} -It is a compile-time error if $s$ is \code{\RETURN;}, -unless $T_v$ -is \VOID, \DYNAMIC, or \code{Null}. -% -It is a compile-time error if $s$ is \code{\RETURN\,\,$e$;}, -$T_v$ is \VOID, -and \flatten{S} is neither \VOID, \DYNAMIC, \code{Null}. -% -It is a compile-time error if $s$ is \code{\RETURN\,\,$e$;}, -$T_v$ is neither \VOID{} nor \DYNAMIC, -and \flatten{S} is \VOID. -% -It is a compile-time error if $s$ is \code{\RETURN\,\,$e$;}, -\flatten{S} is not \VOID, -$S$ is not assignable to $T_v$, -and flatten{S} is not a subtype of $T_v$. -\EndCase - -\commentary{% -Comparing to Dart before null-safety, this means that it is no longer allowed -to return an expression whose flattened static type is \VOID{} in an \code{async} -function with future value type \code{Null}; nor is it allowed, in an \code{async} -function with future value type \VOID, to return an expression whose flattened -static type is not \VOID, \DYNAMIC, or \code{Null}. Conversely, it is -allowed to return a future when the future value type is a suitable future; -for instance, we can have \code{return Future.value(42)} in an \code{async} function -with declared return type \code{Future>}. Finally, let $S$ be -\code{Future} or \code{FutureOr}; it is then no longer allowed to -return an expression with static type $S$, unless the future value type is a -supertype of $S$. This differs from Dart before null-safety in that it was -allowed to return an expression of these types with a declared return type -of the form \code{Future} for any $T$.% -} - -The dynamic semantics specified at line 15597 -is changed as follows, where $f$ is the enclosing function with declared -return type $T$, and $e$ is the returned expression: - -\Case{Spec change} -When $f$ is a synchronous non-generator, evaluation proceeds as follows: -The expression $e$ is evaluated to an object $o$. -A dynamic error occurs unless the dynamic type of $o$ is a subtype of -the actual return type of $f$ -(\ref{actualTypes}). -Then the return statement $s$ completes returning $o$ -(\ref{statementCompletion}). - -\commentary{% -The case where the evaluation of $e$ throws is covered by the general rule -which propagates the throwing completion from $e$ to $s$ to the function body.% -} - -When $f$ is an asynchronous non-generator with future value type $T_v$ -(\ref{functions}), evaluation proceeds as follows: -The expression $e$ is evaluated to an object $o$. -If the run-time type of $o$ is a subtype of \code{Future<$T_v$>}, -let \code{v} be a fresh variable bound to $o$ and -evaluate \code{\AWAIT{} v} to an object $r$; -otherwise let $r$ be $o$. -A dynamic error occurs unless the dynamic type of $r$ -is a subtype of the actual value of $T_v$ -(\ref{actualTypes}). -Then the return statement $s$ completes returning $r$ -(\ref{statementCompletion}). - -\commentary{% -The cases where $f$ is a generator cannot occur, -because in that case $s$ is a compile-time error.% -} -\EndCase - \subsubsection{Static errors} -\paragraph{Nullability definitions} - -We say that a type $T$ is \Index{nullable} if \code{Null <: $T$} and not \code{$T$ <: Object}. -This is equivalent to the syntactic criterion that $T$ is any of: - -\begin{itemize} -\item \code{Null} -\item \code{$S$?} for some $S$ -\item \code{FutureOr<$S$>} for some $S$ where $S$ is nullable -\item \DYNAMIC{} -\item \VOID{} -\end{itemize} - -Nullable types are types which are definitively known to be nullable, regardless -of instantiation of type variables. - -We say that a type $T$ is \Index{non-nullable} if \code{$T$ <: Object}. -This is equivalent to the syntactic criterion that $T$ is any of: - -\begin{itemize} -\item \code{Never} -\item Any function type (including \FUNCTION) -\item Any interface type except \code{Null}. -\item \code{FutureOr<$S$>} where $S$ is non-nullable -\item \code{$X$ \EXTENDS\ $S$} where $S$ is non-nullable -\item \code{$X$ \& $S$} where $S$ is non-nullable -\end{itemize} - -Non-nullable types are types which are either definitively known to be -non-nullable regardless of instantiation of type variables. - -Note that there are types which are neither nullable nor non-nullable. For -example \code{X \EXTENDS\ T} where $T$ is nullable is neither nullable nor -non-nullable. - -We say that a type $T$ is \Index{strictly non-nullable} if \code{$T$ <: Object} and not -\code{Null <: $T$}. This is equivalent to the syntactic criterion that $T$ is any of: - -\begin{itemize} -\item \code{Never} -\item Any function type (including \code{Function}) -\item Any interface type except \code{Null}. -\item \code{FutureOr} where $S$ is strictly non-nullable -\item \code{X \EXTENDS\ S} where $S$ is strictly non-nullable -\item \code{X \& S} where $S$ is strictly non-nullable -\end{itemize} -We say that a type $T$ is \Index{potentially nullable} if $T$ is not non-nullable. -Note that this is different from saying that $T$ is nullable. For example, a -type variable \code{X \EXTENDS\ Object?} is a type which is potentially nullable but -not nullable. The potentially nullable types include all of the types -which are either definitely nullable, potentially instantiable to a nullable -type. - -We say that a type $T$ is \Index{potentially non-nullable} if $T$ is not nullable. -Note that this is different from saying that $T$ is non-nullable. For example, -a type variable \code{$X$ \EXTENDS\ Object?} is a type which is potentially non-nullable -but not non-nullable. +\paragraph{Nullability definitions} \paragraph{Errors and Warnings} -It is an error to call a method, setter, getter or operator on an expression -whose type is potentially nullable and not \DYNAMIC, except for the methods, -setters, getters, and operators on \code{Object}. - -It is an error to read a field or tear off a method from an expression whose -type is potentially nullable and not \DYNAMIC, except for the methods and -fields on \code{Object}. - -It is an error to call an expression whose type is potentially nullable and not -\DYNAMIC. - -It is an error if a top level variable or static variable with a non-nullable -type has no initializer expression unless the variable is marked with a \LATE{} -or \EXTERNAL{} modifier. - -It is an error if a class declaration declares an instance variable with a -potentially non-nullable type and no initializer expression, and the class has a -generative constructor where the variable is not initialized via an initializing -formal or an initializer list entry, unless the variable is marked with a -\LATE, \ABSTRACT, or \EXTERNAL{} modifier. - -It is an error if a mixin declaration or a class declaration with no generative -constructors declares an instance variable without an initializing expression -which is final or whose type is potentially non-nullable, unless the variable is -marked with a \LATE, \ABSTRACT, or \EXTERNAL{} modifier. - -It is an error to derive a mixin from a class declaration which contains an -instance variable with a potentially non-nullable type and no initializer -expression unless the variable is marked with the \LATE{} modifier. - -It is an error if the body of a method, function, getter, or function expression -with a potentially non-nullable return type may complete normally. - -It is an error if an optional parameter (named or otherwise) with no default -value has a potentially non-nullable type except in the parameter list of an -abstract method declaration. - -It is an error if a required named parameter has a default value. +[LATER] It is an error if the body of a method, function, getter, or +function expression with a potentially non-nullable return type may +complete normally. -It is an error if a required named parameter is not bound to an argument at a -call site. +[LATER] For the purposes of errors and warnings, the null aware +operators \code{?.}, \code{?..}, and \code{?[]} are checked as if the +receiver of the operator had non-nullable type. More specifically, if +the type of the receiver of a null aware operator is $T$, then the +operator is checked as if the receiver had type \NonNullType{$T$} +(see definition below). -It is an error to call the default \code{List} constructor. - -For the purposes of errors and warnings, the null aware operators \code{?.}, \code{?..}, -and \code{?[]} are checked as if the receiver of the operator had non-nullable type. -More specifically, if the type of the receiver of a null aware operator is $T$, -then the operator is checked as if the receiver had type \NonNullTypeOf{$T$} (see -definition below). - -It is an error for a class to extend, implement, or mixin a type of the form -\code{$T$?} for any $T$. - -It is an error for a class to extend, implement, or mixin the type \code{Never}. - -It is not an error to call or tear-off a method, setter, or getter, or to read -or write a field, on a receiver of static type \code{Never}. Implementations that -provide feedback about dead or unreachable code are encouraged to indicate that -any arguments to the invocation are unreachable. - -It is not an error to apply an expression of type \code{Never} in the function -position of a function call. Implementations that provide feedback about dead or -unreachable code are encouraged to indicate that any arguments to the call are -unreachable. - -It is an error if the static type of $e$ in the expression \code{\THROW\,\,$e$} is not -assignable to \code{Object}. - -It is not an error for the body of a \LATE{} field to reference \THIS. - -It is an error for a variable to be declared as \LATE{} in any of the following -positions: in a formal parameter list of any kind; in a catch clause; in the -variable binding section of a c-style \FOR{} loop, a for-in loop, an \AWAIT-for -loop, or a \syntax{forElement} in a collection literal. - -It is an error for the initializer expression of a \LATE{} local variable to use -a prefix \AWAIT{} expression that is not nested inside of another function -expression. +It is an error for the initializer expression of a \LATE{} local +variable to use a prefix \AWAIT{} expression that is not nested inside +of another function expression. It is an error for a class with a generative \CONST{} constructor to have a \code{\LATE\ \FINAL} instance variable. @@ -23919,7 +24127,7 @@ \subsubsection{Static errors} is not primitive. \item If $T$ is an enum type, it is a warning if the switch does not handle all enum cases, either explicitly or via a default. -\item If $T$ is \code{$Q$?} where $Q$ is an enum type, it is a warning if the switch does +\item If $T$ is \code{$Q$?}\ where $Q$ is an enum type, it is a warning if the switch does not handle all enum cases and \code{null}, either explicitly or via a default. \end{itemize} @@ -23936,7 +24144,7 @@ \subsubsection{Static errors} to use a null aware member access with receiver $C$. \commentary{E.g., \code{$C$?.staticMethod()} is a warning.} -It is a warning to use a null aware operator (\code{?.}, \code{?[]}, \code{?..}, \code{??}, \code{??=}, or +[ONGOING] It is a warning to use a null aware operator (\code{?.}, \code{?[]}, \code{?..}, \code{??}, \code{??=}, or \code{...?}) on an expression of type $T$ if $T$ is strictly non-nullable. It is a warning to use the null check operator (\code{!}) on an expression of type @@ -23985,7 +24193,7 @@ \subsubsection{Local variables and definite (un)assignment} type, or is \LATE. The errors specified above are summarized in the following table, where \code{int} is -used as an example of an arbitrary potentially non-nullable type, \code{int?} is +used as an example of an arbitrary potentially non-nullable type, \code{int?}\ is used as an example of an arbitrary nullable type, and $T$ is used to stand for a type of any nullability. A variable which has an initializer (explicit or implicit) is always considered definitely assigned, and is never considered @@ -24036,7 +24244,7 @@ \subsubsection{Local variables and inference} if the variable has a nullable type, then the non-nullable version of that type is also a type of interest. Treating the initialization as an assignment for the purposes of promotion means that initializing a mutable variable declared at -type \code{$T$?} with a value of non-nullable type $T$ immediately promotes the +type \code{$T$?}\ with a value of non-nullable type $T$ immediately promotes the variable to the non-nullable type.% } @@ -24091,7 +24299,7 @@ \subsubsection{Expression typing} Consider an expression $e$ of the form \code{$e_1$\,==\,$e_2$} where the static type of $e_1$ is $T_1$ and the static type of $e_2$ is $T_2$. Let $S$ be the type of the -formal parameter of \code{operator\,\,==} in the interface of \NonNullTypeOf{$T_1$}. +formal parameter of \code{operator\,\,==} in the interface of \NonNullType{$T_1$}. It is a compile-time error unless $T_2$ is assignable to \code{$S$?}. Similarly, consider an expression $e$ of the form \code{\SUPER\,==\,$e_2$} that occurs in a @@ -24143,7 +24351,7 @@ \subsubsection{Super-bounded types} the language specification. The definition of a top type is changed: $T$ is a top type if and only if -\code{Object?} is a subtype of $T$. Note that the helper predicate \IsTopTypeName{} +\code{Object?}\ is a subtype of $T$. Note that the helper predicate \IsTopTypeName{} provides a syntactic characterization of the same concept. The definition of a super-bounded type is changed such that occurrences of @@ -24162,7 +24370,7 @@ \subsubsection{Least and greatest closure} The definitions of least and greatest closure are changed in null safe libraries to substitute \code{Never} in positions where previously \code{Null} would have been -substituted, and \code{Object?} in positions where previously \code{Object} or \DYNAMIC{} +substituted, and \code{Object?}\ in positions where previously \code{Object} or \DYNAMIC{} would have been substituted. \subsubsection{Const type variable elimination} @@ -24216,7 +24424,7 @@ \subsubsection{Combined member signatures} This is achieved by changing this paragraph, line 4373, to the following: -"Let $m_{\metavar{all}}$ be the result of applying \NnbdTopMergeName{} to +"Let $m_{\metavar{all}}$ be the result of applying \TopMergeTypeName{} to the elements in $M_{\metavar{all}}$, ordered according to the interface \List{I}{1}{k} that each signature came from." @@ -24319,33 +24527,15 @@ \subsubsection{Null promotion} A check of the form \code{$e$ == null} or of the form \code{$e$\,\,\IS\,\,Null} where $e$ has static type $T$ promotes the type of $e$ to \code{Null} in the \code{true} continuation, and to -\NonNullTypeOf{$T$} in the \code{false} continuation. +\NonNullType{$T$} in the \code{false} continuation. A check of the form \code{$e$ != null} or of the form \code{$e$\,\,\IS\,\,$T$} where $e$ has static -type \code{$T$?} promotes the type of $e$ to $T$ in the \code{true} continuation, and to +type \code{$T$?}\ promotes the type of $e$ to $T$ in the \code{true} continuation, and to \code{Null} in the \code{false} continuation. -The static type of an expression \code{$e$!} is \NonNullTypeOf{$T$} where $T$ is the +The static type of an expression \code{$e$!} is \NonNullType{$T$} where $T$ is the static type of $e$. -The \NonNullTypeOfName{} function defines the null-promoted version of a type, and is -defined as follows. - -\begin{itemize} -\item \NonNullTypeOf{Null} = \code{Never} -\item \NonNullTypeOf{$C$<$T_1$, \ldots, $T_n$>} = \code{$C$<$T_1$, \ldots, $T_n$>}, - for class $C$ other than \code{Null} (including \code{Object}). -\item \NonNullTypeOf{FutureOr<$T$>} = \code{FutureOr<$T$>} -\item \NonNullTypeOf{$T_0$ \FUNCTION(\ldots)} = \code{$T_0$ \FUNCTION(\ldots)} -\item \NonNullTypeOf{\FUNCTION} = \FUNCTION{} -\item \NonNullTypeOf{Never} = \code{Never} -\item \NonNullTypeOf{\DYNAMIC} = \DYNAMIC{} -\item \NonNullTypeOf{\VOID} = \VOID{} -\item \NonNullTypeOf{$X$} = \code{$X$ \& \NonNullTypeOf{$B$}}, where $B$ is the bound of $X$. -\item \NonNullTypeOf{$X$ \& $T$} = \code{$X$ \& \NonNullTypeOf{$T$}} -\item \NonNullTypeOf{$T$?} = \NonNullTypeOf{$T$} -\end{itemize} - \paragraph{Extended Type promotion, Definite Assignment, and Reachability} These are extended as per @@ -24361,7 +24551,7 @@ \subsection{Helper predicates} top types. \begin{itemize} -\item \IsTopType{$T$?} is true iff \IsTopType{$T$} or \IsObjectType{$T$} +\item \IsTopType{$T$?}\ is true iff \IsTopType{$T$} or \IsObjectType{$T$} \item \IsTopType{\DYNAMIC} is true \item \IsTopType{\VOID} is true \item \IsTopType{FutureOr<$T$>} is \IsTopType{$T$} @@ -24397,7 +24587,7 @@ \subsection{Helper predicates} \begin{itemize} \item \IsNullType{Null} is true -\item \IsNullType{$T$?} is true iff \IsNullType{$T$} or \IsBottomType{$T$} +\item \IsNullType{$T$?}\ is true iff \IsNullType{$T$} or \IsBottomType{$T$} \item \IsNullType{$T$} is false otherwise \end{itemize} @@ -24794,50 +24984,50 @@ \subsubsection{Boolean conditional evaluation} \subsection{Migration features} -The \NnbdTopMergeName{} of two types $T$ and $S$ is the unique type $R$ defined +The \TopMergeTypeName{} of two types $T$ and $S$ is the unique type $R$ defined as: \begin{itemize} -\item \NnbdTopMerge{Object?}{Object?} = \code{Object?} -\item \NnbdTopMerge{\DYNAMIC}{\DYNAMIC} = \code{\DYNAMIC} -\item \NnbdTopMerge{\VOID}{\VOID} = \code{\VOID} -\item \NnbdTopMerge{Object?}{\VOID} = \code{Object?} +\item \TopMergeType{Object?}{Object?} = \code{Object?} +\item \TopMergeType{\DYNAMIC}{\DYNAMIC} = \code{\DYNAMIC} +\item \TopMergeType{\VOID}{\VOID} = \code{\VOID} +\item \TopMergeType{Object?}{\VOID} = \code{Object?} - And the reverse -\item \NnbdTopMerge{\DYNAMIC}{\VOID} = \code{Object?} +\item \TopMergeType{\DYNAMIC}{\VOID} = \code{Object?} - And the reverse -\item \NnbdTopMerge{Object?}{\DYNAMIC} = \code{Object?} +\item \TopMergeType{Object?}{\DYNAMIC} = \code{Object?} - And the reverse -\item \NnbdTopMerge{$T$?}{$S$?} = \code{\NnbdTopMerge{$T$}{$S$}?} +\item \TopMergeType{$T$?}{$S$?} = \code{\TopMergeType{$T$}{$S$}?} \item And for all other types, recursively applying the transformation over the structure of the type - - e.g. \NnbdTopMerge{$C$<$T$>}{$C$<$S$>} = \code{$C$<\NnbdTopMerge{$T$}{$S$}>} -\item When computing the \NnbdTopMergeName{} of two method parameters at least one of + - e.g. \TopMergeType{$C$<$T$>}{$C$<$S$>} = \code{$C$<\TopMergeType{$T$}{$S$}>} +\item When computing the \TopMergeTypeName{} of two method parameters at least one of which is marked as covariant, the following algorithm is used to compute the canonical parameter type. Given two corresponding parameters of type $T_1$ and $T_2$ where at least one of the parameters has a \COVARIANT{} declaration: - if \code{$T_1$ <: $T_2$} and \code{$T_2$ <: $T_1$} then the result is \NnbdTopMerge{$T_1$}{$T_2$}, + if \code{$T_1$ <: $T_2$} and \code{$T_2$ <: $T_1$} then the result is \TopMergeType{$T_1$}{$T_2$}, and it is covariant. Otherwise, if \code{$T_1$ <: $T_2$} then the result is $T_2$ and it is covariant. Otherwise the result is $T_1$ and it is covariant. \end{itemize} -In other words, \NnbdTopMergeName{} takes two types which are structurally equal +In other words, \TopMergeTypeName{} takes two types which are structurally equal except for the particular choice of top types, and finds a single canonical type to represent them. -The \NnbdTopMergeName{} of two types is not defined for +The \TopMergeTypeName{} of two types is not defined for types which are not otherwise structurally equal. -The \NnbdTopMergeName{} of more than two types is defined by taking the -\NnbdTopMergeName{} of the first two, and then recursively taking the -\NnbdTopMergeName{} of the rest. +The \TopMergeTypeName{} of more than two types is defined by taking the +\TopMergeTypeName{} of the first two, and then recursively taking the +\TopMergeTypeName{} of the rest. If a class $C$ library implements the same generic class $I$ more than once as \code{$I_0$, \ldots, $I_n$}, and at least one of the $I_i$ is not syntactically -equal to the others, then it is an error if \NnbdTopMerge{$S_0$, \ldots}{$S_n$} is not +equal to the others, then it is an error if \TopMergeType{$S_0$, \ldots}{$S_n$} is not defined where $S_i$ is \NormalizedTypeOf{$I_i$}. Otherwise, $C$ is considered to -implement the canonical interface given by \NnbdTopMerge{$S_0$, \ldots}{$S_n$}. This +implement the canonical interface given by \TopMergeType{$S_0$, \ldots}{$S_n$}. This determines the outcome of dynamic instance checks applied to instances of $C$, as well as static subtype checks on expressions of type $C$. @@ -24846,9 +25036,9 @@ \subsection{Migration features} \List{T}{0}{n} be the signatures of the inherited members. If there is exactly one $T_i$ such that \code{$T_i$ <: $T_k$} for all $k \in 0 .. n$ then the signature of $m$ is considered to be $T_i$. If there is more than one such $T_i$, then it -is an error if the \NnbdTopMergeName{} of \List{S}{0}{n} does not exist, where $S_i$ +is an error if the \TopMergeTypeName{} of \List{S}{0}{n} does not exist, where $S_i$ is \NormalizedTypeOf{$T_i$}. Otherwise, the signature of $m$ for the purposes of member -lookup is the \NnbdTopMergeName{} of the $S_i$. +lookup is the \TopMergeTypeName{} of the $S_i$. !!!TODO!!! From 5c53b76786805657455ccbae7cb88c62cb5a201b Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Mon, 1 Nov 2021 18:16:21 +0100 Subject: [PATCH 08/85] Rebase --- specification/dart.sty | 4 +- specification/dartLangSpec.tex | 1540 +++++++++++++++++--------------- 2 files changed, 810 insertions(+), 734 deletions(-) diff --git a/specification/dart.sty b/specification/dart.sty index 804d51ce8..4d6f6101a 100644 --- a/specification/dart.sty +++ b/specification/dart.sty @@ -196,11 +196,11 @@ % Same appearance, but not adding an entry to the index. \newcommand{\NoIndex}[1]{% - \leavevmode\marginpar{\ensuremath{_{^\vartriangle}}}\emph{#1}} + \leavevmode\marginpar{\ensuremath{\diamond}}\emph{#1}} % Mark a compile-time error in the margin. \newcommand{\Error}[1]{% - \leavevmode\marginpar{\ensuremath{_{^\ominus}}}{#1}} + \leavevmode\marginpar{\ensuremath{\ominus}}{#1}} % Used to specify comma separated lists of similar symbols. \newcommand{\List}[3]{\ensuremath{{#1}_{#2},\,\ldots,\ {#1}_{#3}}} diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index f261b7b2e..adf8f2e6b 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -132,6 +132,8 @@ % - Correct to include metadata. % - Clarify the section about assignable expressions. % - Integrate the null safety specification into this document. +% - Rename grammar non-terminals from `...Signature` to `...Header`: They are +% syntactic constructs, unlike the semantic concept 'member signature'. % % 2.14 % - Add constraint on type of parameter which is covariant-by-declaration in @@ -661,21 +663,21 @@ \section{Notation} \LMHash{}% \BlindDefineSymbol{x, op, y}% The specifications of operators often involve statements such as -\code{$x$ \metavar{op} $y$} is equivalent to the method invocation -\IndexCustom{\rm\code{$x$.\metavar{op}($y$)}}{% - x.op(y)@\code{$x$.\metavar{op}($y$)}}. +\code{$x$\,\,\op\,\,$y$} is equivalent to the method invocation +\IndexCustom{\rm\code{$x$.\op($y$)}}{x.op(y)@\code{$x$.\op($y$)}}. Such specifications should be understood as a shorthand for: \begin{itemize} \item - $x$ $op$ $y$ is equivalent to the method invocation + \code{$x$\,\,\op\,\,$y$} is equivalent to the method invocation \code{$x$.\metavar{op'}($y$)}, - assuming the class of $x$ actually declared a non-operator method named $op'$ - defining the same function as the operator $op$. + assuming the class of $x$ + actually declared a non-operator method named \metavar{op'} + defining the same function as the operator \op. \end{itemize} \rationale{% This circumlocution is required because -{\rm\code{$x$.\metavar{op}($y$)}}, where op is an operator, is not legal syntax. +{\rm\code{$x$.\op($y$)}}, where op is an operator, is not legal syntax. However, it is painfully verbose, and we prefer to state this rule once here, and use a concise and clear notation across the specification.% } @@ -1030,8 +1032,8 @@ \subsection{Scoping} } \LMHash{}% -It is a compile-time error if there is more than one entity with the same name -declared in the same scope. +It is a \Error{compile-time error} if there is more than one entity +with the same name declared in the same scope. \commentary{% It is therefore impossible, e.g., to define a class that declares @@ -1441,12 +1443,6 @@ \section{Variables} That is, any kind of variable which is not a local variable.% } -\LMHash{}% -A compile-time error occurs if a static variable has a -non-nullable type and no initializing expression, -unless the variable declaration has -the modifier \LATE{} or \EXTERNAL. - \LMHash{}% A \IndexCustom{constant variable}{variable!constant} is a variable whose declaration includes the modifier \CONST. @@ -1656,7 +1652,7 @@ \subsection{Implicitly Induced Getters and Setters} either by an initializing formal or an initializer list entry. \commentary{% -It is a compile-time error if a final instance variable +It is a \Error{compile-time error} if a final instance variable that has been initialized by means of an initializing formal of a constructor $k$ is also initialized in the initializer list of $k$ @@ -2019,7 +2015,7 @@ \section{Functions} cannot complete normally (\commentary{that is, it cannot reach the end and ``fall through''}, cf.~\ref{statementCompletion}), - it is a compile-time error if + it is a \Error{compile-time error} if the addition of \code{\RETURN;} at the end of the body would be a compile-time error. \commentary{% @@ -2054,7 +2050,7 @@ \section{Functions} \YIELD{} or \YIELD*.% } Let $T$ be the declared return type of the function that has this body. - It is a compile-time error if one of the following conditions hold: + It is a \Error{compile-time error} if one of the following conditions hold: \begin{itemize} \item The function is synchronous, $T$ is not \VOID, and it would have been a compile-time error @@ -2090,7 +2086,7 @@ \section{Functions} \end{itemize} \LMHash{}% -It is a compile-time error +It is a \Error{compile-time error} if an \ASYNC, \code{\ASYNC*} or \code{\SYNC*} modifier is attached to the body of a setter or constructor. @@ -2121,16 +2117,16 @@ \section{Functions} } \LMHash{}% -It is a compile-time error if the declared return type of +It is a \Error{compile-time error} if the declared return type of a function marked \ASYNC{} is not a supertype of \code{Future<$T$>} for some type $T$. -It is a compile-time error if the declared return type of +It is a \Error{compile-time error} if the declared return type of a function marked \code{\SYNC*} is not a supertype of \code{Iterable<$T$>} for some type $T$. -It is a compile-time error if the declared return type of +It is a \Error{compile-time error} if the declared return type of a function marked \code{\ASYNC*} is not a supertype of \code{Stream<$T$>} for some type $T$. -It is a compile-time error if the declared return type of +It is a \Error{compile-time error} if the declared return type of a function marked \code{\SYNC*} or \code{\ASYNC*} is \VOID. \LMHash{}% @@ -2201,7 +2197,7 @@ \subsection{Function Declarations} (\ref{formalParameters}). \LMHash{}% -It is a compile-time error to preface a function declaration +It is a \Error{compile-time error} to preface a function declaration with the built-in identifier \STATIC. \LMHash{}% @@ -2334,11 +2330,11 @@ \subsection{Formal Parameters} the formal parameter scope of $f$. \LMHash{}% -It is a compile-time error if a formal parameter +It is a \Error{compile-time error} if a formal parameter is declared as a constant variable (\ref{variables}). % We put the following error here because it's shared among all kinds of % functions except one. -It is a compile-time error if an optional formal parameter +It is a \Error{compile-time error} if an optional formal parameter (\commentary{named or positional}) with no default value has a potentially non-nullable type (\ref{typeNullability}), @@ -2368,7 +2364,7 @@ \subsubsection{Required Formals} \item By means of a declaration which is similar to a function type, derived from \synt{functionFormalParameter}. - It is a compile-time error if any default values are specified + It is a \Error{compile-time error} if any default values are specified in the signature of such a function type. \end{itemize} @@ -2393,13 +2389,13 @@ \subsubsection{Required Formals} \end{grammar} \LMHash{}% -It is a compile-time error if a formal parameter has +It is a \Error{compile-time error} if a formal parameter has the modifier \CONST{} or the modifier \LATE. -It is a compile-time error if \VAR{} occurs as +It is a \Error{compile-time error} if \VAR{} occurs as the first token of a \synt{fieldFormalParameter}. \LMHash{}% -It is a compile-time error if a parameter derived from +It is a \Error{compile-time error} if a parameter derived from \synt{fieldFormalParameter} occurs as a parameter of a function which is not a non-redirecting generative constructor. @@ -2422,7 +2418,7 @@ \subsubsection{Required Formals} } \LMHash{}% -It is a compile-time error if the modifier \COVARIANT{} occurs on +It is a \Error{compile-time error} if the modifier \COVARIANT{} occurs on a parameter of a function which is not an instance method, instance setter, or instance operator. @@ -2452,20 +2448,21 @@ \subsubsection{Optional and Named Formals} a later version of the language specification. \LMHash{}% -It is a compile-time error if an optional or named formal parameter has +It is a \Error{compile-time error} if an optional or named formal parameter has the modifier \CONST{} or the modifier \LATE. -It is a compile-time error if the default value of an optional parameter is -not a constant expression (\ref{constants}). +It is a \Error{compile-time error} if the default value of an optional parameter +is not a constant expression (\ref{constants}). If no default is specified for an optional parameter whose declared type is nullable (\ref{typeNullability}), a default value of \NULL{} is implicitly induced. -Otherwise, when the declared type is potentially non-nullable, -it is a compile-time error if an optional parameter has no default value. -It is a compile-time error if a required named parameter has a default value. +Otherwise, when the declared type is potentially non-nullable, it is a +\Error{compile-time error} if an optional parameter has no default value. +It is a +\Error{compile-time error} if a required named parameter has a default value. \LMHash{}% -It is a compile-time error if the name of a named optional parameter +It is a \Error{compile-time error} if the name of a named optional parameter begins with an `_' character. \rationale{% @@ -2496,7 +2493,7 @@ \subsubsection{Covariant Parameters} } \LMHash{}% -It is a compile-time error if the modifier \COVARIANT{} occurs +It is a \Error{compile-time error} if the modifier \COVARIANT{} occurs in the declaration of a formal parameter of a function which is not an instance method, an instance setter, or an operator. @@ -2819,7 +2816,7 @@ \subsection{External Functions} will throw a \code{NoSuchMethodError} or some subclass thereof. \LMHash{}% -An implementation specific compile-time error can be raised +An implementation specific \Error{compile-time error} can be raised at an \EXTERNAL{} function declaration. \commentary{% @@ -2978,10 +2975,10 @@ \section{Classes} that name denotes the interface of the class. \LMHash{}% -It is a compile-time error if a class named $C$ declares +It is a \Error{compile-time error} if a class named $C$ declares a member with basename (\ref{classMemberConflicts}) $C$. If a generic class named $G$ declares a type variable named $X$, -it is a compile-time error +it is a \Error{compile-time error} if $X$ is equal to $G$, or if $G$ has a member whose basename is $X$, or if $G$ has a constructor named \code{$G$.$X$}. @@ -3030,7 +3027,7 @@ \subsection{Fully Implementing an Interface} \BlindDefineSymbol{C, I, m}% Let $C$ be a concrete class declared in library $L$, with interface $I$. Assume that $I$ has a member signature $m$ which is accessible to $L$. -It is a compile-time error if $C$ does not have +It is a \Error{compile-time error} if $C$ does not have a concrete member with the same name as $m$ and accessible to $L$, unless $C$ has a non-trivial \code{noSuchMethod} (\ref{theMethodNoSuchMethod}). @@ -3048,7 +3045,7 @@ \subsection{Fully Implementing an Interface} (\ref{covariantParameters}) to each parameter $p$ in $m''$ where the corresponding parameter in $m$ has the modifier \COVARIANT. -It is a compile-time error if $m'$ is not a correct override of $m$ +It is a \Error{compile-time error} if $m'$ is not a correct override of $m$ (\ref{correctMemberOverrides}), unless that concrete member is a \code{noSuchMethod} forwarder (\ref{theMethodNoSuchMethod}). @@ -3131,7 +3128,7 @@ \subsection{Fully Implementing an Interface} Assume that a direct or indirect superinterface of $C$ has a method signature $m_s$ with the same name as $m'$ and accessible to $L$, such that $m_s$ has a parameter $p_s$ that corresponds to $p$. -In this situation, a compile-time error occurs +In this situation, a \Error{compile-time error} occurs if the type of $p$ is not a subtype and not a supertype of the type of $p_s$. \commentary{% @@ -3160,7 +3157,7 @@ \subsection{Instance Methods} Consider a class $C$ and an instance member declaration $D$ in $C$, with member signature $m$ (\ref{interfaces}). -It is a compile-time error if $D$ overrides a declaration +It is a \Error{compile-time error} if $D$ overrides a declaration % Note that $m'$ is accessible, due to the definition of `overrides'. with member signature $m'$ from a direct superinterface of $C$ @@ -3179,7 +3176,7 @@ \subsection{Instance Methods} \LMHash{}% For each parameter $p$ of $m$ where \COVARIANT{} is present, -it is a compile-time error if there exists +it is a \Error{compile-time error} if there exists a direct or indirect superinterface of $C$ which has an accessible method signature $m''$ with the same name as $m$, such that $m''$ has a parameter $p''$ that corresponds to $p$ @@ -3270,9 +3267,9 @@ \subsubsection{Operators} \lit{\gtilde}. \LMHash{}% -It is a compile-time error if the arity of the user-declared operator +It is a \Error{compile-time error} if the arity of the user-declared operator \lit{[]=} is not 2. -It is a compile-time error if the arity of a user-declared operator +It is a \Error{compile-time error} if the arity of a user-declared operator with one of the names: \lit{<}, \lit{>}, @@ -3293,7 +3290,7 @@ \subsubsection{Operators} \lit{\gtgt}, \lit{[]} is not 1. -It is a compile-time error if the arity of the user-declared operator +It is a \Error{compile-time error} if the arity of the user-declared operator \lit{-} is not 0 or 1. @@ -3313,15 +3310,16 @@ \subsubsection{Operators} } \LMHash{}% -It is a compile-time error if the arity of the user-declared operator +It is a \Error{compile-time error} if the arity of the user-declared operator \lit{\gtilde} is not 0. \LMHash{}% -It is a compile-time error to declare an optional parameter in an operator. +It is a +\Error{compile-time error} to declare an optional parameter in an operator. \LMHash{}% -It is a compile-time error if a user-declared operator \lit{[]=} +It is a \Error{compile-time error} if a user-declared operator \lit{[]=} declares a return type other than \VOID. \commentary{% @@ -3460,7 +3458,7 @@ \subsubsection{The Method \code{noSuchMethod}} which is noSuchMethod forwarded. \LMHash{}% -It is a compile-time error if the name $m$ is noSuchMethod forwarded +It is a \Error{compile-time error} if the name $m$ is noSuchMethod forwarded in a concrete class $C$, and a superclass of $C$ has an accessible concrete declaration of $m$ which is not a noSuchMethod forwarder. @@ -3783,7 +3781,7 @@ \subsection{Setters} \LMHash{}% Setters are functions (\ref{functions}) that are used to set -the values of object properties. +the values of properties. \begin{grammar} ::= ? \SET{} @@ -3795,15 +3793,20 @@ \subsection{Setters} } \LMHash{}% +A \Index{library setter} is a setter that is declared in a library scope +(\commentary{i.e., it is a top-level declaration}). +Setters declared in a class come in two variants. A setter definition that is prefixed with the \STATIC{} modifier defines a static setter. Otherwise, it defines an instance setter. + +\LMHash{}% The name of a setter is obtained by appending the string `=' to -the identifier given in its function header. +the identifier given in the setter header. \commentary{% -Hence, a setter name can never conflict with, override or be overridden by -a getter or method.% +Hence, a setter name can never conflict with, override, or be overridden by +a getter or a method.% } \LMHash{}% @@ -3816,7 +3819,7 @@ \subsection{Setters} either implicitly or explicitly. \LMHash{}% -It is a compile-time error if a setter's formal parameter list +It is a \Error{compile-time error} if a setter's formal parameter list does not consist of exactly one required formal parameter $p$. \rationale{% We could enforce this via the grammar, @@ -3824,11 +3827,17 @@ \subsection{Setters} } \LMHash{}% -It is a compile-time error if a setter declares a return type other than \VOID. -It is a compile-time error if a class has +It is a +\Error{compile-time error} if a setter declares a return type other than \VOID. +It is a \Error{compile-time error} if a class has a setter named \code{$v$=} with argument type $T$ and a getter named $v$ with return type $S$, -and $S$ may not be assigned to $T$. +and $S$ is not a subtype of $T$. + +\commentary{% +It is not sufficient that $S$ is assignable to $T$. +In particular, if $S$ is \DYNAMIC{} then $T$ must be a top type.% +} \commentary{% The rules for when a setter correctly overrides another member @@ -3927,7 +3936,8 @@ \subsection{Instance Variables} and the instance variables inherited by $C$ from its superclass. \LMHash{}% -It is a compile-time error if an instance variable is declared to be constant. +It is a +\Error{compile-time error} if an instance variable is declared to be constant. \rationale{% The notion of a constant instance variable @@ -3959,7 +3969,7 @@ \subsection{Instance Variables} does not have any of the modifiers \LATE, \ABSTRACT, or \EXTERNAL, and that it declares an instance variable named $v$ that does not have an initializing expression. -A compile-time error occurs if $C$ has no generative constructors, +A \Error{compile-time error} occurs if $C$ has no generative constructors, and $v$ is final or the type of $v$ is potentially non-nullable. \LMHash{}% @@ -3994,7 +4004,7 @@ \subsection{Constructors} A \Index{constructor name} always begins with the name of its immediately enclosing class, and may optionally be followed by a dot and an identifier \id. -It is a compile-time error if the name of a constructor +It is a \Error{compile-time error} if the name of a constructor is not a constructor name. \LMHash{}% @@ -4081,11 +4091,11 @@ \subsubsection{Generative Constructors} it declares an \Index{initializing formal parameter}. A term of the form \code{\THIS.\id} is contained in $p$, and \id{} is the \NoIndex{name} of $p$. -It is a compile-time error if \id{} is not also the name of +It is a \Error{compile-time error} if \id{} is not also the name of an instance variable of the immediately enclosing class or enum. \LMHash{}% -It is a compile-time error for an initializing formal parameter +It is a \Error{compile-time error} for an initializing formal parameter to occur in any function which is not a generative constructor. Also, it is a compile-time error for an initializing formal parameter to occur in a redirecting or external constructor. @@ -4097,7 +4107,7 @@ \subsubsection{Generative Constructors} the immediately enclosing class or enum. If $p$ has a type annotation $T$ then the declared type of $p$ is $T$. Otherwise, the declared type of $p$ is $T_{id}$. -It is a compile-time error if the declared type of $p$ +It is a \Error{compile-time error} if the declared type of $p$ is not a subtype of $T_{id}$. \LMHash{}% @@ -4229,7 +4239,7 @@ \subsubsection{Generative Constructors} \IndexCustom{redirectee constructor}{constructor!redirectee} for this declaration is then the constructor denoted by \code{$C$<$X_1 \ldots,\ X_m$>} respectively \code{$C$<$X_1 \ldots,\ X_m$>.\id}. -It is a compile-time error if the static argument list type +It is a \Error{compile-time error} if the static argument list type (\ref{actualArgumentLists}) of \code{($e_1 \ldots,\ e_p,\ x_1$:\ $e_{p+1}, \ldots,\ x_q$:\ $e_{p+q}$)} is not an assignable match for the formal parameter list of the redirectee. @@ -4261,12 +4271,13 @@ \subsubsection{Generative Constructors} $q'$ is the redirectee constructor of $q$, or $q''$ is the redirectee constructor of $q$ and $q'$ is redirection-reachable from $q''$. -It is a compile-time error if a redirecting generative constructor +It is a \Error{compile-time error} if a redirecting generative constructor is redirection-reachable from itself. \LMHash{}% When $\ConstMetavar$ is \CONST, -it is a compile-time error if the redirectee is not a constant constructor. +it is a +\Error{compile-time error} if the redirectee is not a constant constructor. Moreover, when $\ConstMetavar$ is \CONST, each $e_i,\ i \in 1 .. p+q$, must be a potentially constant expression (\ref{constantConstructors}). @@ -4354,9 +4365,9 @@ \subsubsection{Generative Constructors} An initializer of the form \code{$v$ = $e$} is equivalent to an initializer of the form \code{\THIS.$v$ = $e$}, and both forms are called \Index{instance variable initializers}. -It is a compile-time error if the enclosing class +It is a \Error{compile-time error} if the enclosing class does not declare an instance variable named $v$. -It is a compile-time error unless the static type of $e$ +It is a \Error{compile-time error} unless the static type of $e$ is assignable to the declared type of $v$. \LMHash{}% @@ -4371,7 +4382,7 @@ \subsubsection{Generative Constructors} \noindent{}% Let $S$ be the superclass of the enclosing class of $s$. -It is a compile-time error if class $S$ does not declare +It is a \Error{compile-time error} if class $S$ does not declare a generative constructor named $S$ (respectively \code{$S$.\id}). Otherwise, the static analysis of $s$ is performed as specified in Section~\ref{bindingActualsToFormals}, @@ -4388,22 +4399,22 @@ \subsubsection{Generative Constructors} \LMHash{}% Let \DefineSymbol{k} be a generative constructor. Then $k$ may include at most one superinitializer in its initializer list -or a compile-time error occurs. +or a \Error{compile-time error} occurs. If no superinitializer is provided, an implicit superinitializer of the form \SUPER{}() is added at the end of $k$'s initializer list, unless the enclosing class is class \code{Object}. -It is a compile-time error if a superinitializer appears +It is a \Error{compile-time error} if a superinitializer appears in $k$'s initializer list at any other position than at the end. -It is a compile-time error if more than one initializer corresponding +It is a \Error{compile-time error} if more than one initializer corresponding to a given instance variable appears in $k$'s initializer list. -It is a compile-time error if $k$'s initializer list contains +It is a \Error{compile-time error} if $k$'s initializer list contains an initializer for a variable that is initialized by means of an initializing formal of $k$. -It is a compile-time error if $k$'s initializer list contains +It is a \Error{compile-time error} if $k$'s initializer list contains an initializer for a final variable $f$ whose declaration includes an initialization expression. -It is a compile-time error if $k$ includes an initializing formal +It is a \Error{compile-time error} if $k$ includes an initializing formal for a final variable $f$ whose declaration includes an initialization expression. @@ -4416,16 +4427,16 @@ \subsubsection{Generative Constructors} where $v$ is not initialized via an initializing formal of $k$, and $v$ is not initialized via an initializer list entry of $k$. In this situation, -a compile-time error occurs if any of the following criteria holds: +a \Error{compile-time error} occurs if any of the following criteria holds: \begin{itemize} \item The type of $v$ is potentially non-nullable (\ref{typeNullability}). -\item $v$ is final. +\item $v$ is final. \end{itemize} \LMHash{}% -It is a compile-time error if $k$'s initializer list contains +It is a \Error{compile-time error} if $k$'s initializer list contains an initializer for a variable that is not an instance variable declared in the immediately surrounding class. @@ -4436,7 +4447,7 @@ \subsubsection{Generative Constructors} } \LMHash{}% -It is a compile-time error if a generative constructor of class \code{Object} +It is a \Error{compile-time error} if a generative constructor of class \code{Object} includes a superinitializer. @@ -4610,11 +4621,19 @@ \subsubsection{Factories} the form \code{\FACTORY\,\,$M$.\id} is $M$ if $M$ is not a generic type; otherwise the return type is -\code{$M$<$T_1, \ldots,\ T_n$>} -where $T_1, \ldots, T_n$ are the type parameters of the enclosing class. +\code{$M$<\List{X}{1}{s}>} +where \List{X}{1}{s} are the type parameters of the enclosing class. + +\commentary{% +In particular, it is an error for a factory constructor to return an expression +whose type is potentially nullable, +except that a factory constructor in the class \code{Null}, +had it existed, +could return an expression of type \code{Null}.% +} \LMHash{}% -It is a compile-time error if $M$ is not the name of +It is a \Error{compile-time error} if $M$ is not the name of %% TODO(eernst): Come Dart 3.0, add 'mixin'. the immediately enclosing class or enum. @@ -4683,14 +4702,14 @@ \subsubsection{Factories} \code{$T$<$S_1 \ldots,\ S_p$>.\id}. \LMHash{}% -It is a compile-time error if $T$ does not denote +It is a \Error{compile-time error} if $T$ does not denote a class accessible in the current scope. If $T$ does denote such a class $D$, -it is a compile-time error if $R$ does not denote a constructor. +it is a \Error{compile-time error} if $R$ does not denote a constructor. % It is by induction sufficient to check for abstractness one level down, % because it is an error on the redirectee if this occurs after multiple % redirections: -Otherwise, it is a compile-time error +Otherwise, it is a \Error{compile-time error} if $R$ denotes a generative constructor and $D$ is abstract. Otherwise, the \IndexCustom{redirectee constructor}{constructor!redirectee} @@ -4702,7 +4721,7 @@ \subsubsection{Factories} $q'$ is the redirectee constructor of $q$, or $q''$ is the redirectee constructor of $q$ and $q'$ is redirection-reachable from $q''$. -It is a compile-time error if a redirecting factory constructor +It is a \Error{compile-time error} if a redirecting factory constructor is redirection-reachable from itself. \LMHash{}% @@ -4712,7 +4731,7 @@ \subsubsection{Factories} when $k$ takes no named arguments, and \code{($T_1 \ldots,\ T_n,\ T_{n+1}\ x_{n+1},\ \ldots,\ T_{n+k}\ x_{n+k}$)} when $k$ takes some named arguments. -It is a compile-time error if $\argumentList{T}$ +It is a \Error{compile-time error} if $\argumentList{T}$ is not a subtype match for the formal parameter list of the redirectee. \rationale{% @@ -4739,7 +4758,7 @@ \subsubsection{Factories} } \LMHash{}% -It is a compile-time error if $k$ explicitly specifies +It is a \Error{compile-time error} if $k$ explicitly specifies a default value for an optional parameter. \rationale{% @@ -4749,7 +4768,8 @@ \subsubsection{Factories} } \LMHash{}% -It is a compile-time error if a formal parameter of $k'$ has a default value +It is a +\Error{compile-time error} if a formal parameter of $k'$ has a default value whose type is not a subtype of the type annotation on the corresponding formal parameter in $k$. @@ -4774,7 +4794,7 @@ \subsubsection{Factories} } \LMHash{}% -It is a compile-time error if $k$ is prefixed with the \CONST{} modifier +It is a \Error{compile-time error} if $k$ is prefixed with the \CONST{} modifier but $k'$ is not a constant constructor (\ref{constantConstructors}). \LMHash{}% @@ -4783,7 +4803,7 @@ \subsubsection{Factories} Let \DefineSymbol{\List{X}{1}{m}} be the formal type parameters declared by the class that contains the declaration of $k'$. Let \DefineSymbol{F'} be the function type of $k'$ (\ref{constructors}). -It is a compile-time error if $[T_1/X_1, \ldots, T_m/X_m]F'$ +It is a \Error{compile-time error} if $[T_1/X_1, \ldots, T_m/X_m]F'$ is not a subtype of the function type of $k$. \commentary{% @@ -4855,8 +4875,12 @@ \subsubsection{Constant Constructors} non-redirecting generative constant constructors. \LMHash{}% -It is a compile-time error if a non-redirecting generative constant constructor -is declared by a class that has a instance variable which is not final. +It is a +\Error{compile-time error} if a non-redirecting generative constant constructor +is declared by a class that has an instance variable which is not final. +It is a +\Error{compile-time error} if a non-redirecting generative constant constructor +is declared by a class that has an instance variable which is late and final. \commentary{% The above refers to both locally declared and inherited instance variables.% @@ -4865,7 +4889,7 @@ \subsubsection{Constant Constructors} \LMHash{}% If a non-redirecting generative constant constructor \DefineSymbol{k} is declared by a class $C$, -it is a compile-time error +it is a \Error{compile-time error} for an instance variable declared in $C$ to have an initializing expression that is not a constant expression. @@ -4882,21 +4906,21 @@ \subsubsection{Constant Constructors} in the initializer list of a constant constructor must specify a generative constant constructor of the superclass of the immediately enclosing class, -or a compile-time error occurs. +or a \Error{compile-time error} occurs. \LMHash{}% Any expression that appears within the initializer list of a constant constructor must be a potentially constant expression (\ref{constants}), -or a compile-time error occurs. +or a \Error{compile-time error} occurs. \LMHash{}% When a constant constructor \DefineSymbol{k} is invoked from a constant object expression, -it is a compile-time error if +it is a \Error{compile-time error} if the invocation of $k$ at run time would throw an exception, -and it is a compile-time error if +and it is a \Error{compile-time error} if substitution of the actual arguments for the formal parameters yields an initializing expression $e$ in the initializer list of $k$ which is not a constant expression. @@ -4971,7 +4995,7 @@ \subsection{Superclasses} \end{itemize} \LMHash{}% -It is a compile-time error to specify an \EXTENDS{} clause +It is a \Error{compile-time error} to specify an \EXTENDS{} clause for class \code{Object}. \begin{grammar} @@ -4986,7 +5010,7 @@ \subsection{Superclasses} the type-parameter scope of $C$. \LMHash{}% -It is a compile-time error if the type +It is a \Error{compile-time error} if the type in the \EXTENDS{} clause of a class which is declared in a non-system library is not a class building type @@ -5018,7 +5042,7 @@ \subsection{Superclasses} \end{itemize} \LMHash{}% -It is a compile-time error if a class $C$ is a superclass of itself. +It is a \Error{compile-time error} if a class $C$ is a superclass of itself. \subsubsection{Inheritance and Overriding} @@ -5183,15 +5207,15 @@ \subsection{Superinterfaces} the type-parameter scope of $C$. \LMHash{}% -It is a compile-time error if an element +It is a \Error{compile-time error} if an element in the type list of an \IMPLEMENTS{} clause that occurs in a non-system library is not a class building type (\ref{classBuildingTypes}). -It is a compile-time error if two elements in said type list +It is a \Error{compile-time error} if two elements in said type list %% TODO(eernst): Refer to nnbd notion of 'same type'. specifies the same type. -It is a compile-time error if the superclass of a class $C$ is +It is a \Error{compile-time error} if the superclass of a class $C$ is one of the elements of the type list of the \IMPLEMENTS{} clause of $C$. \rationale{% @@ -5205,7 +5229,7 @@ \subsection{Superinterfaces} } \LMHash{}% -It is a compile-time error if a class $C$ has two superinterfaces +It is a \Error{compile-time error} if a class $C$ has two superinterfaces that are different instantiations of the same generic class. \commentary{% For example, a class can not have @@ -5215,7 +5239,7 @@ \subsection{Superinterfaces} \LMHash{}% When a generic class $C$ declares a type parameter $X$, -it is a compile-time error if $X$ occurs in a non-covariant position +it is a \Error{compile-time error} if $X$ occurs in a non-covariant position % Could say `a direct superinterface', but it is easy to see that it is % enough to check direct superinterfaces, and it is then true % for indirect ones as well. @@ -5227,7 +5251,7 @@ \subsection{Superinterfaces} } \LMHash{}% -It is a compile-time error if the interface of a class $C$ is +It is a \Error{compile-time error} if the interface of a class $C$ is a superinterface of itself. \commentary{% @@ -5253,20 +5277,20 @@ \subsection{Class Member Conflicts} \LMHash{}% Let \DefineSymbol{C} be a class. -It is a compile-time error if $C$ +It is a \Error{compile-time error} if $C$ declares a constructor named \code{$C$.$n$} and a static member with basename $n$. -It is a compile-time error if $C$ +It is a \Error{compile-time error} if $C$ declares a static member with basename $n$ and the interface of $C$ has an instance member with basename $n$. -It is a compile-time error if the interface of $C$ +It is a \Error{compile-time error} if the interface of $C$ has an instance method named $n$ and an instance setter with basename $n$. -It is a compile-time error if $C$ declares a static method named $n$ +It is a \Error{compile-time error} if $C$ declares a static method named $n$ and a static setter with basename $n$. \LMHash{}% When \DefineSymbol{C} is a mixin or an extension, -the compile-time errors occur according to the same rules. +the \Error{compile-time errors} occur according to the same rules. \commentary{% This is redundant in some cases. For instance, it is already an error for a mixin to declare a constructor. @@ -5474,7 +5498,7 @@ \section{Interfaces} a member signature named \id{} which is accessible to $L$, let $m$ be the combined member signature named \id{} from \List{I}{1}{k} with respect to $L$. - It is a compile-time error + It is a \Error{compile-time error} if the computation of this combined member signature failed. Otherwise, $M$ contains $m$. \end{itemize} @@ -5796,7 +5820,8 @@ \subsubsection{Inheritance and Overriding} and $I$ \Index{overrides} $m'$ if $m' \in \overrides{I, L}$. \LMHash{}% -All the compile-time errors pertaining to the overriding of instance members +All the \Error{compile-time errors} pertaining to +the overriding of instance members given in section~\ref{classes} hold for overriding between interfaces as well. \LMHash{}% @@ -5809,7 +5834,7 @@ \subsubsection{Inheritance and Overriding} in the textual order that they are declared, with respect to $L$ (\ref{combinedMemberSignatures}). -It is a compile-time error +It is a \Error{compile-time error} if the computation of said combined member signature fails. @@ -5845,14 +5870,6 @@ \subsubsection{Correct Member Overrides} and $m$ may accept 1, 2, 3, or 4 positional arguments, but not vice versa. This is a built-in property of the function type subtype rules. } -\item - %% TODO(eernst): Come nnbd, this warning is removed. - If $m$ and $m'$ are both methods, - $p$ is an optional parameter of $m$, - $p'$ is the parameter of $m'$ corresponding to $p$, - $p$ has default value $d$ and $p'$ has default value $d'$, - then $d$ and $d'$ must be identical, - or a static warning occurs. \end{itemize} \commentary{% @@ -5874,7 +5891,7 @@ \section{Mixins} A mixin describes the difference between a class and its superclass. A mixin is either derived from an existing class declaration or introduced by a mixin declaration. -It is a compile-time error to derive a mixin from +It is a \Error{compile-time error} to derive a mixin from a class that declares a generative constructor, or from a class that has a superclass other than \code{Object}. @@ -5897,7 +5914,7 @@ \subsection{Mixin Classes} \end{grammar} \LMHash{}% -It is a compile-time error if an element in +It is a \Error{compile-time error} if an element in the type list of the \WITH{} clause of a mixin application that occurs in a non-system library is not a class building type @@ -5917,8 +5934,9 @@ \subsection{Mixin Classes} \end{normativeDartCode} \LMHash{}% -It is a compile-time error if $S$ is an enumerated type (\ref{enums}). -It is a compile-time error if any of $M_1, \ldots, M_k$ is an enumerated type +It is a \Error{compile-time error} if $S$ is an enumerated type (\ref{enums}). +It is a +\Error{compile-time error} if any of $M_1, \ldots, M_k$ is an enumerated type (\ref{enums}). \LMHash{}% @@ -5951,7 +5969,7 @@ \subsection{Mixin Classes} In either case, let $K$ be a class declaration with the same constructors, superclass, interfaces and instance members as the defined class. -It is a compile-time error if the declaration of $K$ would cause +It is a \Error{compile-time error} if the declaration of $K$ would cause a compile-time error. % TODO(eernst): Not completely! % We do not want super-invocations on covariant implementations @@ -5979,8 +5997,9 @@ \subsection{Mixin Declaration} %% TODO(eernst): Change this paragraph if we allow factory constructors. \LMHash{}% -It is a compile-time error to declare a constructor in a mixin declaration. -It is a compile-time error if a mixin declaration declares +It is a +\Error{compile-time error} to declare a constructor in a mixin declaration. +It is a \Error{compile-time error} if a mixin declaration declares an instance variable without an initializing expression which is final or whose type is potentially non-nullable (\ref{typeNullability}), @@ -6001,7 +6020,7 @@ \subsection{Mixin Declaration} \end{normativeDartCode} \LMHash{}% -A compile-time error occurs if +A \Error{compile-time error} occurs if the declaration of $M$ occurs in a non-system library, and any of the types $T_1$ through $T_n$ or $I_1$ through $I_k$ is not a class building type @@ -6017,7 +6036,7 @@ \subsection{Mixin Declaration} \LMHash{}% In this declaration, $M_{\metavar{super}}$ is a fresh name. -It is a compile-time error for the mixin declaration if the $M_S$ +It is a \Error{compile-time error} for the mixin declaration if the $M_S$ class declaration would cause a compile-time error. \commentary{% @@ -6054,7 +6073,8 @@ \subsection{Mixin Declaration} as if \SUPER{} were a valid expression with static type $M_S$. \LMHash{}% -It is a compile-time error for the mixin $M$ if this class declaration $N$ +It is a +\Error{compile-time error} for the mixin $M$ if this class declaration $N$ would cause a compile-time error. \commentary{% @@ -6081,7 +6101,7 @@ \subsection{Mixin Declaration} \IndexCustom{mixin member declarations}{mixin!member declaration}. \LMHash{}% -It is possible to +It is possible to \IndexCustom{derive a mixin from a class}{% mixin!derived from a class}. Consider a class declaration of the following form: @@ -6103,7 +6123,7 @@ \subsection{Mixin Declaration} which does not have the modifier \LATE, and that $D$ declares an instance variable $v$ which does not have an initializing expression. -It is then a compile-time error if +It is then a \Error{compile-time error} if the type of $v$ is potentially non-nullable (\ref{typeNullability}). @@ -6123,9 +6143,10 @@ \subsection{Mixin Application} and let $N$ be a name. \LMHash{}% -It is a compile-time error to apply $M$ to $S$ if $S$ does not implement, +It is a +\Error{compile-time error} to apply $M$ to $S$ if $S$ does not implement, directly or indirectly, all of \List{T}{1}{n}. -It is a compile-time error if any of \metavar{members} contains a +It is a \Error{compile-time error} if any of \metavar{members} contains a super-invocation of a member $m$ \commentary{(for example \code{super.foo}, \code{super + 2}, or \code{super[1] = 2})}, and $S$ does not have a concrete implementation of $m$ which is a valid override of the member $m$ in @@ -6284,7 +6305,7 @@ \section{Extensions} } \LMHash{}% -It is a compile-time error if the current library has +It is a \Error{compile-time error} if the current library has a deferred import of a library $L'$ such that the imported namespace from $L'$ contains a name denoting an extension. @@ -6361,7 +6382,7 @@ \section{Extensions} Member naming conflict errors may occur in $D$ in situations that also occur in classes and mixins (\ref{classMemberConflicts}). -Moreover, a compile-time error occurs in the following situations: +Moreover, a \Error{compile-time error} occurs in the following situations: \begin{itemize} \item $D$ declares a member whose basename is \code{E}. @@ -6481,13 +6502,13 @@ \subsection{Explicit Invocation of an Instance Member of an Extension} on the type parameters of $E$ (\ref{instantiationToBound}). % -A compile-time error occurs unless +A \Error{compile-time error} occurs unless \SubtypeNE{T_j}{[T_1/X_1, \ldots, T_s/X_s]B_j}, $j \in 1 .. s$ (\commentary{that is, the bounds cannot be violated}). % -A compile-time error occurs unless the static type of $e$ is assignable to -the instantiated \ON{} type of $a$. +A \Error{compile-time error} occurs unless the static type of $e$ +is assignable to the instantiated \ON{} type of $a$. \commentary{% Note that a compile-time error occurs as well if the static type of $e$ is \VOID{} @@ -6495,7 +6516,7 @@ \subsection{Explicit Invocation of an Instance Member of an Extension} } \LMHash{}% -It is a compile-time error if an extension application occurs +It is a \Error{compile-time error} if an extension application occurs in a location where it is \emph{not} the syntactic receiver of a simple or composite member invocation (\ref{memberInvocations}). @@ -6528,7 +6549,7 @@ \subsection{Explicit Invocation of an Instance Member of an Extension} (\commentary{which is \code{$E$($e$)} when $k$ is zero}) whose corresponding member name is \DefineSymbol{n}, and assume that $r$ has no compile-time errors. -A compile-time error occurs unless the extension denoted by $E$ +A \Error{compile-time error} occurs unless the extension denoted by $E$ declares a member named $n$. Otherwise let \DefineSymbol{\List{X}{1}{k}} be the type parameters of said extension. @@ -6937,7 +6958,7 @@ \subsection{Static analysis of Members of an Extension} the static type of \THIS{} is $T_{on}$. \LMHash{}% -A compile-time error occurs if the body of an extension member +A \Error{compile-time error} occurs if the body of an extension member contains \SUPER. \commentary{% @@ -7035,7 +7056,8 @@ \subsection{Extension Method Closurization} where \id{} is an identifier is then known as an \IndexCustom{extension property extraction}{% extension!property extraction}. -It is a compile-time error unless $E$ declares an instance member named \id. +It is a +\Error{compile-time error} unless $E$ declares an instance member named \id. If said instance member is a method then $e$ has the static type $[S_1/Y_1, \ldots, S_m/Y_m]F$, where $F$ is the function type of said method declaration. @@ -7240,7 +7262,7 @@ \subsection{The \CALL{} Member of an Extension} $i$ is specified elsewhere (\ref{functionExpressionInvocation}). Otherwise, if $S$ has a non-method instance member with basename \CALL{} -then $i$ is a compile-time error. +then $i$ is a \Error{compile-time error}. Otherwise, $i$ is treated as the expression $i'$ which is \noindent @@ -7255,7 +7277,7 @@ \subsection{The \CALL{} Member of an Extension} } \LMHash{}% -It is a compile-time error unless $i'$ is an implicit invocation of +It is a \Error{compile-time error} unless $i'$ is an implicit invocation of an extension instance method named \CALL. \commentary{% @@ -7321,7 +7343,7 @@ \section{Enums} \end{normativeDartCode} \commentary{% -It is also a compile-time error to subclass, mix-in or implement an enum +It is also a \Error{compile-time error} to subclass, mix-in or implement an enum or to explicitly instantiate an enum. These restrictions are given in normative form in sections \ref{superclasses}, \ref{superinterfaces}, \ref{mixinApplication} @@ -7367,8 +7389,8 @@ \section{Generics} and a parameterized type $T$ of the form \code{$C$<$T_1, \ldots,\ T_l$>}. \LMHash{}% -It is a compile-time error if $m \not= l$. -It is a compile-time error if $T$ is not well-bounded +It is a \Error{compile-time error} if $m \not= l$. +It is a \Error{compile-time error} if $T$ is not well-bounded (\ref{superBoundedTypes}). \LMHash{}% @@ -7405,8 +7427,8 @@ \section{Generics} where the static type of \code{f} is a generic function type with formal type parameters $X_1\ \EXTENDS\ B_1, \ldots,\ X_m\ \EXTENDS\ B_m$. -It is a compile-time error if $m \not= l$. -It is a compile-time error if there exists a $j$ +It is a \Error{compile-time error} if $m \not= l$. +It is a \Error{compile-time error} if there exists a $j$ such that $T_j$ is not a subtype of $[T_1/X_1, \ldots, T_m/X_m]B_j$. \commentary{% @@ -7427,7 +7449,8 @@ \section{Generics} A type parameter $T$ may be suffixed with an \EXTENDS{} clause that specifies the \Index{upper bound} for $T$. If no \EXTENDS{} clause is present, the upper bound is \code{Object}. -It is a compile-time error if a type parameter is a supertype of its upper bound +It is a +\Error{compile-time error} if a type parameter is a supertype of its upper bound when that upper bound is itself a type variable. \commentary{% @@ -7768,10 +7791,10 @@ \subsection{Super-Bounded Types} it is either regular-bounded or super-bounded. \LMHash{}% -Any use of a type $T$ which is not well-bounded is a compile-time error. +Any use of a type $T$ which is not well-bounded is a \Error{compile-time error}. \LMHash{}% -It is a compile-time error if a parameterized type $T$ is super-bounded +It is a \Error{compile-time error} if a parameterized type $T$ is super-bounded when it is used in any of the following ways: \begin{itemize} \item $T$ is an immediate subterm of a new expression @@ -8099,7 +8122,7 @@ \subsubsection{Auxiliary Concepts for Instantiation to Bound} } \LMHash{}% -It is a compile-time error +It is a \Error{compile-time error} if a formal type parameter bound $B$ contains a raw type $T$, unless $T$ has simple bounds. @@ -8121,7 +8144,7 @@ \subsubsection{Auxiliary Concepts for Instantiation to Bound} (\commentary{so $T$ is raw}). Then $T$ is equivalent to the parameterized type which is the result obtained by applying instantiation to bound to $T$. -It is a compile-time error if the instantiation to bound fails. +It is a \Error{compile-time error} if the instantiation to bound fails. \commentary{% This rule is applicable for all occurrences of raw types, @@ -8268,7 +8291,7 @@ \subsubsection{The Instantiation to Bound Algorithm} } \LMHash{}% -A raw type $T$ is a compile-time error if instantiation to bound on $T$ +A raw type $T$ is a \Error{compile-time error} if instantiation to bound on $T$ yields a type which is not well-bounded (\ref{superBoundedTypes}). @@ -8379,7 +8402,7 @@ \section{Metadata} each of which begin with the character \lit{@}, followed by a constant expression $e$ derivable from \synt{metadatum}. -It is a compile-time error if $e$ is not one of the following: +It is a \Error{compile-time error} if $e$ is not one of the following: \begin{itemize} \item A reference to a constant variable. \item A call to a constant constructor. @@ -8742,7 +8765,7 @@ \subsection{Constants} % \ref{const} requires each actual argument to be a constant expression, % but here we also catch errors during evaluation, e.g., `C(1, 0)` where % `C(double x, double y): z = x / y;`. - It is a compile-time error if a constant object expression is + It is a \Error{compile-time error} if a constant object expression is not a constant expression (\ref{const}). \item @@ -8917,7 +8940,7 @@ \subsection{Constants} and $T$ is a constant type expression, and it is further constant if $e$ is constant. \commentary{% - It is a compile-time error to evaluate the constant expression + It is a \Error{compile-time error} to evaluate the constant expression if the cast operation would throw, that is, if $e$ evaluates to an object which is not the null object and not of type $T$.% @@ -8978,15 +9001,15 @@ \subsection{Constants} % If that happens in a const invociation, it's a compile-time error. \LMHash{}% -It is a compile-time error if an expression is required to be +It is a \Error{compile-time error} if an expression is required to be a constant expression, but its evaluation would throw an exception. -It is a compile-time error if an assertion is evaluated as part of +It is a \Error{compile-time error} if an assertion is evaluated as part of a constant object expression evaluation, and the assertion would throw an exception. \LMHash{}% -It is a compile-time error if the value of a constant expression +It is a \Error{compile-time error} if the value of a constant expression depends on itself. \commentary{% @@ -9325,7 +9348,7 @@ \subsection{Numbers} representing the numeric value $i - 2^{64}$, \item{} Otherwise $l$ evaluates to an instance of the \code{int} class representing the numeric value $i$. - It is a compile-time error if the integer $i$ cannot be represented + It is a \Error{compile-time error} if the integer $i$ cannot be represented exactly by an instance of \code{int}. \end{itemize} @@ -9347,7 +9370,7 @@ \subsection{Numbers} \LMHash{}% An integer literal with static type \code{double} and numeric value $i$ evaluates to an instance of the \code{double} class representing the value $i$. -It is a compile-time error if the value $i$ +It is a \Error{compile-time error} if the value $i$ cannot be represented \emph{precisely} by an instance of \code{double}. \commentary{% @@ -9361,11 +9384,11 @@ \subsection{Numbers} } \LMHash{}% -It is a compile-time error for a class to extend, mix in or implement +It is a \Error{compile-time error} for a class to extend, mix in or implement \code{int}. -It is a compile-time error for a class to extend, mix in or implement +It is a \Error{compile-time error} for a class to extend, mix in or implement \code{double}. -It is a compile-time error for any class +It is a \Error{compile-time error} for any class other than \code{int} and \code{double} to extend, mix in or implement \code{num}. @@ -9393,7 +9416,7 @@ \subsection{Booleans} Both \NoIndex{true} and \NoIndex{false} are instances of the built-in class \code{bool}, and there are no other objects that implement \code{bool}. -It is a compile-time error for a class to +It is a \Error{compile-time error} for a class to extend, mix in or implement \code{bool}. \commentary{% @@ -9688,9 +9711,9 @@ \subsection{Strings} \item \syntax{`\\u{' `}'} is the Unicode code point represented by the - \syntax{}. - It is a compile-time error if the value of the - \syntax{} + \synt{HEX\_DIGIT\_SEQUENCE}. + It is a \Error{compile-time error} if the value of the + \synt{HEX\_DIGIT\_SEQUENCE} is not a valid Unicode code point. \commentary{For example,} {\color{commentaryColor}{\syntax{`\\u{0A}'}}\color{normativeColor}} @@ -9717,10 +9740,10 @@ \subsection{Strings} into the string value. \LMHash{}% -It is a compile-time error if a non-raw string literal contains +It is a \Error{compile-time error} if a non-raw string literal contains a character sequence of the form \syntax{`\\x'} that is not followed by a sequence of two hexadecimal digits. -It is a compile-time error if a non-raw string literal contains +It is a \Error{compile-time error} if a non-raw string literal contains a character sequence of the form \syntax{`\\u'} that is not followed by either a sequence of four hexadecimal digits, or by curly brace delimited sequence of hexadecimal digits. @@ -9733,7 +9756,7 @@ \subsection{Strings} \LMHash{}% All string literals evaluate to instances of the built-in class \code{String}. -It is a compile-time error for a class to +It is a \Error{compile-time error} for a class to extend, mix in or implement \code{String}. The \code{String} class overrides the \lit{==} operator inherited from the \code{Object} class. @@ -9828,8 +9851,8 @@ \subsection{Symbols} } \LMHash{}% -A symbol literal \code{\#\metavar{op}} -where \metavar{op} is derived from \synt{operator} +A symbol literal \code{\#\op} +where \op{} is derived from \synt{operator} evaluates to an instance of \code{Symbol} representing that particular operator name. @@ -9984,11 +10007,11 @@ \subsection{Collection Literals} of the form \synt{ifElement} or \synt{forElement}. \LMHash{}% -It is a compile-time error if -the \synt{forLoopParts} of a \synt{forElement} -declares an iteration variable that has -the modifier \CONST{} or the modifier \LATE, -using the same rules as with a \FOR{} statement +Assume that $f$ is a \synt{forElement} whose \synt{forLoopParts} +declares an iteration variable +with the modifier \CONST{} or \LATE{}. +In this situation, a \Error{compile-time error} occurs, +using the same rules as in a \FOR{} statement (\ref{for}). \commentary{% @@ -10481,7 +10504,7 @@ \subsubsection{Collection Literal Element Evaluation} \LMHash{}% \Case{For element} -Let $P$ be derived from \syntax{} and +Let $P$ be derived from \synt{forLoopParts} and let $\ell$ be a \synt{forElement} of the form \code{\AWAIT?\,\,\FOR\,\,($P$)\,\,$\ell_1$}, where `\AWAIT?' indicates that \AWAIT{} may be present or absent. @@ -10582,7 +10605,7 @@ \subsubsection{List Literal Inference} If $S$ is \code{Null} and the spread operator is \lit{...?}, the inferred element type of $\ell$ is \code{Null}. \item - Otherwise, a compile-time error occurs. + Otherwise, a \Error{compile-time error} occurs. \end{itemize} \vspace{-5mm} \EndCase @@ -10612,7 +10635,7 @@ \subsubsection{List Literal Inference} where $P$ is derived from \synt{forLoopParts} and `\AWAIT?' indicates that \AWAIT{} may be present or absent. -The same compile-time errors occur for $\ell$ as +The same \Error{compile-time errors} occur for $\ell$ as the errors that would occur with the corresponding \FOR{} statement \code{\AWAIT?\,\,\FOR\,\,($P$)\,\,\{\}}, located in the same scope as $\ell$. @@ -10693,9 +10716,9 @@ \subsubsection{Lists} \LMHash{}% Let $e$ be a list literal of the form \code{<$T$>[\List{\ell}{1}{m}]}. -It is a compile-time error if a leaf element of $e$ is a +It is a \Error{compile-time error} if a leaf element of $e$ is a \synt{mapElement}. -It is a compile-time error if, for some $j \in 1 .. m$, +It is a \Error{compile-time error} if, for some $j \in 1 .. m$, $\ell_j$ does not have an element type, or the element type of $\ell_j$ may not be assigned to $T$. @@ -10743,9 +10766,9 @@ \subsubsection{Lists} } \LMHash{}% -It is a compile-time error +It is a \Error{compile-time error} if an element of a constant list literal is not constant. -It is a compile-time error if the type argument of a constant list literal +It is a \Error{compile-time error} if the type argument of a constant list literal (\commentary{no matter whether it is explicit or inferred}) is not a constant type expression (\ref{constants}). @@ -10832,7 +10855,7 @@ \subsubsection{Set and Map Literal Disambiguation} \LMHash{}% Some terms are syntactically ambiguous because they contain a \lit{?} which may be considered part of a nullable type -or part of a \syntax{conditionalExpression}. +or part of a \synt{conditionalExpression}. In all such cases, the ambiguity is resolved in favor of the set literal and not the map literal. @@ -10921,7 +10944,7 @@ \subsubsection{Set and Map Literal Disambiguation} \code{Set<$T_1$>}. If $k = 2$ then $e$ is a map literal with static type \code{Map<$T_1$,\,\,$T_2$>}. - Otherwise a compile-time error occurs. + Otherwise a \Error{compile-time error} occurs. \item When $S$ implements (\ref{interfaceSuperinterfaces}) @@ -10933,7 +10956,7 @@ \subsubsection{Set and Map Literal Disambiguation} When ${\cal L} \not= \emptyset$ (\commentary{that is, $e$ has leaf elements}): If $\cal L$ contains a \synt{mapElement} as well as an \synt{expressionElement}, - a compile-time error occurs. + a \Error{compile-time error} occurs. Otherwise, if $\cal L$ contains an \synt{expressionElement}, $e$ is a set literal. Otherwise $\cal L$ contains a \synt{mapElement}, and $e$ is a map literal. @@ -11172,7 +11195,7 @@ \subsubsection{Set and Map Literal Inference} the inferred element type of $\ell$ is \code{Null}, and the inferred key and value type pair $(\code{Null}, \code{Null})$. \item - Otherwise, a compile-time error occurs. + Otherwise, a \Error{compile-time error} occurs. \end{itemize} \noindent @@ -11198,7 +11221,7 @@ \subsubsection{Set and Map Literal Inference} If $S$ is \code{Null} and the spread operator is \lit{...?}, the inferred element type of $\ell$ is \code{Null}. \item - Otherwise, a compile-time error occurs. + Otherwise, a \Error{compile-time error} occurs. \end{itemize} \noindent @@ -11225,7 +11248,7 @@ \subsubsection{Set and Map Literal Inference} If $S$ is \code{Null} and the spread operator is \lit{...?}, the inferred key and value type pair $(\code{Null}, \code{Null})$. \item - Otherwise, a compile-time error occurs. + Otherwise, a \Error{compile-time error} occurs. \end{itemize} \vspace{-5mm} \EndCase @@ -11285,7 +11308,7 @@ \subsubsection{Set and Map Literal Inference} where $P$ is derived from \synt{forLoopParts} and `\AWAIT?' indicates that \AWAIT{} may be present or absent. -The same compile-time errors occur for $\ell$ as +The same \Error{compile-time errors} occur for $\ell$ as the errors that would occur with the corresponding \FOR{} statement \code{\AWAIT?\,\,\FOR\,\,($P$)\,\,\{\}}, located in the same scope as $\ell$. @@ -11396,7 +11419,7 @@ \subsubsection{Set and Map Literal Inference} the least upper bound of the key types of the elements and $V$ is the least upper bound of the value types. \item - Otherwise, a compile-time error occurs. + Otherwise, a \Error{compile-time error} occurs. \commentary{In this case the literal cannot be disambiguated.} \end{itemize} \end{itemize} @@ -11456,9 +11479,9 @@ \subsubsection{Sets} \LMHash{}% Let $e$ be a set literal of the form \code{<$T$>\{\List{\ell}{1}{m}\}}. -It is a compile-time error if a leaf element of $e$ is a +It is a \Error{compile-time error} if a leaf element of $e$ is a \synt{mapElement}. -It is a compile-time error if, for some $j \in 1 .. m$, +It is a \Error{compile-time error} if, for some $j \in 1 .. m$, $\ell_j$ does not have an element type, or the element type of $\ell_j$ may not be assigned to $T$. @@ -11518,17 +11541,18 @@ \subsubsection{Sets} } \LMHash{}% -It is a compile-time error if +It is a \Error{compile-time error} if a collection literal element in a constant set literal is not a constant expression. It is a compile-time error if an element in a constant set literal does not have primitive equality (\ref{theOperatorEqualsEquals}). -It is a compile-time error if two elements of a constant set literal are equal +It is a +\Error{compile-time error} if two elements of a constant set literal are equal according to their \lit{==} operator (\ref{equality}). -It is a compile-time error if the type argument of a constant set literal +It is a \Error{compile-time error} if the type argument of a constant set literal (\commentary{no matter whether it is explicit or inferred}) is not a constant type expression (\ref{constants}). @@ -11641,9 +11665,9 @@ \subsubsection{Maps} \LMHash{}% Let $e$ be a map literal of the form \code{<$K$,\,$V$>\{\List{\ell}{1}{m}\}}. -It is a compile-time error if a leaf element of $e$ is an +It is a \Error{compile-time error} if a leaf element of $e$ is an \synt{expressionElement}. -It is a compile-time error if, for some $j \in 1 .. m$, +It is a \Error{compile-time error} if, for some $j \in 1 .. m$, $\ell_j$ does not have a key and value type pair; or the key and value type pair of $\ell_j$ is $(K_j, V_j)$, and $K_j$ may not be assigned to $K$ or @@ -11715,16 +11739,17 @@ \subsubsection{Maps} } \LMHash{}% -It is a compile-time error +It is a \Error{compile-time error} if a collection literal element in a constant map literal is not constant. It is a compile-time error if a key in a constant map literal does not have primitive equality (\ref{theOperatorEqualsEquals}). -It is a compile-time error if two keys of a constant map literal are equal +It is a +\Error{compile-time error} if two keys of a constant map literal are equal according to their \lit{==} operator (\ref{equality}). -It is a compile-time error if a type argument of a constant map literal +It is a \Error{compile-time error} if a type argument of a constant map literal (\commentary{no matter whether it is explicit or inferred}) is not a constant type expression (\ref{constants}). @@ -11815,7 +11840,7 @@ \subsection{Throw} \LMHash{}% Consider an expression of the form \code{\THROW\,\,$e$} where $e$ is an expression with static type $T$. -It is a compile-time error unless $T$ is assignable to \code{Object}. +It is a \Error{compile-time error} unless $T$ is assignable to \code{Object}. \LMHash{}% The static type of a throw expression is \code{Never}. @@ -12285,7 +12310,7 @@ \subsection{This} } \LMHash{}% -It is a compile-time error if \THIS{} appears, implicitly or explicitly, +It is a \Error{compile-time error} if \THIS{} appears, implicitly or explicitly, in a top-level function or variable initializer, in a factory constructor, or in a static method or variable initializer, or in the initializing expression of a non-late instance variable. @@ -12308,7 +12333,7 @@ \subsection{Instance Creation} } \LMHash{}% -It is a compile-time error if +It is a \Error{compile-time error} if the type $T$ in an instance creation expression of one of the forms \noindent @@ -12348,7 +12373,7 @@ \subsubsection{New} \code{\NEW{} $T$($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}. \LMHash{}% -It is a compile-time error if $T$ is not +It is a \Error{compile-time error} if $T$ is not a class or a parameterized type accessible in the current scope, or if $T$ is a parameterized type which is not a class. \commentary{% @@ -12369,12 +12394,12 @@ \subsubsection{New} \item If $e$ is of the form \code{\NEW{} $T$.\id($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} - it is a compile-time error if \code{$R$.\id} is not the name of + it is a \Error{compile-time error} if \code{$R$.\id} is not the name of a constructor declared by $R$, or \id{} is not accessible. \item If $e$ is of the form \code{\NEW{} $T$($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} - it is a compile-time error if $R$ is not the name of + it is a \Error{compile-time error} if $R$ is not the name of a constructor declared by $R$. \end{itemize} @@ -12382,15 +12407,15 @@ \subsubsection{New} Let $q$ be the above-mentioned constructor named \code{$R$.\id} or $R$. \LMHash{}% -It is a compile-time error if $R$ is abstract +It is a \Error{compile-time error} if $R$ is abstract and $q$ is not a factory constructor. -It is a compile-time error if $R$ is a non-generic class +It is a \Error{compile-time error} if $R$ is a non-generic class and $T$ is a parameterized type. %% We assume that inference has taken place, so actual type arguments %% are always given explicitly. -It is a compile-time error if $R$ is a generic class +It is a \Error{compile-time error} if $R$ is a generic class and $T$ is not a parameterized type. -It is a compile-time error if $R$ is a generic class, +It is a \Error{compile-time error} if $R$ is a generic class, $T$ is a parameterized type, and $m \not= p$. \commentary{That is, the number of type arguments is incorrect.} It is a compile-time error if $R$ is a generic class, @@ -12400,7 +12425,7 @@ \subsubsection{New} \LMHash{}% If $q$ is a redirecting factory constructor, -it is a compile-time error if $q$ in some number of +it is a \Error{compile-time error} if $q$ in some number of redirecting factory redirections redirects to itself. \commentary{% It is possible and allowed for a redirecting factory $q'$ @@ -12415,7 +12440,7 @@ \subsubsection{New} Let $S_i$ be the static type of the formal parameter of the constructor \code{$R$.\id} (respectively $R$) corresponding to the actual argument $a_i$, $i \in 1 .. n+k$. -It is a compile-time error if the static type of +It is a \Error{compile-time error} if the static type of $a_i, i \in 1 .. n + k$ is not assignable to $[U_1/X_1, \ldots, U_m/X_m]S_i$. \commentary{% @@ -12544,17 +12569,17 @@ \subsubsection{Const} \code{\CONST{} $T$($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}. \LMHash{}% -It is a compile-time error if $T$ is not +It is a \Error{compile-time error} if $T$ is not a class or a parameterized type accessible in the current scope, or if $T$ is a parameterized type which is not a class. -It is a compile-time error if $T$ is a deferred type +It is a \Error{compile-time error} if $T$ is a deferred type (\ref{staticTypes}). \commentary{% In particular, $T$ must not be a type variable.% } \LMHash{}% -It is a compile-time error if $a_i$ is not a constant expression +It is a \Error{compile-time error} if $a_i$ is not a constant expression for some $i \in 1 .. n + k$. \LMHash{}% @@ -12568,19 +12593,20 @@ \subsubsection{Const} \LMHash{}% If $T$ is a parameterized type, -it is a compile-time error if $U_j$ is not a constant type expression for any +it is a +\Error{compile-time error} if $U_j$ is not a constant type expression for any $j \in 1 .. m$. \begin{itemize} \item If $e$ is of the form \code{\CONST{} $T$.\id($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} - it is a compile-time error if \code{$R$.\id} is not the name of + it is a \Error{compile-time error} if \code{$R$.\id} is not the name of a constant constructor declared by $R$, or \id{} is not accessible. \item If $e$ is of the form \code{\CONST{} $T$($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} - it is a compile-time error if $R$ is not the name of + it is a \Error{compile-time error} if $R$ is not the name of a constant constructor declared by $R$. \end{itemize} @@ -12592,18 +12618,18 @@ \subsubsection{Const} %% of text in the previous subsection, or just loosely say "exactly the %% same errors".. \LMHash{}% -It is a compile-time error if $R$ is abstract +It is a \Error{compile-time error} if $R$ is abstract and $q$ is not a factory constructor. -It is a compile-time error if $R$ is a non-generic class +It is a \Error{compile-time error} if $R$ is a non-generic class and $T$ is a parameterized type. %% We assume that inference has taken place, so actual type arguments %% are always given explicitly. -It is a compile-time error if $R$ is a generic class +It is a \Error{compile-time error} if $R$ is a generic class and $T$ is not a parameterized type. -It is a compile-time error if $R$ is a generic class, +It is a \Error{compile-time error} if $R$ is a generic class, $T$ is a parameterized type, and $m \not= p$. \commentary{That is, the number of type arguments is incorrect.} -It is a compile-time error if $R$ is a generic class, +It is a \Error{compile-time error} if $R$ is a generic class, $T$ is a parameterized type, and $T$ is not regular-bounded (\ref{superBoundedTypes}). @@ -12612,7 +12638,7 @@ \subsubsection{Const} Let $S_i$ be the static type of the formal parameter of the constructor \code{$R$.\id} (respectively $R$) corresponding to the actual argument $a_i$, $i \in 1 .. n+k$. -It is a compile-time error if the static type of +It is a \Error{compile-time error} if the static type of $a_i, i \in 1 .. n + k$ is not assignable to $[U_1/X_1, \ldots, U_m/X_m]S_i$. \commentary{% @@ -12684,7 +12710,7 @@ \subsubsection{Const} } \LMHash{}% -It is a compile-time error if evaluation of a constant object +It is a \Error{compile-time error} if evaluation of a constant object results in an uncaught exception being thrown. \commentary{% @@ -13146,7 +13172,7 @@ \subsubsection{Binding Actuals to Formals} \LMHash{}% % We cannot pass the same named parameter twice. -It is a compile-time error if $q_j = q_k$ for any $j \ne k$. +It is a \Error{compile-time error} if $q_j = q_k$ for any $j \ne k$. \commentary{% Note that $f$ denotes a function in a semantic sense, @@ -13186,11 +13212,11 @@ \subsubsection{Binding Actuals to Formals} no further static checks are performed on the invocation $i$ (\commentary{apart from separate static checks on subterms like arguments}), and the static type of $i$ is \DYNAMIC. -Otherwise, it is a compile-time error if the static type of $f$ is not +Otherwise, it is a \Error{compile-time error} if the static type of $f$ is not function-type bounded. \LMHash{}% -A compile-time error occurs if the static type of $f$ is potentially nullable +A \Error{compile-time error} occurs if the static type of $f$ is potentially nullable and not \DYNAMIC{} (\ref{typeNullability}). @@ -13283,11 +13309,11 @@ \subsubsection{Binding Actuals to Formals} \LMHash{}% % Type inference is assumed complete, so we must have % the correct number of type arguments. -It is a compile-time error if $r \not= s$. -It is a compile-time error if $r = s$ and for some $j \in 1 .. s$, +It is a \Error{compile-time error} if $r \not= s$. +It is a \Error{compile-time error} if $r = s$ and for some $j \in 1 .. s$, $A_j \not<: [A_1/X_1, \ldots, A_r/X_s]B_j$. -It is a compile-time error unless $h \le m \le n$. -It is a compile-time error unless both of the following are satisfied: +It is a \Error{compile-time error} unless $h \le m \le n$. +It is a \Error{compile-time error} unless both of the following are satisfied: \begin{itemize} \item If $l > 0$ then $F$ has named parameters and @@ -13310,9 +13336,9 @@ \subsubsection{Binding Actuals to Formals} The static type of $i$ is $[A_1/X_1, \ldots, A_r/X_s]S_0$. \LMHash{}% -It is a compile-time error if $T_j$ may not be assigned to +It is a \Error{compile-time error} if $T_j$ may not be assigned to $S_j, j \in 1 .. m$. -It is a compile-time error if $T_{m+j}$ may not be assigned to +It is a \Error{compile-time error} if $T_{m+j}$ may not be assigned to $S_{q_j}, j \in 1 .. l$. \commentary{% @@ -13461,7 +13487,7 @@ \subsubsection{Unqualified Invocation} then $i$ is treated as \code{\NEW\,\,$i$}. If $D$ is not a class declaration, or it declares a class named $C$ that has no constructor named $C$, - a compile-time error occurs. + a \Error{compile-time error} occurs. \item Otherwise, if $D$ is a declaration of a local function, @@ -13504,7 +13530,7 @@ \subsubsection{Unqualified Invocation} \Case{Lexical lookup yields an import prefix} When the lexical lookup of \id{} yields an import prefix, -a compile-time error occurs. +a \Error{compile-time error} occurs. \EndCase \Case{Lexical lookup yields nothing} @@ -13555,7 +13581,7 @@ \subsubsection{Function Expression Invocation} \LMHash{}% Consider the situation where $e_f$ denotes a class $C$ that contains a declaration of a constructor named $C$, -or it is of the form \code{$e'_f$.\id} +or it is of the form \code{$e'_f$.\id} or \code{$e'_f$<\metavar{typeArguments}>.\id} where $e'_f$ denotes a class $C$ that contains a declaration of a constructor named \code{$C$.\id}. @@ -13573,7 +13599,7 @@ \subsubsection{Function Expression Invocation} } \LMHash{}% -Otherwise, it is a compile-time error if $e_f$ is a type literal. +Otherwise, it is a \Error{compile-time error} if $e_f$ is a type literal. \commentary{% This error was already specified elsewhere @@ -14373,30 +14399,48 @@ \subsubsection{Ordinary Invocation} $i$ of the form \noindent -\code{$e$?.$m$<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}. +\code{% +$e$?.$m$<\List{A}{1}{r}>(\List{a}{1}{n},\ \PairList{x}{\!\!:\,\,a}{n+1}{n+k})}. \commentary{% Note that non-generic invocations arise as the special case where the number of type arguments is zero, -in which case the type argument list is omitted, -and similarly for formal type parameter lists (\ref{generics}).% +in which case the type argument list is omitted +(\ref{generics}).% } +%% TODO(eernst): We are likely to decide that this is an error. \LMHash{}% -A warning occurs if the static type of $e$ is non-nullable. -\commentary{In this case, the \lit{?} is superfluous.} +A warning occurs if $e$ is a type literal, +and $i$ is then treated as + +\noindent +\code{% +$e$.$m$<\List{A}{1}{r}>(\List{a}{1}{n},\ \PairList{x}{\!\!:\,\,a}{n+1}{n+k})}. \LMHash{}% -Otherwise, $i$ is treated as +Let $S$ be the static type of $e$. +A warning occurs if $S$ is non-nullable. +\commentary{In this case, the \lit{?} is superfluous.} +Apart from that, +the static analysis of $i$ yields +exactly the \Error{same compile-time errors} and warnings as +the static analysis of the following expression $i'$, +where $v$ is a fresh variable whose type is \NonNullType{$S$}: \noindent -\Let{$v$}{$e$}{$v$\,\,==\,\,\NULL\ ?\ \NULL\ :\ $v$.$m$<$A_1, \ldots,\ A_r$>(} +\code{% +$v$.$m$<\List{A}{1}{r}>(\List{a}{1}{n},\ \PairList{x}{\!\!:\,\,a}{n+1}{n+k})}. \noindent -\code{\qquad{}$a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} +Let $T$ be the static type of $i'$. +The static type of $i$ is then \code{$T$?}. -\noindent -which determines the static analysis and dynamic semantics of $i$. +\LMHash{}% +The dynamic semantics of $i$ is determined by the semantics of other constructs, +because all conditional ordinary method invocations are eliminated by the +null-shortening transformation +(\ref{nullShorteningTransformation}). \EndCase \LMHash{}% @@ -14420,7 +14464,7 @@ \subsubsection{Ordinary Invocation} \LMHash{}% Otherwise (\commentary{when $C$ does not denote a type alias}), -a compile-time error occurs +a \Error{compile-time error} occurs unless $C$ denotes a class, a mixin, or an extension that declares a static member named $m$, which we will call the @@ -14428,7 +14472,7 @@ \subsubsection{Ordinary Invocation} of $i$. When the denoted member is a static method, let $F$ be its function type; when the denoted member is a static getter, let $F$ be its return type; -when the denoted member is neither, a compile-time error occurs. +when the denoted member is neither, a \Error{compile-time error} occurs. \LMHash{}% The static analysis of $i$ is then performed @@ -14490,7 +14534,7 @@ \subsubsection{Ordinary Invocation} whose static analysis is specified separately below. \LMHash{}% -Otherwise, it is a compile-time error if $T$ does not have an accessible +Otherwise, it is a \Error{compile-time error} if $T$ does not have an accessible (\ref{privacy}) instance member named $m$, unless either: @@ -14640,7 +14684,7 @@ \subsubsection{Ordinary Invocation} \end{itemize} \LMHash{}% -It is a compile-time error to invoke an instance method on a type literal +It is a \Error{compile-time error} to invoke an instance method on a type literal that is immediately followed by the token `.' (a period). \commentary{% For instance, \code{int.toString()} is an error.% @@ -14656,6 +14700,7 @@ \subsubsection{Ordinary Invocation} to an object---that would not even be possible for an extension.% } +%% TODO(eernst): If we decide that C?.id() is an error, remove it. \commentary{% A member access on a type literal (e.g., \code{C.id()}, \code{C.id}, or \code{C?.id()}), @@ -14891,7 +14936,7 @@ \subsubsection{Superinvocations} } \LMHash{}% -It is a compile-time error if a method superinvocation occurs +It is a \Error{compile-time error} if a method superinvocation occurs in a top-level function or variable initializer, in an instance variable initializer or initializer list, in class \code{Object}, @@ -14917,7 +14962,7 @@ \subsubsection{Superinvocations} the getter $m$ with respect to $L$ in \SuperClass{} (\ref{lookup}), and let $F$ be the return type of $D$. -If both lookups failed, a compile-time error occurs. +If both lookups failed, a \Error{compile-time error} occurs. \LMHash{}% Otherwise (\commentary{when one of the lookups succeeded}), @@ -15036,19 +15081,26 @@ \subsection{Property Extraction} property extraction!conditional} $i$ of the form \code{$e$?.\id}. +%% TODO(eernst): We are likely to decide that this is an error. \LMHash{}% -%% TODO(eernst): 'A compile-time error occurs if $e$ is a type literal.' -%% when we change the rules about type literals as receivers. -If $e$ is a type literal, $i$ is treated as \code{$e$.\id}. +A warning occurs if $e$ is a type literal, +and $i$ is then treated as \code{$e$.\id}. \LMHash{}% -A warning occurs if the static type of $e$ is non-nullable. +Let $S$ be the static type of $e$. +A warning occurs if $S$ is non-nullable. \commentary{In this case, the \lit{?} is superfluous.} +Apart from that, +the static analysis of $i$ yields +exactly the \Error{same compile-time errors} and warnings as +the static analysis of the expression \code{$v$.\id}, +where $v$ is a fresh variable whose type is \NonNullType{$S$}. \LMHash{}% -Otherwise, $i$ is treated as -\Let{$v$}{$e$}{$v$\,\,==\,\,\NULL\ ?\ \NULL\ :\ $v$.\id}, -which determines the static analysis and dynamic semantics. +The dynamic semantics of $i$ is determined by the semantics of other constructs, +because all conditional property extraction expressions are eliminated by the +null-shortening transformation +(\ref{nullShorteningTransformation}). \EndCase \LMHash{}% @@ -15066,7 +15118,7 @@ \subsection{Property Extraction} (\ref{typedef}). \LMHash{}% -A compile-time error occurs +A \Error{compile-time error} occurs unless $C$ denotes a class, a mixin, or an extension that declares a static member named $m$, which we will call the @@ -15076,7 +15128,7 @@ \subsection{Property Extraction} the static type of $i$ is the return type of said getter; if the denoted member is a static method, the static type of $i$ is the function type of said method; -if the denoted member is neither, a compile-time error occurs. +if the denoted member is neither, a \Error{compile-time error} occurs. \LMHash{}% Evaluation of a static property extraction $i$ of the form \code{$C$.\id} @@ -15129,7 +15181,7 @@ \subsubsection{Getter Invocation and Method Closurization} Consider an unconditional property extraction $i$ (\ref{propertyExtraction}) of the form \code{$e$.\id}. -It is a compile-time error if \id{} is the name of +It is a \Error{compile-time error} if \id{} is the name of an instance member of the built-in class \code{Object} and $e$ is a type literal. @@ -15164,11 +15216,11 @@ \subsubsection{Getter Invocation and Method Closurization} whose static analysis is specified separately below. \LMHash{}% -Otherwise, it is a compile-time error if $T$ does not have +Otherwise, it is a \Error{compile-time error} if $T$ does not have a method or getter named \id{} unless $T$ is \DYNAMIC{} bounded, or $T$ is \FUNCTION{} bounded -(\ref{bindingActualsToFormals}) +(\ref{typesBoundedByTypes}) and \id{} is \CALL. The static type of $i$ is: @@ -15266,7 +15318,7 @@ \subsubsection{Super Getter Access and Method Closurization} \LMHash{}% Let $S$ be the superclass of the immediately enclosing class. -It is a compile-time error if $S$ does not have +It is a \Error{compile-time error} if $S$ does not have an accessible instance method or getter named \id. The static type of $i$ is: @@ -15886,7 +15938,7 @@ \subsection{Assignment} \item When the lexical lookup yields a declaration $D$ of a local variable $v$ (\commentary{which may be a formal parameter}), - a compile-time error occurs if $v$ is final + a \Error{compile-time error} occurs if $v$ is final or if the static type of $e$ is not assignable to the declared type of $v$. \item When the lexical lookup yields a declaration $D$ @@ -15906,7 +15958,7 @@ \subsection{Assignment} proceeds as specified elsewhere.% } - Otherwise, a compile-time error occurs, + Otherwise, a \Error{compile-time error} occurs, unless the static type of $e$ is assignable to the parameter type of $D$. \item When the lexical lookup yields nothing, @@ -15984,7 +16036,7 @@ \subsection{Assignment} where $p$ is an import prefix and \id{} is an identifier. \LMHash{}% -A compile-time error occurs, +A \Error{compile-time error} occurs, unless $p$ has a member which is a setter $s$ named \code{id=} (\commentary{which may be implicitly induced by a variable declaration}) such that the static type of $e$ @@ -15993,7 +16045,6 @@ \subsection{Assignment} \LMHash{}% The static type of $a$ is the static type of $e$. -\LMHash{}% \LMHash{}% Evaluation of an assignment $a$ of the form \code{$p$.\id{} = $e$} proceeds as follows: @@ -16004,20 +16055,33 @@ \subsection{Assignment} \EndCase \LMHash{}% -\Case{\code{$e_1$?.$v$ = $e_2$}} -Consider an assignment $a$ of the form \code{$e_1$?.$v$ = $e_2$}. -Exactly the same compile-time errors that would be caused by -\code{$e_1$.$v$ = $e_2$} are also generated in the case of $a$. -The static type of $a$ is the static type of $e_2$. +\Case{\code{$e_1$?.\id{} = $e_2$}} +Consider an assignment $a$ of the form \code{$e_1$?.\id{} = $e_2$}. + +%% TODO(eernst): We are likely to decide that this is an error. +\LMHash{}% +A warning occurs if $e_1$ is a type literal, +and $a$ is then treated as \code{$e_1$.\id{} = $e_2$}. + +\LMHash{}% +Let $S$ be the static type of $e_1$. +A warning occurs if $S$ is non-nullable. +\commentary{In this case, the \lit{?} is superfluous.} +Apart from that, +the static analysis of $a$ yields +exactly the \Error{same compile-time errors} and warnings as +the static analysis of \code{$v$.\id{} = $e_2$}, +where $v$ is a fresh variable whose type is \NonNullType{$S$}. +Let $T$ be the static type of $e_2$. +The static type of $a$ is then $T?$. \LMHash{}% Evaluation of an assignment $a$ of the form \code{$e_1$?.$v$ = $e_2$} proceeds as follows: -If $e_1$ is a type literal, $a$ is equivalent to \code{$e_1$.$v$ = $e_2$}. -Otherwise evaluate $e_1$ to an object $o$. +Evaluate $e_1$ to an object $o$. If $o$ is the null object, $a$ evaluates to the null object (\ref{null}). -Otherwise let $x$ be a fresh variable bound to $o$ -and evaluate \code{$x$.$v$ = $e_2$} to an object $r$. +Otherwise let $v$ be a fresh variable bound to $o$ +and evaluate \code{$v$.\id{} = $e_2$} to an object $r$. Then $a$ evaluates to $r$. \EndCase @@ -16026,9 +16090,9 @@ \subsection{Assignment} Consider an assignment $a$ of the form \code{$e_1$.$v$ = $e_2$}. Let $T$ be the static type of $e_1$. If $T$ is \DYNAMIC, no further checks are performed. -Otherwise, it is a compile-time error unless +Otherwise, it is a \Error{compile-time error} unless $T$ has an accessible instance setter named \code{$v$=}. -It is a compile-time error unless the static type of $e_2$ +It is a \Error{compile-time error} unless the static type of $e_2$ may be assigned to the declared type of the formal parameter of said setter. Whether or not $T$ is \DYNAMIC, the static type of $a$ is the static type of $e_2$. @@ -16083,9 +16147,9 @@ \subsection{Assignment} \Case{\code{\SUPER.$v$ = $e$}} Consider an assignment $a$ of the form \code{\SUPER.$v$ = $e$}. Let $S_{static}$ be the superclass of the immediately enclosing class. -It is a compile-time error if $S_{static}$ does not have +It is a \Error{compile-time error} if $S_{static}$ does not have a concrete accessible instance setter named \code{$v$=}. -Otherwise, it is a compile-time error if the static type of $e$ +Otherwise, it is a \Error{compile-time error} if the static type of $e$ may not be assigned to the static type of the formal parameter of said setter. The static type of $a$ is the static type of $e$. @@ -16122,12 +16186,13 @@ \subsection{Assignment} Consider an assignment $a$ of the form \code{$e_1$[$e_2$] = $e_3$}. Let $T$ be the static type of $e_1$. If $T$ is \DYNAMIC, no further checks are performed. -Otherwise, it is a compile-time error unless +Otherwise, it is a \Error{compile-time error} unless $T$ has a method named \code{[]=}. Let $S_2$ be the static type of the first formal parameter of the method \code{[]=}, and $S_3$ the static type of the second. -It is a compile-time error unless the static type of $e_2$ respectively $e_3$ +It is a +\Error{compile-time error} unless the static type of $e_2$ respectively $e_3$ may be assigned to $S_2$ respectively $S_3$. Whether or not $T$ is \DYNAMIC, the static type of $a$ is the static type of $e_3$. @@ -16149,11 +16214,13 @@ \subsection{Assignment} \Case{\code{\SUPER[$e_1$] = $e_2$}} Consider an assignment $a$ of the form \code{\SUPER[$e_1$] = $e_2$}. Let $S_{static}$ be the superclass of the immediately enclosing class. -It is a compile-time error if $S_{static}$ does not have a method \code{[]=}. +It is a +\Error{compile-time error} if $S_{static}$ does not have a method \code{[]=}. Otherwise, let $S_1$ be the static type of the first formal parameter of the method \code{[]=}, and $S_2$ the static type of the second. -It is a compile-time error if the static type of $e_1$ respectively $e_2$ +It is a +\Error{compile-time error} if the static type of $e_1$ respectively $e_2$ may not be assigned to $S_1$ respectively $S_2$. The static type of $a$ is the static type of $e_2$. @@ -16170,9 +16237,15 @@ \subsubsection{Compound Assignment} \Case{\code{$v$ ??= $e$}} Consider a compound assignment $a$ of the form \code{$v$ ??= $e$} where $v$ is an identifier or an identifier qualified by an import prefix. -Exactly the same compile-time errors that would be caused by -\code{$v$ = $e$} -are also generated in the case of $a$. + +\LMHash{}% +Let $S$ be the static type of $v$. +A warning occurs if $S$ is non-nullable. +\commentary{In this case, the \lit{??} is superfluous.} +Apart from that, +the static analysis of $a$ yields +exactly the \Error{same compile-time errors} and warnings as +the static analysis of \code{$v$ = $e$}. The static type of $a$ is the least upper bound of the static type of $v$ and the static type of $e$. @@ -16186,58 +16259,75 @@ \subsubsection{Compound Assignment} \EndCase \LMHash{}% -\Case{\code{$C$.$v$ ??= $e$}} -Consider a compound assignment $a$ of the form \code{$C$.$v$ ??= $e$} +\Case{\code{$C$.\id{} ??= $e$}} +Consider a compound assignment $a$ of the form \code{$C$.\id{} ??= $e$} where $C$ is a type literal -that may or may not be qualified by an import prefix. -Exactly the same compile-time errors that would be caused by -\code{$C$.$v$ = $e$} -are also generated in the case of $a$. +that may or may not be qualified by an import prefix, +and \id{} is an identifier. + +\LMHash{}% +The static analysis of $a$ yields +the \Error{same compile-time errors} and warnings as +the static analysis of \code{$C$.\id{} = $e$}. +Moreover, a warning occurs if +the static type of \code{$C$.\id} is non-nullable. +\commentary{In this case, the \lit{??} is superfluous.} The static type of $a$ is the least upper bound of -the static type of \code{$C$.$v$} and the static type of $e$. +\NonNullType{$S$} and the static type of $e$. \LMHash{}% -Evaluation of a compound assignment $a$ of the form \code{$C$.$v$ ??= $e$} +Evaluation of a compound assignment $a$ of the form \code{$C$.\id{} ??= $e$} where $C$ is a type literal proceeds as follow: -Evaluate \code{$C$.$v$} to an object $o$. -If $o$ is not the null object (\ref{null}), $a$ evaluates to $o$. -Otherwise evaluate \code{$C$.$v$ = $e$} to an object $r$, +Evaluate \code{$C$.\id} to an object $o$. +If $o$ is not the null object (\ref{null}) then $a$ evaluates to $o$. +Otherwise evaluate \code{$C$.\id{} = $e$} to an object $r$, and then $a$ evaluates to $r$. \EndCase \LMHash{}% -\Case{\code{$e_1$.$v$ ??= $e_2$}} -Consider a compound assignment $a$ of the form \code{$e_1$.$v$ ??= $e_2$}. -Let $T$ be the static type of $e_1$ and let $x$ be a fresh variable of type $T$. -Except for errors inside $e_1$ and references to the name $x$, -exactly the same compile-time errors that would be caused by -\code{$x$.$v$ = $e_2$} -are also generated in the case of $a$. -Moreover, it is a compile-time error if $T$ does not have a getter named $v$. +\Case{\code{$e_1$.\id{} ??= $e_2$}} +Consider a compound assignment $a$ of the form \code{$e_1$.\id{} ??= $e_2$}, +where $e_1$ and $e_2$ are expressions and \id{} is an identifier. + +\LMHash{}% +The static analysis of $a$ yields +the \Error{same compile-time errors} and warnings as +the static analysis of \code{$e_1$.\id{} = $e_2$}. +Moreover, it is a \Error{compile-time error} +if the static type of $e_1$ does not have a getter named \id. +Let $S$ be the static type of \code{$e_1$.\id}. +A warning occurs if $S$ is non-nullable. +\commentary{In this case, the \lit{??} is superfluous.} The static type of $a$ is the least upper bound of -the static type of \code{$e_1$.$v$} and the static type of $e_2$. +\NonNullType{$S$} and the static type of $e_2$. \LMHash{}% -Evaluation of a compound assignment $a$ of the form \code{$e_1$.$v$ ??= $e_2$} +Evaluation of a compound assignment $a$ of the form \code{$e_1$.\id{} ??= $e_2$} proceeds as follows: Evaluate $e_1$ to an object $u$. -Let $x$ be a fresh variable bound to $u$. -Evaluate \code{$x$.$v$} to an object $o$. -If $o$ is not the null object (\ref{null}), $a$ evaluates to $o$. -Otherwise evaluate \code{$x$.$v$ = $e_2$} to an object $r$, +Let $v$ be a fresh variable bound to $u$. +Evaluate \code{$v$.\id} to an object $o$. +If $o$ is not the null object (\ref{null}) then $a$ evaluates to $o$. +Otherwise evaluate \code{$v$.\id{} = $e_2$} to an object $r$, and then $a$ evaluates to $r$. \EndCase \LMHash{}% \Case{\code{$e_1$[$e_2$] ??= $e_3$}} -Consider a compound assignment $a$ of the form \code{$e_1$[$e_2$] ??= $e_3$}. -Exactly the same compile-time errors that would be caused by -\code{$e_1$[$e_2$] = $e_3$} -are also generated in the case of $a$. -Moreover, it is a compile-time error +Consider a compound assignment $a$ of the form \code{$e_1$[$e_2$] ??= $e_3$}, +where $e_1$, $e_2$, and $e_3$ are expressions. + +\LMHash{}% +The static analysis of $a$ yields +the \Error{same compile-time errors} and warnings as +the static analysis of \code{$e_1$[$e_2$] = $e_3$}. +Moreover, it is a \Error{compile-time error} if the static type of $e_1$ does not have an `\code{operator []}'. +Let $S$ be the static type of \code{$e_1$[$e_2$]}. +A warning occurs if $S$ is non-nullable. +\commentary{In this case, the \lit{??} is superfluous.} The static type of $a$ is the least upper bound of -the static type of \code{$e_1$[$e_2$]} and the static type of $e_3$. +\NonNullType{$S$} and the static type of $e_3$. \LMHash{}% Evaluation of a compound assignment $a$ of the form @@ -16254,142 +16344,168 @@ \subsubsection{Compound Assignment} \EndCase \LMHash{}% -\Case{\code{\SUPER.$v$ ??= $e$}} -Consider a compound assignment $a$ of the form \code{\SUPER.$v$ ??= $e$}. -Exactly the same compile-time errors that would be caused by -\code{\SUPER.$v$ = $e$} -are also generated in the case of $a$. -Moreover, exactly the same compile-time errors that would be caused by -evaluation of the expression \code{\SUPER.$v$} -are also generated in the case of $a$. +\Case{\code{\SUPER.\id{} ??= $e$}} +Consider a compound assignment $a$ of the form \code{\SUPER.\id{} ??= $e$}, +where $e$ is an expression and \id{} is an identifier. + +\LMHash{}% +The static analysis of $a$ yields +the \Error{same compile-time errors} and warnings as +the static analysis of \code{\SUPER.\id{} = $e$}. +Moreover, it is a \Error{compile-time error} +if the interface of the superclass of the enclosing class +does not have a getter named \id. +Let $S$ be the return type of said getter. +A warning occurs if $S$ is non-nullable. +\commentary{In this case, the \lit{??} is superfluous.} The static type of $a$ is the least upper bound of -the static type of \code{\SUPER.$v$} and the static type of $e$. +\NonNullType{$S$} and the static type of $e$. \LMHash{}% -Evaluation of a compound assignment $a$ of the form \code{\SUPER.$v$ ??= $e$} +Evaluation of a compound assignment $a$ of the form \code{\SUPER.\id{} ??= $e$} proceeds as follows: -Evaluate \code{\SUPER.$v$} to an object $o$. +Evaluate \code{\SUPER.\id} to an object $o$. If $o$ is not the null object (\ref{null}) then $a$ evaluates to $o$. -Otherwise evaluate \code{\SUPER.$v$ = $e$} to an object $r$, +Otherwise evaluate \code{\SUPER.\id{} = $e$} to an object $r$, and then $a$ evaluates to $r$. \EndCase \LMHash{}% -\Case{\code{$e_1$?.$v$ ??= $e_2$}} -Consider a compound assignment $a$ of the form \code{$e_1$?.$v$ ??= $e_2$}. -Exactly the same compile-time errors that would be caused by -\code{$e_1$.$v$ ??= $e_2$} -are also generated in the case of $a$. -% Note: We use the static type of \code{$e_1$?.$v$} rather than -% \code{$e_1$.$v$} even though the latter would be simpler. This is because -% the former will remain correct if NNBD is introduced, and because it reduces -% the amount of synthetic syntax. +\Case{\code{$e_1$?.\id{} ??= $e_2$}} +Consider a compound assignment $a$ of the form \code{$e_1$?.\id{} ??= $e_2$}, +where $e_1$ and $e_2$ are expressions and \id{} is an identifier. + +%% TODO(eernst): We are likely to decide that this is an error. +\LMHash{}% +A warning occurs if $e_1$ is a type literal, +and $a$ is then treated as \code{$e_1$.\id{} ??= $e_2$}. + +\LMHash{}% +The static analysis of $a$ yields +the \Error{same compile-time errors} and warnings as +the static analysis of \code{$e_1$?.\id{} = $e_2$}. +Moreover, let $S$ be the static type of $e_1$. +It is a \Error{compile-time error} +if \NonNullType{$S$} does not have a getter named \id. +Let $U$ be the return type of said getter. +A warning occurs if $U$ is non-nullable. +\commentary{In this case, the \lit{??} is superfluous.} The static type of $a$ is the least upper bound of -the static type of \code{$e_1$?.$v$} and the static type of $e_2$. +\NonNullType{$U$} and the static type of $e_2$. \LMHash{}% -Evaluation of a compound assignment $a$ of the form \code{$e_1$?.$v$ ??= $e_2$} +Evaluation of a compound assignment $a$ of the form +\code{$e_1$?.\id{} ??= $e_2$} proceeds as follows: Evaluate $e_1$ to an object $u$. If $u$ is the null object (\ref{null}) then $a$ evaluates to the null object. -Otherwise, let $x$ be a fresh variable bound to $u$. -Evaluate \code{$x$.$v$} to an object $o$. +Otherwise, let $v$ be a fresh variable bound to $u$. +Evaluate \code{$v$.\id} to an object $o$. If $o$ is not the null object (\ref{null}) then $a$ evaluates to $o$. -Otherwise evaluate \code{$x$.$v$ = $e_2$} to an object $r$, +Otherwise evaluate \code{$v$.\id{} = $e_2$} to an object $r$, and then $a$ evaluates to $r$. \EndCase \LMHash{}% -\Case{\code{$C$?.$v$ ??= $e_2$}} -A compound assignment of the form \code{$C$?.$v$ ??= $e_2$} -where $C$ is a type literal -that may or may not be qualified by an import prefix -is equivalent to the expression \code{$C$.$v$ ??= $e$}. +\Case{\code{$v$ \op= $e$}} +Let \op{} be any valid operator different from \lit{??}. +Consider a compound assignment $a$ of the form \code{$v$ \op= $e$}, +where $v$ is an identifier which may be prefixed by an import prefix, +and $e$ is an expression. +$a$ is treated as \code{$v$ = $v$ \op{} $e$}. \EndCase \LMHash{}% -\Case{\code{$v$ $op$= $e$}} -For any other valid operator $op$, -a compound assignment of the form \code{$v$ $op$= $e$} -is equivalent to \code{$v$ = $v$ $op$ $e$}, -where $v$ is an identifier or an identifier qualified by an import prefix. +\Case{\code{$C$.\id{} \op= $e$}} +Let \op{} be any valid operator different from \lit{??}. +Consider a compound assignment $a$ +of the form \code{$C$.\id{} \op= $e$} +where $C$ is a type literal, $e$ is an expression, and +\id{} is an identifier that may or may not be qualified by an import prefix. +$a$ is treated as \code{$C$.\id{} = $C$.\id{} \op{} $e$}. \EndCase \LMHash{}% -\Case{\code{$C$.$v$ $op$= $e$}} -A compound assignment of the form \code{$C$.$v$ $op$= $e$} -where $C$ is a type literal -that may or may not be qualified by an import prefix -is equivalent to \code{$C$.$v$ = $C$.$v$ $op$ $e$}. -\EndCase - -\LMHash{}% -\Case{\code{$e_1$.$v$ $op$= $e_2$}} -Consider a compound assignment $a$ of the form \code{$e_1$.$v$ $op$= $e_2$}. -Let $x$ be a fresh variable whose static type is the static type of $e_1$. -Except for errors inside $e_1$ and references to the name $x$, -exactly the same compile-time errors that would be caused by -\code{$x$.$v$ = $x$.$v$ $op$ $e_2$} +\Case{\code{$e_1$.\id{} \op= $e_2$}} +Let \op{} be any valid operator different from \lit{??}. +Consider a compound assignment $a$ +of the form \code{$e_1$.\id{} \op= $e_2$}. +Let $v$ be a fresh variable whose static type is the static type of $e_1$. +The same \Error{compile-time errors} that would be caused by +\code{$v$.\id{} = $v$.\id{} \op{} $e_2$} are also generated in the case of $a$. -The static type of $a$ is the static type of \code{$e_1$.$v$ $op$ $e_2$}. +The static type of $a$ is the static type of +\code{$e_1$.\id{} \op{} $e_2$}. \LMHash{}% -Evaluation of a compound assignment $a$ of the form \code{$e_1$.$v$ $op$= $e_2$} +Evaluation of a compound assignment $a$ +of the form \code{$e_1$.\id{} \op= $e_2$} proceeds as follows: -Evaluate $e_1$ to an object $u$ and let $x$ be a fresh variable bound to $u$. -Evaluate \code{$x$.$v$ = $x$.$v$ $op$ $e_2$} to an object $r$ -and then $a$ evaluates to $r$. +Evaluate $e_1$ to an object $u$. +Let $v$ be a fresh variable bound to $u$. +Evaluate \code{$v$.\id{} = $v$.\id{} \op{} $e_2$} to an object $r$. +Then $a$ evaluates to $r$. \EndCase \LMHash{}% -\Case{\code{$e_1$[$e_2$] $op$= $e_3$}} -Consider a compound assignment $a$ of the form \code{$e_1$[$e_2$] $op$= $e_3$}. -Let $x$ and $i$ be fresh variables +\Case{\code{$e_1$[$e_2$] \op= $e_3$}} +Let \op{} be any valid operator different from \lit{??}. +Consider a compound assignment $a$ +of the form \code{$e_1$[$e_2$] \op= $e_3$}. +Let $v$ and $i$ be fresh variables where the static type of the former is the static type of $e_1$ and the static type of the latter is the static type of $e_2$. -Except for errors inside $e_1$ and $e_2$ and references to -the names $x$ and $i$, -exactly the same compile-time errors that would be caused by -\code{$x$[$i$] = $x$[$i$] $op$ $e_3$} +The same \Error{compile-time errors} that would be caused by +\code{$v$[$i$] = $v$[$i$] \op{} $e_3$} are also generated in the case of $a$. -The static type of $a$ is the static type of \code{$x$[$i$] $op$ $e_3$}. +The static type of $a$ is the static type of +\code{$v$[$i$] \op{} $e_3$}. \LMHash{}% -Evaluation of s compound assignment $a$ of the form -\code{$e_1$[$e_2$] $op$= $e_3$} +Evaluation of a compound assignment $a$ of the form +\code{$e_1$[$e_2$] \op= $e_3$} proceeds as follows: -Evaluate $e_1$ to an object $u$ and evaluate $e_2$ to an object $v$. -Let $x$ and $i$ be fresh variables bound to $u$ and $v$ respectively. -Evaluate \code{$x$[$i$] = $x$[$i$] $op$ $e_3$} to an object $r$, -and then $a$ evaluates to $r$. +Evaluate $e_1$ to an object $u$ and evaluate $e_2$ to an object $o$. +Let $v$ and $i$ be fresh variables bound to $u$ and $o$ respectively. +Evaluate \code{$v$[$i$] = $v$[$i$] \op{} $e_3$} to an object $r$. +Then $a$ evaluates to $r$. \EndCase \LMHash{}% -\Case{\code{$e_1$?.$v$ $op$= $e_2$}} -Consider a compound assignment $a$ of the form \code{$e_1$?.$v$ $op$= $e_2$}. -Exactly the same compile-time errors that would be caused by -\code{$e_1$.$v$ $op$= $e_2$} -are also generated in the case of $a$. -The static type of $a$ is the static type of \code{$e_1$.$v$ $op$= $e_2$}. +\Case{\code{$e_1$?.\id{} \op= $e_2$}} +Let \op{} be any valid operator different from \lit{??}. +Consider a compound assignment $a$ +of the form \code{$e_1$?.\id{} \op= $e_2$}, +where $e_1$ and $e_2$ are expressions and \id{} is an identifier. + +%% TODO(eernst): We are likely to decide that this is an error. +\LMHash{}% +A warning occurs if $e_1$ is a type literal, +and $a$ is then treated as \code{$e_1$.\id{} \op= $e_2$}. + +\LMHash{}% +Let $S$ be the static type of $e_1$. +A warning occurs if $S$ is non-nullable. +\commentary{In this case, the \lit{?} is superfluous.} +Apart from that, +the static analysis of $a$ yields +the same \Error{compile-time errors} and warnings as +the static analysis of $a'$ which is \code{$v$.\id{} \op= $e_2$}, +where $v$ is a fresh variable whose type is \NonNullType{$S$}. +Let $T$ be the static type of $a'$. +The static type of $a$ is then $T?$. \LMHash{}% Evaluation of a compound assignment $a$ of the form -\code{$e_1$?.$v$ $op$= $e_2$} +\code{$e_1$?.\id{} \op= $e_2$} proceeds as follows: -Evaluate $e_1$ to an object $u$. -If $u$ is the null object, then $a$ evaluates to the null object (\ref{null}). -Otherwise let $x$ be a fresh variable bound to $u$. -Evaluate \code{$x$.$v$ $op$= $e_2$} to an object $r$. +Evaluate $e_1$ to an object $o$. +If $o$ is the null object, then $a$ evaluates to the null object (\ref{null}). +Otherwise let $v$ be a fresh variable bound to $o$. +Evaluate \code{$v$.\id{} \op= $e_2$} to an object $r$. Then $a$ evaluates to $r$. \EndCase -\LMHash{}% -\Case{\code{$C$?.$v$ $op$ = $e_2$}} -A compound assignment of the form \code{$C$?.$v$ $op$ = $e_2$} -where $C$ is a type literal -is equivalent to the expression \code{$C$.$v$ $op$ = $e_2$}. -\EndCase - \subsection{Conditional} \LMLabel{conditional} @@ -16428,7 +16544,7 @@ \subsection{Conditional} \end{itemize} \LMHash{}% -It is a compile-time error if +It is a \Error{compile-time error} if the static type of $e_1$ may not be assigned to \code{bool}. The static type of $c$ is the least upper bound (\ref{leastUpperBounds}) of the static type of $e_2$ and the static type of $e_3$. @@ -16536,7 +16652,7 @@ \subsection{Logical Boolean Expressions} \end{itemize} \LMHash{}% -It is a compile-time error if +It is a \Error{compile-time error} if the static type of $e_1$ may not be assigned to \code{bool} or if the static type of $e_2$ may not be assigned to \code{bool}. The static type of a logical boolean expression is \code{bool}. @@ -16644,10 +16760,10 @@ \subsection{Relational Expressions} or an expression $e_1$, with argument $e_2$. \LMHash{}% -A relational expression of the form $e_1$ $op$ $e_2$ is equivalent to -the method invocation \code{$e_1$.$op$($e_2$)}. -A relational expression of the form \SUPER{} $op$ $e_2$ is equivalent to -the method invocation \code{\SUPER.$op$($e_2$)}. +A relational expression of the form \code{$e_1$\,\,\op\,\,$e_2$} +is equivalent to the method invocation \code{$e_1$.\op($e_2$)}. +A relational expression of the form \code{\SUPER\,\,\op\,\,$e_2$} +is equivalent to the method invocation \code{\SUPER.\op($e_2$)}. \subsection{Bitwise Expressions} @@ -16680,10 +16796,10 @@ \subsection{Bitwise Expressions} with argument $e_2$. \LMHash{}% -A bitwise expression of the form \code{$e_1$ $op$ $e_2$} is equivalent to -the method invocation $e_1.op(e_2)$. -A bitwise expression of the form \code{\SUPER{} $op$ $e_2$} is equivalent to -the method invocation \code{\SUPER.$op$($e_2$)}. +A bitwise expression of the form \code{$e_1$\,\,\op\,\,$e_2$} +is equivalent to the method invocation \code{$e_1$.\op($e_2$)}. +A bitwise expression of the form \code{\SUPER\,\,\op\,\,$e_2$} +is equivalent to the method invocation \code{\SUPER.\op($e_2$)}. \commentary{% It should be obvious that the static type rules for these expressions @@ -16718,10 +16834,10 @@ \subsection{Shift} with argument $e_2$. \LMHash{}% -A shift expression of the form $e_1$ $op$ $e_2$ is equivalent to -the method invocation \code{$e_1$.$op$($e_2$)}. -A shift expression of the form \SUPER{} $op$ $e_2$ is equivalent to -the method invocation \code{\SUPER.$op$($e_2$)}. +A shift expression of the form \code{$e_1$\,\,\op\,\,$e_2$} +is equivalent to the method invocation \code{$e_1$.\op($e_2$)}. +A shift expression of the form \code{\SUPER\,\,\op\,\,$e_2$} +is equivalent to the method invocation \code{\SUPER.\op($e_2$)}. \commentary{% Note that this definition implies left-to-right evaluation order @@ -16757,10 +16873,10 @@ \subsection{Additive Expressions} with argument $e_2$. \LMHash{}% -An additive expression of the form $e_1$ $op$ $e_2$ is equivalent to -the method invocation \code{$e_1$.$op$($e_2$)}. -An additive expression of the form \SUPER{} $op$ $e_2$ is equivalent to -the method invocation \code{\SUPER.$op$($e_2$)}. +An additive expression of the form \code{$e_1$\,\,\op\,\,$e_2$} +is equivalent to the method invocation \code{$e_1$.\op($e_2$)}. +An additive expression of the form \code{\SUPER\,\,\op\,\,$e_2$} +is equivalent to the method invocation \code{\SUPER.\op($e_2$)}. \LMHash{}% The static type of an additive expression is usually determined @@ -16826,10 +16942,10 @@ \subsection{Multiplicative Expressions} with argument $e_2$. \LMHash{}% -A multiplicative expression of the form $e_1$ $op$ $e_2$ is equivalent to -the method invocation \code{$e_1$.$op$($e_2$)}. -A multiplicative expression of the form \SUPER{} $op$ $e_2$ is equivalent to -the method invocation \code{\SUPER.$op$($e_2$)}. +A multiplicative expression of the form \code{$e_1$\,\,\op\,\,$e_2$} +is equivalent to the method invocation \code{$e_1$.\op($e_2$)}. +A multiplicative expression of the form \code{\SUPER\,\,\op\,\,$e_2$} +is equivalent to the method invocation \code{\SUPER.\op($e_2$)}. \LMHash{}% The static type of a multiplicative expression is usually determined @@ -16927,7 +17043,7 @@ \subsection{Unary Expressions} an instance of the \code{int} class representing the numeric value $-i$. If $i$ is zero and the \code{int} class can represent a negative zero value, then the resulting instance instead represents that negative zero value. -It is a compile-time error if the integer $-i$ cannot be represented +It is a \Error{compile-time error} if the integer $-i$ cannot be represented exactly by an instance of \code{int}. \LMHash{}% @@ -16935,7 +17051,7 @@ \subsection{Unary Expressions} to an instance of the \code{double} class representing the numeric value $-i$. If $i$ is zero, the resulting instance instead represents the \emph{negative} zero double value, \code{-0.0}. -It is a compile-time error if the integer $-i$ cannot be represented +It is a \Error{compile-time error} if the integer $-i$ cannot be represented exactly by an instance of \code{double}. \commentary{% We treat \code{-$l$} \emph{as if} it is a single integer literal @@ -16945,10 +17061,10 @@ \subsection{Unary Expressions} } \LMHash{}% -Any other expression of the form \code{$op$ $e$} is equivalent to -the method invocation \code{$e.op()$}. -An expression of the form \code{$op$ \SUPER} is equivalent to -the method invocation (\ref{superInvocations}) \code{\SUPER.$op()$}. +Any other expression of the form \code{\op\,\,$e$} is equivalent to +the method invocation \code{$e$.\op()}. +An expression of the form \code{\op\,\,\SUPER} is equivalent to +the method invocation (\ref{superInvocations}) \code{\SUPER.\op()}. \subsection{Await Expressions} @@ -17039,7 +17155,7 @@ \subsection{Await Expressions} } \commentary{% -It is not a compile-time error if the type of $e$ is not +It is not a \Error{compile-time error} if the type of $e$ is not a supertype or subtype of \code{Future}. Tools may choose to give a hint in such cases.% } @@ -17072,19 +17188,37 @@ \subsection{Postfix Expressions} \end{grammar} \LMHash{}% -A \Index{postfix expression} is either a primary expression; -a function, method or getter invocation; +A \Index{postfix expression} is either a primary expression +(\ref{expressions}); +a function, method or getter invocation +(\ref{ordinaryInvocation}, \ref{propertyExtraction}); +a null check; an invocation of a named constructor; or an invocation of a postfix operator on an expression $e$. -All but the latter two are specified elsewhere. +All but the latter three are specified elsewhere. + +\LMHash{}% +\Case{Null checks} +Consider an expression $e$ of the form \code{$e_1$\!!}\ where +$e_1$ is derived from \syntax{ *}. + +\LMHash{}% +Let $S$ be the static type of $e_1$. +A warning occurs if $S$ is non-nullable. +\commentary{In this case, the null check is redundant.} +The static type of $e$ is \NonNullType{$S$}. \LMHash{}% +$e$ is evaluated as follows: $e_1$ is evaluated to an object $o$. +If $o$ is the null object then a dynamic error occurs, +otherwise $e$ evaluates to $o$. + \Case{Constructor Invocations} Consider a \synt{constructorInvocation} $e$ of the form \code{$n$<\metavar{typeArguments}>.\id(\metavar{arguments})}. If $n$ does not denote a class $C$ that declares a constructor named \code{$C$.\id}, -a compile-time error occurs. +a \Error{compile-time error} occurs. \LMHash{}% Otherwise, if $e$ occurs in a constant context @@ -17108,15 +17242,15 @@ \subsection{Postfix Expressions} \Case{\code{$v$++}, \code{$v$-{}-}} Consider a postfix expression $e$ of the form \code{$v$\,\op}, where $v$ is an identifier and \op{} is either \lit{++} or \lit{-{}-}. -A compile-time error occurs unless $v$ denotes a variable, +A \Error{compile-time error} occurs unless $v$ denotes a variable, or $v$ denotes a getter and there is an associated setter \code{$v$=}. Let $T$ be the static type of the variable $v$ or the return type of the getter. -A compile-time error occurs if $T$ is not \DYNAMIC{} +A \Error{compile-time error} occurs if $T$ is not \DYNAMIC{} and $T$ does not have an operator \lit{+} (when \op{} is \lit{++}) or operator \lit{-} (when \op{} is \lit{-{}-}), or if the return type of this operator is not assignable to the variable respectively the argument type of the setter. -A compile-time error occurs if \code{int} is not assignable to +A \Error{compile-time error} occurs if \code{int} is not assignable to the parameter type of said operator. The static type of $e$ is $T$. @@ -17142,16 +17276,17 @@ \subsection{Postfix Expressions} \Case{\code{$C$.$v$++}, \code{$C$.$v$-{}-}} Consider a postfix expression $e$ of the form \code{$C$.$v$\,\op}, where $C$ is a type literal and \op{} is either \lit{++} or \lit{-{}-}. -A compile-time error occurs unless \code{$C$.$v$} denotes a static getter +A \Error{compile-time error} occurs +unless \code{$C$.$v$} denotes a static getter and there is an associated static setter \code{$v$=} (\commentary{possibly implicitly induced by a static variable}). Let $T$ be the return type of said getter. -A compile-time error occurs if $T$ is not \DYNAMIC{} +A \Error{compile-time error} occurs if $T$ is not \DYNAMIC{} and $T$ does not have an operator \lit{+} (when \op{} is \lit{++}) or operator \lit{-} (when \op{} is \lit{-{}-}), or if the return type of this operator is not assignable to the argument type of the setter. -A compile-time error occurs if \code{int} is not assignable to +A \Error{compile-time error} occurs if \code{int} is not assignable to the parameter type of said operator. The static type of $e$ is $T$. @@ -17170,16 +17305,16 @@ \subsection{Postfix Expressions} Consider a postfix expression $e$ of the form \code{$e_1$.$v$\,\op} where \op{} is either \lit{++} or \lit{-{}-}. Let $S$ be the static type of $e_1$. -A compile-time error occurs unless $S$ has +A \Error{compile-time error} occurs unless $S$ has a getter named $v$ and a setter named \code{$v$=} (\commentary{possibly implicitly induced by an instance variable}). Let $T$ be the return type of said getter. -A compile-time error occurs if $T$ is not \DYNAMIC{} +A \Error{compile-time error} occurs if $T$ is not \DYNAMIC{} and $T$ does not have an operator \lit{+} (when \op{} is \lit{++}) or operator \lit{-} (when \op{} is \lit{-{}-}), or if the return type of this operator is not assignable to the argument type of the setter. -A compile-time error occurs if \code{int} is not assignable to +A \Error{compile-time error} occurs if \code{int} is not assignable to the parameter type of said operator. The static type of $e$ is $T$. @@ -17200,19 +17335,19 @@ \subsection{Postfix Expressions} where \op{} is either \lit{++} or \lit{-{}-}. Let $S_1$ be the static type of $e_1$ and $S_2$ be the static type of $e_2$. -A compile-time error occurs unless $S_1$ has +A \Error{compile-time error} occurs unless $S_1$ has an operator \lit{[]} and an operator \lit{[]=}. Let $T$ be the return type of the former. -A compile-time error occurs unless $S_2$ is assignable to +A \Error{compile-time error} occurs unless $S_2$ is assignable to the first parameter type of said operator \lit{[]=}. -A compile-time error occurs if $T$ is not \DYNAMIC{} +A \Error{compile-time error} occurs if $T$ is not \DYNAMIC{} and $T$ does not have an operator \lit{+} (when \op{} is \lit{++}) or operator \lit{-} (when \op{} is \lit{-{}-}), or if the return type of this operator is not assignable to the second argument type of said operator \lit{[]=}. % We allow `e1[e2]++;` also when the entry has static type double, % so we can't just say `assignable'. -A compile-time error occurs if passing the integer literal \code{1} +A \Error{compile-time error} occurs if passing the integer literal \code{1} as an argument to said operator \lit{+} or \lit{-} would be an error. The static type of $e$ is $T$. @@ -17229,24 +17364,35 @@ \subsection{Postfix Expressions} \EndCase \LMHash{}% -\Case{\code{$e_1$?.$v$++}, \code{$e_1$?.$v$-{}-}} -Consider a postfix expression $e$ of the form \code{$e_1$?.$v$\,\op} -where \op{} is either \lit{++} or \lit{-{}-}. -Exactly the same compile-time errors that would be caused by -\code{$e_1$.$v$\,\op} -are also generated in the case of \code{$e_1$?.$v$\,\op}. -The static type of $e$ is the static type of \code{$e_1$.$v$}. +\Case{\code{$e_1$?.\id++}, \code{$e_1$?.\id-{}-}} +Consider a postfix expression $e$ of the form \code{$e_1$?.\id\,\op} +where \id{} is an identifier and \op{} is either \lit{++} or \lit{-{}-}. +%% TODO(eernst): We are likely to decide that this is an error. \LMHash{}% -Evaluation of a postfix expression $e$ -of the form \code{$e_1$?.$v$++} respectively \code{$e_1$?.$v$-{}-} +Let \DefineSymbol{S} be the static type of $e_1$. +A warning occurs if $e_1$ is a type literal, +and $e$ is then treated as \code{$e_1$.\id\,\op}. + +\LMHash{}% +Otherwise, a warning occurs if $S$ is non-nullable. +\commentary{In this case the \lit{?} is superfluous.} +Next, the static analysis of $e$ yields +the same \Error{compile-time errors} and warnings that would be caused by +static analysis of \code{$v$.\id\,\op}, +where $v$ is a fresh variable whose type is \NonNullType{$S$}. +Let $T$ be the static type of \code{$v$.\id\,\op}; +the static type of $e$ is then \code{$T$?}. + +\LMHash{}% +Evaluation of a postfix expression $e$ of the form \code{$e_1$?.\id\,\op} +where \id{} is an identifier and \op{} is either \lit{++} or \lit{-{}-} proceeds as follows: -If $e_1$ is a type literal, evaluation of $e$ is equivalent to -evaluation of \code{$e_1$.$v$++} respectively \code{$e_1$.$v$-{}-}. -Otherwise evaluate $e_1$ to an object $u$. -if $u$ is the null object, $e$ evaluates to the null object (\ref{null}). -Otherwise let $x$ be a fresh variable bound to $u$. -Evaluate \code{$x$.$v$++} respectively \code{$x$.$v$-{}-} to an object $o$. +Evaluate $e_1$ to an object $o$. +If $o$ is the null object then $e$ evaluates to the null object +(\ref{null}). +Otherwise let $v$ be a fresh variable bound to $o$. +Evaluate \code{$v$.\id\,\op} to an object $o$. Then $e$ evaluates to $o$. \EndCase @@ -17457,7 +17603,7 @@ \subsection{Lexical Lookup} \Case{$D$ exists} In this case, at least one declaration with basename \id{} is in scope at the location $\ell$. -It is a compile-time error if the name of $D$ is not $n$, +It is a \Error{compile-time error} if the name of $D$ is not $n$, unless $D$ is an instance member or a local variable (\commentary{which may be a formal parameter}). @@ -17474,12 +17620,12 @@ \subsection{Lexical Lookup} \LMHash{}% If $D$ is an instance member, -it is a compile-time error if $\ell$ does not have access to \THIS. +it is a \Error{compile-time error} if $\ell$ does not have access to \THIS. \EndCase \LMHash{}% \Case{$D$ does not exist} -It is a compile-time error if $\ell$ does not have access to \THIS{} +It is a \Error{compile-time error} if $\ell$ does not have access to \THIS{} (\ref{classes}). \EndCase @@ -17535,7 +17681,7 @@ \subsection{Lexical Lookup} \item Consider the case where $D$ is a formal type parameter declaration of a class or a mixin. - It is a compile-time error if $\ell$ occurs inside + It is a \Error{compile-time error} if $\ell$ occurs inside a static method, static getter, or static setter, or inside a static variable initializer. % NB: There is _no_ error when it occurs in an instance variable initializer, @@ -17656,11 +17802,11 @@ \subsection{Identifier Reference} the identifiers produced by the production \synt{BUILT\_IN\_IDENTIFIER}. \commentary{% -Note that it is a syntax error if a built-in identifier +Note that it is a \Error{compile-time error} if a built-in identifier is used as the declared name of %% TODO(eernst): Come extension types, add them. a prefix, class, mixin, enum, type parameter, type alias, or extension. -Similarly, it is a syntax error to use a built-in identifier +It is a \Error{compile-time error} to use a built-in identifier other than \DYNAMIC{} or \FUNCTION{} as an identifier in a type annotation or a type parameter bound.% } @@ -17675,9 +17821,10 @@ \subsection{Identifier Reference} } \LMHash{}% -It is a compile-time error if either of the identifiers \AWAIT{} or \YIELD{} +It is a +\Error{compile-time error} if either of the identifiers \AWAIT{} or \YIELD{} is used as an \synt{identifier} in a function body -marked with either \ASYNC, \code{\ASYNC*}, or \code{\SYNC*}. +marked with either \ASYNC, \code{\ASYNC*} or \code{\SYNC*}. \rationale{% This makes the identifiers \AWAIT{} and \YIELD{} behave like reserved words @@ -17763,7 +17910,7 @@ \subsection{Identifier Reference} (\ref{lexicalLookup}) for \id{} yields an import prefix $p$. % A prefix can never be used as a stand-alone expression. -In this case a compile-time error occurs, +In this case a \Error{compile-time error} occurs, unless the token immediately following $e$ is \lit{.}. No static type is associated with $e$ in this case. @@ -18397,7 +18544,8 @@ \subsection{Local Function Declaration} or \code{$T$ \id{} \metavar{signature} \{ \metavar{statements} \}} causes a new function named \id{} to be added to the current scope. -It is a compile-time error to reference a local function before its declaration. +It is a +\Error{compile-time error} to reference a local function before its declaration. \commentary{% This implies that local functions can be directly recursive, @@ -18517,7 +18665,7 @@ \subsection{If} otherwise the block statement $s_2$ is executed. \LMHash{}% -It is a compile-time error if the type of the expression $b$ +It is a \Error{compile-time error} if the type of the expression $b$ may not be assigned to \code{bool}. \LMHash{}% @@ -18577,8 +18725,8 @@ \subsubsection{For Loop} (\commentary{used to update the iteration state}). \LMHash{}% -A compile-time error occurs if $D$ has the modifier \LATE. -A compile-time error occurs if the static type of $c$ is not +A \Error{compile-time error} occurs if $D$ has the modifier \LATE. +A \Error{compile-time error} occurs if the static type of $c$ is not assignable to \code{bool}. \LMHash{}% @@ -18592,7 +18740,7 @@ \subsubsection{For Loop} (\commentary{used to update the iteration state}). \LMHash{}% -A compile-time error occurs if the static type of $c$ is not +A \Error{compile-time error} occurs if the static type of $c$ is not assignable to \code{bool}. %% TODO(eernst): The description of the dynamic semantics is highly @@ -18660,7 +18808,7 @@ \subsubsection{For-in} Let $D$ be derived from \syntax{?}\ and consider a \FOR{} statement $S_{\metavar{for}}$ of the form \code{\FOR\ ($D$\,\,\id\,\,\IN\,\,$e$)\ $S$}. -A compile-time error occurs if $D$ contains +A \Error{compile-time error} occurs if $D$ contains the modifier \CONST{} or the modifier \LATE. \LMHash{}% @@ -18680,7 +18828,7 @@ \subsubsection{For-in} If the static type of $e$ is \DYNAMIC{} then $T$ is \code{Iterable<\DYNAMIC>}, otherwise $T$ is the static type of $e$. -It is a compile-time error if $T$ is not assignable to +It is a \Error{compile-time error} if $T$ is not assignable to \code{Iterable}. \commentary{% @@ -18709,11 +18857,11 @@ \subsubsection{Asynchronous For-in} \code{\AWAIT\,\,\FOR\ ($D$\,\,\id\,\,\IN\,\,$e$)\ $s$}. \LMHash{}% -A compile-time error occurs if $D$ contains +A \Error{compile-time error} occurs if $D$ contains the modifier \CONST{} or the modifier \LATE. -It is a compile-time error if an asynchronous for-in statement appears +It is a \Error{compile-time error} if an asynchronous for-in statement appears inside a synchronous function (\ref{functions}). -It is a compile-time error if a traditional for loop (\ref{forLoop}) is +It is a \Error{compile-time error} if a traditional for loop (\ref{forLoop}) is prefixed by the \AWAIT{} keyword. \rationale{% @@ -18730,7 +18878,7 @@ \subsubsection{Asynchronous For-in} % This error can occur due to implicit casts and null. It is a dynamic type error if $o$ is not an instance of a class that implements \code{Stream}. -It is a compile-time error if $D$ is empty +It is a \Error{compile-time error} if $D$ is empty and \id{} is a final or constant variable. \LMHash{}% @@ -18851,7 +18999,7 @@ \subsection{While} } \LMHash{}% -It is a compile-time error if +It is a \Error{compile-time error} if the static type of $e$ may not be assigned to \code{bool}. @@ -18886,7 +19034,7 @@ \subsection{Do} If $o$ is \TRUE, then the do statement is re-executed. \LMHash{}% -It is a compile-time error if the static type of $e$ +It is a \Error{compile-time error} if the static type of $e$ may not be assigned to \code{bool}. @@ -18974,9 +19122,48 @@ \subsection{Switch} if equal objects did not match.% } +\LMHash{}% +Let $T$ be the static type of $e$. +Let $j \in 1 .. n$. +Assume that the switch statement does not have a \DEFAULT{} clause. +In this situation, +a warning occurs if any of the following criteria is satisfied: + +\begin{itemize} +\item $T$ is an enumerated type with values \List{\id}{1}{k} + (\ref{enums}), + and the set $\{\List{e}{1}{n}\}$ is different from + the set $\{\List{\code{$S$.\id}}{1}{k}\}$. +\item $T$ is \code{$U$?}\ for some $U$ which is an enumerated type + with values \List{\id}{1}{k}, + and the set $\{\List{e}{1}{n}\}$ is different from + $\{\List{\code{$U$.\id}}{1}{k},\,\,\NULL\}$. +\end{itemize} + \commentary{% -The \SWITCH{} statement should only be used in -very limited situations (e.g., interpreters or scanners).% +In short, it is a static warning +if a switch statement over an enum is not exhaustive.% +} + +\LMHash{}% +Let $j \in 1 .. n$ if the switch statement has a \DEFAULT{} case, +and $j \in 1 .. n - 1$, otherwise. +Assume that $s_j$ is non-empty. +It is then a \Error{compile-time error} if $s_j$ may complete normally. + +%% TODO(eernst): Come flow analysis, replace this by a section ref. +\commentary{% +This document does not yet specify the Dart flow analysis, +but it will be specified in a future version. +It is determined by the flow analysis whether any +statement or list of statements may complete normally +(\ref{statementCompletion}).% +} + +\rationale{% +The behavior of switch cases intentionally differs from the C tradition. +Implicit fall through is a known cause of programming errors +and is therefore disallowed.% } \LMHash{}% @@ -18991,6 +19178,7 @@ \subsection{Switch} \} \end{normativeDartCode} +\noindent or the form \begin{normativeDartCode} @@ -19001,29 +19189,30 @@ \subsection{Switch} \} \end{normativeDartCode} +\noindent proceeds as follows: \LMHash{}% The statement \code{\VAR{} \id{} = $e$;} is evaluated, where \id{} is a fresh variable. -% This error can occur due to implicit casts and standard subsumption. -%% TODO(eernst): But why couldn't $e$ be an instance of a subtype?! -It is a dynamic error if the value of $e$ is -not an instance of the same class as the constants $e_1, \ldots, e_n$. - \commentary{% Note that if there are no case clauses ($n = 0$), the type of $e$ does not matter.% } \LMHash{}% -Next, the case clause \CASE{} $e_{1}$: $s_{1}$ is matched against \id, -if $n > 0$. -Otherwise if there is a \DEFAULT{} clause, -the case statements $s_{n+1}$ are executed (\ref{case-execute}). +Next, if $n > 0$ then the case clause +\code{\CASE\,\,$e_1$:\,\,$s_1$} +is matched against \id. +Otherwise $n = 0$; +if there is a \DEFAULT{} clause, +the case statements $s_{n+1}$ are executed (\ref{case-execute}), +and otherwise the switch statement completes normally. \LMHash{}% -Matching of a \CASE{} clause \CASE{} $e_{k}: s_{k}$ of a switch statement +Matching of a case clause \code{\CASE\,\,$e_j$:\,\,$s_j$}, +$j \in 1 .. n$, +of a switch statement \begin{normativeDartCode} \SWITCH{} ($e$) \{ @@ -19034,23 +19223,26 @@ \subsection{Switch} \} \end{normativeDartCode} +\noindent against the value of a variable \id{} proceeds as follows: \LMHash{}% -The expression \code{$e_k$ == \id} is evaluated to an object $o$. -% This error can occur due to implicit casts and null. -It is a dynamic error if the run-time type of $o$ is not \code{bool}. -If $o$ is \FALSE{} the following case, -\CASE{} $e_{k+1}: s_{k+1}$ is matched against \id{} if $k < n$, -and if $k = n$, then the \DEFAULT{} clause's statements are executed +The expression \code{$e_j$ == \id} is evaluated to an object $o$. +If $o$ is \FALSE{} then if $j < n$ then the following case, +\code{\CASE\,\,$e_{j+1}$:\,\,$s_{j+1}$}, is matched against \id{}; +otherwise (\commentary{where $j = n$}), +the \DEFAULT{} clause's statements are executed (\ref{case-execute}). If $o$ is \TRUE, let $h$ be the smallest number -such that $h \ge k$ and $s_h$ is non-empty. +such that $h \ge j$ and $s_h$ is non-empty. If no such $h$ exists, let $h = n + 1$. -The case statements $s_h$ are then executed (\ref{case-execute}). +The case statements $s_h$ are then executed +(\ref{case-execute}). \LMHash{}% -Matching of a \CASE{} clause \CASE{} $e_{k}: s_{k}$ of a switch statement +Matching of a case clause \code{\CASE\,\,$e_j$:\,\,$s_j$}, +$j \in 1 .. n$, +of a switch statement \begin{normativeDartCode} \SWITCH{} ($e$) \{ @@ -19060,81 +19252,20 @@ \subsection{Switch} \} \end{normativeDartCode} +\noindent against the value of a variable \id{} proceeds as follows: \LMHash{}% -The expression \code{$e_k$ == \id} is evaluated to an object $o$. -% This error can occur due to implicit casts and null. -It is a dynamic error if the run-time type of $o$ is not \code{bool}. -If $o$ is \FALSE{} the following case, -\CASE{} $e_{k+1}: s_{k+1}$ is matched against \id{} if $k < n$. +The expression \code{$e_j$ == \id} is evaluated to an object $o$. +If $o$ is \FALSE{} then if $j < n$ then the following case, +\code{\CASE\,\,$e_{j+1}$:\,\,$s_{j+1}$}, is matched against \id; +otherwise (\commentary{where $j = n$}), +the switch statement completes normally. If $o$ is \TRUE, let $h$ be the smallest integer -such that $h \ge k$ and $s_h$ is non-empty. +such that $h \ge j$ and $s_h$ is non-empty. If such a $h$ exists, the case statements $s_h$ are executed (\ref{case-execute}). -Otherwise the switch statement completes normally -(\ref{statementCompletion}). - -\LMHash{}% -It is a compile-time error if the type of $e$ -may not be assigned to the type of $e_k$. -Let $s$ be the last statement of the statement sequence $s_k$. -If $s$ is a non-empty block statement, let $s$ instead be -the last statement of the block statement. -It is a compile-time error if $s$ is not -a \BREAK, \CONTINUE, \RETHROW, or \RETURN{} statement, -or an expression statement where the expression is a \THROW{} expression. - -\rationale{% -The behavior of switch cases intentionally differs from the C tradition. -Implicit fall through is a known cause of programming errors -and therefore disallowed. -Why not simply break the flow implicitly at the end of every case, -rather than requiring explicit code to do so? -This would indeed be cleaner. -It would also be cleaner to insist that each case have -a single (possibly compound) statement. -We have chosen not to do so in order to -facilitate porting of switch statements from other languages. -Implicitly breaking the control flow at the end of a case would silently alter -the meaning of ported code that relied on fall-through, -potentially forcing the programmer to deal with subtle bugs. -Our design ensures that the difference is immediately brought to -the coder's attention. -The programmer will be notified at compile time if they forget to end a case -with a statement that terminates the straight-line control flow. - -The sophistication of the analysis of fall-through is another issue. -For now, we have opted for a very straightforward syntactic requirement. -There are obviously situations where code does not fall through, -and yet does not conform to these simple rules, e.g.:% -} - -\begin{dartCode} -\SWITCH{} (x) \{ - \CASE{} 1: \TRY{} \{ $\ldots$ \RETURN; \} \FINALLY{} \{ $\ldots$ \RETURN; \} -\} -\end{dartCode} - -\rationale{% -Very elaborate code in a case clause is probably bad style in any case, -and such code can always be refactored.% -} - -\LMHash{}% -It is a static warning if all of the following conditions hold: -\begin{itemize} -\item The switch statement does not have a default clause. -\item The static type of $e$ is an enumerated - type with elements $\id_1, \ldots, \id_n$. -\item The sets $\{e_1, \ldots, e_k\} $ and $\{\id_1, \ldots, \id_n\}$ - are not the same. -\end{itemize} - -\commentary{% -In other words, a static warning will be emitted -if a switch statement over an enum is not exhaustive.% -} +Otherwise the switch statement completes normally. \subsubsection{Switch case statements} @@ -19218,8 +19349,8 @@ \subsection{Rethrow} % occur as a statement or in a statement (e.g., in a ), and this % induction can only end in a . Let $f$ be the immediately enclosing function of $S$. -A compile-time error occurs unless $S$ is located in an \ON-\CATCH{} clause -whose immediately enclosing function is $f$. +A \Error{compile-time error} occurs unless $S$ is located in +an \ON-\CATCH{} clause whose immediately enclosing function is $f$. \LMHash{}% Execution of a \code{\RETHROW} statement proceeds as follows: @@ -19354,7 +19485,7 @@ \subsection{Try} the execution of $b$. \LMHash{}% -It is a compile-time error if $T_i$, $1 \le i \le n$ is a deferred type. +It is a \Error{compile-time error} if $T_i$, $1 \le i \le n$ is a deferred type. \subsubsection{\ON{}-\CATCH{} clauses} @@ -19460,22 +19591,22 @@ \subsection{Return} (\ref{generativeConstructors}). % % Returning without an object is only ok for "voidy" return types. -It is a compile-time error if $s$ is \code{\RETURN;}, +It is a \Error{compile-time error} if $s$ is \code{\RETURN;}, unless $T$ is \VOID, \DYNAMIC, or \code{Null}. % % Returning with an object in a void function % is only ok when the return type "voidy". -It is a compile-time error if $s$ is \code{\RETURN\,\,$e$;}, +It is a \Error{compile-time error} if $s$ is \code{\RETURN\,\,$e$;}, $T$ is \VOID, and $S$ is neither \VOID, \DYNAMIC, nor \code{Null}. % % Returning void in a "non-voidy" function is an error. -It is a compile-time error if $s$ is \code{\RETURN\,\,$e$;}, +It is a \Error{compile-time error} if $s$ is \code{\RETURN\,\,$e$;}, $T$ is neither \VOID{} nor \DYNAMIC, and $S$ is \VOID. % % Otherwise, returning an unassignable value is an error. -It is a compile-time error if $s$ is \code{\RETURN\,\,$e$;}, +It is a \Error{compile-time error} if $s$ is \code{\RETURN\,\,$e$;}, $S$ is not \VOID, and $S$ is not assignable to $T$. @@ -19501,7 +19632,7 @@ \subsection{Return} with future value type \DefineSymbol{T_v}. % % Returning without an object is only ok with a "voidy" future value type. -It is a compile-time error if $s$ is \code{\RETURN;}, +It is a \Error{compile-time error} if $s$ is \code{\RETURN;}, unless $T_v$ is \VOID, \DYNAMIC, or \code{Null}. @@ -19514,17 +19645,17 @@ \subsection{Return} % Returning with an object in a void async function is only ok % when the returned value has a "voidy" type. -It is a compile-time error if $s$ is \code{\RETURN\,\,$e$;}, +It is a \Error{compile-time error} if $s$ is \code{\RETURN\,\,$e$;}, $T_v$ is \VOID, and \Flatten{$S$} is neither \VOID, \DYNAMIC, nor \code{Null}. % % Returning async-void in a "non-async-voidy" function is an error. -It is a compile-time error if $s$ is \code{\RETURN\,\,$e$;}, +It is a \Error{compile-time error} if $s$ is \code{\RETURN\,\,$e$;}, $T_v$ is neither \VOID{} nor \DYNAMIC, and \Flatten{$S$} is \VOID. % % Otherwise, returning an un-deasync-assignable value is an error. -It is a compile-time error if $s$ is \code{\RETURN\,\,$e$;}, +It is a \Error{compile-time error} if $s$ is \code{\RETURN\,\,$e$;}, \Flatten{$S$} is not \VOID, $S$ is not assignable to $T_v$, and \Flatten{$S$} is not a subtype of $T_v$. @@ -19547,7 +19678,7 @@ \subsection{Return} \LMHash{}% \Case{Generator functions} -It is a compile-time error if a return statement of +It is a \Error{compile-time error} if a return statement of the form \code{\RETURN\,\,$e$;} appears in a generator function. \rationale{% @@ -19560,7 +19691,7 @@ \subsection{Return} \LMHash{}% \Case{Generative constructors} -It is a compile-time error if a return statement of +It is a \Error{compile-time error} if a return statement of the form \code{\RETURN\,\,$e$;} appears in a generative constructor (\ref{generativeConstructors}). @@ -19688,10 +19819,11 @@ \subsection{Break} \LMHash{}% Let $s_b$ be a \BREAK{} statement. If $s_b$ is of the form \code{\BREAK{} $L$;}, -then it is a compile-time error if $s_b$ is not enclosed in a labeled statement +then it is a +\Error{compile-time error} if $s_b$ is not enclosed in a labeled statement with the label $L$ within the innermost function in which $s_b$ occurs. If $s_b$ is of the form \code{\BREAK;}, -then it is a compile-time error if $s_b$ is not enclosed in an +then it is a \Error{compile-time error} if $s_b$ is not enclosed in an \code{\AWAIT{} \FOR} (\ref{asynchronousFor-in}), \DO{} (\ref{do}), \FOR{} (\ref{for}), \SWITCH{} (\ref{switch}) or \WHILE{} (\ref{while}) statement within @@ -19718,13 +19850,13 @@ \subsection{Continue} \LMHash{}% Let $s_c$ be a \CONTINUE{} statement. If $s_c$ is of the form \code{\CONTINUE{} $L$;}, -then it is a compile-time error if $s_c$ is not enclosed in either an +then it is a \Error{compile-time error} if $s_c$ is not enclosed in either an \code{\AWAIT{} \FOR} (\ref{asynchronousFor-in}), \DO{} (\ref{do}), \FOR{} (\ref{for}), or \WHILE{} (\ref{while}) statement labeled with $L$, or in a \SWITCH{} statement with a case clause labeled with $L$, within the innermost function in which $s_c$ occurs. If $s_c$ is of the form \code{\CONTINUE;} -then it is a compile-time error if $s_c$ is not enclosed in an +then it is a \Error{compile-time error} if $s_c$ is not enclosed in an \code{\AWAIT{} \FOR} (\ref{asynchronousFor-in}) \DO{} (\ref{do}), \FOR{} (\ref{for}), or \WHILE{} (\ref{while}) statement within the innermost function in which $s_c$ occurs. @@ -19751,9 +19883,9 @@ \subsection{Yield} \LMHash{}% Let $s$ be a yield statement of the form \code{\YIELD\,\,$e$;}. Let $f$ be the immediately enclosing function of $s$. -It is a compile-time error if there is no such function, +It is a \Error{compile-time error} if there is no such function, or it is not a generator. -It is a compile-time error if the static type of $e$ +It is a \Error{compile-time error} if the static type of $e$ may not be assigned to the element type of $f$ (\ref{functions}). @@ -19847,7 +19979,7 @@ \subsection{Yield-Each} \LMHash{}% Let $s$ be a yield-each statement of the form `\code{\YIELD*\,\,$e$;}'. Let $f$ be the immediately enclosing function of $s$. -It is a compile-time error if there is no such function, +It is a \Error{compile-time error} if there is no such function, or it is not a generator. \LMHash{}% @@ -19855,10 +19987,10 @@ \subsection{Yield-Each} (\ref{functions}), and let $T$ be the static type of $e$. If $f$ is a synchronous generator, -it is a compile-time error if $T$ may not be assigned to +it is a \Error{compile-time error} if $T$ may not be assigned to \code{Iterable<$T_f$>}. Otherwise $f$ is an asynchronous generator, -and it is a compile-time error if $T$ may not be assigned to +and it is a \Error{compile-time error} if $T$ may not be assigned to \code{Stream<$T_f$>}. \LMHash{}% @@ -19997,7 +20129,7 @@ \subsection{Assert} a stack trace corresponding to the current execution state at the assertion. \LMHash{}% -It is a compile-time error if the type of $c$ +It is a \Error{compile-time error} if the type of $c$ may not be assigned to \code{bool}. \rationale{% @@ -20141,7 +20273,7 @@ \section{Libraries and Scripts} \LMHash{}% % A setter can not be paired with a function, class, etc. -It is a compile-time error if the local namespace of library $L$ +It is a \Error{compile-time error} if the local namespace of library $L$ has two declarations with the same basename, except when they are a getter and a setter. @@ -20174,7 +20306,7 @@ \subsection{Imports} (\ref{uris}). An import specifies a URI $s$ where the declaration of an imported library is to be found. -It is a compile-time error if the specified URI of an import +It is a \Error{compile-time error} if the specified URI of an import does not refer to a library declaration. \LMHash{}% @@ -20207,9 +20339,9 @@ \subsection{Imports} } \LMHash{}% -It is a compile-time error if the prefix used in a deferred import +It is a \Error{compile-time error} if the prefix used in a deferred import is also used as the prefix of another import clause. -It is a compile-time error if \id{} is an import prefix, +It is a \Error{compile-time error} if \id{} is an import prefix, and the current library declares a top-level member with basename \id. \LMHash{}% @@ -20654,7 +20786,7 @@ \subsection{Exports} (\ref{uris}). An export specifies a URI $s$ where the declaration of an exported library is to be found. -It is a compile-time error if the specified URI +It is a \Error{compile-time error} if the specified URI does not refer to a library declaration. \LMHash{}% @@ -20697,7 +20829,7 @@ \subsection{Exports} conflict merging (\ref{conflictMergingOfNamespaces}) to \List{\metavar{NS}}{\metavar{exported},1}{\metavar{exported},m}. -A compile-time error occurs if any name in +A \Error{compile-time error} occurs if any name in \NamespaceName{\metavar{merged}} is conflicted (\ref{conflictMergingOfNamespaces}). @@ -20981,13 +21113,13 @@ \subsection{Parts} the URI that is the value of $s$. The top-level declarations at that URI are then compiled by the Dart compiler in the scope of the current library. -It is a compile-time error if the contents of the URI are not +It is a \Error{compile-time error} if the contents of the URI are not a valid part declaration. -It is a compile-time error if the referenced part declaration $p$ names +It is a \Error{compile-time error} if the referenced part declaration $p$ names a library other than the current library as the library to which $p$ belongs. \LMHash{}% -It is a compile-time error if a library contains +It is a \Error{compile-time error} if a library contains two part directives with the same URI. \LMHash{}% @@ -21001,7 +21133,7 @@ \subsection{Parts} \LMHash{}% Let $L$ be a library, let $u$ be a URI, and let $L_1$ and $L_2$ be distinct libraries which are reachable from $L$. -It is a compile-time error if $L_1$ and $L_2$ both contain +It is a \Error{compile-time error} if $L_1$ and $L_2$ both contain a part directive with URI $u$. \commentary{% @@ -21055,7 +21187,8 @@ \subsection{Scripts} } \LMHash{}% -It is a compile-time error if a library's export scope contains a declaration +It is a +\Error{compile-time error} if a library's export scope contains a declaration named \code{main}, and the library is not a script. \commentary{This restriction ensures that all top-level \code{main} declarations introduce a script main-function, so there cannot be a top-level getter or field @@ -21083,7 +21216,7 @@ \subsection{URIs} \end{grammar} \LMHash{}% -It is a compile-time error if a string literal that describes a URI +It is a \Error{compile-time error} if a string literal that describes a URI or a string literal that is used in a \synt{uriTest} contains a string interpolation. @@ -21337,12 +21470,13 @@ \subsection{Static Types} \end{itemize} \LMHash{}% -Any occurrence of a malformed type in a library is a compile-time error. +Any occurrence of a +malformed type in a library is a \Error{compile-time error}. \LMHash{}% A type $T$ is \IndexCustom{deferred}{type!deferred} if{}f it is of the form $p.T$ where $p$ is a deferred prefix. -It is a compile-time error to use a deferred type +It is a \Error{compile-time error} to use a deferred type in a type annotation, type test, type cast or as a type parameter. However, all other compile-time errors must be issued under the assumption that all deferred libraries have successfully been loaded. @@ -21525,8 +21659,9 @@ \subsection{Type Aliases} \LMHash{}% Under the assumption that \List{X}{1}{s} are types such that $X_j <: B_j$, for all $j \in 1 .. s$, -it is a compile-time error if $T$ is not regular-bounded, -and it is a compile-time error if any type occurring in $T$ is not well-bounded. +it is a \Error{compile-time error} if $T$ is not regular-bounded, +and it is a +\Error{compile-time error} if any type occurring in $T$ is not well-bounded. \commentary{% This means that the bounds declared for @@ -21542,8 +21677,8 @@ \subsection{Type Aliases} let $T_1,\ \ldots,\ T_l$ be types and let $U$ be the parameterized type \code{\id<$T_1, \ldots,\ T_l$>} in a location where \id{} denotes $D$. -It is a compile-time error if $l \not= s$. -It is a compile-time error if $U$ is not well-bounded +It is a \Error{compile-time error} if $l \not= s$. +It is a \Error{compile-time error} if $U$ is not well-bounded (\ref{superBoundedTypes}). \LMHash{}% @@ -21610,7 +21745,7 @@ \subsection{Type Aliases} } \LMHash{}% -It is a compile-time error if a default value is specified for +It is a \Error{compile-time error} if a default value is specified for a formal parameter in these older forms, or if a formal parameter has the modifier \COVARIANT. @@ -21645,7 +21780,7 @@ \subsection{Type Aliases} Let $D$ be a type alias declaration, and let $M$ be the transitive closure of the type alias declarations that $D$ depends on. -A compile-time error occurs if $D \in M$. +A \Error{compile-time error} occurs if $D \in M$. \commentary{% In other words, it is an error for a type alias declaration @@ -21815,9 +21950,9 @@ \subsection{Type Aliases} \end{itemize} \LMHash{}% -A compile-time error occurs if $D$ expands to a type variable, +A \Error{compile-time error} occurs if $D$ expands to a type variable, and $T$ uses $D$ as a class. -A compile-time error occurs if $T$ uses $D$ as a class, +A \Error{compile-time error} occurs if $T$ uses $D$ as a class, and $T$ is not regular-bounded. \commentary{For example:} @@ -22733,7 +22868,7 @@ \subsection{Type Normalization} where $R_i$ is \NormalizedTypeOf{$T_i$}, for $i \in 1 .. k$. \item If $T_u$ is of the form \FunctionTypePositionalStd{T_0} - + \noindent then $T_r$ is \FunctionTypePositional{R_0}{ }{X}{B}{s}{R}{n}{k} @@ -22878,7 +23013,7 @@ \subsection{Intersection Types} \commentary{% An intersection type will never occur as a nested type, that is, -it will never occurs as +it will never occurs as an actual type argument in a parameterized type, as a parameter type or a return type in a function type, as the right operand of another intersection type, @@ -22977,8 +23112,8 @@ \subsection{Type \code{Null}} } \LMHash{}% -Attempting to instantiate \code{Null} causes a compile-time error. -It is a compile-time error for a class to extend, mix in or implement +Attempting to instantiate \code{Null} causes a \Error{compile-time error}. +It is a \Error{compile-time error} for a class to extend, mix in or implement \code{Null}. The \code{Null} class declares exactly the same members with the same signatures as the class \code{Object}. @@ -23230,7 +23365,7 @@ \subsection{Type \DYNAMIC} \metavar{typeArguments} is a list of actual type arguments derived from \synt{typeArguments}, and \metavar{arguments} is an actual argument list derived from \synt{arguments}. - It is a compile-time error if \id{} is the name of + It is a \Error{compile-time error} if \id{} is the name of a non-generic method declared in \code{Object}. \commentary{% No generic methods are declared in \code{Object}. @@ -23304,7 +23439,7 @@ \subsection{Type FutureOr} That is, \code{FutureOr} is in a sense the union of $T$ and the corresponding future type. The last point guarantees that -\code{FutureOr<$T$>} <: \code{Object}, +\code{FutureOr<$T$>} <: \code{Object?}, and also that \code{FutureOr} is covariant in its type parameter, just like class types: if $S$ <: $T$ then \code{FutureOr<$S$>} <: \code{FutureOr<$T$>}.% @@ -23313,7 +23448,7 @@ \subsection{Type FutureOr} \LMHash{}% If the type arguments passed to \code{FutureOr} would incur compile-time errors if applied to a normal generic class with one type parameter, -the same compile-time errors are issued for \code{FutureOr}. +the same \Error{compile-time errors} are issued for \code{FutureOr}. The name \code{FutureOr} as an expression denotes a \code{Type} object representing the type \code{FutureOr}. @@ -23408,7 +23543,7 @@ \subsection{Type Void} In support of the notion that the value of an expression with static type \VOID{} should be discarded, such objects can only be used in specific situations: -The occurrence of an expression of type \VOID{} is a compile-time error +The occurrence of an expression of type \VOID{} is a \Error{compile-time error} unless it is permitted according to one of the following rules. \begin{itemize} @@ -23438,6 +23573,7 @@ \subsection{Type Void} where it is not an error to have it.% } \item + %% This relies on \IsMoreTopType{\VOID}{$T$} = \VOID. In a conditional expression \code{$e$\,?\,$e_1$\,:\,$e_2$}, $e_1$ and $e_2$ may have type \VOID. \rationale{% @@ -23447,20 +23583,13 @@ \subsection{Type Void} in some context where it is not an error to have it.% } \item + %% This relies on \IsMoreTopType{\VOID}{$T$} = \VOID. In a null coalescing expression \code{$e_1$\,??\,$e_2$}, $e_2$ may have type \VOID. \rationale{% The static type of the null coalescing expression is then \VOID, which in turn restricts where it can occur.% } -\item - In an expression of the form \code{\AWAIT\,\,$e$}, $e$ may have type \VOID. - \rationale{% - This rule was adopted because it was a substantial breaking change - to turn this situation into an error - at the time where the treatment of \VOID{} was changed. - Tools may choose to give a hint in such cases.% - } \item \commentary{% In a return statement \code{\RETURN\,$e$;}, @@ -23528,13 +23657,13 @@ \subsection{Type Void} Finally, we need to address situations involving implicit usage of an object whose static type can be \VOID. % -It is a compile-time error for a for-in statement to have an iterator +It is a \Error{compile-time error} for a for-in statement to have an iterator expression of type $T$ such that \code{Iterator<\VOID{}>} is the most specific instantiation of \code{Iterator} that is a superinterface of $T$, unless the iteration variable has type \VOID. % -It is a compile-time error for an asynchronous for-in statement +It is a \Error{compile-time error} for an asynchronous for-in statement to have a stream expression of type $T$ such that \code{Stream<\VOID{}>} is the most specific instantiation of \code{Stream} that is a superinterface of $T$, @@ -23712,7 +23841,7 @@ \subsection{Parameterized Types} Let $T$ be a parameterized type \code{$G$<$S_1, \ldots,\ S_n$>}. \LMHash{}% -It is a compile-time error if $G$ is not a generic type, +It is a \Error{compile-time error} if $G$ is not a generic type, or $G$ is a generic type, but the number of formal type parameters in the declaration of $G$ is not $n$. Otherwise, let @@ -23727,7 +23856,7 @@ \subsection{Parameterized Types} or $T$ is not well-bounded (\ref{superBoundedTypes}). \LMHash{}% -It is a compile-time error if $T$ is malbounded. +It is a \Error{compile-time error} if $T$ is malbounded. \LMHash{}% $T$ is evaluated as follows. @@ -23931,7 +24060,7 @@ \subsubsection{Reserved Words} \LMHash{}% A \Index{reserved word} can only be used in the syntactic positions specified by the grammar. -In particular, a compile-time error occurs if a reserved word is used +In particular, a \Error{compile-time error} occurs if a reserved word is used where an identifier is expected. \commentary{% @@ -24091,68 +24220,14 @@ \subsubsection{Static errors} operator is checked as if the receiver had type \NonNullType{$T$} (see definition below). -It is an error for the initializer expression of a \LATE{} local -variable to use a prefix \AWAIT{} expression that is not nested inside -of another function expression. - -It is an error for a class with a generative \CONST{} constructor to have a -\code{\LATE\ \FINAL} instance variable. - -It is not a compile time error to write to a \FINAL{} non-local or instance -variable if that variable is declared \LATE{} and does not have an initializer. -For local variables, see the section below. - -It is an error if the object being iterated over by a \code{for-in} loop has a static -type which is not \DYNAMIC, and is not a subtype of \code{Iterable}. - -It is an error if the type of the value returned from a factory constructor is -not a subtype of the class type associated with the class in which it is defined -(specifically, it is an error to return a nullable type from a factory -constructor for any class other than \code{Null}). - -It is an error if any case of a switch statement except the last case (the -default case if present) may complete normally. The previous syntactic -restriction requiring the last statement of each case to be one of an enumerated -list of statements (break, continue, return, throw, or rethrow) is removed. - -Given a switch statement which switches over an expression $e$ of type $T$, -where the cases are dispatched based on expressions \List{e}{0}{k}: - -\begin{itemize} -\item It is no longer required that the $e_i$ evaluate to instances of the same - class. -\item It is an error if any of the $e_i$ evaluate to a value whose static type is - not a subtype of $T$. -\item It is an error if any of the $e_i$ evaluate to constants for which equality - is not primitive. -\item If $T$ is an enum type, it is a warning if the switch does not handle all - enum cases, either explicitly or via a default. -\item If $T$ is \code{$Q$?}\ where $Q$ is an enum type, it is a warning if the switch does - not handle all enum cases and \code{null}, either explicitly or via a default. -\end{itemize} - -It is an error if a class has a setter and a getter with the same basename where -the return type of the getter is not a subtype of the argument type of the -setter. Note that this error specifically requires subtyping and not -assignability and hence makes no exception for \DYNAMIC. - -If the static type of $e$ is \VOID, the expression \code{\AWAIT\,\,$e$} is a compile-time -error. This implies that the list item at line 18281 -will be removed from the language specification. - -Let $C$ be a type literal denoting a class, mixin, or extension. It is a warning -to use a null aware member access with receiver $C$. -\commentary{E.g., \code{$C$?.staticMethod()} is a warning.} - -[ONGOING] It is a warning to use a null aware operator (\code{?.}, \code{?[]}, \code{?..}, \code{??}, \code{??=}, or -\code{...?}) on an expression of type $T$ if $T$ is strictly non-nullable. - -It is a warning to use the null check operator (\code{!}) on an expression of type -$T$ if $T$ is strictly non-nullable. +[LATER] It is not a compile time error to write to a \FINAL{} +non-local or instance variable if that variable is declared \LATE{} +and does not have an initializer. For local variables, see the +section below. -It is no longer a warning to override a method which has a default value for a -parameter with a method with a different default value for the corresponding -parameter. +[ONGOING] It is a warning to use a null aware operator (\code{?.}, +\code{?[]}, \code{?..}, \code{??}, \code{??=}, or \code{...?}) on an +expression of type $T$ if $T$ is strictly non-nullable. \subsubsection{Local variables and definite (un)assignment} @@ -24300,20 +24375,20 @@ \subsubsection{Expression typing} Consider an expression $e$ of the form \code{$e_1$\,==\,$e_2$} where the static type of $e_1$ is $T_1$ and the static type of $e_2$ is $T_2$. Let $S$ be the type of the formal parameter of \code{operator\,\,==} in the interface of \NonNullType{$T_1$}. -It is a compile-time error unless $T_2$ is assignable to \code{$S$?}. +It is a \Error{compile-time error} unless $T_2$ is assignable to \code{$S$?}. Similarly, consider an expression $e$ of the form \code{\SUPER\,==\,$e_2$} that occurs in a class whose superclass is $C$, where the static type of $e_2$ is $T_2$. Let $S$ be the formal parameter type of the concrete declaration of \code{operator\,\,==} found by method lookup in $C$ \commentary{(if that search succeeds, otherwise it is a compile-time error)}. -It is a compile-time error unless $T_2$ is assignable to \code{$S$?}. +It is a \Error{compile-time error} unless $T_2$ is assignable to \code{$S$?}. \commentary{% Even if the static type of $e_1$ is potentially nullable, the parameter type of the \code{operator\,\,==} of the corresponding non-null type is taken into account, because that instance method will not be invoked when $e_1$ is null. Similarly, -it is not a compile-time error for the static type of $e_2$ to be potentially +it is not a \Error{compile-time error} for the static type of $e_2$ to be potentially nullable, even when the parameter type of said \code{operator\,\,==} is non-nullable. This is again safe, because the instance method will not be invoked when $e_2$ is null.% @@ -24632,11 +24707,11 @@ \subsubsection{The main function} following: Let $L$ be a library that exports a declaration $D$ named \code{main}. It is a -compile-time error unless $D$ is a non-getter function declaration. It is a -compile-time error if $D$ declares more than two required positional +\Error{compile-time error} unless $D$ is a non-getter function declaration. It is a +\Error{compile-time error} if $D$ declares more than two required positional parameters, or if there are any required named parameters. It is a -compile-time error if $D$ declares at least one positional parameter, and -the first positional parameter has a type which is not a supertype of +\Error{compile-time error} if $D$ declares at least one positional parameter, +and the first positional parameter has a type which is not a supertype of \code{List}. Implementations are free to impose any additional restrictions on the @@ -24739,6 +24814,7 @@ \subsubsection{Null check operator} and otherwise the expression evaluates to $v$. \subsubsection{Null aware operator} +\LMLabel{nullShorteningTransformation} The semantics of the null aware operator \code{?.} are defined via a source to source translation of expressions into Dart code extended with a let binding construct. @@ -24878,8 +24954,8 @@ \subsubsection{Null aware operator} expressions, not in the case for \code{$e$.m(\metavar{args})}. \commentary{% -This means that the null-shorting transformation stops at operators. For -instance, \code{$e$?.$f$ + $b$} is a compile-time error because \code{$e$?.$f$} can be null, it is +This means that the null-shorting transformation stops at operators. +For instance, \code{$e$?.$f$ + $b$} is a compile-time error because \code{$e$?.$f$} can be null, it is not an expression where both \code{.$f$} and \code{+ $b$} will be skipped if \code{$e$} is null. Similarly, both \code{-$a$?.$f$} and \code{~$a$?.$f$} are errors, and do not null-short like \code{$a$?.$f$.op()}.% From 6f87b6b3394f63d26136d20bc810f787a6c5635c Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Tue, 9 Nov 2021 09:50:05 +0100 Subject: [PATCH 09/85] WIP --- specification/dart.sty | 4 +- specification/dartLangSpec.tex | 79 ++++++++++++++++++---------------- 2 files changed, 45 insertions(+), 38 deletions(-) diff --git a/specification/dart.sty b/specification/dart.sty index 4d6f6101a..804d51ce8 100644 --- a/specification/dart.sty +++ b/specification/dart.sty @@ -196,11 +196,11 @@ % Same appearance, but not adding an entry to the index. \newcommand{\NoIndex}[1]{% - \leavevmode\marginpar{\ensuremath{\diamond}}\emph{#1}} + \leavevmode\marginpar{\ensuremath{_{^\vartriangle}}}\emph{#1}} % Mark a compile-time error in the margin. \newcommand{\Error}[1]{% - \leavevmode\marginpar{\ensuremath{\ominus}}{#1}} + \leavevmode\marginpar{\ensuremath{_{^\ominus}}}{#1}} % Used to specify comma separated lists of similar symbols. \newcommand{\List}[3]{\ensuremath{{#1}_{#2},\,\ldots,\ {#1}_{#3}}} diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index adf8f2e6b..6aa7e21df 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -1451,10 +1451,12 @@ \section{Variables} or a \Error{compile-time error} occurs. \commentary{% -An initializing expression $e$ of a constant variable declaration -occurs in a constant context -(\ref{constantContexts}). -This means that \CONST{} modifiers in $e$ need not be specified explicitly.% +An initializing expression of a constant variable occurs in a constant context +(\ref{constantContexts}), +which means that \CONST{} modifiers need not be specified explicitly. +We need not specify that it is a compile-time error for a constant variable +to have the modifier \LATE, +because such declarations are not derivable syntactically.% } \rationale{% @@ -1469,6 +1471,10 @@ \section{Variables} (\ref{instanceVariables}).% } +\LMHash{}% +A \IndexCustom{final variable}{variable!final} +is a variable whose declaration includes the modifier \FINAL. + \subsection{Implicitly Induced Getters and Setters} \LMLabel{implicitlyInducedGettersAndSetters} @@ -1537,6 +1543,17 @@ \subsection{Implicitly Induced Getters and Setters} In these cases, the declared type of \id{} is $T$. \EndCase +\commentary{% +A future version of this specification will specify type inference, +but currently it is unspecified and assumed to have taken place already. +For example, an instance variable declaration of the form +\code{\VAR\,\,x;} could have been transformed into +\code{$T$\,\,x;} based on member signatures named \code{x} in superinterfaces +of the enclosing class. +Hence, the type \DYNAMIC{} is only used as mentioned above when +type inference fails to provide a different type.% +} + \LMHash{}% \Case{Setter: Mutable variable with declared type} A variable declaration of one of the forms @@ -1652,7 +1669,7 @@ \subsection{Implicitly Induced Getters and Setters} either by an initializing formal or an initializer list entry. \commentary{% -It is a \Error{compile-time error} if a final instance variable +It is a compile-time error if a final instance variable that has been initialized by means of an initializing formal of a constructor $k$ is also initialized in the initializer list of $k$ @@ -10359,10 +10376,7 @@ \subsubsection{Collection Literal Element Evaluation} Evaluate $e$ to an object $o_{\metavar{spread}}$. \begin{enumerate} \item - When $\ell$ is `\code{...$e$}': - %% TODO(eernst): Come NNBD, this error cannot occur any more: delete. - If $o_{\metavar{spread}}$ is the null object then a dynamic error occurs. - Otherwise evaluation proceeds with step 2. + When $\ell$ is `\code{...$e$}': Evaluation proceeds with step 2. When $\ell$ is `\code{...?$e$}': If $o_{\metavar{spread}}$ is the null object then @@ -10583,15 +10597,13 @@ \subsubsection{List Literal Inference} let $S$ be the inferred type of $e$ in context \code{Iterable<$P$>}. Otherwise (\commentary{when $\ell$ is `\code{...?$e$}'}), -%% TODO(eernst): Come NNBD, add a \ref{} to the specification of -%% 'the non-nullable type of'. -let $S$ be the non-nullable type of %% TODO(eernst): Clarify whether inference will indeed have that context; -%% it is clear that we need to eliminate `Null` from $S$, and also that $e$ +%% it is clear that we need to eliminate `Null` from $S'$, and also that $e$ %% is allowed to have a potentially nullable type, and it seems inconvenient %% if we use `Iterable<$P$>` as context type and fail in the case where $e$, %% say, has type `List<$U$>?` for some $U$. -the inferred type of $e$ in context \code{Iterable<$P$>?}. +let $S'$ be the inferred type of $e$ in context \code{Iterable<$P$>?} +and $S$ be \NonNullTypeOf{$S'$}. \begin{itemize} \item @@ -14079,11 +14091,9 @@ \subsection{Member Invocations} \code{\gtilde{}$r$} & \gtilde{} \\ \code{$r$ $\oplus$ $e$} & \oplus \\ \code{$r$[$e$]} & \code{[]} \\ - %% TODO(eernst): Come nnbd, uncomment this. - % \code{$r$?[$e$]} & \code{[]} \\ + \code{$r$?[$e$]} & \code{[]} \\ \code{$r$[$e_1$] = $e_2$} & \code{[]=} \\ - %% TODO(eernst): Come nnbd, uncomment this. - % \code{$r$?[$e_1$] = $e_2$} & \code{[]=} \\ + \code{$r$?[$e_1$] = $e_2$} & \code{[]=} \\ \code{$r$(\metavar{args})} & \CALL{} \\ \code{$r$<\metavar{types}>(\metavar{args})} & \CALL{} \\ \end{array} @@ -14095,8 +14105,7 @@ \subsection{Member Invocations} \code{$r$.\id{} $\otimes$= $e$} & \id \\ \code{$r$?.\id{} $\otimes$= $e$} & \id \\ \code{$r$[$e_1$] $\otimes$= $e_2$} & \code{[]} \\ - %% TODO(eernst): Come nnbd, uncomment this. - % \code{$r$?[$e_1$] $\otimes$= $e_2$} & \code{[]} \\ + \code{$r$?[$e_1$] $\otimes$= $e_2$} & \code{[]} \\ \code{++$r$.\id, -{}-$r$.\id} & \id \\ \code{$r$.\id++, $r$.\id-{}-} & \id \\ \code{++$r$[$e$], -{}-$r$[$e$]} & \code{[]} \\ @@ -14204,11 +14213,9 @@ \subsection{Member Invocations} \code{$r$?.\id<\metavar{types}>(\metavar{args})} & \code{$r'$\,==\,\NULL\ ?\ \NULL\ :\ $r$.\id<\metavar{types}>(\metavar{args})} \\ - %% TODO(eernst): Come nnbd, uncomment this. \code{$r$?[$e$]} & \code{$r'$\,==\,\NULL\ ?\ \NULL\ :\ $r'$[$e$]} \\ - %% TODO(eernst): Come nnbd, uncomment this. \code{$r$?[$e_1$] = $e_2$} & \code{$r'$\,==\,\NULL\ ?\ \NULL\ :\ $r'$[$e_1$] = $e_2$} \\ @@ -14229,11 +14236,9 @@ \subsection{Member Invocations} \code{$r$[$e_1$] $\otimes$= $e_2$} & \Let{$v$}{$e_1$}{$r'$[$v$] = $r'$[$v$]\,$\otimes$\,$e_2$} \\ - %% TODO(eernst): Come nnbd, un-comment this. 4 similar cases below. - %% \code{$r$?[$e_1$] $\otimes$= $e_2$} & - %% \code{$r'$\,==\,\NULL\ ?\ \NULL\ :\ % - %% $r'$[$e_1$]\,\,$\otimes$=\,\,$e_2$} - %% \\ + \code{$r$?[$e_1$] $\otimes$= $e_2$} & + \code{$r'$\,==\,\NULL\ ?\ \NULL\ :\ $r'$[$e_1$]\,\,$\otimes$=\,\,$e_2$} + \\ \code{++$r$.\id} & \code{$r$.\id{} += 1} \\ @@ -16561,6 +16566,15 @@ \subsection{If-null Expressions} ::= (`??' )* \end{grammar} +\LMHash{}% +Consider an if-null expression $e$ of the form \code{$e_1$ ?? $e_2$}. +Let $S$ be the static type of $e_1$. +A warning occurs if $S$ is non-nullable. +\commentary{In this case, the \lit{??} is superfluous.} +The static type of $e$ is the least upper bound +(\ref{leastUpperBounds}) +of \NonNullTypeOf{$S$} and the static type of $e_2$. + \LMHash{}% Evaluation of an if-null expression $e$ of the form \code{$e_1$ ?? $e_2$} proceeds as follows: @@ -16571,10 +16585,6 @@ \subsection{If-null Expressions} Otherwise evaluate $e_2$ to an object $r$, and then $e$ evaluates to $r$. -\LMHash{}% -The static type of $e$ is the least upper bound (\ref{leastUpperBounds}) of -the static type of $e_1$ and the static type of $e_2$. - \subsection{Logical Boolean Expressions} \LMLabel{logicalBooleanExpressions} @@ -18725,7 +18735,8 @@ \subsubsection{For Loop} (\commentary{used to update the iteration state}). \LMHash{}% -A \Error{compile-time error} occurs if $D$ has the modifier \LATE. +A \Error{compile-time error} occurs if $D$ has +the modifier \CONST{} or the modifier \LATE. A \Error{compile-time error} occurs if the static type of $c$ is not assignable to \code{bool}. @@ -24225,10 +24236,6 @@ \subsubsection{Static errors} and does not have an initializer. For local variables, see the section below. -[ONGOING] It is a warning to use a null aware operator (\code{?.}, -\code{?[]}, \code{?..}, \code{??}, \code{??=}, or \code{...?}) on an -expression of type $T$ if $T$ is strictly non-nullable. - \subsubsection{Local variables and definite (un)assignment} As part of the null safety release, errors for local variables are specified to From b1c3e3dbc3adae0ecd952a37af20e0e8f444f4bb Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Wed, 10 Nov 2021 16:11:36 +0100 Subject: [PATCH 10/85] Corrected last part of section Variables --- specification/dartLangSpec.tex | 2 ++ 1 file changed, 2 insertions(+) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 6aa7e21df..94e764e4b 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -24351,6 +24351,8 @@ \subsubsection{Local variables and inference} \subsubsection{Expression typing} +!!!TODO!!! + It is permitted to invoke or tear-off a method, setter, getter, or operator that is defined on \code{Object} on potentially nullable type. The type used for static analysis of such an invocation or tear-off shall be the type declared on the From 97308b5a0d5886ada9ab7362762152cb3576cb62 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 12 Nov 2021 00:12:18 +0100 Subject: [PATCH 11/85] Did expression typing, spread element, i2b --- specification/dartLangSpec.tex | 405 ++++++++++++++++++--------------- 1 file changed, 226 insertions(+), 179 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 94e764e4b..6b80abecf 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -8180,6 +8180,7 @@ \subsubsection{The Instantiation to Bound Algorithm} We now specify how the \Index{instantiation to bound} algorithm proceeds. +\BlindDefineSymbol{T,X_j,k,B_j,S_j}% Let $T$ be a raw type. Let \List{X}{1}{k} be the formal type parameters in the declaration of $G$, and let \List{B}{1}{k} be their bounds. @@ -8197,6 +8198,7 @@ \subsubsection{The Instantiation to Bound Algorithm} } \LMHash{}% +\BlindDefineSymbol{U_{i,j}}% Let $U_{i,0}$ be $S_i$, for all $i \in 1 .. k$. \commentary{% This is the "current value" of the bound for type variable $i$, at step 0; @@ -8210,6 +8212,7 @@ \subsubsection{The Instantiation to Bound Algorithm} \def\TransitivelyDepends{\ensuremath{\rightarrow^{+}_m}} \LMHash{}% +\BlindDefineSymbol{\Depends}% Let \Depends{} be a relation among the type variables \List{X}{1}{k} such that $X_p \Depends X_q$ iff $X_q$ occurs in $U_{p,m}$. @@ -8217,10 +8220,12 @@ \subsubsection{The Instantiation to Bound Algorithm} So each type variable is related to, that is, depends on, every type variable in its bound, which might include itself.% } +\BlindDefineSymbol{\TransitivelyDepends}% Let \TransitivelyDepends{} be the transitive (\commentary{but not reflexive}) closure of \Depends. For each $m$, let $U_{i,m+1}$, for $i \in 1 .. k$, +\BlindDefineSymbol{V_m}% be determined by the following iterative process, where $V_m$ denotes \code{$G$<$U_{1,m},\ \ldots,\ U_{k,m}$>}: @@ -8247,7 +8252,7 @@ \subsubsection{The Instantiation to Bound Algorithm} $U_{i,m+1}$ is then obtained from $U_{i,m}$ by substituting \DYNAMIC{} for every occurrence of a variable in $M_q$ that is in a position in $V_m$ which is not contravariant, - and substituting \code{Null} for every occurrence of a variable in $M_q$ + and substituting \code{Never} for every occurrence of a variable in $M_q$ which is in a contravariant position in $V_m$. \item[2.] @@ -8262,7 +8267,7 @@ \subsubsection{The Instantiation to Bound Algorithm} $U_{i,m+1}$ is obtained from $U_{i,m}$ by substituting $U_{j,m}$ for every occurrence of $X_j$ that is in a position in $V_m$ which is not contravariant, - and substituting \code{Null} for every occurrence of $X_j$ + and substituting \code{Never} for every occurrence of $X_j$ which is in a contravariant position in $V_m$. \item[3.] @@ -8317,8 +8322,8 @@ \subsubsection{The Instantiation to Bound Algorithm} } \begin{dartCode} -\CLASS{} C{}> \{\} -\TYPEDEF{} F{}> = X \FUNCTION(X); +\CLASS{} C \{\} +\TYPEDEF{} F, Y \EXTENDS{} C{}> = X \FUNCTION(Y); \\ F f; // \comment{Compile-time error.} \end{dartCode} @@ -8326,34 +8331,8 @@ \subsubsection{The Instantiation to Bound Algorithm} \commentary{% With these declarations, the raw \code{F} which is used as a type annotation is a compile-time error: -The algorithm yields \code{F{}>}, -and that is neither a regular-bounded nor a super-bounded type. -% -The resulting type can be specified explicitly as -\code{C<\DYNAMIC{}> \FUNCTION(C<\DYNAMIC{}>)}. -That type exists, -we just cannot express it by passing a type argument to \code{F}, -so we make it an error rather than allowing it implicitly.% -} - -\rationale{% -The core reason why it makes sense to make such a raw type an error -is that there is no subtype relationship -between the relevant parameterized types.% -} -\commentary{% -For instance, \code{F} and \code{F} are unrelated, -even when \SubtypeNE{\code{T1}}{\code{T2}} or vice versa. -In fact, there is no type \code{T} whatsoever -such that a variable with declared type \code{F} -could be assigned to a variable of type -\code{C<\DYNAMIC{}> \FUNCTION(C<\DYNAMIC{}>)}. -% -So the raw \code{F}, if permitted, -would not be ``a supertype of \code{F} for all possible \code{T}'', -it would be a type which is unrelated to \code{F} -for \emph{every single} \code{T} that satisfies the bound of \code{F}. -This is so useless that we made it an error.% +The algorithm yields \code{F,\,\,C{}>}, +and that is neither a regular-bounded nor a super-bounded type.% } \LMHash{}% @@ -10542,7 +10521,8 @@ \subsubsection{List Literal Inference} \LMLabel{listLiteralInference} \LMHash{}% -This section specifies how a list literal \metavar{list} is traversed and an +This section specifies how a list literal +\DefineSymbol{\metavar{list}} is traversed, and an \IndexCustom{inferred element type}{list literal!element type} for \metavar{list} is determined. We specify first how to infer the element type of a single element, @@ -10550,6 +10530,7 @@ \subsubsection{List Literal Inference} the element type of \metavar{list} as a whole. \LMHash{}% +\BlindDefineSymbol{P, P_e} The context type $P$ (\ref{setAndMapLiteralDisambiguation}) for each element of \metavar{list} is @@ -10561,7 +10542,7 @@ \subsubsection{List Literal Inference} (\ref{setAndMapLiteralDisambiguation}). \LMHash{}% -Let $\ell$ be a term derived from \synt{element}. +Let \DefineSymbol{\ell} be a term derived from \synt{element}. Inference of the element type of $\ell$ with context type $P$ proceeds as follows, where the context type for inference of an element type is always $P$, @@ -10592,16 +10573,12 @@ \subsubsection{List Literal Inference} \LMHash{}% \Case{Spread element} +\BlindDefineSymbol{S}% Let $e$ be the expression of $\ell$. If $\ell$ is `\code{...$e$}', let $S$ be the inferred type of $e$ in context \code{Iterable<$P$>}. Otherwise (\commentary{when $\ell$ is `\code{...?$e$}'}), -%% TODO(eernst): Clarify whether inference will indeed have that context; -%% it is clear that we need to eliminate `Null` from $S'$, and also that $e$ -%% is allowed to have a potentially nullable type, and it seems inconvenient -%% if we use `Iterable<$P$>` as context type and fail in the case where $e$, -%% say, has type `List<$U$>?` for some $U$. let $S'$ be the inferred type of $e$ in context \code{Iterable<$P$>?} and $S$ be \NonNullTypeOf{$S'$}. @@ -10614,8 +10591,9 @@ \subsubsection{List Literal Inference} If $S$ is \DYNAMIC, the inferred element type of $\ell$ is \DYNAMIC. \item - If $S$ is \code{Null} and the spread operator is \lit{...?}, - the inferred element type of $\ell$ is \code{Null}. + If $S$ is a subtype of \code{Never}, + or if $S$ is a subtype of \code{Null} and the spread operator is \lit{...?}, + the inferred element type of $\ell$ is \code{Never}. \item Otherwise, a \Error{compile-time error} occurs. \end{itemize} @@ -10642,19 +10620,20 @@ \subsubsection{List Literal Inference} \LMHash{}% \Case{For element} +\BlindDefineSymbol{p}% In this case $\ell$ is of the form -\code{\AWAIT?\,\,\FOR\,\,($P$)\,\,$\ell_1$} -where $P$ is derived from \synt{forLoopParts} and +\code{\AWAIT?\,\,\FOR\,\,($p$)\,\,$\ell_1$} +where $p$ is derived from \synt{forLoopParts} and `\AWAIT?' indicates that \AWAIT{} may be present or absent. The same \Error{compile-time errors} occur for $\ell$ as the errors that would occur with the corresponding \FOR{} statement -\code{\AWAIT?\,\,\FOR\,\,($P$)\,\,\{\}}, +\code{\AWAIT?\,\,\FOR\,\,($p$)\,\,\{\}}, located in the same scope as $\ell$. Moreover, the errors and type analysis of $\ell$ is performed as if it occurred in the body scope of said \FOR{} statement. \commentary{% -For instance, if $P$ is of the form +For instance, if $p$ is of the form \code{\VAR\,\,v\,\,\IN\,\,$e_1$} then the variable \code{v} is in scope for $\ell$.% } @@ -10680,6 +10659,7 @@ \subsubsection{List Literal Inference} \IndexCustom{type inference on a list literal}{% type inference!list literal} as a whole. +\BlindDefineSymbol{\metavar{list},\ell_j,P}% Assume that \metavar{list} is derived from \synt{listLiteral} and contains the elements \List{\ell}{1}{n}, and the context type for \metavar{list} is $P$. @@ -10914,9 +10894,11 @@ \subsubsection{Set and Map Literal Disambiguation} (\ref{setAndMapLiteralInference}). \LMHash{}% +\BlindDefineSymbol{e,{\cal L},C}% Let $e$ be a \synt{setOrMapLiteral} with leaf elements $\cal L$ and context type $C$. If $C$ is \FreeContext{} then let $S$ be undefined. +\BlindDefineSymbol{S}% %% TODO(eernst): Define `greatest closure' of a context type %% when we define `context type'. Otherwise let $S$ be the greatest closure of \futureOrBase{C} @@ -11010,6 +10992,7 @@ \subsubsection{Set and Map Literal Inference} \LMLabel{setAndMapLiteralInference} \LMHash{}% +\BlindDefineSymbol{e}% This section specifies how a \synt{setOrMapLiteral} $e$ is traversed and an associated \IndexCustom{inferred element type}{set or map literal!element type} @@ -11053,6 +11036,7 @@ \subsubsection{Set and Map Literal Inference} \end{dartCode} \LMHash{}% +\BlindDefineSymbol{\metavar{collection},P}% Let \metavar{collection} be a collection literal derived from \synt{setOrMapLiteral}. The inferred type of an \synt{element} is an element type $T$, @@ -11063,6 +11047,7 @@ \subsubsection{Set and Map Literal Inference} \begin{itemize} \item + \BlindDefineSymbol{P_e}% If \metavar{collection} is unambiguously a set (\ref{setAndMapLiteralDisambiguation}) then $P$ is \code{Set<$P_e$>}, @@ -11092,6 +11077,7 @@ \subsubsection{Set and Map Literal Inference} provides a partial constraint on the inferred type.% } \item + \BlindDefineSymbol{P_k,P_v}% If \metavar{collection} is unambiguously a map then $P$ is \code{Map<$P_k$,\,\,$P_v$>} where $P_k$ and $P_v$ are determined by downwards inference, @@ -11118,6 +11104,7 @@ \subsubsection{Set and Map Literal Inference} if can be a map and has no element type. \LMHash{}% +\BlindDefineSymbol{\ell}% Let $\ell$ be a term derived from \synt{element}. \IndexCustom{Inference of the type of}{% type inference!collection literal element} @@ -11157,6 +11144,7 @@ \subsubsection{Set and Map Literal Inference} \LMHash{}% \Case{Spread element} +\BlindDefineSymbol{S}% In this case $\ell$ is of the form `\code{...$e$}' or `\code{...?$e$}'. If $P$ is \FreeContext{} then let $S$ be @@ -11203,19 +11191,23 @@ \subsubsection{Set and Map Literal Inference} and rely on other elements to disambiguate.% } \item - If $S$ is \code{Null} and the spread operator is \lit{...?} then - the inferred element type of $\ell$ is \code{Null}, - and the inferred key and value type pair $(\code{Null}, \code{Null})$. + If $S$ is a subtype of \code{Never}, + or if $S$ is a subtype of \code{Null} and the spread operator is \lit{...?}, + then the inferred element type of $\ell$ is \code{Never}, + and the inferred key and value type pair $(\code{Never}, \code{Never})$. \item Otherwise, a \Error{compile-time error} occurs. \end{itemize} \noindent -%% TODO(eernst): Clarify why we shouldn't be able to use a context type of -%% `Iterable<$P_e$>` in the same way: infer $e$ in context `Iterable<$P_e$>` -%% as well. -Otherwise, if $P$ is \code{Set<$P_e$>} then let $S$ be -the inferred type of $e$ in context \code{Iterable<$P_e$>}, and then: +\BlindDefineSymbol{S}% +Otherwise $P$ is not \FreeContext. +If $\ell$ is `\code{...$e$}' and $P$ is \code{Set<$P_e$>} +then let $S$ be the inferred type of $e$ in context \code{Iterable<$P_e$>}; +and if $\ell$ is \code{...?$e$} and $P$ is \code{Set<$P_e$>} +then let $S'$ be the inferred type of $e$ in context \code{Iterable<$P_e$>?} +and let $S$ be \NonNullTypeOf{$S'$}. +Next: \begin{itemize} \item @@ -11230,15 +11222,23 @@ \subsubsection{Set and Map Literal Inference} If $S$ is \DYNAMIC, the inferred element type of $\ell$ is \DYNAMIC. \item - If $S$ is \code{Null} and the spread operator is \lit{...?}, - the inferred element type of $\ell$ is \code{Null}. + If $S$ is a subtype of \code{Never}, + or if $S$ is a subtype of \code{Null} and the spread operator is \lit{...?}, + the inferred element type of $\ell$ is \code{Never}. \item Otherwise, a \Error{compile-time error} occurs. \end{itemize} \noindent -Otherwise, if $P$ is \code{Map<$P_k$,\,\,$P_v$>} then let $S$ be -the inferred type of $e$ in context $P$, and then: +\BlindDefineSymbol{S}% +Otherwise $P$ is \code{Map<$P_k$,\,\,$P_v$>} +(\commentary{because $P$ was created such that it only has those three forms}). +If $\ell$ is `\code{...$e$}' then let $S$ be +the inferred type of $e$ in context $P$; +if $\ell$ is `\code{...?$e$}' then let $S'$ be +the inferred type of $e$ in context \code{$P$?}, +and let $S$ be \NonNullTypeOf{$S'$}. +Next: \begin{itemize} \item @@ -11257,8 +11257,9 @@ \subsubsection{Set and Map Literal Inference} \noindent $(\DYNAMIC, \DYNAMIC)$. \item - If $S$ is \code{Null} and the spread operator is \lit{...?}, - the inferred key and value type pair $(\code{Null}, \code{Null})$. + If $S$ is a subtype of \code{Never}, + or if $S$ is a subtype of \code{Null} and the spread operator is \lit{...?}, + the inferred key and value type pair is $(\code{Never}, \code{Never})$. \item Otherwise, a \Error{compile-time error} occurs. \end{itemize} @@ -11267,11 +11268,13 @@ \subsubsection{Set and Map Literal Inference} \LMHash{}% \Case{If element} +\BlindDefineSymbol{b,\ell_1,\ell_2}% In this case $\ell$ is of the form \code{\IF\,\,($b$)\,\,$\ell_1$} or \code{\IF\,\,($b$)\,\,$\ell_1$\,\,\ELSE\,\,$\ell_2$}. The condition $b$ is always inferred with a context type of \code{bool}. +\LMHash{}% Assume that `\code{\ELSE\,\,$\ell_2$}' is not present. Then: \begin{itemize} \item @@ -11282,6 +11285,7 @@ \subsubsection{Set and Map Literal Inference} the inferred key and value type pair of $\ell$ is $(K, V)$. \end{itemize} +\LMHash{}% Otherwise, `\code{\ELSE\,\,$\ell_2$}' is present. It is a compile error if $\ell_1$ must be a set and $\ell_2$ must be a map, or vice versa. @@ -11292,6 +11296,7 @@ \subsubsection{Set and Map Literal Inference} a \DYNAMIC{} spread in either branch does not cause the error to occur.% } +\LMHash{}% Then: \begin{itemize} @@ -11315,24 +11320,27 @@ \subsubsection{Set and Map Literal Inference} \LMHash{}% \Case{For element} +\BlindDefineSymbol{p,\ell_1}% In this case $\ell$ is of the form -\code{\AWAIT?\,\,\FOR\,\,($P$)\,\,$\ell_1$} -where $P$ is derived from \synt{forLoopParts} and +\code{\AWAIT?\,\,\FOR\,\,($p$)\,\,$\ell_1$} +where $p$ is derived from \synt{forLoopParts} and `\AWAIT?' indicates that \AWAIT{} may be present or absent. +\LMHash{}% The same \Error{compile-time errors} occur for $\ell$ as the errors that would occur with the corresponding \FOR{} statement -\code{\AWAIT?\,\,\FOR\,\,($P$)\,\,\{\}}, +\code{\AWAIT?\,\,\FOR\,\,($p$)\,\,\{\}}, located in the same scope as $\ell$. -Moreover, the errors and type analysis of $\ell$ is performed +Moreover, the errors and type analysis of $\ell_1$ is performed as if it occurred in the body scope of said \FOR{} statement. \commentary{% -For instance, if $P$ is of the form +For instance, if $p$ is of the form \code{\VAR\,\,v\,\,\IN\,\,$e_1$} -then the variable \code{v} is in scope for $\ell$.% +then the variable \code{v} is in scope for $\ell_1$.% } +\LMHash{}% Inference for the parts (\commentary{% such as the iterable expression of a for-in, @@ -11361,6 +11369,7 @@ \subsubsection{Set and Map Literal Inference} \IndexCustom{type inference on a set or map literal}{% type inference!set or map literal} as a whole. +\BlindDefineSymbol{\metavar{collection},P}% Assume that \metavar{collection} is derived from \synt{setOrMapLiteral}, and the context type for \metavar{collection} is $P$. @@ -14057,8 +14066,8 @@ \subsection{Top level Getter Invocation} proceeds as follows: \LMHash{}% -The getter function $m$ is invoked. -The value of $i$ is the result returned by the call to the getter function. +The getter $m$ is invoked. +The value of $i$ is the result returned by the call to the getter. \commentary{% Note that the invocation is always defined. Per the rules for identifier references, @@ -15284,7 +15293,7 @@ \subsubsection{Getter Invocation and Method Closurization} (\ref{getters}) \id{} in $o$ with respect to $L$. Otherwise, the body of $f$ is executed with \THIS{} bound to $o$. -The value of $i$ is the result returned by the call to the getter function. +The value of $i$ is the result returned by the call to the getter. \LMHash{}% If the getter lookup has failed, @@ -15363,7 +15372,7 @@ \subsubsection{Super Getter Access and Method Closurization} Let $f$ be the result of looking up getter \id{} in $S$ with respect to $L$. The body of $f$ is executed with \THIS{} bound to the current value of \THIS. -The value of $i$ is the result returned by the call to the getter function. +The value of $i$ is the result returned by the call to the getter. \commentary{% The getter lookup will not fail, because it is a compile-time error to have @@ -16690,31 +16699,71 @@ \subsection{Equality} or an expression $e_1$, with argument $e_2$. \LMHash{}% -Evaluation of an equality expression $ee$ of the form \code{$e_1$ == $e_2$} +An equality expression of the form \code{$e_1$ != $e_2$} is equivalent to +the expression \code{!($e_1$ == $e_2$)}. +An equality expression of the form \code{\SUPER{} != $e$} is equivalent to +the expression \code{!(\SUPER{} == $e$)}. + +\LMHash{}% +Consider an expression $e$ of the form \code{$e_1$\,==\,$e_2$} +where the static type of $e_1$ is $T_1$ +and the static type of $e_2$ is $T_2$. +Let $S$ be the type of the formal parameter of +\code{operator\,\,==} in the interface of \NonNullTypeOf{$T_1$}, +using \code{Object} if \NonNullTypeOf{$T_1$} is \code{Never}. +It is a \Error{compile-time error} unless $T_2$ is assignable to \code{$S$?}. + +\LMHash{}% +Similarly, consider an expression $e$ of the form +\code{\SUPER\,==\,$e_2$} that occurs in a class whose superclass is +$C$, where the static type of $e_2$ is $T_2$. Let $S$ be the formal +parameter type of the concrete declaration of \code{operator\,\,==} +found by method lookup in $C$ +(\commentary{if that search succeeds, otherwise it is a compile-time error}). +It is a \Error{compile-time error} unless $T_2$ is assignable to +\code{$S$?}. + +\commentary{% +Even if the static type of $e_1$ is potentially nullable, the +parameter type of the \code{operator\,\,==} of the corresponding +non-null type is taken into account, because that instance method will +not be invoked when $e_1$ is null. Similarly, it is not a +\Error{compile-time error} for the static type of $e_2$ to be +potentially nullable, even when the parameter type of said +\code{operator\,\,==} is non-nullable. This is again safe, because +the instance method will not be invoked when $e_2$ is null.% +} + +\LMHash{}% +The static type of an equality expression is \code{bool}. +\commentary{Even when the left operand has type \code{Never}.} + +\LMHash{}% +Evaluation of an equality expression $e$ of the form \code{$e_1$ == $e_2$} proceeds as follows: \begin{itemize} \item The expression $e_1$ is evaluated to an object $o_1$. \item The expression $e_2$ is evaluated to an object $o_2$. \item If either $o_1$ or $o_2$ is the null object (\ref{null}), - then $ee$ evaluates to \TRUE{} if both $o_1$ and $o_2$ are the null object + then $e$ evaluates to \TRUE{} if both $o_1$ and $o_2$ are the null object and to \FALSE{} otherwise. Otherwise, -\item evaluation of $ee$ is equivalent to the method invocation +\item evaluation of $e$ is equivalent to the method invocation \code{$o_1$.==($o_2$)}. \end{itemize} \LMHash{}% -Evaluation of an equality expression $ee$ of the form -\code{\SUPER{} == $e$} +Evaluation of an equality expression $e$ of the form +\code{\SUPER{} == $e_2$} proceeds as follows: \begin{itemize} -\item The expression $e$ is evaluated to an object $o$. +\item The expression $e_2$ is evaluated to an object $o$. \item If either \THIS{} or $o$ is the null object (\ref{null}), - then $ee$ evaluates to evaluates to \TRUE{} + then $e$ evaluates to evaluates to \TRUE{} if both \THIS{} and $o$ are the null object and to \FALSE{} otherwise. Otherwise, -\item evaluation of $ee$ is equivalent to the method invocation +\item evaluation of $e$ is equivalent to the method invocation \code{\SUPER.==($o$)}. \end{itemize} @@ -16731,20 +16780,6 @@ \subsection{Equality} \NULL{} == $e$ or $e$ == \NULL.% } -\LMHash{}% -An equality expression of the form \code{$e_1$ != $e_2$} is equivalent to -the expression \code{!($e_1$ == $e_2$)}. -An equality expression of the form \code{\SUPER{} != $e$} is equivalent to -the expression \code{!(\SUPER{} == $e$)}. - -%The expression $e_1$ is evaluated to an object $o_1$; -% then the expression $e_2$ is evaluated to an object $o_2$. -% Next, if $o_1$ and $o_2$ are the same object, -% then $ee$ evaluates to \TRUE, otherwise $ee$ evaluates to \FALSE. - -\LMHash{}% -The static type of an equality expression is \code{bool}. - \subsection{Relational Expressions} \LMLabel{relationalExpressions} @@ -19407,137 +19442,135 @@ \subsection{Try} A try statement consists of a block statement, followed by at least one of: \begin{enumerate} \item - A set of \ON{}-\CATCH{} clauses, each of which specifies + A sequence of \ON-\CATCH{} clauses derived from \synt{onPart}, + each of which specifies (either explicitly or implicitly) the type of exception object to be handled, one or two exception parameters, and a block statement. \item -A \FINALLY{} clause, which consists of a block statement. + A \FINALLY{} clause, which consists of a block statement. \end{enumerate} -\rationale{% -The syntax is designed to be upward compatible with -existing Javascript programs. -The \ON{} clause can be omitted, -leaving what looks like a Javascript catch clause.% -} - \LMHash{}% A try statement of the form -\code{\TRY{} $s_1$ $on-catch_1 \ldots on-catch_n$;} +\code{\TRY\,\,$B_1$\,\,$on-catch_1\,\,\ldots\,\,on-catch_n$;} is equivalent to the statement -\code{\TRY{} $s_1$ $on-catch_1 \ldots on-catch_n$ \FINALLY{} $\{\}$}. +\code{% + \TRY\,\,$B_1$\,\,$on-catch_1\,\,\ldots\,\,on-catch_n$\,\,\FINALLY\,\,$\{\}$}. \LMHash{}% -An \ON{}-\CATCH{} clause of the form -\code{\ON{} $T$ \CATCH{} ($p_1$) $s$} -is equivalent to an \ON{}-\CATCH{} clause -\code{\ON{} $T$ \CATCH{} ($p_1$, $p_2$) $s$} +An \ON-\CATCH{} clause of the form +\code{\ON\,\,$T$\,\,\CATCH\,\,($p_1$)\,\,$B$} +is equivalent to the \ON-\CATCH{} clause +\code{\ON\,\,$T$\,\,\CATCH\,\,($p_1$,\,\,$p_2$)\,\,$B$} where $p_2$ is a fresh identifier. - -\LMHash{}% -An \ON{}-\CATCH{} clause of the form -\code{\ON{} $T$ $s$} -is equivalent to an \ON{}-\CATCH{} clause -\code{\ON{} $T$ \CATCH{} ($p_1$, $p_2$) $s$} +% +An \ON-\CATCH{} clause of the form +\code{\ON\,\,$T$\,\,$B$} +is equivalent to the \ON-\CATCH{} clause +% We have to break the line here, hence `{}`. +\code{\ON\,\,$T$\,\,\CATCH{} ($p_1$,\,\,$p_2$)\,\,$B$} where $p_1$ and $p_2$ are fresh identifiers. - -\LMHash{}% -An \ON{}-\CATCH{} clause of the form -\code{\CATCH{} ($p$) $s$} -is equivalent to an \ON{}-\CATCH{} clause -\code{\ON{} \DYNAMIC{} \CATCH{} ($p$, $p_2$) $s$} +% +An \ON-\CATCH{} clause of the form +\code{\CATCH\,\,($p$)\,\,$B$} +is equivalent to the \ON-\CATCH{} clause +\code{\ON\,\,Object\,\,\CATCH\,\,($p$,\,\,$p_2$)\,\,$B$} where $p_2$ is a fresh identifier. - -An \ON{}-\CATCH{} clause of the form -\code{\CATCH{} ($p_1$, $p_2$) $s$} -is equivalent to an \ON{}-\CATCH{} clause -\code{\ON{} \DYNAMIC{} \CATCH{} ($p_1$, $p_2$) $s$}. - -\LMHash{}% -An \ON{}-\CATCH{} clause of the form -\code{\ON{} $T$ \CATCH{} ($p_1$, $p_2$) $s$} -introduces a new scope $CS$ in which final local variables -specified by $p_1$ and $p_2$ are defined. -The statement $s$ is enclosed within $CS$. -The static type of $p_1$ is $T$ +% +An \ON-\CATCH{} clause of the form +\code{\CATCH\,\,($p_1$,\,\,$p_2$)\,\,$B$} +is equivalent to the \ON-\CATCH{} clause +\code{\ON\,\,Object\,\,\CATCH\,\,($p_1$,\,\,$p_2$)\,\,$B$}. + +\LMHash{}% +An \ON-\CATCH{} clause of the form +\code{\ON\,\,$T$\,\,\CATCH\,\,($p_1$,\,\,$p_2$)\,\,$B$} +introduces a new scope $CS$ into which final local variables +with the names $p_1$ and $p_2$ are introduced. +The current scope for the immediately enclosing \TRY{} statement +is the enclosing scope for $CS$, +and $CS$ is the current scope for the block $B$. +The static type of $p_1$ is $T$, and the static type of $p_2$ is \code{StackTrace}. +It is a \Error{compile-time error} if $T$ is a deferred type. \LMHash{}% -Execution of a \TRY{} statement $s$ of the form: +Consider a \TRY{} statement $S$ of the following form: \begin{normativeDartCode} -\TRY{} $b$ -\ON{} $T_1$ \CATCH{} ($e_1$, $t_1$) $c_1$ +\TRY{} $B$ +\ON{} $T_1$ \CATCH{} ($e_1$, $t_1$) $B_1$ \ldots{} -\ON{} $T_n$ \CATCH{} ($e_n$, $t_n$) $c_n$ -\FINALLY{} $f$ +\ON{} $T_n$ \CATCH{} ($e_n$, $t_n$) $B_n$ +\FINALLY{} $B_f$ \end{normativeDartCode} -proceeds as follows: \LMHash{}% -First $b$ is executed. -If execution of $b$ throws (\ref{statementCompletion}) +Execution of $S$ proceeds as follows. +First $B$ is executed. +If execution of $B$ throws (\ref{statementCompletion}) with exception object $e$ and stack trace $t$, -then $e$ and $t$ are matched against the \ON{}-\CATCH{} clauses -to yield a new completion (\ref{on-catch}). +then $e$ and $t$ are matched against the \ON-\CATCH{} clauses +to yield a new completion +(\ref{matchingOfOnCatchClauses}). -Then, even if execution of $b$ did not complete normally -or matching against the \ON{}-\CATCH{} clauses did not complete normally, -the $f$ block is executed. +\LMHash{}% +Then, even if execution of $B$ did not complete normally +or matching against the \ON-\CATCH{} clauses did not complete normally, +the $B_f$ block is executed. -If execution of $f$ does not complete normally, +\LMHash{}% +If execution of $B_f$ does not complete normally, execution of the \TRY{} statement completes in the same way. -Otherwise if execution of $b$ threw (\ref{statementCompletion}), +Otherwise, if execution of $B$ threw (\ref{statementCompletion}), the \TRY{} statement completes in the same way as -the matching against the \ON{}-\CATCH{} clauses. +the matching against the \ON-\CATCH{} clauses. Otherwise the \TRY{} statement completes in the same way as -the execution of $b$. - -\LMHash{}% -It is a \Error{compile-time error} if $T_i$, $1 \le i \le n$ is a deferred type. +the execution of $B$. -\subsubsection{\ON{}-\CATCH{} clauses} -\LMLabel{on-catch} +\subsubsection{Matching of \ON-\CATCH{} Clauses} +\LMLabel{matchingOfOnCatchClauses} \LMHash{}% -Matching an exception object $e$ and stack trace $t$ against -a (potentially empty) sequence of \ON{}-\CATCH{} clauses of the form +Consider a (potentially empty) sequence $s$ of +\ON-\CATCH{} clauses of the following form: \begin{normativeDartCode} -\ON{} $T_1$ \CATCH{} ($e_1$, $st_1$) \{ $s_1$ \} +\ON{} $T_1$ \CATCH{} ($p_1$, $q_1$) $B_1$ \ldots -\ON{} $T_n$ \CATCH{} ($e_n$, $st_n$) \{ $s_n$ \} +\ON{} $T_n$ \CATCH{} ($p_n$, $q_n$) $B_n$ \end{normativeDartCode} -proceeds as follows: \LMHash{}% -If there are no \ON{}-\CATCH{} clauses ($n = 0$), matching throws -the exception object $e$ and stack trace $t$ +Matching of an exception object $e$ and stack trace $t$ against $s$ +proceeds as follows. +If there are no \ON-\CATCH{} clauses ($n = 0$), +matching throws the exception object $e$ and stack trace $t$ (\ref{statementCompletion}). \LMHash{}% -Otherwise the exception is matched against the first clause. +Otherwise, when $n > 0$, +the exception is matched against the first clause. \LMHash{}% -Otherwise, if the type of $e$ is a subtype of $T_1$, -then the first clause matches, -and then $e_1$ is bound to the exception object $e$ -and $t_1$ is bound to the stack trace $t$, -and $s_1$ is executed in this scope. +If the type of $e$ is a subtype of $T_1$ then the first clause matches. +In this case $p_1$ is bound to the exception object $e$ +and $q_1$ is bound to the stack trace $t$, +and $B_1$ is executed in this scope. The matching completes in the same way as this execution. \LMHash{}% Otherwise, if the first clause did not match $e$, -$e$ and $t$ are recursively matched against -the remaining \ON{}-\CATCH{} clauses: +then $e$ and $t$ are recursively matched against +the second to $n$th \ON-\CATCH{} clauses of $s$: \begin{normativeDartCode} -\ON{} $T_2$ \CATCH{} ($e_2$, $t_2$) \{ $s_2$ \} +\ON{} $T_2$ \CATCH{} ($p_2$, $q_2$) $B_2$ \ldots -\ON{} $T_n$ \CATCH{} ($e_n$, $t_n$) \{ $s_n$ \} +\ON{} $T_n$ \CATCH{} ($p_n$, $q_n$) $B_n$ \end{normativeDartCode} @@ -23053,7 +23086,7 @@ \subsection{Intersection Types} of $S$ is the interface of $T$. -\subsection{Type \code{Never}} +\subsection{Type Never} \LMLabel{typeNever} \LMHash{}% @@ -23081,8 +23114,25 @@ \subsection{Type \code{Never}} can be used to hold any function that accepts a single, positional argument.% } +\LMHash{}% +% Note that we are not even checking invocations of members of `Object` +% statically, so `(throw 0).noSuchMethod(1);` is OK. The analyzer will +% actually hint that `(1)` is dead code, so it isn't accepted silently. +Every member access +(\commentary{% +such as calling a method, operator, or getter, or tearing off a method% +}) +on a receiver whose static type is \code{Never} +is treated by the static analysis as producing a result of type \code{Never}. +Invoking an expression of type \code{Never} as a function +is treated by the static analysis as producing a result of type \code{Never}. +In both cases, no syntactically correct list of actual arguments is an error +(\commentary{% +except of course that individual expressions in that list could have errors% +}). + \commentary{% -With a receiver whose static type is \code{Never}, +Hence, with a receiver whose static type is \code{Never}, it is not an error to invoke any method, setter, or getter, or to tear off any method. In other words, \code{Never} is considered to have all members, @@ -23091,13 +23141,9 @@ \subsection{Type \code{Never}} Implementations that provide feedback about dead or unreachable code are encouraged to indicate that arguments passed to any such invocation -will not be evaluated at run time. - -It is not an error to apply an expression of type \code{Never} -in the function position of a function call. -Implementations that provide feedback about dead or unreachable code -are encouraged to indicate that any arguments to the call -will not be evaluated at run time.% +will not be evaluated at run time, +and similarly for any arguments passed to an expression of type \code{Never} +which is invoked as a function.% } @@ -24350,6 +24396,7 @@ \subsubsection{Local variables and inference} T}. \subsubsection{Expression typing} +\subsubsection{Super-bounded types} !!!TODO!!! From 144a38dc5612085c1fd6fce73d7172f7782fef49 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 12 Nov 2021 16:47:08 +0100 Subject: [PATCH 12/85] Super-bounded types, least and greatest closure --- specification/dartLangSpec.tex | 300 +++++++++++++++++++++++++++++---- 1 file changed, 263 insertions(+), 37 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 6b80abecf..f07b29df1 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -7737,10 +7737,10 @@ \subsection{Super-Bounded Types} including some cases where a limited form of violation is allowed. \LMHash{}% -A \Index{top type} is a type $T$ such that \code{Object} is a subtype of $T$. +A \Index{top type} is a type $T$ such that \code{Object?} is a subtype of $T$. \commentary{% -For instance, \code{Object}, \DYNAMIC, and \VOID{} are top types, -and so are \code{FutureOr<\VOID>} and \code{FutureOr{}>}.% +For instance, \code{Object?}, \DYNAMIC, \VOID, and +\code{FutureOr{}>} are top types.% } % We define the property of being regular-bounded for all types, @@ -7751,8 +7751,8 @@ \subsection{Super-Bounded Types} % situations. \LMHash{}% -Every type which is not a parameterized type is \Index{regular-bounded}. - +Every type which is not a parameterized type is +\IndexCustom{regular-bounded}{type!regular-bounded}. \commentary{% In particular, every non-generic class and every function type is a regular-bounded type.% @@ -7760,15 +7760,14 @@ \subsection{Super-Bounded Types} \LMHash{}% Let $T$ be a parameterized type of the form -\code{$G$<$S_1, \ldots,\ S_n$>} +\code{$G$<\List{T}{1}{s}>} where $G$ denotes a generic class or a generic type alias. -Let -\code{$X_1\ \EXTENDS\ B_1, \ldots,\ X_n\ \EXTENDS\ B_n$} +Let \TypeParametersStd{} be the formal type parameter declarations of $G$. $T$ is \Index{regular-bounded} if{}f -$S_j$ is a subtype of -$[S_1/X_1, \ldots,\ S_n/X_n]B_j$, -for all $j \in 1 .. n$. +$T_j$ is a subtype of +$[T_1/X_1, \ldots,\ T_s/X_s]B_j$, +for all $j \in 1 .. s$. \commentary{% This means that regular-bounded types are those types @@ -7776,8 +7775,8 @@ \subsection{Super-Bounded Types} } \LMHash{}% -Let $T$ be a parameterized type of the form -\code{$G$<$S_1, \ldots,\ S_n$>} +\BlindDefineSymbol{T,T_j,s}% +Let $T$ be a parameterized type of the form \code{$G$<\List{T}{1}{s}>} where $G$ denotes a generic class or a generic type alias. $T$ is \Index{super-bounded} if{}f the following conditions are both true: @@ -7785,15 +7784,17 @@ \subsection{Super-Bounded Types} \item $T$ is not regular-bounded. \item - Let $T'$ be the result of replacing every occurrence in $T$ - of a top type in a covariant position by \code{Null}, - and every occurrence in $T$ - of \code{Null} in a contravariant position by \code{Object}. + Let $T'$ be the result of replacing every occurrence in $T$ of + a type $S$ in a contravariant position where \SubtypeNE{S}{\code{Never}} + by \code{Object?}, + and every occurrence in $T$ of a top type in + a position which is not contravariant + by \code{Never}. It is then required that $T'$ is regular-bounded. % Moreover, if $G$ denotes a generic type alias with body $U$, it is required that every type that occurs as a subterm of - $[S_1/X_1, \ldots,\ S_n/X_n]U$ + $[T_1/X_1, \ldots,\ T_s/X_s]U$ is well-bounded (defined below). \end{itemize} @@ -7804,24 +7805,30 @@ \subsection{Super-Bounded Types} } \LMHash{}% -A type $T$ is \Index{well-bounded} if{}f +A type is \Index{well-bounded} if{}f it is either regular-bounded or super-bounded. - -\LMHash{}% -Any use of a type $T$ which is not well-bounded is a \Error{compile-time error}. +Any use of a type which is not well-bounded is a \Error{compile-time error}. \LMHash{}% It is a \Error{compile-time error} if a parameterized type $T$ is super-bounded +\BlindDefineSymbol{T}% when it is used in any of the following ways: \begin{itemize} -\item $T$ is an immediate subterm of a new expression +\item $T$ occurs in an instance creation (\ref{new}) - or a constant object expression + of the form \code{\NEW?\,\,$T$(\metavar{parameters})} or + \code{\NEW?\,\,$T$.\id(\metavar{parameters})}, + or it occurs in the corresponding location in a constant object expression (\ref{const}). -\item $T$ is an immediate subterm of a redirecting factory constructor - header + \commentary{% + This includes the case where the actual type arguments have been provided + by type inference, which is assumed to have taken place already.% + } +\item $T$ occurs in the \synt{constructorDesignation} of + a redirecting factory constructor header (\ref{redirectingFactoryConstructors}). -\item $T$ is an immediate subterm of an \EXTENDS{} clause of a class +\item $T$ occurs in an extends clause of a class of the form + \code{\EXTENDS\,\,$T$} (\ref{superclasses}), or it occurs as an element in the type list of an \IMPLEMENTS{} clause %% TODO(eernst): Come extension types, add ref. Maybe mixin class? @@ -10899,12 +10906,10 @@ \subsubsection{Set and Map Literal Disambiguation} with leaf elements $\cal L$ and context type $C$. If $C$ is \FreeContext{} then let $S$ be undefined. \BlindDefineSymbol{S}% -%% TODO(eernst): Define `greatest closure' of a context type -%% when we define `context type'. Otherwise let $S$ be the greatest closure of \futureOrBase{C} -(\ref{typeFutureOr}). +(\ref{leastAndGreatestClosureOfTypes}, \ref{typeFutureOr}). -%% TODO(eernst): Delete when `context type', `greatest closure' are defined. +%% TODO(eernst): Delete when `context type' is defined. \commentary{% A future version of this document will specify context types. The basic intuition is that a @@ -10920,12 +10925,7 @@ \subsubsection{Set and Map Literal Disambiguation} This gives rise to an \IndexCustom{unconstrained context type}{context type!unconstrained}, \IndexCustom{\rm\FreeContext}{[]@\FreeContext}, -which may also occur in a composite term, e.g., \code{List<\FreeContext>}. -%% TODO(eernst): Clarify why we do not just use i2b, rather than -%% introducing the notion of a greatest (and least) closure. -The greatest closure of a context type $C$ is -approximately the least common supertype of all types -obtainable by replacing \FreeContext{} by a type.% +which may also occur in a composite term, e.g., \code{List<\FreeContext>}.% } \LMHash{}% @@ -22958,6 +22958,230 @@ \subsection{Type Normalization} } +\subsection{Least and Greatest Closure of Types} +\LMLabel{leastAndGreatestClosureOfTypes} + +\LMHash{}% +This section specifies how type variables can be +eliminated from a given type $T$ +by computing an upper respectively lower bound of $T$ known as the +least respectively the greatest closure of $T$. + +\commentary{% +The greatest closure $T_G$ of a type $T$ differs from +instantiation to bound +(\ref{instantiationToBound}) +in several ways. +First, instantiation to bound yields a type based on +a generic class or type alias $G$, +whereas the greatest closure takes a parameterized type, +so $T$ has received some actual type arguments +and is of the form \code{$G$<\List{T}{1}{s}>}. +Next, the greatest closure is an upper bound, i.e., +\SubtypeNE{T}{T_G} is guaranteed. +Instantiation to bound on a generic class $G$ will yield a type $T_I$ +such that \SubtypeNE{\code{$G$<\List{U}{1}{s}>}}{T_I} +for all \List{U}{1}{s} such that +\code{$G$<\List{U}{1}{s}>} is a regular-bounded type; +but in some cases that subtype relationship does not hold, +especially with type aliases involving invariance. +Also, instantiation to bound uses the declared bounds whenever possible, +but the greatest closure uses \code{Object?}\ immediately. +For instance, with \code{\CLASS\,\,C \{\}}, +the greatest closure of \code{C<$Y$>} with respect to $\{Y\}$ is +the super-bounded type \code{C}, +but the instantiation to bound on \code{C} is \code{C}.% +} + +\LMHash{}% +\BlindDefineSymbol{S,L,X_j,n}% +Let $S$ be a type and $L$ a set of type variables of the form +$\{\List{X}{1}{n}\}$. + +\LMHash{}% +We define the +\IndexCustom{least closure}{type!least closure} +of $S$ with respect to $L$ to be $S$ +with every covariant occurrence of $X_j$ replaced with \code{Never}, +and every contravariant occurrence of $X_j$ replaced with \code{Object?}, +for each $j \in 1 .. n$. +The invariant occurrences are treated as described explicitly below. + +\LMHash{}% +Similarly, we define the +\IndexCustom{greatest closure}{type!greatest closure} +of $S$ with respect to $L$ to be $S$ +with every covariant occurrence of $X_j$ replaced with \code{Object?}, +and every contravariant occurrence of $X_j$ replaced with \code{Never}, +for each $j \in 1 .. n$. +The invariant occurrences are treated as described explicitly below. + +\begin{itemize} +\item + If $S$ is $X$, where $X$ is a type variable in $L$, then + the least closure of $S$ with respect to $L$ is \code{Never}, + and the greatest closure of $S$ with respect to $L$ is \code{Object?}. +\item + If $S$ does not contain any type variables from $L$ + (\commentary{% + for instance, if $S$ is an atomic type, \ref{typeNormalization}}) + then the least and greatest closure of $S$ is $S$. +\item + If $S$ is a type of the form $T?$ then + \begin{itemize} + \item + the least closure of $S$ with respect to $L$ is $U?$, + where $U$ is the least closure of $T$ with respect to $L$. + \item + the greatest closure of $S$ with respect to $L$ is $U?$, + where $U$ is the greatest closure of $T$ with respect to $L$. + \end{itemize} +\item + If $S$ is a type of the form \code{FutureOr<$T$>} then + \begin{itemize} + \item + the least closure of $S$ with respect to $L$ is \code{FutureOr<$U$>}, + where $U$ is the least closure of $T$ with respect to $L$. + \item + the greatest closure of $S$ with respect to $L$ is \code{FutureOr<$U$>}, + where $U$ is the greatest closure of $T$ with respect to $L$. + \end{itemize} +\item + If $S$ is an interface type of the type \code{$C$<\List{T}{1}{k}>} then + \begin{itemize} + \item + the least closure of $S$ with respect to $L$ is \code{$C$<\List{U}{1}{k}>}, + where $U_i$ is the least closure of $T_i$ with respect to $L$, + for each $i \in 1 .. k$. + \item + the greatest closure of $S$ with respect to $L$ + is \code{$C$<\List{U}{1}{k}>}, + where $U_i$ is the greatest closure of $T_i$ with respect to $L$, + for each $i \in 1 .. k$. + \end{itemize} +\item + If $S$ is a type of the form + + \noindent + \FunctionTypePositionalStd{T_0} + + \noindent + and no type variable in $L$ occurs in any of the $B_j$, $j \in 1 .. s$, + then + \begin{itemize} + \item + the least closure of $S$ with respect to $L$ is + + \noindent + \FunctionTypePositional{U_0}{\\}{X}{B}{s}{U}{n}{k} + + \noindent + where + $U_0$ is the least closure of $T_0$ with respect to $L$, + $U_j$ is the greatest closure of $T_j$ with respect to $L$ + for each $j \in 1 .. n + k$. + It is assumed that type variables have been consistently renamed + if necessary, + such that no $X_j$ appears in $L$, + for each $j \in 1 .. s$. + \item + the greatest closure of $S$ with respect to $L$ is + + \noindent + \FunctionTypePositional{U_0}{\\}{X}{B}{s}{U}{n}{k} + + \noindent + where $U_0$ is the greatest closure of $T_0$ with respect to $L$, + $U_j$ is the least closure of $T_j$ with respect to $L$ + for each $j \in 1 .. n + k$. + It is assumed that type variables have been consistently renamed + if necessary, + such that no $X_j$ appears in $L$, + for each $j \in 1 .. s$. + \end{itemize} +\item + If $S$ is a type of the form + + \noindent + \FunctionTypeNamedStd{T_0} + + \noindent + where $r_j$ is derived from \code{\REQUIRED?}, $j \in n + 1 .. n + k$, + and no type variable in $L$ occurs in any of the $B_j$, $j \in 1 .. s$, + then + \begin{itemize} + \item + the least closure of $S$ with respect to $L$ is + + \noindent + \FunctionTypeNamed{U_0}{ }{X}{B}{s}{U}{n}{x}{k} + + \noindent + where + $U_0$ is the least closure of $T_0$ with respect to $L$, + $U_j$ is the greatest closure of $T_j$ with respect to $L$ + for each $j \in 1 .. n + k$. + It is assumed that type variables have been consistently renamed + if necessary, + such that no $X_j$ appears in $L$, + for each $j \in 1 .. s$. + \item + the greatest closure of $S$ with respect to $L$ is + + \noindent + \FunctionTypeNamed{U_0}{ }{X}{B}{s}{U}{n}{x}{k} + + \noindent + where $U_0$ is the greatest closure of $T_0$ with respect to $L$, + $U_j$ is the least closure of $T_j$ with respect to $L$ + for each $j \in 1 .. n + k$. + It is assumed that type variables have been consistently renamed + if necessary, + such that no $X_j$ appears in $L$, + for each $j \in 1 .. s$. + \end{itemize} +\item + If $S$ is a type of one of the forms + + \noindent + \FunctionTypePositionalStd{T_0} + + \noindent + \FunctionTypeNamedStd{T_0} + + \noindent + where $L$ contains one or more free type variables that occur in $B_j$ + for some $j \in 1 .. s$, + then the least closure of $S$ with respect to $L$ is \code{Never}, + and the greatest closure of $S$ with respect to $L$ is \FUNCTION. +\end{itemize} + +\LMHash{}% +A \Index{type schema} is a type where +\FreeContext{} may occur as if it were a type variable. + +\commentary{% +For example, \code{List<\FreeContext>} and \code{int} are type schemas. +Type schemas are used to express and propagate constraints on types +during type inference. +Type inference is not yet specified in this document, +but it will be specified in the future.% +} + +\LMHash{}% +Let $P$ be a type schema. +We define the least and greatest closure of $P$ +with respect to \FreeContext{} and a set of type variables $L$ as +the least and greatest closure with respect to $L \cup \{\FreeContext\}$, +where \FreeContext{} is treated as a type variable. + +\commentary{% +Note that the least closure of a type schema is always a subtype of +any type which matches the schema, and the greatest closure of a type +schema is always a supertype of any type which matches the schema.% +} + + \subsection{Types Bounded by Types} \LMLabel{typesBoundedByTypes} @@ -24524,6 +24748,8 @@ \subsubsection{Const type variable elimination} \subsubsection{Extension method resolution} +!!!TODO!!! + For the purposes of extension method resolution, there is no special treatment of nullable types with respect to what members are considered accessible. That is, the only members of a nullable type that are considered accessible From 80cef19ad25aef1b3bbe085e94dc0f6a65d573ad Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 12 Nov 2021 17:04:03 +0100 Subject: [PATCH 13/85] Assignability, generics --- specification/dart.sty | 6 ----- specification/dartLangSpec.tex | 48 +++++++--------------------------- 2 files changed, 10 insertions(+), 44 deletions(-) diff --git a/specification/dart.sty b/specification/dart.sty index 804d51ce8..bb4318495 100644 --- a/specification/dart.sty +++ b/specification/dart.sty @@ -471,12 +471,6 @@ \newcommand{\Supertype}[3]{\ensuremath{{#1}\vdash{#2}\,:>\,{#3}}} \newcommand{\SupertypeStd}[2]{\Supertype{\Delta}{#1}{#2}} -% Judgment expressing that an assignability relation exists. -\newcommand{\AssignableRelationSymbol}{\ensuremath{\Longleftrightarrow}} -\newcommand{\Assignable}[3]{% - \ensuremath{{#1}\vdash{#2}\,\AssignableRelationSymbol\,{#3}}} -\newcommand{\AssignableStd}[2]{\Assignable{\Gamma}{#1}{#2}} - % Semantic function delivering the superinterfaces of a class. \newcommand{\Superinterfaces}[1]{\ensuremath{\metavar{Superinterfaces}({#1})}} \newcommand{\Superinterface}[2]{{#1}\in\Superinterfaces{#2}} diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index f07b29df1..fd497844e 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -7465,7 +7465,7 @@ \section{Generics} \LMHash{}% A type parameter $T$ may be suffixed with an \EXTENDS{} clause that specifies the \Index{upper bound} for $T$. -If no \EXTENDS{} clause is present, the upper bound is \code{Object}. +If no \EXTENDS{} clause is present, the upper bound is \code{Object?}. It is a \Error{compile-time error} if a type parameter is a supertype of its upper bound when that upper bound is itself a type variable. @@ -22665,20 +22665,14 @@ \subsubsection{Additional Subtyping Concepts} \Index{assignable}. \rationale{% -This rule may surprise readers accustomed to conventional typechecking. -The intent of the \AssignableRelationSymbol{} relation -is not to ensure that an assignment is guaranteed to succeed dynamically. -Instead, it aims to only flag assignments -that are almost certain to be erroneous, -without precluding assignments that may work. - -For example, assigning an object of static type \code{Object} -to a variable with static type \code{String}, -while not guaranteed to be correct, -might be fine if the run-time value happens to be a string. - -A static analyzer or compiler -may support more strict static checks as an option.% +The use of the type \DYNAMIC{} is intended to shift type checks from +compile time to run time, +thus allowing operations that are not statically safe, +but which may succeed or fail at run time. +This treatment of \DYNAMIC{} ensures that +expressions of type \DYNAMIC{} are treated +as if the compiler would implicitly insert a downcast whenever needed, +in order to make the program type correct.% } @@ -24746,32 +24740,10 @@ \subsubsection{Const type variable elimination} \} \end{dartCode} -\subsubsection{Extension method resolution} +\subsubsection{Combined member signatures} !!!TODO!!! -For the purposes of extension method resolution, there is no special treatment -of nullable types with respect to what members are considered accessible. That -is, the only members of a nullable type that are considered accessible -(and hence which take precedence over extensions) are the members on \code{Object}. - -For the purposes of extension method resolution, the type \code{Never} is considered -to implement all members, and hence no extension may apply to an expression of -type \code{Never}. - -\subsubsection{Assignability} - -The definition of assignability is changed as follows. - -A type $T$ is \Index{assignable} to a type $S$ if $T$ is \DYNAMIC, or if $T$ is a -subtype of $S$. - -\subsubsection{Generics} - -The default bound of generic type parameters is treated as \code{Object?}. - -\subsubsection{Combined member signatures} - This section, line 4241 in the language specification defines the notion of a _combined member signature_. In Dart before null-safety it is based on the textually first From a5aa267275c3d0aa7465b873cc5f465ae9cf5ded Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 12 Nov 2021 18:52:55 +0100 Subject: [PATCH 14/85] Extreme types --- specification/dartLangSpec.tex | 326 ++++++++++++++++++++------------- 1 file changed, 195 insertions(+), 131 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index fd497844e..b8d30d411 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -5573,8 +5573,8 @@ \subsection{Combined Member Signatures} \LMHash{}% This section specifies how to compute a member signature which will -appropriately stand for a prioritized set of several member signatures, -taken from a given list of interfaces. +appropriately stand for a set of several member signatures, +taken from a given set of interfaces. \commentary{% In general, a combined member signature has a type which is @@ -5585,12 +5585,7 @@ \subsection{Combined Member Signatures} several different declarations of \id{} and does not override \id. In case of failure, it serves to specify the situations -where a developer must add a declaration in order to resolve an ambiguity. -The member signatures are prioritized in the sense that we will select -a member signature from the interface with the lowest possible index -in the case where several member signatures are equally suitable -to be chosen as the combined member signature. -That is, ``the first interface wins''.% +where a developer must add a declaration in order to resolve an ambiguity.% } \LMHash{}% @@ -5609,23 +5604,25 @@ \subsection{Combined Member Signatures} In particular, private methods from different libraries are never equal. Top types differ as well. For instance, -\FunctionTypeSimple{\DYNAMIC}{} and \FunctionTypeSimple{\code{Object}}{} +\FunctionTypeSimple{\DYNAMIC}{} and \FunctionTypeSimple{\code{Object?}}{} are not equal, even though they are subtypes of each other. We need this distinction because management of top type discrepancies is one of the purposes of computing a combined interface.% } \LMHash{}% -\BlindDefineSymbol{\id, L, I_j, k}% +\BlindDefineSymbol{n, L, I_j, k}% Now we define combined member signatures. -Let \id{} be an identifier, $L$ a library, -\List{I}{1}{k} a list of interfaces, +Let $n$ be a name +(an identifier optionally followed by \lit{=}), +$L$ a library, +\List{I}{1}{k} a set of interfaces, and \DefineSymbol{M_0} the set of -all member signatures from \List{I}{1}{k} named \id{} +all member signatures from \List{I}{1}{k} named $n$ and accessible to $L$. The \IndexCustom{combined member signature - named \id{} from \List{I}{1}{k} with respect to $L$}{% + named $n$ from \List{I}{1}{k} with respect to $L$}{% combined member signature} is the member signature which is obtained as follows: @@ -5637,8 +5634,9 @@ \subsection{Combined Member Signatures} the combined member signature is $m'$. \LMHash{}% -Otherwise, $M_0$ contains more than one member signature -\DefineSymbol{\List{m}{1}{q}}. +\BlindDefineSymbol{m_j,q} +Otherwise, $M_0$ contains more than one member signature named $n$. +Let \List{m}{1}{q} be those member signatures. \LMHash{}% \Case{Failing mixtures} @@ -5660,7 +5658,7 @@ \subsection{Combined Member Signatures} \Case{Methods and setters} In this case $M_0$ consists of setter signatures only, or method signatures only, -because the name \id{} in the former case always ends in \lit{=}, +because the name $n$ in the former case always ends in \lit{=}, which is never true in the latter case. \LMHash{}% @@ -5674,7 +5672,7 @@ \subsection{Combined Member Signatures} \commentary{% A useful intuition about this situation is that the given member signatures do not agree on -which type is suitable for the member named \id. +which type is suitable for the member named $n$. Otherwise we have a set of member signatures which are ``most specific'' in the sense that their function types are subtypes of them all.% } @@ -5683,7 +5681,7 @@ \subsection{Combined Member Signatures} \def\Nall{\ensuremath{N_{\mbox{\scriptsize{}all}}}} \def\Mall{\ensuremath{M_{\mbox{\scriptsize{}all}}}} -\def\MallFirst{\ensuremath{M_{\mbox{\scriptsize{}first}}}} +\def\mall{\ensuremath{m_{\metavar{all}}}} \LMHash{}% Otherwise, when a set $N$ as specified above exists, @@ -5691,7 +5689,7 @@ \subsection{Combined Member Signatures} \BlindDefineSymbol{\Mall}% and let $\Mall{} = \{ m_i\;|\;i \in \Nall\}$. \commentary{% -That is, \Mall{} contains all member signatures named \id{} +That is, \Mall{} contains all member signatures named $n$ with the most specific type. Dart subtyping is a partial pre-order, which ensures that such a greatest set of least elements exists, @@ -5699,39 +5697,29 @@ \subsection{Combined Member Signatures} We can have several such signatures because member signatures can be such that they are not equal, and yet their function types are subtypes of each other. -We need to compute one member signature from \Mall, -and we do that by using the ordering of the given interfaces.% -} - -\LMHash{}% -\BlindDefineSymbol{\MallFirst}% -Let $j \in 1 .. k$ be the smallest number such that -$\MallFirst{} = \Mall{} \cap I_j$ is non-empty. -Let $m_i$ be the single element that \MallFirst{} contains. -\commentary{% -This set contains exactly one element because it is non-empty -and no interface contains more than one member signature named \id. -In other words, we choose $m_i$ as the member signature from -the first possible interface -among the most specific member signatures \Mall.% -} +We need to compute one member signature from \Mall.% } \LMHash{}% +Let \mall{} be the result of applying \TopMergeTypeName{} to +the elements in \Mall. The combined member signature is then $m'$, -which is obtained from $m_i$ by adding the modifier \COVARIANT{} +which is obtained from \mall{} by adding the modifier \COVARIANT{} to each parameter $p$ (if it is not already present) when there exists a $j \in 1 .. q$ such that the parameter corresponding to $p$ (\ref{covariantParameters}) has the modifier \COVARIANT. +} + \commentary{% In other words, each parameter in the combined member signature is marked covariant if any of the corresponding parameters are marked covariant, not just among the most specific signatures, -but among \emph{all} signatures named \id{} (which are accessible to $L$) -in the given list of interfaces.% +but among \emph{all} signatures named $n$ +which are accessible to $L$ +in the given set of interfaces.% } \EndCase @@ -10587,7 +10575,7 @@ \subsubsection{List Literal Inference} Otherwise (\commentary{when $\ell$ is `\code{...?$e$}'}), let $S'$ be the inferred type of $e$ in context \code{Iterable<$P$>?} -and $S$ be \NonNullTypeOf{$S'$}. +and $S$ be \NonNullType{$S'$}. \begin{itemize} \item @@ -11206,7 +11194,7 @@ \subsubsection{Set and Map Literal Inference} then let $S$ be the inferred type of $e$ in context \code{Iterable<$P_e$>}; and if $\ell$ is \code{...?$e$} and $P$ is \code{Set<$P_e$>} then let $S'$ be the inferred type of $e$ in context \code{Iterable<$P_e$>?} -and let $S$ be \NonNullTypeOf{$S'$}. +and let $S$ be \NonNullType{$S'$}. Next: \begin{itemize} @@ -11237,7 +11225,7 @@ \subsubsection{Set and Map Literal Inference} the inferred type of $e$ in context $P$; if $\ell$ is `\code{...?$e$}' then let $S'$ be the inferred type of $e$ in context \code{$P$?}, -and let $S$ be \NonNullTypeOf{$S'$}. +and let $S$ be \NonNullType{$S'$}. Next: \begin{itemize} @@ -16582,7 +16570,7 @@ \subsection{If-null Expressions} \commentary{In this case, the \lit{??} is superfluous.} The static type of $e$ is the least upper bound (\ref{leastUpperBounds}) -of \NonNullTypeOf{$S$} and the static type of $e_2$. +of \NonNullType{$S$} and the static type of $e_2$. \LMHash{}% Evaluation of an if-null expression $e$ of the form \code{$e_1$ ?? $e_2$} @@ -16709,8 +16697,8 @@ \subsection{Equality} where the static type of $e_1$ is $T_1$ and the static type of $e_2$ is $T_2$. Let $S$ be the type of the formal parameter of -\code{operator\,\,==} in the interface of \NonNullTypeOf{$T_1$}, -using \code{Object} if \NonNullTypeOf{$T_1$} is \code{Never}. +\code{operator\,\,==} in the interface of \NonNullType{$T_1$}, +using \code{Object} if \NonNullType{$T_1$} is \code{Never}. It is a \Error{compile-time error} unless $T_2$ is assignable to \code{$S$?}. \LMHash{}% @@ -22799,6 +22787,167 @@ \subsection{Type Nullability} } +\subsection{Functions Dealing with Extreme Types} +\LMLabel{functionsDealingWithExtremeTypes} + +\LMHash{}% +This section defines several helper functions that are concerned with +the characterization and computation of types near the top or the bottom +of the subtype graph. + +\LMHash{}% +The functions are syntactic in nature such that termination is obvious. +In particular, they do not rely on subtyping. + +\LMHash{}% +The \Index{\TopMergeTypeName} of two types computes +a canonical type which represents both of them, +in the case where they are structurally identical +modulo the choice among top types. +It is defined as follows: + +\begin{itemize} +\item \DefEquals{\TopMergeType{Object?}{Object?}}{\code{Object?}}. +\item \DefEquals{\TopMergeType{\DYNAMIC}{\DYNAMIC}}{\code{\DYNAMIC}}. +\item \DefEquals{\TopMergeType{\VOID}{\VOID}}{\code{\VOID}}. +\item \DefEquals{\TopMergeType{Object?}{\VOID}}{\code{Object?}}, and\\ + \DefEquals{\TopMergeType{\VOID}{Object?}}{\code{Object?}}. +\item \DefEquals{\TopMergeType{\DYNAMIC}{\VOID}}{\code{Object?}}, and\\ + \DefEquals{\TopMergeType{\VOID}{\DYNAMIC}}{\code{Object?}} +\item \DefEquals{\TopMergeType{Object?}{\DYNAMIC}}{\code{Object?}}, and\\ + \DefEquals{\TopMergeType{\DYNAMIC}{Object?}}{\code{Object?}}. +\item \DefEquals{\TopMergeType{$T$?}{$S$?}}{\code{\TopMergeType{$T$}{$S$}?}}. +\item For all other types, the function is applied recursively. + + \commentary{% + E.g. + $\TopMergeType{$C$<$T$>}{$C$<$S$>} = \code{$C$<\TopMergeType{$T$}{$S$}>}$, + and\\ + $\TopMergeType{\FUNCTION($T$)}{\FUNCTION($S$)} =\\ + \code{\FUNCTION(\TopMergeType{$T$}{$S$})}$.% + } +\end{itemize} + +\commentary{% +Note that \TopMergeTypeName{} is not defined for +types which are structurally different, +apart from being or containing different top types. +} + +\rationale{% +\TopMergeTypeName{} yields \code{Object?}\ in the case where +the arguments differ in the choice of top types. +This reflects the intention that the resulting type should yield +a high degree of static type safety.% +} + +\commentary{% +For instance, if a class $C$ inherits a method $m$ that accepts +an argument of type \code{List<\DYNAMIC>} from one superinterface, +and another method $m$ that accepts +an argument of type \code{List<\VOID>} +from another superinterface, +the parameter will have type \code{List} in $C$, +and this will ensure a more strict static typing by default than, say, +\code{List<\DYNAMIC>}.% +} + +\LMHash{}% +\TopMergeTypeName{} of more than two types is defined by taking +\TopMergeTypeName{} of the first two, +and then recursively taking \TopMergeTypeName{} of the rest. + +\LMHash{}% +The \Index{\IsTopTypeName} predicate is true for any type which is in +the equivalence class of top types. +It is a syntactic characterization of top types +(\ref{superBoundedTypes}). + +\begin{itemize} +\item \DefEquals{\IsTopType{$T$?}}{\IsTopType{$T$} \vee \IsObjectType{$T$}}. +\item \DefEquals{\IsTopType{\DYNAMIC}}{\metavar{true}}. +\item \DefEquals{\IsTopType{\VOID}}{\metavar{true}}. +\item \DefEquals{\IsTopType{FutureOr<$T$>}}{\IsTopType{$T$}}. +\item \DefEquals{\IsTopType{$T$}}{\metavar{false}}, otherwise. +\end{itemize} + +\LMHash{}% +The \Index{\IsObjectTypeName} predicate is true if{}f +the argument is a subtype and a supertype of \code{Object}. + +\begin{itemize} +\item \DefEquals{\IsObjectType{Object}}{\metavar{true}}. +\item \DefEquals{\IsObjectType{FutureOr<$T$>}}{\IsObjectType{$T$}}. +\item \DefEquals{\IsObjectType{$T$}}{\metavar{false}}, otherwise. +\end{itemize} + +\LMHash{}% +The \Index{\IsBottomTypeName} predicate is true if{}f +the argument is a subtype of \code{Never}. + +\begin{itemize} +\item \DefEquals{\IsBottomType{Never}}{\metavar{true}}. +\item \DefEquals{\IsBottomType{$X$\,\,\&\,\,$T$}}{\IsBottomType{$T$}}. +\item \DefEquals{\IsBottomType{$X$\,\,\EXTENDS\,\,$T$}}{\IsBottomType{$T$}}. +\item \DefEquals{\IsBottomType{$T$}}{\metavar{false}}, otherwise. +\end{itemize} + +\LMHash{}% +The \Index{\IsNullTypeName} predicate is true if{}f +the argument is a subtype and a supertype of \code{Null}. + +\begin{itemize} +\item \DefEquals{\IsNullType{Null}}{\metavar{true}}. +\item \DefEquals{\IsNullType{$T$?}}{\IsNullType{$T$} \vee \IsBottomType{$T$}}. +\item \DefEquals{\IsNullType{$T$}}{\metavar{false}}, otherwise. +\end{itemize} + +\LMHash{}% +The \Index{\IsMoreTopTypeName} predicate defines a total order on +top and \code{Object} types. + +\begin{itemize} +\item \DefEquals{\IsMoreTopType{\VOID}{$T$}}{\metavar{true}}. +\item \DefEquals{\IsMoreTopType{$T$}{\VOID}}{\metavar{false}}. +\item \DefEquals{\IsMoreTopType{\DYNAMIC}{$T$}}{\metavar{true}}. +\item \DefEquals{\IsMoreTopType{$T$}{\DYNAMIC}}{\metavar{false}}. +\item \DefEquals{\IsMoreTopType{Object}{$T$}}{\metavar{true}}. +\item \DefEquals{\IsMoreTopType{$T$}{Object}}{\metavar{false}}. +\item \DefEquals{\IsMoreTopType{$T$?}{$S$?}}{\IsMoreTopType{$T$}{$S$}}. +\item \DefEquals{\IsMoreTopType{$T$}{$S$?}}{\metavar{true}}. +\item \DefEquals{\IsMoreTopType{$T$?}{$S$}}{\metavar{false}}. +\item \DefEquals{\IsMoreTopType{FutureOr<$T$>}{FutureOr<$S$>}}{% + \IsMoreTopType{$T$}{$S$}}. +\end{itemize} + +\LMHash{}% +The \Index{\IsMoreBottomTypeName} predicate defines an almost total order on +bottom and \code{Null} types. +\commentary{% +This does not consistently order +two different type variables with the same bound.% +} + +\begin{itemize} +\item \DefEquals{\IsMoreBottomType{Never}{$T$}}{\metavar{true}}. +\item \DefEquals{\IsMoreBottomType{$T$}{Never}}{\metavar{false}}. +\item \DefEquals{\IsMoreBottomType{Null}{$T$}}{\metavar{true}}. +\item \DefEquals{\IsMoreBottomType{$T$}{Null}}{\metavar{false}}. +\item \DefEquals{\IsMoreBottomType{$T$?}{$S$?}}{\IsMoreBottomType{$T$}{$S$}}. +\item \DefEquals{\IsMoreBottomType{$T$}{$S$?}}{\metavar{true}}. +\item \DefEquals{\IsMoreBottomType{$T$?}{$S$}}{\metavar{false}}. +\item \DefEquals{\IsMoreBottomType{$X$\,\,\&\,\,$T$}{$Y$\,\,\&\,\,$S$}}{% + \IsMoreBottomType{$T$}{$S$}}. +\item \DefEquals{\IsMoreBottomType{$X$\,\,\&\,\,$T$}{$S$}}{\metavar{true}}. +\item \DefEquals{\IsMoreBottomType{$S$}{$X$\,\,\&\,\,$T$}}{\metavar{false}}. +\item \DefEquals{% + \IsMoreBottomType{$X$\,\,\EXTENDS\,\,$T$}{$Y$\,\,\EXTENDS\,\,$S$}}{% + \IsMoreBottomType{$T$}{$S$}}. +\end{itemize} + +!!!TODO!!! Clean up this section: We should say that these definitions are used in order (first applicable case must be used). We should specify the acceptable arguments is each case, and argue that we're never calling them on anything which is not acceptable. + + \subsection{Type Normalization} \LMLabel{typeNormalization} @@ -24740,7 +24889,7 @@ \subsubsection{Const type variable elimination} \} \end{dartCode} -\subsubsection{Combined member signatures} +\subsubsection{Implicit conversions} !!!TODO!!! @@ -24870,91 +25019,6 @@ \subsubsection{Null promotion} These are extended as per [separate proposal](https://github.com/dart-lang/language/blob/master/resources/type-system/flow-analysis.md). -\subsection{Helper predicates} - -The following helper predicates are used to classify types. They are syntactic -in nature such that termination is obvious. In particular, they do not rely on -subtyping. - -The \IsTopTypeName{} predicate is true for any type which is in the equivalence class of -top types. - -\begin{itemize} -\item \IsTopType{$T$?}\ is true iff \IsTopType{$T$} or \IsObjectType{$T$} -\item \IsTopType{\DYNAMIC} is true -\item \IsTopType{\VOID} is true -\item \IsTopType{FutureOr<$T$>} is \IsTopType{$T$} -\item \IsTopType{$T$} is false otherwise -\end{itemize} - -\IsTopType{$T$} is true if and only if $T$ is a supertype of \code{Object?}. - -The \IsObjectTypeName{} predicate is true for any type which is in the equivalence class -of \code{Object}. - -\begin{itemize} -\item \IsObjectType{Object} is true -\item \IsObjectType{FutureOr<$T$>} is \IsObjectType{$T$} -\item \IsObjectType{$T$} is false otherwise -\end{itemize} - -\IsObjectType{$T$} is true if and only if $T$ is a subtype and a supertype of -\code{Object}. - -The \IsBottomTypeName{} predicate is true for things in the equivalence class of \code{Never}. - -\begin{itemize} -\item \IsBottomType{Never} is true -\item \IsBottomType{$X$ \& $T$} is true iff \IsBottomType{$T$} -\item \IsBottomType{$X$ \EXTENDS\ $T$} is true iff \IsBottomType{$T$} -\item \IsBottomType{$T$} is false otherwise -\end{itemize} - -\IsBottomType{$T$} is true if and only if $T$ is a subtype of \code{Never}. - -The \IsNullTypeName{} predicate is true for things in the equivalence class of \code{Null}. - -\begin{itemize} -\item \IsNullType{Null} is true -\item \IsNullType{$T$?}\ is true iff \IsNullType{$T$} or \IsBottomType{$T$} -\item \IsNullType{$T$} is false otherwise -\end{itemize} - -\IsNullType{$T$} is true if and only if $T$ is a subtype and a supertype of \code{Null}. - -The \IsMoreTopTypeName{} predicate defines a total order on top and \code{Object} types. - -\begin{itemize} -\item \IsMoreTopType{\VOID}{$T$} = \code{true} -\item \IsMoreTopType{$T$}{\VOID} = \code{false} -\item \IsMoreTopType{\DYNAMIC}{$T$} = \code{true} -\item \IsMoreTopType{$T$}{\DYNAMIC} = \code{false} -\item \IsMoreTopType{Object}{$T$} = \code{true} -\item \IsMoreTopType{$T$}{Object} = \code{false} -\item \IsMoreTopType{$T$?}{$S$?} = \IsMoreTopType{$T$}{$S$} -\item \IsMoreTopType{$T$}{$S$?} = \code{true} -\item \IsMoreTopType{$T$?}{$S$} = \code{false} -\item \IsMoreTopType{FutureOr<$T$>}{FutureOr<$S$>} = \IsMoreTopType{$T$}{$S$} -\end{itemize} - -The \IsMoreBottomTypeName{} predicate defines an (almost) total order on bottom and -\code{Null} types. This does not currently consistently order two different type -variables with the same bound. - -\begin{itemize} -\item \IsMoreBottomType{Never}{$T$} = \code{true} -\item \IsMoreBottomType{$T$}{Never} = \code{false} -\item \IsMoreBottomType{Null}{$T$} = \code{true} -\item \IsMoreBottomType{$T$}{Null} = \code{false} -\item \IsMoreBottomType{$T$?}{$S$?} = \IsMoreBottomType{$T$}{$S$} -\item \IsMoreBottomType{$T$}{$S$?} = \code{true} -\item \IsMoreBottomType{$T$?}{$S$} = \code{false} -\item \IsMoreBottomType{$X$ \& $T$}{$Y$ \& $S$} = \IsMoreBottomType{$T$}{$S$} -\item \IsMoreBottomType{$X$ \& $T$}{$S$} = \code{true} -\item \IsMoreBottomType{$S$, $X$ \& $T$} = \code{false} -\item \IsMoreBottomType{$X$ \EXTENDS\ $T$}{$Y$ \EXTENDS\ $S$} = \IsMoreBottomType{$T$}{$S$} -\end{itemize} - \subsubsection{The main function} The section 'Scripts' in the language specification is replaced by the From 4c706bd7d66dd8eb845a406d024a3133ac4fa97e Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Mon, 15 Nov 2021 16:42:23 +0100 Subject: [PATCH 15/85] Extreme types, helper functions --- specification/dartLangSpec.tex | 36 ++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index b8d30d411..51ea3e618 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -5702,7 +5702,12 @@ \subsection{Combined Member Signatures} \LMHash{}% Let \mall{} be the result of applying \TopMergeTypeName{} to -the elements in \Mall. +\NormalizedTypeOfName{} of the elements in \Mall. +\commentary{% +Note that these types are mutual subtypes, +and hence the normalized forms are structurally identical, +modulo different choices of top type in some locations.% +} The combined member signature is then $m'$, which is obtained from \mall{} by adding the modifier \COVARIANT{} to each parameter $p$ (if it is not already present) @@ -22798,6 +22803,8 @@ \subsection{Functions Dealing with Extreme Types} \LMHash{}% The functions are syntactic in nature such that termination is obvious. In particular, they do not rely on subtyping. +In each of these function definitions, +the first applicable case must be used. \LMHash{}% The \Index{\TopMergeTypeName} of two types computes @@ -22812,10 +22819,10 @@ \subsection{Functions Dealing with Extreme Types} \item \DefEquals{\TopMergeType{\VOID}{\VOID}}{\code{\VOID}}. \item \DefEquals{\TopMergeType{Object?}{\VOID}}{\code{Object?}}, and\\ \DefEquals{\TopMergeType{\VOID}{Object?}}{\code{Object?}}. -\item \DefEquals{\TopMergeType{\DYNAMIC}{\VOID}}{\code{Object?}}, and\\ - \DefEquals{\TopMergeType{\VOID}{\DYNAMIC}}{\code{Object?}} \item \DefEquals{\TopMergeType{Object?}{\DYNAMIC}}{\code{Object?}}, and\\ \DefEquals{\TopMergeType{\DYNAMIC}{Object?}}{\code{Object?}}. +\item \DefEquals{\TopMergeType{\DYNAMIC}{\VOID}}{\code{Object?}}, and\\ + \DefEquals{\TopMergeType{\VOID}{\DYNAMIC}}{\code{Object?}} \item \DefEquals{\TopMergeType{$T$?}{$S$?}}{\code{\TopMergeType{$T$}{$S$}?}}. \item For all other types, the function is applied recursively. @@ -22832,6 +22839,9 @@ \subsection{Functions Dealing with Extreme Types} Note that \TopMergeTypeName{} is not defined for types which are structurally different, apart from being or containing different top types. +When \TopMergeTypeName{} is used in this specification, +each case where \TopMergeTypeName{} is undefined +is handled explicitly.% } \rationale{% @@ -22842,7 +22852,10 @@ \subsection{Functions Dealing with Extreme Types} } \commentary{% -For instance, if a class $C$ inherits a method $m$ that accepts +For instance, \TopMergeTypeName{} is used during the computation of +the interface of a class member which is declared +in multiple superinterfaces. +If a class $C$ inherits a method $m$ that accepts an argument of type \code{List<\DYNAMIC>} from one superinterface, and another method $m$ that accepts an argument of type \code{List<\VOID>} @@ -22856,7 +22869,8 @@ \subsection{Functions Dealing with Extreme Types} \TopMergeTypeName{} of more than two types is defined by taking \TopMergeTypeName{} of the first two, and then recursively taking \TopMergeTypeName{} of the rest. - +\commentary{The ordering of the arguments makes no difference.} + \LMHash{}% The \Index{\IsTopTypeName} predicate is true for any type which is in the equivalence class of top types. @@ -22871,7 +22885,7 @@ \subsection{Functions Dealing with Extreme Types} \item \DefEquals{\IsTopType{$T$}}{\metavar{false}}, otherwise. \end{itemize} -\LMHash{}% +\noindent The \Index{\IsObjectTypeName} predicate is true if{}f the argument is a subtype and a supertype of \code{Object}. @@ -22881,7 +22895,7 @@ \subsection{Functions Dealing with Extreme Types} \item \DefEquals{\IsObjectType{$T$}}{\metavar{false}}, otherwise. \end{itemize} -\LMHash{}% +\noindent The \Index{\IsBottomTypeName} predicate is true if{}f the argument is a subtype of \code{Never}. @@ -22892,7 +22906,7 @@ \subsection{Functions Dealing with Extreme Types} \item \DefEquals{\IsBottomType{$T$}}{\metavar{false}}, otherwise. \end{itemize} -\LMHash{}% +\noindent The \Index{\IsNullTypeName} predicate is true if{}f the argument is a subtype and a supertype of \code{Null}. @@ -22902,7 +22916,7 @@ \subsection{Functions Dealing with Extreme Types} \item \DefEquals{\IsNullType{$T$}}{\metavar{false}}, otherwise. \end{itemize} -\LMHash{}% +\noindent The \Index{\IsMoreTopTypeName} predicate defines a total order on top and \code{Object} types. @@ -22920,7 +22934,7 @@ \subsection{Functions Dealing with Extreme Types} \IsMoreTopType{$T$}{$S$}}. \end{itemize} -\LMHash{}% +\noindent The \Index{\IsMoreBottomTypeName} predicate defines an almost total order on bottom and \code{Null} types. \commentary{% @@ -22945,8 +22959,6 @@ \subsection{Functions Dealing with Extreme Types} \IsMoreBottomType{$T$}{$S$}}. \end{itemize} -!!!TODO!!! Clean up this section: We should say that these definitions are used in order (first applicable case must be used). We should specify the acceptable arguments is each case, and argue that we're never calling them on anything which is not acceptable. - \subsection{Type Normalization} \LMLabel{typeNormalization} From 29c41cdaf7c076829716f2a796278ef7829d80ec Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Tue, 16 Nov 2021 13:26:15 +0100 Subject: [PATCH 16/85] Rebase --- specification/dartLangSpec.tex | 204 +++++++++++++++++++-------------- 1 file changed, 115 insertions(+), 89 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 51ea3e618..fa5b34c96 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -3621,11 +3621,11 @@ \subsubsection{The Method \code{noSuchMethod}} whose dynamic type implements \code{Map}, with the same keys and values as the map resulting from evaluation of - \code{\{$\#x_1$: $x_1, \ldots,\ \#x_m$: $x_m$\}}. + \code{\{\NamedArgumentList{\#x}{x}{1}{m}\}}. \item \code{$im$.typeArguments} evaluates to an unmodifiable list whose dynamic type implements \code{List}, containing the same objects as the list resulting from evaluation of - \code{[$X_1, \ldots,\ X_r$]}. + \code{[\List{X}{1}{r}]}. \end{itemize} \LMHash{}% @@ -4246,19 +4246,19 @@ \subsubsection{Generative Constructors} where $R$ is of one of the forms \noindent -\code{$\THIS{}$($e_1 \ldots,\ e_p,\ x_1$:\ $e_{p+1}, \ldots,\ x_q$:\ $e_{p+q}$)} +\code{\THIS(\ArgumentList{e}{p}{x}{q})} \noindent -\code{$\THIS.\id$($e_1 \ldots,\ e_p,\ x_1$:\ $e_{p+1}, \ldots,\ x_q$:\ $e_{p+q}$)}. +\code{\THIS.\id(\ArgumentList{e}{p}{x}{q})}. \LMHash{}% The \IndexCustom{redirectee constructor}{constructor!redirectee} for this declaration is then the constructor denoted by -\code{$C$<$X_1 \ldots,\ X_m$>} respectively \code{$C$<$X_1 \ldots,\ X_m$>.\id}. +\code{$C$<\List{X}{1}{m}>} respectively \code{$C$<\List{X}{1}{m}>.\id}. It is a \Error{compile-time error} if the static argument list type (\ref{actualArgumentLists}) -of \code{($e_1 \ldots,\ e_p,\ x_1$:\ $e_{p+1}, \ldots,\ x_q$:\ $e_{p+q}$)} +of the actual argument list \code{(\ArgumentList{e}{p}{x}{q})} is not an assignable match for the formal parameter list of the redirectee. \commentary{% @@ -4391,11 +4391,11 @@ \subsubsection{Generative Constructors} Consider a \Index{superinitializer} $s$ of the form \noindent -\code{\SUPER($a_1, \ldots,\ a_n,\ x_{n+1}:\ a_{n+1}, \ldots,\ x_{n+k}$:\ $a_{n+k}$)} +\code{\SUPER(\ArgumentListStd)} respectively \noindent -\code{\SUPER.\id($a_1, \ldots,\ a_n,\ x_{n+1}:\ a_{n+1}, \ldots,\ x_{n+k}$:\ $a_{n+k}$)}. +\code{\SUPER.\id(\ArgumentListStd)}. \noindent{}% Let $S$ be the superclass of the enclosing class of $s$. @@ -4489,18 +4489,18 @@ \subsubsection{Generative Constructors} If $k$ is redirecting then its redirect clause has the form \noindent -\code{\THIS.$g$($a_1, \ldots,\ a_n,\ x_{n+1}$:\ $a_{n+1}, \ldots,\ x_{n+k}$:\ $a_{n+k}$)} +\code{\THIS.$g$(\ArgumentListStd)} where \DefineSymbol{g} identifies another generative constructor of the immediately surrounding class. Then execution of $k$ to initialize $i$ proceeds by evaluating the argument list -\code{($a_1, \ldots,\ a_n,\ x_{n+1}$:\ $a_{n+1}, \ldots,\ x_{n+k}$:\ $a_{n+k}$)} +\code{(\ArgumentListStd)} to an actual argument list $a$ of the form -\code{($o_1, \ldots,\ o_n,\ x_{n+1}$:\ $o_{n+1}, \ldots,\ x_{n+k}$:\ $o_{n+k}$)} +\code{(\ArgumentList{o}{n}{x}{k})} in an environment where the type parameters of the enclosing class are bound to -$t_1, \ldots, t_m$. +\List{t}{1}{m}. \LMHash{}% Next, the body of $g$ is executed to initialize $i$ @@ -4589,17 +4589,17 @@ \subsubsection{Generative Constructors} Consider a superinitializer \DefineSymbol{s} of the form \noindent -\code{\SUPER($a_1, \ldots,\ a_n,\ x_{n+1}:\ a_{n+1}, \ldots,\ x_{n+k}$:\ $a_{n+k}$)} +\code{\SUPER(\ArgumentListStd)} respectively \noindent -\code{\SUPER.\id($a_1, \ldots,\ a_n,\ x_{n+1}$:\ $a_{n+1}, \ldots,\ x_{n+k}$:\ $a_{n+k}$)}. +\code{\SUPER.\id(\ArgumentListStd)}. \LMHash{}% \BlindDefineSymbol{C, S, u_j, p}% Let $C$ be the class in which $s$ appears and let $S$ be the superclass of $C$. If $S$ is generic (\ref{generics}), -let $u_1, \ldots, u_p$ be the actual type arguments passed to $S$, +let \List{u}{1}{p} be the actual type arguments passed to $S$, obtained by substituting the actual bindings \List{t}{1}{m} of the formal type parameters of $C$ in the superclass as specified in the header of $C$. @@ -4609,9 +4609,9 @@ \subsubsection{Generative Constructors} \LMHash{}% Execution of $s$ proceeds as follows: The argument list -\code{($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} +\code{(\ArgumentListStd)} is evaluated to an actual argument list $a$ of the form -\code{($o_1, \ldots,\ o_n,\ x_{n+1}$: $o_{n+1}, \ldots,\ x_{n+k}$: $o_{n+k}$)}. +\code{(\ArgumentList{o}{n}{x}{k})}. Then the body of the superconstructor $k$ is executed in an environment where the formal parameters of $k$ are bound to the corresponding actual arguments from $a$, @@ -7240,7 +7240,7 @@ \subsection{The \CALL{} Member of an Extension} and $i$ an expression of the form \noindent -\code{$a$<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$:\ $a_{n+1}, \ldots,\ x_{n+k}$:\ $a_{n+k}$)} +\code{$a$<\TypeArgumentListStd>(\ArgumentListStd)} \noindent (\commentary{where the type argument list is omitted when $r$ is zero}). @@ -7248,7 +7248,7 @@ \subsection{The \CALL{} Member of an Extension} (\ref{notation}) \noindent -\code{$a$.\CALL<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$:\ $a_{n+1}, \ldots,\ x_{n+k}$:\ $a_{n+k}$)}. +\code{$a$.\CALL<\TypeArgumentListStd>(\ArgumentListStd)}. \commentary{% In other words, an invocation of an extension application @@ -7263,7 +7263,7 @@ \subsection{The \CALL{} Member of an Extension} and let $i$ be an expression of the form \noindent -\code{$e$<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$:\ $a_{n+1}, \ldots,\ x_{n+k}$:\ $a_{n+k}$)} +\code{$e$<\TypeArgumentListStd>(\ArgumentListStd)} \noindent (\commentary{where the type argument list is again omitted when $r$ is zero}). @@ -7276,7 +7276,7 @@ \subsection{The \CALL{} Member of an Extension} Otherwise, $i$ is treated as the expression $i'$ which is \noindent -\code{$e$.\CALL<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$:\ $a_{n+1}, \ldots,\ x_{n+k}$:\ $a_{n+k}$)}. +\code{$e$.\CALL<\TypeArgumentListStd>(\ArgumentListStd)}. \commentary{% Note that $i'$ can be an implicit invocation of @@ -9311,9 +9311,15 @@ \subsection{Numbers} is either a hexadecimal integer literal or a decimal integer literal. \LMHash{}% +%% TODO(eernst): Clarify during review: The null safety spec had this: "The +%% implicit conversion of integer literals to double literals is performed +%% when the context type is \code{double} or \code{double?}", which is +%% included with the rules below. However, using that rule directly would +%% rule out `FutureOr x = 1;`, which is allowed by the rule below, and +%% by current implementations. Let $l$ be an integer literal that is not the operand -of by a unary minus operator, -and let $T$ be the static context type of $l$. +of a unary minus operator, +and let $T$ be the context type of $l$. If \code{double} is assignable to $T$ and \code{int} is not assignable to $T$, then the static type of $l$ is \code{double}; otherwise the static type of $l$ is \code{int}. @@ -9337,12 +9343,14 @@ \subsection{Numbers} and $l$ is not the operand of a unary minus operator, then evaluation of $l$ proceeds as follows: \begin{itemize} - \item{} If $l$ is a hexadecimal integer literal, +\item + If $l$ is a hexadecimal integer literal, $2^{63} \le i < 2^{64}$ and the \code{int} class is implemented as signed 64-bit two's complement integers, then $l$ evaluates to an instance of the \code{int} class representing the numeric value $i - 2^{64}$, - \item{} Otherwise $l$ evaluates to an instance of the \code{int} class +\item + Otherwise $l$ evaluates to an instance of the \code{int} class representing the numeric value $i$. It is a \Error{compile-time error} if the integer $i$ cannot be represented exactly by an instance of \code{int}. @@ -9355,7 +9363,8 @@ \subsection{Numbers} For example, Dart compiled to JavaScript may use the JavaScript number type, equivalent to Dart \code{double}, to represent integers, and if so, integer literals with more than 53 bits of precision cannot be represented -exactly.% +exactly. +See~\ref{integerImplementations} for more details.% } \LMHash{}% @@ -12351,16 +12360,16 @@ \subsection{Instance Creation} the type $T$ in an instance creation expression of one of the forms \noindent -\code{\NEW{} $T$.\id($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}, +\code{\NEW{} $T$.\id(\ArgumentListStd)}, \noindent -\code{\NEW{} $T$($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}, +\code{\NEW{} $T$(\ArgumentListStd)}, \noindent -\code{\CONST{} $T$.\id($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}, +\code{\CONST{} $T$.\id(\ArgumentListStd)}, \noindent -\code{\CONST{} $T$($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} +\code{\CONST{} $T$(\ArgumentListStd)} \noindent is an enumerated type (\ref{enums}). @@ -12380,11 +12389,11 @@ \subsubsection{New} Let $e$ be a new expression of the form \noindent -\code{\NEW{} $T$.\id($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} +\code{\NEW{} $T$.\id(\ArgumentListStd)} or the form \noindent -\code{\NEW{} $T$($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}. +\code{\NEW{} $T$(\ArgumentListStd)}. \LMHash{}% It is a \Error{compile-time error} if $T$ is not @@ -12407,12 +12416,12 @@ \subsubsection{New} \begin{itemize} \item If $e$ is of the form - \code{\NEW{} $T$.\id($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} + \code{\NEW{} $T$.\id(\ArgumentListStd)} it is a \Error{compile-time error} if \code{$R$.\id} is not the name of a constructor declared by $R$, or \id{} is not accessible. \item If $e$ is of the form - \code{\NEW{} $T$($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} + \code{\NEW{} $T$(\ArgumentListStd)} it is a \Error{compile-time error} if $R$ is not the name of a constructor declared by $R$. \end{itemize} @@ -12471,13 +12480,13 @@ \subsubsection{New} First, the argument part \noindent -\code{<$U_1, \ldots,\ U_m$>($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} +\code{<\List{U}{1}{m}>(\ArgumentListStd)} \noindent is evaluated, yielding the evaluated actual argument part \noindent -\code{<$u_1, \ldots,\ u_m$>($o_1, \ldots,\ o_n,\ x_{n+1}$: $o_{n+1},\ \ldots,\ x_{n+k}$: $o_{n+k}$)}. +\code{<\List{u}{1}{m}>(\ArgumentList{o}{n}{x}{k})}. \noindent \commentary{Note that the non-generic case is covered by letting $m = 0$.} @@ -12529,7 +12538,7 @@ \subsubsection{New} where \code{\CONST?}\ indicates that \CONST{} may be present or absent, the remaining evaluation of $e$ is equivalent to evaluating -\code{\NEW{} $c$($v_1, \ldots,\ v_n,\ x_{n+1}$: $v_{n+1}, \ldots,\ x_{n+k}$: $v_{n+k}$)} +\code{\NEW{} $c$(\ArgumentList{v}{n}{x}{k})} in an environment where $v_j$ is a fresh variable bound to $o_j$ for $j \in 1 .. n + k$, and $X_j$ is bound to $u_j$ for $j \in 1 .. m$. @@ -12576,11 +12585,11 @@ \subsubsection{Const} Let $e$ be a constant object expression of the form \noindent -\code{\CONST{} $T$.\id($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} +\code{\CONST{} $T$.\id(\ArgumentListStd)} or the form \noindent -\code{\CONST{} $T$($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}. +\code{\CONST{} $T$(\ArgumentListStd)}. \LMHash{}% It is a \Error{compile-time error} if $T$ is not @@ -12614,12 +12623,12 @@ \subsubsection{Const} \begin{itemize} \item If $e$ is of the form - \code{\CONST{} $T$.\id($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} + \code{\CONST{} $T$.\id(\ArgumentListStd)} it is a \Error{compile-time error} if \code{$R$.\id} is not the name of a constant constructor declared by $R$, or \id{} is not accessible. \item If $e$ is of the form - \code{\CONST{} $T$($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} + \code{\CONST{} $T$(\ArgumentListStd)} it is a \Error{compile-time error} if $R$ is not the name of a constant constructor declared by $R$. \end{itemize} @@ -12667,11 +12676,11 @@ \subsubsection{Const} \LMHash{}% If $e$ is of the form -\code{\CONST{} $T$.\id($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} +\code{\CONST{} $T$.\id(\ArgumentListStd)} let $i$ be the value of the expression $e'$: \noindent -\code{\NEW{} $T$.\id($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}. +\code{\NEW{} $T$.\id(\ArgumentListStd)}. \commentary{% Let $o$ be the result of an evaluation of $e'$, @@ -12686,9 +12695,9 @@ \subsubsection{Const} \LMHash{}% If $e$ is of the form -\code{\CONST{} $T$($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}, +\code{\CONST{} $T$(\ArgumentListStd)}, let $i$ be the value of -\code{\NEW{} $T$($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}. +\code{\NEW{} $T$(\ArgumentListStd)}. \commentary{% Which is well-defined for the same reason.% } @@ -13149,7 +13158,7 @@ \subsubsection{Actual Argument List Evaluation} Evaluation of an actual argument part of the form \noindent -\code{<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_m,\ q_1$: $a_{m+1}, \ldots,\ q_l$: $a_{m+l}$)} +\code{<\TypeArgumentListStd>(\ArgumentList{a}{m}{q}{l})} proceeds as follows: \LMHash{}% @@ -13182,7 +13191,7 @@ \subsubsection{Binding Actuals to Formals} \LMHash{}% Consider an invocation $i$ of a function $f$ with an actual argument part of the form -\code{<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_m,\ q_1$: $a_{m+1}, \ldots,\ q_l$: $a_{m+l}$)}. +\code{<\TypeArgumentListStd>(\ArgumentList{a}{m}{q}{l})}. \LMHash{}% % We cannot pass the same named parameter twice. @@ -13391,13 +13400,13 @@ \subsubsection{Binding Actuals to Formals} An evaluated actual argument part \noindent -\code{<$t_1, \ldots,\ t_r$>($o_1, \ldots,\ o_m,\ q_1$: $o_{m+1},\ \ldots,\ q_l$: $o_{m+l}$)} +\code{<\List{t}{1}{r}>(\ArgumentList{o}{m}{q}{l})} \noindent derived from an actual argument part of the form \noindent -\code{<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_m,\ q_1$: $a_{m+1}, \ldots,\ q_l$: $a_{m+l}$)} +\code{<\TypeArgumentListStd>(\ArgumentList{a}{m}{q}{l})} \noindent is bound to the formal type parameters and formal parameters of $f$ as follows: @@ -13424,14 +13433,14 @@ \subsubsection{Binding Actuals to Formals} If $m < h$, or $m > n$, a \code{NoSuchMethodError} is thrown. % When l>0, h=n and there are k named parameters p_{h+1}..p_{h+k}. Furthermore, each -$q_i, i \in 1 .. l$, +$q_i, i \in m + 1 .. m + l$, must have a corresponding named parameter in the set $\{p_{h+1}, \ldots, p_{h+k}\}$, or a \code{NoSuchMethodError} is thrown. % Finally, bindings! Then $p_i$ is bound to $o_i, i \in 1 .. m$, -and $q_j$ is bound to $o_{m+j}, j \in 1 .. l$. +and $q_j$ is bound to $o_j, j \in m + 1 .. m + l$. All remaining formal parameters of $f$ are bound to their default values. \commentary{% @@ -13453,10 +13462,10 @@ \subsubsection{Binding Actuals to Formals} of $p_i$ is not a supertype of the dynamic type of $o_i, i \in 1 .. m$. % Check the types of named arguments. % This error can occur due to implicit casts, covariance, and dynamic calls. -It is a dynamic type error if $o_{m+j}$ is +It is a dynamic type error if $o_j$ is not the null object and the actual type (\ref{actualTypes}) -of $q_j$ is not a supertype of the dynamic type of $o_{m+j}, j \in 1 .. l$. +of $q_j$ is not a supertype of the dynamic type of $o_j, j \in m + 1 .. m + l$. \subsubsection{Unqualified Invocation} @@ -13466,7 +13475,7 @@ \subsubsection{Unqualified Invocation} An unqualified function invocation $i$ has the form \noindent -\code{\id<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}, +\code{\id<\TypeArgumentListStd>(\ArgumentListStd)}, \noindent where \id{} is an identifier. @@ -13582,7 +13591,7 @@ \subsubsection{Function Expression Invocation} A function expression invocation $i$ has the form \noindent -\code{$e_f$<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}, +\code{$e_f$<\TypeArgumentListStd>(\ArgumentListStd)}, \noindent where $e_f$ is an expression. @@ -13655,7 +13664,7 @@ \subsubsection{Function Expression Invocation} the ordinary invocation \noindent -\code{$e_f$.\CALL<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}. +\code{$e_f$.\CALL<\TypeArgumentListStd>(\ArgumentListStd)}. \LMHash{}% Otherwise, the static analysis of $i$ is performed as specified @@ -13667,7 +13676,7 @@ \subsubsection{Function Expression Invocation} Evaluation of a function expression invocation \noindent -\code{$e_f$<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} +\code{$e_f$<\TypeArgumentListStd>(\ArgumentListStd)} \noindent proceeds to evaluate $e_f$, yielding an object $o$. @@ -13675,7 +13684,7 @@ \subsubsection{Function Expression Invocation} If $o$ is a function object then the function invocation \noindent -\code{$f$<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} +\code{$f$<\TypeArgumentListStd>(\ArgumentListStd)} \noindent is evaluated by binding actuals to formals @@ -13690,7 +13699,7 @@ \subsubsection{Function Expression Invocation} and its result is then the result of evaluating $i$: \noindent -\code{$f$.call<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}. +\code{$f$.\CALL<\TypeArgumentListStd>(\ArgumentListStd)}. \LMHash{}% Otherwise $o$ has no method named \CALL. @@ -13707,12 +13716,12 @@ \subsubsection{Function Expression Invocation} whose dynamic type implements \code{Map}, with the keys and values resulting from evaluation of - \code{\{$\#x_{n+1}$: $a_{n+1}, \ldots,\ \#x_{n+k}$: $a_{n+k}$\}}. + \code{\{\NamedArgumentList{\#x}{a}{n+1}{n+k}\}}. \item \code{$im$.typeArguments} evaluates to an unmodifiable list whose dynamic type implements \code{List}, with the values resulting from evaluation of - \code{[$A_1, \ldots,\ A_r$]}. + \code{[\TypeArgumentListStd]}. \end{itemize} \LMHash{}% @@ -14403,11 +14412,11 @@ \subsubsection{Ordinary Invocation} Consider a \IndexCustom{conditional ordinary method invocation}{% method invocation!conditional ordinary} +\BlindDefineSymbol{i,e,m,A_j,r,a_j,n,x_j,k}% $i$ of the form \noindent -\code{% -$e$?.$m$<\List{A}{1}{r}>(\List{a}{1}{n},\ \PairList{x}{\!\!:\,\,a}{n+1}{n+k})}. +\code{$e$?.$m$<\TypeArgumentListStd>(\ArgumentListStd)}. \commentary{% Note that non-generic invocations arise as the special case where @@ -14423,7 +14432,7 @@ \subsubsection{Ordinary Invocation} \noindent \code{% -$e$.$m$<\List{A}{1}{r}>(\List{a}{1}{n},\ \PairList{x}{\!\!:\,\,a}{n+1}{n+k})}. +$e$.$m$<\TypeArgumentListStd>(\ArgumentListStd)}. \LMHash{}% Let $S$ be the static type of $e$. @@ -14437,7 +14446,7 @@ \subsubsection{Ordinary Invocation} \noindent \code{% -$v$.$m$<\List{A}{1}{r}>(\List{a}{1}{n},\ \PairList{x}{\!\!:\,\,a}{n+1}{n+k})}. +$v$.$m$<\TypeArgumentListStd>(\ArgumentListStd)}. \noindent Let $T$ be the static type of $i'$. @@ -14454,7 +14463,9 @@ \subsubsection{Ordinary Invocation} \Case{\code{$C$.$m$<$\cdots$>($\cdots$)}} A \IndexCustom{static member invocation}{member invocation!static} $i$ is an invocation of the form -\code{$C$.$m$<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$:\ $a_{n+1}, \ldots,\ x_{n+k}$:\ $a_{n+k}$)}, +\BlindDefineSymbol{i,C,m,A_j,r,a_j,n,x_j,k}% +\code{% +$C$.$m$<\TypeArgumentListStd>(\ArgumentListStd)}, where $C$ is a term derived from \synt{typeName} that denotes a type or an extension. @@ -14477,6 +14488,7 @@ \subsubsection{Ordinary Invocation} which we will call the \IndexCustom{denoted member}{static member invocation!denoted member} of $i$. +\BlindDefineSymbol{F}% When the denoted member is a static method, let $F$ be its function type; when the denoted member is a static getter, let $F$ be its return type; when the denoted member is neither, a \Error{compile-time error} occurs. @@ -14488,10 +14500,12 @@ \subsubsection{Ordinary Invocation} The static type of $i$ is as specified there. \LMHash{}% +\BlindDefineSymbol{i,C,m,A_j,r,a_j,n,x_j,k}% Evaluation of a static method invocation $i$ of the form \noindent -\code{$C$.$m$<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} +\code{% +$C$.$m$<\TypeArgumentListStd>(\ArgumentListStd)} \noindent proceeds as follows: @@ -14510,15 +14524,18 @@ \subsubsection{Ordinary Invocation} invoke said getter and let $v_f$ be a fresh variable bound to the returned object. Then the value of $i$ is the value of -\code{$v_f$<$A_1, \ldots,\ A_r$>($a_1,\ \ldots,\ a_n,\ x_{n+1}$: $a_{n+1},\ \ldots,\ x_{n+k}$: $a_{n+k}$)}. +\code{% +$v_f$<\TypeArgumentListStd>(\ArgumentListStd)}. \EndCase \LMHash{}% \Case{\code{$e$.$m$<$\cdots$>($\cdots$)}} An \IndexCustom{unconditional ordinary method invocation}{% method invocation!unconditional ordinary} -$i$ has the form -\code{$e$.$m$<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}, +\BlindDefineSymbol{i,e,m,A_j,r,a_j,n,x_j,k}% +$i$ is of the form +\code{% +$e$.$m$<\TypeArgumentListStd>(\ArgumentListStd)}, where $e$ is an expression that is not a type literal, and does not denote an extension. @@ -14528,7 +14545,7 @@ \subsubsection{Ordinary Invocation} } \LMHash{}% -Let $T$ be the static type of $e$. +Let \DefineSymbol{T} be the static type of $e$. \LMHash{}% If $T$ is \DYNAMIC{} bounded @@ -14602,6 +14619,7 @@ \subsubsection{Ordinary Invocation} } \LMHash{}% +\BlindDefineSymbol{L,d,F}% Otherwise, \code{$T$.$m$} denotes an instance member. Let $L$ be the library that contains $i$. Let $d$ be the result of method lookup for $m$ in $T$ with respect to $L$, @@ -14621,7 +14639,7 @@ \subsubsection{Ordinary Invocation} the ordinary invocation \noindent -\code{$e$.$m$.call<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}, +\code{$e$.$m$.\CALL<\TypeArgumentListStd>(\ArgumentListStd)}, \noindent which determines any further static analysis. @@ -14691,8 +14709,8 @@ \subsubsection{Ordinary Invocation} \end{itemize} \LMHash{}% -It is a \Error{compile-time error} to invoke an instance method on a type literal -that is immediately followed by the token `.' (a period). +It is a \Error{compile-time error} to invoke an instance method on +a type literal that is immediately followed by the token `.' (a period). \commentary{% For instance, \code{int.toString()} is an error.% } @@ -14729,10 +14747,13 @@ \subsubsection{Ordinary Invocation} \LMHash{}% Evaluation of an unconditional ordinary method invocation $i$ of the form -\code{$e$.$m$<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} +\BlindDefineSymbol{i,e,m,A_j,r,a_j,n,x_j,k}% +\code{% +$e$.$m$<\TypeArgumentListStd>(\ArgumentListStd)} proceeds as follows: \LMHash{}% +\BlindDefineSymbol{o,f}% First, the expression $e$ is evaluated to an object $o$. Let $f$ be the result of looking up (\ref{lookup}) @@ -14759,9 +14780,11 @@ \subsubsection{Ordinary Invocation} Then the value of $i$ is the value of \noindent -\code{$v_g$<$A_1, \ldots,\ A_r$>($a_1,\ \ldots,\ a_n,\ x_{n+1}$: $a_{n+1},\ \ldots,\ x_{n+k}$: $a_{n+k}$)}. +\code{% +$v_g$<\TypeArgumentListStd>(\ArgumentListStd)}. \LMHash{}% +\BlindDefineSymbol{im}% If getter lookup has also failed, then a new instance $im$ of the predefined class \code{Invocation} is created, such that: @@ -14776,11 +14799,12 @@ \subsubsection{Ordinary Invocation} whose dynamic type implements \code{Map}, with the keys and values resulting from the evaluation of - \code{\{$\#x_{n+1}$: $a_{n+1}, \ldots,\ \#x_{n+k}$: $a_{n+k}$\}}. + \code{% + \{\NamedArgumentList{\#x}{a}{n+1}{n+k}\}}. \item \code{$im$.typeArguments} evaluates to an unmodifiable list whose dynamic type implements \code{List}, containing the objects resulting from the evaluation of - \code{[$A_1, \ldots,\ A_r$]}. + \code{[\TypeArgumentListStd]}. \end{itemize} \LMHash{}% @@ -14933,7 +14957,7 @@ \subsubsection{Superinvocations} \BlindDefineSymbol{i, m, A_j, a_j, x_j}% \noindent -\code{\SUPER.$m$<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}. +\code{\SUPER.$m$<\TypeArgumentListStd>(\ArgumentListStd)}. \commentary{% Note that non-generic invocations arise as @@ -14992,13 +15016,13 @@ \subsubsection{Superinvocations} has the form \noindent -\code{\SUPER<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}, +\code{\SUPER<\TypeArgumentListStd>(\ArgumentListStd)}, \noindent and it is treated as \noindent -\code{\SUPER.\CALL<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}. +\code{\SUPER.\CALL<\TypeArgumentListStd>(\ArgumentListStd)}. \commentary{% The type argument list is again omitted when $r = 0$.% @@ -15034,7 +15058,7 @@ \subsubsection{Superinvocations} \LMHash{}% Otherwise perform the binding of actual arguments to formal parameters for -\code{$f$<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} +\code{$f$<\TypeArgumentListStd>(\ArgumentListStd)} as specified in Section~\ref{bindingActualsToFormals}, and execute the body of $f$ with said bindings plus a binding of \THIS{} to $o$. @@ -15086,6 +15110,7 @@ \subsection{Property Extraction} \Case{Conditional} Consider a \IndexCustom{conditional property extraction expression}{% property extraction!conditional} +\BlindDefineSymbol{i,e,\id}% $i$ of the form \code{$e$?.\id}. %% TODO(eernst): We are likely to decide that this is an error. @@ -15112,6 +15137,7 @@ \subsection{Property Extraction} \LMHash{}% \Case{Static} +\BlindDefineSymbol{i,C,\id}% Let \id{} be an identifier; a \IndexCustom{static property extraction}{% property extraction!static} @@ -15166,8 +15192,8 @@ \subsection{Property Extraction} \Case{Implicit} Let $e$ be an expression whose static type is an interface type that has a method named \CALL. -In the case where the context type for $e$ is a function type -or the type \FUNCTION, +Let $S$ be the context type for $e$. +In the case where \NonNullType{S} is a function type or the type \FUNCTION, $e$ is treated as \code{$e$.\CALL}. \commentary{% @@ -15242,8 +15268,8 @@ \subsubsection{Getter Invocation and Method Closurization} if $T$ has an accessible instance getter named \id. \item The function type of the method signature \code{$T$.\id}, if $T$ has an accessible instance method named \id. -\item \FUNCTION{} if $T$ is \FUNCTION{} bounded and \id{} is \CALL. -\item The type \DYNAMIC{} otherwise. +\item \FUNCTION, if $T$ is \FUNCTION{} bounded and \id{} is \CALL. +\item The type \DYNAMIC, otherwise. \commentary{This only occurs when $T$ is \DYNAMIC{} bounded.} \end{itemize} @@ -15408,7 +15434,7 @@ \subsubsection{Instance Method Closurization} \begin{normativeDartCode} <\TypeParameters{X}{B'}{s}> ($\PairList{T}{p}{1}{n},\ $\{$T_{n+1}\ p_{n+1} = d_1, \ldots,\ T_{n+k}\ p_{n+k} = d_k$\}) => -\quad$u$.$m$<\List{X}{1}{s}>($\List{p}{1}{n},\ p_{n+1}$: $p_{n+1}, \ldots,\ p_{n+k}$: $p_{n+k}$); +\quad$u$.$m$<\List{X}{1}{s}>(\ArgumentList{p}{n}{p}{k}); \end{normativeDartCode} where $f$ is an instance method named $m$ which has type parameter declarations @@ -15564,7 +15590,7 @@ \subsubsection{Super Closurization} \begin{normativeDartCode} <\TypeParameters{X}{B'}{s}> ($\PairList{T}{p}{1}{n},\ $\{$T_{n+1}\ p_{n+1} = d_1, \ldots,\ T_{n+k}\ p_{n+k} = d_k$\}) => -\quad\SUPER$.m$<\List{X}{1}{s}>($\List{p}{1}{n},\ p_{n+1}$: $p_{n+1}, \ldots,\ p_{n+k}$: $p_{n+k}$); +\quad\SUPER$.m$<\List{X}{1}{s}>(\ArgumentList{p}{n}{p}{k}); \end{normativeDartCode} where $f$ is an instance method named $m$ which has type parameter declarations @@ -15852,7 +15878,7 @@ \subsubsection{Generic Method Instantiation} \noindent in which case \metavar{arguments} would be -\code{\List{p}{1}{n},\ $p_{n+1}$:\ $p_{n+1}$,\ $p_{n+k}$:\ $p_{n+k}$}.% +\code{\ArgumentList{p}{n}{p}{k}}.% } \LMHash{}% From a70cae777584b4eec362f7b78b9d83206a8d4c42 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Tue, 16 Nov 2021 14:48:44 +0100 Subject: [PATCH 17/85] .call insertion on nullable type; clean up BlindDefineSymbol --- specification/dartLangSpec.tex | 62 ++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index fa5b34c96..20c48c1dc 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -5634,7 +5634,7 @@ \subsection{Combined Member Signatures} the combined member signature is $m'$. \LMHash{}% -\BlindDefineSymbol{m_j,q} +\BlindDefineSymbol{m_j, q}% Otherwise, $M_0$ contains more than one member signature named $n$. Let \List{m}{1}{q} be those member signatures. @@ -7768,7 +7768,7 @@ \subsection{Super-Bounded Types} } \LMHash{}% -\BlindDefineSymbol{T,T_j,s}% +\BlindDefineSymbol{T, T_j, s}% Let $T$ be a parameterized type of the form \code{$G$<\List{T}{1}{s}>} where $G$ denotes a generic class or a generic type alias. $T$ is \Index{super-bounded} if{}f the following conditions are both true: @@ -8180,7 +8180,7 @@ \subsubsection{The Instantiation to Bound Algorithm} We now specify how the \Index{instantiation to bound} algorithm proceeds. -\BlindDefineSymbol{T,X_j,k,B_j,S_j}% +\BlindDefineSymbol{T, X_j, k, B_j, S_j}% Let $T$ be a raw type. Let \List{X}{1}{k} be the formal type parameters in the declaration of $G$, and let \List{B}{1}{k} be their bounds. @@ -10539,7 +10539,7 @@ \subsubsection{List Literal Inference} the element type of \metavar{list} as a whole. \LMHash{}% -\BlindDefineSymbol{P, P_e} +\BlindDefineSymbol{P, P_e}% The context type $P$ (\ref{setAndMapLiteralDisambiguation}) for each element of \metavar{list} is @@ -10668,7 +10668,7 @@ \subsubsection{List Literal Inference} \IndexCustom{type inference on a list literal}{% type inference!list literal} as a whole. -\BlindDefineSymbol{\metavar{list},\ell_j,P}% +\BlindDefineSymbol{\metavar{list}, \ell_j, P}% Assume that \metavar{list} is derived from \synt{listLiteral} and contains the elements \List{\ell}{1}{n}, and the context type for \metavar{list} is $P$. @@ -10903,7 +10903,7 @@ \subsubsection{Set and Map Literal Disambiguation} (\ref{setAndMapLiteralInference}). \LMHash{}% -\BlindDefineSymbol{e,{\cal L},C}% +\BlindDefineSymbol{e, {\cal L}, C}% Let $e$ be a \synt{setOrMapLiteral} with leaf elements $\cal L$ and context type $C$. If $C$ is \FreeContext{} then let $S$ be undefined. @@ -11038,7 +11038,7 @@ \subsubsection{Set and Map Literal Inference} \end{dartCode} \LMHash{}% -\BlindDefineSymbol{\metavar{collection},P}% +\BlindDefineSymbol{\metavar{collection}, P}% Let \metavar{collection} be a collection literal derived from \synt{setOrMapLiteral}. The inferred type of an \synt{element} is an element type $T$, @@ -11079,7 +11079,7 @@ \subsubsection{Set and Map Literal Inference} provides a partial constraint on the inferred type.% } \item - \BlindDefineSymbol{P_k,P_v}% + \BlindDefineSymbol{P_k, P_v}% If \metavar{collection} is unambiguously a map then $P$ is \code{Map<$P_k$,\,\,$P_v$>} where $P_k$ and $P_v$ are determined by downwards inference, @@ -11270,7 +11270,7 @@ \subsubsection{Set and Map Literal Inference} \LMHash{}% \Case{If element} -\BlindDefineSymbol{b,\ell_1,\ell_2}% +\BlindDefineSymbol{b, \ell_1, \ell_2}% In this case $\ell$ is of the form \code{\IF\,\,($b$)\,\,$\ell_1$} or \code{\IF\,\,($b$)\,\,$\ell_1$\,\,\ELSE\,\,$\ell_2$}. @@ -11322,7 +11322,7 @@ \subsubsection{Set and Map Literal Inference} \LMHash{}% \Case{For element} -\BlindDefineSymbol{p,\ell_1}% +\BlindDefineSymbol{p, \ell_1}% In this case $\ell$ is of the form \code{\AWAIT?\,\,\FOR\,\,($p$)\,\,$\ell_1$} where $p$ is derived from \synt{forLoopParts} and @@ -11371,7 +11371,7 @@ \subsubsection{Set and Map Literal Inference} \IndexCustom{type inference on a set or map literal}{% type inference!set or map literal} as a whole. -\BlindDefineSymbol{\metavar{collection},P}% +\BlindDefineSymbol{\metavar{collection}, P}% Assume that \metavar{collection} is derived from \synt{setOrMapLiteral}, and the context type for \metavar{collection} is $P$. @@ -13862,9 +13862,9 @@ \subsection{Generic Function Instantiation} } \LMHash{}% -\BlindDefineSymbol{f,G} +\BlindDefineSymbol{f, G} Let $f$ be an expression whose static type $G$ is -\BlindDefineSymbol{T_0,X_j,B_j,s,p}% +\BlindDefineSymbol{T_0, X_j, B_j, s, p}% a generic function type of the form \RawFunctionType{T_0}{X}{B}{s}{\metavar{p}} where \code{($p$)} is derived from \synt{formalParameterList}. @@ -14412,7 +14412,7 @@ \subsubsection{Ordinary Invocation} Consider a \IndexCustom{conditional ordinary method invocation}{% method invocation!conditional ordinary} -\BlindDefineSymbol{i,e,m,A_j,r,a_j,n,x_j,k}% +\BlindDefineSymbol{i, e, m, A_j, r, a_j, n, x_j, k}% $i$ of the form \noindent @@ -14463,7 +14463,7 @@ \subsubsection{Ordinary Invocation} \Case{\code{$C$.$m$<$\cdots$>($\cdots$)}} A \IndexCustom{static member invocation}{member invocation!static} $i$ is an invocation of the form -\BlindDefineSymbol{i,C,m,A_j,r,a_j,n,x_j,k}% +\BlindDefineSymbol{i, C, m, A_j, r, a_j, n, x_j, k}% \code{% $C$.$m$<\TypeArgumentListStd>(\ArgumentListStd)}, where $C$ is a term derived from \synt{typeName} @@ -14500,7 +14500,7 @@ \subsubsection{Ordinary Invocation} The static type of $i$ is as specified there. \LMHash{}% -\BlindDefineSymbol{i,C,m,A_j,r,a_j,n,x_j,k}% +\BlindDefineSymbol{i, C, m, A_j, r, a_j, n, x_j, k}% Evaluation of a static method invocation $i$ of the form \noindent @@ -14532,7 +14532,7 @@ \subsubsection{Ordinary Invocation} \Case{\code{$e$.$m$<$\cdots$>($\cdots$)}} An \IndexCustom{unconditional ordinary method invocation}{% method invocation!unconditional ordinary} -\BlindDefineSymbol{i,e,m,A_j,r,a_j,n,x_j,k}% +\BlindDefineSymbol{i, e, m, A_j, r, a_j, n, x_j, k}% $i$ is of the form \code{% $e$.$m$<\TypeArgumentListStd>(\ArgumentListStd)}, @@ -14619,7 +14619,7 @@ \subsubsection{Ordinary Invocation} } \LMHash{}% -\BlindDefineSymbol{L,d,F}% +\BlindDefineSymbol{L, d, F}% Otherwise, \code{$T$.$m$} denotes an instance member. Let $L$ be the library that contains $i$. Let $d$ be the result of method lookup for $m$ in $T$ with respect to $L$, @@ -14747,13 +14747,13 @@ \subsubsection{Ordinary Invocation} \LMHash{}% Evaluation of an unconditional ordinary method invocation $i$ of the form -\BlindDefineSymbol{i,e,m,A_j,r,a_j,n,x_j,k}% +\BlindDefineSymbol{i, e, m, A_j, r, a_j, n, x_j, k}% \code{% $e$.$m$<\TypeArgumentListStd>(\ArgumentListStd)} proceeds as follows: \LMHash{}% -\BlindDefineSymbol{o,f}% +\BlindDefineSymbol{o, f}% First, the expression $e$ is evaluated to an object $o$. Let $f$ be the result of looking up (\ref{lookup}) @@ -15110,7 +15110,7 @@ \subsection{Property Extraction} \Case{Conditional} Consider a \IndexCustom{conditional property extraction expression}{% property extraction!conditional} -\BlindDefineSymbol{i,e,\id}% +\BlindDefineSymbol{i, e, \id}% $i$ of the form \code{$e$?.\id}. %% TODO(eernst): We are likely to decide that this is an error. @@ -15137,7 +15137,7 @@ \subsection{Property Extraction} \LMHash{}% \Case{Static} -\BlindDefineSymbol{i,C,\id}% +\BlindDefineSymbol{i, C, \id}% Let \id{} be an identifier; a \IndexCustom{static property extraction}{% property extraction!static} @@ -15202,7 +15202,9 @@ \subsection{Property Extraction} (\ref{functionClosurization}) by desugaring it to a method closurization on \CALL. This only occurs when it is statically known that it is a callable object, -and when the context type requires a function.% +and when the context type requires a function. +Note that the static type of $e$ can not be potentially nullable, +because no such types have a method named \CALL.% } \EndCase @@ -17607,7 +17609,7 @@ \subsection{Lexical Lookup} } \LMHash{}% -\BlindDefineSymbol{n, id}% +\BlindDefineSymbol{n, \id}% Consider the situation where a name $n$ has basename \id{} (\ref{classMemberConflicts}) @@ -19640,7 +19642,7 @@ \subsection{Return} to be \FutureValueTypeOf{$T$}. \LMHash{}% -\BlindDefineSymbol{s,e,S,f,T}% +\BlindDefineSymbol{s, e, S, f, T}% Consider a return statement $s$ of the form \code{\RETURN\,\,$e$?;}. Let $S$ be the static type of $e$, if $e$ is present, let $f$ be the immediately enclosing function, @@ -19776,7 +19778,7 @@ \subsection{Return} \LMHash{}% \Case{Synchronous non-generator functions and factory constructors} Let $s$ be a statement of the form \code{\RETURN\,\,$e$;}. -\BlindDefineSymbol{s,e,f}% +\BlindDefineSymbol{s, e, f}% Let $f$ be the immediately enclosing function, and consider the case where $f$ is a synchronous non-generator or a factory constructor. @@ -19798,7 +19800,7 @@ \subsection{Return} \LMHash{}% \Case{Asynchronous non-generator functions} Let $s$ be a statement of the form \code{\RETURN\,\,$e$;}. -\BlindDefineSymbol{s,e,f,T_v}% +\BlindDefineSymbol{s, e, f, T_v}% Let $f$ be the immediately enclosing function, and consider the case where $f$ is an asynchronous non-generator with future value type $T_v$. @@ -23040,7 +23042,7 @@ \subsection{Type Normalization} \LMHash{}% The function \Index{\NormalizedTypeOfName} is then defined as follows: -\BlindDefineSymbol{T_a,T_u,T_r}% +\BlindDefineSymbol{T_a, T_u, T_r}% Let $T_a$ be a type (\commentary{where `a' stands for `argument'}). Let $T_u$ be the transitive alias expansion of $T_a$ @@ -23175,7 +23177,7 @@ \subsection{Least and Greatest Closure of Types} } \LMHash{}% -\BlindDefineSymbol{S,L,X_j,n}% +\BlindDefineSymbol{S, L, X_j, n}% Let $S$ be a type and $L$ a set of type variables of the form $\{\List{X}{1}{n}\}$. @@ -24972,6 +24974,8 @@ \subsubsection{Implicit conversions} \subsubsection{Constant Objects} +!!!TODO!!! + The definition of potentially constant expressions is extended to include type casts and instance checks on potentially constant types, as follows. From 53d080bac74377fb5cbb3e7d6f387ccd3f0a6f56 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Tue, 16 Nov 2021 21:06:48 +0100 Subject: [PATCH 18/85] Rebase --- specification/dartLangSpec.tex | 173 ++++++++++++++++----------------- 1 file changed, 83 insertions(+), 90 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 20c48c1dc..82c968a44 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -8666,14 +8666,13 @@ \subsection{Constants} \item A literal boolean, \TRUE{} or \FALSE{} (\ref{booleans}), is a potentially constant and constant expression. - \item A literal number (\ref{numbers}) is a potentially constant and constant expression if it evaluates to an instance of type \code{int} or \code{double}. % A too-large integer literal does not evaluate to an object. - -\item A literal string (\ref{strings}) with string interpolations +\item + A literal string (\ref{strings}) with string interpolations (\ref{stringInterpolation}) with expressions $e_1$, \ldots, $e_n$ is a potentially constant expression if $e_1$, \ldots, $e_n$ are potentially constant expressions. @@ -8692,19 +8691,15 @@ \subsection{Constants} for constant objects, which could contain arbitrary code.% } - \item A literal symbol (\ref{symbols}) is a potentially constant and constant expression. - \item The literal \NULL{} (\ref{null}) is a potentially constant and constant expression. - \item An identifier that denotes a constant variable is a potentially constant and constant expression. - \item A qualified reference to a static constant variable (\ref{variables}) @@ -8716,7 +8711,6 @@ \subsection{Constants} The same is true if $C$ is accessed via a prefix $p$; \code{$p$.$C$.$v$} is a constant unless $p$ is a deferred prefix.% } - \item A simple or qualified identifier denoting a class, a mixin or a type alias that is not qualified by a deferred prefix, @@ -8729,7 +8723,6 @@ \subsection{Constants} a constant \code{Type} instance representing the type of $C$ unless $p$ is a deferred prefix.% } - \item Let $e$ be a simple or qualified identifier denoting a top-level function (\ref{functions}) @@ -8743,13 +8736,11 @@ \subsection{Constants} then $e$ is a potentially constant and constant expression if{}f each $T_j, j \in 1 .. s$, is a constant type expression (\ref{constants}). - \item An identifier expression denoting a parameter of a constant constructor (\ref{constantConstructors}) that occurs in the initializer list of the constructor, is a potentially constant expression. - \item A constant object expression (\ref{const}), \code{\CONST{} $C$<$T_1,\ \ldots,\ T_k$>(\metavar{arguments})} or @@ -8763,7 +8754,6 @@ \subsection{Constants} % `C(double x, double y): z = x / y;`. It is a \Error{compile-time error} if a constant object expression is not a constant expression (\ref{const}). - \item A constant list literal (\ref{lists}), \code{\CONST{} <$T$>[$e_1$, \ldots, $e_n$]}, or @@ -8773,7 +8763,6 @@ \subsection{Constants} and $e_1$, \ldots{} , $e_n$ are constant expressions. It is further a constant expression if the list literal evaluates to an object. - \item A constant set literal (\ref{sets}), \code{\CONST{} <$T$>\{$e_1$, \ldots, $e_n$\}}, or @@ -8784,7 +8773,6 @@ \subsection{Constants} and $e_1$, \ldots{} , $e_n$ are constant expressions. It is further a constant expression if the set literal evaluates to an object. - \item A constant map literal (\ref{maps}), \code{\CONST{} <$K$, $V$>\{$k_1$: $v_1$, \ldots, $k_n$: $v_n$\}}, or @@ -8793,13 +8781,11 @@ \subsection{Constants} is a potentially constant expression. It is further a constant expression if the map literal evaluates to an object. - \item A parenthesized expression \code{($e$)} is a potentially constant expression if $e$ is a potentially constant expression. It is further a constant expression if $e$ is a constant expression. - \item An expression of the form \code{identical($e_1$, $e_2$)} is a potentially constant expression @@ -8809,12 +8795,10 @@ \subsection{Constants} (\ref{objectIdentity}). It is further a constant expression if $e_1$ and $e_2$ are constant expressions. - \item An expression of the form \code{$e_1$\,!=\,$e_2$} is equivalent to \code{!($e_1$\,==\,$e_2$)} in every way, including whether it is potentially constant or constant. - \item An expression of the form \code{$e_1$\,==\,$e_2$} is potentially constant if $e_1$ and $e_2$ are both potentially constant expressions. @@ -8824,13 +8808,11 @@ \subsection{Constants} (\ref{theOperatorEqualsEquals}), or $e_2$ evaluates to the null object (\ref{null}). - \item An expression of the form \code{!$e_1$} is potentially constant if $e_1$ is potentially constant. It is further constant if $e_1$ is a constant expression that evaluates to an instance of type \code{bool}. - \item An expression of the form \code{$e_1$\,\&\&\,$e_2$} is potentially constant if $e_1$ and $e_2$ @@ -8841,7 +8823,6 @@ \subsection{Constants} \item $e_1$ evaluates to \TRUE{} and $e_2$ is a constant expression that evaluates to an instance of type \code{bool}. \end{enumerate} - \item An expression of the form \code{$e_1$\,||\,$e_2$} is potentially constant if $e_1$ and $e_2$ @@ -8852,7 +8833,6 @@ \subsection{Constants} \item $e_1$ evaluates to \FALSE{} and $e_2$ is a constant expression that evaluates to an instance of type \code{bool}. \end{enumerate} - \item An expression of the form \code{\gtilde$e_1$} is a potentially constant expression @@ -8860,8 +8840,8 @@ \subsection{Constants} It is further a constant expression if $e_1$ is a constant expression that evaluates to an instance of type \code{int} such that \gtilde{} denotes an instance operator invocation. - -\item An expression of one of the forms \code{$e_1$\,\&\,$e_2$}, +\item + An expression of one of the forms \code{$e_1$\,\&\,$e_2$}, \code{$e_1$\,|\,$e_2$}, or \code{$e_1$\,\^\,$e_2$} is potentially constant if $e_1$ and $e_2$ are both potentially constant expressions. It is further constant if both $e_1$ and $e_2$ are constant expressions that @@ -8870,8 +8850,8 @@ \subsection{Constants} such that the operator symbol \lit{\&}, \lit{|}, respectively \lit{\^} denotes an instance operator invocation. - -\item An expression of one of the forms +\item + An expression of one of the forms \code{$e_1$\,\gtgt\,$e_2$}, \code{$e_1$\,\gtgtgt\,$e_2$}, or \code{$e_1$\,\ltlt\,$e_2$} is potentially constant if $e_1$ and $e_2$ are both potentially constant expressions. @@ -8880,8 +8860,8 @@ \subsection{Constants} such that the operator symbol \lit{\gtgt}, \lit{\gtgtgt}, respectively \lit{\ltlt} denotes an instance operator invocation. - -\item An expression of the form \code{$e_1$\,+\,$e_2$} is +\item + An expression of the form \code{$e_1$\,+\,$e_2$} is a potentially constant expression if $e_1$ and $e_2$ are both potentially constant expressions. It is further a constant expression @@ -8889,15 +8869,14 @@ \subsection{Constants} and either both evaluate to an instance of \code{int} or \code{double}, or both evaluate to an instance of \code{String}, such that \lit{+} denotes an instance operator invocation. - \item An expression of the form \code{-$e_1$} is a potentially constant expression if $e_1$ is a potentially constant expression. It is further a constant expression if $e_1$ is a constant expression that evaluates to an instance of type \code{int} or \code{double}, such that \lit{-} denotes an instance operator invocation. - -\item An expression of the form \code{$e_1$\,-\,$e_2$}, \code{$e_1$\,*\,$e_2$}, +\item + An expression of the form \code{$e_1$\,-\,$e_2$}, \code{$e_1$\,*\,$e_2$}, \code{$e_1$\,/\,$e_2$},\code{$e_1$\,\gtilde/\,$e_2$}, \code{$e_1$\,\%\,$e_2$}, \code{$e_1$\,<\,$e_2$}, \code{$e_1$\,<=\,$e_2$}, \code{$e_1$\,>\,$e_2$}, or \code{$e_1$\,>=\,$e_2$} @@ -8907,8 +8886,8 @@ \subsection{Constants} evaluate to instances of \code{int} or \code{double}, such that the given operator symbol denotes an invocation of an instance operator. - -\item An expression of the form \code{$e_1$\,?\,$e_2$\,:\,$e_3$} +\item + An expression of the form \code{$e_1$\,?\,$e_2$\,:\,$e_3$} is potentially constant if $e_1$, $e_2$, and $e_3$ are all potentially constant expressions. It is constant if $e_1$ is a constant expression and either @@ -8916,37 +8895,35 @@ \subsection{Constants} \item $e_1$ evaluates to \TRUE{} and $e_2$ is a constant expression, or \item $e_1$ evaluates to \FALSE{} and $e_3$ is a constant expression. \end{enumerate} - -\item An expression of the form \code{$e_1$\,??\,$e_2$} is potentially constant +\item + An expression of the form \code{$e_1$\,??\,$e_2$} is potentially constant if $e_1$ and $e_2$ are both potentially constant expressions. It is further constant if $e_1$ is a constant expression and either \begin{enumerate} \item $e_1$ evaluates to an object which is not the null object, or \item $e_1$ evaluates to the null object, and $e_2$ is a constant expression. \end{enumerate} - -\item An expression of the form \code{$e$.length} is potentially constant +\item + An expression of the form \code{$e$.length} is potentially constant if $e$ is a potentially constant expression. It is further constant if $e$ is a constant expression that evaluates to an instance of \code{String}, such that \code{length} denotes an instance getter invocation. - -\item An expression of the form \code{$e$\,\,as\,\,$T$} is potentially constant +\item + An expression of the form \code{$e$\,\,\AS\,\,$T$} is potentially constant if $e$ is a potentially constant expression - and $T$ is a constant type expression, + and $T$ is a potentially constant type expression, and it is further constant if $e$ is constant. \commentary{% - It is a \Error{compile-time error} to evaluate the constant expression + It is a \Error{compile-time error} to evaluate this constant expression if the cast operation would throw, that is, - if $e$ evaluates to an object which is not the null object - and not of type $T$.% + if $e$ evaluates to an object which is not of type $T$.% } - -\item An expression of the form \code{$e$\,\,is\,\,$T$} is potentially constant +\item + An expression of the form \code{$e$\,\,\IS\,\,$T$} is potentially constant if $e$ is a potentially constant expression and $T$ is a constant type expression, and it is further constant if $e$ is constant. - \item An expression of the form \code{$e$\,\,is!\,\,$T$} is equivalent to \code{!($e$\,\,is\,\,$T$)} in every way, @@ -8954,34 +8931,46 @@ \subsection{Constants} \end{itemize} \LMHash{}% -A -\Index{constant type expression} -is one of: +We introduce \Index{constant type expressions} and +\Index{potentially constant type expressions} +as follows: \begin{itemize} \item - An simple or qualified identifier - denoting a type declaration (a type alias, class or mixin declaration) - that is not qualified by a deferred prefix, - optionally followed by type arguments of the form - \code{<$T_1$,\ \ldots,\ $T_n$>} - where $T_1$, \ldots, $T_n$ are constant type expressions. + Consider a type $T$ + which is designated by a simple or qualified identifier + denoting a declaration of a class, mixin, or type alias, + which is not qualified by a deferred prefix, + and which is optionally followed by + type arguments of the form \code{<\List{T}{1}{r}>}. + $T$ is a potentially constant type expression + respectively a constant type expression + if{}f $T_j$ is a potentially constant respectively constant type expression + for each $j \in 1 .. r$. \item A type of the form \code{FutureOr<$T$>} - where $T$ is a constant type expression. + is a potentially constant type expression + respectively a constant type expression + if{}f $T$ is a potentially constant type expression + respectively a constant type expression. +\item + A type variable is a potentially constant type expression. \item %% TODO(eernst): This does not allow for type variables introduced by %% the type itself. `Function(X)` could be a constant type expression, %% but that is not covered by the current rules: `X` is a type variable, %% and they are never allowed. A function type - \code{$R$ Function<\metavar{typeParameters}>(\metavar{argumentTypes})} + \code{$R$ Function<\metavar{typeParameters}>(\metavar{parameterTypes})} (where $R$ and \code{<\metavar{typeParameters}>} may be omitted) - and where $R$, \metavar{typeParameters} and \metavar{argumentTypes} - (if present) contain only constant type expressions. + is a is a potentially constant type expression + respectively a constant type expression + if $R$, \metavar{typeParameters}, and \metavar{parameterTypes} + (if present) contain only potentially constant type expressions + respectively constant type expressions. \item - The type \VOID. + The type \VOID is a potentially constant and a constant type expression. \item - The type \DYNAMIC. + The type \DYNAMIC is a potentially constant and a constant type expression. \end{itemize} % Being potentially constant is entirely structural, not type based, @@ -8993,9 +8982,6 @@ \subsection{Constants} % allow simple operations on basic types (num, String, bool, Null). These can % be computed statically without running user code. -% A validly typed potentially constant expression can still fail when evaluated. -% If that happens in a const invociation, it's a compile-time error. - \LMHash{}% It is a \Error{compile-time error} if an expression is required to be a constant expression, @@ -23666,26 +23652,29 @@ \subsection{Type \FUNCTION} \LMHash{}% If a class or mixin declaration implements \FUNCTION, it has no effect. -It is as if the \FUNCTION{} was removed from the \code{implements} clause -(and if it's the only implemented interface, the entire clause is removed). +It works as if \FUNCTION{} were removed from the \code{implements} clause +(and if it is the only implemented interface, the entire clause is removed). The resulting class or mixin interface does not have \FUNCTION{} as a superinterface. \LMHash{}% If a mixin application mixes \FUNCTION{} onto a superclass, it follows the normal rules for mixin-application, but since the result of that mixin -application is equivalent to a class with \code{implements Function}, and +application is equivalent to a class with \code{implements \FUNCTION}, and that clause has no effect, the resulting class also does not -implement \FUNCTION. \commentary{The \FUNCTION{} class declares no -concrete instance members, so the mixin application creates a sub-class -of the superclass with no new members and no new interfaces.} +implement \FUNCTION. +\commentary{% +The \FUNCTION{} class declares no concrete instance members, +so the mixin application creates a sub-class +of the superclass with no new members and no new interfaces.% +} \rationale{% Since using \FUNCTION{} in these ways has no effect, it would be reasonable to disallow it completely, like we do extending, implementing or mixing in types like \code{int} or \code{String}. -For backwards compatibility with Dart 1 programs, -the syntax is allowed to remain, even if it has no effect. +The syntax is allowed to remain for now for backwards compatibility, +even though it has no effect. Tools may choose to warn users that their code has no effect.% } @@ -25040,21 +25029,23 @@ \subsubsection{Constant Objects} \subsubsection{Null promotion} -The machinery of type promotion is extended to promote the type of variables -based on nullability checks subject to the same set of restrictions as normal -promotion. The relevant checks and the types they are considered to promote to -are as follows. +The machinery of type promotion is extended to promote the type of +variables based on nullability checks subject to the same set of +restrictions as normal promotion. The relevant checks and the types +they are considered to promote to are as follows. -A check of the form \code{$e$ == null} or of the form \code{$e$\,\,\IS\,\,Null} where $e$ has static -type $T$ promotes the type of $e$ to \code{Null} in the \code{true} continuation, and to +A check of the form \code{$e$ == null} or of the form +\code{$e$\,\,\IS\,\,Null} where $e$ has static type $T$ promotes the +type of $e$ to \code{Null} in the \code{true} continuation, and to \NonNullType{$T$} in the \code{false} continuation. -A check of the form \code{$e$ != null} or of the form \code{$e$\,\,\IS\,\,$T$} where $e$ has static -type \code{$T$?}\ promotes the type of $e$ to $T$ in the \code{true} continuation, and to -\code{Null} in the \code{false} continuation. +A check of the form \code{$e$ != null} or of the form +\code{$e$\,\,\IS\,\,$T$} where $e$ has static type +\code{$T$?}\ promotes the type of $e$ to $T$ in the \code{true} +continuation, and to \code{Null} in the \code{false} continuation. -The static type of an expression \code{$e$!} is \NonNullType{$T$} where $T$ is the -static type of $e$. +The static type of an expression \code{$e$!} is \NonNullType{$T$} +where $T$ is the static type of $e$. \paragraph{Extended Type promotion, Definite Assignment, and Reachability} @@ -25063,16 +25054,18 @@ \subsubsection{Null promotion} \subsubsection{The main function} +!!!TODO!!! + The section 'Scripts' in the language specification is replaced by the following: -Let $L$ be a library that exports a declaration $D$ named \code{main}. It is a -\Error{compile-time error} unless $D$ is a non-getter function declaration. It is a -\Error{compile-time error} if $D$ declares more than two required positional -parameters, or if there are any required named parameters. It is a -\Error{compile-time error} if $D$ declares at least one positional parameter, -and the first positional parameter has a type which is not a supertype of -\code{List}. +Let $L$ be a library that exports a declaration $D$ named \code{main}. +It is a \Error{compile-time error} unless $D$ is a non-getter function +declaration. It is a \Error{compile-time error} if $D$ declares more +than two required positional parameters, or if there are any required +named parameters. It is a \Error{compile-time error} if $D$ declares +at least one positional parameter, and the first positional parameter +has a type which is not a supertype of \code{List}. Implementations are free to impose any additional restrictions on the signature of \code{main}. From d539aea8d7cae5f1451dd6795226ac7ed5fb5243 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Tue, 16 Nov 2021 21:47:28 +0100 Subject: [PATCH 19/85] Main --- specification/dartLangSpec.tex | 154 ++++++++++++++++++--------------- 1 file changed, 86 insertions(+), 68 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 82c968a44..94751d0b7 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -8952,13 +8952,11 @@ \subsection{Constants} respectively a constant type expression if{}f $T$ is a potentially constant type expression respectively a constant type expression. -\item - A type variable is a potentially constant type expression. \item %% TODO(eernst): This does not allow for type variables introduced by %% the type itself. `Function(X)` could be a constant type expression, %% but that is not covered by the current rules: `X` is a type variable, - %% and they are never allowed. + %% and they are never constant type expressions. A function type \code{$R$ Function<\metavar{typeParameters}>(\metavar{parameterTypes})} (where $R$ and \code{<\metavar{typeParameters}>} may be omitted) @@ -8968,9 +8966,11 @@ \subsection{Constants} (if present) contain only potentially constant type expressions respectively constant type expressions. \item - The type \VOID is a potentially constant and a constant type expression. + The type \VOID{} is a potentially constant and a constant type expression. +\item + The type \DYNAMIC{} is a potentially constant and a constant type expression. \item - The type \DYNAMIC is a potentially constant and a constant type expression. + A type variable is a potentially constant type expression. \end{itemize} % Being potentially constant is entirely structural, not type based, @@ -21201,6 +21201,76 @@ \subsection{Parts} \subsection{Scripts} \LMLabel{scripts} +\LMHash{}% +Let $L$ be a library that exports a declaration $D$ named \code{main}. +It is a \Error{compile-time error} unless $D$ is +a non-getter function declaration. +It is a \Error{compile-time error} if $D$ declares +more than two required positional parameters, +or if there are any required named parameters. +It is a \Error{compile-time error} if $D$ declares +at least one positional parameter, +and the first positional parameter has a type which is +not a supertype of \code{List}. + +\LMHash{}% +Implementations are free to impose any additional restrictions on +the signature of \code{main}. + +\LMHash{}% +A \Index{script} is a library that exports a declaration named \code{main}. +A script $L$ is executed as follows: + +\LMHash{}% +First, $L$ is compiled as a library as specified above. +Then, the top-level function defined by \code{main} +in the exported namespace of $L$ is invoked as follows: + +\LMHash{}% +If \code{main} can be called with two positional arguments, +it is invoked with the following two actual arguments: + +\begin{itemize} +\item + An object whose run-time type implements \code{List}. +\item + An object specified when the current isolate $i$ was created, + (\commentary{% + for example through the invocation of \code{Isolate.spawnUri} + that spawned $i$% + }), + or the null object if no such object was supplied. + A dynamic error occurs if + the run-time type of this object is not a subtype of + the declared type of the corresponding parameter of \code{main}. +\end{itemize} + +\LMHash{}% +If \code{main} cannot be called with two positional arguments, +but it can be called with one positional argument, +it is invoked with an object whose run-time type implements \code{List} +as the only argument. + +\LMHash{}% +If \code{main} cannot be called with one or two positional arguments, +it is invoked with no arguments. + +\LMHash{}% +In each of the above three cases, +an implementation is free to provide additional arguments +allowed by the signature of \code{main} +(\commentary{% +the above rules ensure that the corresponding parameters are optional% +}). +But the implementation must ensure that a dynamic error occurs +if an actual argument does not have a run-time type which is +a subtype of the declared type of the parameter. + +\LMHash{}% +A Dart program will typically be executed by executing a script. +The procedure whereby this script is chosen is implementation specific. + +---------------------------------------------------------------------- \LMHash{}% A \Index{script} is a library whose exported namespace (\ref{exports}) includes a top-level function declaration named \code{main} @@ -25052,72 +25122,20 @@ \subsubsection{Null promotion} These are extended as per [separate proposal](https://github.com/dart-lang/language/blob/master/resources/type-system/flow-analysis.md). -\subsubsection{The main function} +\subsubsection{Runtime type equality operator} !!!TODO!!! -The section 'Scripts' in the language specification is replaced by the -following: - -Let $L$ be a library that exports a declaration $D$ named \code{main}. -It is a \Error{compile-time error} unless $D$ is a non-getter function -declaration. It is a \Error{compile-time error} if $D$ declares more -than two required positional parameters, or if there are any required -named parameters. It is a \Error{compile-time error} if $D$ declares -at least one positional parameter, and the first positional parameter -has a type which is not a supertype of \code{List}. - -Implementations are free to impose any additional restrictions on the -signature of \code{main}. - -A _script_ is a library that exports a declaration named \code{main}. -A script $L$ is executed as follows: - -First, $L$ is compiled as a library as specified above. -Then, the top-level function defined by \code{main} -in the exported namespace of $L$ is invoked as follows: - -If \code{main} can be called with two positional arguments, -it is invoked with the following two actual arguments: - -\begin{itemize} -\item An object whose run-time type implements \code{List}. -\item An object specified when the current isolate $i$ was created, - for example through the invocation of \code{Isolate.spawnUri} that spawned $i$, - or the null object if no such object was supplied. - A dynamic error occurs if the run-time type of this object is not a - subtype of the declared type of the corresponding parameter of \code{main}. -\end{itemize} - -If \code{main} cannot be called with two positional arguments, but it can be -called with one positional argument, it is invoked with an object whose -run-time type implements \code{List} as the only argument. - -If \code{main} cannot be called with one or two positional arguments, it is -invoked with no arguments. - -In each of the above three cases, an implementation is free to provide -additional arguments allowed by the signature of \code{main} -(\commentary{the above rules ensure that the corresponding parameters are optional}). -But the implementation must ensure that a dynamic error occurs if an -actual argument does not have a run-time type which is a subtype of -the declared type of the parameter. - -A Dart program will typically be executed by executing a script. The -procedure whereby this script is chosen is implementation specific. - -\subsection{Runtime semantics} - -\subsubsection{Runtime type equality operator} - -Two objects $T_1$ and $T_2$ which are instances of \code{Type} (that is, runtime type -objects) are considered equal if and only if the runtime type objects $T_1$ and -$T_2$ corresponds to the types $S_1$ and $S_2$ respectively, and the normal forms -\NormalizedTypeOf{$S_1$} and \NormalizedTypeOf{$S_2$} are syntactically equal up to equivalence of -bound variables. Note that we do not equate primitive top types. \code{List<\VOID>} -and \code{List<\DYNAMIC>} are still considered distinct runtime type objects. Note -that we also do not equate function -types which differ in the placement of \REQUIRED{} on parameter types. +Two objects $T_1$ and $T_2$ which are instances of \code{Type} (that +is, runtime type objects) are considered equal if and only if the +runtime type objects $T_1$ and $T_2$ corresponds to the types $S_1$ +and $S_2$ respectively, and the normal forms \NormalizedTypeOf{$S_1$} +and \NormalizedTypeOf{$S_2$} are syntactically equal up to equivalence +of bound variables. Note that we do not equate primitive top types. +\code{List<\VOID>} and \code{List<\DYNAMIC>} are still considered +distinct runtime type objects. Note that we also do not equate +function types which differ in the placement of \REQUIRED{} on +parameter types. \subsubsection{Constant evaluation and canonicalization} From 1f120dc459121db59b3959cfd4730a929f6ec0a7 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Wed, 17 Nov 2021 12:07:57 +0100 Subject: [PATCH 20/85] Type Type --- specification/dartLangSpec.tex | 211 ++++++++++++++++++++------------- 1 file changed, 128 insertions(+), 83 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 94751d0b7..e7db88403 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -923,10 +923,13 @@ \section{Overview} Type annotations declare the types of variables and functions (including methods and constructors). \item - %% TODO(eernst): Change when integrating instantiate-to-bounds.md. Type annotations may be omitted, in which case they are generally - filled in with the type \DYNAMIC{} + filled in using type inference, or using the type \DYNAMIC{} (\ref{typeDynamic}). + \commentary{% + Type inference will be specified in a future version of this document + (\ref{overview}).% + } \end{enumerate}% } @@ -7964,12 +7967,7 @@ \subsection{Instantiation to Bound} For instance, with the declaration \code{Type listType() => List;}, evaluation of the raw type expression \code{List} in the body yields an instance of class \code{Type} reifying \code{List}, -because \code{List} is subject to instantiation to bound. -Note that \code{List} is not syntactically an expression, -but it is still possible to get access to -a \code{Type} instance reifying \code{List} -without instantiation to bound, -because it can be the value of a type variable.% +because \code{List} is subject to instantiation to bound.% } \rationale{% @@ -8716,7 +8714,9 @@ \subsection{Constants} a mixin or a type alias that is not qualified by a deferred prefix, is a potentially constant and constant expression. \commentary{% - The constant expression always evaluates to a \code{Type} object. + The constant expression always evaluates to a \code{Type} object + which is a reified type + (\ref{typeType}). For example, if $C$ is the name of a class or type alias, the expression \code{$C$} is a constant, and if $C$ is imported with a prefix $p$, \code{$p$.$C$} is @@ -18010,7 +18010,8 @@ \subsection{Identifier Reference} \item If $D$ is a class, mixin, enum, or type alias, the value of $e$ is an object implementing the class \code{Type} - which reifies the corresponding type. + which reifies the corresponding type + (\ref{typeType}). \item If $D$ is a type parameter $X$ then the value of $e$ is the value of the actual type argument corresponding to $X$ @@ -21689,41 +21690,6 @@ \subsection{Dynamic Type System} (\ref{actualTypes}).% } -\LMHash{}% -When types are reified as instances of the built-in class \code{Type}, -those objects override the \lit{==} operator -inherited from the \code{Object} class, so that -two \code{Type} objects are equal according to operator \lit{==} -if{}f the corresponding types are subtypes of each other. - -\commentary{% -For example, the \code{Type} objects for the types -\DYNAMIC{} and \code{Object} are equal to each other -and hence \code{dynamic\,==\,Object} must evaluate to \TRUE. -No constraints are imposed on the built-in function \code{identical}, -so \code{identical(dynamic, Object)} may be \TRUE{} or \FALSE. - -Similarly, \code{Type} instances for distinct type alias declarations -declaring a name for the same function type are equal:% -} - -\begin{dartCode} -\TYPEDEF{} F = \VOID{} \FUNCTION{}(X); -\TYPEDEF{} G = \VOID{} \FUNCTION{}(Y); -\\ -\VOID{} main() \{ - assert(F == G); -\} -\end{dartCode} - -\LMHash{}% -\commentary{% -Instances of \code{Type} can be obtained in various ways, -for example by using reflection, -by reading the \code{runtimeType} of an object, -or by evaluating a \emph{type literal} expression.% -} - \LMHash{}% An expression is a \emph{type literal} if it is an identifier, or a qualified identifier, @@ -23610,7 +23576,7 @@ \subsection{Type Never} } -\subsection{Type \code{Null}} +\subsection{Type Null} \LMLabel{typeNull} \LMHash{}% @@ -23656,6 +23622,109 @@ \subsection{Type \code{Null}} } +\subsection{Type Type} +\LMLabel{typeType} + +\LMHash{}% +The Dart runtime supports a very limited kind of introspective reflection +for all programs +(\commentary{% +that is, without including any reflection support mechanisms, +e.g., importing \code{dart:mirrors}, +or using reflection related code generation% +}). +In particular, evaluation of a type literal as an expression yields +an object whose run-time type is a subtype of the built-in type \code{Type}. +System libraries may deliver such objects as well +(\commentary{e.g., the method \code{runtimeType} on \code{Object}}). +If an object $o$ is obtained in this manner +as a reification of the Dart type $T$, +we say that $o$ is a \Index{reified type}, +and we say that $o$ \IndexCustom{reifies}{type!reifies} $T$. + +%% TODO(eernst): Define "same type" in one location in this spec, use it here. +\LMHash{}% +A reified type identifies the underlying Dart type in the sense that +it supports equality tests with other reified types as follows. +Let $o_1$ and $o_2$ be reified types that reify $S_1$ respectively $S_2$, +and let $o_3$ be an object which is not a reified type. +Let $U_j$ be the transitive alias expansion of $S_j$, for $j \in 1 .. 3$, +and let $v_j$ be a fresh variable whose value is $o_j$, for $j \in 1 .. 3$. +It is then guaranteed that \code{$v_1$ == $v_2$} if{}f +\NormalizedTypeOf{$U_1$} and \NormalizedTypeOf{$U_2$} +are syntactically equal, +up to equivalence of bound variables +and up to designations of the same type using different syntax +(\commentary{e.g., \code{C} and \code{prefix.C} may denote the same type}). +Conversely, \code{$v_1$ == $v_3$} will yield false. + +\commentary{% +Note that we do not equate primitive top types. +For example, +reified types reifying \code{List<\VOID>} and \code{List<\DYNAMIC>} +are not equal. +However, a type variable which is declared by a function type +is treated as if it were consistently renamed to a fresh name, +thus making types identical if possible +(also known as alpha equivalence). +For example:% +} + +\begin{dartCode} +\TYPEDEF{} F = \VOID{} \FUNCTION{}(X); +\TYPEDEF{} G = \VOID{} \FUNCTION{}(Y); +\\ +\VOID{} main() \{ + print(F == G); // \comment{Prints 'true'.} +\} +\end{dartCode} + +\commentary{% +Note that reified types have a primitive operator \lit{==} +(\ref{theOperatorEqualsEquals}). +This property cannot be assumed for arbitrary expressions of type \code{Type}. +For instance, we can declare +\code{\CLASS\,\,MyType\,\,\EXTENDS\,\,Type \{\ldots\}} +and override operator \lit{==}.% +} + +\LMHash{}% +Let $e_1$ and $e_2$ be constant expressions +(\ref{constants}) +evaluating to $o_1$ respectively $o_2$, +which are reified types reifying the Dart types $S_1$ respecively $S_2$. +Let $v_1$ and $v_2$ be fresh variables bound to $o_1$ respectively $o_2$ +We then have \code{identical($v_1$, $v_2$)} if{}f +\code{$S_1$\,\,==\,\,$S_2$}. + +\commentary{% +In other words, constant reified types are canonicalized. +For runtime implementations which implement identity by choosing a +canonical representative for the equivalence class of equal instances, +the choice of what type object to canonicalize to is not +observable in the language. +} + +\rationale{% +As mentioned at the beginning of this section, +reified types support a very limited kind of introspective reflection. +In particular, we can compare reified types for equality, +and in the case where two reified types are equal +it is known that they reify types which are subtypes of each other +(that is, they are ``the same type'' with respect to subtyping). +But we cannot compare reified types for any inequality, that is, +we cannot determine whether one is a subtype of the other. +It is also impossible to deconstruct a reified type. +E.g., we cannot obtain the reified type for \code{$T$} +by performing operations on a given reified type for \code{List<$T$>}. +This design was chosen in order to ensure that Dart programs +do not incur the substantial implications in terms of +program size and run-time performance +that are very difficult to avoid +when a more powerful form of reflection is supported.% +} + + \subsection{Function Types} \LMLabel{functionTypes} @@ -23708,7 +23777,7 @@ \subsection{Function Types} } -\subsection{Type \FUNCTION} +\subsection{Type Function} \LMLabel{functionType} \LMHash{}% @@ -23749,7 +23818,7 @@ \subsection{Type \FUNCTION} } -\subsection{Type \DYNAMIC} +\subsection{Type dynamic} \LMLabel{typeDynamic} \LMHash{}% @@ -25122,44 +25191,20 @@ \subsubsection{Null promotion} These are extended as per [separate proposal](https://github.com/dart-lang/language/blob/master/resources/type-system/flow-analysis.md). -\subsubsection{Runtime type equality operator} - -!!!TODO!!! - -Two objects $T_1$ and $T_2$ which are instances of \code{Type} (that -is, runtime type objects) are considered equal if and only if the -runtime type objects $T_1$ and $T_2$ corresponds to the types $S_1$ -and $S_2$ respectively, and the normal forms \NormalizedTypeOf{$S_1$} -and \NormalizedTypeOf{$S_2$} are syntactically equal up to equivalence -of bound variables. Note that we do not equate primitive top types. -\code{List<\VOID>} and \code{List<\DYNAMIC>} are still considered -distinct runtime type objects. Note that we also do not equate -function types which differ in the placement of \REQUIRED{} on -parameter types. - -\subsubsection{Constant evaluation and canonicalization} - -\paragraph{Type literals} - -Two constant type literals $T_1$ and $T_2$ compare as identical if they -are equal using the definition of runtime type equality specified above. - -For runtime implementations which implement identity by choosing a -canonical representative for the equivalence class of equal instances, -the choice of what type object to canonicalize to is not -observable in the language. \paragraph{Constant instances} -Any two -instances which are otherwise identical except for their generic type arguments -shall be considered identical if those generic type arguments compare equal -using the definition of runtime type object equality defined above. That is, -comparison (or canonicalization) of constant instances of generic classes is -performed relative to the normal forms of their generic type arguments. -Hence, an instance of -\code{$C$<$T_0$>} compares identical to \code{C<$T_1$>} if $T_0$ and $T_1$ have the same normal form -(up to the identity of bound variables), and the objects are otherwise +!!!TODO!!! + +Any two instances which are otherwise identical except for their +generic type arguments shall be considered identical if those generic +type arguments compare equal using the definition of runtime type +object equality defined above. That is, comparison (or +canonicalization) of constant instances of generic classes is +performed relative to the normal forms of their generic type +arguments. Hence, an instance of \code{$C$<$T_0$>} compares identical +to \code{C<$T_1$>} if $T_0$ and $T_1$ have the same normal form (up to +the identity of bound variables), and the objects are otherwise identical. Implementations of the Dart runtime semantics rely on canonicalization of From ed17e8438d5e15fa91dc4660bb3f1b3095c83e4e Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Wed, 17 Nov 2021 15:23:15 +0100 Subject: [PATCH 21/85] Type Type fixes --- specification/dartLangSpec.tex | 58 ++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index e7db88403..bb8c2d8e5 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -2717,7 +2717,7 @@ \subsection{Type of a Function} different type parameters, F-bounds, and the types of formal parameters. However, we do not wish to distinguish between two function types if they have the same structure and only differ in the choice of names. -This treatment of names is also known as alpha-equivalence.% +This treatment of names is also known as alpha equivalence.% } \LMHash{}% @@ -5233,7 +5233,6 @@ \subsection{Superinterfaces} is not a class building type (\ref{classBuildingTypes}). It is a \Error{compile-time error} if two elements in said type list -%% TODO(eernst): Refer to nnbd notion of 'same type'. specifies the same type. It is a \Error{compile-time error} if the superclass of a class $C$ is one of the elements of the type list of the \IMPLEMENTS{} clause of $C$. @@ -23628,35 +23627,45 @@ \subsection{Type Type} \LMHash{}% The Dart runtime supports a very limited kind of introspective reflection for all programs -(\commentary{% -that is, without including any reflection support mechanisms, -e.g., importing \code{dart:mirrors}, -or using reflection related code generation% -}). +(\commentary{that is, without including any reflection support mechanisms}). In particular, evaluation of a type literal as an expression yields an object whose run-time type is a subtype of the built-in type \code{Type}. -System libraries may deliver such objects as well -(\commentary{e.g., the method \code{runtimeType} on \code{Object}}). +System libraries may deliver such objects as well. +In particular, the getter \code{runtimeType} on \code{Object} +returns a reified type for the run-time type of the receiver. + +\LMHash{}% If an object $o$ is obtained in this manner -as a reification of the Dart type $T$, +as a reification of the type $T$, we say that $o$ is a \Index{reified type}, and we say that $o$ \IndexCustom{reifies}{type!reifies} $T$. -%% TODO(eernst): Define "same type" in one location in this spec, use it here. \LMHash{}% -A reified type identifies the underlying Dart type in the sense that +We define what it means for two types to be the same as follows: +Let $T_1$ and $T_2$ be types. +Let $U_j$ be the transitive alias expansion +(\ref{typedef}) of $T_j$, for $j \in 1 .. 2$. +We say that $T_1$ and $T_2$ are the \Index{same type} +if{}f \NormalizedTypeOf{$U_1$} and \NormalizedTypeOf{$U_2$} +(\ref{typeNormalization}) +are syntactically equal, +up to equivalence of bound variables, +and up to replacement of identifiers or qualified identifiers +resolving to the same type declaration +(\commentary{% +e.g., \code{C} and \code{prefix.C} could resolve to +the same class declaration% +}). + +\LMHash{}% +A reified type identifies the underlying type in the sense that it supports equality tests with other reified types as follows. -Let $o_1$ and $o_2$ be reified types that reify $S_1$ respectively $S_2$, +Let $o_1$ and $o_2$ be reified types that reify $T_1$ respectively $T_2$, and let $o_3$ be an object which is not a reified type. -Let $U_j$ be the transitive alias expansion of $S_j$, for $j \in 1 .. 3$, -and let $v_j$ be a fresh variable whose value is $o_j$, for $j \in 1 .. 3$. -It is then guaranteed that \code{$v_1$ == $v_2$} if{}f -\NormalizedTypeOf{$U_1$} and \NormalizedTypeOf{$U_2$} -are syntactically equal, -up to equivalence of bound variables -and up to designations of the same type using different syntax -(\commentary{e.g., \code{C} and \code{prefix.C} may denote the same type}). -Conversely, \code{$v_1$ == $v_3$} will yield false. +Let $v_j$ be a fresh variable bound to $o_j$, for $j \in 1 .. 3$. +It is then guaranteed that \code{$v_1$ == $v_2$} evaluates to \TRUE{} +if{}f $T_1$ and $T_2$ are the same type as defined above. +Conversely, \code{$v_1$ == $v_3$} evaluates to \FALSE. \commentary{% Note that we do not equate primitive top types. @@ -23692,10 +23701,10 @@ \subsection{Type Type} Let $e_1$ and $e_2$ be constant expressions (\ref{constants}) evaluating to $o_1$ respectively $o_2$, -which are reified types reifying the Dart types $S_1$ respecively $S_2$. +which are reified types reifying the types $T_1$ respecively $T_2$. Let $v_1$ and $v_2$ be fresh variables bound to $o_1$ respectively $o_2$ We then have \code{identical($v_1$, $v_2$)} if{}f -\code{$S_1$\,\,==\,\,$S_2$}. +\code{$T_1$\,\,==\,\,$T_2$}. \commentary{% In other words, constant reified types are canonicalized. @@ -23717,6 +23726,7 @@ \subsection{Type Type} It is also impossible to deconstruct a reified type. E.g., we cannot obtain the reified type for \code{$T$} by performing operations on a given reified type for \code{List<$T$>}. + This design was chosen in order to ensure that Dart programs do not incur the substantial implications in terms of program size and run-time performance From 80321097d0363427924db4fac927c1a114dc2ebe Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Wed, 17 Nov 2021 15:55:29 +0100 Subject: [PATCH 22/85] More typeType fixes --- specification/dartLangSpec.tex | 97 ++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 45 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index bb8c2d8e5..40f397eec 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -5233,9 +5233,11 @@ \subsection{Superinterfaces} is not a class building type (\ref{classBuildingTypes}). It is a \Error{compile-time error} if two elements in said type list -specifies the same type. -It is a \Error{compile-time error} if the superclass of a class $C$ is -one of the elements of the type list of the \IMPLEMENTS{} clause of $C$. +denotes the same type +(\ref{typeType}). +It is a \Error{compile-time error} if the superclass of a class $C$ denotes +the same type as one of the elements of +the type list of the \IMPLEMENTS{} clause of $C$. \rationale{% One might argue that it is harmless to repeat a type in the superinterface list, @@ -8062,15 +8064,9 @@ \subsubsection{Auxiliary Concepts for Instantiation to Bound} Meta-variables (\ref{metaVariables}) like $S$ and $T$ are understood to denote types, -and they are considered to be equal (as in `$S$ is $T$') -in the same sense as in the section about subtype rules -(\ref{subtypeRules}). -% -In particular, -even though two identical pieces of syntax may denote two distinct types, -and two different pieces of syntax may denote the same type, -the property of interest here is whether they denote the same type -and not whether they are spelled identically. +and they are considered to be or not be the same type +in the same sense as in the section about the type \code{Type} +(\ref{typeType}). The intuition behind the situation where a type raw-depends on another type is that we need to compute any missing type arguments for the latter @@ -11595,9 +11591,10 @@ \subsubsection{Sets} $o_2$ with contents $o_{21}, \ldots, o_{2n}$ and actual type argument $t_2$ be the result of evaluating them. Then \code{identical($o_1$, $o_2$)} evaluates to \TRUE{} if{}f -%% TODO(eernst): Refer to nnbd notion of 'same type'. -\code{$t_1$ == $t_2$} and \code{identical($o_{1i}$, $o_{2i}$)} -evaluates to \TRUE{} for all $i \in 1 .. n$. +\code{identical($T_1$, $T_2$)} evaluates to \TRUE{} +(\ref{typeType}), +and \code{identical($o_{1i}$, $o_{2i}$)} evaluates to \TRUE{} +for all $i \in 1 .. n$. \commentary{% In other words, constant set literals are canonicalized if they have @@ -15485,9 +15482,9 @@ \subsubsection{Instance Method Closurization} \LMHash{}% If $T$ is a non-generic class then for $j \in 1 .. n+k$, -%% TODO(eernst): Refer to nnbd notion of 'same type'. -$T_j$ is a type annotation that denotes the same type as that -which is denoted by the type annotation on +$T_j$ is a type annotation that denotes the same type +(\ref{typeType}) +as that which is denoted by the type annotation on the corresponding parameter declaration in $D$. If that parameter declaration has no type annotation then $T_j$ is \DYNAMIC. @@ -15639,9 +15636,9 @@ \subsubsection{Super Closurization} \LMHash{}% If $S$ is a non-generic class then for $j \in 1 .. n+k$, -%% TODO(eernst): Refer to nnbd notion of 'same type'. -$T_j$ is a type annotation that denotes the same type as that -which is denoted by the type annotation on +$T_j$ is a type annotation that denotes the same type +(\ref{typeType}) +as that which is denoted by the type annotation on the corresponding parameter declaration in $D$. If that parameter declaration has no type annotation then $T_j$ is \DYNAMIC. @@ -15900,8 +15897,7 @@ \subsubsection{Generic Method Instantiation} Consider the situation where the program evaluates two invocations of this method with the same receiver $o$, and with actual type arguments whose actual values are -%% TODO(eernst): Refer to nnbd notion of 'same type'. -the same types \List{t}{1}{s} for both invocations, +the same types (\ref{typeType}) \List{t}{1}{s} for both invocations, and assume that the invocations returned the instances $o_1$ respectively $o_2$. % @@ -22277,7 +22273,8 @@ \subsubsection{Subtype Rules} \IndexCustom{instantiating}{instantiation!subtype rule} it, that is, by consistently replacing each occurrence of a given meta-variable by -concrete syntax denoting the same type. +concrete syntax denoting the same type +(\ref{typeType}). \commentary{% In general, this means that two or more occurrences of @@ -22779,7 +22776,7 @@ \subsection{Type Nullability} so it cannot be non-nullable. It follows that all four concepts are distinct: -The same type $X$ is potentially nullable, but not nullable, +The given type $X$ is potentially nullable, but not nullable, and $X$ is also potentially non-nullable, but not non-nullable.% } @@ -22919,7 +22916,7 @@ \subsection{Functions Dealing with Extreme Types} \TopMergeTypeName{} of the first two, and then recursively taking \TopMergeTypeName{} of the rest. \commentary{The ordering of the arguments makes no difference.} - + \LMHash{}% The \Index{\IsTopTypeName} predicate is true for any type which is in the equivalence class of top types. @@ -22935,7 +22932,7 @@ \subsection{Functions Dealing with Extreme Types} \end{itemize} \noindent -The \Index{\IsObjectTypeName} predicate is true if{}f +The \Index{\IsObjectTypeName} predicate is true if{}f the argument is a subtype and a supertype of \code{Object}. \begin{itemize} @@ -23644,17 +23641,27 @@ \subsection{Type Type} We define what it means for two types to be the same as follows: Let $T_1$ and $T_2$ be types. Let $U_j$ be the transitive alias expansion -(\ref{typedef}) of $T_j$, for $j \in 1 .. 2$. -We say that $T_1$ and $T_2$ are the \Index{same type} -if{}f \NormalizedTypeOf{$U_1$} and \NormalizedTypeOf{$U_2$} -(\ref{typeNormalization}) -are syntactically equal, +(\ref{typedef}) of $T_j$, for $j \in 1 .. 2$, +and let $S_j$ be \NormalizedTypeOf{$U_j$}, for $j \in 1 .. 2$ +(\ref{typeNormalization}). +We then say that $T_1$ and $T_2$ are the \Index{same type} +if{}f $S_1$ and $S_2$ are syntactically equal, up to equivalence of bound variables, and up to replacement of identifiers or qualified identifiers resolving to the same type declaration (\commentary{% e.g., \code{C} and \code{prefix.C} could resolve to the same class declaration% +}), +and excluding the case where two identifiers or qualified identifiers +occurring at corresponding positions in $S_1$ and $S_2$ +are syntactically identical, +but resolve to different declarations +(\commentary{% +e.g., one occurrence of \code{C} could resolve to a +class declaration imported from a library $L_1$, +and another occurrence of \code{C} could resolve to a +class declaration imported from a different library $L_2$% }). \LMHash{}% @@ -23702,9 +23709,9 @@ \subsection{Type Type} (\ref{constants}) evaluating to $o_1$ respectively $o_2$, which are reified types reifying the types $T_1$ respecively $T_2$. -Let $v_1$ and $v_2$ be fresh variables bound to $o_1$ respectively $o_2$ +Let $v_1$ and $v_2$ be fresh variables bound to $o_1$ respectively $o_2$. We then have \code{identical($v_1$, $v_2$)} if{}f -\code{$T_1$\,\,==\,\,$T_2$}. +\code{$v_1$\,\,==\,\,$v_2$}. \commentary{% In other words, constant reified types are canonicalized. @@ -24324,22 +24331,22 @@ \subsubsection{Void Soundness} \ABSTRACT{} \CLASS A \{ final X x; A(this.x); - Object foo(X x); + Object? foo(X x); \} \\ \CLASS{} B \EXTENDS{} A \{ B(X x): super(x); - Object foo(Object x) => x; + Object? foo(Object? x) => x; \} \\ -Object f(X x) => x; +Object? f(X x) => x; \\ \VOID{} main() \{ \VOID x = 42; print(f(x)); // \comment{(1)} \\ A<\VOID{}> a = B<\VOID{}>(x); - A aObject = a; + A aObject = a; print(aObject.x); // \comment{(2)} print(a.foo(x)); // \comment{(3)} \} @@ -24356,25 +24363,25 @@ \subsubsection{Void Soundness} which is or contains a type variable whose value could be \VOID, so we are allowed to return \code{x} in the body of \code{f}, even though this means that we indirectly get access to the value -of an expression of type \VOID, under the static type \code{Object}. +of an expression of type \VOID, under the static type \code{Object?}. At (2), we indirectly obtain access to the value of the variable \code{x} with type \VOID, because we use an assignment to get access to the instance of \code{B} which was created with type argument \VOID{} under the type -\code{A}. -Note that \code{A} and \code{A<\VOID{}>} are subtypes of each other, +\code{A}. +Note that \code{A} and \code{A<\VOID{}>} are subtypes of each other, that is, they are equivalent according to the subtype rules, so neither static nor dynamic type checks will fail. At (3), we indirectly obtain access to the value of the variable \code{x} with type \VOID{} -under the static type \code{Object}, +under the static type \code{Object?}, because the statically known method signature of \code{foo} has parameter type \VOID, but the actual implementation of \code{foo} which is invoked -is an override whose parameter type is \code{Object}, -which is allowed because \code{Object} and \VOID{} are both top types.% +is an override whose parameter type is \code{Object?}, +which is allowed because \code{Object?} and \VOID{} are both top types.% } \rationale{% @@ -24395,7 +24402,7 @@ \subsubsection{Void Soundness} from one variable or parameter to the next one, all with type \VOID, explicitly, or as the value of a type parameter. In particular, we could require that method overrides should -never override return type \code{Object} by return type \VOID, +never override return type \code{Object?} by return type \VOID, or parameter types in the opposite direction; parameterized types with type argument \VOID{} could not be assigned to variables where the corresponding type argument is anything other than From 82338a3bd2fd780a642665b1753fe5256096cfe9 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Thu, 18 Nov 2021 01:28:59 +0100 Subject: [PATCH 23/85] Constant instances (in particular: about canonical instances) --- specification/dartLangSpec.tex | 63 ++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 40f397eec..49e7d911d 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -10793,6 +10793,13 @@ \subsubsection{Lists} the result of evaluating a constant expression.% } +\LMHash{}% +The canonical instance of a constant list literal has a type argument +which is transitively alias expanded +(\ref{typedef}) +and normalized +(\ref{typeNormalization}). + \LMHash{}% A run-time list literal \code{<$T$>[\List{\ell}{1}{m}]} @@ -11607,6 +11614,13 @@ \subsubsection{Sets} the result of evaluating a constant expression.% } +\LMHash{}% +The canonical instance of a constant set literal has a type argument +which is transitively alias expanded +(\ref{typedef}) +and normalized +(\ref{typeNormalization}). + \LMHash{}% A run-time set literal \code{<$T$>\{\List{\ell}{1}{n}\}} is evaluated as follows: @@ -11802,6 +11816,13 @@ \subsubsection{Maps} the result of evaluating a constant expression.% } +\LMHash{}% +The canonical instance of a constant map literal has type arguments +which are transitively alias expanded +(\ref{typedef}) +and normalized +(\ref{typeNormalization}). + \LMHash{}% A run-time map literal \code{<$T_1, T_2$>\{\List{\ell}{1}{m}\}} @@ -12714,6 +12735,14 @@ \subsubsection{Const} The constructor need only be executed once per call site, at compile time.% } +\LMHash{}% +For instances of a generic class, +the canonical instance of a constant object expression has type arguments +which are transitively alias expanded +(\ref{typedef}) +and normalized +(\ref{typeNormalization}). + \LMHash{}% It is a \Error{compile-time error} if evaluation of a constant object results in an uncaught exception being thrown. @@ -13792,6 +13821,14 @@ \subsection{Function Closurization} are canonicalized.% } +\LMHash{}% +The canonical instance of a constant function closurization has +parameter types and a return type +which are transitively alias expanded +(\ref{typedef}) +and normalized +(\ref{typeNormalization}). + \subsection{Generic Function Instantiation} \LMLabel{genericFunctionInstantiation} @@ -25208,36 +25245,10 @@ \subsubsection{Null promotion} These are extended as per [separate proposal](https://github.com/dart-lang/language/blob/master/resources/type-system/flow-analysis.md). - \paragraph{Constant instances} !!!TODO!!! -Any two instances which are otherwise identical except for their -generic type arguments shall be considered identical if those generic -type arguments compare equal using the definition of runtime type -object equality defined above. That is, comparison (or -canonicalization) of constant instances of generic classes is -performed relative to the normal forms of their generic type -arguments. Hence, an instance of \code{$C$<$T_0$>} compares identical -to \code{C<$T_1$>} if $T_0$ and $T_1$ have the same normal form (up to -the identity of bound variables), and the objects are otherwise -identical. - -Implementations of the Dart runtime semantics rely on canonicalization of -constant objects to allow the identity semantics specified above to be -implemented as fast pointer equality checks on the reference to the canonical -form. The definition above defines equivalence classes of constant objects for -which we must choose the canonical representative. - -We define the choice of the canonical instance representing an -equivalence class of constant objects as follows. - -All generic constant constructors and generic constant -literals are evaluated using the type arguments provided, and canonicalization -is performed with respect to the normal form of the type arguments. This -ensures a consistent semantics, since it is not observable which instance is -chosen as the canonical representative. \subsubsection{Null check operator} From 358edf00b7a14547031dac59f43e0299906cc3f4 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 19 Nov 2021 13:37:03 +0100 Subject: [PATCH 24/85] Corrected variable initialization specification ("has been stored" rather than "first execution of getter/setter") --- specification/dartLangSpec.tex | 100 ++++++++++++++++++++------------- 1 file changed, 62 insertions(+), 38 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 49e7d911d..fa7977b7f 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -1446,6 +1446,13 @@ \section{Variables} That is, any kind of variable which is not a local variable.% } +\LMHash{}% +A \IndexCustom{non-local variable}{variable!non-local} +is a library variable, a class variable, or an instance variable. +\commentary{% +That is, any kind of variable which is not a local variable.% +} + \LMHash{}% A \IndexCustom{constant variable}{variable!constant} is a variable whose declaration includes the modifier \CONST. @@ -1576,13 +1583,10 @@ \subsection{Implicitly Induced Getters and Setters} A variable declaration of the form \code{\STATIC?\,\,\LATE?\,\,\VAR\,\,\id{} = $e$;} implicitly induces a setter with the header -\code{\VOID\,\,\SET\,\,\id(\DYNAMIC\,\,$x$)}, +\code{\VOID\,\,\SET\,\,\id($T$\,\,$x$)}, whose execution sets the value of \id{} to the incoming argument $x$. - -\commentary{% -Type inference could have provided a type different from \DYNAMIC{} -(\ref{overview}).% -} +The type $T$ is obtained from type inference +(\ref{overview}). \EndCase \LMHash{}% @@ -1607,7 +1611,7 @@ \subsection{Implicitly Induced Getters and Setters} A variable declaration of the form \code{\STATIC?\,\,\LATE\,\,\FINAL\,\,$T$\,\,\id;} implicitly induces a setter (\ref{setters}) with the header -\code{\VOID\,\,\SET\,\,\id($T$\,\,$x$)}. +\code{\VOID\,\,\SET\,\,\id(\DYNAMIC\,\,$x$)}. If this setter is executed in a situation where the variable \id{} has not been bound, it will bind \id{} to the object that $x$ is bound to. @@ -1931,7 +1935,39 @@ \subsection{Evaluation of Implicit Variable Getters} \commentary{% Note that these static or library variables can be \emph{implicitly} late-initialized, in the sense that they do not have - the modifier \LATE.% + the modifier \LATE. + + An initializing expression can have side effects + that are significant during initialization. + For example:% + } + +\begin{dartCode} +bool b = \TRUE; +int i = (() => (b = !b) ? (i = 10) : i + 1 )(); +\\ +\VOID{} main() \{ + print(i); // '11'. +\} +\end{dartCode} + + \commentary{% + In this example, \code{main} invokes + the implicitly induced getter named \code{i}, + and no value has been stored in the variable \code{i} at this point. + Hence, evaluation of the initializing expression proceeds. + This causes \code{b} to be toggled to \FALSE, + which again causes \code{i + 1} to be evaluated. + This causes the getter \code{i} to be invoked again, + and it is still true that no value has been stored in the variable, + so the initializing expression is evaluated again. + This toggles \code{b} to \TRUE, + which causes \code{i = 10} to be evaluated, + which causes the implicitly induced setter named \code{i=} to be invoked, + and the most recent invocation of the getter \code{i} + returns 10. + This makes \code{i + 1} evaluate to 11, + which is stored in the variable.% } \item \emph{Constant variable.} If $d$ declares a constant variable with the initializing expression $e$, @@ -1959,6 +1995,7 @@ \subsection{Evaluation of Implicit Variable Getters} the implicitly induced getter is a late-uninitialized getter. This determines the semantics of an invocation. \end{itemize} +\EndCase % Reduce whitespace after itemized list: This is just an end symbol. \vspace{-\baselineskip}\EndCase @@ -4001,6 +4038,11 @@ \subsection{Instance Variables} is considered to be covariant-by-declaration (\ref{covariantParameters}). +\LMHash{}% +A \Error{compile-time error} occurs if an instance variable declaration +includes the modifier \COVARIANT, +but it does not implicitly induce a setter. + \commentary{% The modifier \COVARIANT{} on an instance variable has no other effects. In particular, the return type of the implicitly induced getter @@ -8699,7 +8741,7 @@ \subsection{Constants} that is not qualified by a deferred prefix, is a potentially constant and constant expression. \commentary{% - For example, if class $C$ declares a constant static variable $v$, + For example, if class $C$ declares a constant class variable $v$, \code{$C$.$v$} is a constant. The same is true if $C$ is accessed via a prefix $p$; \code{$p$.$C$.$v$} is a constant unless $p$ is a deferred prefix.% @@ -13562,11 +13604,13 @@ \subsubsection{Unqualified Invocation} \vspace{-2ex} \EndCase +\LMHash{}% \Case{Lexical lookup yields an import prefix} When the lexical lookup of \id{} yields an import prefix, a \Error{compile-time error} occurs. \EndCase +\LMHash{}% \Case{Lexical lookup yields nothing} When the lexical lookup of \id{} yields nothing, $i$ is treated as @@ -17282,21 +17326,23 @@ \subsection{Postfix Expressions} All but the latter three are specified elsewhere. \LMHash{}% -\Case{Null checks} +\Case{Non-null assertions} Consider an expression $e$ of the form \code{$e_1$\!!}\ where $e_1$ is derived from \syntax{ *}. \LMHash{}% Let $S$ be the static type of $e_1$. A warning occurs if $S$ is non-nullable. -\commentary{In this case, the null check is redundant.} +\commentary{In this case, the non-null assertion is redundant.} The static type of $e$ is \NonNullType{$S$}. \LMHash{}% $e$ is evaluated as follows: $e_1$ is evaluated to an object $o$. If $o$ is the null object then a dynamic error occurs, otherwise $e$ evaluates to $o$. +\EndCase +\LMHash{}% \Case{Constructor Invocations} Consider a \synt{constructorInvocation} $e$ of the form \code{$n$<\metavar{typeArguments}>.\id(\metavar{arguments})}. @@ -18176,7 +18222,7 @@ \subsection{Type Test} such a refinement would accept more code without errors, but not reject any code now error-free. -The rule only applies to locals and parameters, +The rule only applies to local variables (including formal parameters), as non-local variables could be modified via side-effecting functions or methods that are not accessible to a local analysis. @@ -19815,6 +19861,7 @@ \subsection{Return} The case where the evaluation of $e$ throws is covered by the general rule which propagates the throwing completion from $e$ to $s$ to the function body.% } +\EndCase \LMHash{}% \Case{Asynchronous non-generator functions} @@ -25245,18 +25292,6 @@ \subsubsection{Null promotion} These are extended as per [separate proposal](https://github.com/dart-lang/language/blob/master/resources/type-system/flow-analysis.md). -\paragraph{Constant instances} - -!!!TODO!!! - - -\subsubsection{Null check operator} - -When evaluating an expression of the form \code{$e$!}, -where $e$ evaluates to a value $v$, -a dynamic type error occurs if $v$ is \code{null}, -and otherwise the expression evaluates to $v$. - \subsubsection{Null aware operator} \LMLabel{nullShorteningTransformation} @@ -25407,20 +25442,9 @@ \subsubsection{Null aware operator} \subsubsection{Late fields and variables} -A non-local \LATE{} variable declaration $D$ implicitly induces a getter -into the enclosing scope. It also induces an implicit setter iff one of the -following conditions is satisfied: - - - $D$ is non-final. - - $D$ is late, final, and has no initializing expression. - -The late final variable declaration with no initializer is permitted, and -introduces a variable which may be assigned to so long as the variable is not -known to be definitely assigned. The property that the variable is never -mutated after initialization is enforced dynamically rather than statically. +!!!TODO!!! -An instance variable declaration may be declared \COVARIANT{} iff it introduces -an implicit setter. +A variable which is marked as \LATE{} ... A read of a field or variable which is marked as \LATE{} which has not yet been written to causes the initializer expression of the variable to be evaluated to @@ -25468,7 +25492,7 @@ \subsubsection{Late fields and variables} \} \end{dartCode} -A toplevel or static variable with an initializer is evaluated as if it +A toplevel or class variable with an initializer is evaluated as if it was marked \LATE. Note that this is a change from pre-NNBD semantics in that: \begin{itemize} From f30b8cefd51041b34ffffe62e703bb7b3d1a0ea8 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 19 Nov 2021 19:09:18 +0100 Subject: [PATCH 25/85] Small adjustments to Variables --- specification/dartLangSpec.tex | 64 +++++++++------------------------- 1 file changed, 17 insertions(+), 47 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index fa7977b7f..5b1094c4b 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -1967,7 +1967,17 @@ \subsection{Evaluation of Implicit Variable Getters} and the most recent invocation of the getter \code{i} returns 10. This makes \code{i + 1} evaluate to 11, - which is stored in the variable.% + which is stored in the variable. + Finally, the invocation of the getter \code{i} in \code{main} + completes returning 11.% + } + + \commentary{% + Note that this is a change from the semantics older versions of Dart: + Throwing an exception during initializer evaluation no longer sets the + variable to \code{null}, + and reading the variable during initializer evaluation + no longer causes a dynamic error.% } \item \emph{Constant variable.} If $d$ declares a constant variable with the initializing expression $e$, @@ -1995,7 +2005,9 @@ \subsection{Evaluation of Implicit Variable Getters} the implicitly induced getter is a late-uninitialized getter. This determines the semantics of an invocation. \end{itemize} -\EndCase + +% Reduce whitespace after itemized list: This is just an end symbol. +\vspace{-\baselineskip}\EndCase % Reduce whitespace after itemized list: This is just an end symbol. \vspace{-\baselineskip}\EndCase @@ -25444,41 +25456,9 @@ \subsubsection{Late fields and variables} !!!TODO!!! -A variable which is marked as \LATE{} ... - -A read of a field or variable which is marked as \LATE{} which has not yet been -written to causes the initializer expression of the variable to be evaluated to -a value, assigned to the variable or field, and returned as the value of the -read. - -\begin{itemize} -\item If there is no initializer expression, the read causes a runtime error to be - thrown. -\item Evaluating the initializer expression may validly cause a write to the field - or variable, assuming that the field or variable is not final. In this - case, the variable assumes the written value. The final value of the - initializer expression overwrites any intermediate written values. -\item Evaluating the initializer expression may cause an exception to be thrown. - If the variable was written to before the exception was thrown, the value of - the variable on subsequent reads is the last written value. If the variable - was not written before the exception was thrown, then the next read attempts - to evaluate the initializer expression again. -\item If a variable or field is read from during the process of evaluating its own - initializer expression, and no write to the variable has occurred, the read - is treated as a first read and the initializer expression is evaluated - again. -\end{itemize} - -Let $D$ be a \LATE{} and \FINAL{} non-local variable declaration named \code{v} -without an initializing expression. -It is a run-time error, to invoke the setter \code{v=} which is -implicitly induced by $D$ if a value has previously been assigned to \code{v} -(which could be due to an initializing formal or a constructor initializer -list, or due to an invocation of the setter). - -Let $D$ be a \LATE{} and \FINAL{} local variable declaration named \code{v}. It is a -run-time error, to assign a value to \code{v} if a value has previously -been assigned to \code{v}. +Let $D$ be a \LATE{} and \FINAL{} local variable declaration named \code{v}. +It is a run-time error, to assign a value to \code{v} +if a value has previously been assigned to \code{v}. Note that this includes the implicit initializing writes induced by evaluating the initializer during a read. Hence, the following program @@ -25492,16 +25472,6 @@ \subsubsection{Late fields and variables} \} \end{dartCode} -A toplevel or class variable with an initializer is evaluated as if it -was marked \LATE. Note that this is a change from pre-NNBD semantics in that: - -\begin{itemize} -\item Throwing an exception during initializer evaluation no longer sets the - variable to \code{null} -\item Reading the variable during initializer evaluation is no longer checked for, - and does not cause an error. -\end{itemize} - \subsubsection{Boolean conditional evaluation} The requirement that the condition in a boolean conditional control expression From 02f09c320f1ae8868dea96673ef402798d34d80a Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 26 Nov 2021 16:18:36 +0100 Subject: [PATCH 26/85] Rebase Update 'Identifier Expression' with respect to local variables. Add new sections on type inference, flow analysis, definite assignment, promotion. Change variable terminology to say 'bound to' consistently (not 'store'). Update 'Local Variable Declaration'. Update 'Assignment' with respect to local variables. --- specification/dartLangSpec.tex | 555 +++++++++++++++++++++++++-------- 1 file changed, 421 insertions(+), 134 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 5b1094c4b..e93078036 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -544,7 +544,7 @@ \section{Notation} \LMHash{}% Reserved words and built-in identifiers -(\ref{identifierReference}) +(\ref{identifierExpression}) appear in {\bf bold}. \commentary{% @@ -924,26 +924,13 @@ \section{Overview} variables and functions (including methods and constructors). \item Type annotations may be omitted, in which case they are generally - filled in using type inference, or using the type \DYNAMIC{} + filled in using type inference + (\ref{typeInference}), + or using the type \DYNAMIC{} (\ref{typeDynamic}). - \commentary{% - Type inference will be specified in a future version of this document - (\ref{overview}).% - } \end{enumerate}% } -%% TODO(eernst): Update when we add inference. -\commentary{% -Dart as implemented includes extensive support for inference of omitted types. -This specification makes the assumption that inference has taken place, -and hence inferred types are considered to be present in the program already. -However, in some cases no information is available -to infer an omitted type annotation, -and hence this specification still needs to specify how to deal with that. -A future version of this specification will also specify type inference.% -} - \LMHash{}% Dart programs are organized in a modular fashion into units called \NoIndex{libraries} (\ref{librariesAndScripts}). @@ -1486,6 +1473,7 @@ \section{Variables} is a variable whose declaration includes the modifier \FINAL. + \subsection{Implicitly Induced Getters and Setters} \LMLabel{implicitlyInducedGettersAndSetters} @@ -1554,8 +1542,8 @@ \subsection{Implicitly Induced Getters and Setters} \EndCase \commentary{% -A future version of this specification will specify type inference, -but currently it is unspecified and assumed to have taken place already. +Type inference is assumed to have taken place already +(\ref{typeInference}). For example, an instance variable declaration of the form \code{\VAR\,\,x;} could have been transformed into \code{$T$\,\,x;} based on member signatures named \code{x} in superinterfaces @@ -1954,12 +1942,12 @@ \subsection{Evaluation of Implicit Variable Getters} \commentary{% In this example, \code{main} invokes the implicitly induced getter named \code{i}, - and no value has been stored in the variable \code{i} at this point. + and the variable \code{i} has not been bound at this point. Hence, evaluation of the initializing expression proceeds. This causes \code{b} to be toggled to \FALSE, which again causes \code{i + 1} to be evaluated. This causes the getter \code{i} to be invoked again, - and it is still true that no value has been stored in the variable, + and it is still true that the variable has not been bound, so the initializing expression is evaluated again. This toggles \code{b} to \TRUE, which causes \code{i = 10} to be evaluated, @@ -1967,7 +1955,7 @@ \subsection{Evaluation of Implicit Variable Getters} and the most recent invocation of the getter \code{i} returns 10. This makes \code{i + 1} evaluate to 11, - which is stored in the variable. + and the variable is then bound to 11. Finally, the invocation of the getter \code{i} in \code{main} completes returning 11.% } @@ -2860,7 +2848,7 @@ \subsection{External Functions} or a non-redirecting constructor (\ref{generativeConstructors}, \ref{factories}). External functions are introduced via the built-in identifier \EXTERNAL{} -(\ref{identifierReference}) +(\ref{identifierExpression}) followed by the function header. \rationale{% @@ -3309,7 +3297,7 @@ \subsubsection{Operators} \LMHash{}% An operator declaration is identified using the built-in identifier -(\ref{identifierReference}) +(\ref{identifierExpression}) \OPERATOR. \LMHash{}% @@ -4681,7 +4669,7 @@ \subsubsection{Factories} \LMHash{}% A \IndexCustom{factory}{constructor!factory} is a constructor prefaced by the built-in identifier -(\ref{identifierReference}) +(\ref{identifierExpression}) \FACTORY. \begin{grammar} @@ -6508,7 +6496,7 @@ \subsection{Explicit Invocation of an Instance Member of an Extension} \commentary{% Type inference is not yet specified in this document, and is assumed to have taken place already -(\ref{overview}), +(\ref{typeInference}), but the following describes the intended treatment. This section and its subsections have similar commentary about type inference below, marked 'With type inference: \ldots'. @@ -7873,7 +7861,8 @@ \subsection{Super-Bounded Types} (\ref{const}). \commentary{% This includes the case where the actual type arguments have been provided - by type inference, which is assumed to have taken place already.% + by type inference, which is assumed to have taken place already + (\ref{typeInference}).% } \item $T$ occurs in the \synt{constructorDesignation} of a redirecting factory constructor header @@ -7989,16 +7978,8 @@ \subsection{Instantiation to Bound} or from an invocation of a generic function. \commentary{% -%% TODO(eernst): When we add a specification of type inference, we will adjust -%% the specification of i2b such that it allows for taking initial values for -%% the actual type arguments (that is, $U_{j,0}$ is given "from the caller" for -%% some $j$; probably "the caller" will always be type inference), and all other -%% parts of the algorithm remain unchanged. -%% I think it will be more confusing than helpful to start writing this now, -%% and it will be a small adjustment when we add a spec of type inference. So -%% at this point we just specify i2b as a stand-alone algorithm. Note that type inference is assumed to have taken place already -(\ref{overview}), +(\ref{typeInference}), so type arguments are not considered to be omitted if they are inferred. This means that instantiation to bound is a backup mechanism, which will be used when no information is available for inference.% @@ -11947,7 +11928,7 @@ \subsection{Throw} \LMHash{}% If $v$ is an instance of class \code{Error} or a subclass thereof, -and it is the first time that \code{Error} object is thrown, +and it is the first time where this \code{Error} object is thrown, the stack trace $t$ is stored on $v$ so that it will be returned by the \code{stackTrace} getter inherited from \code{Error}. @@ -11985,17 +11966,11 @@ \subsection{Function Expressions} The grammar does not allow a function literal to declare a return type, but it is possible for a function literal to have a \IndexCustom{declared return type}{literal!function!declared return type}, -because it can be obtained by means of type inference. +because it can be obtained by means of type inference +(\ref{typeInference}). Such a return type is included when we refer to the declared return type of a function. -\commentary{% -Type inference will be specified in a future version of this document. -Currently we consider type inference to be a phase that has completed, -and this document specifies the meaning of Dart programs -where inferred types have already been added.% -} - \LMHash{}% We say that a type $T$ \IndexCustom{derives a future type}{type!derives a future type} @@ -13814,7 +13789,7 @@ \subsection{Function Closurization} Let $f$ be an expression denoting a declaration of a top-level function, a local function, a static method of a class, of a mixin, or of an extension -(\ref{identifierReference}); +(\ref{identifierExpression}); or let $f$ be a function literal (\ref{functionExpressions}). Evaluation of $f$ yields a function object @@ -13908,7 +13883,8 @@ \subsection{Generic Function Instantiation} type arguments separately (it must then receive \emph{all} type arguments, not just some of them), and that yields a non-generic function object. -The type arguments are passed implicitly, based on type inference; +The type arguments are passed implicitly, based on type inference +(\ref{typeInference}); %% TODO(eernst): Come constructor-tearoffs, revise this. a future version of Dart may allow for passing them explicitly.% } @@ -13929,7 +13905,7 @@ \subsection{Generic Function Instantiation} \commentary{% \noindent -Each function object stored in \code{functions} +Each function object in \code{functions} has dynamic type \code{int\,\,\FUNCTION(int)}, and it is obtained by implicitly ``passing the actual type argument \code{int}'' @@ -13964,7 +13940,9 @@ \subsection{Generic Function Instantiation} \LMHash{}% \IndexCustom{Generic function type instantiation}{% generic function type instantiation}: -Type inference is applied to $G$ with context type $F$, +Type inference +(\ref{typeInference}) +is applied to $G$ with context type $F$, and it yields the actual type argument list \BlindDefineSymbol{T_j}% \List{T}{1}{s}. @@ -13975,7 +13953,7 @@ \subsection{Generic Function Instantiation} in which case the above mentioned compile-time error occurs. It will be specified in a future version of this document how type inference computes \List{T}{1}{s} -(\ref{overview}).% +(\ref{typeInference}).% } \LMHash{}% @@ -14147,7 +14125,7 @@ \subsection{Top level Getter Invocation} The value of $i$ is the result returned by the call to the getter. \commentary{% Note that the invocation is always defined. -Per the rules for identifier references, +Per the rules for identifier expressions, an identifier will not be treated as a top-level getter invocation unless the getter $i$ is defined.% } @@ -15783,7 +15761,9 @@ \subsubsection{Generic Method Instantiation} type arguments separately during closurization (it must then receive \emph{all} type arguments, not just some of them), and that yields a non-generic function object. -The type arguments are passed implicitly, based on type inference; +The type arguments are passed implicitly, based on type inference +(\ref{typeInference}); +%% TODO(eernst): Come constructor tearoffs, revise this. a future version of Dart may allow for passing them explicitly.% } \commentary{Here is an example:} @@ -15805,7 +15785,7 @@ \subsubsection{Generic Method Instantiation} \commentary{% \noindent -The function object which is stored in \code{f} at the end of \code{main} +The function object that \code{f} is bound to at the end of \code{main} has dynamic type \code{int\,\,\FUNCTION(int,\,[List])}, and it is obtained by implicitly ``passing the actual type argument \code{int}'' @@ -15863,7 +15843,9 @@ \subsubsection{Generic Method Instantiation} succeeds, that is: \LMHash{}% -Type inference is applied to $G$ with context type $F$, +Type inference +(\ref{typeInference}) +is applied to $G$ with context type $F$, and it succeeds, yielding the actual type argument list \List{T}{1}{s}. @@ -16047,17 +16029,43 @@ \subsection{Assignment} \item When the lexical lookup yields a declaration $D$ of a local variable $v$ (\commentary{which may be a formal parameter}), - a \Error{compile-time error} occurs if $v$ is final - or if the static type of $e$ is not assignable to the declared type of $v$. + a \Error{compile-time error} occurs in each of the following situations: + \begin{itemize} + \item + $D$ has the modifier \CONST. + \item + % We could rephrase the next item to include this case: Change the next + % item to not say anything about an initializing expression. This case is + % then covered because the variable in this case is 'definitely assigned' + % everywhere. However, it seems more readable to have a simple and direct + % rule about !late & final & init. + $D$ has the modifier \FINAL, but not \LATE, + and $v$ has an initializing expression. + \item + $D$ has the modifier \FINAL, but not \LATE, + $v$ does not have an initializing expression, + and $v$ is potentially assigned + (\ref{flowAnalysis}). + \item + $D$ has the modifiers \LATE{} and \FINAL, + and $v$ has an initializing expression. + \item + $D$ has the modifiers \LATE{} and \FINAL, + $v$ does not have an initializing expression, + and $v$ is definitely assigned. + \item + The static type of $e$ is not assignable to the declared type of $v$. + \end{itemize} \item When the lexical lookup yields a declaration $D$ which is not a local variable, it is guaranteed to be a setter - (\commentary{that may be explicit or induced implicitly by a variable}) + (\commentary{that may be implicitly induced by a variable}) because other declarations do not have a name of the form \code{\id=}. - If $D$ is the declaration of a static setter in class or mixin $C$ + If $D$ is the declaration of a static setter + in a class, mixin, or extension $C$ then $a$ is treated as (\ref{notation}) the assignment \code{$C$.\id{} = $e$}. @@ -16067,8 +16075,8 @@ \subsection{Assignment} proceeds as specified elsewhere.% } - Otherwise, a \Error{compile-time error} occurs, - unless the static type of $e$ is assignable to the parameter type of $D$. + A \Error{compile-time error} occurs + if the static type of $e$ is not assignable to the parameter type of $D$. \item When the lexical lookup yields nothing, $a$ is treated as @@ -16097,37 +16105,58 @@ \subsection{Assignment} the static type of $a$ is the static type of $e$. \LMHash{}% +\BlindDefineSymbol{a, \id, e}% Evaluation of an assignment $a$ of the form \code{\id{} = $e$} proceeds as follows. Perform a lexical lookup of \code{\id=} from the location of \id. \begin{itemize} \item - In the case where the lexical lookup yields - a declaration $D$ of a local variable $v$, - (\commentary{which may be a formal parameter}), - the expression $e$ is evaluated to an object $o$, - and the variable $v$ is bound to $o$. - Then $a$ evaluates to the object $o$ - (\ref{expressionEvaluation}). + Consider the case where the lexical lookup yields + a declaration $D$ of a local variable $v$ + (\commentary{which may be a formal parameter}). + The expression $e$ is evaluated to an object \DefineSymbol{o}. + Then: + + \begin{itemize} + \item + Consider the case where $D$ has the modifiers \LATE{} and \FINAL{} + (\commentary{% + in which case $v$ has no initializer and is not definitely assigned, + or a compile-time error would have occurred% + }). + If $v$ has previously been bound to an object then a dynamic error occurs. + Otherwise, $v$ is bound to $o$, and then $a$ evaluates to $o$ + (\ref{expressionEvaluation}). + \item + Consider the case where $D$ does not have the modifiers \LATE{} and \FINAL{} + (\commentary{it could have one of them, just not both}). + In this case the variable $v$ is bound to $o$, + and then $a$ evaluates to $o$. + + \commentary{% + If $D$ has the modifier \FINAL{} then $v$ is definitely unassigned, + or a compile-time error would have occurred.% + } + \end{itemize} \item In the case where the lexical lookup of \code{\id=} from the location of \id{} - yields a declaration $D$, + yields a declaration $D$ which is not a local variable, $D$ is necessarily a top level setter $s$ (\commentary{possibly implicitly induced by a variable}). - The expression $e$ is evaluated to an object $o$. - Then the setter $s$ is invoked - with its formal parameter bound to $o$. - Then $a$ evaluates to the object $o$. - \commentary{% - $D$ cannot be a static setter in a class $C$, + $D$ cannot be a static setter in a class, mixin, or extension $C$, because $a$ is then treated as \code{$C$.\id{} = $e$}, which is specified elsewhere.% } + + The expression $e$ is evaluated to an object $o$. + Then the setter $s$ is invoked + with its formal parameter bound to $o$. + Then $a$ evaluates to the object $o$. \item \commentary{% The case where the lexical lookup of \code{\id=} @@ -17865,8 +17894,8 @@ \subsection{Lexical Lookup} } -\subsection{Identifier Reference} -\LMLabel{identifierReference} +\subsection{Identifier Expression} +\LMLabel{identifierExpression} \LMHash{}% An \Index{identifier expression} consists of a single identifier; @@ -17977,10 +18006,12 @@ \subsection{Identifier Reference} } \LMHash{}% -A \Index{qualified name} is two or three identifiers separated by \lit{.}. -All but the last one must be a \synt{typeIdentifier}. -It is used to denote a declaration which is imported with a prefix, -or a \STATIC{} declaration in a class, mixin, enum, or extension, or both. +A \Index{qualified name} is an identifier preceded by +one or two terms derived from \syntax{ `.'}. +It is used to denote an entity whose declaration is accessible +via an import prefix, +or via the name of a class, mixin, or extension declaration, +or both. \LMHash{}% The static type of an identifier expression $e$ which is an identifier \id{} @@ -17995,15 +18026,14 @@ \subsection{Identifier Reference} \begin{itemize} \item - If $D$ declares a class, mixin, enum, type alias, an enumerated type, + If $D$ declares a class, a mixin, a type alias, an enumerated type, or a type parameter, the static type of $e$ is \code{Type}. \item If $D$ is the declaration of a library getter (\commentary{which may be implicitly induced by a library variable}), - the static type of $e$ is the static type of the - library getter invocation \id{} - (\ref{topLevelGetterInvocation}). + the static type of $e$ is the return type of said + library getter. \item If $D$ is a static method, library function, or local function, the static type of $e$ is the function type of $D$. @@ -18020,13 +18050,36 @@ \subsection{Identifier Reference} the static type of $e$ is the return type of the getter \code{$C$.\id}. \item - If $D$ is a local variable declaration - (\commentary{which can be a formal parameter}) - the static type of $e$ is the type of the variable $v$ declared by $D$, - unless $v$ is known to have some type $T$, - where $T$ is a subtype of any other type $S$ - such that $v$ is known to have type $S$, - in which case the static type of $e$ is $T$. + Consider the case where $D$ is a local variable declaration + (\commentary{which can be a formal parameter}). + A \Error{compile-time error} occurs if both of the following hold: + \begin{itemize} + \item $D$ has the modifier \LATE, or it has the modifier \FINAL{} + (\commentary{it can have both}), + or the variable \id{} has a type which is potentially non-nullable + (\ref{typeNullability}). + \item The variable \id{} is definitely unassigned. + \end{itemize} + + \commentary{% + When the variable is non-\LATE, non-\FINAL, and has a nullable type, + there is no error. + In this case the run-time value will be the null object, + because we know that the variable is unassigned.% + } + + A \Error{compile-time error} occurs if all of the following hold: + \begin{itemize} + \item $D$ does not have the modifier \LATE. + \item $D$ has the modifier \FINAL, or + the variable \id{} has a potentially non-nullable type. + \item The variable \id{} is potentially unassigned. + \end{itemize} + + The static type of $e$ is the type $T$ of the variable \id{} declared by $D$, + except that this variable may have been promoted to a subtype $S$ of $T$ + (\ref{flowAnalysis}), + in which case the static type of $e$ is $S$. \item % Extension getter invocation by scope. If $D$ is a declaration of an instance getter @@ -18043,8 +18096,8 @@ \subsection{Identifier Reference} which is an instance member of a class.% } \end{itemize} -\vspace{-1ex} -\EndCase +% Reduce whitespace after itemized list: This is just an end symbol. +\vspace{-\baselineskip}\EndCase \LMHash{}% \Case{Lexical lookup yields an import prefix} @@ -18649,15 +18702,6 @@ \subsection{Local Variable Declaration} of $v$. Otherwise, the variable $v$ is bound to $o$. -\commentary{% -Note that $e$ could have been transformed due to implicit coercions. -For example, \code{myFunction} could be transformed into -\code{myFunction} due to generic function instantiation -(\ref{genericFunctionInstantiation}). -Such transformations are assumed to have taken place already -in the declarations above.% -} - \LMHash{}% Let $D$ be a \LATE{} and \FINAL{} local variable declaration that declares a variable $v$. @@ -18669,6 +18713,22 @@ \subsection{Local Variable Declaration} then a dynamic error occurs (\commentary{it does not matter whether $o$ is the same object as $o'$}). +\commentary{% +Note that this includes the implicit initializing writes induced by +evaluating the variable. +Hence, the following program encounters a dynamic error +when it evaluates \code{x}, +just before it would call \code{print}.% +} + +\begin{dartCode} +\VOID\ main() \{ + int i = 0; + \LATE\ \FINAL\ int x = i++ == 0 ? x + 1 : 0; + print(x); +\} +\end{dartCode} + \subsection{Local Function Declaration} \LMLabel{localFunctionDeclaration} @@ -21970,9 +22030,8 @@ \subsection{Type Aliases} omitted in the program, but are added during static analysis via instantiation to bound (\ref{instantiationToBound}) -or via type inference, -which will be specified later -(\ref{overview}).% +or via type inference +(\ref{typeInference}).% } \LMHash{}% @@ -23107,8 +23166,11 @@ \subsection{Type Normalization} \LMHash{}% This section specifies a function that normalizes Dart types. -In this section it is assumed that type inference and -instantiation to bound has taken place, +In this section it is assumed that type inference +(\ref{typeInference}) +and instantiation to bound +(\ref{instantiationToBound}) +have taken place, and that no type under consideration is a compile-time error. \LMHash{}% @@ -23460,9 +23522,8 @@ \subsection{Least and Greatest Closure of Types} \commentary{% For example, \code{List<\FreeContext>} and \code{int} are type schemas. Type schemas are used to express and propagate constraints on types -during type inference. -Type inference is not yet specified in this document, -but it will be specified in the future.% +during type inference +(\ref{typeInference}).% } \LMHash{}% @@ -24744,6 +24805,251 @@ \subsubsection{Least Upper Bounds} } +\section{Type Inference} +\LMLabel{typeInference} + +\LMHash{}% +This specification does not yet specify type inference in Dart. +A future version of this specification will include that topic. + +\LMHash{}% +In other parts of this specification, +type inference is generally assumed to have taken place already. +However, commentary is used in several locations to give some informal +information about the effects of type inference. + + +\section{Flow Analysis} +\LMLabel{flowAnalysis} + +%% TODO(eernst): Come flow analysis, rewrite this section entirely. +\LMHash{}% +This specification does not fully specify the flow analysis in Dart, +but a future version of it will do so.\!\footnote{% +A preliminary specification of the flow analysis is available at +\url{https://github.com/dart-lang/language/blob/master/resources/type-system/flow-analysis.md}.% +} +In this section we specify a few concepts and special cases +in the flow analysis +that do not depend on the detailed control flow, +so as to enable the rest of the specification to refer to information +obtained during this analysis. + +\LMHash{}% +The flow analysis provides information about reachability of code, +about the definite assignment status of each local variable, +and about type test related events in the control flow +that determine type promotion of local variables. + + +\subsection{Definite Assignment} +\LMLabel{definiteAssignment} + +\LMHash{}% +At each location where a local variable can be referred +(\commentary{% +e.g., as an expression, or as the left hand side of an assignment% +}), +the variable has a status as being +\IndexCustom{definitely assigned}{local variable!definitely assigned} or +\IndexCustom{definitely unassigned}{local variable!definitely unassigned}. + +\commentary{% +The precise flow analysis which determines this status at each location +will be specified in a future version of this specification. +The intuition is that +when a local variable $v$ is definitely assigned at a given location $\ell$ +then it is guaranteed that $v$ is bound to an object +if and when the execution reaches $\ell$. +Similarly, if $v$ is definitely unassigned at location $\ell$ +then it is guaranteed that $v$ is unbound +if and when the execution reaches $\ell$. +It is possible for a local variable to be +both not definitely assigned and not definitely unassigned, +namely, in the situation where +it is not statically known whether the variable is bound or unbound.% +} + +\LMHash{}% +A local variable $v$ is definitely assigned and not definitely unassigned +at any location where $v$ is in scope, +in the following cases: + +\begin{itemize} +\item $v$ is a formal parameter. +\item $v$ has an initializing expression. + \commentary{% + In this case $v$ may or may not be \LATE, that makes no difference.% + } +\item $v$ occurs in a \synt{forStatement} of the form + \code{\FOR\,\,($D$\,\,\IN\,\,$e$)\,\,$S$} where $D$ declares $v$. + (\ref{for-in}). +\end{itemize} + +\LMHash{}% +Moreover, if $v$ is a local variable that occurs in +a \synt{forStatement} of the form +\code{\FOR\,\,($v$\,\,\IN\,\,$e$)\,\,$S$}, +then $v$ is definitely assigned and not definitely unassigned +at the beginning of $S$. + +\LMHash{}% +We say that a local variable is +\IndexCustom{potentially assigned}{local variable!potentially assigned} +if it is not definitely unassigned, +and that a local variable is +\IndexCustom{potentially unassigned}{local variable!potentially unassigned} +if it is not definitely assigned. + + +\subsection{Type Promotion} +\LMLabel{typePromotion} + +\LMHash{}% +Each local variable $v$ has a declared type $T$ +(\ref{localVariableDeclaration}), +and it has a type $S$, defined below, which is a subtype of $T$. +Type promotion refers to the situation where $S$ is different from $T$. + +\commentary{% +Intuitively, this is the kind of situation where +knowledge about the code which has been executed +justifies giving the local variable a ``better'' type +than the declared type. +Type promotion is also sometimes known as `occurrence typing'. + +For example, if \code{x} is declared as \code{int?\,\,x;} and we know +that it was updated by the assignment \code{x\,=\,1} most recently, +or we know that \code{x\,\,\IS\,\,int} evaluated to \TRUE, +then we can give \code{x} the type \code{int} rather than \code{int?}.% +} + +\LMHash{}% +Type promotion relies on information from flow analysis. +Flow analysis yields various kinds of information, +including a non-empty +\IndexCustom{stack of types of interest}{% + local variable!stack of types of interest} +for each local variable $v$, +and for each location $\ell$ where $v$ is in scope. +The top of the stack of types of interest for $v$ at $\ell$ is the +\IndexCustom{type}{local variable!type} +of $v$ at $\ell$. +We say that the type of $v$ has been +\IndexCustom{promoted}{type!promoted} +to $S$ at a location $\ell$, +when the type of $v$ at $\ell$ is $S$, +and $S$ is a proper subtype of the declared type of $v$. + +\rationale{% +We use the term `the type of $v$' to denote the complex typing property +which is associated with a control flow analysis and +a location dependent notion of type promotion, +and we use `the declared type of $v$' to denote the much simpler concept of +the type associated with $v$ based on its declaration alone. +It might seem much more natural to use `the type of $v$' to denote the latter, +and something like `the promoted type of $v$' to denote the former. + +However, we would then have a terminology where +`the static type of an expression $e$' +should be defined to mean `the promoted type of $e$' +in the case where $e$ is a local variable, +and `the static type of $e$' in all other cases, +and we would need to talk about `the promoted type of $v$' +in a large number of locations where we currently +use `the type of $v$'. + +So the chosen terminology allows us to confine the explicit reference to +the special typing properties of local variables: +they are the only expressions that may have different +types at different locations in the same scope, +except that other expressions may of course contain local variables +and hence get different types at different locations. +This yields a simpler and more consistent terminology +concerning the static type of an expression in general.% +} + +\LMHash{}% +Let $M$ be the set of types that occur in +the stack of types of interest for $v$ at a location $\ell$. +A \Index{type of interest} for $v$ at $\ell$ +is a type which is a member of $M$, +or the type \NonNullType{$U$}, +where the type $U$ is a member of $M$. + +\commentary{% +In particular, if $T?$ is a type of interest +then $T$ is also a type of interest.% +} + +\LMHash{}% +The initial stack of types of interest for $v$ has exactly one element, +namely the declared type. +This is the stack of types of interest +for the declaring occurrence of the name of $v$ +(\commentary{% +that is, the very first time the variable is mentioned, \ref{variables}% +}). + +\commentary{% +The flow analysis will be specified in +a future version of this specification. +Among other things, the flow analysis determines how to compute +the stack of types of interest for each local variable $v$ +at a given location $\ell$, +based on the same properties of locations +that are immediate predecessors of $\ell$ +in the control flow graph, +ending in the declaration of $v$.% +} + +\LMHash{}% +If a local variable $v$ has an initializing expression +and does not have the modifier \FINAL, +%% As per the null safety feature spec we should have the following, +%% (because variables with 'implicit initializers' are exactly for-in +%% iteration variables), but the implementations do _not_ promote in +%% this case, so we might need a breaking change process: +% +% or if the variable is declared by $D$ in a \synt{forStatement} +% of the form \code{\FOR\,\,($D$\,\,\IN\,\,$e$)\,\,$S$}, +then the declaration of $v$ is treated as an assignment +for the purposes of promotion. + +\commentary{% +This has no effect in the case where $v$ +does not have an explicit declared type, +e.g., \code{\VAR\,\,x\,\,=\,\,$e$;}, +because the declared type will then +already be exactly the static type of the initializing expression. +However, it does promote the type of \code{x} +in a situation like the following:% +} + +\begin{dartCode} +\VOID test() \{ + int? x = 3; // \comment{\code{x} has declared type \code{int?}}. + x.isEven; // \comment{Valid, \code{x} has been promoted to \code{int}}. + x = null; // \comment{Valid, demotes \code{x} to the declared type.} +\} +\end{dartCode} + +\LMHash{}% +There is one special case, +arising from the fact that intersection types +are subject to many restrictions +(\ref{intersectionTypes}). +Consider the situation +where the declaration of a local variable $v$ is of the form +\code{\LATE?\,\,\VAR\,\,$v$ = $e$;} +where the static type of $e$ is of the form \code{$X$\,\&\,$T$} +where $X$ is a type variable. +In this situation, the declared type of $v$ is \code{X} +(\ref{localVariableDeclaration}), +but the type \code{$X$\,\&\,$T$} is added to the stack of types of interest, +and the type of $v$ is immediately promoted to \code{$X$\,\&\,$T$}. + + \section{Reference} \LMLabel{reference} @@ -24904,6 +25210,7 @@ \subsection{Operator Precedence} \section{Null safety} %% !!!TODO!!! +\LMLabel{} %% !!!At the end: Search Null, change to Never where appropriate %% !!!Search all `TODO`.*null @@ -25153,6 +25460,7 @@ \subsubsection{Least and greatest closure} would have been substituted. \subsubsection{Const type variable elimination} +\LMLabel{} If performing inference on a constant value of a generic class results in inferred type arguments to the generic class which contain free type variables @@ -25280,6 +25588,7 @@ \subsubsection{Constant Objects} \end{dartCode} \subsubsection{Null promotion} +\LMLabel{} The machinery of type promotion is extended to promote the type of variables based on nullability checks subject to the same set of @@ -25299,11 +25608,6 @@ \subsubsection{Null promotion} The static type of an expression \code{$e$!} is \NonNullType{$T$} where $T$ is the static type of $e$. -\paragraph{Extended Type promotion, Definite Assignment, and Reachability} - -These are extended as per -[separate proposal](https://github.com/dart-lang/language/blob/master/resources/type-system/flow-analysis.md). - \subsubsection{Null aware operator} \LMLabel{nullShorteningTransformation} @@ -25452,28 +25756,11 @@ \subsubsection{Null aware operator} \code{$a$?.$f$.op()}.% } -\subsubsection{Late fields and variables} +\subsubsection{Boolean conditional evaluation} +\LMLabel{} !!!TODO!!! -Let $D$ be a \LATE{} and \FINAL{} local variable declaration named \code{v}. -It is a run-time error, to assign a value to \code{v} -if a value has previously been assigned to \code{v}. - -Note that this includes the implicit initializing writes induced by -evaluating the initializer during a read. Hence, the following program -terminates with an error. - -\begin{dartCode} -int i = 0; -\LATE\ \FINAL\ int x = i++ == 0 ? x + 1 : 0; -\VOID\ main() \{ - print(x); -\} -\end{dartCode} - -\subsubsection{Boolean conditional evaluation} - The requirement that the condition in a boolean conditional control expression (e.g. the a conditional statement, conditional element, \code{while} loop, etc) be assignable to \code{bool} is unchanged from pre null-safe Dart. The change in From 61e41a3dcb86f154b9a6b2ff8c910ca31bd8de66 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Mon, 29 Nov 2021 09:10:35 +0100 Subject: [PATCH 27/85] Boolean conversion; type test; made true/false terminology consistent with `Object` terminology --- specification/dartLangSpec.tex | 165 ++++++++++++++++++++------------- 1 file changed, 99 insertions(+), 66 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index e93078036..0b915f236 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -4345,12 +4345,12 @@ \subsubsection{Generative Constructors} must be a potentially constant expression (\ref{constantConstructors}). \LMHash{}% -% This error can occur due to a failed implicit cast. +% This error can occur due to a failed implicit cast from \DYNAMIC. It is a dynamic type error if an actual argument passed in an invocation of a redirecting generative constructor $k$ is not a subtype of the actual type (\ref{actualTypes}) of the corresponding formal parameter in the declaration of $k$. -% This error can occur due to a failed implicit cast. +% This error can occur due to a failed implicit cast from \DYNAMIC. It is a dynamic type error if an actual argument passed to the redirectee $k'$ of a redirecting generative constructor is not a subtype of the actual type @@ -7242,7 +7242,7 @@ \subsection{Extension Method Closurization} because the function object obtained by extension method closurization was subject to a generic function instantiation which gave \code{T} the value \code{double}, -which makes `\code{\THIS\,\,\IS\,\,T}' evaluate to false.% +which makes `\code{\THIS\,\,\IS\,\,T}' evaluate to the \FALSE{} object.% } @@ -9422,21 +9422,30 @@ \subsection{Booleans} \LMLabel{booleans} \LMHash{}% -The reserved words \TRUE{} and \FALSE{} evaluate to objects -\IndexCustom{true}{true, the object} and -\IndexCustom{false}{false, the object} +The reserved words \TRUE{} and \FALSE{} evaluate to +\IndexCustom{the \TRUE{} object}{true, the object} and +\IndexCustom{the \FALSE{} object}{false, the object} that represent the boolean values true and false respectively. They are the \IndexCustom{boolean literals}{literal!boolean}. +\commentary{% +We use the notation `the \TRUE{} object' and `the \FALSE{} object' +rather than a plain `the true object` and `the false object` +(as opposed to \NULL{} which evaluates to `the null object', +not `the \NULL{} object') +because phrases like `the false object' are too easy to misunderstand.% +} + \begin{grammar} ::= \TRUE{} \alt \FALSE{} \end{grammar} \LMHash{}% -Both \NoIndex{true} and \NoIndex{false} are instances of +Both the true object and the false object are instances of the built-in class \code{bool}, -and there are no other objects that implement \code{bool}. +and there are no other objects whose dynamic type is \code{bool} +or a subtype thereof. It is a \Error{compile-time error} for a class to extend, mix in or implement \code{bool}. @@ -10211,7 +10220,7 @@ \subsection{Collection Literals} %% Same text again. -\subsubsection{Type Promotion} +\subsubsection{Collection Literal Type Promotion} \LMLabel{collectionLiteralTypePromotion} \LMHash{}% @@ -10632,7 +10641,7 @@ \subsubsection{List Literal Inference} \code{\IF\,\,($b$)\,\,$\ell_1$\,\,\ELSE\,\,$\ell_2$}. The condition $b$ is always inferred with a context type of \code{bool}. -Assume that `\code{\ELSE\,\,$\ell_2$}' is not present. +Consider the situation where `\code{\ELSE\,\,$\ell_2$}' is not present. Then, if the inferred element type of $\ell_1$ is $S$, the inferred element type of $\ell$ is $S$. @@ -12794,7 +12803,7 @@ \subsubsection{Const} \OPERATOR *(v) => \NEW{} IntPair(x*v, y*v); \} \\ -\CONST a1 = \CONST{} A(true); // \comment{compile-time error} +\CONST a1 = \CONST{} A(\TRUE); // \comment{compile-time error} \CONST a2 = \CONST{} A(5); // \comment{legal} \CONST a3 = \CONST{} A(\CONST{} IntPair(1,2)); // \comment{compile-time error} \end{dartCode} @@ -12939,13 +12948,13 @@ \subsection{Function Invocation} $j$ is positioned after its last element, so that its current value is the null object (\ref{null}) - and the current call to \code{moveNext()} on $j$ returns false, + and the current call to \code{moveNext()} on $j$ returns the \FALSE{} object, as must all further calls. \item If it throws an exception object $e$ and stack trace $t$ then the current value of $j$ is the null object (\ref{null}) and the current call to \code{moveNext()} throws $e$ and $t$ as well. - Further calls to \code{moveNext()} must return false. + Further calls to \code{moveNext()} must return the \FALSE{} object. \end{itemize} \LMHash{}% @@ -13845,7 +13854,8 @@ \subsection{Function Closurization} Let $e_1$ and $e_2$ be two constant expressions that both evaluate to a function object which is obtained by function closurization of the same function declaration. -In this case \code{identical($e_1$, $e_2$)} shall evaluate to true. +In this case \code{identical($e_1$, $e_2$)} +shall evaluate to the \TRUE{} object. \commentary{% That is, constant expressions whose evaluation is a function closurization @@ -13985,12 +13995,14 @@ \subsection{Generic Function Instantiation} Let $f_1$ and $f_2$ be two constant expressions that are subject to generic function instantiation. Assume that $f_1$ and $f_2$ without a context type evaluate to $o_1$ -respectively $o_2$ such that \code{identical($o_1$, $o_2$)} is true. +respectively $o_2$ such that \code{identical($o_1$, $o_2$)} +evaluates to the \TRUE{} object. Assume that the given context types give rise to a successful generic function type instantiation with the same actual type arguments for $f_1$ and $f_2$, yielding the non-generic function objects $o'_1$ respectively $o'_2$. -In this case \code{identical($o'_1$, $o'_2$)} shall evaluate to true. +In this case \code{identical($o'_1$, $o'_2$)} +shall evaluate to the \TRUE{} object. \commentary{% That is, constant expressions whose evaluation is @@ -14004,18 +14016,21 @@ \subsection{Generic Function Instantiation} (\commentary{which may or may not be constant}) that are subject to generic function instantiation. Assume that $g_1$ and $g_2$ without a context type evaluate to $o_1$ -respectively $o_2$ such that \code{$o_1$ == $o_2$)} is true. +respectively $o_2$ such that \code{$o_1$ == $o_2$)} +evaluates to the \TRUE{} object. Assume that the given context types give rise to a successful generic function type instantiation with the same actual type arguments for $g_1$ and $g_2$, yielding the non-generic function objects $o'_1$ respectively $o'_2$. -In this case \code{$o'_1$ == $o'_2$} shall evaluate to true. +In this case \code{$o'_1$ == $o'_2$} +shall evaluate to the \TRUE{} object. \commentary{% When one or both of the expressions is not constant, it is unspecified whether -\code{identical($o_1$,\,\,$o_2$)} evaluates to \TRUE{} or \FALSE, -but operator \lit{==} yields true for equal function objects +\code{identical($o_1$,\,\,$o_2$)} evaluates to +the \TRUE{} object or the \FALSE{} object, +but operator \lit{==} returns the \TRUE{} object for equal function objects instantiated with the same actual type arguments.% } @@ -15576,7 +15591,7 @@ \subsubsection{Instance Method Closurization} Assume that $o_1$ and $o_2$ are objects, $m$ is an identifier, and $c_1$ and $c_2$ are function objects obtained by closurization of $m$ on $o_1$ respectively $o_2$. -Then \code{$c_1$ == $c_2$} evaluates to true +Then \code{$c_1$ == $c_2$} evaluates to the \TRUE{} object if and only if $o_1$ and $o_2$ is the same object. \commentary{% @@ -15587,10 +15602,12 @@ \subsubsection{Instance Method Closurization} and two closurizations of a method $m$ from non-identical objects are not equal. Assuming that $v_i$ is a fresh variable bound to an object, $i \in 1 .. 2$, -it also follows that \code{identical($v_1.m, v_2.m$)} must be false +it also follows that \code{identical($v_1.m, v_2.m$)} must +evaluate to the \FALSE{} objce when $v_1$ and $v_2$ are not bound to the same object. However, Dart implementations are not required to canonicalize function objects, -which means that \code{identical($v_1.m, v_2.m$)} is not guaranteed to be true, +which means that \code{identical($v_1.m, v_2.m$)} +is not guaranteed to evaluate to the \TRUE{} object, even when it is known that $v_1$ and $v_2$ are bound to the same object.% } @@ -15731,7 +15748,7 @@ \subsubsection{Super Closurization} is evaluated on two occasions where \THIS{} is bound to $o_1$ respectively $o_2$, and the resulting function objects are $c_1$ respectively $c_2$: -\code{$c_1$ == $c_2$} is then true +\code{$c_1$ == $c_2$} will then evaluate to the \TRUE{} object if and only if $o_1$ and $o_2$ is the same object. @@ -16663,7 +16680,7 @@ \subsection{Conditional} \LMHash{}% First, $e_1$ is evaluated to an object $o_1$. -% This error can occur due to implicit casts and null. +% This error can occur due to an implicit cast from \DYNAMIC. It is a dynamic error if the run-time type of $o_1$ is not \code{bool}. If $r$ is \TRUE, then the value of $c$ is the result of evaluating the expression $e_2$. @@ -16742,22 +16759,22 @@ \subsection{Logical Boolean Expressions} \LMHash{}% Evaluation of a logical boolean expression $b$ of the form $e_1 || e_2$ causes the evaluation of $e_1$ to an object $o_1$. -% This error can occur due to implicit casts and null. +% This error can occur due to an implicit downcast from \DYNAMIC. It is a dynamic error if the run-time type of $o_1$ is not \code{bool}. If $o_1$ is \TRUE, the result of evaluating $b$ is \TRUE, otherwise $e_2$ is evaluated to an object $o_2$. -% This error can occur due to implicit casts and null. +% This error can occur due to an implicit downcast from \DYNAMIC. It is a dynamic error if the run-time type of $o_2$ is not \code{bool}. Otherwise the result of evaluating $b$ is $o_2$. \LMHash{}% Evaluation of a logical boolean expression $b$ of the form $e_1 \&\& e_2$ causes the evaluation of $e_1$ producing an object $o_1$. -% This error can occur due to implicit casts and null. +% This error can occur due to an implicit downcast from \DYNAMIC. It is a dynamic error if the run-time type of $o_1$ is not \code{bool}. If $o_1$ is \FALSE, the result of evaluating $b$ is \FALSE, otherwise $e_2$ is evaluated to an object $o_2$. -% This error can occur due to implicit casts and null. +% This error can occur due to an implicit downcast from \DYNAMIC. It is a dynamic error if the run-time type of $o_2$ is not \code{bool}. Otherwise the result of evaluating $b$ is $o_2$. @@ -18232,7 +18249,7 @@ \subsection{Type Test} \LMLabel{typeTest} \LMHash{}% -The \Index{is-expression} tests if an object is a member of a type. +An \Index{\IS{} expression} tests if an object is a member of a type. \begin{grammar} ::= @@ -18241,33 +18258,44 @@ \subsection{Type Test} \end{grammar} \LMHash{}% -Evaluation of the is-expression \code{$e$ \IS{} $T$} proceeds as follows: +The static type of an \IS{} expression is \code{bool}. + +\LMHash{}% +Evaluation of an \IS{} expression of the form \code{$e$\,\,\IS\,\,$T$} +proceeds as follows: \LMHash{}% The expression $e$ is evaluated to an object $v$. If the dynamic type of $v$ is a subtype of $T$, -the is-expression evaluates to \TRUE. -Otherwise it evaluates to \FALSE. +the \IS{} expression evaluates to the \TRUE{} object. +Otherwise it evaluates to the \FALSE{} object. \commentary{% -It follows that \code{$e$ \IS{} Object} is always true. +It follows that \code{$e$\,\,\IS\,\,Object?} will always evaluate +to the \TRUE{} object, +and \code{$e$\,\,\IS\,\,Object} evaluates to the \TRUE{} object wheneven +$e$ evaluates to an object which is not the null object. This makes sense in a language where everything is an object. -Also note that \code{\NULL{} \IS{} $T$} is false -unless $T = \code{Object}$, $T = \code{\DYNAMIC}$ or $T = \code{Null}$. -The former two are useless, as is anything -of the form \code{$e$ \IS{} Object} or \code{$e$ \IS{} \DYNAMIC}. -Users should test for the null object (\ref{null}) directly -rather than via type tests.% +Also note that \code{\NULL\,\,\IS\,\,$T$} will evaluate to the \FALSE{} object +unless $T$ is a a nullable type (which includes all top types and \code{Null}). +The test on top types is useless, +because \code{$e$ \IS{} $T$} \emph{always} evaluates to the \TRUE{} object +when $T$ is a top type. +As a matter of style, a test for the null object (\ref{null}) +should be expressed directly as \code{$e$\,\,==\,\,\NULL}, +rather than via a type test \code{$e$\,\,\IS\,\,Null}.% } \LMHash{}% -The is-expression \code{$e$ \IS{}! $T$} is equivalent to -\code{!($e$ \IS{} $T$)}. +The \IS{} expression \code{$e$\,\,\IS!\,\,$T$} is equivalent to +\code{!($e$\,\,\IS\,\,$T$)}. +%% TODO(eernst): Come flow analysis, delete material about "shows .. type". \LMHash{}% -Let $v$ be a local variable (\commentary{which can be a formal parameter}). -An is-expression of the form \code{$v$ \IS{} $T$} +Let $v$ be a local variable +(\commentary{which can be a formal parameter}). +An \IS{} expression of the form \code{$v$\,\,\IS\,\,$T$} shows that $v$ has type $T$ if $T$ is a subtype of the type of the expression $v$. % @@ -18304,9 +18332,6 @@ \subsection{Type Test} if suitable heuristics indicate that a promotion is likely to be intended.% } -\LMHash{}% -The static type of an is-expression is \code{bool}. - \subsection{Type Cast} \LMLabel{typeCast} @@ -18321,18 +18346,17 @@ \subsection{Type Cast} \end{grammar} \LMHash{}% - Evaluation of the cast expression \code{$e$ \AS{} $T$} proceeds as follows: +The static type of a cast expression \code{$e$\,\,\AS\,\,$T$} is $T$. + +\LMHash{}% +Evaluation of the cast expression \code{$e$\,\,\AS\,\,$T$} proceeds as follows: \LMHash{}% The expression $e$ is evaluated to an object $v$. % This error can occur, by design of `as`. -It is a dynamic type error if $o$ is not the null object (\ref{null}), -and the dynamic type of $o$ is not a subtype of $T$. +It is a dynamic type error if the dynamic type of $o$ is not a subtype of $T$. Otherwise $e$ evaluates to $v$. -\LMHash{}% -The static type of a cast expression \code{$e$ \AS{} $T$} is $T$. - \section{Statements} \LMLabel{statements} @@ -18838,7 +18862,7 @@ \subsection{If} Under reasonable scope rules such code is problematic. If we assume that \code{v} is declared in the scope of the method \code{main()}, -then when \code{somePredicate} is false, +then when \code{somePredicate} evaluates to the \FALSE{} object, \code{v} will be uninitialized when accessed. The cleanest approach would be to require a block following the test, rather than an arbitrary statement. @@ -18862,7 +18886,7 @@ \subsection{If} \LMHash{}% First, the expression $b$ is evaluated to an object $o$. -% This error can occur due to implicit casts and null. +% This error can occur due to an implicit downcast from \DYNAMIC. It is a dynamic error if the run-time type of $o$ is not \code{bool}. If $o$ is \TRUE, then the block statement $s_1$ is executed, otherwise the block statement $s_2$ is executed. @@ -18967,7 +18991,7 @@ \subsubsection{For Loop} the previous execution of step \ref{allocateFreshVar}. \item The expression $[v'/v]c$ is evaluated to an object $o$. - % This error can occur due to implicit casts and null. + % This error can occur due to an implicit downcast from \DYNAMIC. It is a dynamic error if the run-time type of $o$ is not \code{bool}. If $o$ is \FALSE, the for loop completes normally. Otherwise, execution continues at step \ref{beginIteration}. @@ -19079,7 +19103,7 @@ \subsubsection{Asynchronous For-in} \LMHash{}% The expression $e$ is evaluated to an object $o$. -% This error can occur due to implicit casts and null. +% This error can occur due to an implicit downcast from \DYNAMIC. It is a dynamic type error if $o$ is not an instance of a class that implements \code{Stream}. It is a \Error{compile-time error} if $D$ is empty @@ -19180,7 +19204,7 @@ \subsection{While} \LMHash{}% The expression $e$ is evaluated to an object $o$. -% This error can occur due to implicit casts and null. +% This error can occur due to an implicit downcast from \DYNAMIC. It is a dynamic error if the run-time type of $o$ is not \code{bool}. \LMHash{}% @@ -19231,7 +19255,7 @@ \subsection{Do} \LMHash{}% Then, the expression $e$ is evaluated to an object $o$. -% This error can occur due to implicit casts and null. +% This error can occur due to an implicit downcast from \DYNAMIC. It is a dynamic error if the run-time type of $o$ is not \code{bool}. If $o$ is \FALSE, execution of the do statement completes normally (\ref{statementCompletion}). @@ -20209,7 +20233,7 @@ \subsection{Yield-Each} then: \begin{enumerate} \item - % This error can occur due to implicit casts. + % This error can occur due to an implicit downcast from \DYNAMIC. It is a dynamic type error if the class of $o$ is not a subtype of \code{Iterable<$T_f$>}. Otherwise @@ -20244,7 +20268,7 @@ \subsection{Yield-Each} If $m$ is marked \code{\ASYNC*} (\ref{functions}), then: \begin{itemize} \item - % This error can occur due to implicit casts. + % This error can occur due to an implicit downcast from \DYNAMIC. It is a dynamic type error if the class of $o$ is not a subtype of \code{Stream<$T_f$>}. Otherwise @@ -20317,7 +20341,7 @@ \subsection{Assert} \LMHash{}% The expression $c$ is evaluated to an object $r$. -% This error can occur due to implicit casts and null. +% This error can occur due to an implicit downcast from \DYNAMIC. It is a dynamic type error if $r$ is not of type \code{bool}. \commentary{% Hence it is a compile-time error if that situation arises @@ -21762,7 +21786,7 @@ \subsection{Static Types} % objects in constructor args - these cannot represent parameterized types. -\subsubsection{Type Promotion} +\subsubsection{Local Variable Type Promotion} \LMLabel{typePromotion} \LMHash{}% @@ -24297,7 +24321,8 @@ \subsection{Type Void} must compare equal (according to the \lit{==} operator \ref{equality}) to any instance of \code{Type} which reifies the type \code{Object} (\ref{dynamicTypeSystem}). -It is not guaranteed that \code{identical(\VOID, Object)} evaluates to true. +It is not guaranteed that \code{identical(\VOID, Object)} evaluates to +the \TRUE{} object. In fact, it is not recommended that implementations strive to achieve this, because it may be more important to ensure that diagnostic messages (including stack traces and dynamic error messages) @@ -25049,6 +25074,15 @@ \subsection{Type Promotion} but the type \code{$X$\,\&\,$T$} is added to the stack of types of interest, and the type of $v$ is immediately promoted to \code{$X$\,\&\,$T$}. +\commentary{% +Note that \code{$X$\,\&\,$T$} is not otherwise automatically a +type of interest just because $X$ is a type of interest. +In particular, if $w$ is declared as +\code{\LATE?\,\,$X$\,\,$w$ = $e$;} +where the static type of $e$ is \code{$X$\,\&\,$T$}, +the type of $w$ is \emph{not} immediately promoted to \code{$X$\,\&\,$T$}.% +} + \section{Reference} \LMLabel{reference} @@ -25756,7 +25790,7 @@ \subsubsection{Null aware operator} \code{$a$?.$f$.op()}.% } -\subsubsection{Boolean conditional evaluation} +\subsection{Other documents that need to be integrated} \LMLabel{} !!!TODO!!! @@ -25858,7 +25892,6 @@ \subsection{Migration features} https://github.com/dart-lang/language/blob/master/resources/type-system/subtyping.md https://github.com/dart-lang/language/blob/master/resources/type-system/upper-lower-bounds.md -https://github.com/dart-lang/language/blob/master/resources/type-system/normalization.md https://github.com/dart-lang/language/blob/master/resources/type-system/flow-analysis.md https://github.com/dart-lang/language/blob/master/resources/type-system/inference.md %% !!!END TODO!!! From d6cf23dfe3d90d8a86b6a922b4a01b55fb171d49 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Mon, 29 Nov 2021 15:21:17 +0100 Subject: [PATCH 28/85] Constant types; type variable elimination in constants --- specification/dartLangSpec.tex | 183 +++++++++++++++++++++++---------- 1 file changed, 131 insertions(+), 52 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 0b915f236..4403454ff 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -8764,8 +8764,8 @@ \subsection{Constants} If generic function instantiation does apply to $e$ and the provided actual type arguments are \List{T}{1}{s} then $e$ is a potentially constant and constant expression - if{}f each $T_j, j \in 1 .. s$, is a constant type expression - (\ref{constants}). + if{}f each $T_j, j \in 1 .. s$, is a constant type + (\ref{constantTypes}). \item An identifier expression denoting a parameter of a constant constructor (\ref{constantConstructors}) @@ -8789,7 +8789,8 @@ \subsection{Constants} \code{\CONST{} <$T$>[$e_1$, \ldots, $e_n$]}, or \code{<$T$>[$e_1$, \ldots, $e_n$]} that occurs in a constant context, - is a potentially constant expression if $T$ is a constant type expression, + is a potentially constant expression if $T$ is a constant type + (\ref{constantTypes}), and $e_1$, \ldots{} , $e_n$ are constant expressions. It is further a constant expression if the list literal evaluates to an object. @@ -8799,7 +8800,8 @@ \subsection{Constants} \code{<$T$>\{$e_1$, \ldots, $e_n$\}} that occurs in a constant context, is a potentially constant expression - if $T$ is a constant type expression, + if $T$ is a constant type + (\ref{constantTypes}), and $e_1$, \ldots{} , $e_n$ are constant expressions. It is further a constant expression if the set literal evaluates to an object. @@ -8942,7 +8944,8 @@ \subsection{Constants} \item An expression of the form \code{$e$\,\,\AS\,\,$T$} is potentially constant if $e$ is a potentially constant expression - and $T$ is a potentially constant type expression, + and $T$ is a potentially constant type + (\ref{constantTypes}), and it is further constant if $e$ is constant. \commentary{% It is a \Error{compile-time error} to evaluate this constant expression @@ -8952,7 +8955,8 @@ \subsection{Constants} \item An expression of the form \code{$e$\,\,\IS\,\,$T$} is potentially constant if $e$ is a potentially constant expression - and $T$ is a constant type expression, + and $T$ is a constant type + (\ref{constantTypes}), and it is further constant if $e$ is constant. \item An expression of the form \code{$e$\,\,is!\,\,$T$} @@ -8960,10 +8964,48 @@ \subsection{Constants} including whether it's potentially constant or constant. \end{itemize} +% Being potentially constant is entirely structural, not type based, +% but the program still has to satisfy strong-mode typing. + +% Constant expressions (like "const Foo(42)") always evaluate to the +% same value, with at most one value per source location. +% Potentially constant expressions that are not constant only +% allow simple operations on basic types (num, String, bool, Null). These can +% be computed statically without running user code. + \LMHash{}% -We introduce \Index{constant type expressions} and -\Index{potentially constant type expressions} -as follows: +It is a \Error{compile-time error} if an expression is required to be +a constant expression, +but its evaluation would throw an exception. +It is a \Error{compile-time error} if an assertion is evaluated as part of +a constant object expression evaluation, +and the assertion would throw an exception. + +\LMHash{}% +It is a \Error{compile-time error} if the value of a constant expression +depends on itself. + +\commentary{% +As an example, consider:% +} + +\begin{dartCode} +\CLASS{} CircularConsts \{ + // \comment{Illegal program - mutually recursive compile-time constants} + \STATIC{} \CONST{} i = j; // \comment{a compile-time constant} + \STATIC{} \CONST{} j = i; // \comment{a compile-time constant} +\} +\end{dartCode} + + +\subsubsection{Constant Types} +\LMLabel{constantTypes} + +\LMHash{}% +A \Index{potentially constant type} respectively \Index{constant type} +is a term derived from \synt{type} which is used as a type +(\commentary{not as an expression that yields an instance of \code{Type}}) +that satisfy the following criteria: \begin{itemize} \item Consider a type $T$ @@ -8972,70 +9014,105 @@ \subsection{Constants} which is not qualified by a deferred prefix, and which is optionally followed by type arguments of the form \code{<\List{T}{1}{r}>}. - $T$ is a potentially constant type expression - respectively a constant type expression - if{}f $T_j$ is a potentially constant respectively constant type expression + $T$ is a potentially constant type respectively a constant type + if{}f $T_j$ is a potentially constant respectively constant type for each $j \in 1 .. r$. \item A type of the form \code{FutureOr<$T$>} - is a potentially constant type expression - respectively a constant type expression - if{}f $T$ is a potentially constant type expression - respectively a constant type expression. + is a potentially constant type + respectively a constant type + if{}f $T$ is a potentially constant type + respectively a constant type. \item %% TODO(eernst): This does not allow for type variables introduced by - %% the type itself. `Function(X)` could be a constant type expression, + %% the type itself. `Function(X)` could be a constant type, %% but that is not covered by the current rules: `X` is a type variable, - %% and they are never constant type expressions. + %% and they are never constant type. A function type \code{$R$ Function<\metavar{typeParameters}>(\metavar{parameterTypes})} (where $R$ and \code{<\metavar{typeParameters}>} may be omitted) - is a is a potentially constant type expression - respectively a constant type expression + is a is a potentially constant type + respectively a constant type if $R$, \metavar{typeParameters}, and \metavar{parameterTypes} - (if present) contain only potentially constant type expressions - respectively constant type expressions. + (if present) contain only potentially constant types + respectively constant types. \item - The type \VOID{} is a potentially constant and a constant type expression. + The type \VOID{} is a potentially constant and constant type. \item - The type \DYNAMIC{} is a potentially constant and a constant type expression. + The type \DYNAMIC{} is a potentially constant and constant type. \item - A type variable is a potentially constant type expression. + A type variable is a potentially constant type. \end{itemize} -% Being potentially constant is entirely structural, not type based, -% but the program still has to satisfy strong-mode typing. -% Constant expressions (like "const Foo(42)") always evaluate to the -% same value, with at most one value per source location. -% Potentially constant expressions that are not constant only -% allow simple operations on basic types (num, String, bool, Null). These can -% be computed statically without running user code. +\subsubsection{Type Variable Elimination in Constants} +\LMLabel{typeVariableEliminationInConstants} \LMHash{}% -It is a \Error{compile-time error} if an expression is required to be -a constant expression, -but its evaluation would throw an exception. -It is a \Error{compile-time error} if an assertion is evaluated as part of -a constant object expression evaluation, -and the assertion would throw an exception. +\BlindDefineSymbol{e, e'}% +Let $e$ be an expression that occurs in a constant context +or whose first token is \CONST, +and $e'$ be the expression yielded by type inference +(\ref{typeInference}) +applied to $e$. +In this case an additional transformation is applied: \LMHash{}% -It is a \Error{compile-time error} if the value of a constant expression -depends on itself. +Let \List{X}{1}{r} be the free type variables declared by +a class, mixin, extension, or method that contains $e$, +such that for each $j \in 1 .. r$, each $X_j$ +occurs in a list of actual type arguments +which was added by type inference +(\commentary{% +so $X_j$ has one or more occurrences in $e'$, +but not all of those occurrences exist in $e$% +}). +For each $j \in 1 .. r$, +if a specific occurrence of $X_j$ in $e'$ was added by type inference, +the outermost enclosing type argument which was added by type inference +and which contains this occurrence +is replaced by its least closure with respect to \List{X}{1}{r} +(\ref{leastAndGreatestClosureOfTypes}). \commentary{% -As an example, consider:% +Note that a free type variable which is explicitly used +as or in a type argument in a constant expression +is still a compile-time error. +For example:% } \begin{dartCode} -\CLASS{} CircularConsts \{ - // \comment{Illegal program - mutually recursive compile-time constants} - \STATIC{} \CONST{} i = j; // \comment{a compile-time constant} - \STATIC{} \CONST{} j = i; // \comment{a compile-time constant} +\CLASS\ G \{ + \VOID\ foo() \{ + \CONST\ List c = []; // \comment{Compile-time error.} + \CONST\ List d = []; // \comment{Infers \code{[]}, then becomes \code{[]}.} + \} \} \end{dartCode} +\rationale{% +The type variable is not a constant type +(\ref{constantTypes}), +which implies that the inferred expression $e'$ is a compile-time error. +The additional transformation yields a subtype of the inferred type. +It typically replaces the type variable by \code{Never}, +but it may also replace the type variable by a top type +(if the type variable occurs contravariantly), +or the type may be changed more radically +(e.g., a generic function type using $X_j$ as a type parameter bound +may be changed to \FUNCTION{} or \code{Never}). +In any case, the chosen subtype often yields +a constant which has no errors and is useful.% +} + +\commentary{% +For example, \code{\CONST\,\,[]} is actually usable as +a list of any type, +and it is not a problem that every invocation of \code{add} +on this list will fail (statically or dynamically), +because that is already true for every constant list.% +} + \subsubsection{Further Remarks on Constants and Potential Constants} \LMLabel{furtherCommentsOnConstantsAndPotentiallyConstants} @@ -10796,8 +10873,8 @@ \subsubsection{Lists} if an element of a constant list literal is not constant. It is a \Error{compile-time error} if the type argument of a constant list literal (\commentary{no matter whether it is explicit or inferred}) -is not a constant type expression -(\ref{constants}). +is not a constant type +(\ref{constantTypes}). \rationale{% The binding of a formal type parameter of an enclosing class or function @@ -11609,8 +11686,8 @@ \subsubsection{Sets} (\ref{equality}). It is a \Error{compile-time error} if the type argument of a constant set literal (\commentary{no matter whether it is explicit or inferred}) -is not a constant type expression -(\ref{constants}). +is not a constant type +(\ref{constantTypes}). \rationale{% The binding of a formal type parameter of an enclosing class or function @@ -11814,8 +11891,8 @@ \subsubsection{Maps} (\ref{equality}). It is a \Error{compile-time error} if a type argument of a constant map literal (\commentary{no matter whether it is explicit or inferred}) -is not a constant type expression -(\ref{constants}). +is not a constant type +(\ref{constantTypes}). \rationale{% The binding of a formal type parameter of an enclosing class or function @@ -12658,7 +12735,7 @@ \subsubsection{Const} \LMHash{}% If $T$ is a parameterized type, it is a -\Error{compile-time error} if $U_j$ is not a constant type expression for any +\Error{compile-time error} if $U_j$ is not a constant type for any $j \in 1 .. m$. \begin{itemize} @@ -25624,6 +25701,8 @@ \subsubsection{Constant Objects} \subsubsection{Null promotion} \LMLabel{} +!!! + The machinery of type promotion is extended to promote the type of variables based on nullability checks subject to the same set of restrictions as normal promotion. The relevant checks and the types From 32de0476102768dbef42a9225498533571b4738d Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Tue, 30 Nov 2021 10:00:29 +0100 Subject: [PATCH 29/85] Conditional expression; operators && and ||; `if` statements; delete old section about promotion --- specification/dartLangSpec.tex | 186 +++++++++------------------------ 1 file changed, 52 insertions(+), 134 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 4403454ff..885d99b7a 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -16743,7 +16743,7 @@ \subsection{Conditional} \LMLabel{conditional} \LMHash{}% -A \Index{conditional expression} evaluates one of two expressions +A \Index{conditional expression} evaluates one of two expressions, based on a boolean condition. \begin{grammar} @@ -16752,34 +16752,23 @@ \subsection{Conditional} \end{grammar} \LMHash{}% -Evaluation of a conditional expression $c$ of the form $e_1 ? e_2 : e_3$ -proceeds as follows: +Consider a conditional expression $c$ of the form +\code{$e_1$\,\,?\,\,$e_2$\,\,:\,\,$e_3$}. +It is a \Error{compile-time error} if +the static type of $e_1$ may not be assigned to \code{bool}. +The static type of $c$ is the least upper bound (\ref{leastUpperBounds}) of +the static type of $e_2$ and the static type of $e_3$. \LMHash{}% -First, $e_1$ is evaluated to an object $o_1$. +Evaluation of a conditional expression $c$ of the form +\code{$e_1$\,\,?\,\,$e_2$\,\,:\,\,$e_3$} +proceeds as follows: +Evaluate $e_1$ to an object $o_1$. % This error can occur due to an implicit cast from \DYNAMIC. It is a dynamic error if the run-time type of $o_1$ is not \code{bool}. -If $r$ is \TRUE, then the value of $c$ is +If $o_1$ is the \TRUE{} object, then the value of $c$ is the result of evaluating the expression $e_2$. -Otherwise the value of $c$ is the result of evaluating the expression $e_3$. - -\LMHash{}% -If $e_1$ shows that a local variable $v$ has type $T$, -then the type of $v$ is known to be $T$ in $e_2$, -unless any of the following are true: -\begin{itemize} -\item $v$ is potentially mutated in $e_2$, -\item $v$ is potentially mutated within a function other - than the one where $v$ is declared, or -\item $v$ is accessed by a function defined in $e_2$ and - $v$ is potentially mutated anywhere in the scope of $v$. -\end{itemize} - -\LMHash{}% -It is a \Error{compile-time error} if -the static type of $e_1$ may not be assigned to \code{bool}. -The static type of $c$ is the least upper bound (\ref{leastUpperBounds}) of -the static type of $e_2$ and the static type of $e_3$. +Otherwise, the value of $c$ is the result of evaluating the expression $e_3$. \subsection{If-null Expressions} @@ -16834,8 +16823,15 @@ \subsection{Logical Boolean Expressions} an expression $e_1$ with argument $e_2$. \LMHash{}% -Evaluation of a logical boolean expression $b$ of the form $e_1 || e_2$ causes -the evaluation of $e_1$ to an object $o_1$. +It is a \Error{compile-time error} if +the static type of $e_1$ may not be assigned to \code{bool} +or if the static type of $e_2$ may not be assigned to \code{bool}. +The static type of a logical boolean expression is \code{bool}. + +\LMHash{}% +Evaluation of a logical boolean expression $b$ of the form +\code{$e_1$\,\,||\,\,$e_2$} +causes the evaluation of $e_1$ to an object $o_1$. % This error can occur due to an implicit downcast from \DYNAMIC. It is a dynamic error if the run-time type of $o_1$ is not \code{bool}. If $o_1$ is \TRUE, the result of evaluating $b$ is \TRUE, @@ -16845,8 +16841,9 @@ \subsection{Logical Boolean Expressions} Otherwise the result of evaluating $b$ is $o_2$. \LMHash{}% -Evaluation of a logical boolean expression $b$ of the form $e_1 \&\& e_2$ -causes the evaluation of $e_1$ producing an object $o_1$. +Evaluation of a logical boolean expression $b$ of the form +\code{$e_1$\,\,\&\&\,\,$e_2$} +causes the evaluation of $e_1$ to an object $o_1$. % This error can occur due to an implicit downcast from \DYNAMIC. It is a dynamic error if the run-time type of $o_1$ is not \code{bool}. If $o_1$ is \FALSE, the result of evaluating $b$ is \FALSE, @@ -16855,45 +16852,6 @@ \subsection{Logical Boolean Expressions} It is a dynamic error if the run-time type of $o_2$ is not \code{bool}. Otherwise the result of evaluating $b$ is $o_2$. -\LMHash{}% -A logical boolean expression $b$ of the form $e_1 \&\& e_2$ -shows that a local variable $v$ has type $T$ -if both of the following conditions hold: -\begin{itemize} -\item Either $e_1$ shows that $v$ has type $T$ - or $e_2$ shows that $v$ has type $T$. -\item $v$ is not mutated in $e_2$ or within a function - other than the one where $v$ is declared. -\end{itemize} - -\LMHash{}% -If $e_1$ shows that a local variable $v$ has type $T$, -then the type of $v$ is known to be $T$ in $e_2$, -unless any of the following are true: -\begin{itemize} -%% The first item here is unnecessary for soundness, -%% and is retained mainly for backwards compatibility. -%% If $e_1$ shows that $v$ has type $T$, then any assignment -%% in $e_1$ does not invalidate that. -%% Removing the line is visible in the semantics, though, because: -%% num x; -%% (x ??= 42) != null && x is int & x.toRadixString(16) != "" -%% is allowed without the line, and disallowed with. -%% At time of writing, the analyzer disallows the code. -\item $v$ is potentially mutated in $e_1$, -\item $v$ is potentially mutated in $e_2$, -\item $v$ is potentially mutated within a function other - than the one where $v$ is declared, or -\item $v$ is accessed by a function defined in $e_2$ and - $v$ is potentially mutated anywhere in the scope of $v$. -\end{itemize} - -\LMHash{}% -It is a \Error{compile-time error} if -the static type of $e_1$ may not be assigned to \code{bool} -or if the static type of $e_2$ may not be assigned to \code{bool}. -The static type of a logical boolean expression is \code{bool}. - \subsection{Equality} \LMLabel{equality} @@ -18907,21 +18865,26 @@ \subsection{If} \LMLabel{if} \LMHash{}% -The \Index{if statement} allows for conditional execution of statements. +The \Index{\IF{} statement} allows for conditional execution of statements. \begin{grammar} ::= \IF{} `(' `)' (\ELSE{} )? \end{grammar} \LMHash{}% -An if statement of the form -\code{\IF{} ($e$) $s_1$ \ELSE{} $s_2$} -where $s_1$ is not a block statement is equivalent to the statement -\code{\IF{} ($e$) \{$s_1$\} \ELSE{} $s_2$}. -An if statement of the form -\code{\IF{} ($e$) $s_1$ \ELSE{} $s_2$} -where $s_2$ is not a block statement is equivalent to the statement -\code{\IF{} ($e$) $s_1$ \ELSE{} \{$s_2$\}}. +An \IF{} statement of the form +\code{\IF\,\,($e$)\,\,$S$} +is treated as +(\ref{overview}) +\code{\IF\,\,($e$)\,\,$S$\,\,\ELSE\,\,\{\}}. +An \IF{} statement of the form +\code{\IF\,\,($e$)\,\,$S_1$\,\,\ELSE\,\,$S_2$} +where $S_1$ is not a block statement is treated as +\code{\IF\,\,($e$)\,\,\{\,$S_1$\,\}\,\,\ELSE\,\,$S_2$}. +An \IF{} statement of the form +\code{\IF\,\,($e$)\,\,$S_1$\,\,\ELSE\,\,$S_2$} +where $S_2$ is not a block statement is treated as +\code{\IF\,\,($e$)\,\,$S_1$\,\,\ELSE\,\,\{\,$S_2$\,\}}. \rationale{% The reason for this equivalence is to catch errors such as% @@ -18929,15 +18892,14 @@ \subsection{If} \begin{dartCode} \VOID{} main() \{ - \IF{} (somePredicate) - \VAR{} v = 2; + \IF{} (somePredicate) \VAR{} v = 2; print(v); \} \end{dartCode} \rationale{% Under reasonable scope rules such code is problematic. -If we assume that \code{v} is declared +If we specify that \code{v} is declared in the scope of the method \code{main()}, then when \code{somePredicate} evaluates to the \FALSE{} object, \code{v} will be uninitialized when accessed. @@ -18956,37 +18918,21 @@ \subsection{If} } \LMHash{}% -Execution of an if statement of the form -\code{\IF{} ($b$) $s_1$ \ELSE{} $s_2$} -where $s_1$ and $s_2$ are block statements, -proceeds as follows: +Consider an \IF{} statement of any of the forms mentioned above +(\commentary{the statement then starts with \code{\IF\,\,($e$)}}). +It is a \Error{compile-time error} if the type of the expression $e$ +is not assignable to \code{bool}. \LMHash{}% -First, the expression $b$ is evaluated to an object $o$. +Execution of an \IF{} statement of the form +\code{\IF\,\,($e$)\,\,$S_1$\,\,\ELSE\,\,$S_2$} +where $S_1$ and $S_2$ are block statements +proceeds as follows: +Evaluate the expression $e$ to an object $o$. % This error can occur due to an implicit downcast from \DYNAMIC. It is a dynamic error if the run-time type of $o$ is not \code{bool}. -If $o$ is \TRUE, then the block statement $s_1$ is executed, -otherwise the block statement $s_2$ is executed. - -\LMHash{}% -It is a \Error{compile-time error} if the type of the expression $b$ -may not be assigned to \code{bool}. - -\LMHash{}% -If $b$ shows that a local variable $v$ has type $T$, -then the type of $v$ is known to be $T$ in $s_1$, -unless any of the following are true -\begin{itemize} -\item $v$ is potentially mutated in $s_1$, -\item $v$ is potentially mutated within a function other - than the one where $v$ is declared, or -\item $v$ is accessed by a function defined in $s_1$ and - $v$ is potentially mutated anywhere in the scope of $v$. -\end{itemize} - -\LMHash{}% -An if statement of the form \code{\IF{} ($e$) $s$} is equivalent to -the if statement \code{\IF{} ($e$) $s$ \ELSE{} \{\}}. +If $o$ is \TRUE, then execute the block statement $S_1$, +otherwise execute the block statement $S_2$. \subsection{For} @@ -21863,34 +21809,6 @@ \subsection{Static Types} % objects in constructor args - these cannot represent parameterized types. -\subsubsection{Local Variable Type Promotion} -\LMLabel{typePromotion} - -\LMHash{}% -The static type system ascribes a static type to every expression. -In some cases, the type of a local variable -(\commentary{which can be a formal parameter}) -may be promoted from the declared type, based on control flow. - -\LMHash{}% -We say that a variable $v$ is known to have type $T$ -whenever we allow the type of $v$ to be promoted. -The exact circumstances when type promotion is allowed are given in -the relevant sections of the specification -(\ref{logicalBooleanExpressions}, \ref{conditional} and \ref{if}). - -\LMHash{}% -Type promotion for a variable $v$ is allowed only when we can deduce that -such promotion is valid based on an analysis of certain boolean expressions. -In such cases, we say that -the boolean expression $b$ shows that $v$ has type $T$. -As a rule, for all variables $v$ and types $T$, a boolean expression -does not show that $v$ has type $T$. -Those situations where an expression does show that a variable has a type are -mentioned explicitly in the relevant sections of this specification -(\ref{typeTest} and \ref{logicalBooleanExpressions}). - - \subsection{Dynamic Type System} \LMLabel{dynamicTypeSystem} From 5983ae2bc58a5e04c0f9cc139eea178bd9563a3e Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Wed, 1 Dec 2021 13:43:53 +0100 Subject: [PATCH 30/85] Integrate Null Promotion; put v==null rules into Type Promotion, and fill in enough commentary to make that section make sense --- specification/dartLangSpec.tex | 108 ++++++++++++++++++++++++++++----- 1 file changed, 92 insertions(+), 16 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 885d99b7a..8b2a80cbd 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -25002,15 +25002,6 @@ \subsection{Type Promotion} then $T$ is also a type of interest.% } -\LMHash{}% -The initial stack of types of interest for $v$ has exactly one element, -namely the declared type. -This is the stack of types of interest -for the declaring occurrence of the name of $v$ -(\commentary{% -that is, the very first time the variable is mentioned, \ref{variables}% -}). - \commentary{% The flow analysis will be specified in a future version of this specification. @@ -25020,9 +25011,21 @@ \subsection{Type Promotion} based on the same properties of locations that are immediate predecessors of $\ell$ in the control flow graph, -ending in the declaration of $v$.% +ending in the declaration of $v$. +At this point we just specify the initial step, +and the situations where promotion may occur.% } +\LMHash{}% +The initial stack of types of interest for $v$ has exactly one element, +namely the declared type +(\ref{localVariableDeclaration}). +This is the stack of types of interest +for the declaring occurrence of the name of $v$ +(\commentary{% +i.e., the very first time the variable is mentioned, \ref{variables}% +}). + \LMHash{}% If a local variable $v$ has an initializing expression and does not have the modifier \FINAL, @@ -25062,20 +25065,91 @@ \subsection{Type Promotion} Consider the situation where the declaration of a local variable $v$ is of the form \code{\LATE?\,\,\VAR\,\,$v$ = $e$;} -where the static type of $e$ is of the form \code{$X$\,\&\,$T$} +where the static type of $e$ is of the form \code{$X$\,\&\,\,$T$} where $X$ is a type variable. In this situation, the declared type of $v$ is \code{X} (\ref{localVariableDeclaration}), -but the type \code{$X$\,\&\,$T$} is added to the stack of types of interest, -and the type of $v$ is immediately promoted to \code{$X$\,\&\,$T$}. +but the type \code{$X$\,\&\,\,$T$} is added to the stack of types of interest, +and the type of $v$ is immediately promoted to \code{$X$\,\&\,\,$T$}. \commentary{% -Note that \code{$X$\,\&\,$T$} is not otherwise automatically a +Note that \code{$X$\,\&\,\,$T$} is not otherwise automatically a type of interest just because $X$ is a type of interest. In particular, if $w$ is declared as \code{\LATE?\,\,$X$\,\,$w$ = $e$;} -where the static type of $e$ is \code{$X$\,\&\,$T$}, -the type of $w$ is \emph{not} immediately promoted to \code{$X$\,\&\,$T$}.% +where the static type of $e$ is \code{$X$\,\&\,\,$T$}, +the type of $w$ is \emph{not} promoted to \code{$X$\,\&\,\,$T$}.% +} + +\LMHash{}% +We now mention the locations where promotion may occur +independently of the control flow. + +\commentary{% +We might say that these promotions are ``atomic'', +and all other promotions are derived from these, +and from the control flow.% +} + +\LMHash{}% +Let $\ell$ be a location, +and let $v$ be a local variable which is in scope at $\ell$. +Assume that $\ell$ occurs after the declaration of $v$. +The expressions that give rise to promotion or demotion +of the type of $v$ at $\ell$ +are the following: + +\begin{itemize} +\item A type test of the form \code{$v$\,\,\IS\,\,$T$}. +\item A type check of the form \code{$v$\,\,\AS\,\,$T$}. +\item An assignment of the form \code{$v$\,\,=\,\,$e$} + where the static type of $e$ is a type of interest for $v$ at $\ell$, + and similarly for compound assignments + (\ref{compoundAssignment}) + including \lit{??=}. +\item An equality test of the form \code{$v$\,\,==\,\,$e$} or + \code{$e$\,\,==\,\,$v$}, where $e$ is \NULL, + optionally wrapped in one or more parentheses, + and similarly for \lit{!=}. +\end{itemize} + +\LMHash{}% +In particular, +a check of the form \code{$v$\,\,==\,\,\NULL}, +\code{\NULL\,\,==\,\,$v$}, +or \code{$v$\,\,\IS\,\,Null} +where $v$ has type $T$ at $\ell$ +promotes the type of $v$ +to \code{Null} in the \TRUE{} continuation, +and to \NonNullType{$T$} in the \FALSE{} continuation. + +\LMHash{}% +%% TODO(eernst), for review: The null safety spec says that `T?` is +%% promoted to `T`, but implementations _do_ promote `X extends int?` to +%% `X & int`. So I specify that. +A check of the form \code{$v$\,\,!=\,\,\NULL}, +\code{\NULL\,\,!=\,\,$v$}, +or \code{$v$\,\,\IS\,\,$T$} +where $v$ has type $T$ at $\ell$ +promotes the type of $v$ +to \NonNullType{$T$} in the \TRUE{} continuation, +and to \code{Null} in the \FALSE{} continuation. + +\commentary{% +The resulting type of $v$ may be the obvious one, e.g., +\code{$v$\,\,=\,\,1} may promote $v$ to \code{int}, +but it may also give rise to a demotion +(changing the type of $v$ to a supertype of the type of $v$ at $\ell$), +and it may have no effect on the type of $v$ +(e.g., when the static type of $e$ is not a type of interest). +These details will be specified in a future version of this specification. + +Every non-trivial type of control flow determines the control flow graph, +and the control flow graph determines which of the above +may or must have occurred at $\ell$, +and hence gives rise to the stack of types of interest of $v$ at $\ell$, +which in turn determines the type of $v$ at $\ell$. +This part of the flow analysis will also be specified in the future.% } @@ -25642,6 +25716,8 @@ \subsubsection{Null promotion} \subsubsection{Null aware operator} \LMLabel{nullShorteningTransformation} +!!! + The semantics of the null aware operator \code{?.} are defined via a source to source translation of expressions into Dart code extended with a let binding construct. The translation is defined using meta-level functions over syntax. We use the From aa29b9bc1ea6b886a253116789f1f4af4e6dfc8f Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Thu, 2 Dec 2021 00:01:20 +0100 Subject: [PATCH 31/85] Null shorting --- specification/dartLangSpec.tex | 397 +++++++++++++++++++++++++++++++++ 1 file changed, 397 insertions(+) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 8b2a80cbd..1989e4aed 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -16087,6 +16087,403 @@ \subsubsection{Generic Method Instantiation} } +%% TODO(eernst), for review: The null safety spec uses 'shortening' and +%% 'shorting'; checking some dictionaries, it seems fine to use 'shorting'. +\subsection{Null Shorting} +\LMLabel{nullShorting} + +\LMHash{}% +The semantics of the null aware member access operator \lit{?.} +is defined in terms of a program transformation known as +\Index{null shorting}. +This program transformation eliminates all occurrences of \lit{?.} +by introducing \LET{} expressions +(\ref{notation}) +and equality comparisons with \NULL. +Null shorting is performed after the static analysis, +and after the transformation that eliminates cascades +(\ref{cascades}). + +\LMHash{}% +We use the phrase null-shorting as an adjective in order to +indicate the connections between null shorting and other concepts +(\commentary{ +e.g., the null-shorting translation plays an important role in null shorting% +}). + +\commentary{% +Let $e$ be an expression of the form +\code{$e_0\,\,s_1\,\,\ldots\,\,s_i\,\,s_{i+1}\,\,\ldots\,\,s_k$} +where $e_0$ is an expression derived from +\syntax{ *}, +$s_j$ is derived from \synt{selector} for $j \in 1 .. k$, +and the first token in $s_{i+1}$ is \lit{?.}. +The intuition is that null shorting ensures that +the execution of $e$ will skip $s_{i+1}\,\,\ldots\,\,s_k$ +in the case where $e_0\,\,s_1\,\,\ldots\,\,s_i$ evaluates to +the \NULL{} object. + +Note that cascades +(\ref{cascades}) +are included in this treatment, +because they are transformed into \LET{} expressions +containing expressions derived from \syntax{ *}.% +} + +\LMHash{}% +Null shorting is defined using meta-level functions over syntax. +We use the notation +\IndexCustom{\metaCode{Exp}}{Exp@\metaCode{Exp}} +to denote the meta-level type of terms derived from \synt{expression}, and +\IndexCustom{\metaCode{$T \rightarrow S$}}{S -> T@\metaCode{$T \rightarrow S$}} +to denote the meta-level type of functions mapping +terms of meta-level type \metaCode{$T$} to +terms of meta-level type \metaCode{$S$}. + +\LMHash{}% +We use the phrase +\IndexCustom{base-level}{null shorting!base-level} +to indicate that a term is Dart syntax, +as opposed to meta-level syntax. +\commentary{For example, \code{x\,\,+\,\,1}.} + +\LMHash{}% +\metaCode{fn[x:\,\,Exp]:\,\,Exp\,\,=>\,\,E} +defines a meta-level function of type +\metaCode{Exp\,\,$\rightarrow$\,\,Exp} +(\commentary{that is, a function from expressions to expressions}), +and +\metaCode{fn[k:\,\,Exp\,\,$\rightarrow$\,\,Exp]:\,\,Exp\,\,=>\,\,E} +defines a meta-level function of type +\metaCode{Exp\,\,$\rightarrow$\,\,Exp\,\,$\rightarrow$\,\,Exp}. +Where obvious from context, +we elide the parameter and return types on the meta-level functions. +The meta-variables \metaCode{F} and \metaCode{G} +range over meta-level functions. +Application of a meta-level function \metaCode{F} to an argument \metaCode{p} +is written as \metaCode{F[p]}. + +\LMHash{}% +Null shorting transforms an expression \metaCode{e} +(\commentary{whose meta-level type is \code{Exp}}) +into a meta-level function \metaCode{F} of type +\metaCode{(Exp\,\,$\rightarrow$\,\,Exp)\,\,$\rightarrow$\,\,Exp}, +which takes as an argument the continuation of \metaCode{e}, +and produces an expression semantically equivalent to \metaCode{e} +with all occurrences of \metaCode{?.} eliminated +in favor of explicit sequencing using \LET{} expressions. + +\LMHash{}% +Let +\IndexCustom{\metaCode{ID}}{null shorting!\metaCode{ID}} +be the identity function +\metaCode{fn[x:\,\,Exp]:\,\,Exp\,\,=>\,\,x}. + +\LMHash{}% +The +\IndexCustom{expression translation}{null shorting!expression translation} +of an expression \metaCode{e} is +the result of applying the null-shorting translation +of \metaCode{e} to \metaCode{ID}. +That is, if \metaCode{e} translates to \metaCode{F}, +then \metaCode{F[ID]} is the expression translation of \metaCode{e}. + +\LMHash{}% +We use +\IndexCustom{\metaCode{EXP(e)}}{null shorting!\metaCode{EXP(e)}} +as a shorthand for +the expression translation of \metaCode{e}. +That is, if the null-shorting translation of \metaCode{e} is \metaCode{F}, +then \metaCode{EXP(e)} is \metaCode{F[ID]}. + +\LMHash{}% +We extend the expression translation to argument lists in the obvious way, +using +\IndexCustom{\metaCode{ARGS(args)}}{null shorting!\metaCode{ARGS(args)}} +to denote the result of applying the expression translation pointwise +to the arguments in the argument list \metaCode{args}. + +\LMHash{}% +We use three combinators to express the translation. + +\LMHash{}% +The null-shorting combinator +\IndexCustom{\metaCode{SHORT}}{null shorting!\metaCode{SHORT}} +is defined as follows: + +{ +\def\Base#1{\textcolor{normativeColor}{#1}} +\def\Meta#1{\textcolor{metaColor}{#1}} +\begin{metaLevelCode} +SHORT = fn[r: Exp, c: Exp $\rightarrow$ Exp] => + fn[k: Exp $\rightarrow$ Exp]: Exp => + \Base{\LET\,\,$v$\,\,=\,\,\Meta{r}\,\,\IN\,\,% +$v$\,\,==\,\,\NULL\,\,?\,\,\NULL\,\,:}\,\,k[c[\Base{$v$}]] +\end{metaLevelCode} +} + +\noindent +where $v$ is a fresh base-level variable. +The \metaCode{SHORT} combinator is used +to give semantics to uses of the \code{?.}\ operator. +It is parameterized over the receiver of the conditional property access +(\metaCode{r}) +and a meta-level function +(\metaCode{c}) +which given a fresh base-level variable ($v$) +bound to the result of evaluating the receiver, +produces the final expression. +The result is parameterized over the continuation of +the expression being translated. +The continuation is only called in the case that +the result of evaluating the receiver is non-null. + +\LMHash{}% +The null-shorting combinator +\IndexCustom{\metaCode{PASSTHRU}}{null shorting!\metaCode{PASSTHRU}} +is defined as follows: + +\begin{metaLevelCode} +PASSTHRU = fn[F: (Exp $\rightarrow$ Exp) $\rightarrow$ Exp, % +c: Exp $\rightarrow$ Exp] => + fn[k: Exp $\rightarrow$ Exp]: Exp => + F[fn[x] => k[c[x]]] +\end{metaLevelCode} + +\LMHash{}% +The \metaCode{PASSTHRU} combinator is used to give semantics to +expression forms which propagate null-shorting behavior. +It is parameterized +over the translation \metaCode{F} of +the potentially null-shorting expression, +and over a meta-level function \metaCode{c} +which given an expression which denotes the value of the translated +null-shorting expression produces the final expression being translated. +The result is parameterized over the continuation of +the expression being translated, +which is called unconditionally. + +\LMHash{}% +The null-shorting termination combinator +\IndexCustom{\metaCode{TERM}}{null shorting!\metaCode{TERM}} +is defined as follows: + +\begin{metaLevelCode} +TERM = fn[r: Exp] => fn[k: Exp $\rightarrow$ Exp]: Exp => k[r] +\end{metaLevelCode} + +\LMHash{}% +The \metaCode{TERM} combinator is used to give semantics to +expressions which neither short-circuit nor propagate null-shorting behavior. +It is parameterized over the translated expression, +and simply passes on the expression to its continuation. + +\LMHash{}% +The program transformation \Index{null shorting} is defined as follows, +where \metavar{typeArgs} and \metavar{args} are derived from +\synt{typeArguments} respectively \synt{arguments}. + +{ %% Create a local scope and define commands to control text color. +\def\Base#1{\textcolor{normativeColor}{#1}} +\def\Meta#1{\textcolor{metaColor}{#1}} +% +\LMHash{}% +\Case{\id} +An identifier \id{} translates to \metaCode{TERM[\Base{\id}]}. +\EndCase + +\LMHash{}% +\Case{\code{$e$?.\id}} +Assume that $e$ is derived from \syntax{ *}. +The conditional property access \code{$e$?.\id} then translates to\\ +\metaCode{SHORT[EXP(\Base{$e$}), fn[x] => x\Base{.\id}]}. +\EndCase + +\LMHash{}% +\Case{\code{$e$.\id}} +Assume that $e$ is derived from \syntax{ *}. +If $e$ translates to \Meta{$F$} then \code{$e$.\id} translates to +\metaCode{PASSTHRU[$F$, fn[x] => x\Base{.\id}]}. +\EndCase + +\LMHash{}% +\Case{\code{$e$?.$m$<\ldots>(\ldots)}} +Assume that $e$ is derived from \syntax{ *}. +The null aware method invocation +\code{$e$?.$m$<\metavar{typeArgs}>(\metavar{args})} then translates to +\metaCode{SHORT[EXP(\Base{$e$}), fn[x] => % + x\Base{.$m$<\metavar{typeArgs}>(\Meta{ARGS(\Base{\metavar{args}})})}]}. +\commentary{% +A non-generic invocation is included as +the case where \metavar{typeArgs} is empty (and \code{<>} is omitted), +which is also true for the cases below.% +} +\EndCase + +\LMHash{}% +\Case{\code{$e$.$m$<\ldots>(\ldots)}} +Assume that $e$ is derived from \syntax{ *}. +If $e$ translates to \Meta{$F$} then +\code{$e$.$m$<\metavar{typeArgs}>(\metavar{args})} +translates to\\ +\metaCode{PASSTHRU[$F$, fn[x] => % + x\Base{.$m$<\metavar{typeArgs}>(\Meta{ARGS(\Base{\metavar{args}})})}]}. +\EndCase + +\LMHash{}% +\Case{\code{$e$<\ldots>(\ldots)}} +Assume that $e$ is derived from \syntax{ *}. +If $e$ translates to \Meta{$F$} then +\code{$e$<\metavar{typeArgs}>(\metavar{args})} +translates to\\ +\metaCode{PASSTHRU[$F$, fn[x] => % + x\Base{<\metavar{typeArgs}>(\Meta{ARGS(\Base{\metavar{args}})})}]}. +\EndCase + +\LMHash{}% +\Case{\code{$e_1$?[$e_2$]}} +Assume that $e$ is derived from \syntax{ *}, +and $e_2$ is an expression. +If $e_1$ translates to \Meta{$F$} then \code{$e_1$?[$e_2$]} +translates to\\ +\metaCode{SHORT[EXP(\Base{$e_1$}), fn[x] => % + x\Base{[\Meta{EXP(\Base{$e_2$})}]}]}. +\EndCase + +\LMHash{}% +\Case{\code{$e_1$[$e_2$]}} +Assume that $e_1$ is derived from \syntax{ *}, +and $e_2$ is an expression. +If $e_1$ translates to \Meta{$F$} then \code{$e_1$[$e_2$]} +translates to\\ +\metaCode{PASSTHRU[$F$, fn[x] => x\Base{[\Meta{EXP(\Base{$e_2$})}]}]}. +\EndCase + +\LMHash{}% +\Case{\code{$e$!}} +Assume that $e$ is derived from \syntax{ *}. +If $e$ translates to \Meta{$F$} then \code{$e$!} +translates to +\metaCode{PASSTHRU[$F$, fn[x] => x\Base{!}]}. +\EndCase + +\LMHash{}% +\Case{\code{$e_1$?.\id\,\,=\,\,$e_2$}} +Assume that $e_1$ is derived from \syntax{ *}, +and $e_2$ is an expression. +The assignment \code{$e_1$?.\id\,\,=\,\,$e_2$} +translates to\\ +\metaCode{SHORT[EXP(\Base{$e_1$}), fn[x] => % + x\Base{.\id\,\,=\,\,\Meta{EXP(\Base{$e_2$})}}]}. +\EndCase + +\LMHash{}% +\Case{\code{$e_1$?.\id\,\,$\diamond$=\,\,$e_2$}} +The other assignment operators are handled equivalently. +\EndCase + +\LMHash{}% +\Case{\code{$e_1$.\id\,\,=\,\,$e_2$}} +Assume that $e_1$ is derived from \syntax{ *}, +and $e_2$ is an expression. +If $e_1$ translates to \Base{$F$} then \code{$e_1$.\id\,\,=\,\,$e_2$} +translates to\\ +\metaCode{PASSTHRU[$F$, fn[x] => x\Base{.\id\,\,= \Meta{EXP(\Base{$e_2$})}}]}. +\EndCase + +\LMHash{}% +\Case{\code{$e_1$.\id\,\,$\diamond$=\,\,$e_2$}} +The other assignment operators are handled equivalently. +\EndCase + +\LMHash{}% +\Case{\code{$e_1$?[$e_2$] = $e_3$}} +Assume that $e_1$ is derived from \syntax{ *}, +and $e_2$ and $e_3$ are expressions. +If $e_1$ translates to \Meta{$F$} then \code{$e_1$?[$e_2$] = $e_3$} +translates to +\metaCode{SHORT[EXP(\Base{$e_1$}), fn[x] => % + x\Base{[}EXP(\Base{$e_2$})\Base{] =} EXP(\Base{$e_3$})]}. +\EndCase + +\LMHash{}% +\Case{\code{$e_1$?[$e_2$] $\diamond$= $e_3$}} +The other assignment operators are handled equivalently. +\EndCase + +\LMHash{}% +\Case{\code{$e_1$[$e_2$] = $e_3$}} +Assume that $e_1$ is derived from \syntax{ *}, +and $e_2$ and $e_3$ are expressions. +If $e_1$ translates to \Meta{$F$} then \code{$e_1$[$e_2$] = $e_3$} +translates to +\metaCode{PASSTHRU[$F$, fn[x] => x\Base{[\Meta{EXP(\Base{$e_2$})}] % +=} EXP(\Base{$e_3$})]}. +\EndCase + +\LMHash{}% +\Case{\code{$e_1$[$e_2$] $\diamond$= $e_3$}} +The other assignment operators are handled equivalently. +\EndCase + +\LMHash{}% +\Case{Other} +All other expressions are translated compositionally using +the \metaCode{TERM} combinator. \commentary{For example:} + +\commentary{% + \begin{itemize} + \item + A list literal \Base{\code{[\,$e_1$, \ldots, $e_n$\,]}} + translates to\\ + \metaCode{TERM[\Base{[\,% + \Meta{EXP(\Base{$e_1$}), \ldots, EXP(\Base{$e_n$})}\,]}]}. + \item + A parenthesized expression \Base{\code{($e$)}} translates to + \metaCode{TERM[\Base{(\Meta{EXP(\Base{$e$})})}]}. + \end{itemize}% +} +% Reduce whitespace after itemized list: This is just an end symbol. +\vspace{-\baselineskip}\EndCase +% +} %% End of local scope defining \Base and \Meta. + +\LMHash{}% +An invocation of any of several operators is considered equivalent to +a member access +(\ref{notation}). +This applies to relational expressions, bitwise expressions, +shift expressions, additive expressions, multiplicative expressions, +and unary expressions. +} + +\commentary{% +For example, \code{$a$ + $b$} is specified as equivalent to +\code{$a$.plus($b$)}, +where \code{plus} is assumed to be +a method with the same behavior as \code{+}. +Similarly, \code{-$e$} is equivalent to \code{$e$.unaryMinus()}.% +} + +\LMHash{}% +This equivalence is \emph{not} applicable in the above rules. +Hence, operators not mentioned specifically in a rule are handled +in the case for 'other' expressions, +not in the case for \code{$e$.$m$<\metavar{typeArgs}>(\metavar{args})}. + +\commentary{% +This means that null shorting stops at operators. +For instance, \code{$e$?.\id\,\,+\,\,$b$} is +not an expression where \code{.\id} and \code{+\,\,$b$} will be skipped +if \code{$e$} is null\,---\,it is +a compile-time error, because \code{$e$?.\id} can be null, +and operator \lit{+} is not a member of +the interface of a potentially nullable type. +Similarly, both \code{-$e$?.\id} and \code{\gtilde{}$e$?.\id} are errors, +because \code{$e$?.\id} can be null.% +} + + \subsection{Assignment} \LMLabel{assignment} From 8b283a9e80abec0905b64505318d7471be791010 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Thu, 2 Dec 2021 00:34:23 +0100 Subject: [PATCH 32/85] Small fixes in section Null Shorting --- specification/dartLangSpec.tex | 54 ++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 1989e4aed..39af495ef 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -16096,8 +16096,9 @@ \subsection{Null Shorting} The semantics of the null aware member access operator \lit{?.} is defined in terms of a program transformation known as \Index{null shorting}. -This program transformation eliminates all occurrences of \lit{?.} -by introducing \LET{} expressions +This program transformation transforms expressions +and leaves other program elements unchanged. +It eliminates all occurrences of \lit{?.} by introducing \LET{} expressions (\ref{notation}) and equality comparisons with \NULL. Null shorting is performed after the static analysis, @@ -16107,8 +16108,8 @@ \subsection{Null Shorting} \LMHash{}% We use the phrase null-shorting as an adjective in order to indicate the connections between null shorting and other concepts -(\commentary{ -e.g., the null-shorting translation plays an important role in null shorting% +(\commentary{% +e.g., ``the null-shorting translation is used during null shorting''% }). \commentary{% @@ -16145,7 +16146,7 @@ \subsection{Null Shorting} \IndexCustom{base-level}{null shorting!base-level} to indicate that a term is Dart syntax, as opposed to meta-level syntax. -\commentary{For example, \code{x\,\,+\,\,1}.} +\commentary{For example, \code{x\,\,+\,\,1} is a base-level expression.} \LMHash{}% \metaCode{fn[x:\,\,Exp]:\,\,Exp\,\,=>\,\,E} @@ -16155,7 +16156,7 @@ \subsection{Null Shorting} and \metaCode{fn[k:\,\,Exp\,\,$\rightarrow$\,\,Exp]:\,\,Exp\,\,=>\,\,E} defines a meta-level function of type -\metaCode{Exp\,\,$\rightarrow$\,\,Exp\,\,$\rightarrow$\,\,Exp}. +\metaCode{(Exp\,\,$\rightarrow$\,\,Exp)\,\,$\rightarrow$\,\,Exp}. Where obvious from context, we elide the parameter and return types on the meta-level functions. The meta-variables \metaCode{F} and \metaCode{G} @@ -16164,12 +16165,11 @@ \subsection{Null Shorting} is written as \metaCode{F[p]}. \LMHash{}% -Null shorting transforms an expression \metaCode{e} -(\commentary{whose meta-level type is \code{Exp}}) +Null shorting transforms an expression $e$ into a meta-level function \metaCode{F} of type \metaCode{(Exp\,\,$\rightarrow$\,\,Exp)\,\,$\rightarrow$\,\,Exp}, -which takes as an argument the continuation of \metaCode{e}, -and produces an expression semantically equivalent to \metaCode{e} +which takes as an argument the continuation of $e$, +and produces an expression semantically equivalent to $e$ with all occurrences of \metaCode{?.} eliminated in favor of explicit sequencing using \LET{} expressions. @@ -16181,27 +16181,31 @@ \subsection{Null Shorting} \LMHash{}% The -\IndexCustom{expression translation}{null shorting!expression translation} -of an expression \metaCode{e} is -the result of applying the null-shorting translation -of \metaCode{e} to \metaCode{ID}. -That is, if \metaCode{e} translates to \metaCode{F}, -then \metaCode{F[ID]} is the expression translation of \metaCode{e}. +\IndexCustom{null-shorting expression translation}{% + null shorting!expression translation} +of an expression $e$ +(in this section abbreviated as the \NoIndex{expression translation} of $e$) +is the result of applying the null-shorting translation (defined below) +of $e$ to \metaCode{ID}. +That is, if $e$ translates to \metaCode{F}, +then \metaCode{F[ID]} is the expression translation of $e$. \LMHash{}% We use -\IndexCustom{\metaCode{EXP(e)}}{null shorting!\metaCode{EXP(e)}} +\IndexCustom{\metaCode{EXP(\textcolor{normativeColor}{$e$})}}{% + null shorting!\metaCode{EXP(\textcolor{normativeColor}{$e$})}} as a shorthand for -the expression translation of \metaCode{e}. -That is, if the null-shorting translation of \metaCode{e} is \metaCode{F}, -then \metaCode{EXP(e)} is \metaCode{F[ID]}. +the expression translation of $e$. +That is, if the null-shorting translation of $e$ is \metaCode{F}, +then \metaCode{EXP(\textcolor{normativeColor}{$e$})} is \metaCode{F[ID]}. \LMHash{}% We extend the expression translation to argument lists in the obvious way, using -\IndexCustom{\metaCode{ARGS(args)}}{null shorting!\metaCode{ARGS(args)}} +\IndexCustom{\metaCode{ARGS(\textcolor{normativeColor}{\metavar{args}})}}{% + null shorting!\metaCode{ARGS(\textcolor{normativeColor}{\metavar{args}})}} to denote the result of applying the expression translation pointwise -to the arguments in the argument list \metaCode{args}. +to the arguments in the argument list \metavar{args}. \LMHash{}% We use three combinators to express the translation. @@ -16234,7 +16238,7 @@ \subsection{Null Shorting} bound to the result of evaluating the receiver, produces the final expression. The result is parameterized over the continuation of -the expression being translated. +the expression being translated (\metaCode{k}). The continuation is only called in the case that the result of evaluating the receiver is non-null. @@ -16434,8 +16438,8 @@ \subsection{Null Shorting} \commentary{% \begin{itemize} \item - A list literal \Base{\code{[\,$e_1$, \ldots, $e_n$\,]}} - translates to\\ + A list literal \Base{\code{[\List{e}{1}{n}]}} + translates to \metaCode{TERM[\Base{[\,% \Meta{EXP(\Base{$e_1$}), \ldots, EXP(\Base{$e_n$})}\,]}]}. \item From d07437d104229d35d6e11bbcacbaa2ef9418e4e2 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Thu, 2 Dec 2021 16:07:58 +0100 Subject: [PATCH 33/85] Revisiting all occurrences of \NULL/Null/null --- specification/dartLangSpec.tex | 312 +++++++++++++++------------------ 1 file changed, 138 insertions(+), 174 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 39af495ef..2b50d5919 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -4109,11 +4109,13 @@ \subsection{Constructors} since the constructor is not in any scope used for resolving identifiers.% } +%% TODO(eernst), for review: With null safety, do not have a name for the +%% class that induces the top type. We could introduce \code{Any} and say +%% that both \code{Object} and \code{Null} are subclasses thereof. \LMHash{}% If{}f no constructor is specified for a class $C$, it implicitly has a default constructor \code{C():\ \SUPER()\ \{\}}, -unless $C$ is the built-in class \code{Object}. -%% TODO(eernst): With null safety, add `or \code{Null}`. +unless $C$ is one of the built-in classes \code{Object} or \code{Null}. \subsubsection{Generative Constructors} @@ -4583,11 +4585,17 @@ \subsubsection{Generative Constructors} } \LMHash{}% -Then if any instance variable of $i$ declared -by the immediately enclosing class or enum +Then if any non-\LATE{} instance variable of $i$ +declared by the immediately enclosing class is not yet bound to an object, all such variables are initialized with the null object (\ref{null}). +\commentary{% +Note that compile-time errors enforce the property that +any such variable has a nullable type, +and hence it can be bound to the null object.% +} + \LMHash{}% Then, unless the enclosing class is \code{Object}, the explicitly specified or implicitly added superinitializer (\ref{initializerLists}) is executed to @@ -4701,16 +4709,11 @@ \subsubsection{Factories} \LMHash{}% % This error can occur due to an implicit cast. -It is a dynamic type error if a factory returns a non-null object +It is a dynamic type error if a factory returns an object whose type is not a subtype of its actual (\ref{actualTypes}) return type. -\rationale{% -It seems useless to allow a factory to return the null object (\ref{null}). -But it is more uniform to allow it, as the rules currently do.% -} - \rationale{% Factories address classic weaknesses associated with constructors in other languages. @@ -7137,6 +7140,7 @@ \subsection{Extension Method Closurization} Evaluate $e_1$ to an object $o$. Let $u$ be a fresh final variable bound to $o$. Then $e$ evaluates to a function object which is equivalent to: + \begin{itemize} \item \begin{normativeDartCode} @@ -7145,23 +7149,24 @@ \subsection{Extension Method Closurization} \quad$E$<\List{S}{1}{m}>($u$) \quad.\id<\List{X}{1}{s}>($\List{p}{1}{n},\ p_{n+1}$: $p_{n+1}, \ldots,\ p_{n+k}$: $p_{n+k}$); \end{normativeDartCode} -where \id{} declares type parameters -\TypeParametersStd, -required parameters \List{p}{1}{n}, -and named parameters \List{p}{n+1}{n+k} with defaults \List{d}{1}{k}, -using \code{null} for parameters whose default value is not specified. + where \id{} declares type parameters + \TypeParametersStd, + required parameters \List{p}{1}{n}, + and named parameters \List{p}{n+1}{n+k} with defaults \List{d}{1}{k}, + using \NULL{} for parameters whose default value is not specified. + \item \begin{normativeDartCode} <\TypeParameters{X}{B'}{s}> ($\PairList{T}{p}{1}{n},\ $[$T_{n+1}\ p_{n+1} = d_1, \ldots,\ T_{n+k}\ p_{n+k} = d_k$]) => \quad$E$<\List{S}{1}{m}>($u$).\id<\List{X}{1}{s}>(\List{p}{1}{n+k}); \end{normativeDartCode} -where \id{} declares type parameters -\TypeParametersStd, -required parameters \List{p}{1}{n}, -and optional positional parameters -\List{p}{n+1}{n+k} with defaults \List{d}{1}{k}, -using \code{null} for parameters whose default value is not specified. + where \id{} declares type parameters + \TypeParametersStd, + required parameters \List{p}{1}{n}, + and optional positional parameters + \List{p}{n+1}{n+k} with defaults \List{d}{1}{k}, + using \NULL{} for parameters whose default value is not specified. \end{itemize} \LMHash{}% @@ -8617,12 +8622,17 @@ \subsection{Object Identity} \LMHash{}% The predefined Dart function \code{identical()} is defined such that \code{identical($c_1$, $c_2$)} if{}f: + \begin{itemize} -\item $c_1$ evaluates to either the null object (\ref{null}) +\item + $c_1$ evaluates to either the null object (\ref{null}) or an instance of \code{bool} and \code{$c_1$ == $c_2$}, OR -\item $c_1$ and $c_2$ are instances of \code{int} and \code{$c_1$ == $c_2$}, OR -\item $c_1$ and $c_2$ are constant strings and \code{$c_1$ == $c_2$}, OR -\item $c_1$ and $c_2$ are instances of \code{double} +\item + $c_1$ and $c_2$ are instances of \code{int} and \code{$c_1$ == $c_2$}, OR +\item + $c_1$ and $c_2$ are constant strings and \code{$c_1$ == $c_2$}, OR +\item + $c_1$ and $c_2$ are instances of \code{double} and one of the following holds: \begin{itemize} \item $c_1$ and $c_2$ are non-zero and \code{$c_1$ == $c_2$}. @@ -8632,14 +8642,18 @@ \subsection{Object Identity} with the same underlying bit pattern. \end{itemize} OR -\item $c_1$ and $c_2$ are constant lists that are defined to be identical +\item + $c_1$ and $c_2$ are constant lists that are defined to be identical in the specification of literal list expressions (\ref{lists}), OR -\item $c_1$ and $c_2$ are constant maps that are defined to be identical +\item + $c_1$ and $c_2$ are constant maps that are defined to be identical in the specification of literal map expressions (\ref{maps}), OR -\item $c_1$ and $c_2$ are constant objects of the same class $C$ +\item + $c_1$ and $c_2$ are constant objects of the same class $C$ and the value of each instance variable of $c_1$ is identical to the value of the corresponding instance variable of $c_2$. OR -\item $c_1$ and $c_2$ are the same object. +\item + $c_1$ and $c_2$ are the same object. \end{itemize} \commentary{% @@ -9404,12 +9418,13 @@ \subsection{Numbers} is either a hexadecimal integer literal or a decimal integer literal. \LMHash{}% -%% TODO(eernst): Clarify during review: The null safety spec had this: "The +%% TODO(eernst), for review: The null safety spec had this: "The %% implicit conversion of integer literals to double literals is performed %% when the context type is \code{double} or \code{double?}", which is %% included with the rules below. However, using that rule directly would -%% rule out `FutureOr x = 1;`, which is allowed by the rule below, and -%% by current implementations. +%% rule out `FutureOr x = 1;`, which is allowed by the rule below, +%% and by current implementations, so we may need a breaking change process +%% if we insist that `FutureOr x = 1;` must be a compile-time error. Let $l$ be an integer literal that is not the operand of a unary minus operator, and let $T$ be the context type of $l$. @@ -9507,7 +9522,7 @@ \subsection{Booleans} \commentary{% We use the notation `the \TRUE{} object' and `the \FALSE{} object' -rather than a plain `the true object` and `the false object` +rather than a plain `the true object' and `the false object' (as opposed to \NULL{} which evaluates to `the null object', not `the \NULL{} object') because phrases like `the false object' are too easy to misunderstand.% @@ -9912,12 +9927,14 @@ \subsubsection{String Interpolation} (where any of $s_0, \ldots, s_n$ can be empty) is evaluated by evaluating each expression $e_i$ ($1 \le i \le n$) into a string $r_i$ in the order they occur in the source text, as follows: + \begin{itemize} \item Evaluate $e_i$ to an object $o_i$. \item Invoke the \code{toString} method on $o_i$ with no arguments, and let $r_i$ be the returned object. -\item If $r_i$ is the null object, a dynamic error occurs. \end{itemize} + +\LMHash{}% Finally, the result of the evaluation of $s$ is the concatenation of the strings $s_0$, $r_1$, \ldots, $r_n$, and $s_n$. @@ -11268,6 +11285,7 @@ \subsubsection{Set and Map Literal Inference} the inferred element type of $\ell$ is the type argument of $S$ at \code{Iterable}. + %% TODO(eernst): Come inference, update terminology/notation. \commentary{% This is the result of constraint matching for $X$ using the constraint $S\,\,<:\,\,\code{Iterable<$X$>}$. @@ -12007,7 +12025,7 @@ \subsection{Throw} } \LMHash{}% -If $v$ is the null object (\ref{null}), then a \code{NullThrownError} is thrown. +If $v$ is the null object (\ref{null}), then a dynamic error occurs. Otherwise let $t$ be a stack trace corresponding to the current execution state, and the \THROW{} statement throws with $v$ as exception object and $t$ as stack trace (\ref{expressionEvaluation}). @@ -12672,15 +12690,18 @@ \subsubsection{New} the bindings that resulted from the evaluation of the argument list, and with the type parameters, if any, of $q$ bound to the actual type arguments $u_1, \ldots, u_m$. -If this execution returns an object +If this execution returns with an object $o$ (\ref{statementCompletion}) -then $e$ evaluates to the returned object. -Otherwise, if the execution completes normally or returns with no object, -then $e$ evaluates to the null object (\ref{null}). -Otherwise the execution throws an exception $x$ and stack trace $t$, +then $e$ evaluates to $o$. +Otherwise, the execution throws an exception $x$ and stack trace $t$, and then evaluation of $e$ also throws $x$ and $t$ (\ref{expressionEvaluation}). +\commentary{% +Note that the execution cannot complete normally or return without an object, +because that would give rise to a compile-time error.% +} + \rationale{% A factory constructor can be declared in an abstract class and used safely, as it will either produce a valid instance or throw.% @@ -13019,19 +13040,22 @@ \subsection{Function Invocation} an iterator $j$ from the iterable and calling \code{moveNext()}, execution of the body of $f$ will begin. When execution of the body of $f$ completes (\ref{statementCompletion}), + \begin{itemize} -\item If it returns without an object or it completes normally +\item + If it returns without an object or it completes normally (\ref{statementCompletion}), - $j$ is positioned after its last element, - so that its current value is the null object - (\ref{null}) - and the current call to \code{moveNext()} on $j$ returns the \FALSE{} object, + $j$ is positioned after its last element. + The current invocation of \code{moveNext()} on $j$ + returns the \FALSE{} object, as must all further calls. -\item If it throws an exception object $e$ and stack trace $t$ - then the current value of $j$ is the null object - (\ref{null}) - and the current call to \code{moveNext()} throws $e$ and $t$ as well. - Further calls to \code{moveNext()} must return the \FALSE{} object. + In this situation, the outcome of invoking \code{current} is undefined + (\commentary{e.g., it could throw or return a default value}). +\item + If it throws an exception object $e$ and stack trace $t$ then + the current invocation of \code{moveNext()} throws $e$ and $t$ as well. + In this situation the outcome of invoking \code{current} on $j$ is undefined. + Subsequent calls to \code{moveNext()} must return the \FALSE{} object. \end{itemize} \LMHash{}% @@ -13088,6 +13112,7 @@ \subsection{Function Invocation} $o$ is completed with the error $e$ and stack trace $t$. If execution of the body throws before the body suspends the first time, completion of $o$ happens at some future time after the invocation has returned. + \rationale{% The caller needs time to set up error handling for the returned future, so the future is not completed with an error @@ -13582,16 +13607,12 @@ \subsubsection{Binding Actuals to Formals} of the $i$th type argument of $f$, for actual type arguments $t_1, \ldots, t_r$. % Check the types of positional arguments. % This error can occur due to implicit casts, covariance, and dynamic calls. -It is a dynamic type error if $o_i$ is not the null object (\ref{null}) -and the actual type -(\ref{actualTypes}) -of $p_i$ is not a supertype of the dynamic type of $o_i, i \in 1 .. m$. +It is a dynamic type error if the actual type of $p_i$ is not +a supertype of the dynamic type of $o_i, i \in 1 .. m$. % Check the types of named arguments. % This error can occur due to implicit casts, covariance, and dynamic calls. -It is a dynamic type error if $o_j$ is -not the null object and the actual type -(\ref{actualTypes}) -of $q_j$ is not a supertype of the dynamic type of $o_j, j \in m + 1 .. m + l$. +It is a dynamic type error if the actual type of $q_j$ is not +a supertype of the dynamic type of $o_j, j \in m + 1 .. m + l$. \subsubsection{Unqualified Invocation} @@ -13914,7 +13935,7 @@ \subsection{Function Closurization} the function type that we can read off of the declaration of $f$ because it may need to be a specific internal platform defined class, but $C$ does not have the freedom to be a subtype of -a different and more special function type, and it cannot be \code{Null}.% +a different and more special function type.% } \LMHash{}% @@ -14057,8 +14078,6 @@ \subsection{Generic Function Instantiation} \LMHash{}% Execution of $f$ proceeds as follows. Evaluate $f$ to an object $o$. -%% TODO(eernst): Come null-safety, remove the next line. -A dynamic error occurs if $o$ is the null object. Let \RawFunctionType{S_0}{Y}{B'}{s}{$q$} be the dynamic type of $o$ (\commentary{by soundness, this type is a subtype of $G$}). $f$ then evaluates to a function object $o'$ with dynamic type @@ -14599,9 +14618,9 @@ \subsubsection{Ordinary Invocation} \LMHash{}% The dynamic semantics of $i$ is determined by the semantics of other constructs, -because all conditional ordinary method invocations are eliminated by the -null-shortening transformation -(\ref{nullShorteningTransformation}). +because all conditional ordinary method invocations are eliminated during +the null-shorting transformation +(\ref{nullShorting}). \EndCase \LMHash{}% @@ -15275,9 +15294,9 @@ \subsection{Property Extraction} \LMHash{}% The dynamic semantics of $i$ is determined by the semantics of other constructs, -because all conditional property extraction expressions are eliminated by the -null-shortening transformation -(\ref{nullShorteningTransformation}). +because all conditional property extraction expressions are eliminated during +the null shorting transformation +(\ref{nullShorting}). \EndCase \LMHash{}% @@ -15570,38 +15589,33 @@ \subsubsection{Instance Method Closurization} The \Index{closurization of method} $f$ on object $o$ is defined to be equivalent (\commentary{except for equality, as noted below}) to: + \begin{itemize} -%\item $(a) \{\RETURN{}$ $u$ $op$ $a;$\} if $f$ is named $op$ -% and $op$ is one of \code{<, >, <=, >=, ==, -, +, /, \gtilde/, *, \%, $|$, -% \^{}, \&, $<<$, $>>$} (this precludes closurization of unary -). -%\item $() \{\RETURN{}$ \gtilde{} $u;$\} if $f$ is named \gtilde. -%\item $(a) \{\RETURN{}$ $u[a];$\} if $f$ is named \code{[]}. -%\item $(a, b) \{\RETURN{}$ $u[a] = b;$\} if $f$ is named \code{[]=}. \item \begin{normativeDartCode} <\TypeParameters{X}{B'}{s}> ($\PairList{T}{p}{1}{n},\ $\{$T_{n+1}\ p_{n+1} = d_1, \ldots,\ T_{n+k}\ p_{n+k} = d_k$\}) => \quad$u$.$m$<\List{X}{1}{s}>(\ArgumentList{p}{n}{p}{k}); \end{normativeDartCode} -where $f$ is an instance method named $m$ -which has type parameter declarations -\TypeParametersStd, -required parameters \List{p}{1}{n}, -and named parameters \List{p}{n+1}{n+k} with defaults \List{d}{1}{k}, -using \code{null} for parameters whose default value is not specified. + where $f$ is an instance method named $m$ + which has type parameter declarations + \TypeParametersStd, + required parameters \List{p}{1}{n}, + and named parameters \List{p}{n+1}{n+k} with defaults \List{d}{1}{k}, + using \NULL{} for parameters whose default value is not specified. \item \begin{normativeDartCode} <\TypeParameters{X}{B'}{s}> ($\PairList{T}{p}{1}{n},\ $[$T_{n+1}\ p_{n+1} = d_1, \ldots,\ T_{n+k}\ p_{n+k} = d_k$]) => \quad$u$.$m$<\List{X}{1}{s}>(\List{p}{1}{n+k}); \end{normativeDartCode} -where $f$ is an instance method named $m$ -which has type parameter declarations -\TypeParametersStd, -required parameters \List{p}{1}{n}, -and optional positional parameters -\List{p}{n+1}{n+k} with defaults \List{d}{1}{k}, -using \code{null} for parameters whose default value is not specified. + where $f$ is an instance method named $m$ + which has type parameter declarations + \TypeParametersStd, + required parameters \List{p}{1}{n}, + and optional positional parameters + \List{p}{n+1}{n+k} with defaults \List{d}{1}{k}, + using \NULL{} for parameters whose default value is not specified. \end{itemize} \LMHash{}% @@ -16087,11 +16101,15 @@ \subsubsection{Generic Method Instantiation} } -%% TODO(eernst), for review: The null safety spec uses 'shortening' and -%% 'shorting'; checking some dictionaries, it seems fine to use 'shorting'. \subsection{Null Shorting} \LMLabel{nullShorting} +%% TODO(eernst), for review: The null safety spec uses both of the words +%% 'shortening' and 'shorting'; checking some dictionaries, it seems best +%% to use 'shorting', because it has "using a shortcut" among its meanings, +%% whereas 'shortening' means "making shorter" more literally. So I used +%% 'shorting' everywhere. + \LMHash{}% The semantics of the null aware member access operator \lit{?.} is defined in terms of a program transformation known as @@ -16291,11 +16309,6 @@ \subsection{Null Shorting} \def\Base#1{\textcolor{normativeColor}{#1}} \def\Meta#1{\textcolor{metaColor}{#1}} % -\LMHash{}% -\Case{\id} -An identifier \id{} translates to \metaCode{TERM[\Base{\id}]}. -\EndCase - \LMHash{}% \Case{\code{$e$?.\id}} Assume that $e$ is derived from \syntax{ *}. @@ -16433,10 +16446,12 @@ \subsection{Null Shorting} \LMHash{}% \Case{Other} All other expressions are translated compositionally using -the \metaCode{TERM} combinator. \commentary{For example:} +\metaCode{TERM} and \metaCode{EXP}. \commentary{For example:} \commentary{% \begin{itemize} + \item + An identifier \id{} translates to \metaCode{TERM[\Base{\id}]}. \item A list literal \Base{\code{[\List{e}{1}{n}]}} translates to @@ -16808,9 +16823,8 @@ \subsection{Assignment} \LMHash{}% % This error can occur due to implicit casts and mixin+covariance. -It is a dynamic type error if $o$ is not the null object (\ref{null}) -and the dynamic type of $o$ is -not a subtype of the actual type of the formal parameter of \code{$v$=} +It is a dynamic type error if the dynamic type of $o$ is not +a subtype of the actual type of the formal parameter of \code{$v$=} (\ref{actualTypes}) in $S_{static}$. \EndCase @@ -17334,13 +17348,16 @@ \subsection{Equality} \code{\SUPER{} == $e_2$} proceeds as follows: \begin{itemize} -\item The expression $e_2$ is evaluated to an object $o$. -\item If either \THIS{} or $o$ is the null object (\ref{null}), - then $e$ evaluates to evaluates to \TRUE{} - if both \THIS{} and $o$ are the null object - and to \FALSE{} otherwise. -Otherwise, -\item evaluation of $e$ is equivalent to the method invocation +\item + The expression $e_2$ is evaluated to an object $o$. +\item + If either \THIS{} or $o$ is the null object (\ref{null}), + then $e$ evaluates to the \TRUE{} object + if both \THIS{} and $o$ are the null object, + and to the \FALSE{} object otherwise. + Otherwise, +\item + evaluation of $e$ is equivalent to the method invocation \code{\SUPER.==($o$)}. \end{itemize} @@ -17814,7 +17831,7 @@ \subsection{Postfix Expressions} (\ref{expressions}); a function, method or getter invocation (\ref{ordinaryInvocation}, \ref{propertyExtraction}); -a null check; +a non-null assertion; an invocation of a named constructor; or an invocation of a postfix operator on an expression $e$. All but the latter three are specified elsewhere. @@ -18517,8 +18534,8 @@ \subsection{Identifier Expression} \commentary{% When the variable is non-\LATE, non-\FINAL, and has a nullable type, there is no error. - In this case the run-time value will be the null object, - because we know that the variable is unassigned.% + In this case it is known that the run-time value will be the null object, + because the variable is unassigned.% } A \Error{compile-time error} occurs if all of the following hold: @@ -18709,7 +18726,7 @@ \subsection{Type Test} \commentary{% It follows that \code{$e$\,\,\IS\,\,Object?} will always evaluate to the \TRUE{} object, -and \code{$e$\,\,\IS\,\,Object} evaluates to the \TRUE{} object wheneven +and \code{$e$\,\,\IS\,\,Object} evaluates to the \TRUE{} object whenever $e$ evaluates to an object which is not the null object. This makes sense in a language where everything is an object. @@ -21832,7 +21849,7 @@ \subsection{Scripts} \begin{itemize} \item - An object whose run-time type implements \code{List}. + An object whose run-time type is a subtype of \code{List}. \item An object specified when the current isolate $i$ was created, (\commentary{% @@ -21848,7 +21865,8 @@ \subsection{Scripts} \LMHash{}% If \code{main} cannot be called with two positional arguments, but it can be called with one positional argument, -it is invoked with an object whose run-time type implements \code{List} +it is invoked with an object whose run-time type is +a subtype of \code{List} as the only argument. \LMHash{}% @@ -21870,55 +21888,6 @@ \subsection{Scripts} A Dart program will typically be executed by executing a script. The procedure whereby this script is chosen is implementation specific. ----------------------------------------------------------------------- -\LMHash{}% -A \Index{script} is a library whose exported namespace (\ref{exports}) includes -a top-level function declaration named \code{main} -that has either zero, one or two required arguments. - -A script $S$ is executed as follows: - -\LMHash{}% -First, $S$ is compiled as a library as specified above. -Then, the top-level function defined by \code{main} -in the exported namespace of $S$ is invoked (\ref{functionInvocation}) -as follows: -If \code{main} can be called with with two positional arguments, -it is invoked with the following two actual arguments: -\begin{enumerate} -\item An object whose run-time type implements \code{List}. -\item An object specified when the current isolate $i$ was created, -for example through the invocation of \code{Isolate.spawnUri} that spawned $i$, -or the null object (\ref{null}) if no such object was supplied. -\end{enumerate} -If \code{main} cannot be called with two positional arguments, -but it can be called with one positional argument, -it is invoked with an object whose run-time type implements \code{List} -as the only argument. -If \code{main} cannot be called with one or two positional arguments, -it is invoked with no arguments. - -\commentary{% -Note that if \code{main} requires more than two positional arguments, -the library is not considered a script.% -} - -\commentary{% -A Dart program will typically be executed by executing a script.% -} - -\LMHash{}% -It is a -\Error{compile-time error} if a library's export scope contains a declaration -named \code{main}, and the library is not a script. -\commentary{This restriction ensures that all top-level \code{main} declarations -introduce a script main-function, so there cannot be a top-level getter or field -named \code{main}, nor can it be a function that requires more than two -arguments. The restriction allows tools to fail early on invalid \code{main} -methods, without needing to know whether a library will be used as the entry -point of a Dart program. It is possible that this restriction will be removed -in the future.} - \subsection{URIs} \LMLabel{uris} @@ -22996,14 +22965,6 @@ \subsubsection{Being a subtype} each of the premises of $R$, continuing until a rule with no premises is reached. -\commentary{% -For rule \SrnNull, note that the \code{Null} type -is a subtype of all non-$\bot$ types, -even though it doesn't actually extend or implement those types. -The other types are effectively treated as if they were nullable, -which makes the null object (\ref{null}) assignable to them.% -} - \LMHash{}% The first premise in the rules~\SrnLeftTypeAlias{} and~\SrnRightTypeAlias{} @@ -25067,7 +25028,8 @@ \subsection{Parameterized Types} %% replaced by the bottom type (`Null`, for now) in locations of the member %% type where it occurs contravariantly. For instance, `c.f` should have %% static type `void Function(Null)` when `c` has static type `C` for any -%% `T`, and we have `class C { void Function(X) f; }`. +%% `T`, and we have `class C { void Function(X) f; }`. Cf. issue +%% https://github.com/dart-lang/language/issues/297. \subsubsection{Actual Types} @@ -25230,7 +25192,10 @@ \section{Type Inference} \LMLabel{typeInference} \LMHash{}% -This specification does not yet specify type inference in Dart. +This specification does not yet specify type inference in Dart.\!\footnote{% +A preliminary specification is available at +\url{https://github.com/dart-lang/language/blob/master/resources/type-system/inference.md}.% +} A future version of this specification will include that topic. \LMHash{}% @@ -25247,7 +25212,7 @@ \section{Flow Analysis} \LMHash{}% This specification does not fully specify the flow analysis in Dart, but a future version of it will do so.\!\footnote{% -A preliminary specification of the flow analysis is available at +A preliminary specification is available at \url{https://github.com/dart-lang/language/blob/master/resources/type-system/flow-analysis.md}.% } In this section we specify a few concepts and special cases @@ -25524,10 +25489,11 @@ \subsection{Type Promotion} to \code{Null} in the \TRUE{} continuation, and to \NonNullType{$T$} in the \FALSE{} continuation. -\LMHash{}% %% TODO(eernst), for review: The null safety spec says that `T?` is %% promoted to `T`, but implementations _do_ promote `X extends int?` to -%% `X & int`. So I specify that. +%% `X & int`. So I've specified the latter. This is also more consistent +%% with the approach used with `==`. +\LMHash{}% A check of the form \code{$v$\,\,!=\,\,\NULL}, \code{\NULL\,\,!=\,\,$v$}, or \code{$v$\,\,\IS\,\,$T$} @@ -26366,8 +26332,6 @@ \subsection{Migration features} https://github.com/dart-lang/language/blob/master/resources/type-system/subtyping.md https://github.com/dart-lang/language/blob/master/resources/type-system/upper-lower-bounds.md -https://github.com/dart-lang/language/blob/master/resources/type-system/flow-analysis.md -https://github.com/dart-lang/language/blob/master/resources/type-system/inference.md %% !!!END TODO!!! \section*{Appendix: Algorithmic Subtyping} From d21c33defe4d1a60a1a7f2a02e47e6f7bbe07126 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 3 Dec 2021 17:02:42 +0100 Subject: [PATCH 34/85] Did much of Standard ... Bounds --- specification/dartLangSpec.tex | 794 +++++++++++++++++++++++++++------ 1 file changed, 660 insertions(+), 134 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 2b50d5919..eebfdbb30 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -10743,7 +10743,8 @@ \subsubsection{List Literal Inference} If the inferred element type of $\ell_1$ is $S_1$ and the inferred element type of $\ell_2$ is $S_2$, the inferred element type of $\ell$ is -the least upper bound of $S_1$ and $S_2$. +the standard upper bound of $S_1$ and $S_2$ +(\ref{standardUpperBoundsAndStandardLowerBounds}). \EndCase \LMHash{}% @@ -10796,8 +10797,9 @@ \subsubsection{List Literal Inference} \item If $P$ is \FreeContext{} then the inferred element type for \metavar{list} is $T$, - where $T$ is the least upper bound of - the inferred element types of \List{\ell}{1}{n}. + where $T$ is the standard upper bound + (\ref{standardUpperBoundsAndStandardLowerBounds}) + of the inferred element types of \List{\ell}{1}{n}. \item %% TODO(eernst): Feature spec says $P$, but how do we know $P$ is a type? @@ -11433,7 +11435,8 @@ \subsubsection{Set and Map Literal Inference} If the inferred element type of $\ell_1$ is $S_1$ and the inferred element type of $\ell_2$ is $S_2$, the inferred element type of $\ell$ is - the least upper bound of $S_1$ and $S_2$. + the standard upper bound of $S_1$ and $S_2$ + (\ref{standardUpperBoundsAndStandardLowerBounds}). \item If the inferred key and value type pair of $e_1$ is $(K_1, V_1)$ @@ -11441,8 +11444,8 @@ \subsubsection{Set and Map Literal Inference} $(K_2, V_2)$, the inferred key and value type pair of $\ell$ is $(K, V)$, - where $K$ is the least upper bound of $K_1$ and $K_2$, and - and $V$ is the least upper bound of $V_1$ and $V_2$. + where $K$ is the standard upper bound of $K_1$ and $K_2$, and + and $V$ is the standard upper bound of $V_1$ and $V_2$. \end{itemize} \vspace{-5mm} \EndCase @@ -11510,8 +11513,9 @@ \subsubsection{Set and Map Literal Inference} \item If $P$ is \FreeContext{} then the static type of \metavar{collection} is \code{Set<$T$>} - where $T$ is the least upper bound of - the inferred element types of the elements. + where $T$ is the standard upper bound + (\ref{standardUpperBoundsAndStandardLowerBounds}) + of the inferred element types of the elements. \item %% TODO(eernst): Feature spec says $P$, but how do we know $P$ is a type? Otherwise, the static type of \metavar{collection} is $T$ @@ -11532,13 +11536,14 @@ \subsubsection{Set and Map Literal Inference} If $P_k$ is \FreeContext{} or $P$ is \FreeContext, the static key type of \metavar{collection} is $K$ - where $K$ is the least upper bound of \List{K}{1}{n}. + where $K$ is the standard upper bound of \List{K}{1}{n} + (\ref{standardUpperBoundsAndStandardLowerBounds}). Otherwise the static key type of \metavar{collection} is $K$ where $K$ is determined by downwards inference. If $P_v$ is \FreeContext{} or $P$ is \FreeContext, the static value type of \metavar{collection} is $V$ - where $V$ is the least upper bound of \List{V}{1}{n}. + where $V$ is the standard upper bound of \List{V}{1}{n}. Otherwise the static value type of \metavar{collection} is $V$ where $V$ is determined by downwards inference. @@ -11561,13 +11566,14 @@ \subsubsection{Set and Map Literal Inference} and at least one element must be a set, then \metavar{collection} is a set literal with static type \code{Set<$T$>} where $T$ is - the least upper bound of the element types of the elements. + the standard upper bound of the element types of the elements + (\ref{standardUpperBoundsAndStandardLowerBounds}). \item If all elements can be a map, and at least one element must be a map, then $e$ is a map literal with static type \code{Map<$K$,\,\,$V$>} where $K$ is - the least upper bound of the key types of the elements and $V$ is - the least upper bound of the value types. + the standard upper bound of the key types of the elements and $V$ is + the standard upper bound of the value types. \item Otherwise, a \Error{compile-time error} occurs. \commentary{In this case the literal cannot be disambiguated.} @@ -16894,7 +16900,8 @@ \subsubsection{Compound Assignment} exactly the \Error{same compile-time errors} and warnings as the static analysis of \code{$v$ = $e$}. The static type of $a$ is -the least upper bound of the static type of $v$ and the static type of $e$. +the standard upper bound of the static type of $v$ and the static type of $e$ +(\ref{standardUpperBoundsAndStandardLowerBounds}). \LMHash{}% Evaluation of a compound assignment $a$ of the form \code{$v$ ??= $e$} @@ -16919,8 +16926,9 @@ \subsubsection{Compound Assignment} Moreover, a warning occurs if the static type of \code{$C$.\id} is non-nullable. \commentary{In this case, the \lit{??} is superfluous.} -The static type of $a$ is the least upper bound of -\NonNullType{$S$} and the static type of $e$. +The static type of $a$ is the standard upper bound +(\ref{standardUpperBoundsAndStandardLowerBounds}) +of \NonNullType{$S$} and the static type of $e$. \LMHash{}% Evaluation of a compound assignment $a$ of the form \code{$C$.\id{} ??= $e$} @@ -16945,8 +16953,9 @@ \subsubsection{Compound Assignment} Let $S$ be the static type of \code{$e_1$.\id}. A warning occurs if $S$ is non-nullable. \commentary{In this case, the \lit{??} is superfluous.} -The static type of $a$ is the least upper bound of -\NonNullType{$S$} and the static type of $e_2$. +The static type of $a$ is the standard upper bound +(\ref{standardUpperBoundsAndStandardLowerBounds}) +of \NonNullType{$S$} and the static type of $e_2$. \LMHash{}% Evaluation of a compound assignment $a$ of the form \code{$e_1$.\id{} ??= $e_2$} @@ -16973,8 +16982,9 @@ \subsubsection{Compound Assignment} Let $S$ be the static type of \code{$e_1$[$e_2$]}. A warning occurs if $S$ is non-nullable. \commentary{In this case, the \lit{??} is superfluous.} -The static type of $a$ is the least upper bound of -\NonNullType{$S$} and the static type of $e_3$. +The static type of $a$ is the standard upper bound +(\ref{standardUpperBoundsAndStandardLowerBounds}) +of \NonNullType{$S$} and the static type of $e_3$. \LMHash{}% Evaluation of a compound assignment $a$ of the form @@ -17005,8 +17015,9 @@ \subsubsection{Compound Assignment} Let $S$ be the return type of said getter. A warning occurs if $S$ is non-nullable. \commentary{In this case, the \lit{??} is superfluous.} -The static type of $a$ is the least upper bound of -\NonNullType{$S$} and the static type of $e$. +The static type of $a$ is the standard upper bound +(\ref{standardUpperBoundsAndStandardLowerBounds}) +of \NonNullType{$S$} and the static type of $e$. \LMHash{}% Evaluation of a compound assignment $a$ of the form \code{\SUPER.\id{} ??= $e$} @@ -17037,8 +17048,9 @@ \subsubsection{Compound Assignment} Let $U$ be the return type of said getter. A warning occurs if $U$ is non-nullable. \commentary{In this case, the \lit{??} is superfluous.} -The static type of $a$ is the least upper bound of -\NonNullType{$U$} and the static type of $e_2$. +The static type of $a$ is the standard upper bound +(\ref{standardUpperBoundsAndStandardLowerBounds}) +of \NonNullType{$U$} and the static type of $e_2$. \LMHash{}% Evaluation of a compound assignment $a$ of the form @@ -17171,8 +17183,9 @@ \subsection{Conditional} \code{$e_1$\,\,?\,\,$e_2$\,\,:\,\,$e_3$}. It is a \Error{compile-time error} if the static type of $e_1$ may not be assigned to \code{bool}. -The static type of $c$ is the least upper bound (\ref{leastUpperBounds}) of -the static type of $e_2$ and the static type of $e_3$. +The static type of $c$ is the standard upper bound +(\ref{standardUpperBoundsAndStandardLowerBounds}) +of the static type of $e_2$ and the static type of $e_3$. \LMHash{}% Evaluation of a conditional expression $c$ of the form @@ -17202,8 +17215,8 @@ \subsection{If-null Expressions} Let $S$ be the static type of $e_1$. A warning occurs if $S$ is non-nullable. \commentary{In this case, the \lit{??} is superfluous.} -The static type of $e$ is the least upper bound -(\ref{leastUpperBounds}) +The static type of $e$ is the standard upper bound +(\ref{standardUpperBoundsAndStandardLowerBounds}) of \NonNullType{$S$} and the static type of $e_2$. \LMHash{}% @@ -23698,6 +23711,623 @@ \subsection{Type Normalization} } +\subsubsection{Standard Upper Bounds and Standard Lower Bounds} +\LMLabel{standardUpperBoundsAndStandardLowerBounds} + +\LMHash{}% +This section specifies how to compute the +\IndexCustom{standard upper bound}{type!standard upper bound} +as well as the +\IndexCustom{standard lower bound}{type!standard lower bound} +of two or more types. + +\rationale{% +If the least upper bound $U$ exists, +then the standard upper bound is guaranteed to be a supertype of $U$, +and the standard upper bound is in any case guaranteed to be +a supertype of both operands. + +If the greatest lower bound $L$ exists, +then the standard lower bound is guaranteed to be a subtype of $L$, +and the standard lower bound is in any case guaranteed to be +a subtype of both operands.% + +The standard upper and lower bounds are intended to compute +the least upper bound respectively the greatest lower bound whenever possible, +and otherwise to deliver an approximation thereof. +} + +\commentary{% +It is easy to see that some pairs of types do not have a least upper bound, +because the set of types which are supertypes of both of them +does not have a least element. +For example:% +} + +\begin{dartCode} +\CLASS{} I \{\} +\CLASS{} J \{\} +\CLASS{} A \IMPLEMENTS{} I, J \{\} +\CLASS{} B \IMPLEMENTS{} I, J \{\} +\end{dartCode} + +\commentary{% +No \emph{least} upper bound exists for \code{A} and \code{B}, +because \code{I} and \code{J} are incomparable, +and no other supertypes of \code{A} and \code{B} +are subtypes of both \code{I} and \code{J}.% +} + +\commentary{% +For backward compatibility, +the standard upper and lower bounds are computed +in the same way as in earlier versions of Dart, +in the case where the operands are +interface types based on distinct classes. +E.g., \code{List{}>} and \code{Iterable} +are handled in this manner, +because \code{List} and \code{Iterable} are not the same class.% +} + +\rationale{% +Consequently, the standard upper and lower bound algorithms may yield +results which are suboptimal in the sense that +we can rather easily find a better approximation of the +least upper bound or the greatest lower bound. + +In all other cases, +the standard upper and lower bound are computed recursively, +based on the structure of the type, +which ensures that if the results for all subterms yield an actual +least upper bound respectively greatest lower bound, +then the overall result will preserve that property.% +} + +\LMHash{}% +The following general assumptions and rules apply in this section: +The set of clauses defining each function in this section are ordered, +i.e., the first clause that matches is the one that must be used. +If a rule is dealing with type variables, +then it is assumed that the type variables have been renamed to fresh names, +such that no type variables are captured due to accidental name clashes. +Finally, it is assumed that type aliases, +including actual type arguments if any, +have been replaced by their transitive alias expansion +(\ref{typedef}), +and that all types are denoted by a canonical name. + + +\commentary{% +That is, we do not encounter the same type under the names +\code{$C$} and \code{\metavar{myPrefix}.$C$}, +and we do not encounter different types with the same name.% +} + +\LMHash{}% +Let $T_1$ and $T_2$ be two types. +We define the +\Index{standard upper bound} +of $T_1$ and $T_2$ to be +\UpperBoundType{$T_1$}{$T_2$}, +where \UpperBoundTypeName{} is defined below. +We define the standard upper bound of \List{T}{1}{n} where $n > 2$ +to be \UpperBoundType{$T_1$}{$T'$}, +where $T'$ is the standard upper bound of \List{T}{2}{n}. + +\begin{itemize} +\item + \DefEquals{\UpperBoundType{$T$}{$T$}}{T}. +\item + If \IsTopType{$T_1$} and \IsTopType{$T_2$} then\\[1mm] + \DefEquals{\UpperBoundType{$T_1$}{$T_2$}}{% + \left\{% + \begin{array}{l} + T_1\mbox{, if \IsMoreTopType{$T_1$}{$T_2$}.}\\ + T_2\mbox{, otherwise.} + \end{array} + \right.% + } +\item + \DefEquals{\UpperBoundType{$T_1$}{$T_2$}}{T_1}, if \IsTopType{$T_1$}. +\item + \DefEquals{\UpperBoundType{$T_1$}{$T_2$}}{T_2}, if \IsTopType{$T_2$}. +\item + If \IsBottomType{$T_1$} and \IsBottomType{$T_2$} then\\[1mm] + \DefEquals{\UpperBoundType{$T_1$}{$T_2$}}{% + \left\{% + \begin{array}{l} + T_2\mbox{, if \IsMoreBottomType{$T_1$}{$T_2$}.}\\ + T_1\mbox{, otherwise.} + \end{array} + \right.% + } +\item + \DefEquals{\UpperBoundType{$T_1$}{$T_2$}}{T_2}, if \IsBottomType{$T_1$}. +\item + \DefEquals{\UpperBoundType{$T_1$}{$T_2$}}{T_1}, if \IsBottomType{$T_2$}. +\item + If \IsNullType{$T_1$} and \IsNullType{$T_2$} then\\[1mm] + \DefEquals{\UpperBoundType{$T_1$}{$T_2$}}{% + \left\{% + \begin{array}{l} + T_2\mbox{, if \IsMoreBottomType{$T_1$}{$T_2$}.}\\ + T_1\mbox{, otherwise.} + \end{array} + \right.% + } +\item + If \IsNullType{$T_1$} then\\[1mm] + \DefEquals{\UpperBoundType{$T_1$}{$T_2$}}{% + \left\{% + \begin{array}{l} + T_2\mbox{, if $T_2$ is nullable.}\\ + \code{$T_2$?}\mbox{, otherwise.} + \end{array} + \right.% + } +\item + If \IsNullType{$T_2$} then\\[1mm] + \DefEquals{\UpperBoundType{$T_1$}{$T_2$}}{% + \left\{% + \begin{array}{l} + T_1\mbox{, if $T_1$ is nullable.}\\ + \code{$T_1$?}\mbox{, otherwise.} + \end{array} + \right.% + } +\item + If \IsObjectType{$T_1$} and \IsObjectType{$T_2$} then\\[1mm] + \DefEquals{\UpperBoundType{$T_1$}{$T_2$}}{% + \left\{% + \begin{array}{l} + T_1\mbox{, if \IsMoreTopType{$T_1$}{$T_2$}.}\\ + T_2\mbox{, otherwise.} + \end{array} + \right.% + } +\item + If \IsObjectType{$T_1$} then\\ + \DefEquals{\UpperBoundType{$T_1$}{$T_2$}}{% + \left\{ + \begin{array}{l} + T_1\mbox{, if $T_2$ is non-nullable.}\\ + \code{$T_1$?}\mbox{, otherwise.} + \end{array} + \right.% + } +\item + If \IsObjectType{$T_2$} then\\[1mm] + \DefEquals{\UpperBoundType{$T_1$}{$T_2$}}{% + \left\{% + \begin{array}{l} + T_2\mbox{, if $T_1$ is non-nullable.}\\ + \code{$T_2$?}\mbox{, otherwise} + \end{array} + \right.% + } +\item + \DefEquals{\UpperBoundType{$T_1$?}{$T_2$?}}{\code{$S$?}},\\ + where $S$ is \UpperBoundType{$T_1$}{$T_2$}. +\item + \DefEquals{\UpperBoundType{$T_1$?}{$T_2$}}{\code{$S$?}},\\ + where $S$ is \UpperBoundType{$T_1$}{$T_2$}. +\item + \DefEquals{\UpperBoundType{$T_1$}{$T_2$?}}{\code{$S$?}},\\ + where $S$ is \UpperBoundType{$T_1$}{$T_2$}. +\item + Let $X_1$ be a type variable with bound $B_1$.\\[1mm] + \DefEquals{\UpperBoundType{$X_1$}{$T_2$}}{% + \left\{% + \begin{array}{l} + T_2\mbox{, if \SubtypeNE{X_1}{T_2}.}\\ + X_1\mbox{, otherwise and if \SubtypeNE{T_2}{X_1}.}\\ + \UpperBoundType{$B_{1a}$}{$T_2$}\mbox{,}\\ + \mbox{\quad{}otherwise.} + \end{array} + \right.% + }\\[1mm] + where $B_{1a}$ is the greatest closure of $B_1$ with respect to $X_1$ + (\ref{leastAndGreatestClosureOfTypes}). +\item + Let $X_1$ be a type variable with bound $B_1$, + and $S_1$ a subtype of $B_1$.\\[1mm] + \DefEquals{\UpperBoundType{$X_1$\,\&\,$S_1$}{$T_2$}}{% + \left\{% + \begin{array}{l} + %% TODO(eernst), for review: Why do we not have the following instead? + % T_2\mbox{, if \SubtypeNE{\code{$X_1$\,\&\,$S_1$}}{T_2}.}\\ + T_2\mbox{, if \SubtypeNE{X_1}{T_2}.}\\ + %% TODO(eernst), for review: Why do we not have the following as well? + % X_1 \& S_1\mbox{, otherwise and if \SubtypeNE{T_2}{X_1 \& S_1}.}\\ + X_1\mbox{, otherwise and if \SubtypeNE{T_2}{X_1}.}\\ + \UpperBoundType{$S_{1a}$}{$T_2$}\mbox{,}\\ + \mbox{\quad{}otherwise.} + \end{array} + \right.% + }\\[1mm] + where $S_{1a}$ is the greatest closure of $S_1$ with respect to $X_1$. +\item + Let $X_2$ be a type variable with bound $B_2$.\\[1mm] + \DefEquals{\UpperBoundType{$T_1$}{$X_2$}}{% + \left\{% + \begin{array}{l} + X_2\mbox{, if \SubtypeNE{T_1}{X_2}.}\\ + T_1\mbox{, otherwise and if \SubtypeNE{X_2}{T_1}.}\\ + \UpperBoundType{$T_1$}{$B_{2a}$}\mbox{,}\\ + \mbox{\quad{}otherwise.} + \end{array} + \right.% + }\\[1mm] + where $B_{2a}$ is the greatest closure of $B_2$ with respect to $X_2$. +\item + %% TODO(eernst), for review: Consider the same changes as in the converse. + Let $X_2$ be a type variable with bound $B_2$, + and $S_2$ a subtype of $B_2$.\\[1mm] + \DefEquals{\UpperBoundType{$T_1$}{$X_2$\,\&\,$S_2$}}{% + \left\{% + \begin{array}{l} + X_2\mbox{, if \SubtypeNE{T_1}{X_2}.}\\ + T_1\mbox{, otherwise and if \SubtypeNE{X_2}{T_1}.}\\ + \UpperBoundType{$T_1$}{$B_{2a}$}\mbox{,}\\ + \mbox{\quad{}otherwise.} + \end{array} + \right.% + }\\[1mm] + where $S_{2a}$ is the greatest closure of $S_2$ with respect to $X_2$. +\item + \DefEquals{\UpperBoundType{$T$ \FUNCTION<\ldots>(\ldots)}{\FUNCTION}}{% + \FUNCTION}. +\item + \DefEquals{\UpperBoundType{\FUNCTION}{$T$ \FUNCTION<\ldots>(\ldots)}}{% + \FUNCTION}. +\item + Let $U_1$ and $U_2$ be, respectively, + + \noindent + \code{$T_1$\,\FUNCTION<$X_1$\,\EXTENDS\,$B_{11}$,\,\ldots,\,$X_m$\,% + \EXTENDS\,$B_{1m}$>($P_{11}$,\,\ldots,\,$P_{1k}$)} + + \noindent + \code{$T_2$\,\FUNCTION<$X_1$\,\EXTENDS\,$B_{21}$,\,\ldots,\,$X_m$\,% + \EXTENDS\,$B_{2m}$>($P_{21}$,\,\ldots,\,$P_{2l}$)} + + \noindent + such that each $B_{1i}$ and $B_{2i}$ are syntactically equal types, + and both have the same number of required positional parameters. + Let $q$ be $\metavar{min}(k, l)$, + let $T_3$ be \UpperBoundType{$T_1$}{$T_2$}, + let $B_{3i}$ be $B_{1i}$, and + let $P_{3i}$ be \LowerBoundType{$P_{1i}$}{$P_{2i}$}. + Then \DefEquals{\UpperBoundType{$U_1$}{$U_2$}}{% + \code{$T_3$\,\FUNCTION<$X_1$\,\EXTENDS\,$B_{31}$,\,\ldots,\,$X_m$\,\EXTENDS\,$B_{3m}$>($P_{31}$,\,\ldots,\,$P_{3q}$)}}. +\item + Let $U_1$ and $U_2$ be, respectively, + + \noindent + \code{$T_1$\,\FUNCTION<$X_1$\,\EXTENDS\,$B_{11}$,\,\ldots,\,$X_m$\,% + \EXTENDS\,$B_{1m}$>($P_{11}$,\,\ldots,\,$P_{1k}$,\,$\metavar{Named}_1$)} + + \noindent + \code{$T_2$\,\FUNCTION<$X_1$\,\EXTENDS\,$B_{21}$,\,\ldots,\,$X_m$\,% + \EXTENDS\,$B_{2m}$>($P_{21}$,\,\ldots,\,$P_{2k}$,\,$\metavar{Named}_2$)} + + \noindent + where $\metavar{Named}_1$ and $\metavar{Named}_2$ declare some named parameters, + and consider the case where the following is satisfied: + + \begin{itemize} + \item Each $B_{1i}$ and $B_{2i}$ are syntactically equal types. + \item All positional parameters are required. + \item $\metavar{Named}_1$ contains an entry (optional or required) of the form \code{$R_{1i}$ $x_i$} + for every required named parameter \code{$R_{2i}$ $x_i$} in $\metavar{Named}_2$. + \item $\metavar{Named}_2$ contains an entry (optional or required) of the form \code{$R_{2i}$ $x_i$} + for every required named parameter \code{$R_{1i}$ $x_i$} in $\metavar{Named}_1$. + \end{itemize} + + Then \DefEquals{\UpperBoundType{$U_1$}{$U_2$}}{U_3}, where $U_3$ is + + \noindent + \code{$T_3$\,\FUNCTION<$X_1$\,\EXTENDS\,$B_{31}$,\,\ldots,\,$X_m$\,\EXTENDS\,$B_{3m}$>($P_{31}$,\,\ldots,\,$P_{3k}$,\,$\metavar{Named}_3$)}, + + \noindent + where: + + \begin{itemize} + \item $T_3$ is \UpperBoundType{$T_1$}{$T_2$}. + \item $B_{3i}$ is $B_{1i}$. + \item $P_{3i}$ is \LowerBoundType{$P_{1i}$}{$P_{2i}$}. + \item $\metavar{Named}_3$ contains exactly \code{$R_{3i}$ $x_i$} for each $x_i$ in both $\metavar{Named}_1$ and + $\metavar{Named}_2$. + \begin{itemize} + \item where \code{$R_{1i}$ $x_i$} is in $\metavar{Named}_1$, + \item where \code{$R_{2i}$ $x_i$} is in $\metavar{Named}_2$, + \item and $R_{3i}$ is \LowerBoundType{$R_{1i}$}{$R_{2i}$}, + \item and \code{$R_{3i}$ $x_i$} is required if $x_i$ is required in either $\metavar{Named}_1$ or + $\metavar{Named}_2$. + \end{itemize} + \end{itemize} +\item + \UpperBoundType{$T$ \FUNCTION<\ldots>(\ldots)}{$S$ \FUNCTION<\ldots>(\ldots)} = \FUNCTION{} otherwise +\item + \UpperBoundType{$T$ \FUNCTION<\ldots>(\ldots)}{$T_2$} = \UpperBoundType{Object}{$T_2$} +\item + \UpperBoundType{$T_1$}{$T$ \FUNCTION<\ldots>(\ldots)} = \UpperBoundType{$T_1$}{Object} +\item + \UpperBoundType{FutureOr<$T_1$>}{FutureOr<$T_2$>} = \code{FutureOr<$T_3$>} where $T_3$ = \UpperBoundType{$T_1$}{$T_2$} +\item + \UpperBoundType{Future<$T_1$>}{FutureOr<$T_2$>} = \code{FutureOr<$T_3$>} where $T_3$ = \UpperBoundType{$T_1$}{$T_2$} +\item + \UpperBoundType{FutureOr<$T_1$>}{Future<$T_2$>} = \code{FutureOr<$T_3$>} where $T_3$ = \UpperBoundType{$T_1$}{$T_2$} +\item + \UpperBoundType{$T_1$}{FutureOr<$T_2$>} = \code{FutureOr<$T_3$>} where $T_3$ = \UpperBoundType{$T_1$}{$T_2$} +\item + \UpperBoundType{FutureOr<$T_1$>}{$T_2$} = \code{FutureOr<$T_3$>} where $T_3$ = \UpperBoundType{$T_1$}{$T_2$} +\item \UpperBoundType{$T_1$}{$T_2$} = $T_2$ if \SubtypeNE{T_1}{T_2} + - Note that both types must be class types at this point +\item + \UpperBoundType{$T_1$}{$T_2$} = $T_1$ if \SubtypeNE{T_2}{T_1} + - Note that both types must be class types at this point +- \UpperBoundType{$C$<$T_0$, \ldots, $T_n$>}{$C$<$S_0$, \ldots, $S_n$>} = \code{$C$<$R_0$, \ldots, $R_f$n>} where $R_i$ is \UpperBoundType{$T_i$}{$S_i$} +- \UpperBoundType{$C_0$<$T_0$, \ldots, $T_n$>}{$C_1$<$S_0$, \ldots, $S_k$>} = least upper bound of two + interfaces as in Dart 1, with modifications for handling mixed null safe and + legacy code as follows: + - For an upper bound computation in a legacy library, the set of + super-interfaces used consists of the `LEGACY_ERASURE` of the + super-interfaces of the two types. + - For an upper bound computation in an opted in library, no modification of + the set of super-interfaces is performed. +\end{itemize} + +\paragraph{Lower bounds} + +\LMHash{}% +We define the lower bound of two types $T_1$ and $T_2$ to be \LowerBoundType{$T_1$}{$T_2$} as +follows. + +\begin{itemize} +\item \LowerBoundType{$T$}{$T$} = $T$ + +\item \LowerBoundType{$T_1$}{$T_2$} where \IsTopType{$T_1$} and \IsTopType{$T_2$} = + - $T_1$ if \IsMoreTopType{$T_2$}{$T_1$} + - $T_2$ otherwise +\item \LowerBoundType{$T_1$}{$T_2$} = $T_2$ if \IsTopType{$T_1$} +\item \LowerBoundType{$T_1$}{$T_2$} = $T_1$ if \IsTopType{$T_2$} + +\item \LowerBoundType{$T_1$}{$T_2$} where \IsBottomType{$T_1$} and \IsBottomType{$T_2$} = + - $T_1$ if \IsMoreBottomType{$T_1$}{$T_2$} + - $T_2$ otherwise +\item \LowerBoundType{$T_1$}{$T_2$} = $T_2$ if \IsBottomType{$T_2$} +\item \LowerBoundType{$T_1$}{$T_2$} = $T_1$ if \IsBottomType{$T_1$} +\item \LowerBoundType{$T_1$}{$T_2$} where \IsNullType{$T_1$} and \IsNullType{$T_2$} = + - $T_1$ if \IsMoreBottomType{$T_1$}{$T_2$} + - $T_2$ otherwise +\item \LowerBoundType{Null}{$T_2$} = + - \code{Null} if \SubtypeNE{\code{Null}}{T_2} + - \code{Never} otherwise +\item \LowerBoundType{$T_1$}{Null} = + - \code{Null} if \SubtypeNE{\code{Null}}{T_1} + - \code{Never} otherwise +\item \LowerBoundType{$T_1$}{$T_2$} where \IsObjectType{$T_1$} and \IsObjectType{$T_2$} = + - $T_1$ if \IsMoreTopType{$T_2$}{$T_1$} + - $T_2$ otherwise +\item \LowerBoundType{$T_1$}{$T_2$} where \IsObjectType{$T_1$} = + - $T_2$ if $T_2$ is non-nullable + - \NonNullType{$T_2$} if \NonNullType{$T_2$} is non-nullable + - \code{Never} otherwise +\item \LowerBoundType{$T_1$}{$T_2$} where \IsObjectType{$T_2$} = + - $T_1$ if $T_1$ is non-nullable + - \NonNullType{$T_1$} if \NonNullType{$T_1$} is non-nullable + - \code{Never} otherwise +\item \LowerBoundType{$T_1$?}{$T_2$?} = \code{$S$?} where $S$ is \LowerBoundType{$T_1$}{$T_2$} +\item \LowerBoundType{$T_1$?}{$T_2$} = $S$ where $S$ is \LowerBoundType{$T_1$}{$T_2$} +\item \LowerBoundType{$T_1$}{$T_2$?} = $S$ where $S$ is \LowerBoundType{$T_1$}{$T_2$} +\item \LowerBoundType{$T_0$ \FUNCTION<$X_0$ \EXTENDS{} $B_{00}$, \ldots, $X_m$ \EXTENDS{} $B_{0m}$>($P_{00}$, \ldots, $P_{0k}$)}{% + $T_1$ \FUNCTION<$X_0$ \EXTENDS{} $B_{10}$, \ldots, $X_m$ \EXTENDS{} $B_{1m}$>($P_{10}$, \ldots, $P_{1l}$)} = + \code{$R_0$ \FUNCTION<$X_0$ \EXTENDS{} $B_{20}$, \ldots, $X_m$ \EXTENDS{} $B_{2m}$>($P_{20}$, \ldots, $P_{2q}$)} if: + - each $B_{0i}$ and $B_{1i}$ are equal types (syntactically) + - $q$ is max($k$, $l$) + - $R_0$ is \LowerBoundType{$T_0$}{$T_1$} + - $B_{2i}$ is $B_{0i}$ + - $P_{2i}$ is \UpperBoundType{$P_{0i}$}{$P_{1i}$} for $i \leq \metavar{min}(k, l)$ + - $P_{2i}$ is $P_{0i}$ for $k < i \leq q$ + - $P_{2i}$ is $P_{1i}$ for $l < i \leq q$ + - $P_{2i}$ is optional if $P_{0i}$ or $P_{1i}$ is optional, or if $\metavar{min}(k, l) < i \leq q$ +\item \LowerBoundType{$T_0$ \FUNCTION<$X_0$ \EXTENDS{} $B_{00}$, \ldots, $X_m$ \EXTENDS{} $B_{0m}$>($P_{00}$, \ldots, $P_{0k}$, $\metavar{Named}_0$)}{% + $T_1$ \FUNCTION<$X_0$ \EXTENDS{} $B_{10}$, \ldots, $X_m$ \EXTENDS{} $B_{1m}$>($P_{10}$, \ldots, $P_{1k}$, $\metavar{Named}_1$)} = + \code{$R_0$ \FUNCTION<$X_0$ \EXTENDS{} $B_{20}$, \ldots, $X_m$ \EXTENDS{} $B_{2m}$>($P_{20}$, \ldots, $P_{2k}$, $\metavar{Named}_2$)} if: + - each $B_{0i}$ and $B_{1i}$ are equal types (syntactically) + - $R_0$ is \LowerBoundType{$T_0$}{$T_1$} + - $B_{2i}$ is $B_{0i}$ + - $P_{2i}$ is \UpperBoundType{$P_{0i}$}{$P_{1i}$} + - $\metavar{Named}_2$ contains \code{$R_{2i}$ $x_i$} for each $x_i$ in both $\metavar{Named}_0$ and $\metavar{Named}_1$ + - where \code{$R_{0i}$ $x_i$} is in $\metavar{Named}_0$ + - where \code{$R_{1i}$ $x_i$} is in $\metavar{Named}_1$ + - and $R_{2i}$ is \UpperBoundType{$R_{0i}$}{$R_{1i}$} + - and \code{$R_{2i}$ $x_i$} is required if $x_i$ is required in both $\metavar{Named}_0$ and $\metavar{Named}_1$ + - $\metavar{Named}_2$ contains \code{$R_{0i}$ $x_i$} for each $x_i$ in $\metavar{Named}_0$ and not $\metavar{Named}_1$ + - where $x_i$ is optional in $\metavar{Named}_2$ + - $\metavar{Named}_2$ contains \code{$R_{1i}$ $x_i$} for each $x_i$ in $\metavar{Named}_1$ and not $\metavar{Named}_0$ + - where $x_i$ is optional in $\metavar{Named}_2$ +\item \LowerBoundType{$T$ \FUNCTION<\ldots>(\ldots)}{$S$ \FUNCTION<\ldots>(\ldots)} = \code{Never} otherwise +\item \LowerBoundType{$T_1$}{$T_2$} = $T_1$ if \SubtypeNE{T_1}{T_2} +\item \LowerBoundType{$T_1$}{$T_2$} = $T_2$ if \SubtypeNE{T_2}{T_1} +\item \LowerBoundType{FutureOr<$T_1$>}{FutureOr<$T_2$>} = \code{FutureOr<$S$>} + - where $S$ is \LowerBoundType{$T_1$}{$T_2$} +\item \LowerBoundType{FutureOr<$T_1$>}{Future<$T_2$>} = \code{Future<$S$>} + - where $S$ is \LowerBoundType{$T_1$}{$T_2$} +\item \LowerBoundType{Future<$T_1$>}{FutureOr<$T_2$>} = \code{Future<$S$>} + - where $S$ is \LowerBoundType{$T_1$}{$T_2$} +\item \LowerBoundType{FutureOr<$T_1$>}{$T_2$} = $S$ + - where $S$ is \LowerBoundType{$T_1$}{$T_2$} +\item \LowerBoundType{$T_1$}{FutureOr<$T_2$>} = $S$ + - where $S$ is \LowerBoundType{$T_1$}{$T_2$} +\item \LowerBoundType{$T_1$}{$T_2$} = \code{Never} otherwise +\end{itemize} + +\paragraph{Issues and Interesting examples} + +The definition of upper bound for type variables does not guarantee termination. +Counterexample: + +\begin{dartCode} +\VOID{} foo, S \EXTENDS{} List>() \{ + T x; + S y; + \VAR{} a = (x == y) ? x : y; +\} +\end{dartCode} + +It should be changed to close the bound with respect to all of the type +variables declared in the same scope, using the greatest closure definition. + +\paragraph{Asymmetry} + +The current algorithm is asymmetric. There is an equivalence class of top +types, and we correctly choose a canonical representative for bare top types +using the \IsMoreTopTypeName{} predicate. However, when two different top types are +embedded in two mutual subtypes, we don't correctly choose a canonical +representative. + +\begin{dartCode} +\IMPORT{} 'dart:async'; + +\VOID{} main() \{ + List> x; + List<\DYNAMIC> y; + String s; + // List<\DYNAMIC> + var a = (x == y) ? x : y; + // List> + var b = (x == y) ? y : x; +\end{dartCode} + +The best solution for this is probably to normalize the types. This is fairly +straightforward: we just normalize `FutureOr` to the normal form of $T$ when +$T$ is a top type. We can then inductively apply this across the rest of the +types. Then, whenever we have mutual subtypes, we just return the normal form. +This would be breaking, albeit hopefully only in a minor way. + +An alternative would be to try to define an ordering on mutual subtypes. This +can probably be done, but is a bit ugly. For example, consider `Map>` vs `Map, dynamic>`. The obvious way to +proceed is to define the total order by defining a traversal order on types, and +then defining the ordering lexicographically. That is, saying that $T$ is +greater than $S$ if the first pair of top types encountered in the traversal +that are not identical are `T0` and `S0` respectively, and \IsMoreTopType{$T_0$}{$S_0$}. + +A similar treatment would need to be done for the bottom types as well, since +there are two equivalences there. + - `X extends T` is equivalent to \code{Null} if $T$ is equivalent to \code{Null}. + - `FutureOr` is equivalent `Future`. + +A possible variant of the previous approach would be to define a finer grained +variant of the subtyping relation which is a total order on mutual subtypes. +That is, if `<::` is the extended relation, we would want that `T <:: S` implies +that `T <: S`, but also that `T <:: S` and `S <:: T` implies that $S$ and $T$ +are syntactically (rather than just semantically) equal. + + +---------------------------------------------------------------------- !!!End + +\LMHash{}% +% does this diverge in some cases? +Given two interfaces $I$ and $J$, +let $S_I$ be the set of superinterfaces of $I$, +let $S_J$ be the set of superinterfaces of $J$ +and let $S = (\{I\} \cup S_I) \cap (\{J\} \cup S_J)$. +Furthermore, +we define $S_n = \{T | T \in S \wedge depth(T) = n\}$ for any finite $n$ +where $depth(T)$ is the number of steps in the longest inheritance path +from $T$ to \code{Object}. +%TODO(lrn): Specify: "inheritance path" is a path in the superinterface graph. +Let $q$ be the largest number such that $S_q$ has cardinality one, +which must exist because $S_0$ is $\{\code{Object}\}$. +The least upper bound of $I$ and $J$ is the sole element of $S_q$. + +\LMHash{}% +The least upper bound of \DYNAMIC{} and any type $T$ is \DYNAMIC. +The least upper bound of \VOID{} and any type $T \ne \DYNAMIC{}$ is \VOID. +The least upper bound of $\bot$ and any type $T$ is $T$. +Let $U$ be a type variable with upper bound $B$. +The least upper bound of $U$ and a type $T \ne \bot$ is +the least upper bound of $B$ and $T$. + +\LMHash{}% +The least upper bound operation is commutative and idempotent, +but it is not associative. + +% Function types + +%% TODO(eernst): This section should use the new syntax for function types +%% (\FunctionTypePositionalStd{} etc.); these updates are made by CL 84908. + +\LMHash{}% +The least upper bound of a function type and an interface type $T$ is +the least upper bound of \FUNCTION{} and $T$. +Let $F$ and $G$ be function types. +If $F$ and $G$ differ in their number of required parameters, +then the least upper bound of $F$ and $G$ is \FUNCTION. +Otherwise: +\begin{itemize} +\item If + +\noindent +\code{$F = $ <$X_1\ B_1, \ldots,\ X_s\ B_s$>($T_1, \ldots,\ T_r,\ $[$T_{r+1}, \ldots,\ T_n$]) $ \rightarrow T_0$} and + +\noindent +\code{$G = $ <$X_1\ B_1, \ldots,\ X_s\ B_s$>($S_1, \ldots,\ S_r,\ $[$S_{r+1}, \ldots,\ S_k$]) $ \rightarrow S_0$} + +\noindent +where $k \le n$ then the least upper bound of $F$ and $G$ is + +\noindent +\code{<$X_1\ B_1, \ldots,\ X_s\ B_s$>($L_1, \ldots,\ L_r,\ $[$L_{r+1}, \ldots,\ L_k$]) $ \rightarrow L_0$} + +\noindent +where $L_i$ is the least upper bound of $T_i$ and $S_i, i \in 0 .. k$. +\item If + +\noindent +\code{$F = $ <$X_1\ B_1, \ldots,\ X_s\ B_s$>($T_1, \ldots,\ T_r,\ $[$T_{r+1}, \ldots,\ T_n$]) $ \rightarrow T_0$}, + +\noindent +\code{$G = $ <$X_1\ B_1, \ldots,\ X_s\ B_s$>($S_1, \ldots,\ S_r,\ $\{ \ldots{} \}) $ \rightarrow S_0$} + +\noindent +then the least upper bound of $F$ and $G$ is + +\noindent +\code{<$X_1\ B_1, \ldots,\ X_s\ B_s$>($L_1, \ldots,\ L_r$) $ \rightarrow L_0$} + +\noindent +where $L_i$ is the least upper bound of $T_i$ and $S_i, i \in 0 .. r$. +\item If + +\noindent +\code{$F = $ <$X_1\ B_1, \ldots,\ X_s\ B_s$>($T_1, \ldots,\ T_r,\ $\{$T_{r+1}\ p_{r+1}, \ldots,\ T_f\ p_f$\}) $ \rightarrow T_0$}, + +\noindent +\code{$G = $ <$X_1\ B_1, \ldots,\ X_s\ B_s$>($S_1, \ldots,\ S_r,\ $\{$S_{r+1}\ q_{r+1}, \ldots,\ S_g\ q_g$\}) $ \rightarrow S_0$} + +then let +$\{x_m, \ldots, x_n\} = \{p_{r+1}, \ldots, p_f\} \cap \{q_{r+1}, \ldots, q_g\}$ +and let $X_j$ be the least upper bound of the types of $x_j$ in $F$ and +$G, j \in m .. n$. +Then the least upper bound of $F$ and $G$ is + +\noindent +\code{<$X_1\ B_1, \ldots,\ X_s\ B_s$>($L_1, \ldots,\ L_r,\ $\{$X_m\ x_m, \ldots,\ X_n\ x_n$\}) $ \rightarrow L_0$} + +where $L_i$ is the least upper bound of $T_i$ and $S_i, i \in 0 .. r$ +\end{itemize} + +\commentary{% +Note that the non-generic case is covered by using $s = 0$, +in which case the type parameter declarations are omitted (\ref{generics}).% +} +----------------------------------------------------------------------!!!End + + \subsection{Least and Greatest Closure of Types} \LMLabel{leastAndGreatestClosureOfTypes} @@ -23993,7 +24623,8 @@ \subsection{Interface Types} Let $T$ be a type. We say that $T$ is an \Index{interface type} if{}f $T$ is of the form \code{$C$<\List{T}{1}{k}>}, -where $C$ denotes a class different from \code{Never} or a mixin, +where $C$ denotes a class different from \code{Never}, +or $C$ denotes a mixin, and \List{T}{1}{k} are arbitrary types. \commentary{% @@ -25083,111 +25714,6 @@ \subsubsection{Actual Types} } -\subsubsection{Least Upper Bounds} -\LMLabel{leastUpperBounds} - -% TODO(eernst): This section has been updated to take generic functions -% into account, but no other changes have been performed. Hence, we still -% need to update this section to use Dart 2 rules for LUB. - -\LMHash{}% -% does this diverge in some cases? -Given two interfaces $I$ and $J$, -let $S_I$ be the set of superinterfaces of $I$, -let $S_J$ be the set of superinterfaces of $J$ -and let $S = (\{I\} \cup S_I) \cap (\{J\} \cup S_J)$. -Furthermore, -we define $S_n = \{T | T \in S \wedge depth(T) = n\}$ for any finite $n$ -where $depth(T)$ is the number of steps in the longest inheritance path -from $T$ to \code{Object}. -%TODO(lrn): Specify: "inheritance path" is a path in the superinterface graph. -Let $q$ be the largest number such that $S_q$ has cardinality one, -which must exist because $S_0$ is $\{\code{Object}\}$. -The least upper bound of $I$ and $J$ is the sole element of $S_q$. - -\LMHash{}% -The least upper bound of \DYNAMIC{} and any type $T$ is \DYNAMIC. -The least upper bound of \VOID{} and any type $T \ne \DYNAMIC{}$ is \VOID. -The least upper bound of $\bot$ and any type $T$ is $T$. -Let $U$ be a type variable with upper bound $B$. -The least upper bound of $U$ and a type $T \ne \bot$ is -the least upper bound of $B$ and $T$. - -\LMHash{}% -The least upper bound operation is commutative and idempotent, -but it is not associative. - -% Function types - -%% TODO(eernst): This section should use the new syntax for function types -%% (\FunctionTypePositionalStd{} etc.); these updates are made by CL 84908. - -\LMHash{}% -The least upper bound of a function type and an interface type $T$ is -the least upper bound of \FUNCTION{} and $T$. -Let $F$ and $G$ be function types. -If $F$ and $G$ differ in their number of required parameters, -then the least upper bound of $F$ and $G$ is \FUNCTION. -Otherwise: -\begin{itemize} -\item If - -\noindent -\code{$F = $ <$X_1\ B_1, \ldots,\ X_s\ B_s$>($T_1, \ldots,\ T_r,\ $[$T_{r+1}, \ldots,\ T_n$]) $ \rightarrow T_0$} and - -\noindent -\code{$G = $ <$X_1\ B_1, \ldots,\ X_s\ B_s$>($S_1, \ldots,\ S_r,\ $[$S_{r+1}, \ldots,\ S_k$]) $ \rightarrow S_0$} - -\noindent -where $k \le n$ then the least upper bound of $F$ and $G$ is - -\noindent -\code{<$X_1\ B_1, \ldots,\ X_s\ B_s$>($L_1, \ldots,\ L_r,\ $[$L_{r+1}, \ldots,\ L_k$]) $ \rightarrow L_0$} - -\noindent -where $L_i$ is the least upper bound of $T_i$ and $S_i, i \in 0 .. k$. -\item If - -\noindent -\code{$F = $ <$X_1\ B_1, \ldots,\ X_s\ B_s$>($T_1, \ldots,\ T_r,\ $[$T_{r+1}, \ldots,\ T_n$]) $ \rightarrow T_0$}, - -\noindent -\code{$G = $ <$X_1\ B_1, \ldots,\ X_s\ B_s$>($S_1, \ldots,\ S_r,\ $\{ \ldots{} \}) $ \rightarrow S_0$} - -\noindent -then the least upper bound of $F$ and $G$ is - -\noindent -\code{<$X_1\ B_1, \ldots,\ X_s\ B_s$>($L_1, \ldots,\ L_r$) $ \rightarrow L_0$} - -\noindent -where $L_i$ is the least upper bound of $T_i$ and $S_i, i \in 0 .. r$. -\item If - -\noindent -\code{$F = $ <$X_1\ B_1, \ldots,\ X_s\ B_s$>($T_1, \ldots,\ T_r,\ $\{$T_{r+1}\ p_{r+1}, \ldots,\ T_f\ p_f$\}) $ \rightarrow T_0$}, - -\noindent -\code{$G = $ <$X_1\ B_1, \ldots,\ X_s\ B_s$>($S_1, \ldots,\ S_r,\ $\{$S_{r+1}\ q_{r+1}, \ldots,\ S_g\ q_g$\}) $ \rightarrow S_0$} - -then let -$\{x_m, \ldots, x_n\} = \{p_{r+1}, \ldots, p_f\} \cap \{q_{r+1}, \ldots, q_g\}$ -and let $X_j$ be the least upper bound of the types of $x_j$ in $F$ and -$G, j \in m .. n$. -Then the least upper bound of $F$ and $G$ is - -\noindent -\code{<$X_1\ B_1, \ldots,\ X_s\ B_s$>($L_1, \ldots,\ L_r,\ $\{$X_m\ x_m, \ldots,\ X_n\ x_n$\}) $ \rightarrow L_0$} - -where $L_i$ is the least upper bound of $T_i$ and $S_i, i \in 0 .. r$ -\end{itemize} - -\commentary{% -Note that the non-generic case is covered by using $s = 0$, -in which case the type parameter declarations are omitted (\ref{generics}).% -} - - \section{Type Inference} \LMLabel{typeInference} From 92cc0986f86a03e6627ab21b471837cb85b8c1ce Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Tue, 7 Dec 2021 18:08:59 +0100 Subject: [PATCH 35/85] Standard bounds --- specification/dartLangSpec.tex | 780 +++++++++++++++++++++------------ 1 file changed, 491 insertions(+), 289 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index eebfdbb30..a3f7d81a2 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -23722,15 +23722,14 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} of two or more types. \rationale{% -If the least upper bound $U$ exists, -then the standard upper bound is guaranteed to be a supertype of $U$, -and the standard upper bound is in any case guaranteed to be +If the least upper bound $U$ of the given operands exists, +then the standard upper bound is guaranteed to be a supertype of $U$. +The standard upper bound is in any case guaranteed to be a supertype of both operands. - -If the greatest lower bound $L$ exists, +Similarly, if the greatest lower bound $L$ of the given operands exists, then the standard lower bound is guaranteed to be a subtype of $L$, -and the standard lower bound is in any case guaranteed to be -a subtype of both operands.% +The standard lower bound is in any case guaranteed to be +a subtype of both operands. The standard upper and lower bounds are intended to compute the least upper bound respectively the greatest lower bound whenever possible, @@ -23758,19 +23757,24 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} are subtypes of both \code{I} and \code{J}.% } -\commentary{% +\rationale{% For backward compatibility, the standard upper and lower bounds are computed in the same way as in earlier versions of Dart, in the case where the operands are -interface types based on distinct classes. -E.g., \code{List{}>} and \code{Iterable} +interface types based on distinct classes +(\ref{dartOneStandardUpperBound}).% +} + +\commentary{% +For example, \code{List} and \code{Iterable} are handled in this manner, because \code{List} and \code{Iterable} are not the same class.% } \rationale{% -Consequently, the standard upper and lower bound algorithms may yield +Consequently, when such types occur as or in the operands, +the standard upper and lower bound algorithms may yield results which are suboptimal in the sense that we can rather easily find a better approximation of the least upper bound or the greatest lower bound. @@ -23788,7 +23792,8 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} The set of clauses defining each function in this section are ordered, i.e., the first clause that matches is the one that must be used. If a rule is dealing with type variables, -then it is assumed that the type variables have been renamed to fresh names, +then it is assumed that the type variables have been +consistently renamed to fresh names whenever necessary, such that no type variables are captured due to accidental name clashes. Finally, it is assumed that type aliases, including actual type arguments if any, @@ -23796,23 +23801,77 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} (\ref{typedef}), and that all types are denoted by a canonical name. - \commentary{% That is, we do not encounter the same type under the names \code{$C$} and \code{\metavar{myPrefix}.$C$}, -and we do not encounter different types with the same name.% +and we do not encounter different types with the same name. +This can be achieved for any given program by making sure that +every top-level name reference is accessed using +an import prefix which is unique for the library that contains +the given declaration, +even when it is accessed from the same library. +This means that $C$ is \emph{always} accessed as, say, +\code{\metavar{library87381}.$C$}.% } \LMHash{}% -Let $T_1$ and $T_2$ be two types. +Consequently, when we say that two types $T_1$ and $T_2$ are +\IndexCustom{syntactically equal}{type!syntactically equal}, +it refers to the situation where both $T_1$ and $T_2$ have been +transformed in the above sense +(\commentary{by alpha-renaming, alias expansion, and canonical naming}). + +%% TODO(eernst), for review: Is this the correct associativity of SUB/SLB? +\LMHash{}% We define the \Index{standard upper bound} -of $T_1$ and $T_2$ to be -\UpperBoundType{$T_1$}{$T_2$}, -where \UpperBoundTypeName{} is defined below. -We define the standard upper bound of \List{T}{1}{n} where $n > 2$ +of a single type $T$ to be $T$. +We define the standard upper bound of two types $T_1$ and $T_2$ to be +\UpperBoundType{$T_1$}{$T_2$}, where \UpperBoundTypeName{} is defined below. +Finally, we define the standard upper bound +of \List{T}{1}{n} where $n > 2$ to be \UpperBoundType{$T_1$}{$T'$}, where $T'$ is the standard upper bound of \List{T}{2}{n}. +We define the +\Index{standard lower bound} +of one or more types in the same way, +but using \LowerBoundTypeName{} rather than \UpperBoundTypeName{} everywhere. + +\LMHash{}% +It is at times convenient to be able to apply +\UpperBoundTypeName{} (respectively \LowerBoundTypeName) +to parameter type declarations, +which is defined as follows. +Assume that $P_1$ and $P_2$ are two formal parameter type declarations +with declared type $T_1$ respectively $T_2$, +such that both are positional or both are named, +with the same name \DefineSymbol{n}. +Then \UpperBoundType{$P_1$}{$P_2$} (respectively \LowerBoundType{$P_1$}{$P_2$}) +is the formal parameter declaration $P$, +with the following proporties: + +\begin{itemize} +\item + The declared type of $P$ is \UpperBoundType{$T_1$}{$T_2$} + (respectively\\ + \LowerBoundType{$T_1$}{$T_2$}). +\item + $P$ is positional if $P_1$ and $P_2$ are positional. + In this case, the name of $P$ is a fresh identifier. + \commentary{% + Names of positional parameter types are optional in some cases, + but using a fresh name is always possible.% + } +\item + $P$ is named if $P_1$ and $P_2$ are named. + In this case, the name of $P$ is $n$. + $P$ is marked with the modifier \REQUIRED{} + if both $P_1$ and $P_2$ have this modifier + (respectively, if either $P_1$ or $P_2$ has this modifier). +\end{itemize} + +\LMHash{}% +The function \UpperBoundTypeName{} is defined as follows. \begin{itemize} \item @@ -23828,9 +23887,8 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} \right.% } \item - \DefEquals{\UpperBoundType{$T_1$}{$T_2$}}{T_1}, if \IsTopType{$T_1$}. -\item - \DefEquals{\UpperBoundType{$T_1$}{$T_2$}}{T_2}, if \IsTopType{$T_2$}. + \DefEquals{\UpperBoundType{$T_1$}{$T_2$}}{T_i}, + if \IsTopType{$T_i$}, for each $i \in 1 .. 2$. \item If \IsBottomType{$T_1$} and \IsBottomType{$T_2$} then\\[1mm] \DefEquals{\UpperBoundType{$T_1$}{$T_2$}}{% @@ -23906,14 +23964,14 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} \right.% } \item - \DefEquals{\UpperBoundType{$T_1$?}{$T_2$?}}{\code{$S$?}},\\ - where $S$ is \UpperBoundType{$T_1$}{$T_2$}. + \DefEquals{\UpperBoundType{$T_1$?}{$T_2$?}}{\code{$T_3$?}},\\ + where $T_3$ is \UpperBoundType{$T_1$}{$T_2$}. \item - \DefEquals{\UpperBoundType{$T_1$?}{$T_2$}}{\code{$S$?}},\\ - where $S$ is \UpperBoundType{$T_1$}{$T_2$}. + \DefEquals{\UpperBoundType{$T_1$?}{$T_2$}}{\code{$T_3$?}},\\ + where $T_3$ is \UpperBoundType{$T_1$}{$T_2$}. \item - \DefEquals{\UpperBoundType{$T_1$}{$T_2$?}}{\code{$S$?}},\\ - where $S$ is \UpperBoundType{$T_1$}{$T_2$}. + \DefEquals{\UpperBoundType{$T_1$}{$T_2$?}}{\code{$T_3$?}},\\ + where $T_3$ is \UpperBoundType{$T_1$}{$T_2$}. \item Let $X_1$ be a type variable with bound $B_1$.\\[1mm] \DefEquals{\UpperBoundType{$X_1$}{$T_2$}}{% @@ -23999,7 +24057,12 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} let $B_{3i}$ be $B_{1i}$, and let $P_{3i}$ be \LowerBoundType{$P_{1i}$}{$P_{2i}$}. Then \DefEquals{\UpperBoundType{$U_1$}{$U_2$}}{% - \code{$T_3$\,\FUNCTION<$X_1$\,\EXTENDS\,$B_{31}$,\,\ldots,\,$X_m$\,\EXTENDS\,$B_{3m}$>($P_{31}$,\,\ldots,\,$P_{3q}$)}}. + \code{$T_3$\,\FUNCTION<$X_1$\,\EXTENDS\,$B_{31}$,\,\ldots,\,$X_m$\,% + \EXTENDS\,$B_{3m}$>($P_{31}$,\,\ldots,\,$P_{3q}$)}}. + + \commentary{% + This case includes non-generic function types by allowing $m$ to be zero.% + } \item Let $U_1$ and $U_2$ be, respectively, @@ -24012,22 +24075,23 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} \EXTENDS\,$B_{2m}$>($P_{21}$,\,\ldots,\,$P_{2k}$,\,$\metavar{Named}_2$)} \noindent - where $\metavar{Named}_1$ and $\metavar{Named}_2$ declare some named parameters, + where $\metavar{Named}_j$ declares a non-empty set of named parameters + with names $\metavar{NamesOfNamed}_j$, $j \in 1 .. 2$, and consider the case where the following is satisfied: \begin{itemize} \item Each $B_{1i}$ and $B_{2i}$ are syntactically equal types. - \item All positional parameters are required. - \item $\metavar{Named}_1$ contains an entry (optional or required) of the form \code{$R_{1i}$ $x_i$} - for every required named parameter \code{$R_{2i}$ $x_i$} in $\metavar{Named}_2$. - \item $\metavar{Named}_2$ contains an entry (optional or required) of the form \code{$R_{2i}$ $x_i$} - for every required named parameter \code{$R_{1i}$ $x_i$} in $\metavar{Named}_1$. + \item For each required entry named $n$ in $\metavar{Named}_1$, + $\metavar{Named}_2$ contains an entry named $n$ + (\commentary{which may or may not be required}). + \item For each required entry named $n$ in $\metavar{Named}_2$, + $\metavar{Named}_1$ contains an entry named $n$ + (\commentary{which may or may not be required}). \end{itemize} - Then \DefEquals{\UpperBoundType{$U_1$}{$U_2$}}{U_3}, where $U_3$ is - - \noindent - \code{$T_3$\,\FUNCTION<$X_1$\,\EXTENDS\,$B_{31}$,\,\ldots,\,$X_m$\,\EXTENDS\,$B_{3m}$>($P_{31}$,\,\ldots,\,$P_{3k}$,\,$\metavar{Named}_3$)}, + Then \DefEqualsNewline{\UpperBoundType{$U_1$}{$U_2$}}{% + \code{$T_3$\,\FUNCTION<$X_1$\,\EXTENDS\,$B_{31}$,\,\ldots,\,$X_m$\,% + \EXTENDS\,$B_{3m}$>($P_{31}$,\,\ldots,\,$P_{3k}$,\,$\metavar{Named}_3$)}}, \noindent where: @@ -24036,296 +24100,434 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} \item $T_3$ is \UpperBoundType{$T_1$}{$T_2$}. \item $B_{3i}$ is $B_{1i}$. \item $P_{3i}$ is \LowerBoundType{$P_{1i}$}{$P_{2i}$}. - \item $\metavar{Named}_3$ contains exactly \code{$R_{3i}$ $x_i$} for each $x_i$ in both $\metavar{Named}_1$ and - $\metavar{Named}_2$. - \begin{itemize} - \item where \code{$R_{1i}$ $x_i$} is in $\metavar{Named}_1$, - \item where \code{$R_{2i}$ $x_i$} is in $\metavar{Named}_2$, - \item and $R_{3i}$ is \LowerBoundType{$R_{1i}$}{$R_{2i}$}, - \item and \code{$R_{3i}$ $x_i$} is required if $x_i$ is required in either $\metavar{Named}_1$ or - $\metavar{Named}_2$. - \end{itemize} + \item $\metavar{Named}_3$ declares the set + of named parameter types with the names + $\metavar{NamesOfNamed}_1\cap\metavar{NamesOfNamed}_2$, + such that for each $P'$ in $\metavar{Named}_3$ with name $n$, + where $P'_1 \in \metavar{Named}_1$ + and $P'_2 \in \metavar{Named}_2$ + also have the name $n$, + $P'$ is \LowerBoundType{$P'_1$}{$P'_2$}. \end{itemize} + + \commentary{% + This case includes non-generic function types by allowing $m$ to be zero.% + } +%% +%% TODO(eernst), for review: Why do we not have a rule for +%% \UpperBoundType{T1 Function(P1..Pm, [...])}{T2 Function(P1..Pk, {...}}} +%% = T3 Function(R1..Rk), where the left operand has at least k parameters, +%% plus the converse? +%% \item - \UpperBoundType{$T$ \FUNCTION<\ldots>(\ldots)}{$S$ \FUNCTION<\ldots>(\ldots)} = \FUNCTION{} otherwise + \DefEquals{\UpperBoundType{$S_1$ \FUNCTION<\ldots>(\ldots)}{% + $S_2$ \FUNCTION<\ldots>(\ldots)}}{\FUNCTION}. + \commentary{% + This is a catch-all rule for the standard upper bound of two function types: + At least one operand in the following cases is not a function type.% + } \item - \UpperBoundType{$T$ \FUNCTION<\ldots>(\ldots)}{$T_2$} = \UpperBoundType{Object}{$T_2$} + \DefEqualsNewline{\UpperBoundType{$S$ \FUNCTION<\ldots>(\ldots)}{$T_2$}}{% + \UpperBoundType{Object}{$T_2$}}. \item - \UpperBoundType{$T_1$}{$T$ \FUNCTION<\ldots>(\ldots)} = \UpperBoundType{$T_1$}{Object} + \DefEqualsNewline{\UpperBoundType{$T_1$}{$S$ \FUNCTION<\ldots>(\ldots)}}{% + \UpperBoundType{$T_1$}{Object}}. \item - \UpperBoundType{FutureOr<$T_1$>}{FutureOr<$T_2$>} = \code{FutureOr<$T_3$>} where $T_3$ = \UpperBoundType{$T_1$}{$T_2$} + \DefEquals{\UpperBoundType{FutureOr<$T_1$>}{FutureOr<$T_2$>}}{% + \code{FutureOr<$T_3$>}}, + where $T_3$ = \UpperBoundType{$T_1$}{$T_2$}. \item - \UpperBoundType{Future<$T_1$>}{FutureOr<$T_2$>} = \code{FutureOr<$T_3$>} where $T_3$ = \UpperBoundType{$T_1$}{$T_2$} + \DefEquals{\UpperBoundType{Future<$T_1$>}{FutureOr<$T_2$>}}{% + \code{FutureOr<$T_3$>}}, + where $T_3$ = \UpperBoundType{$T_1$}{$T_2$}. \item - \UpperBoundType{FutureOr<$T_1$>}{Future<$T_2$>} = \code{FutureOr<$T_3$>} where $T_3$ = \UpperBoundType{$T_1$}{$T_2$} + \DefEquals{\UpperBoundType{FutureOr<$T_1$>}{Future<$T_2$>}}{% + \code{FutureOr<$T_3$>}}, + where $T_3$ = \UpperBoundType{$T_1$}{$T_2$}. \item - \UpperBoundType{$T_1$}{FutureOr<$T_2$>} = \code{FutureOr<$T_3$>} where $T_3$ = \UpperBoundType{$T_1$}{$T_2$} + \DefEquals{\UpperBoundType{$T_1$}{FutureOr<$T_2$>}}{% + \code{FutureOr<$T_3$>}}, + where $T_3$ = \UpperBoundType{$T_1$}{$T_2$}. \item - \UpperBoundType{FutureOr<$T_1$>}{$T_2$} = \code{FutureOr<$T_3$>} where $T_3$ = \UpperBoundType{$T_1$}{$T_2$} -\item \UpperBoundType{$T_1$}{$T_2$} = $T_2$ if \SubtypeNE{T_1}{T_2} - - Note that both types must be class types at this point -\item - \UpperBoundType{$T_1$}{$T_2$} = $T_1$ if \SubtypeNE{T_2}{T_1} - - Note that both types must be class types at this point -- \UpperBoundType{$C$<$T_0$, \ldots, $T_n$>}{$C$<$S_0$, \ldots, $S_n$>} = \code{$C$<$R_0$, \ldots, $R_f$n>} where $R_i$ is \UpperBoundType{$T_i$}{$S_i$} -- \UpperBoundType{$C_0$<$T_0$, \ldots, $T_n$>}{$C_1$<$S_0$, \ldots, $S_k$>} = least upper bound of two - interfaces as in Dart 1, with modifications for handling mixed null safe and - legacy code as follows: - - For an upper bound computation in a legacy library, the set of - super-interfaces used consists of the `LEGACY_ERASURE` of the - super-interfaces of the two types. - - For an upper bound computation in an opted in library, no modification of - the set of super-interfaces is performed. -\end{itemize} + \DefEquals{\UpperBoundType{FutureOr<$T_1$>}{$T_2$}}{% + \code{FutureOr<$T_3$>}}, + where $T_3$ = \UpperBoundType{$T_1$}{$T_2$}. +\item + \DefEquals{\UpperBoundType{$T_1$}{$T_2$}}{T_2}, + if \SubtypeNE{T_1}{T_2}. -\paragraph{Lower bounds} + \commentary{ + In this and in the following cases, both types must be interface types.% + } +\item + \DefEquals{\UpperBoundType{$T_1$}{$T_2$}}{T_1}, + if \SubtypeNE{T_2}{T_1}. +\item + \DefEqualsNewline{% + \UpperBoundType{$C$<$T_0$, \ldots, $T_n$>}{$C$<$S_0$, \ldots, $S_n$>}}{% + \code{$C$<$R_0$, \ldots, $R_n$>}}, + where $R_i$ is \UpperBoundType{$T_i$}{$S_i$}, $i \in 1 .. n$. +\item + \UpperBoundType{$C_0$<$T_0$, \ldots, $T_n$>}{$C_1$<$S_0$, \ldots, $S_k$>} + is determined using the algorithm described below + (\ref{dartOneStandardUpperBound}). +\end{itemize} \LMHash{}% -We define the lower bound of two types $T_1$ and $T_2$ to be \LowerBoundType{$T_1$}{$T_2$} as -follows. +The function \LowerBoundType{$T_1$}{$T_2$} is defined as follows. \begin{itemize} -\item \LowerBoundType{$T$}{$T$} = $T$ - -\item \LowerBoundType{$T_1$}{$T_2$} where \IsTopType{$T_1$} and \IsTopType{$T_2$} = - - $T_1$ if \IsMoreTopType{$T_2$}{$T_1$} - - $T_2$ otherwise -\item \LowerBoundType{$T_1$}{$T_2$} = $T_2$ if \IsTopType{$T_1$} -\item \LowerBoundType{$T_1$}{$T_2$} = $T_1$ if \IsTopType{$T_2$} - -\item \LowerBoundType{$T_1$}{$T_2$} where \IsBottomType{$T_1$} and \IsBottomType{$T_2$} = - - $T_1$ if \IsMoreBottomType{$T_1$}{$T_2$} - - $T_2$ otherwise -\item \LowerBoundType{$T_1$}{$T_2$} = $T_2$ if \IsBottomType{$T_2$} -\item \LowerBoundType{$T_1$}{$T_2$} = $T_1$ if \IsBottomType{$T_1$} -\item \LowerBoundType{$T_1$}{$T_2$} where \IsNullType{$T_1$} and \IsNullType{$T_2$} = - - $T_1$ if \IsMoreBottomType{$T_1$}{$T_2$} - - $T_2$ otherwise -\item \LowerBoundType{Null}{$T_2$} = - - \code{Null} if \SubtypeNE{\code{Null}}{T_2} - - \code{Never} otherwise -\item \LowerBoundType{$T_1$}{Null} = - - \code{Null} if \SubtypeNE{\code{Null}}{T_1} - - \code{Never} otherwise -\item \LowerBoundType{$T_1$}{$T_2$} where \IsObjectType{$T_1$} and \IsObjectType{$T_2$} = - - $T_1$ if \IsMoreTopType{$T_2$}{$T_1$} - - $T_2$ otherwise -\item \LowerBoundType{$T_1$}{$T_2$} where \IsObjectType{$T_1$} = - - $T_2$ if $T_2$ is non-nullable - - \NonNullType{$T_2$} if \NonNullType{$T_2$} is non-nullable - - \code{Never} otherwise -\item \LowerBoundType{$T_1$}{$T_2$} where \IsObjectType{$T_2$} = - - $T_1$ if $T_1$ is non-nullable - - \NonNullType{$T_1$} if \NonNullType{$T_1$} is non-nullable - - \code{Never} otherwise -\item \LowerBoundType{$T_1$?}{$T_2$?} = \code{$S$?} where $S$ is \LowerBoundType{$T_1$}{$T_2$} -\item \LowerBoundType{$T_1$?}{$T_2$} = $S$ where $S$ is \LowerBoundType{$T_1$}{$T_2$} -\item \LowerBoundType{$T_1$}{$T_2$?} = $S$ where $S$ is \LowerBoundType{$T_1$}{$T_2$} -\item \LowerBoundType{$T_0$ \FUNCTION<$X_0$ \EXTENDS{} $B_{00}$, \ldots, $X_m$ \EXTENDS{} $B_{0m}$>($P_{00}$, \ldots, $P_{0k}$)}{% - $T_1$ \FUNCTION<$X_0$ \EXTENDS{} $B_{10}$, \ldots, $X_m$ \EXTENDS{} $B_{1m}$>($P_{10}$, \ldots, $P_{1l}$)} = - \code{$R_0$ \FUNCTION<$X_0$ \EXTENDS{} $B_{20}$, \ldots, $X_m$ \EXTENDS{} $B_{2m}$>($P_{20}$, \ldots, $P_{2q}$)} if: - - each $B_{0i}$ and $B_{1i}$ are equal types (syntactically) - - $q$ is max($k$, $l$) - - $R_0$ is \LowerBoundType{$T_0$}{$T_1$} - - $B_{2i}$ is $B_{0i}$ - - $P_{2i}$ is \UpperBoundType{$P_{0i}$}{$P_{1i}$} for $i \leq \metavar{min}(k, l)$ - - $P_{2i}$ is $P_{0i}$ for $k < i \leq q$ - - $P_{2i}$ is $P_{1i}$ for $l < i \leq q$ - - $P_{2i}$ is optional if $P_{0i}$ or $P_{1i}$ is optional, or if $\metavar{min}(k, l) < i \leq q$ -\item \LowerBoundType{$T_0$ \FUNCTION<$X_0$ \EXTENDS{} $B_{00}$, \ldots, $X_m$ \EXTENDS{} $B_{0m}$>($P_{00}$, \ldots, $P_{0k}$, $\metavar{Named}_0$)}{% - $T_1$ \FUNCTION<$X_0$ \EXTENDS{} $B_{10}$, \ldots, $X_m$ \EXTENDS{} $B_{1m}$>($P_{10}$, \ldots, $P_{1k}$, $\metavar{Named}_1$)} = - \code{$R_0$ \FUNCTION<$X_0$ \EXTENDS{} $B_{20}$, \ldots, $X_m$ \EXTENDS{} $B_{2m}$>($P_{20}$, \ldots, $P_{2k}$, $\metavar{Named}_2$)} if: - - each $B_{0i}$ and $B_{1i}$ are equal types (syntactically) - - $R_0$ is \LowerBoundType{$T_0$}{$T_1$} - - $B_{2i}$ is $B_{0i}$ - - $P_{2i}$ is \UpperBoundType{$P_{0i}$}{$P_{1i}$} - - $\metavar{Named}_2$ contains \code{$R_{2i}$ $x_i$} for each $x_i$ in both $\metavar{Named}_0$ and $\metavar{Named}_1$ - - where \code{$R_{0i}$ $x_i$} is in $\metavar{Named}_0$ - - where \code{$R_{1i}$ $x_i$} is in $\metavar{Named}_1$ - - and $R_{2i}$ is \UpperBoundType{$R_{0i}$}{$R_{1i}$} - - and \code{$R_{2i}$ $x_i$} is required if $x_i$ is required in both $\metavar{Named}_0$ and $\metavar{Named}_1$ - - $\metavar{Named}_2$ contains \code{$R_{0i}$ $x_i$} for each $x_i$ in $\metavar{Named}_0$ and not $\metavar{Named}_1$ - - where $x_i$ is optional in $\metavar{Named}_2$ - - $\metavar{Named}_2$ contains \code{$R_{1i}$ $x_i$} for each $x_i$ in $\metavar{Named}_1$ and not $\metavar{Named}_0$ - - where $x_i$ is optional in $\metavar{Named}_2$ -\item \LowerBoundType{$T$ \FUNCTION<\ldots>(\ldots)}{$S$ \FUNCTION<\ldots>(\ldots)} = \code{Never} otherwise -\item \LowerBoundType{$T_1$}{$T_2$} = $T_1$ if \SubtypeNE{T_1}{T_2} -\item \LowerBoundType{$T_1$}{$T_2$} = $T_2$ if \SubtypeNE{T_2}{T_1} -\item \LowerBoundType{FutureOr<$T_1$>}{FutureOr<$T_2$>} = \code{FutureOr<$S$>} - - where $S$ is \LowerBoundType{$T_1$}{$T_2$} -\item \LowerBoundType{FutureOr<$T_1$>}{Future<$T_2$>} = \code{Future<$S$>} - - where $S$ is \LowerBoundType{$T_1$}{$T_2$} -\item \LowerBoundType{Future<$T_1$>}{FutureOr<$T_2$>} = \code{Future<$S$>} - - where $S$ is \LowerBoundType{$T_1$}{$T_2$} -\item \LowerBoundType{FutureOr<$T_1$>}{$T_2$} = $S$ - - where $S$ is \LowerBoundType{$T_1$}{$T_2$} -\item \LowerBoundType{$T_1$}{FutureOr<$T_2$>} = $S$ - - where $S$ is \LowerBoundType{$T_1$}{$T_2$} -\item \LowerBoundType{$T_1$}{$T_2$} = \code{Never} otherwise -\end{itemize} +\item + \DefEquals{\LowerBoundType{$T$}{$T$}}{T}. +\item + If \IsTopType{$T_1$} and \IsTopType{$T_2$} then\\[1mm] + \DefEquals{\LowerBoundType{$T_1$}{$T_2$}}{% + \left\{% + \begin{array}{l} + T_1\mbox{, if \IsMoreTopType{$T_2$}{$T_1$}.}\\ + T_2\mbox{, otherwise.} + \end{array} + \right.% + } +\item + \DefEquals{\LowerBoundType{$T_1$}{$T_2$}}{T_2}, if \IsTopType{$T_1$}. +\item + \DefEquals{\LowerBoundType{$T_1$}{$T_2$}}{T_1}, if \IsTopType{$T_2$}. +\item + If \IsBottomType{$T_1$} and \IsBottomType{$T_2$} then\\[1mm] + \DefEquals{\LowerBoundType{$T_1$}{$T_2$}}{% + \left\{% + \begin{array}{l} + T_1\mbox{, if \IsMoreBottomType{$T_1$}{$T_2$}.}\\ + T_2\mbox{, otherwise.} + \end{array} + \right.% + } +\item + \DefEquals{\LowerBoundType{$T_1$}{$T_2$}}{T_i}, + if \IsBottomType{$T_i$}, for each $i \in 1 .. 2$. +\item + If \IsNullType{$T_1$} and \IsNullType{$T_2$} then\\[1mm] + \DefEquals{\LowerBoundType{$T_1$}{$T_2$}}{% + \left\{% + \begin{array}{l} + T_1\mbox{, if \IsMoreBottomType{$T_1$}{$T_2$}.}\\ + T_2\mbox{, otherwise.} + \end{array} + \right.% + } +\item + %% TODO(eernst), for review: The null safety spec uses `Null` as the left + %% operand, but implementations seem to use the slightly more general + %% \IsNullType{$T_1$}. So I specified that. + If \IsNullType{$T_1$} then\\[1mm] + \DefEquals{\LowerBoundType{$T_1$}{$T_2$}}{% + \left\{% + \begin{array}{l} + T_1\mbox{, if \SubtypeNE{T_1}{T_2}.}\\ + \code{Never}\mbox{, otherwise.} + \end{array} + \right.% + } +\item + %% TODO(eernst), for review: The null safety spec uses `Null` as the right + %% operand, but implementations seem to use the slightly more general + %% \IsNullType{$T_2$}. So I specified that. + If \IsNullType{$T_2$} then\\[1mm] + \DefEquals{\LowerBoundType{$T_1$}{$T_2$}}{% + \left\{% + \begin{array}{l} + T_2\mbox{, if \SubtypeNE{T_2}{T_1}.}\\ + \code{Never}\mbox{, otherwise.} + \end{array} + \right.% + } +\item + If \IsObjectType{$T_1$} and \IsObjectType{$T_2$} then\\[1mm] + \DefEquals{\LowerBoundType{$T_1$}{$T_2$}}{% + \left\{% + \begin{array}{l} + T_1\mbox{, if \IsMoreTopType{$T_2$}{$T_1$}.}\\ + T_2\mbox{, otherwise.} + \end{array} + \right.% + } +\item + If \IsObjectType{$T_1$} then\\[1mm] + \DefEquals{\LowerBoundType{$T_1$}{$T_2$}}{% + \left\{% + \begin{array}{l} + T_2\mbox{, if $T_2$ is non-nullable.}\\ + \NonNullType{$T_2$}\mbox{, if \NonNullType{$T_2$} is non-nullable.}\\ + \code{Never}\mbox{, otherwise.} + \end{array} + \right.% + } +\item + If \IsObjectType{$T_2$} then\\[1mm] + \DefEquals{\LowerBoundType{$T_1$}{$T_2$}}{% + \left\{% + \begin{array}{l} + T_1\mbox{, if $T_1$ is non-nullable.}\\ + \NonNullType{$T_1$}\mbox{, if \NonNullType{$T_1$} is non-nullable.}\\ + \code{Never}\mbox{, otherwise.} + \end{array} + \right.% + } +\item + \DefEquals{\LowerBoundType{$T_1$?}{$T_2$?}}{\code{$T_3$?}},\\ + where $T_3$ is \LowerBoundType{$T_1$}{$T_2$}. +\item + \DefEquals{\LowerBoundType{$T_1$?}{$T_2$}}{T_3},\\ + where $T_3$ is \LowerBoundType{$T_1$}{$T_2$}. +\item + \DefEquals{\LowerBoundType{$T_1$}{$T_2$?}}{T_3},\\ + where $T_3$ is \LowerBoundType{$T_1$}{$T_2$}. +%% +%% Note that we do not need rules like the following (as opposed to the +%% situation with \UpperBoundTypeName{}), because they are already covered +%% by the cases below where $T_1$ and $T_2$ are subtypes of each other: +%% +%% \DefEquals{\LowerBoundType{$T_1$}{\FUNCTION}}{T_1}, +%% where $T_1$ is a function type. +%% \DefEquals{\UpperBoundType{\FUNCTION}{$T_2$}}{T_2}, +%% where $T_2$ is a function type. +%% +\item + Let $U_1$ and $U_2$ be, respectively, + + \noindent + \code{$T_1$\,\FUNCTION<$X_1$\,\EXTENDS\,$B_{11}$,\,\ldots,\,$X_m$\,% + \EXTENDS\,$B_{1m}$>($P_{11}$,\,\ldots,\,$P_{1k}$)} -\paragraph{Issues and Interesting examples} + \noindent + \code{$T_2$\,\FUNCTION<$X_1$\,\EXTENDS\,$B_{21}$,\,\ldots,\,$X_m$\,% + \EXTENDS\,$B_{2m}$>($P_{21}$,\,\ldots,\,$P_{2l}$)} -The definition of upper bound for type variables does not guarantee termination. -Counterexample: + \noindent + such that each $B_{1i}$ and $B_{2i}$ are syntactically equal types. + Let $q$ be $\metavar{max}(k, l)$, + let $T_3$ be \LowerBoundType{$T_1$}{$T_2$}, + let $B_{3i}$ be $B_{1i}$, and + let $P_{3i}$ be determined as follows: + + \begin{itemize} + \item $P_{3i}$ is \UpperBoundType{$P_{1i}$}{$P_{2i}$} for + $i \leq \metavar{min}(k, l)$. + \item $P_{3i}$ is $P_{1i}$ for $k < i \leq q$, when $k = q$; + and $P_{3i}$ is $P_{2i}$ for $l < i \leq q$, when $l = q$. + \item $P_{3i}$ is optional if $P_{1i}$ or $P_{2i}$ is optional, + or if $\metavar{min}(k, l) < i \leq q$. + \end{itemize} + + Then \DefEqualsNewline{\LowerBoundType{$U_1$}{$U_2$}}{% + \code{$T_3$\,\FUNCTION<$X_1$\,\EXTENDS\,$B_{31}$,\,\ldots,\,$X_m$\,% + \EXTENDS\,$B_{3m}$>($P_{31}$,\,\ldots,\,$P_{3q}$)}}. -\begin{dartCode} -\VOID{} foo, S \EXTENDS{} List>() \{ - T x; - S y; - \VAR{} a = (x == y) ? x : y; -\} -\end{dartCode} + \commentary{% + This case includes non-generic function types by allowing $m$ to be zero.% + } +\item + Let $U_1$ and $U_2$ be, respectively, -It should be changed to close the bound with respect to all of the type -variables declared in the same scope, using the greatest closure definition. + \noindent + \code{$T_1$\,\FUNCTION<$X_1$\,\EXTENDS\,$B_{11}$,\,\ldots,\,$X_m$\,% + \EXTENDS\,$B_{1m}$>($P_{11}$,\,\ldots,\,$P_{1k}$,\,$\metavar{Named}_1$)} -\paragraph{Asymmetry} + \noindent + \code{$T_2$\,\FUNCTION<$X_1$\,\EXTENDS\,$B_{21}$,\,\ldots,\,$X_m$\,% + \EXTENDS\,$B_{2m}$>($P_{21}$,\,\ldots,\,$P_{2k}$,\,$\metavar{Named}_2$)} + + \noindent + where $\metavar{Named}_j$ declares a non-empty set of named parameters + with names $\metavar{NamesOfNamed}_j$, $j \in 1 .. 2$, + and consider the case where + each $B_{1i}$ and $B_{2i}$ are syntactically equal types. + Then \DefEqualsNewline{\LowerBoundType{$U_1$}{$U_2$}}{%U_3}, where $U_3$ is + \code{$T_3$\,\FUNCTION<$X_1$\,\EXTENDS\,$B_{31}$,\,\ldots,\,$X_m$\,% + \EXTENDS\,$B_{3m}$>($P_{31}$,\,\ldots,\,$P_{3k}$,\,$\metavar{Named}_3$)}}, -The current algorithm is asymmetric. There is an equivalence class of top -types, and we correctly choose a canonical representative for bare top types -using the \IsMoreTopTypeName{} predicate. However, when two different top types are -embedded in two mutual subtypes, we don't correctly choose a canonical -representative. + \noindent + where: -\begin{dartCode} -\IMPORT{} 'dart:async'; + \begin{itemize} + \item $T_3$ is \LowerBoundType{$T_1$}{$T_2$}. + \item $B_{3i}$ is $B_{1i}$. + \item $P_{3i}$ is \UpperBoundType{$P_{1i}$}{$P_{2i}$}. + \item $\metavar{Named}_3$ declares the set + of named parameter types with the names + $\metavar{NamesOfNamed}_1\cup\metavar{NamesOfNamed}_2$, + such that for each $P'$ in $\metavar{Named}_3$ with name $n$, + the following is satisfied: + \begin{itemize} + \item If $\metavar{Named}_1$ declares $P'_1$ with name $n$, + and $\metavar{Named}_2$ declares $P'_2$ with name $n$, + then $P'$ is \UpperBoundType{$P'_1$}{$P'_2$}. + \item Otherwise, if $\metavar{Named}_1$ declares $P'_1$ with name $n$, + $P'$ is $P'_1$, + except that $P'$ does not have the modifier \REQUIRED. + \commentary{$P'_1$ may or may not have that modifier, but $P'$ does not.} + \item Otherwise, it is guaranteed that + $\metavar{Named}_2$ declares a $P'_2$ with name $n$. + In this case $P'$ is $P'_2$, + except that $P'$ does not have the modifier \REQUIRED. + \end{itemize} + \end{itemize} -\VOID{} main() \{ - List> x; - List<\DYNAMIC> y; - String s; - // List<\DYNAMIC> - var a = (x == y) ? x : y; - // List> - var b = (x == y) ? y : x; -\end{dartCode} + \commentary{% + This case includes non-generic function types by allowing $m$ to be zero.% + } +\item + \DefEquals{% + \LowerBoundType{$S_1$ \FUNCTION<\ldots>(\ldots)}{% + $S_2$ \FUNCTION<\ldots>(\ldots)}}{% + \code{Never}}, + otherwise. + \commentary{% + This is a catch-all rule for the standard lower bound of two function types: + At least one operand in the following cases is not a function type.% + } +\item + \DefEquals{\LowerBoundType{$T_1$}{$T_2$}}{T_1}, if \SubtypeNE{T_1}{T_2}. +\item + \DefEquals{\LowerBoundType{$T_1$}{$T_2$}}{T_2}, if \SubtypeNE{T_2}{T_1}. +\item + \DefEquals{\LowerBoundType{FutureOr<$T_1$>}{FutureOr<$T_2$>}}{% + \code{FutureOr<$T_3$>}},\\ + where $T_3$ is \LowerBoundType{$T_1$}{$T_2$}. +\item + \DefEquals{\LowerBoundType{FutureOr<$T_1$>}{Future<$T_2$>}}{% + \code{Future<$T_3$>}},\\ + where $T_3$ is \LowerBoundType{$T_1$}{$T_2$}. +\item + \DefEquals{\LowerBoundType{Future<$T_1$>}{FutureOr<$T_2$>}}{% + \code{Future<$T_3$>}},\\ + where $T_3$ is \LowerBoundType{$T_1$}{$T_2$}. +\item + \DefEquals{\LowerBoundType{FutureOr<$T_1$>}{$T_2$}}{T_3},\\ + where $T_3$ is \LowerBoundType{$T_1$}{$T_2$}. +\item + \DefEquals{\LowerBoundType{$T_1$}{FutureOr<$T_2$>}}{T_3},\\ + where $T_3$ is \LowerBoundType{$T_1$}{$T_2$}. +\item + \DefEquals{\LowerBoundType{$T_1$}{$T_2$}}{\code{Never}}, otherwise. +\end{itemize} -The best solution for this is probably to normalize the types. This is fairly -straightforward: we just normalize `FutureOr` to the normal form of $T$ when -$T$ is a top type. We can then inductively apply this across the rest of the -types. Then, whenever we have mutual subtypes, we just return the normal form. -This would be breaking, albeit hopefully only in a minor way. - -An alternative would be to try to define an ordering on mutual subtypes. This -can probably be done, but is a bit ugly. For example, consider `Map>` vs `Map, dynamic>`. The obvious way to -proceed is to define the total order by defining a traversal order on types, and -then defining the ordering lexicographically. That is, saying that $T$ is -greater than $S$ if the first pair of top types encountered in the traversal -that are not identical are `T0` and `S0` respectively, and \IsMoreTopType{$T_0$}{$S_0$}. - -A similar treatment would need to be done for the bottom types as well, since -there are two equivalences there. - - `X extends T` is equivalent to \code{Null} if $T$ is equivalent to \code{Null}. - - `FutureOr` is equivalent `Future`. - -A possible variant of the previous approach would be to define a finer grained -variant of the subtyping relation which is a total order on mutual subtypes. -That is, if `<::` is the extended relation, we would want that `T <:: S` implies -that `T <: S`, but also that `T <:: S` and `S <:: T` implies that $S$ and $T$ -are syntactically (rather than just semantically) equal. - - ----------------------------------------------------------------------- !!!End - -\LMHash{}% -% does this diverge in some cases? -Given two interfaces $I$ and $J$, -let $S_I$ be the set of superinterfaces of $I$, -let $S_J$ be the set of superinterfaces of $J$ -and let $S = (\{I\} \cup S_I) \cap (\{J\} \cup S_J)$. -Furthermore, -we define $S_n = \{T | T \in S \wedge depth(T) = n\}$ for any finite $n$ -where $depth(T)$ is the number of steps in the longest inheritance path -from $T$ to \code{Object}. -%TODO(lrn): Specify: "inheritance path" is a path in the superinterface graph. -Let $q$ be the largest number such that $S_q$ has cardinality one, -which must exist because $S_0$ is $\{\code{Object}\}$. -The least upper bound of $I$ and $J$ is the sole element of $S_q$. - -\LMHash{}% -The least upper bound of \DYNAMIC{} and any type $T$ is \DYNAMIC. -The least upper bound of \VOID{} and any type $T \ne \DYNAMIC{}$ is \VOID. -The least upper bound of $\bot$ and any type $T$ is $T$. -Let $U$ be a type variable with upper bound $B$. -The least upper bound of $U$ and a type $T \ne \bot$ is -the least upper bound of $B$ and $T$. - -\LMHash{}% -The least upper bound operation is commutative and idempotent, -but it is not associative. - -% Function types - -%% TODO(eernst): This section should use the new syntax for function types -%% (\FunctionTypePositionalStd{} etc.); these updates are made by CL 84908. - -\LMHash{}% -The least upper bound of a function type and an interface type $T$ is -the least upper bound of \FUNCTION{} and $T$. -Let $F$ and $G$ be function types. -If $F$ and $G$ differ in their number of required parameters, -then the least upper bound of $F$ and $G$ is \FUNCTION. -Otherwise: -\begin{itemize} -\item If +\rationale{ +The rules defining \UpperBoundTypeName{} and \LowerBoundTypeName{} +are somewhat redundant in that they explicitly specify +a lot of pairs of symmetric cases. +It might be sufficient to add ``and the converse'' to +the first of any such pair of rules and eliminate the second one, +or we could introduce meta-level abstraction over this kind of symmetry +as well as other kinds of redundancy. +However, we have chosen to tolerate the redundancy, +because the alternatives are ambiguous or complex.% +} -\noindent -\code{$F = $ <$X_1\ B_1, \ldots,\ X_s\ B_s$>($T_1, \ldots,\ T_r,\ $[$T_{r+1}, \ldots,\ T_n$]) $ \rightarrow T_0$} and -\noindent -\code{$G = $ <$X_1\ B_1, \ldots,\ X_s\ B_s$>($S_1, \ldots,\ S_r,\ $[$S_{r+1}, \ldots,\ S_k$]) $ \rightarrow S_0$} +\subsubsection{The Standard Upper Bound of Distinct Interface Types} +\LMLabel{dartOneStandardUpperBound} -\noindent -where $k \le n$ then the least upper bound of $F$ and $G$ is +\rationale{% +In order to avoid termination issues known from other languages +with the computation of `least upper bound' and similar notions, +early versions of Dart adopted a rather simple algorithm. +In particular, it does not rely on recursive invocations +of the same algorithm on subterms of the operands. +This algorithm has been preserved, +in order to maintain backward compatibility, +in spite of the fact that it may deliver results +which are obviously not as tight as they could have been.% +} -\noindent -\code{<$X_1\ B_1, \ldots,\ X_s\ B_s$>($L_1, \ldots,\ L_r,\ $[$L_{r+1}, \ldots,\ L_k$]) $ \rightarrow L_0$} +\commentary{% +For example, the algorithm yields \code{Object} as the standard upper bound of +\code{List{}>} and \code{Iterable}, +but it is easy to see that a tighter upper bound exists, e.g., +\code{Iterable{}>}.% +} -\noindent -where $L_i$ is the least upper bound of $T_i$ and $S_i, i \in 0 .. k$. -\item If +\LMHash{}% +We define the auxiliary function \NominalTypeDepthName{} +on interface types and \code{Object?} as follows: -\noindent -\code{$F = $ <$X_1\ B_1, \ldots,\ X_s\ B_s$>($T_1, \ldots,\ T_r,\ $[$T_{r+1}, \ldots,\ T_n$]) $ \rightarrow T_0$}, +\begin{itemize} +\item + \DefEquals{\NominalTypeDepth{Object?}}{0}. +\item + \DefEquals{\NominalTypeDepth{Object}}{1}. +\item + \DefEquals{\NominalTypeDepth{Null}}{1}. +\item + Let $T$ be a class or a mixin, + and let $M$ be the set of immediate superinterfaces of $T$. + Then \DefEquals{\NominalTypeDepth{$T$}}{d + 1}, + where $d$ is + $\metavar{max}\,\{\;\NominalTypeDepth{$S$}\;|\;S\;\in M\;\}$. +\end{itemize} -\noindent -\code{$G = $ <$X_1\ B_1, \ldots,\ X_s\ B_s$>($S_1, \ldots,\ S_r,\ $\{ \ldots{} \}) $ \rightarrow S_0$} +\commentary{% -\noindent -then the least upper bound of $F$ and $G$ is +} -\noindent -\code{<$X_1\ B_1, \ldots,\ X_s\ B_s$>($L_1, \ldots,\ L_r$) $ \rightarrow L_0$} +\LMHash{}% +\BlindDefineSymbol{I, J, M}% +The algorithm that determines +the standard upper bound of two distinct interface types +works as follows. +Let $I$ and $J$ be interface types, +let $M_I$ be the set of superinterfaces of $I$, +let $M_J$ be the set of superinterfaces of $J$, +and let $M$ be the set $(\{I\} \cup M_I) \cap (\{J\} \cup M_J)$. -\noindent -where $L_i$ is the least upper bound of $T_i$ and $S_i, i \in 0 .. r$. -\item If +\LMHash{}% +Let $M_n$ be the set +$\{\;T\;|\;T\,\in\,M\;\wedge\;\NominalTypeDepth{$T$}\,=\,n\,\}$ +for any natural number $n$. +Let $q$ be the largest number such that $M_q$ has cardinality one. +Such a number must exist because $M_0$ is $\{\code{Object?}\}$. +The least upper bound of $I$ and $J$ is then the sole element of $M_q$. -\noindent -\code{$F = $ <$X_1\ B_1, \ldots,\ X_s\ B_s$>($T_1, \ldots,\ T_r,\ $\{$T_{r+1}\ p_{r+1}, \ldots,\ T_f\ p_f$\}) $ \rightarrow T_0$}, -\noindent -\code{$G = $ <$X_1\ B_1, \ldots,\ X_s\ B_s$>($S_1, \ldots,\ S_r,\ $\{$S_{r+1}\ q_{r+1}, \ldots,\ S_g\ q_g$\}) $ \rightarrow S_0$} +\subsubsection{Standard Upper Bound Termination} +\LMLabel{standardUpperBoundTermination} -then let -$\{x_m, \ldots, x_n\} = \{p_{r+1}, \ldots, p_f\} \cap \{q_{r+1}, \ldots, q_g\}$ -and let $X_j$ be the least upper bound of the types of $x_j$ in $F$ and -$G, j \in m .. n$. -Then the least upper bound of $F$ and $G$ is +%% TODO(eernst), for review: This section could be turned into a few github +%% issues, but it seems reasonable to let the user of Dart know about it. +%% So do we include it, or do we delete the whole section (and create those +%% github issues)? +%% +%% Note that I omitted the symmetry issue: The given example does not +%% demonstrate any asymmetry (both results have type `List`). +%% So we should study the symmetry properties a bit more if we keep this +%% section. -\noindent -\code{<$X_1\ B_1, \ldots,\ X_s\ B_s$>($L_1, \ldots,\ L_r,\ $\{$X_m\ x_m, \ldots,\ X_n\ x_n$\}) $ \rightarrow L_0$} +\commentary{% +The definition of the standard upper bound for type variables +does not guarantee termination. +Here is a counterexample: +} -where $L_i$ is the least upper bound of $T_i$ and $S_i, i \in 0 .. r$ -\end{itemize} +\begin{dartCode} +\VOID{} foo, S \EXTENDS{} List>() \{ + T x; + S y; + \VAR{} a = (x == y) ? x : y; +\} +\end{dartCode} -\commentary{% -Note that the non-generic case is covered by using $s = 0$, -in which case the type parameter declarations are omitted (\ref{generics}).% +\rationale{% +The algorithm could be changed to use the greatest closure +(\ref{leastAndGreatestClosureOfTypes}) +with respect to all of the type variables declared in the same scope. + +The change has not been introduced at this point +because the phenomenon is expected to be very rare. +It is a breaking change because the standard upper bound of certain types +will be a proper supertype of the results obtained +with the current algorithm.% } -----------------------------------------------------------------------!!!End \subsection{Least and Greatest Closure of Types} From 3d0b2668d8a5d4048b306ed35fb78071129cfa96 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Wed, 8 Dec 2021 02:13:59 +0100 Subject: [PATCH 36/85] Fixed the "SUB issues" section --- specification/dartLangSpec.tex | 81 ++++++++++++++++++++++++---------- 1 file changed, 57 insertions(+), 24 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index a3f7d81a2..c52d6fc2e 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -24040,7 +24040,7 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} \FUNCTION}. \item Let $U_1$ and $U_2$ be, respectively, - + \noindent \code{$T_1$\,\FUNCTION<$X_1$\,\EXTENDS\,$B_{11}$,\,\ldots,\,$X_m$\,% \EXTENDS\,$B_{1m}$>($P_{11}$,\,\ldots,\,$P_{1k}$)} @@ -24073,7 +24073,7 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} \noindent \code{$T_2$\,\FUNCTION<$X_1$\,\EXTENDS\,$B_{21}$,\,\ldots,\,$X_m$\,% \EXTENDS\,$B_{2m}$>($P_{21}$,\,\ldots,\,$P_{2k}$,\,$\metavar{Named}_2$)} - + \noindent where $\metavar{Named}_j$ declares a non-empty set of named parameters with names $\metavar{NamesOfNamed}_j$, $j \in 1 .. 2$, @@ -24087,7 +24087,7 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} \item For each required entry named $n$ in $\metavar{Named}_2$, $\metavar{Named}_1$ contains an entry named $n$ (\commentary{which may or may not be required}). - \end{itemize} + \end{itemize} Then \DefEqualsNewline{\UpperBoundType{$U_1$}{$U_2$}}{% \code{$T_3$\,\FUNCTION<$X_1$\,\EXTENDS\,$B_{31}$,\,\ldots,\,$X_m$\,% @@ -24132,7 +24132,7 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} \item \DefEqualsNewline{\UpperBoundType{$T_1$}{$S$ \FUNCTION<\ldots>(\ldots)}}{% \UpperBoundType{$T_1$}{Object}}. -\item +\item \DefEquals{\UpperBoundType{FutureOr<$T_1$>}{FutureOr<$T_2$>}}{% \code{FutureOr<$T_3$>}}, where $T_3$ = \UpperBoundType{$T_1$}{$T_2$}. @@ -24148,7 +24148,7 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} \DefEquals{\UpperBoundType{$T_1$}{FutureOr<$T_2$>}}{% \code{FutureOr<$T_3$>}}, where $T_3$ = \UpperBoundType{$T_1$}{$T_2$}. -\item +\item \DefEquals{\UpperBoundType{FutureOr<$T_1$>}{$T_2$}}{% \code{FutureOr<$T_3$>}}, where $T_3$ = \UpperBoundType{$T_1$}{$T_2$}. @@ -24295,7 +24295,7 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} %% \item Let $U_1$ and $U_2$ be, respectively, - + \noindent \code{$T_1$\,\FUNCTION<$X_1$\,\EXTENDS\,$B_{11}$,\,\ldots,\,$X_m$\,% \EXTENDS\,$B_{1m}$>($P_{11}$,\,\ldots,\,$P_{1k}$)} @@ -24310,7 +24310,7 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} let $T_3$ be \LowerBoundType{$T_1$}{$T_2$}, let $B_{3i}$ be $B_{1i}$, and let $P_{3i}$ be determined as follows: - + \begin{itemize} \item $P_{3i}$ is \UpperBoundType{$P_{1i}$}{$P_{2i}$} for $i \leq \metavar{min}(k, l)$. @@ -24319,7 +24319,7 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} \item $P_{3i}$ is optional if $P_{1i}$ or $P_{2i}$ is optional, or if $\metavar{min}(k, l) < i \leq q$. \end{itemize} - + Then \DefEqualsNewline{\LowerBoundType{$U_1$}{$U_2$}}{% \code{$T_3$\,\FUNCTION<$X_1$\,\EXTENDS\,$B_{31}$,\,\ldots,\,$X_m$\,% \EXTENDS\,$B_{3m}$>($P_{31}$,\,\ldots,\,$P_{3q}$)}}. @@ -24337,7 +24337,7 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} \noindent \code{$T_2$\,\FUNCTION<$X_1$\,\EXTENDS\,$B_{21}$,\,\ldots,\,$X_m$\,% \EXTENDS\,$B_{2m}$>($P_{21}$,\,\ldots,\,$P_{2k}$,\,$\metavar{Named}_2$)} - + \noindent where $\metavar{Named}_j$ declares a non-empty set of named parameters with names $\metavar{NamesOfNamed}_j$, $j \in 1 .. 2$, @@ -24444,7 +24444,7 @@ \subsubsection{The Standard Upper Bound of Distinct Interface Types} \commentary{% For example, the algorithm yields \code{Object} as the standard upper bound of \code{List{}>} and \code{Iterable}, -but it is easy to see that a tighter upper bound exists, e.g., +but it is easy to see that a tighter upper bound exists, e.g., \code{Iterable{}>}.% } @@ -24490,18 +24490,16 @@ \subsubsection{The Standard Upper Bound of Distinct Interface Types} The least upper bound of $I$ and $J$ is then the sole element of $M_q$. -\subsubsection{Standard Upper Bound Termination} -\LMLabel{standardUpperBoundTermination} +\subsubsection{Standard Upper Bound Issues} +\LMLabel{standardUpperBoundIssues} -%% TODO(eernst), for review: This section could be turned into a few github -%% issues, but it seems reasonable to let the user of Dart know about it. -%% So do we include it, or do we delete the whole section (and create those -%% github issues)? -%% -%% Note that I omitted the symmetry issue: The given example does not -%% demonstrate any asymmetry (both results have type `List`). -%% So we should study the symmetry properties a bit more if we keep this -%% section. +%% TODO(eernst), for review: I kept this here (and updated some parts of it) +%% in order to ensure that this information is preserved. However, it should +%% perhaps not be in the specification. Should it actually be turned into +%% a couple of github issues? In any case, it makes sense to communicate +%% these properties of the algoritm to anyone who needs to know the language +%% in full detail, which could justify keeping it here, or including a +%% reference to some other location where the information is available. \commentary{% The definition of the standard upper bound for type variables @@ -24510,9 +24508,7 @@ \subsubsection{Standard Upper Bound Termination} } \begin{dartCode} -\VOID{} foo, S \EXTENDS{} List>() \{ - T x; - S y; +\VOID{} foo, S \EXTENDS{} List>(T x, S y) \{ \VAR{} a = (x == y) ? x : y; \} \end{dartCode} @@ -24529,6 +24525,43 @@ \subsubsection{Standard Upper Bound Termination} with the current algorithm.% } +\commentary{% +The current algorithm is asymmetric. +There is an equivalence class of top types, +and we correctly choose a canonical representative for bare top types +using the \IsMoreTopTypeName{} predicate. +However, when two different top types are embedded in two mutual subtypes, +we don't correctly choose a canonical representative.% +} + +\begin{dartCode} +\IMPORT{} 'dart:async'; +\\ +\VOID{} main() \{ + List{}> x; + List<\DYNAMIC> y; + + var a = (x == y) ? x : y; // List<\DYNAMIC>. + var b = (x == y) ? y : x; // List{}>. +\} +\end{dartCode} + +\commentary{% +The best solution for this is probably to normalize the types. +This is fairly straightforward: +We just normalize \code{FutureOr<$T$>} to the normal form of $T$ +when $T$ is a top type. +We can then inductively apply this across the rest of the types. +Then, whenever we have mutual subtypes, we just return the normal form. +This would be breaking, albeit hopefully only in a minor way. + +A similar treatment would need to be done for the bottom types as well, +since there are two equivalences there: +A type variable $X$ with bound $T$ is equivalent to \code{Never} +if $T$ is equivalent to \code{Never}, +and \code{FutureOr} is equivalent to \code{Future}.% +} + \subsection{Least and Greatest Closure of Types} \LMLabel{leastAndGreatestClosureOfTypes} From 755852ebd7ec8517f79f57ce503aee4f5e1523ae Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Wed, 8 Dec 2021 20:25:22 +0100 Subject: [PATCH 37/85] Rebase --- specification/dartLangSpec.tex | 227 ++++++++++++++++----------------- 1 file changed, 107 insertions(+), 120 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index c52d6fc2e..2d7c795d3 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -22657,43 +22657,41 @@ \subsection{Subtypes} } \LMHash{}% -%% TODO(eernst): Introduce these specialized intersection types -%% in a suitable location where type promotion is specified. -Types of the form -\IndexCustom{$X \& S$}{type!of the form $X \& S$}% -\IndexExtraEntry{\&@$X \& S$} -arise during static analysis due to type promotion +Intersection types +(\commentary{types of the form \code{$X$\,\&\,$S$}}), +may arise during static analysis due to type promotion (\ref{typePromotion}). They never occur during execution, -they are never a type argument of another type, -nor a return type or a formal parameter type, -and it is always the case that $S$ is a subtype of the bound of $X$. -\commentary{% -The motivation for $X \& S$ is that it represents -the type of a local variable $v$ -whose type is declared to be the type variable $X$, -and which is known to have type $S$ due to promotion. -Similarly, $X \& S$ may be seen as an intersection type, -which is a subtype of $X$ and also a subtype of $S$. -Intersection types are \emph{not} supported in general, -only in this special case.% -} -Every other form of type may occur during static analysis -as well as during execution, -and the subtype relationship is always determined in the same way. +and there are many other restrictions on where they can occur +(\ref{intersectionTypes}). +However, their subtype relations are specified without restrictions. +\commentary{% +It causes no problems that these rules will not be used +in their full generality.% +} +!!! Renumber! % Subtype Rule Numbering \newcommand{\SrnReflexivity}{1} -\newcommand{\SrnTop}{2} -\newcommand{\SrnBottom}{3} -\newcommand{\SrnNull}{4} -\newcommand{\SrnLeftTypeAlias}{5} -\newcommand{\SrnRightTypeAlias}{6} +\newcommand{\SrnRightTop}{2} +\newcommand{\SrnLeftTop}{3} +\newcommand{\SrnBottom}{4} +\newcommand{\SrnRightObjectOne}{5.1} +\newcommand{\SrnRightObjectTwo}{5.2} +\newcommand{\SrnRightObjectThree}{5.3} +\newcommand{\SrnRightObjectFour}{5.4} +\newcommand{\SrnNullOne}{6.1} +\newcommand{\SrnNullTwo}{6.2} \newcommand{\SrnLeftFutureOr}{7} +\newcommand{\SrnLeftNullable}{7b} \newcommand{\SrnTypeVariableReflexivityA}{8} \newcommand{\SrnRightPromotedVariable}{9} \newcommand{\SrnRightFutureOrA}{10} \newcommand{\SrnRightFutureOrB}{11} +\newcommand{\SrnRightNullableOne}{11b.1} +\newcommand{\SrnRightNullableTwo}{11b.2} +\newcommand{\SrnRightNullableThree}{11b.3} +\newcommand{\SrnRightNullableFour}{11b.4} \newcommand{\SrnLeftPromotedVariable}{12} \newcommand{\SrnLeftVariableBound}{13} \newcommand{\SrnRightFunction}{14} @@ -22714,34 +22712,47 @@ \subsection{Subtypes} \def\RuleRawRaw#1#2#3#4{\centerline{\inference[#1]{#3}{#4}}\VSP} % \begin{minipage}[c]{0.49\textwidth} - \Axiom{\SrnReflexivity}{Reflexivity}{S}{S} - \Axiom{\SrnBottom}{Left Bottom}{\bot}{T} + \Axiom{\SrnReflexivity}{Reflexivity}{T}{T} + \Axiom{\SrnBottom}{Left Bottom}{\code{Never}}{T} + \RuleRaw{\SrnRightObjectTwo}{Right Object 2}{% + \SubtypeStd{S}{\code{Object}}}{\code{$X$\,\&\,$S$}}{\code{Object}} + \RuleRaw{\SrnRightObjectThree}{Right Object 3}{% + \SubtypeStd{S}{\code{Object}}}{\code{FutureOr<$S$>}}{\code{Object}} + \Axiom{\SrnNullOne}{Left Null One}{\code{Null}}{\code{$T$?}} \end{minipage} \begin{minipage}[c]{0.49\textwidth} - \RuleRaw{\SrnTop}{Right Top}{T \in \{\code{Object}, \DYNAMIC, \VOID\}}{S}{T} - \RuleRaw{\SrnNull}{Left Null}{T \not= \bot}{\code{Null}}{T} + \RuleRaw{\SrnRightTop}{Right Top}{% + T \in \{\code{Object?}, \DYNAMIC, \VOID\}}{S}{T} + \RuleRaw{\SrnLeftTop}{Left Top}{% + S \in \{\DYNAMIC, \VOID\} & \SubtypeStd{\code{Object?}}{T}}{S}{T} + \RuleRaw{\SrnRightObjectOne}{Right Object 1}{% + \code{$X$\,\EXTENDS\,$B$} & \SubtypeStd{B}{\code{Object}}% + }{X}{\code{Object}} + \RuleRaw{\SrnRightObjectFour}{Right Object 4}{% + $S$\,\not\in \{\code{Null}, \DYNAMIC, \VOID\}\\ + \mbox{$S$ is not of the form \code{$U$?}, $X$, % + \code{$X$\,\&\,$U$}, \code{FutureOr<$U$>}}}{S}{\code{Object}} + \Rule{\SrnNullTwo}{Left Null Two}{\code{Null}}{T}{% + \code{Null}}{\code{FutureOr<$T$>}} \end{minipage} - \ExtraVSP - \RuleRaw{\SrnLeftTypeAlias}{Type Alias Left}{% - \code{\TYPEDEF{} $F$<\TypeParametersNoBounds{X}{s}> = U} & - \SubtypeStd{[S_1/X_1,\ldots,S_s/X_s]U}{T}}{\code{$F$<\List{S}{1}{s}>}}{T} - \RuleRaw{\SrnRightTypeAlias}{Type Alias Right}{% - \code{\TYPEDEF{} $F$<\TypeParametersNoBounds{X}{s}> = U} & - \SubtypeStd{S}{[T_1/X_1,\ldots,T_s/X_s]U}}{S}{\code{$F$<\List{T}{1}{s}>}} - \begin{minipage}[c]{0.49\textwidth} \RuleTwo{\SrnLeftFutureOr}{Left FutureOr}{S}{T}{% \code{Future<$S$>}}{T}{\code{FutureOr<$S$>}}{T} \RuleTwo{\SrnRightPromotedVariable}{Right Promoted Variable}{S}{X}{S}{T}{% S}{X \& T} \Rule{\SrnRightFutureOrB}{Right FutureOr B}{S}{T}{S}{\code{FutureOr<$T$>}} + \Rule{\SrnRightNullableTwo}{Right Nullable 2}{S}{\code{Null}}{S}{% + \code{$T$?}} \Rule{\SrnLeftVariableBound}{Left Variable Bound}{\Delta(X)}{T}{X}{T} \end{minipage} \begin{minipage}[c]{0.49\textwidth} + \RuleTwo{\SrnLeftNullable}{Left Nullable}{S}{T}{\code{Null}}{T}{ + \code{$S$?}}{T} \Axiom{\SrnTypeVariableReflexivityA}{Left Promoted Variable A}{X \& S}{X} \Rule{\SrnRightFutureOrA}{Right FutureOr A}{S}{\code{Future<$T$>}}{% S}{\code{FutureOr<$T$>}} + \Rule{\SrnRightNullableOne}{Right Nullable 1}{S}{T}{S}{\code{$T$?}} \Rule{\SrnLeftPromotedVariable}{Left Promoted Variable B}{S}{T}{X \& S}{T} \RuleRaw{\SrnRightFunction}{Right Function}{T\mbox{ is a function type}}{% T}{\FUNCTION} @@ -22777,6 +22788,7 @@ \subsection{Subtypes} \forall j \in 1 .. s\!:\;\SubtypeStd{S_j}{T_j}}{% \code{$C$<\List{S}{1}{s}>}}{\code{$C$<\List{T}{1}{s}>}} \ExtraVSP + %% !!! Should include mixins (and other non-class interface types, if any). \RuleRaw{\SrnSuperinterface}{Superinterface}{% \code{\CLASS{} $C$<\TypeParametersNoBounds{X}{s}>\,\ldots\,\{\}}\\ \Superinterface{\code{$D$<\List{T}{1}{m}>}}{C} & @@ -22828,10 +22840,9 @@ \subsubsection{Subtype Rules} Whenever a rule contains one or more meta-variables, that rule can be used by \IndexCustom{instantiating}{instantiation!subtype rule} -it, that is, by consistently replacing -each occurrence of a given meta-variable by -concrete syntax denoting the same type -(\ref{typeType}). +it, that is, by choosing a specific type $T$ and metavariable $\cal V$, +and then consistently replacing all occurrences of $\cal V$ by +concrete syntax denoting $T$. \commentary{% In general, this means that two or more occurrences of @@ -22844,11 +22855,12 @@ \subsubsection{Subtype Rules} can be used to conclude \Subtype{\emptyset}{\code{int}}{\code{int}}, where $\emptyset$ denotes the empty environment -(any environment would suffice because no type variables occur). +(any environment would suffice because no type variables occur).% +} -However, the wording `denoting the same type' above covers -additional situations as well: -For instance, we may use rule~\SrnReflexivity{} +\commentary{% +The phrases `same type' and `identical syntax' deserves some extra scrutiny: +We may, e.g., use rule~\SrnReflexivity{} to show that \code{p1.C} is a subtype of \code{p2.C} when \code{C} is a class declared in a library $L$ which is imported by libraries $L_1$ and $L_2$ and @@ -22877,8 +22889,27 @@ \subsubsection{Subtype Rules} } \LMHash{}% -Every \synt{typeName} used in a type mentioned in this section is assumed to -have no compile-time error and denote a type. +In this section, +the notion of two types $T_1$ and $T_2$ being the same type +is taken to mean that $T_1$ and $T_2$ have the same canonical syntax +(\ref{standardUpperBoundsAndStandardLowerBounds}). + +\commentary{% +In other words, we eliminate the difficulties associated with +different syntax denoting the same type, +and different types denoted by the same syntax, +by assuming that every type in the program has been expressed +in a manner where those situations never occur, +because each type is denoted by the same globally unique syntax everywhere. +Note that `same canonical syntax' also requires +transitive expansion of all type aliases +(\ref{typedef}).% +} + +\LMHash{}% +Every \synt{typeName} used in a type mentioned in this section +is assumed to have no compile-time error, +and it is assumed to denote a type. \commentary{% That is, no subtyping relationship can be proven for @@ -22926,9 +22957,11 @@ \subsubsection{Subtype Rules} So $\{ \code{X} \mapsto \code{int}, \code{Y} \mapsto \code{double} \} \uplus \{ \code{Z} \mapsto \code{Object} \} = -\{ \code{X} \mapsto \code{int}, \code{Y} \mapsto \code{double}, \code{Z} \mapsto \code{Object} \}$ +\{ \code{X} \mapsto \code{int}, \code{Y} \mapsto \code{double}, % +\code{Z} \mapsto \code{Object} \}$ and -$\{ \code{X} \mapsto \code{int}, \code{Y} \mapsto \code{FutureOr{}>} \} \uplus +$\{ \code{X} \mapsto \code{int}, \code{Y} \mapsto % +\code{FutureOr{}>} \} \uplus \{ \code{Y} \mapsto \code{int} \} = \{ \code{X} \mapsto \code{int}, \code{Y} \mapsto \code{int} \}$. Note that operator $\uplus$ is concerned with scopes and shadowing, @@ -22978,28 +23011,6 @@ \subsubsection{Being a subtype} each of the premises of $R$, continuing until a rule with no premises is reached. -\LMHash{}% -The first premise in the -rules~\SrnLeftTypeAlias{} and~\SrnRightTypeAlias{} -is a type alias declaration. -This premise is satisfied in each of the following situations: - -\begin{itemize} -\item A non-generic type alias named $F$ is declared. - In this case $s$ is zero, - no assumptions are made about the existence - of any formal type parameters, - and actual type argument lists are omitted everywhere in the rule. -\item We may choose $s$ and \List{X}{1}{s} such that the following holds: - A generic type alias named $F$ is declared, - with formal type parameters \List{X}{1}{s}. - \commentary{% - Each formal type parameter $X_j$ may have a bound, - but the bounds are never used in this context, - so we do not introduce metavariables for them.% - } -\end{itemize} - \LMHash{}% Rule~\SrnRightFunction{} has as a premise that `$T$ is a function type'. This means that $T$ is a type of one of the forms introduced in @@ -23108,7 +23119,7 @@ \subsubsection{Informal Subtype Rule Descriptions} the rule is also valid in any environment and the environment is never used explicitly, so we will not repeat that. -\Item{\SrnTop}{Top} +\Item{\SrnRightTop}{Top} Every type is a subtype of \code{Object}, every type is a subtype of \DYNAMIC, and every type is a subtype of \VOID. @@ -23120,19 +23131,11 @@ \subsubsection{Informal Subtype Rule Descriptions} (\ref{superBoundedTypes}). \Item{\SrnBottom}{Bottom} Every type is a supertype of $\bot$. -\Item{\SrnNull}{Null} - Every type other than $\bot$ is a supertype of \code{Null}. -\Item{\SrnLeftTypeAlias}{Type Alias Left} - An application of a type alias to some actual type arguments is - a subtype of another type $T$ - if the expansion of the type alias to the type that it denotes - is a subtype of $T$. - Note that a non-generic type alias is handled by letting $s = 0$. -\Item{\SrnRightTypeAlias}{Type Alias Right} - A type $S$ is a subtype of an application of a type alias - if $S$ is a subtype of - the expansion of the type alias to the type that it denotes. - Note that a non-generic type alias is handled by letting $s = 0$. +\Item{\SrnNullOne}{Null 1} + \code{Null} is a subtype of every type of the form \code{$T$?}. +\Item{\SrnNullTwo}{Null 2} + \code{Null} is a subtype of \code{FutureOr<$T$>} + if \code{Null} is a subtype of $T$. \Item{\SrnLeftFutureOr}{Left FutureOr} The type \code{FutureOr<$S$>} is a subtype of a given type $T$ if $S$ is a subtype of $T$ and \code{Future<$S$>} is a subtype of $T$, @@ -23603,11 +23606,10 @@ \subsection{Type Normalization} (such as \code{Never} and $X$). In particular, \SubtypeNE{S}{T} and \SubtypeNE{T}{S} holds if and only if -\NormalizedTypeOf{$T$} is syntactically equal to \NormalizedTypeOf{$S$}, -modulo replacement of atomic top types, -and modulo replacement of terms derived from \synt{typeName} -denoting the same type -(such as \code{List{}>} and \code{List{}>}).% +\NormalizedTypeOf{$T$} has the same canonical syntax as \NormalizedTypeOf{$S$} +(\ref{standardUpperBoundsAndStandardLowerBounds}), +modulo replacement of atomic top types +(e.g., \code{List{}>} and \code{List{}>}).% } \LMHash{}% @@ -23815,8 +23817,8 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} } \LMHash{}% -Consequently, when we say that two types $T_1$ and $T_2$ are -\IndexCustom{syntactically equal}{type!syntactically equal}, +Consequently, when we say that two types $T_1$ and $T_2$ have the +\IndexCustom{same canonical syntax}{type!same canonical syntax}, it refers to the situation where both $T_1$ and $T_2$ have been transformed in the above sense (\commentary{by alpha-renaming, alias expansion, and canonical naming}). @@ -24050,7 +24052,7 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} \EXTENDS\,$B_{2m}$>($P_{21}$,\,\ldots,\,$P_{2l}$)} \noindent - such that each $B_{1i}$ and $B_{2i}$ are syntactically equal types, + such that each $B_{1i}$ and $B_{2i}$ are types with the same canonical syntax, and both have the same number of required positional parameters. Let $q$ be $\metavar{min}(k, l)$, let $T_3$ be \UpperBoundType{$T_1$}{$T_2$}, @@ -24080,7 +24082,7 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} and consider the case where the following is satisfied: \begin{itemize} - \item Each $B_{1i}$ and $B_{2i}$ are syntactically equal types. + \item Each $B_{1i}$ and $B_{2i}$ are types with the same canonical syntax. \item For each required entry named $n$ in $\metavar{Named}_1$, $\metavar{Named}_2$ contains an entry named $n$ (\commentary{which may or may not be required}). @@ -24305,7 +24307,7 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} \EXTENDS\,$B_{2m}$>($P_{21}$,\,\ldots,\,$P_{2l}$)} \noindent - such that each $B_{1i}$ and $B_{2i}$ are syntactically equal types. + such that each $B_{1i}$ and $B_{2i}$ are types with the same canonical syntax. Let $q$ be $\metavar{max}(k, l)$, let $T_3$ be \LowerBoundType{$T_1$}{$T_2$}, let $B_{3i}$ be $B_{1i}$, and @@ -24342,7 +24344,7 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} where $\metavar{Named}_j$ declares a non-empty set of named parameters with names $\metavar{NamesOfNamed}_j$, $j \in 1 .. 2$, and consider the case where - each $B_{1i}$ and $B_{2i}$ are syntactically equal types. + each $B_{1i}$ and $B_{2i}$ are types with the same canonical syntax. Then \DefEqualsNewline{\LowerBoundType{$U_1$}{$U_2$}}{%U_3}, where $U_3$ is \code{$T_3$\,\FUNCTION<$X_1$\,\EXTENDS\,$B_{31}$,\,\ldots,\,$X_m$\,% \EXTENDS\,$B_{3m}$>($P_{31}$,\,\ldots,\,$P_{3k}$,\,$\metavar{Named}_3$)}}, @@ -24886,9 +24888,10 @@ \subsection{Intersection Types} \commentary{% An intersection type will never occur as a nested type, that is, -it will never occurs as +it never occurs as or in an actual type argument in a parameterized type, -as a parameter type or a return type in a function type, +a parameter type or a return type in a function type, +a type parameter bound, as the right operand of another intersection type, or as the operand of the nullable type operator \lit{?}.% } @@ -25049,24 +25052,8 @@ \subsection{Type Type} and let $S_j$ be \NormalizedTypeOf{$U_j$}, for $j \in 1 .. 2$ (\ref{typeNormalization}). We then say that $T_1$ and $T_2$ are the \Index{same type} -if{}f $S_1$ and $S_2$ are syntactically equal, -up to equivalence of bound variables, -and up to replacement of identifiers or qualified identifiers -resolving to the same type declaration -(\commentary{% -e.g., \code{C} and \code{prefix.C} could resolve to -the same class declaration% -}), -and excluding the case where two identifiers or qualified identifiers -occurring at corresponding positions in $S_1$ and $S_2$ -are syntactically identical, -but resolve to different declarations -(\commentary{% -e.g., one occurrence of \code{C} could resolve to a -class declaration imported from a library $L_1$, -and another occurrence of \code{C} could resolve to a -class declaration imported from a different library $L_2$% -}). +if{}f $S_1$ and $S_2$ are have the same canonical syntax +(\ref{standardUpperBoundsAndStandardLowerBounds}). \LMHash{}% A reified type identifies the underlying type in the sense that @@ -27129,7 +27116,7 @@ \section*{Appendix: Algorithmic Subtyping} \end{minipage} % \caption{Algorithmic subtype rules. - Rules \SrnTop--\SrnSuperinterface{} are unchanged and hence omitted here.} + Rules \SrnRightTop--\SrnSuperinterface{} are unchanged and hence omitted here.} \label{fig:algorithmicSubtypeRules} \end{figure} @@ -27190,7 +27177,7 @@ \section*{Appendix: Algorithmic Subtyping} followed by the rule whose number is $N+1$. \commentary{% So the order is -\AppSrnReflexivity, \SrnTop--\SrnTypeVariableReflexivityA, +\AppSrnReflexivity, \SrnRightTop--\SrnTypeVariableReflexivityA, \AppSrnTypeVariableReflexivityB, \AppSrnTypeVariableReflexivityC, \AppSrnTypeVariableReflexivityD, \SrnRightPromotedVariable, and so on.% From 13c72de087b803c261c704f4775a56596d49c7d7 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Wed, 8 Dec 2021 23:07:25 +0100 Subject: [PATCH 38/85] Rebase --- specification/dartLangSpec.tex | 180 ++++++++++++++++++++------------- 1 file changed, 110 insertions(+), 70 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 2d7c795d3..633116ddd 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -14170,7 +14170,7 @@ \subsection{Lookup} { % Scope for `lookup' definition. -\def\LookupDefinitionWithStart#1{ +\def\LookupDefinitionWithStart#1{% \LMHash{}% The result of a {\em {#1} lookup for $m$ in $o$ with respect to $L$ starting in class $C$} @@ -14493,7 +14493,7 @@ \subsection{Member Invocations} expression $r$ is an expression of one of the forms shown in Fig.~\ref{fig:memberInvocations}. Each member invocation has a -\IndexCustom{corresponding member name}{ +\IndexCustom{corresponding member name}{% member invocation!corresponding member name} as shown in the figure. @@ -16239,7 +16239,7 @@ \subsection{Null Shorting} \IndexCustom{\metaCode{SHORT}}{null shorting!\metaCode{SHORT}} is defined as follows: -{ +{% \def\Base#1{\textcolor{normativeColor}{#1}} \def\Meta#1{\textcolor{metaColor}{#1}} \begin{metaLevelCode} @@ -22676,29 +22676,27 @@ \subsection{Subtypes} \newcommand{\SrnRightTop}{2} \newcommand{\SrnLeftTop}{3} \newcommand{\SrnBottom}{4} -\newcommand{\SrnRightObjectOne}{5.1} -\newcommand{\SrnRightObjectTwo}{5.2} -\newcommand{\SrnRightObjectThree}{5.3} -\newcommand{\SrnRightObjectFour}{5.4} -\newcommand{\SrnNullOne}{6.1} -\newcommand{\SrnNullTwo}{6.2} -\newcommand{\SrnLeftFutureOr}{7} -\newcommand{\SrnLeftNullable}{7b} -\newcommand{\SrnTypeVariableReflexivityA}{8} -\newcommand{\SrnRightPromotedVariable}{9} -\newcommand{\SrnRightFutureOrA}{10} -\newcommand{\SrnRightFutureOrB}{11} -\newcommand{\SrnRightNullableOne}{11b.1} -\newcommand{\SrnRightNullableTwo}{11b.2} -\newcommand{\SrnRightNullableThree}{11b.3} -\newcommand{\SrnRightNullableFour}{11b.4} -\newcommand{\SrnLeftPromotedVariable}{12} -\newcommand{\SrnLeftVariableBound}{13} -\newcommand{\SrnRightFunction}{14} -\newcommand{\SrnPositionalFunctionType}{15} -\newcommand{\SrnNamedFunctionType}{16} -\newcommand{\SrnCovariance}{17} -\newcommand{\SrnSuperinterface}{18} +%\newcommand{\SrnRightObjectOne}{} +%\newcommand{\SrnRightObjectTwo}{} +%\newcommand{\SrnRightObjectThree}{} +\newcommand{\SrnRightObjectFour}{5} +\newcommand{\SrnNullOne}{6} +\newcommand{\SrnNullTwo}{7} +\newcommand{\SrnLeftFutureOr}{8} +\newcommand{\SrnLeftNullable}{9} +\newcommand{\SrnTypeVariableReflexivityA}{10} +\newcommand{\SrnRightPromotedVariable}{11} +\newcommand{\SrnRightFutureOrA}{12} +\newcommand{\SrnRightFutureOrB}{13} +\newcommand{\SrnRightNullableOne}{14} +\newcommand{\SrnRightNullableTwo}{15} +\newcommand{\SrnLeftPromotedVariable}{16} +\newcommand{\SrnLeftVariableBound}{17} +\newcommand{\SrnRightFunction}{18} +\newcommand{\SrnPositionalFunctionType}{19} +\newcommand{\SrnNamedFunctionType}{20} +\newcommand{\SrnCovariance}{21} +\newcommand{\SrnSuperinterface}{22} \begin{figure}[p] \def\VSP{\vspace{4mm}} @@ -22711,56 +22709,96 @@ \subsection{Subtypes} \centerline{\inference[#1]{#3}{\SubtypeStd{#4}{#5}}}\VSP} \def\RuleRawRaw#1#2#3#4{\centerline{\inference[#1]{#3}{#4}}\VSP} % + % ---------------------------------------------------------------------- + % Omitted rules stated here, with justification for + % the omission. + % ------------------------------------------------ Right Object 1 + % Not needed unless algorithmic: Instance of + % \SrnLeftVariableBound. + % \RuleRaw{\SrnRightObjectOne}{Right Object 1}{% + % \code{$X$\,\EXTENDS\,$B$} & \SubtypeStd{B}{\code{Object}}% + % }{X}{\code{Object}} + % ------------------------------------------------ Right Object 2 + % Not needed unless algorithmic: Instance of + % \SrnLeftPromotedVariable. + % \RuleRaw{\SrnRightObjectTwo}{}{% + % \SubtypeStd{S}{\code{Object}}}{\code{$X$\,\&\,$S$}}{\code{Object}} + % ------------------------------------------------ Right Object 3 + % Not needed unless algorithmic: Derivable from + % \SrnLeftFutureOr{} and \SrnRightObjectFour{} (to get + % Future <: Object). + % \RuleRaw{\SrnRightObjectThree}{}{% + % \SubtypeStd{S}{\code{Object}}}{\code{FutureOr<$S$>}}{\code{Object}} + % ---------------------------------------------------------------------- \begin{minipage}[c]{0.49\textwidth} - \Axiom{\SrnReflexivity}{Reflexivity}{T}{T} - \Axiom{\SrnBottom}{Left Bottom}{\code{Never}}{T} - \RuleRaw{\SrnRightObjectTwo}{Right Object 2}{% - \SubtypeStd{S}{\code{Object}}}{\code{$X$\,\&\,$S$}}{\code{Object}} - \RuleRaw{\SrnRightObjectThree}{Right Object 3}{% - \SubtypeStd{S}{\code{Object}}}{\code{FutureOr<$S$>}}{\code{Object}} - \Axiom{\SrnNullOne}{Left Null One}{\code{Null}}{\code{$T$?}} + % ------------------------------------------------ Reflexivity + \Axiom{\SrnReflexivity}{}{T}{T} + \ExtraVSP + % ------------------------------------------------ Left Top + % Non-algorithmic justification for this rule: Needed + % to prove dynamic/void <: FutureOr?. + \RuleRaw{\SrnLeftTop}{}{% + S \in \{\DYNAMIC, \VOID\}\\ + \SubtypeStd{\code{Object?}}{T}}{S}{T} + % ------------------------------------------------ Left Bottom + \Axiom{\SrnBottom}{}{\code{Never}}{T} + % ------------------------------------------------ Left Null 1 + \Axiom{\SrnNullOne}{}{\code{Null}}{\code{$T$?}} \end{minipage} \begin{minipage}[c]{0.49\textwidth} - \RuleRaw{\SrnRightTop}{Right Top}{% + % ------------------------------------------------ Right Top + \RuleRaw{\SrnRightTop}{}{% T \in \{\code{Object?}, \DYNAMIC, \VOID\}}{S}{T} - \RuleRaw{\SrnLeftTop}{Left Top}{% - S \in \{\DYNAMIC, \VOID\} & \SubtypeStd{\code{Object?}}{T}}{S}{T} - \RuleRaw{\SrnRightObjectOne}{Right Object 1}{% - \code{$X$\,\EXTENDS\,$B$} & \SubtypeStd{B}{\code{Object}}% - }{X}{\code{Object}} - \RuleRaw{\SrnRightObjectFour}{Right Object 4}{% + % ------------------------------------------------ Right Object 4 + \RuleRaw{\SrnRightObjectFour}{}{% $S$\,\not\in \{\code{Null}, \DYNAMIC, \VOID\}\\ - \mbox{$S$ is not of the form \code{$U$?}, $X$, % - \code{$X$\,\&\,$U$}, \code{FutureOr<$U$>}}}{S}{\code{Object}} - \Rule{\SrnNullTwo}{Left Null Two}{\code{Null}}{T}{% + \mbox{$S$ is not of the form \code{$U$?}, $X$,}\\ + \mbox{\code{$X$\,\&\,$U$}, % + or \code{FutureOr<$U$>}}}{S}{\code{Object}} + % ------------------------------------------------ Left Null 2 + \Rule{\SrnNullTwo}{}{\code{Null}}{T}{% \code{Null}}{\code{FutureOr<$T$>}} \end{minipage} \begin{minipage}[c]{0.49\textwidth} - \RuleTwo{\SrnLeftFutureOr}{Left FutureOr}{S}{T}{% - \code{Future<$S$>}}{T}{\code{FutureOr<$S$>}}{T} - \RuleTwo{\SrnRightPromotedVariable}{Right Promoted Variable}{S}{X}{S}{T}{% + % ------------------------------------------------ Left FutureOr + \RuleTwo{\SrnLeftFutureOr}{}{% + \code{Future<$S$>}}{T}{S}{T}{% + \code{FutureOr<$S$>}}{T} + % ------------------------------------------------ Right Promoted Variable + \RuleTwo{\SrnRightPromotedVariable}{}{S}{X}{S}{T}{% S}{X \& T} - \Rule{\SrnRightFutureOrB}{Right FutureOr B}{S}{T}{S}{\code{FutureOr<$T$>}} - \Rule{\SrnRightNullableTwo}{Right Nullable 2}{S}{\code{Null}}{S}{% + % ------------------------------------------------ Right FutureOr B + \Rule{\SrnRightFutureOrB}{}{S}{T}{S}{% + \code{FutureOr<$T$>}} + % ------------------------------------------------ Right Nullable 2 + \Rule{\SrnRightNullableTwo}{}{S}{\code{Null}}{S}{% \code{$T$?}} - \Rule{\SrnLeftVariableBound}{Left Variable Bound}{\Delta(X)}{T}{X}{T} + % ------------------------------------------------ Left Variable Bound + \Rule{\SrnLeftVariableBound}{}{\Delta(X)}{T}{X}{T} \end{minipage} \begin{minipage}[c]{0.49\textwidth} - \RuleTwo{\SrnLeftNullable}{Left Nullable}{S}{T}{\code{Null}}{T}{ + % ------------------------------------------------ Left Nullable + \RuleTwo{\SrnLeftNullable}{}{S}{T}{\code{Null}}{T}{% \code{$S$?}}{T} - \Axiom{\SrnTypeVariableReflexivityA}{Left Promoted Variable A}{X \& S}{X} - \Rule{\SrnRightFutureOrA}{Right FutureOr A}{S}{\code{Future<$T$>}}{% - S}{\code{FutureOr<$T$>}} - \Rule{\SrnRightNullableOne}{Right Nullable 1}{S}{T}{S}{\code{$T$?}} - \Rule{\SrnLeftPromotedVariable}{Left Promoted Variable B}{S}{T}{X \& S}{T} - \RuleRaw{\SrnRightFunction}{Right Function}{T\mbox{ is a function type}}{% - T}{\FUNCTION} + % ------------------------------------------------ Left Promoted Variable A + \Axiom{\SrnTypeVariableReflexivityA}{}{X \& S}{X} + % ------------------------------------------------ Right FutureOr A + \Rule{\SrnRightFutureOrA}{}{S}{% + \code{Future<$T$>}}{S}{\code{FutureOr<$T$>}} + % ------------------------------------------------ Right Nullable 1 + \Rule{\SrnRightNullableOne}{}{S}{T}{S}{\code{$T$?}} + % ------------------------------------------------ Left Promoted Variable B + \Rule{\SrnLeftPromotedVariable}{}{S}{T}{X \& S}{T} + % ------------------------------------------------ Right Function + \RuleRaw{\SrnRightFunction}{}{% + T\mbox{ is a function type}}{T}{\FUNCTION} \end{minipage} % \ExtraVSP - \RuleRawRaw{\SrnPositionalFunctionType}{Positional Function Types}{% - \Delta' = \Delta\uplus\{X_i\mapsto{}B_i\,|\,1 \leq i \leq s\} & + % ------------------------------------------------ Positional Function Type + \RuleRawRaw{\SrnPositionalFunctionType}{}{% + \Gamma' = \Delta\uplus\{X_i\mapsto{}B_i\,|\,1 \leq i \leq s\} & \Subtype{\Delta'}{S_0}{T_0} \\ n_1 \leq n_2 & n_1 + k_1 \geq n_2 + k_2 & @@ -22770,10 +22808,11 @@ \subsection{Subtypes} \RawFunctionTypePositional{T_0}{X}{B}{s}{T}{n_2}{k_2} \end{array}} \ExtraVSP\ExtraVSP - \RuleRawRaw{\SrnNamedFunctionType}{Named Function Types}{% - \Delta' = \Delta\uplus\{X_i\mapsto{}B_i\,|\,1 \leq i \leq s\} & + % ------------------------------------------------ Named Function Type + \RuleRawRaw{\SrnNamedFunctionType}{}{% + \Gamma' = \Delta\uplus\{X_i\mapsto{}B_i\,|\,1 \leq i \leq s\} & \Subtype{\Delta'}{S_0}{T_0} & - \forall j \in 1 .. n\!:\;\Subtype{\Delta'}{T_j}{S_j} \\ + \forall j \in 1 .. n\!:\;\Subtype{\Gamma'}{T_j}{S_j} \\ \{\,\List{y}{n+1}{n+k_2}\,\} \subseteq \{\,\List{x}{n+1}{n+k_1}\,\} \\ \forall p \in 1 .. k_2, q \in 1 .. k_1:\quad y_{n+p} = x_{n+q}\quad\Rightarrow\quad\Subtype{\Delta'}{T_{n+p}}{S_{n+q}}}{% @@ -22783,14 +22822,15 @@ \subsection{Subtypes} \end{array}} % \ExtraVSP - \RuleRaw{\SrnCovariance}{Covariance}{% - \code{\CLASS{} $C$<\TypeParametersNoBounds{X}{s}>\,\ldots\,\{\}} & - \forall j \in 1 .. s\!:\;\SubtypeStd{S_j}{T_j}}{% + % ------------------------------------------------ Covariance + \RuleRaw{\SrnCovariance}{}{% + \mbox{$C$ is an interface type with $s$ type parameters} & + \SubtypeStd{S_j}{T_j}\mbox{, for each $j \in 1..s$}}{% \code{$C$<\List{S}{1}{s}>}}{\code{$C$<\List{T}{1}{s}>}} \ExtraVSP - %% !!! Should include mixins (and other non-class interface types, if any). - \RuleRaw{\SrnSuperinterface}{Superinterface}{% - \code{\CLASS{} $C$<\TypeParametersNoBounds{X}{s}>\,\ldots\,\{\}}\\ + % ------------------------------------------------ Superinterface + \RuleRaw{\SrnSuperinterface}{}{% + \mbox{$C$ is an interface type with type parameters \List{X}{1}{s}}\\ \Superinterface{\code{$D$<\List{T}{1}{m}>}}{C} & \SubtypeStd{[S_1/X_1,\ldots,S_s/X_s]\code{$D$<\List{T}{1}{m}>}}{T}}{% \code{$C$<\List{S}{1}{s}>}\;}{\;T} @@ -24158,7 +24198,7 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} \DefEquals{\UpperBoundType{$T_1$}{$T_2$}}{T_2}, if \SubtypeNE{T_1}{T_2}. - \commentary{ + \commentary{% In this and in the following cases, both types must be interface types.% } \item @@ -24415,7 +24455,7 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} \DefEquals{\LowerBoundType{$T_1$}{$T_2$}}{\code{Never}}, otherwise. \end{itemize} -\rationale{ +\rationale{% The rules defining \UpperBoundTypeName{} and \LowerBoundTypeName{} are somewhat redundant in that they explicitly specify a lot of pairs of symmetric cases. From d5ae815d0d5a6be9af30c93e9254c439583e99f4 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Wed, 8 Dec 2021 23:17:09 +0100 Subject: [PATCH 39/85] Rebase --- specification/dartLangSpec.tex | 70 ++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 633116ddd..012380751 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -22701,13 +22701,15 @@ \subsection{Subtypes} \begin{figure}[p] \def\VSP{\vspace{4mm}} \def\ExtraVSP{\vspace{2mm}} - \def\Axiom#1#2#3#4{\centerline{\inference[#1]{}{\SubtypeStd{#3}{#4}}}\VSP} - \def\Rule#1#2#3#4#5#6{\centerline{\inference[#1]{\SubtypeStd{#3}{#4}}{\SubtypeStd{#5}{#6}}}\VSP} - \def\RuleTwo#1#2#3#4#5#6#7#8{% - \centerline{\inference[#1]{\SubtypeStd{#3}{#4} & \SubtypeStd{#5}{#6}}{\SubtypeStd{#7}{#8}}}\VSP} - \def\RuleRaw#1#2#3#4#5{% - \centerline{\inference[#1]{#3}{\SubtypeStd{#4}{#5}}}\VSP} - \def\RuleRawRaw#1#2#3#4{\centerline{\inference[#1]{#3}{#4}}\VSP} + \def\Axiom#1#2#3{\centerline{\inference[#1]{}{\SubtypeStd{#2}{#3}}}\VSP} + \def\Rule#1#2#3#4#5{% + \centerline{\inference[#1]{\SubtypeStd{#2}{#3}}{\SubtypeStd{#4}{#5}}}\VSP} + \def\RuleTwo#1#2#3#4#5#6#7{% + \centerline{\inference[#1]{\SubtypeStd{#2}{#3} & % + \SubtypeStd{#4}{#5}}{\SubtypeStd{#6}{#7}}}\VSP} + \def\RuleRaw#1#2#3#4{% + \centerline{\inference[#1]{#2}{\SubtypeStd{#3}{#4}}}\VSP} + \def\RuleRawRaw#1#2#3{\centerline{\inference[#1]{#2}{#3}}\VSP} % % ---------------------------------------------------------------------- % Omitted rules stated here, with justification for @@ -22715,90 +22717,92 @@ \subsection{Subtypes} % ------------------------------------------------ Right Object 1 % Not needed unless algorithmic: Instance of % \SrnLeftVariableBound. - % \RuleRaw{\SrnRightObjectOne}{Right Object 1}{% + % \RuleRaw{\SrnRightObjectOne}{% % \code{$X$\,\EXTENDS\,$B$} & \SubtypeStd{B}{\code{Object}}% % }{X}{\code{Object}} % ------------------------------------------------ Right Object 2 % Not needed unless algorithmic: Instance of % \SrnLeftPromotedVariable. - % \RuleRaw{\SrnRightObjectTwo}{}{% - % \SubtypeStd{S}{\code{Object}}}{\code{$X$\,\&\,$S$}}{\code{Object}} + % \RuleRaw{\SrnRightObjectTwo}{% + % \SubtypeStd{S}{\code{Object}}}{% + % \code{$X$\,\&\,$S$}}{\code{Object}} % ------------------------------------------------ Right Object 3 % Not needed unless algorithmic: Derivable from % \SrnLeftFutureOr{} and \SrnRightObjectFour{} (to get % Future <: Object). - % \RuleRaw{\SrnRightObjectThree}{}{% - % \SubtypeStd{S}{\code{Object}}}{\code{FutureOr<$S$>}}{\code{Object}} + % \RuleRaw{\SrnRightObjectThree}{% + % \SubtypeStd{S}{\code{Object}}}{% + % \code{FutureOr<$S$>}}{\code{Object}} % ---------------------------------------------------------------------- \begin{minipage}[c]{0.49\textwidth} % ------------------------------------------------ Reflexivity - \Axiom{\SrnReflexivity}{}{T}{T} + \Axiom{\SrnReflexivity}{T}{T} \ExtraVSP % ------------------------------------------------ Left Top % Non-algorithmic justification for this rule: Needed % to prove dynamic/void <: FutureOr?. - \RuleRaw{\SrnLeftTop}{}{% + \RuleRaw{\SrnLeftTop}{% S \in \{\DYNAMIC, \VOID\}\\ \SubtypeStd{\code{Object?}}{T}}{S}{T} % ------------------------------------------------ Left Bottom - \Axiom{\SrnBottom}{}{\code{Never}}{T} + \Axiom{\SrnBottom}{\code{Never}}{T} % ------------------------------------------------ Left Null 1 - \Axiom{\SrnNullOne}{}{\code{Null}}{\code{$T$?}} + \Axiom{\SrnNullOne}{\code{Null}}{\code{$T$?}} \end{minipage} \begin{minipage}[c]{0.49\textwidth} % ------------------------------------------------ Right Top - \RuleRaw{\SrnRightTop}{}{% + \RuleRaw{\SrnRightTop}{% T \in \{\code{Object?}, \DYNAMIC, \VOID\}}{S}{T} % ------------------------------------------------ Right Object 4 - \RuleRaw{\SrnRightObjectFour}{}{% + \RuleRaw{\SrnRightObjectFour}{% $S$\,\not\in \{\code{Null}, \DYNAMIC, \VOID\}\\ \mbox{$S$ is not of the form \code{$U$?}, $X$,}\\ \mbox{\code{$X$\,\&\,$U$}, % or \code{FutureOr<$U$>}}}{S}{\code{Object}} % ------------------------------------------------ Left Null 2 - \Rule{\SrnNullTwo}{}{\code{Null}}{T}{% + \Rule{\SrnNullTwo}{\code{Null}}{T}{% \code{Null}}{\code{FutureOr<$T$>}} \end{minipage} \begin{minipage}[c]{0.49\textwidth} % ------------------------------------------------ Left FutureOr - \RuleTwo{\SrnLeftFutureOr}{}{% + \RuleTwo{\SrnLeftFutureOr}{% \code{Future<$S$>}}{T}{S}{T}{% \code{FutureOr<$S$>}}{T} % ------------------------------------------------ Right Promoted Variable - \RuleTwo{\SrnRightPromotedVariable}{}{S}{X}{S}{T}{% + \RuleTwo{\SrnRightPromotedVariable}{S}{X}{S}{T}{% S}{X \& T} % ------------------------------------------------ Right FutureOr B - \Rule{\SrnRightFutureOrB}{}{S}{T}{S}{% + \Rule{\SrnRightFutureOrB}{S}{T}{S}{% \code{FutureOr<$T$>}} % ------------------------------------------------ Right Nullable 2 - \Rule{\SrnRightNullableTwo}{}{S}{\code{Null}}{S}{% + \Rule{\SrnRightNullableTwo}{S}{\code{Null}}{S}{% \code{$T$?}} % ------------------------------------------------ Left Variable Bound \Rule{\SrnLeftVariableBound}{}{\Delta(X)}{T}{X}{T} \end{minipage} \begin{minipage}[c]{0.49\textwidth} % ------------------------------------------------ Left Nullable - \RuleTwo{\SrnLeftNullable}{}{S}{T}{\code{Null}}{T}{% + \RuleTwo{\SrnLeftNullable}{S}{T}{\code{Null}}{T}{% \code{$S$?}}{T} % ------------------------------------------------ Left Promoted Variable A - \Axiom{\SrnTypeVariableReflexivityA}{}{X \& S}{X} + \Axiom{\SrnTypeVariableReflexivityA}{X \& S}{X} % ------------------------------------------------ Right FutureOr A - \Rule{\SrnRightFutureOrA}{}{S}{% + \Rule{\SrnRightFutureOrA}{S}{% \code{Future<$T$>}}{S}{\code{FutureOr<$T$>}} % ------------------------------------------------ Right Nullable 1 - \Rule{\SrnRightNullableOne}{}{S}{T}{S}{\code{$T$?}} + \Rule{\SrnRightNullableOne}{S}{T}{S}{\code{$T$?}} % ------------------------------------------------ Left Promoted Variable B - \Rule{\SrnLeftPromotedVariable}{}{S}{T}{X \& S}{T} + \Rule{\SrnLeftPromotedVariable}{S}{T}{X \& S}{T} % ------------------------------------------------ Right Function - \RuleRaw{\SrnRightFunction}{}{% + \RuleRaw{\SrnRightFunction}{% T\mbox{ is a function type}}{T}{\FUNCTION} \end{minipage} % \ExtraVSP % ------------------------------------------------ Positional Function Type \RuleRawRaw{\SrnPositionalFunctionType}{}{% - \Gamma' = \Delta\uplus\{X_i\mapsto{}B_i\,|\,1 \leq i \leq s\} & + \Delta' = \Delta\uplus\{X_i\mapsto{}B_i\,|\,1 \leq i \leq s\} & \Subtype{\Delta'}{S_0}{T_0} \\ n_1 \leq n_2 & n_1 + k_1 \geq n_2 + k_2 & @@ -22810,7 +22814,7 @@ \subsection{Subtypes} \ExtraVSP\ExtraVSP % ------------------------------------------------ Named Function Type \RuleRawRaw{\SrnNamedFunctionType}{}{% - \Gamma' = \Delta\uplus\{X_i\mapsto{}B_i\,|\,1 \leq i \leq s\} & + \Delta' = \Delta\uplus\{X_i\mapsto{}B_i\,|\,1 \leq i \leq s\} & \Subtype{\Delta'}{S_0}{T_0} & \forall j \in 1 .. n\!:\;\Subtype{\Gamma'}{T_j}{S_j} \\ \{\,\List{y}{n+1}{n+k_2}\,\} \subseteq \{\,\List{x}{n+1}{n+k_1}\,\} \\ @@ -22823,13 +22827,13 @@ \subsection{Subtypes} % \ExtraVSP % ------------------------------------------------ Covariance - \RuleRaw{\SrnCovariance}{}{% + \RuleRaw{\SrnCovariance}{% \mbox{$C$ is an interface type with $s$ type parameters} & \SubtypeStd{S_j}{T_j}\mbox{, for each $j \in 1..s$}}{% \code{$C$<\List{S}{1}{s}>}}{\code{$C$<\List{T}{1}{s}>}} \ExtraVSP % ------------------------------------------------ Superinterface - \RuleRaw{\SrnSuperinterface}{}{% + \RuleRaw{\SrnSuperinterface}{% \mbox{$C$ is an interface type with type parameters \List{X}{1}{s}}\\ \Superinterface{\code{$D$<\List{T}{1}{m}>}}{C} & \SubtypeStd{[S_1/X_1,\ldots,S_s/X_s]\code{$D$<\List{T}{1}{m}>}}{T}}{% From 4e7e07176d6f50e2b878af455f5958d78e3fe976 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Thu, 9 Dec 2021 18:45:17 +0100 Subject: [PATCH 40/85] Rebase --- specification/dart.sty | 2 +- specification/dartLangSpec.tex | 754 +++++++++++++++++++-------------- 2 files changed, 448 insertions(+), 308 deletions(-) diff --git a/specification/dart.sty b/specification/dart.sty index bb4318495..d20ba295c 100644 --- a/specification/dart.sty +++ b/specification/dart.sty @@ -402,7 +402,7 @@ % Same as \FunctionTypeNamed except suitable for inline usage, hence omitting % the spacer argument. -\newcommand{\RawFunctionTypeNamed}[8]{% +\newcommand{\RawFunctionTypeNamed}[9]{% \RawFunctionType{#1}{#2}{#3}{#4}{% \FunctionTypeNamedParameters{#5}{#6}{#7}{#8}{r}}} diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 012380751..f64e41e67 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -22670,33 +22670,32 @@ \subsection{Subtypes} in their full generality.% } -!!! Renumber! % Subtype Rule Numbering \newcommand{\SrnReflexivity}{1} \newcommand{\SrnRightTop}{2} \newcommand{\SrnLeftTop}{3} \newcommand{\SrnBottom}{4} -%\newcommand{\SrnRightObjectOne}{} -%\newcommand{\SrnRightObjectTwo}{} -%\newcommand{\SrnRightObjectThree}{} +%\newcommand{\SrnRightObjectOne}{} Redundant +%\newcommand{\SrnRightObjectTwo}{} Redundant +%\newcommand{\SrnRightObjectThree}{} Redundant \newcommand{\SrnRightObjectFour}{5} \newcommand{\SrnNullOne}{6} \newcommand{\SrnNullTwo}{7} \newcommand{\SrnLeftFutureOr}{8} \newcommand{\SrnLeftNullable}{9} -\newcommand{\SrnTypeVariableReflexivityA}{10} +\newcommand{\SrnLeftPromotedVariableOne}{10} \newcommand{\SrnRightPromotedVariable}{11} \newcommand{\SrnRightFutureOrA}{12} \newcommand{\SrnRightFutureOrB}{13} \newcommand{\SrnRightNullableOne}{14} \newcommand{\SrnRightNullableTwo}{15} -\newcommand{\SrnLeftPromotedVariable}{16} +\newcommand{\SrnLeftPromotedVariableTwo}{16} \newcommand{\SrnLeftVariableBound}{17} \newcommand{\SrnRightFunction}{18} \newcommand{\SrnPositionalFunctionType}{19} \newcommand{\SrnNamedFunctionType}{20} \newcommand{\SrnCovariance}{21} -\newcommand{\SrnSuperinterface}{22} +\newcommand{\SrnNominal}{22} \begin{figure}[p] \def\VSP{\vspace{4mm}} @@ -22722,7 +22721,7 @@ \subsection{Subtypes} % }{X}{\code{Object}} % ------------------------------------------------ Right Object 2 % Not needed unless algorithmic: Instance of - % \SrnLeftPromotedVariable. + % \SrnLeftPromotedVariableTwo. % \RuleRaw{\SrnRightObjectTwo}{% % \SubtypeStd{S}{\code{Object}}}{% % \code{$X$\,\&\,$S$}}{\code{Object}} @@ -22737,7 +22736,6 @@ \subsection{Subtypes} \begin{minipage}[c]{0.49\textwidth} % ------------------------------------------------ Reflexivity \Axiom{\SrnReflexivity}{T}{T} - \ExtraVSP % ------------------------------------------------ Left Top % Non-algorithmic justification for this rule: Needed % to prove dynamic/void <: FutureOr?. @@ -22753,12 +22751,10 @@ \subsection{Subtypes} % ------------------------------------------------ Right Top \RuleRaw{\SrnRightTop}{% T \in \{\code{Object?}, \DYNAMIC, \VOID\}}{S}{T} - % ------------------------------------------------ Right Object 4 + % ------------------------------------------------ Right Object \RuleRaw{\SrnRightObjectFour}{% - $S$\,\not\in \{\code{Null}, \DYNAMIC, \VOID\}\\ - \mbox{$S$ is not of the form \code{$U$?}, $X$,}\\ - \mbox{\code{$X$\,\&\,$U$}, % - or \code{FutureOr<$U$>}}}{S}{\code{Object}} + \mbox{$S$ is an interface type,}\\ + \mbox{a function type, or \FUNCTION}}{S}{\code{Object}} % ------------------------------------------------ Left Null 2 \Rule{\SrnNullTwo}{\code{Null}}{T}{% \code{Null}}{\code{FutureOr<$T$>}} @@ -22779,21 +22775,21 @@ \subsection{Subtypes} \Rule{\SrnRightNullableTwo}{S}{\code{Null}}{S}{% \code{$T$?}} % ------------------------------------------------ Left Variable Bound - \Rule{\SrnLeftVariableBound}{}{\Delta(X)}{T}{X}{T} + \Rule{\SrnLeftVariableBound}{\Delta(X)}{T}{X}{T} \end{minipage} \begin{minipage}[c]{0.49\textwidth} % ------------------------------------------------ Left Nullable \RuleTwo{\SrnLeftNullable}{S}{T}{\code{Null}}{T}{% \code{$S$?}}{T} % ------------------------------------------------ Left Promoted Variable A - \Axiom{\SrnTypeVariableReflexivityA}{X \& S}{X} + \Axiom{\SrnLeftPromotedVariableOne}{X \& S}{X} % ------------------------------------------------ Right FutureOr A \Rule{\SrnRightFutureOrA}{S}{% \code{Future<$T$>}}{S}{\code{FutureOr<$T$>}} % ------------------------------------------------ Right Nullable 1 \Rule{\SrnRightNullableOne}{S}{T}{S}{\code{$T$?}} % ------------------------------------------------ Left Promoted Variable B - \Rule{\SrnLeftPromotedVariable}{S}{T}{X \& S}{T} + \Rule{\SrnLeftPromotedVariableTwo}{S}{T}{X \& S}{T} % ------------------------------------------------ Right Function \RuleRaw{\SrnRightFunction}{% T\mbox{ is a function type}}{T}{\FUNCTION} @@ -22801,7 +22797,7 @@ \subsection{Subtypes} % \ExtraVSP % ------------------------------------------------ Positional Function Type - \RuleRawRaw{\SrnPositionalFunctionType}{}{% + \RuleRawRaw{\SrnPositionalFunctionType}{% \Delta' = \Delta\uplus\{X_i\mapsto{}B_i\,|\,1 \leq i \leq s\} & \Subtype{\Delta'}{S_0}{T_0} \\ n_1 \leq n_2 & @@ -22813,16 +22809,18 @@ \subsection{Subtypes} \end{array}} \ExtraVSP\ExtraVSP % ------------------------------------------------ Named Function Type - \RuleRawRaw{\SrnNamedFunctionType}{}{% + \RuleRawRaw{\SrnNamedFunctionType}{% \Delta' = \Delta\uplus\{X_i\mapsto{}B_i\,|\,1 \leq i \leq s\} & \Subtype{\Delta'}{S_0}{T_0} & - \forall j \in 1 .. n\!:\;\Subtype{\Gamma'}{T_j}{S_j} \\ - \{\,\List{y}{n+1}{n+k_2}\,\} \subseteq \{\,\List{x}{n+1}{n+k_1}\,\} \\ - \forall p \in 1 .. k_2, q \in 1 .. k_1:\quad - y_{n+p} = x_{n+q}\quad\Rightarrow\quad\Subtype{\Delta'}{T_{n+p}}{S_{n+q}}}{% + \forall j \in 1 .. n\!:\;\Subtype{\Delta'}{T_j}{S_j}\\ + \{\,\List{y}{n+1}{n+k_2}\,\} \subseteq \{\,\List{x}{n+1}{n+k_1}\,\}\\ + \forall p \in 1 .. k_2, q \in 1 .. k_1:\quad% + y_{n+p} = x_{n+q}\quad\Rightarrow\quad% + (\Subtype{\Delta'}{T_{n+p}}{S_{n+q}})\;\wedge\;% + (r_{n+q} = \REQUIRED{} \Rightarrow r'_{n+p} = \REQUIRED)}{% \begin{array}{c} \Delta\vdash\RawFunctionTypeNamed{S_0}{X}{B}{s}{S}{n}{x}{k_1}{r}\;<:\;\\ - \RawFunctionTypeNamed{T_0}{X}{B}{s}{T}{n}{y}{k_2}{r} + \RawFunctionTypeNamed{T_0}{X}{B}{s}{T}{n}{y}{k_2}{r'} \end{array}} % \ExtraVSP @@ -22833,7 +22831,7 @@ \subsection{Subtypes} \code{$C$<\List{S}{1}{s}>}}{\code{$C$<\List{T}{1}{s}>}} \ExtraVSP % ------------------------------------------------ Superinterface - \RuleRaw{\SrnSuperinterface}{% + \RuleRaw{\SrnNominal}{% \mbox{$C$ is an interface type with type parameters \List{X}{1}{s}}\\ \Superinterface{\code{$D$<\List{T}{1}{m}>}}{C} & \SubtypeStd{[S_1/X_1,\ldots,S_s/X_s]\code{$D$<\List{T}{1}{m}>}}{T}}{% @@ -22873,6 +22871,9 @@ \subsubsection{Meta-Variables} \item $T$ and $S$ range over types, possibly with an index like $T_1$ or $S_j$. \item $B$ ranges over types, again possibly with an index; it is only used as a type variable bound. +\item $r$ and $r'$ range over \REQUIRED{} or empty; + it is used to enable the specification of a named parameter + which may or may not have the modifier \REQUIRED. \end{itemize} @@ -22900,58 +22901,31 @@ \subsubsection{Subtype Rules} \Subtype{\emptyset}{\code{int}}{\code{int}}, where $\emptyset$ denotes the empty environment (any environment would suffice because no type variables occur).% + +However, we must eliminate the difficulties associated with +different syntax denoting the same type, +and different types denoted by the same syntax. +We do this by assuming that every type in the program has been expressed +in a manner where those situations never occur, +because each type is denoted by the same globally unique syntax everywhere.% } -\commentary{% -The phrases `same type' and `identical syntax' deserves some extra scrutiny: -We may, e.g., use rule~\SrnReflexivity{} -to show that \code{p1.C} is a subtype of -\code{p2.C} when \code{C} is a class declared in a -library $L$ which is imported by libraries $L_1$ and $L_2$ and -used in declarations there, -when $L_1$ and $L_2$ are imported with prefixes -\code{p1} respectively \code{p2} by the current library. -The important point is that all occurrences of the same meta-variable -in a given rule instantiation stands for the same type, -even in the case where that type is not denoted by -the same syntax in both cases. - -Conversely, we can \emph{not} use the same rule to conclude -that \code{C} is a subtype of \code{C} -in the case where the former denotes a class declared in library $L_1$ -and the latter denotes a class declared in $L_2$, with $L_1 \not= L_2$. -This situation can arise without compile-time errors, e.g., -if $L_1$ and $L_2$ are imported indirectly into the current library -and the two ``meanings'' of \code{C} are used -as type annotations on variables or formal parameters of functions -declared in intermediate libraries importing $L_1$ respectively $L_2$. -The failure to prove -``\Subtype{\emptyset}{\code{C}}{\code{C}}'' -will then occur, e.g., in a situation where we check whether -such a variable can be passed as an actual argument to such a function, -because the two occurrences of \code{C} do not denote the same type.% -} - -\LMHash{}% -In this section, -the notion of two types $T_1$ and $T_2$ being the same type -is taken to mean that $T_1$ and $T_2$ have the same canonical syntax -(\ref{standardUpperBoundsAndStandardLowerBounds}). +\LMHash{}% +In section~\ref{subtypes} and its subsections, +all types are considered to be denoted by their canonical syntax +(\ref{theCanonicalSyntaxOfTypes}). \commentary{% -In other words, we eliminate the difficulties associated with -different syntax denoting the same type, -and different types denoted by the same syntax, -by assuming that every type in the program has been expressed -in a manner where those situations never occur, -because each type is denoted by the same globally unique syntax everywhere. -Note that `same canonical syntax' also requires +Note that the canonical syntax also implies transitive expansion of all type aliases -(\ref{typedef}).% +(\ref{typedef}). +In other words, subtyping rules do not need to consider type aliases, +because all type aliases have been expanded.% } \LMHash{}% -Every \synt{typeName} used in a type mentioned in this section +Every \synt{typeName} used in a type mentioned +in section~\ref{subtypes} and its subsections is assumed to have no compile-time error, and it is assumed to denote a type. @@ -23043,7 +23017,7 @@ \subsubsection{Subtype Rules} } -\subsubsection{Being a subtype} +\subsubsection{Being a Subtype} \LMLabel{beingASubtype} \LMHash{}% @@ -23067,28 +23041,14 @@ \subsubsection{Being a subtype} } \LMHash{}% -In rules~\SrnCovariance{} and~\SrnSuperinterface, -the first premise is a class declaration. -This premise is satisfied in each of the following situations: - -\begin{itemize} -\item A non-generic class named $C$ is declared. - In this case $s$ is zero, - no assumptions are made about the existence - of any formal type parameters, - and actual type argument lists are omitted everywhere in the rule. -\item We may choose $s$ and \List{X}{1}{s} such that the following holds: - A generic class named $C$ is declared, - with formal type parameters \List{X}{1}{s}. - \commentary{% - Each formal type parameter $X_j$ may have a bound, - but the bounds are never used in this context, - so we do not introduce metavariables for them.% - } -\end{itemize} +In rules~\SrnCovariance{} and~\SrnNominal, +the first premise is that the given name denotes an interface type +(\ref{interfaceTypes}) +with the specified type parameters. +The non-generic case is covered by having zero type parameters. \LMHash{}% -The second premise of rule~\SrnSuperinterface{} specifies that +The second premise of rule~\SrnNominal{} specifies that a parameterized type \code{$D$<\ldots{}>} belongs to \IndexCustom{\Superinterfaces{C}}{superinterfaces(C)@\Superinterfaces{C}}. The semantic function \Superinterfaces{\_} applied to a generic class $C$ yields @@ -23106,23 +23066,12 @@ \subsubsection{Being a subtype} } \commentary{% -The last premise of rule~\SrnSuperinterface{} +The last premise of rule~\SrnNominal{} substitutes the actual type arguments \List{S}{1}{s} for the formal type parameters \List{X}{1}{s}, because \List{T}{1}{m} may contain those formal type parameters.% } -\commentary{% -The rules~\SrnCovariance{} and~\SrnSuperinterface{} -are applicable to interfaces, -but they can be used with classes as well, -because a non-generic class $C$ which is used as a type -denotes the interface of $C$, -and similarly for a parameterized type -\code{$C$<\List{T}{1}{k}>} -where $C$ denotes a generic class.% -} - \subsubsection{Informal Subtype Rule Descriptions} \LMLabel{informalSubtypeRuleDescriptions} @@ -23163,8 +23112,8 @@ \subsubsection{Informal Subtype Rule Descriptions} the rule is also valid in any environment and the environment is never used explicitly, so we will not repeat that. -\Item{\SrnRightTop}{Top} - Every type is a subtype of \code{Object}, +\Item{\SrnRightTop}{Right Top} + Every type is a subtype of \code{Object?}, every type is a subtype of \DYNAMIC, and every type is a subtype of \VOID. Note that this implies that these types are equivalent @@ -23173,34 +23122,49 @@ \subsubsection{Informal Subtype Rule Descriptions} and others with the same property (such as \code{FutureOr}), as top types (\ref{superBoundedTypes}). +\Item{\SrnLeftTop}{Left Top} + If \code{Object?} is a subtype of any given type $T$, + then \DYNAMIC{} and \VOID{} are subtypes of $T$, too. \Item{\SrnBottom}{Bottom} - Every type is a supertype of $\bot$. -\Item{\SrnNullOne}{Null 1} + \code{Never} is a subtype of every type. +\Item{\SrnRightObjectFour}{Right Object} + Interface types, function types, and \FUNCTION{} + are subtypes of \code{Object}. +\Item{\SrnNullOne}{Null Nullable} \code{Null} is a subtype of every type of the form \code{$T$?}. -\Item{\SrnNullTwo}{Null 2} +\Item{\SrnNullTwo}{Null FutureOr} \code{Null} is a subtype of \code{FutureOr<$T$>} if \code{Null} is a subtype of $T$. \Item{\SrnLeftFutureOr}{Left FutureOr} The type \code{FutureOr<$S$>} is a subtype of a given type $T$ - if $S$ is a subtype of $T$ and \code{Future<$S$>} is a subtype of $T$, + if \code{Future<$S$>} is a subtype of $T$ and $S$ is a subtype of $T$, for every type $S$ and $T$. -\Item{\SrnTypeVariableReflexivityA}{Left Promoted Variable} - The type $X \& S$ is a subtype of $X$. -\Item{\SrnRightPromotedVariable}{Right Promoted Variable A} - The type $S$ is a subtype of $X \& T$ if - $S$ is a subtype of both $X$ and $T$. +\Item{\SrnLeftNullable}{Left Nullable} + A nullable type \code{$S$?} is a subtype of a given type $T$ + if \code{$S$} is a subtype of $T$ and \code{Null} is a subtype of $T$. +\Item{\SrnLeftPromotedVariableOne}{Left Promoted Variable A} + The type \code{$X$\,\&\,$S$} is a subtype of $X$. +\Item{\SrnRightPromotedVariable}{Right Promoted Variable} + The type $S$ is a subtype of \code{$X$\,\&\,$T$} + if $S$ is a subtype of both $X$ and $T$. \Item{\SrnRightFutureOrA}{Right FutureOr A} - The type $S$ is a subtype of \code{FutureOr<$T$>} if - $S$ is a subtype of \code{Future<$T$>}. + The type $S$ is a subtype of \code{FutureOr<$T$>} + if $S$ is a subtype of \code{Future<$T$>}. \Item{\SrnRightFutureOrB}{Right FutureOr B} - The type $S$ is a subtype of \code{FutureOr<$T$>} if - $S$ is a subtype of $T$. -\Item{\SrnLeftPromotedVariable}{Left Promoted Variable B} - The type $X \& S$ is a subtype of $T$ if - $S$ is a subtype of $T$. + The type $S$ is a subtype of \code{FutureOr<$T$>} + if $S$ is a subtype of $T$. +\Item{\SrnRightNullableOne}{Right Nullable A} + The type $S$ is a subtype of \code{$T$?} + if $S$ is a subtype of $T$. +\Item{\SrnRightNullableTwo}{Right Nullable B} + The type $S$ is a subtype of \code{$T$?} + if $S$ is a subtype of \code{Null}. +\Item{\SrnLeftPromotedVariableTwo}{Left Promoted Variable B} + The type \code{$X$\,\&\,$S$} is a subtype of $T$ + if $S$ is a subtype of $T$. \Item{\SrnLeftVariableBound}{Left Variable Bound} - The type variable $X$ is a subtype of a type $T$ if - the bound of $X$ + The type variable $X$ is a subtype of a type $T$ + if the bound of $X$ (as specified in the current environment $\Delta$) is a subtype of $T$. \Item{\SrnRightFunction}{Right Function} @@ -23237,8 +23201,9 @@ \subsubsection{Informal Subtype Rule Descriptions} and the set of names of named parameters for the latter is a subset of that for the former; the return type of $F_1$ is a subtype of that of $F_2$; - and each parameter type of $F_1$ is a \emph{supertype} of - the corresponding parameter type of $F_2$, if any. + each parameter type of $F_1$ is a \emph{supertype} of + the corresponding parameter type of $F_2$, if any; + and each required named parameter in $F_1$ is also required in $F_2$. Note that the relationship to function types with no optional parameters, and the relationship between function types with no optional parameters, is covered by letting $k_2 = 0$ respectively $k_1 = k_2 = 0$, @@ -23257,11 +23222,13 @@ \subsubsection{Informal Subtype Rule Descriptions} This rule may have $s = 0$ and cover a non-generic class as well, but that is redundant because this is already covered by rule~\SrnReflexivity. -\Item{\SrnSuperinterface}{Superinterface} +\Item{\SrnNominal}{Nominal Subtyping} Considering the case where $s = 0$ and $m = 0$ first, a parameterized type based on a non-generic class $C$ is a subtype of a parameterized type based on a different non-generic class $D$ if $D$ is a direct superinterface of $C$. + Subtyping relations with indirect superinterfaces are shown by + using this rule repeatedly. When $s > 0$ or $m > 0$, this rule describes a subtype relationship which includes one or more generic classes, in which case we need to give names to the formal type parameters of $C$, @@ -23293,11 +23260,9 @@ \subsubsection{Additional Subtyping Concepts} \LMHash{}% A type $T$ \Index{may be assigned} -to a type $S$ in an environment $\Delta$, -written \AssignableStd{S}{T}, -if{}f either \SubtypeStd{S}{T} or \SubtypeStd{T}{S}. -In this case we say that the types $S$ and $T$ are -\Index{assignable}. +to a type $S$ in an environment $\Delta$ +if{}f $T$ is \DYNAMIC, or \SubtypeStd{T}{S}. +In this case we also say that the type $T$ is \Index{assignable} to $S$. \rationale{% The use of the type \DYNAMIC{} is intended to shift type checks from @@ -23725,7 +23690,7 @@ \subsection{Type Normalization} where $r_j$ is either \REQUIRED{} or empty then $T_r$ is \noindent - \FunctionTypeNamed{R_0}{ }{X}{B}{s}{R}{n}{x}{k} + \FunctionTypeNamed{R_0}{ }{X}{B}{s}{R}{n}{x}{k}{r} \noindent where $R_i$ is \NormalizedTypeOf{$T_i$} for $i \in 0 .. n+k$. @@ -23757,6 +23722,113 @@ \subsection{Type Normalization} } +\subsubsection{The Canonical Syntax of Types} +\LMLabel{theCanonicalSyntaxOfTypes} + +\LMHash{}% +Concrete syntax denoting types gives rise to several difficulties +when used to determine static semantic properties, +like subtyping relationships +(\ref{subtypes}) +or standard bounds +(\ref{standardUpperBoundsAndStandardLowerBounds}). +This section describes how to overcome those difficulties +by means of a canonical syntactic form for types. + +\commentary{% +In particular, the phrases `same type' and `identical syntax' +deserves some extra scrutiny. +If a library $L$ imports the libraries $L_1$ and $L_2$ +(where $L_1$ and $L_2$ are not the same library), +and both $L_1$ and $L_2$ declare a class \code{C}, +then the syntax \code{C} may occur as a type during static analysis of $L$ +in situations where it refers to two distinct types. + +For instance, $L_1$ could declare a variable \code{v} +of type \code{C}-in-$L_1$, +and $L_2$ could declare a function +\code{\VOID\,foo(C\,\,c)\,\,\{\}} +which uses the type \code{C}-in-$L_2$, +and $L$ could contain the expression \code{foo(v)}.% +} + +\rationale{% +This shows that concrete syntax behaves in such a manner that it is +unsafe to consider two types as the same type, +based on the fact that they are denoted by the same syntax. + +Similarly, it is incorrect to consider two terms derived from \synt{type} +as different types based on the fact that they are syntactically different, +as they could in fact be the same type, +e.g., imported with different import prefixes.% + +Consequently, we introduce the notion of the canonical syntax for a type, +which has the property that each type has a unique syntactic form. +We may then consider this canonical syntactic form +as a static semantic value, +rather than just a syntactic form which is dependent on +its location in the program.% +} + +\LMHash{}% +To determine the +\IndexCustom{canonical syntax}{type!canonical syntax of} +of the types in a given library $L_1$ +and all libraries \List{L}{2}{n} reachable from $L_1$ via +one or more import links, +first choose a set of distinct fresh identifiers +\List{\metavar{prefix}}{1}{n}. +Then transform each library $L_i$, $i \in 1 .. n$, +such that $L_i$ imports itself with the prefix $\metavar{prefix}_i$, +and $L_i$ imports \code{dart:core} explicitly +with the suitable prefix $\metavar{prefix}_j$ for some $j$, +and change all existing imports to use the prefix +corresponding to the library which is being imported. + +\LMHash{}% +Next, transform every identifier expression and every \synt{typeName} +that refers to an imported declaration or a library declaration +such that it uses the prefix $\metavar{prefix}_j$ with the relevant $j$, +and such that every name resolves to the same declaration +as it did in the original program. + +\commentary{% +Note that this transformation does not change any occurrence of \VOID; +\VOID{} is a reserved word +and \code{$\metavar{prefix}_j$.\VOID} is a syntax error.% +} + +%% TODO(eernst), for review: Rename private names to fresh public names? +%% Otherwise the type alias could turn into a term containing private names +%% from different libraries. +\LMHash{}% +Finally, replace every type that denotes a type alias +by its transitive alias expansion +(\ref{typedef}). +\commentary{% +Note that the bodies of type alias declarations already use the new prefixes, +so the results of the alias expansion will also use +the new prefixes consistently.% +} + +\LMHash{}% +Every \synt{type} and type literal in the resulting program +is now expressed in a globally unique syntactic form. + +\rationale{% +This means that two terms denoting a type will have the same syntactic form +if and only if they denote the same type. +} + +\LMHash{}% +When we say that two types $T_1$ and $T_2$ have the +\IndexCustom{same canonical syntax}{type!same canonical syntax}, +it refers to the situation where the current library +and all libraries which are reachable via one or more imports +have been transformed as described above, +and the resulting canonical syntaxes are identical. + + \subsubsection{Standard Upper Bounds and Standard Lower Bounds} \LMLabel{standardUpperBoundsAndStandardLowerBounds} @@ -23841,31 +23913,8 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} then it is assumed that the type variables have been consistently renamed to fresh names whenever necessary, such that no type variables are captured due to accidental name clashes. -Finally, it is assumed that type aliases, -including actual type arguments if any, -have been replaced by their transitive alias expansion -(\ref{typedef}), -and that all types are denoted by a canonical name. - -\commentary{% -That is, we do not encounter the same type under the names -\code{$C$} and \code{\metavar{myPrefix}.$C$}, -and we do not encounter different types with the same name. -This can be achieved for any given program by making sure that -every top-level name reference is accessed using -an import prefix which is unique for the library that contains -the given declaration, -even when it is accessed from the same library. -This means that $C$ is \emph{always} accessed as, say, -\code{\metavar{library87381}.$C$}.% -} - -\LMHash{}% -Consequently, when we say that two types $T_1$ and $T_2$ have the -\IndexCustom{same canonical syntax}{type!same canonical syntax}, -it refers to the situation where both $T_1$ and $T_2$ have been -transformed in the above sense -(\commentary{by alpha-renaming, alias expansion, and canonical naming}). +Finally, it is assumed that all types are denoted by their canonical syntax +(\ref{theCanonicalSyntaxOfTypes}). %% TODO(eernst), for review: Is this the correct associativity of SUB/SLB? \LMHash{}% @@ -24765,7 +24814,7 @@ \subsection{Least and Greatest Closure of Types} the least closure of $S$ with respect to $L$ is \noindent - \FunctionTypeNamed{U_0}{ }{X}{B}{s}{U}{n}{x}{k} + \FunctionTypeNamed{U_0}{ }{X}{B}{s}{U}{n}{x}{k}{r} \noindent where @@ -24780,7 +24829,7 @@ \subsection{Least and Greatest Closure of Types} the greatest closure of $S$ with respect to $L$ is \noindent - \FunctionTypeNamed{U_0}{ }{X}{B}{s}{U}{n}{x}{k} + \FunctionTypeNamed{U_0}{ }{X}{B}{s}{U}{n}{x}{k}{r} \noindent where $U_0$ is the greatest closure of $T_0$ with respect to $L$, @@ -24901,20 +24950,29 @@ \subsection{Interface Types} \LMHash{}% Interface types can be used to access a specific set of instance members. -Let $T$ be a type. +Let $T$ be a type, and let $T'$ be the transitive alias expansion of $T$ +(\ref{typedef}). We say that $T$ is an \Index{interface type} if{}f -$T$ is of the form \code{$C$<\List{T}{1}{k}>}, +$T'$ is of the form \code{$C$<\List{T}{1}{k}>}, where $C$ denotes a class different from \code{Never}, -or $C$ denotes a mixin, -and \List{T}{1}{k} are arbitrary types. +or $C$ denotes a mixin. \commentary{% +Note that \List{T}{1}{k} can be arbitrary types. Non-generic classes are included because we can have $k = 0$. In particular, the following types are \emph{not} interface types: \VOID, \DYNAMIC, \FUNCTION, \code{FutureOr<$T$>} for any $T$, \code{Never}, any function type, any type variable, any intersection type, -and any type of the form \code{$T$?}.% +and any type of the form \code{$T$?}. + +Conversely, built-in classes +like \code{Object}, \code{Null}, \code{num}, \code{int}, +\code{String}, and \code{Exception} are interface types, +and so are +\code{Future<$T$>}, \code{Stream<$T$>}, \code{Iterable<$T$>}, +\code{List<$T$>}, \code{Map<$S$,\,\,$T$}, and \code{Set<$T$>}, +for any $S$ and $T$.% } @@ -27129,156 +27187,238 @@ \subsection{Migration features} \section*{Appendix: Algorithmic Subtyping} \LMLabel{algorithmicSubtyping} -% Subtype Rule Numbering -\newcommand{\AppSrnReflexivity}{\ensuremath{1_{\scriptsize\mbox{algo}}}} -\newcommand{\AppSrnTypeVariableReflexivityB}{\SrnTypeVariableReflexivityA.1} -\newcommand{\AppSrnTypeVariableReflexivityC}{\SrnTypeVariableReflexivityA.2} -\newcommand{\AppSrnTypeVariableReflexivityD}{\SrnTypeVariableReflexivityA.3} -\newcommand{\AppSrnRightFutureOrC}{\SrnRightFutureOrB.1} -\newcommand{\AppSrnRightFutureOrD}{\SrnRightFutureOrB.2} - -\begin{figure}[h!] - \def\VSP{\vspace{3mm}} - \def\ExtraVSP{\vspace{1mm}} - \def\Axiom#1#2#3#4{\centerline{\inference[#1]{}{\SubtypeStd{#3}{#4}}}\VSP} - \def\Rule#1#2#3#4#5#6{\centerline{\inference[#1]{\SubtypeStd{#3}{#4}}{\SubtypeStd{#5}{#6}}}\VSP} - \def\RuleTwo#1#2#3#4#5#6#7#8{% - \centerline{\inference[#1]{\SubtypeStd{#3}{#4} & \SubtypeStd{#5}{#6}}{\SubtypeStd{#7}{#8}}}\VSP} - \def\RuleRaw#1#2#3#4#5{% - \centerline{\inference[#1]{#3}{\SubtypeStd{#4}{#5}}}\VSP} - \def\RuleRawRaw#1#2#3#4{\centerline{\inference[#1]{#3}{#4}}\VSP} - % - \begin{minipage}[c]{0.49\textwidth} - \RuleRaw{\AppSrnReflexivity}{Reflexivity}{S\mbox{ not composite}}{S}{S} - \Rule{\AppSrnTypeVariableReflexivityC}{Type Variable Reflexivity B}{X}{T}{X}{X \& T} - \Rule{\AppSrnRightFutureOrC}{Right FutureOr C}{\Delta(X)}{\code{FutureOr<$T$>}}{X}{\code{FutureOr<$T$>}} - \end{minipage} - \begin{minipage}[c]{0.49\textwidth} - \Axiom{\AppSrnTypeVariableReflexivityB}{Type Variable Reflexivity}{X}{X} - \Rule{\AppSrnTypeVariableReflexivityD}{Type Variable Reflexivity C}{X \& S}{T}{X \& S}{X \& T} - \Rule{\AppSrnRightFutureOrD}{Right FutureOr D}{S}{\code{FutureOr<$T$>}}{X \& S}{\code{FutureOr<$T$>}} - \end{minipage} - % - \caption{Algorithmic subtype rules. - Rules \SrnRightTop--\SrnSuperinterface{} are unchanged and hence omitted here.} - \label{fig:algorithmicSubtypeRules} -\end{figure} +\LMHash{}% +The following algorithm computes the same relation as +the one which is specified in Fig.~\ref{fig:subtypeRules}. +It shows that Dart subtyping relationships can be decided +with good performance. \LMHash{}% -The text in this appendix is not part of the specification of the Dart language. -However, we still use the notation where precise information -uses the style associated with normative text in the specification (this style), -\commentary{whereas examples and explanations use commentary style (like this)}. +In this algorithm, types are considered to be the same when they have +the same canonical syntax +(\ref{theCanonicalSyntaxOfTypes}). +The algorithm must be performed such that the first case that matches +is always the case which is performed. +The algorithm produces results which are both positive and negative +(\commentary{% + that is, in some situations the subtype relation is determined to be false% +}), +which is important for performance because +it is then unnecessary to consider any subsequent cases. \LMHash{}% -This appendix presents a variant of the subtype rules given -in Figure~\ref{fig:subtypeRules} on page~\pageref{fig:subtypeRules}. +A type $T_0$ is a subtype of a type $T_1$ (written \SubtypeNE{T_0}{T_1}) when: -\commentary{% -The rules will prove the same set of subtype relationships, -but the rules given here show that there is an efficient implementation -that will determine whether \SubtypeStd{S}{T} holds, -for any given types $S$ and $T$. -It is easy to see that the algorithmic rules will prove at most -the same subtype relationships, -because all rules given here can be proven -by means of rules in Figure~\ref{fig:subtypeRules}. -It is also relatively straightforward to sketch out proofs -that the algorithmic rules can prove at least the same subtype relationships, -also when the following ordering and termination constraints are observed.% -} +\begin{itemize} +\item + \textbf{Reflexivity:} + if $T_0$ and $T_1$ are the same type then \SubtypeNE{T_0}{T_1} + + \commentary{% + Note that this check is necessary as the base case for primitive types, + and type variables, but not for composite types. + In particular, a structural equality check is admissible, + but not required here. + Pragmatically, non-constant time identity checks here are + counter-productive. + So this rule should only be used when $T$ is atomic.% + } +\item + \textbf{Right Top:} + if $T_1$ is \DYNAMIC, \VOID, or \code{Object?} then \SubtypeNE{T_0}{T_1}. +\item + \textbf{Left Top:} + if $T_0$ is \DYNAMIC{} or \VOID{} + then \SubtypeNE{T_0}{T_1} if \SubtypeNE{\code{Object?}}{T_1}. +\item + \textbf{Left Bottom:} + if $T_0$ is \code{Never} then \SubtypeNE{T_0}{T_1}. +\item + \textbf{Right Object:} + if $T_1$ is \code{Object} then: + \begin{itemize} + \item + if $T_0$ is an unpromoted type variable with bound $B$ + then \SubtypeNE{T_0}{T_1} if{}f \SubtypeNE{B}{\code{Object}}. + \item + if $T_0$ is a promoted type variable \code{$X$\,\&\,$S$} + then \SubtypeNE{T_0}{T_1} if{}f \SubtypeNE{S}{\code{Object}}. + \item + if $T_0$ is \code{FutureOr<$S$>} for some $S$, + then \SubtypeNE{T_0}{T_1} if{}f \SubtypeNE{S}{\code{Object}}. + \item + if $T_0$ is \code{Null}, \DYNAMIC, \VOID, or \code{$S$?} for any $S$, + then the subtyping does not hold + (\commentary{% + i.e., the result of the subtyping query is known to be false% + }). + \item + Otherwise \SubtypeNE{T_0}{T_1} is true. + \end{itemize} +\item + \textbf{Left Null:} + if $T_0$ is \code{Null} then: + \begin{itemize} + \item + if $T_1$ is a type variable (promoted or not) the query is false. + \item + if $T_1$ is \code{FutureOr<$S$>} for some $S$, + then the query is true if{}f \SubtypeNE{\code{Null}}{S}. + \item + if $T_1$ is \code{Null} or \code{$S$?} for some $S$, + then the query is true. + \item + Otherwise, the query is false. + \end{itemize} +\item + \textbf{Left FutureOr:} + if $T_0$ is \code{FutureOr<$S_0$>} + then \SubtypeNE{T_0}{T_1} if{}f + \SubtypeNE{\code{Future<$S_0$>}}{T_1} and \SubtypeNE{S_0}{T_1}. +\item + \textbf{Left Nullable:} + if $T_0$ is \code{$S_0$?} then \SubtypeNE{T_0}{T_1} if{}f + \SubtypeNE{S_0}{T_1} and \SubtypeNE{\code{Null}}{T_1}. +\item + \textbf{Type Variable Reflexivity 1:} + if $T_0$ is a type variable $X_0$ + or a promoted type variables \code{$X_0$\,\&\,$S_0$} and $T_1$ is $X_0$ + then \SubtypeNE{T_0}{T_1}. + + \commentary{% + Note that this rule is admissible, and can be safely elided if desired.% + } +\item + \textbf{Type Variable Reflexivity 2:} + if $T_0$ is a type variable $X_0$ + or a promoted type variable \code{$X_0$\,\&\,$S_0$} + and $T_1$ is \code{$X_0$\,\&\,$S_1$} + then \SubtypeNE{T_0}{T_1} if{}f \SubtypeNE{T_0}{S_1}. -\LMHash{}% -The only rule which is modified is number~\SrnReflexivity, -which is modified to \AppSrnReflexivity. -This only changes the applicability of the rule: -This rule is only used for types which are not atomic. -An \IndexCustom{atomic type}{type!atomic} -is a type which is not a type variable, -not a promoted type variable, -not a function type, -and not a parameterized type. - -\commentary{% -In other words, rule \AppSrnReflexivity{} is used for -special types like \DYNAMIC, \VOID, and \FUNCTION, -and it is used for non-generic classes, -but it is not used for any type where it is an operation -that takes more than one comparison to detect whether -it is the same as some other type. -% -The point is that the remaining rules will force -a structural traversal anyway, as far as needed, -and we may hence just as well omit the initial structural traversal -which might take many steps only to report that two large type terms -are not quite identical.% -} - -\LMHash{}% -The rules are ordered by means of their rule numbers: -A rule given here numbered $N.1$ is inserted immediately after rule $N$, -followed by rule $N.2$, and so on, -followed by the rule whose number is $N+1$. -\commentary{% -So the order is -\AppSrnReflexivity, \SrnRightTop--\SrnTypeVariableReflexivityA, -\AppSrnTypeVariableReflexivityB, \AppSrnTypeVariableReflexivityC, -\AppSrnTypeVariableReflexivityD, -\SrnRightPromotedVariable, and so on.% -} - -\LMHash{}% -We now specify the procedure which is used to determine whether -\SubtypeStd{S}{T} holds, -for some specific types $S$ and $T$: -Select the first rule $R$ whose syntactic constraints are satisfied -by the given types $S$ and $T$, -and proceed to show that its premises hold. -If so, we terminate and conclude that the subtype relationship holds. -Otherwise we terminate and conclude -that the subtype relationship does not hold, -except if $R$ is -\SrnRightFutureOrA, \SrnRightFutureOrB, -\AppSrnRightFutureOrC, or \AppSrnRightFutureOrD. -\commentary{% -In particular, for the original query \SubtypeStd{S}{T}, -we do not backtrack into trying to use a rule that has -a higher rule number than that of $R$, -except that we may try all of -the rules with \code{FutureOr<$T$>} to the right.% -} - -\commentary{% -Apart from the fact that the full complexity of subtyping -is potentially incurred each time it is checked whether a premise holds, -the checks applied for each rule is associated with an amount of work -which is constant for all rules except the following: -First, the group of rules -\SrnRightFutureOrA, \SrnRightFutureOrB, -\AppSrnRightFutureOrC, and \AppSrnRightFutureOrD{} -may cause backtracking to take place. -Next, rules \SrnPositionalFunctionType--\SrnCovariance{} -require work proportional to the size of $S$ and $T$, -due to the number of premises that must be checked. -Finally, rule~\SrnSuperinterface{} requires work -proportional to the size of $S$, -and it may also incur the cost of searching up to the entire set of -direct and indirect superinterfaces of the candidate subtype $S$, -until the corresponding premise for one of them is shown to hold, -if any. - -Additional optimizations are applicable. -For instance, -we can immediately conclude that the subtype relationship does not hold -when we are about to check rule~\SrnSuperinterface{} -if $T$ is a type variable or a function type. -For several other forms of type, e.g., -a promoted type variable, -\code{Object}, \DYNAMIC, \VOID, -\code{FutureOr<$T$>} for any $T$, or \FUNCTION, -it is known that it will never occur as $T$ for rule~\SrnSuperinterface, -which means that this seemingly expensive step can be confined to some extent.% -} + \commentary{% + Note that this rule is admissible, and can be safely elided if desired.% + } +\item + \textbf{Right Promoted Variable:} + if $T_1$ is a promoted type variable \code{$X_1$\,\&\,$S_1$} + then \SubtypeNE{T_0}{T_1} if{}f \SubtypeNE{T_0}{X_1} and \SubtypeNE{T_0}{S_1}. +\item + \textbf{Right FutureOr:} + if $T_1$ is \code{FutureOr<$S_1$>} + then \SubtypeNE{T_0}{T_1} if{}f any of the following hold: + \begin{itemize} + \item either \SubtypeNE{T_0}{\code{Future<$S_1$>}}. + \item or \SubtypeNE{T_0}{S_1}. + \item or $T_0$ is $X_0$ and $X_0$ has bound $S_0$ and \SubtypeNE{S_0}{T_1}. + \item or $T_0$ is \code{$X_0$\,\&\,$S_0$} and \SubtypeNE{S_0}{T_1}. + \end{itemize} +\item + \textbf{Right Nullable:} + if $T_1$ is \code{$S_1$?} + then \SubtypeNE{T_0}{T_1} if{}f any of the following hold: + \begin{itemize} + \item either \SubtypeNE{T_0}{S_1}. + \item or \SubtypeNE{T_0}{\code{Null}}. + \item or $T_0$ is $X_0$ and $X_0$ has bound $S_0$ and \SubtypeNE{S_0}{T_1}. + \item or $T_0$ is \code{$X_0$\,\&\,$S_0$} and \SubtypeNE{S_0}{T_1}. + \end{itemize} +\item + \textbf{Left Promoted Variable:} + $T_0$ is a promoted type variable \code{$X_0$\,\&\,$S_0$} + and \SubtypeNE{S_0}{T_1}. +\item + \textbf{Left Type Variable Bound:} + $T_0$ is a type variable $X_0$ with bound $B_0$ + and \SubtypeNE{B_0}{T_1}. +\item + \textbf{Function Type/Function:} + $T_0$ is a function type and $T_1$ is \FUNCTION. +\item + \textbf{Interface Compositionality:} + $T_0$ is an interface type \code{$C_0$<$S_0$, \ldots, $S_k$>} + and $T_1$ is \code{$C_0$<$U_0$, \ldots, $U_k$>} and each \SubtypeNE{S_i}{U_i}. +\item + \textbf{Super-Interface:} + $T_0$ is an interface type with super-interfaces \List{S}{0}{n} + and \SubtypeNE{S_i}{T_1} for some $i$. +\item + \textbf{Positional Function Types:} + $T_0$ is + + \code{$U_0$ \FUNCTION<% + $X_0$\,\EXTENDS\,$B_{00}$, \ldots, $X_k$\,\EXTENDS\,$B_{0k}$>(% + $V_0$\,$x_0$, \ldots, $V_n$ $x_n$, % + [$V_{n+1}$\,\,$x_{n+1}$, \ldots, $V_m$\,\,$x_m$])} + + and $T_1$ is + + \code{$U_1$ \FUNCTION<% + $Y_0$\,\EXTENDS\,$B_{10}$, \ldots, $Y_k$\,\EXTENDS\,$B_{1k}$>(% + $S_0$\,$y_0$, \ldots, $S_p$\,$y_p$, % + [$S_{p+1}$\,$y_{p+1}$, \ldots, $S_q$\,$y_q$])} + + where each of the following hold: + \begin{itemize} + \item $p \geq n$. + \item $m \geq q$. + \item \SubtypeNE{S_i[Z_0/Y_0, \ldots, Z_k/Y_k]}{V_i[Z_0/X_0, \ldots, Z_k/X_k]} + for $i \in 0 .. q$. + \item \SubtypeNE{U_0[Z_0/X_0, \ldots, Z_k/X_k]}{U_1[Z_0/Y_0, \ldots, Z_k/Y_k]}. + \item $B_{0i}[Z_0/X_0, \ldots, Z_k/X_k]$ and $B_{1i}[Z_0/Y_0, \ldots, Z_k/Y_k]$ + have the same canonical syntax, for $i \in 0 .. k$. + \item where the $Z_i$ are fresh type variables with bounds + $B_{0i}[Z_0/X_0, \ldots, Z_k/X_k]$. + \end{itemize} +\item + \textbf{Named Function Types:} + $T_0$ is + + \code{% + $U_0$ \FUNCTION<$X_0$\,\EXTENDS\,$B_{00}$, \ldots, % + $X_k$\,\EXTENDS\,$B_{0k}$>(% + $V_0$\,$x_0$, \ldots, $V_n$\,$x_n$, % + \{ $r_{0,n+1}$\,$V_{n+1}$\,$x_{n+1}$, \ldots, $r_{0m}$\,$V_m$\,$x_m$\})} + + where $r_{0j}$ is empty or \REQUIRED{} for $j \in n+1 .. m$ + and $T_1$ is + + \code{% + $U_1$ \FUNCTION<$Y_0$\,\EXTENDS\,$B_{10}$, \ldots, % + $Y_k$\,\EXTENDS\,$B_{1k}$>(% + $S_0$\,$y_0$, \ldots, $S_n$\,$y_n$, % + \{ $r_{1,n+1}$\,$S_{n+1}$\,$y_{n+1}$, \ldots, $r_{1q}$\,$S_q$\,$y_q$\})} + + where $r_{1j}$ is empty or \REQUIRED{} for $j \in n+1 .. q$ + and the following criteria are all satisfied, + where \List{Z}{1}{k} are fresh type variables with bounds + $B_{0i}[Z_0/X_0, \ldots, Z_k/X_k]$: + + \begin{itemize} + \item + $\{ y_{n+1}, \ldots, y_q \} \subseteq \{ x_{n+1}, \ldots , x_m \}$. + \item + \SubtypeNE{S_i[Z_0/Y_0, \ldots, Z_k/Y_k]}{V_i[Z_0/X_0, \ldots, Z_k/X_k]} + for $i \in 0 .. n$. + \item \SubtypeNE{S_i[Z_0/Y_0, \ldots, Z_k/Y_k]}{V_j[Z_0/X_0, \ldots, Z_k/X_k]} + for $i \in n+1 .. q$, and $y_j = x_i$. + \item + for each $j$ such that $r_{0j}$ is \REQUIRED, there exists an + $i \in n+1 .. q$ such that $x_j = y_i$, and $r_{1i}$ is \REQUIRED. + \item + \SubtypeNE{U_0[Z_0/X_0, \ldots, Z_k/X_k]}{U_1[Z_0/Y_0, \ldots, Z_k/Y_k]}. + \item + $B_{0i}[Z_0/X_0, \ldots, Z_k/X_k]$ and $B_{1i}[Z_0/Y_0, \ldots, Z_k/Y_k]$ + have the same canonical syntax, + for each $i \in 0 .. k$. + \end{itemize} + + \commentary{% + The requirement that \List{Z}{1}{k} are fresh names is as usual required + such that type variable names do not get captured. + It is valid to choose $Z_i$ to be $X_i$ or $Y_i$, + so long as capture is avoided.% + } +\end{itemize} \section*{Appendix: Integer Implementations} From 76d285a65364338555a1e34282eb7a48c54cc912 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 10 Dec 2021 01:25:11 +0100 Subject: [PATCH 41/85] Rebase --- specification/dartLangSpec.tex | 143 ++++++++++++++++++++++----------- 1 file changed, 94 insertions(+), 49 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index f64e41e67..a4d0952a5 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -23755,12 +23755,13 @@ \subsubsection{The Canonical Syntax of Types} \rationale{% This shows that concrete syntax behaves in such a manner that it is unsafe to consider two types as the same type, -based on the fact that they are denoted by the same syntax. +based on the fact that they are denoted by the same syntax, +even during the static analysis of a single expression. Similarly, it is incorrect to consider two terms derived from \synt{type} -as different types based on the fact that they are syntactically different, -as they could in fact be the same type, -e.g., imported with different import prefixes.% +as different types based on the fact that they are syntactically different. +They could in fact be the same type, +e.g., imported with different import prefixes. Consequently, we introduce the notion of the canonical syntax for a type, which has the property that each type has a unique syntactic form. @@ -23776,49 +23777,63 @@ \subsubsection{The Canonical Syntax of Types} of the types in a given library $L_1$ and all libraries \List{L}{2}{n} reachable from $L_1$ via one or more import links, -first choose a set of distinct fresh identifiers +first choose a set of distinct, globally fresh identifiers \List{\metavar{prefix}}{1}{n}. -Then transform each library $L_i$, $i \in 1 .. n$, -such that $L_i$ imports itself with the prefix $\metavar{prefix}_i$, -and $L_i$ imports \code{dart:core} explicitly -with the suitable prefix $\metavar{prefix}_j$ for some $j$, -and change all existing imports to use the prefix -corresponding to the library which is being imported. +Then transform each library $L_i$, $i \in 1 .. n$ as follows: -\LMHash{}% -Next, transform every identifier expression and every \synt{typeName} -that refers to an imported declaration or a library declaration -such that it uses the prefix $\metavar{prefix}_j$ with the relevant $j$, -and such that every name resolves to the same declaration -as it did in the original program. +\begin{enumerate} +\item + Add a set of import directives to $L_i$ that imports + each of the libraries \List{L}{1}{n} with + the corresponding prefix $\metavar{prefix}_j$, $j \in 1 .. n$. + + \commentary{% + This means that every library in the set + $\{\,\List{L}{1}{n}\,\}$ + imports every other library in that set, + even itself and system libraries like \code{dart:core}.% + } +\item + Let \id{} be a non-private identifier that resolves to + a library declaration in the library $L_j$ in the original program; + \id{} is transformed to \code{$\metavar{prefix}_j$.\id}. + Let \code{$p$.\id} be a qualified identifier where $p$ is + an import prefix in the original program, + \id{} is a non-private identifier, + and \code{$p$.\id} resolves to + a library declaration in the library $L_j$ in the original program; + \code{$p$.\id} is transformed to \code{$\metavar{prefix}_j$.\id}. +\item + %% TODO(eernst): We should rename private names to fresh public names. + Replace every type that denotes a type alias + by its transitive alias expansion + (\ref{typedef}). + \commentary{% + Note that the bodies of type alias declarations + already use the new prefixes, + so the results of the alias expansion will also use + the new prefixes consistently.% + } +\end{enumerate} \commentary{% Note that this transformation does not change any occurrence of \VOID; -\VOID{} is a reserved word -and \code{$\metavar{prefix}_j$.\VOID} is a syntax error.% -} +\VOID{} is a reserved word, not an identifier. +Also, \code{$\metavar{prefix}_j$.\VOID} would be a syntax error. -%% TODO(eernst), for review: Rename private names to fresh public names? -%% Otherwise the type alias could turn into a term containing private names -%% from different libraries. -\LMHash{}% -Finally, replace every type that denotes a type alias -by its transitive alias expansion -(\ref{typedef}). -\commentary{% -Note that the bodies of type alias declarations already use the new prefixes, -so the results of the alias expansion will also use -the new prefixes consistently.% +The transformation also does not change identifiers denoting type variables, +There is no need to change those identifiers, because +no occurrence of such an identifier resolves to a declaration in a +different library.% +%% TODO(eernst): Sort out the treatment of private identifiers, too. } \LMHash{}% -Every \synt{type} and type literal in the resulting program -is now expressed in a globally unique syntactic form. - -\rationale{% -This means that two terms denoting a type will have the same syntactic form -if and only if they denote the same type. -} +Every \synt{type} and type literal in the resulting set of libraries +is now expressed in a globally unique syntactic form, +which is the form that we call the +\IndexCustom{canonical syntax of}{type!canonical syntax of} +said types. \LMHash{}% When we say that two types $T_1$ and $T_2$ have the @@ -23828,6 +23843,36 @@ \subsubsection{The Canonical Syntax of Types} have been transformed as described above, and the resulting canonical syntaxes are identical. +\rationale{% +The transformation described here would not be useful in practice +(or even possible---we can't edit \code{dart:core}). +It only serves to show that we can express types using a syntactic form +which is independent of the location. +This is in turn needed in order to ensure that operations are well-defined +even when they bring syntactic elements from different locations together, +such as computations of subtype relationships, +and construction of standard upper or lower bounds. + +We could just as well have replaced the concrete syntax by a semantic +notion of types, +where each entity that denotes a type would be, in some sense, +a reference to a specific declaration +(this is likely to be the approach used by tool implementations). +However, that approach would be somewhat inconvenient in a specification, +because we would need to re-build all the structures that the +syntax offers. +For instance, we would need to support the construction of +a semantic type entity for \code{Map}, +based on the semantic type entity for \code{int}, \code{String}, and \code{Map}, +and we would need to support deconstruction of those entities +in order to prove things like \SubtypeNE{Never}{\code{Map}}. +This would give rise to a lot of mechanism that will simply duplicate +the structure of the syntax. +So we prefer to show that the syntax \emph{can} be location independent, +and that's sufficient to make syntax usable as our representation of +static semantic types.% +} + \subsubsection{Standard Upper Bounds and Standard Lower Bounds} \LMLabel{standardUpperBoundsAndStandardLowerBounds} @@ -27213,7 +27258,7 @@ \section*{Appendix: Algorithmic Subtyping} \item \textbf{Reflexivity:} if $T_0$ and $T_1$ are the same type then \SubtypeNE{T_0}{T_1} - + \commentary{% Note that this check is necessary as the base case for primitive types, and type variables, but not for composite types. @@ -27229,7 +27274,7 @@ \section*{Appendix: Algorithmic Subtyping} \item \textbf{Left Top:} if $T_0$ is \DYNAMIC{} or \VOID{} - then \SubtypeNE{T_0}{T_1} if \SubtypeNE{\code{Object?}}{T_1}. + then \SubtypeNE{T_0}{T_1} if{}f \SubtypeNE{\code{Object?}}{T_1}. \item \textbf{Left Bottom:} if $T_0$ is \code{Never} then \SubtypeNE{T_0}{T_1}. @@ -27265,8 +27310,7 @@ \section*{Appendix: Algorithmic Subtyping} if $T_1$ is \code{FutureOr<$S$>} for some $S$, then the query is true if{}f \SubtypeNE{\code{Null}}{S}. \item - if $T_1$ is \code{Null} or \code{$S$?} for some $S$, - then the query is true. + if $T_1$ is \code{$S$?} for some $S$ then the query is true. \item Otherwise, the query is false. \end{itemize} @@ -27309,7 +27353,7 @@ \section*{Appendix: Algorithmic Subtyping} \begin{itemize} \item either \SubtypeNE{T_0}{\code{Future<$S_1$>}}. \item or \SubtypeNE{T_0}{S_1}. - \item or $T_0$ is $X_0$ and $X_0$ has bound $S_0$ and \SubtypeNE{S_0}{T_1}. + \item or $T_0$ is $X_0$ and $X_0$ has bound $B_0$ and \SubtypeNE{B_0}{T_1}. \item or $T_0$ is \code{$X_0$\,\&\,$S_0$} and \SubtypeNE{S_0}{T_1}. \end{itemize} \item @@ -27319,7 +27363,7 @@ \section*{Appendix: Algorithmic Subtyping} \begin{itemize} \item either \SubtypeNE{T_0}{S_1}. \item or \SubtypeNE{T_0}{\code{Null}}. - \item or $T_0$ is $X_0$ and $X_0$ has bound $S_0$ and \SubtypeNE{S_0}{T_1}. + \item or $T_0$ is $X_0$ and $X_0$ has bound $B_0$ and \SubtypeNE{B_0}{T_1}. \item or $T_0$ is \code{$X_0$\,\&\,$S_0$} and \SubtypeNE{S_0}{T_1}. \end{itemize} \item @@ -27357,7 +27401,10 @@ \section*{Appendix: Algorithmic Subtyping} $S_0$\,$y_0$, \ldots, $S_p$\,$y_p$, % [$S_{p+1}$\,$y_{p+1}$, \ldots, $S_q$\,$y_q$])} - where each of the following hold: + such that each of the following criteria is satisfied, + where the $Z_i$ are fresh type variables with bounds + $B_{0i}[Z_0/X_0, \ldots, Z_k/X_k]$: + \begin{itemize} \item $p \geq n$. \item $m \geq q$. @@ -27366,8 +27413,6 @@ \section*{Appendix: Algorithmic Subtyping} \item \SubtypeNE{U_0[Z_0/X_0, \ldots, Z_k/X_k]}{U_1[Z_0/Y_0, \ldots, Z_k/Y_k]}. \item $B_{0i}[Z_0/X_0, \ldots, Z_k/X_k]$ and $B_{1i}[Z_0/Y_0, \ldots, Z_k/Y_k]$ have the same canonical syntax, for $i \in 0 .. k$. - \item where the $Z_i$ are fresh type variables with bounds - $B_{0i}[Z_0/X_0, \ldots, Z_k/X_k]$. \end{itemize} \item \textbf{Named Function Types:} @@ -27377,7 +27422,7 @@ \section*{Appendix: Algorithmic Subtyping} $U_0$ \FUNCTION<$X_0$\,\EXTENDS\,$B_{00}$, \ldots, % $X_k$\,\EXTENDS\,$B_{0k}$>(% $V_0$\,$x_0$, \ldots, $V_n$\,$x_n$, % - \{ $r_{0,n+1}$\,$V_{n+1}$\,$x_{n+1}$, \ldots, $r_{0m}$\,$V_m$\,$x_m$\})} + \{$r_{0,n+1}$\,$V_{n+1}$\,$x_{n+1}$, \ldots, $r_{0m}$\,$V_m$\,$x_m$\})} where $r_{0j}$ is empty or \REQUIRED{} for $j \in n+1 .. m$ and $T_1$ is @@ -27386,7 +27431,7 @@ \section*{Appendix: Algorithmic Subtyping} $U_1$ \FUNCTION<$Y_0$\,\EXTENDS\,$B_{10}$, \ldots, % $Y_k$\,\EXTENDS\,$B_{1k}$>(% $S_0$\,$y_0$, \ldots, $S_n$\,$y_n$, % - \{ $r_{1,n+1}$\,$S_{n+1}$\,$y_{n+1}$, \ldots, $r_{1q}$\,$S_q$\,$y_q$\})} + \{$r_{1,n+1}$\,$S_{n+1}$\,$y_{n+1}$, \ldots, $r_{1q}$\,$S_q$\,$y_q$\})} where $r_{1j}$ is empty or \REQUIRED{} for $j \in n+1 .. q$ and the following criteria are all satisfied, From d2e5e5701b28bdd3eb6e6439718adddcdb533c33 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 10 Dec 2021 12:21:23 +0100 Subject: [PATCH 42/85] Clarified and corrected Canonical Syntax --- specification/dartLangSpec.tex | 100 ++++++++++++++++++++++++--------- 1 file changed, 74 insertions(+), 26 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index a4d0952a5..d48001082 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -22912,7 +22912,8 @@ \subsubsection{Subtype Rules} \LMHash{}% In section~\ref{subtypes} and its subsections, -all types are considered to be denoted by their canonical syntax +all designations of types are considered to be the same +if{}f they have the same canonical syntax (\ref{theCanonicalSyntaxOfTypes}). \commentary{% @@ -23749,7 +23750,12 @@ \subsubsection{The Canonical Syntax of Types} and $L_2$ could declare a function \code{\VOID\,foo(C\,\,c)\,\,\{\}} which uses the type \code{C}-in-$L_2$, -and $L$ could contain the expression \code{foo(v)}.% +and $L$ could contain the expression \code{foo(v)}. + +Note that even though it would be a compile-time error to use \code{C} in $L$ +(because it is ambiguous), +it is not an error to have an expression like \code{foo(v)}, +and the static analysis of this expression must handle the name clash.% } \rationale{% @@ -23772,16 +23778,28 @@ \subsubsection{The Canonical Syntax of Types} } \LMHash{}% -To determine the +The \IndexCustom{canonical syntax}{type!canonical syntax of} of the types in a given library $L_1$ and all libraries \List{L}{2}{n} reachable from $L_1$ via -one or more import links, -first choose a set of distinct, globally fresh identifiers +one or more import links +is determined as follows. +First, choose a set of distinct, globally fresh identifiers \List{\metavar{prefix}}{1}{n}. Then transform each library $L_i$, $i \in 1 .. n$ as follows: \begin{enumerate} +\item + If $D_T$ is a declaration of a class, mixin, or type alias in $L_i$ + whose name $n$ is private, + and an occurrence of $n$ that resolves to $D$ + exists in a type alias declaration $D_A$ whose name is non-private, + then perform a consistent renaming of + all occurrences of $n$ in $L_i$ that resolve to $D_T$ + to a fresh, non-private identifier. + \commentary{% + So we make $D_T$ public, because it is being leaked anyway.% + } \item Add a set of import directives to $L_i$ that imports each of the libraries \List{L}{1}{n} with @@ -23794,18 +23812,20 @@ \subsubsection{The Canonical Syntax of Types} even itself and system libraries like \code{dart:core}.% } \item - Let \id{} be a non-private identifier that resolves to - a library declaration in the library $L_j$ in the original program; - \id{} is transformed to \code{$\metavar{prefix}_j$.\id}. - Let \code{$p$.\id} be a qualified identifier where $p$ is - an import prefix in the original program, - \id{} is a non-private identifier, - and \code{$p$.\id} resolves to - a library declaration in the library $L_j$ in the original program; - \code{$p$.\id} is transformed to \code{$\metavar{prefix}_j$.\id}. -\item - %% TODO(eernst): We should rename private names to fresh public names. - Replace every type that denotes a type alias + Let \id{} be a non-private type identifier derived from \synt{typeName} + that resolves to a library declaration in the library $L_j$ + in the original program; + \id{} is then transformed to \code{$\metavar{prefix}_j$.\id}. + Let \code{$p$.\id} be derived from \synt{typeName} such that $p$ is + an import prefix in the original program + and \id{} is a non-private identifier, + and consider the case where \code{$p$.\id} resolves to + a library declaration in the library $L_j$ in the original program, + for some $j$; + \code{$p$.\id} is then transformed to \code{$\metavar{prefix}_j$.\id}. +\item + Replace every type in $L_i$ that denotes a type alias + along with its actual type arguments, if any, by its transitive alias expansion (\ref{typedef}). \commentary{% @@ -23817,15 +23837,32 @@ \subsubsection{The Canonical Syntax of Types} \end{enumerate} \commentary{% -Note that this transformation does not change any occurrence of \VOID; -\VOID{} is a reserved word, not an identifier. +This transformation does not change any occurrence of \VOID; +\VOID{} is a reserved word, not a type identifier. Also, \code{$\metavar{prefix}_j$.\VOID} would be a syntax error. +Note that the transformation changes terms derived from \synt{type}, +but it does not change expressions, or any other program element +(except that a \synt{type} can occur in an expression, e.g., \code{[]}). +In particular, it does not change type literals +(that is, expressions denoting types). + The transformation also does not change identifiers denoting type variables, +because they are never resolved to a library declaration, +they are always introduced by a scope which is nested inside the library scope. There is no need to change those identifiers, because -no occurrence of such an identifier resolves to a declaration in a -different library.% -%% TODO(eernst): Sort out the treatment of private identifiers, too. +no occurrence of such an identifier in the type of an expression +denotes a declaration in a different library.% +} + +\rationale{% +The only purpose of this transformation is to obtain a +location-independent designation of all types, +in such a way that each \synt{typeName} resolves to the same declaration +before and after the transformation. +The program behavior may change due to different values returned from +\code{toString()} on reified types, +but the transformation is otherwise semantics preserving.% } \LMHash{}% @@ -23862,15 +23899,21 @@ \subsubsection{The Canonical Syntax of Types} because we would need to re-build all the structures that the syntax offers. For instance, we would need to support the construction of -a semantic type entity for \code{Map}, -based on the semantic type entity for \code{int}, \code{String}, and \code{Map}, +a semantic type entity for \code{Map}, +based on the semantic type entities for +\code{int}, \code{String}, and \code{Map}, and we would need to support deconstruction of those entities -in order to prove things like \SubtypeNE{Never}{\code{Map}}. +in order to prove things like +\SubtypeNE{\code{Map}}{\code{Map}}. This would give rise to a lot of mechanism that will simply duplicate the structure of the syntax. So we prefer to show that the syntax \emph{can} be location independent, and that's sufficient to make syntax usable as our representation of -static semantic types.% +static semantic types. + +We are basically taking the approach that a static semantic type is +an equivalence class of all syntactic elements derived from \synt{type} +that have the same canonical syntax.% } @@ -23961,6 +24004,11 @@ \subsubsection{Standard Upper Bounds and Standard Lower Bounds} Finally, it is assumed that all types are denoted by their canonical syntax (\ref{theCanonicalSyntaxOfTypes}). +\commentary{% +This implies that type aliases have already been fully expanded, +and two types are the same if and only if they have the same syntax.% +} + %% TODO(eernst), for review: Is this the correct associativity of SUB/SLB? \LMHash{}% We define the From b0f1c7f1bbbfaed903e77988bfaed46ac786394a Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 10 Dec 2021 13:51:02 +0100 Subject: [PATCH 43/85] Fixed subsubsection --> subsection, 2* --- specification/dartLangSpec.tex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index d48001082..4e3339cbd 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -23723,7 +23723,7 @@ \subsection{Type Normalization} } -\subsubsection{The Canonical Syntax of Types} +\subsection{The Canonical Syntax of Types} \LMLabel{theCanonicalSyntaxOfTypes} \LMHash{}% @@ -23917,7 +23917,7 @@ \subsubsection{The Canonical Syntax of Types} } -\subsubsection{Standard Upper Bounds and Standard Lower Bounds} +\subsection{Standard Upper Bounds and Standard Lower Bounds} \LMLabel{standardUpperBoundsAndStandardLowerBounds} \LMHash{}% From 791157d71301ecdb5397f6f793c06c38e6030f80 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Wed, 5 Jan 2022 17:39:23 +0100 Subject: [PATCH 44/85] Typos --- specification/dartLangSpec.tex | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 4e3339cbd..b593fc5a7 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -15066,7 +15066,8 @@ \subsubsection{Cascades} Consider an expression of the form \code{$e$?..$s$} where $e$ is a \synt{conditionalExpression} and $s$ is a \synt{cascadeSection}. -A warning occurs unless the static type of $e$ is potentially nullable. +A warning occurs if the static type of $e$ is non-nullable +(\ref{typeNullability}). \LMHash{}% Otherwise, the static analysis and dynamic semantics of @@ -20768,7 +20769,7 @@ \subsection{Assert} \begin{grammar} ::= `;' - ::= \ASSERT{} `(' (`,' )? `,'? `)' + ::= \ASSERT{} `(' (`,' )? `,'? `)' \end{grammar} \LMHash{}% @@ -20874,6 +20875,9 @@ \section{Libraries and Scripts} \LMHash{}% A library contains a string which is derived from \synt{libraryDeclaration}. +It ends in the pseudo-token +\IndexCustom{\synt{EOF}}{@\synt{EOF}}, +which denotes the end of the input. \commentary{% We could say that \synt{libraryDeclaration} is a From 7ff8644bed44909ecf4c6dafbd2a990b199588c1 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Thu, 6 Jan 2022 01:19:45 +0100 Subject: [PATCH 45/85] Whitespace --- specification/dartLangSpec.tex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index b593fc5a7..61f2e19f2 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -1932,7 +1932,7 @@ \subsection{Evaluation of Implicit Variable Getters} \begin{dartCode} bool b = \TRUE; -int i = (() => (b = !b) ? (i = 10) : i + 1 )(); +int i = (() => (b = !b) ? (i = 10) : i + 1)(); \\ \VOID{} main() \{ print(i); // '11'. @@ -17212,7 +17212,7 @@ \subsection{If-null Expressions} \end{grammar} \LMHash{}% -Consider an if-null expression $e$ of the form \code{$e_1$ ?? $e_2$}. +Consider an if-null expression $e$ of the form \code{$e_1$\,\,??\,\,$e_2$}. Let $S$ be the static type of $e_1$. A warning occurs if $S$ is non-nullable. \commentary{In this case, the \lit{??} is superfluous.} @@ -17221,7 +17221,7 @@ \subsection{If-null Expressions} of \NonNullType{$S$} and the static type of $e_2$. \LMHash{}% -Evaluation of an if-null expression $e$ of the form \code{$e_1$ ?? $e_2$} +Evaluation of an if-null expression $e$ of the form \code{$e_1$\,\,??\,\,$e_2$} proceeds as follows: \LMHash{}% From c708d173abb08311f239b154450a2233c67c6c42 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Thu, 6 Jan 2022 18:05:01 +0100 Subject: [PATCH 46/85] Interface type fixes --- specification/dartLangSpec.tex | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 61f2e19f2..834a2ff3f 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -22012,7 +22012,8 @@ \section{Types} \LMLabel{types} \LMHash{}% -Dart supports static typing based on interface types. +Dart supports static typing based on interface types +(\ref{interfaceTypes}). \rationale{% The type system is sound in the sense that @@ -22757,8 +22758,7 @@ \subsection{Subtypes} T \in \{\code{Object?}, \DYNAMIC, \VOID\}}{S}{T} % ------------------------------------------------ Right Object \RuleRaw{\SrnRightObjectFour}{% - \mbox{$S$ is an interface type,}\\ - \mbox{a function type, or \FUNCTION}}{S}{\code{Object}} + \mbox{$S$ is an interface type or \FUNCTION}}{S}{\code{Object}} % ------------------------------------------------ Left Null 2 \Rule{\SrnNullTwo}{\code{Null}}{T}{% \code{Null}}{\code{FutureOr<$T$>}} @@ -23133,8 +23133,7 @@ \subsubsection{Informal Subtype Rule Descriptions} \Item{\SrnBottom}{Bottom} \code{Never} is a subtype of every type. \Item{\SrnRightObjectFour}{Right Object} - Interface types, function types, and \FUNCTION{} - are subtypes of \code{Object}. + Interface types and \FUNCTION{} are subtypes of \code{Object}. \Item{\SrnNullOne}{Null Nullable} \code{Null} is a subtype of every type of the form \code{$T$?}. \Item{\SrnNullTwo}{Null FutureOr} @@ -23323,7 +23322,7 @@ \subsection{Type Nullability} \item \code{Never}. \item Any function type. \item The type \FUNCTION. -\item Any interface type except \code{Null}. +\item Any interface type. \item \code{FutureOr<$S$>}, for any non-nullable type $S$. \item Any type variable $X$ whose bound is non-nullable. \item Any type of the form \code{$X$\,\&\,$S$}, where @@ -24649,8 +24648,6 @@ \subsubsection{The Standard Upper Bound of Distinct Interface Types} \DefEquals{\NominalTypeDepth{Object?}}{0}. \item \DefEquals{\NominalTypeDepth{Object}}{1}. -\item - \DefEquals{\NominalTypeDepth{Null}}{1}. \item Let $T$ be a class or a mixin, and let $M$ be the set of immediate superinterfaces of $T$. @@ -24659,10 +24656,6 @@ \subsubsection{The Standard Upper Bound of Distinct Interface Types} $\metavar{max}\,\{\;\NominalTypeDepth{$S$}\;|\;S\;\in M\;\}$. \end{itemize} -\commentary{% - -} - \LMHash{}% \BlindDefineSymbol{I, J, M}% The algorithm that determines @@ -25051,7 +25044,7 @@ \subsection{Interface Types} (\ref{typedef}). We say that $T$ is an \Index{interface type} if{}f $T'$ is of the form \code{$C$<\List{T}{1}{k}>}, -where $C$ denotes a class different from \code{Never}, +where $C$ denotes a class different from \code{Never} and \code{Null}, or $C$ denotes a mixin. \commentary{% @@ -25059,13 +25052,14 @@ \subsection{Interface Types} Non-generic classes are included because we can have $k = 0$. In particular, the following types are \emph{not} interface types: -\VOID, \DYNAMIC, \FUNCTION, \code{FutureOr<$T$>} for any $T$, \code{Never}, +\VOID, \DYNAMIC, \FUNCTION, \code{FutureOr<$T$>} for any $T$, +\code{Never}, \code{Null}, any function type, any type variable, any intersection type, and any type of the form \code{$T$?}. -Conversely, built-in classes -like \code{Object}, \code{Null}, \code{num}, \code{int}, -\code{String}, and \code{Exception} are interface types, +Conversely, built-in classes like +\code{Object}, \code{num}, \code{int}, \code{String}, and \code{Exception} +are interface types, and so are \code{Future<$T$>}, \code{Stream<$T$>}, \code{Iterable<$T$>}, \code{List<$T$>}, \code{Map<$S$,\,\,$T$}, and \code{Set<$T$>}, From 4c82d007eea20fd3f5558925cde5a370e8a0c6ac Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 7 Jan 2022 11:22:14 +0100 Subject: [PATCH 47/85] Sync with specify_null_safety_new_sections_dec21 --- specification/dartLangSpec.tex | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 834a2ff3f..3fe12d5e6 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -24641,13 +24641,13 @@ \subsubsection{The Standard Upper Bound of Distinct Interface Types} \LMHash{}% We define the auxiliary function \NominalTypeDepthName{} -on interface types and \code{Object?} as follows: +on interface types as follows: \begin{itemize} \item - \DefEquals{\NominalTypeDepth{Object?}}{0}. -\item - \DefEquals{\NominalTypeDepth{Object}}{1}. + % We could make it 1 rather than 0, to "reserve space" for `Object?`, + % but this function is never used with `Object?` anyway. + \DefEquals{\NominalTypeDepth{Object}}{0}. \item Let $T$ be a class or a mixin, and let $M$ be the set of immediate superinterfaces of $T$. @@ -25057,7 +25057,7 @@ \subsection{Interface Types} any function type, any type variable, any intersection type, and any type of the form \code{$T$?}. -Conversely, built-in classes like +Conversely, built-in classes like, e.g., \code{Object}, \code{num}, \code{int}, \code{String}, and \code{Exception} are interface types, and so are From 825de7c1231741732b24834549726b559bd6861c Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 7 Jan 2022 11:51:24 +0100 Subject: [PATCH 48/85] Mark a subtype rule as redundant, in a TODO comment --- specification/dartLangSpec.tex | 2 ++ 1 file changed, 2 insertions(+) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 3fe12d5e6..bc3370903 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -22757,6 +22757,8 @@ \subsection{Subtypes} \RuleRaw{\SrnRightTop}{% T \in \{\code{Object?}, \DYNAMIC, \VOID\}}{S}{T} % ------------------------------------------------ Right Object + % TODO(eernst): Comment out this rule as redundant and + % renumber, it is an instance of \SrnRightFutureOrB. \RuleRaw{\SrnRightObjectFour}{% \mbox{$S$ is an interface type or \FUNCTION}}{S}{\code{Object}} % ------------------------------------------------ Left Null 2 From 8df421c48b25154c0348bd45e7ea88ed732734aa Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Mon, 7 Feb 2022 17:52:40 +0100 Subject: [PATCH 49/85] Adjust no-loops-in-noSuchMethod example --- specification/dartLangSpec.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index bc3370903..a9de47eb7 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -3468,7 +3468,7 @@ \subsubsection{The Method \code{noSuchMethod}} \begin{dartCode} \CLASS{} A \{ - noSuchMethod(\COVARIANT{} Null n) => n; + noSuchMethod(\COVARIANT{} Never n) => n; \} \\ \VOID{} main() \{ From c0f36d84191d0f4393d9081e8be8f7c09f174b5e Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Tue, 8 Feb 2022 10:11:35 +0100 Subject: [PATCH 50/85] Adjust no-loops-in-noSuchMethod example --- specification/dartLangSpec.tex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index a9de47eb7..c03fca2d9 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -3462,8 +3462,8 @@ \subsubsection{The Method \code{noSuchMethod}} rather than a repeated attempt to invoke \code{noSuchMethod} (\ref{bindingActualsToFormals}). Here is an example where a dynamic type error occurs because -an attempt is made to pass an \code{Invocation} -where only the null object is accepted:% +an attempt is made to pass an \code{Invocation}, +and the type of the parameter is \code{Never}:% } \begin{dartCode} From 2e1e710104b6e438acf21d60a5b1c422afc9e451 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Tue, 3 May 2022 11:26:19 +0200 Subject: [PATCH 51/85] Corrected function type subtype rule (bounds); corrected "math" index entries --- specification/dart.sty | 3 ++ specification/dartLangSpec.tex | 58 +++++++++++++++++++++++----------- 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/specification/dart.sty b/specification/dart.sty index d20ba295c..f80225e6e 100644 --- a/specification/dart.sty +++ b/specification/dart.sty @@ -466,6 +466,9 @@ \newcommand{\SubtypeStd}[2]{\Subtype{\Delta}{#1}{#2}} % Subtype judgment where the environment is omitted (NE: "no environment"). \newcommand{\SubtypeNE}[2]{\ensuremath{{#1}\,<:\,{#2}}} +\newcommand{\MutualSubtype}[3]{\ensuremath{{#1}\vdash{#2}\,<:>\,{#3}}} +\newcommand{\MutualSubtypeStd}[2]{\MutualSubtype{\Delta}{#1}{#2}} +\newcommand{\MutualSubtypeNE}[2]{\ensuremath{{#1}\,<:>\,{#2}}} % Judgment expressing that a supertype relation exists. \newcommand{\Supertype}[3]{\ensuremath{{#1}\vdash{#2}\,:>\,{#3}}} diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index c03fca2d9..07fb5e0ed 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -22805,18 +22805,20 @@ \subsection{Subtypes} % ------------------------------------------------ Positional Function Type \RuleRawRaw{\SrnPositionalFunctionType}{% \Delta' = \Delta\uplus\{X_i\mapsto{}B_i\,|\,1 \leq i \leq s\} & + \forall i \in 1..s\!:\;\MutualSubtype{\Delta'}{B_i}{B'\!_i} & \Subtype{\Delta'}{S_0}{T_0} \\ n_1 \leq n_2 & n_1 + k_1 \geq n_2 + k_2 & \forall j \in 1 .. n_2 + k_2\!:\;\Subtype{\Delta'}{T_j}{S_j}}{% \begin{array}{c} \Delta\vdash\RawFunctionTypePositional{S_0}{X}{B}{s}{S}{n_1}{k_1}\;<:\;\\ - \RawFunctionTypePositional{T_0}{X}{B}{s}{T}{n_2}{k_2} + \RawFunctionTypePositional{T_0}{X}{B'\!}{s}{T}{n_2}{k_2} \end{array}} \ExtraVSP\ExtraVSP % ------------------------------------------------ Named Function Type \RuleRawRaw{\SrnNamedFunctionType}{% \Delta' = \Delta\uplus\{X_i\mapsto{}B_i\,|\,1 \leq i \leq s\} & + \forall i \in 1..s\!:\;\MutualSubtype{\Delta'}{B_i}{B'\!_i} & \Subtype{\Delta'}{S_0}{T_0} & \forall j \in 1 .. n\!:\;\Subtype{\Delta'}{T_j}{S_j}\\ \{\,\List{y}{n+1}{n+k_2}\,\} \subseteq \{\,\List{x}{n+1}{n+k_1}\,\}\\ @@ -22826,13 +22828,16 @@ \subsection{Subtypes} (r_{n+q} = \REQUIRED{} \Rightarrow r'_{n+p} = \REQUIRED)}{% \begin{array}{c} \Delta\vdash\RawFunctionTypeNamed{S_0}{X}{B}{s}{S}{n}{x}{k_1}{r}\;<:\;\\ - \RawFunctionTypeNamed{T_0}{X}{B}{s}{T}{n}{y}{k_2}{r'} + \RawFunctionTypeNamed{T_0}{X}{B'\!}{s}{T}{n}{y}{k_2}{r'} \end{array}} % \ExtraVSP % ------------------------------------------------ Covariance + %% TODO(eernst): Type aliases have been expanded so there is no other + %% variance than covariance, but there will be in/out/inout in classes, + %% and then we'll need to handle variance here. \RuleRaw{\SrnCovariance}{% - \mbox{$C$ is an interface type with $s$ type parameters} & + \mbox{$C$ is an interface type with $s$ type parameters.} & \SubtypeStd{S_j}{T_j}\mbox{, for each $j \in 1..s$}}{% \code{$C$<\List{S}{1}{s}>}}{\code{$C$<\List{T}{1}{s}>}} \ExtraVSP @@ -22997,7 +23002,8 @@ \subsubsection{Subtype Rules} In this specification we frequently refer to subtype relationships and assignability without mentioning the environment explicitly, -as in \Index{\SubtypeNE{S}{T}}. +as in +\IndexCustom{\SubtypeNE{S}{T}}{$<:$@\SubtypeNE{S}{T}}. This is only done when a specific location in code is in focus, and it means that the environment is that which is obtained by mapping each type variable in scope at that location @@ -23031,7 +23037,7 @@ \subsubsection{Being a Subtype} A type $S$ is shown to be a \Index{subtype} of another type $T$ in an environment $\Delta$ by providing an instantiation of a rule $R$ whose conclusion is -\IndexCustom{\SubtypeStd{S}{T}}{$\Delta$@\SubtypeStd{S}{T}}, +\IndexCustom{\SubtypeStd{S}{T}}{$\Delta<:$@\SubtypeStd{S}{T}}, along with rule instantiations showing each of the premises of $R$, continuing until a rule with no premises is reached. @@ -23260,9 +23266,17 @@ \subsubsection{Additional Subtyping Concepts} \LMHash{}% $S$ is a \Index{supertype} of $T$ in a given environment $\Delta$, -written \SupertypeStd{S}{T}, +written +\IndexCustom{\SupertypeStd{S}{T}}{$\Delta:>$@\SupertypeStd{S}{T}}, if{}f \SubtypeStd{T}{S}. +\LMHash{}% +$S$ and $T$ are \Index{mutual subtypes} of each other +in a given environment $\Delta$, +written +\IndexCustom{\MutualSubtypeStd{S}{T}}{$\Delta<:>$@\MutualSubtypeStd{S}{T}}, +if{}f both \SubtypeStd{T}{S} and \SubtypeStd{S}{T}. + \LMHash{}% A type $T$ \Index{may be assigned} @@ -23420,7 +23434,8 @@ \subsection{Functions Dealing with Extreme Types} the first applicable case must be used. \LMHash{}% -The \Index{\TopMergeTypeName} of two types computes +The \IndexCustom{\TopMergeTypeName}{topMergeType@\TopMergeTypeName} +of two types computes a canonical type which represents both of them, in the case where they are structurally identical modulo the choice among top types. @@ -23485,7 +23500,8 @@ \subsection{Functions Dealing with Extreme Types} \commentary{The ordering of the arguments makes no difference.} \LMHash{}% -The \Index{\IsTopTypeName} predicate is true for any type which is in +The \IndexCustom{\IsTopTypeName}{isTopType@\IsTopTypeName} +predicate is true for any type which is in the equivalence class of top types. It is a syntactic characterization of top types (\ref{superBoundedTypes}). @@ -23499,8 +23515,9 @@ \subsection{Functions Dealing with Extreme Types} \end{itemize} \noindent -The \Index{\IsObjectTypeName} predicate is true if{}f -the argument is a subtype and a supertype of \code{Object}. +The \IndexCustom{\IsObjectTypeName}{isObjectType@\IsObjectTypeName} +predicate is true if{}f the argument is +a subtype and a supertype of \code{Object}. \begin{itemize} \item \DefEquals{\IsObjectType{Object}}{\metavar{true}}. @@ -23509,8 +23526,8 @@ \subsection{Functions Dealing with Extreme Types} \end{itemize} \noindent -The \Index{\IsBottomTypeName} predicate is true if{}f -the argument is a subtype of \code{Never}. +The \IndexCustom{\IsBottomTypeName}{isBottomType@\IsBottomTypeName} +predicate is true if{}f the argument is a subtype of \code{Never}. \begin{itemize} \item \DefEquals{\IsBottomType{Never}}{\metavar{true}}. @@ -23520,8 +23537,9 @@ \subsection{Functions Dealing with Extreme Types} \end{itemize} \noindent -The \Index{\IsNullTypeName} predicate is true if{}f -the argument is a subtype and a supertype of \code{Null}. +The \IndexCustom{\IsNullTypeName}{isNullType@\IsNullTypeName} +predicate is true if{}f the argument is +a subtype and a supertype of \code{Null}. \begin{itemize} \item \DefEquals{\IsNullType{Null}}{\metavar{true}}. @@ -23530,8 +23548,8 @@ \subsection{Functions Dealing with Extreme Types} \end{itemize} \noindent -The \Index{\IsMoreTopTypeName} predicate defines a total order on -top and \code{Object} types. +The \IndexCustom{\IsMoreTopTypeName}{isMoreTopType@\IsMoreTopTypeName} +predicate defines a total order on top and \code{Object} types. \begin{itemize} \item \DefEquals{\IsMoreTopType{\VOID}{$T$}}{\metavar{true}}. @@ -23548,8 +23566,8 @@ \subsection{Functions Dealing with Extreme Types} \end{itemize} \noindent -The \Index{\IsMoreBottomTypeName} predicate defines an almost total order on -bottom and \code{Null} types. +The \IndexCustom{\IsMoreBottomTypeName}{isMoreBottomType@\IsMoreBottomTypeName} +predicate defines an almost total order on bottom and \code{Null} types. \commentary{% This does not consistently order two different type variables with the same bound.% @@ -23628,7 +23646,9 @@ \subsection{Type Normalization} } \LMHash{}% -The function \Index{\NormalizedTypeOfName} is then defined as follows: +The function +\IndexCustom{\NormalizedTypeOfName}{normalizedType@\NormalizedTypeOfName} +is then defined as follows: \BlindDefineSymbol{T_a, T_u, T_r}% Let $T_a$ be a type (\commentary{where `a' stands for `argument'}). From 3d1e76b33039e342bf5f885d6649d44c652c8e65 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Mon, 9 May 2022 18:16:45 +0200 Subject: [PATCH 52/85] Adjust class building types --- specification/dartLangSpec.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 07fb5e0ed..5e593cdf4 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -25050,7 +25050,7 @@ \subsection{Class Building Types} \item $T$ is a type variable (\ref{generics}). \item $T$ is a function type (\ref{functionTypes}). \item $T$ is a type alias whose transitive alias expansion - (\ref{typedef}) does not denote a class. + (\ref{typedef}) does not denote a class building type. \item $T$ is an enumerated type (\ref{enums}). \item $T$ is a deferred type (\ref{staticTypes}). \end{itemize} From 7ae0fc1b4b03b0e22e63f8a529d3816338bb5f43 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Tue, 26 Jul 2022 17:15:09 +0200 Subject: [PATCH 53/85] Adjust examples declaring non-nullable positional optional parameters with no default --- specification/dartLangSpec.tex | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 5e593cdf4..010738545 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -3447,7 +3447,7 @@ \subsubsection{The Method \code{noSuchMethod}} in order to correctly override \code{noSuchMethod} in \code{Object}. For instance, it can have signature \code{noSuchMethod(Invocation i)} or -\code{noSuchMethod(Object i, [String s = ''])}, +\code{noSuchMethod(Object i, [String? s])}, but not \code{noSuchMethod(Invocation i, String s)}. This implies that the situation where \code{noSuchMethod} is invoked @@ -4218,7 +4218,7 @@ \subsubsection{Generative Constructors} \begin{dartCode} class A \{ - int x; + int? x; A([this.x]); \} \end{dartCode} @@ -4229,8 +4229,8 @@ \subsubsection{Generative Constructors} \begin{dartCode} class A \{ - int x; - A([int x]): this.x = x; + int? x; + A([int? x]): this.x = x; \} \end{dartCode} @@ -15889,7 +15889,7 @@ \subsubsection{Generic Method Instantiation} \} \\ \CLASS{} B \EXTENDS{} /*\,or\,\,\IMPLEMENTS\,*/ A \{ - X fi(X x, [List xs]) => x; + X fi(X x, [List? xs]) => x; \} \\ \VOID{} main() \{ @@ -15901,7 +15901,7 @@ \subsubsection{Generic Method Instantiation} \commentary{% \noindent The function object that \code{f} is bound to at the end of \code{main} -has dynamic type \code{int\,\,\FUNCTION(int,\,[List])}, +has dynamic type \code{int\,\,\FUNCTION(int,\,[List?])}, and it is obtained by implicitly ``passing the actual type argument \code{int}'' to the denoted generic instance method, From d3a16a9bf02ecb55025b36ef63d10980e481a859 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Wed, 2 Nov 2022 12:32:58 +0100 Subject: [PATCH 54/85] Add a TODO to introduce `Any` --- specification/dartLangSpec.tex | 2 ++ 1 file changed, 2 insertions(+) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 010738545..63da1b7d2 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -2991,6 +2991,8 @@ \section{Classes} and neither does any part of a declaration marked \STATIC.% } +%% TODO(eernst): Introduce `Any` and make it a superclass of `Object` +%% and `Null`. \LMHash{}% Every class has a single superclass except class \code{Object} which has no superclass. From e620f6196480a9169332ecaf6e05557268e57b94 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Thu, 3 Nov 2022 14:04:48 +0100 Subject: [PATCH 55/85] Correct example in section about super-bounded types --- specification/dartLangSpec.tex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 63da1b7d2..13c7a0b65 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -7903,9 +7903,10 @@ \subsection{Super-Bounded Types} \begin{dartCode} \CLASS{} A \{ X x; + A(this.x); \} \\ -A a; +A a; \end{dartCode} \commentary{% From 4405e2b16a82afe72546076dfeedb51a5489d8ba Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 2 Dec 2022 14:21:06 +0100 Subject: [PATCH 56/85] Correction based on https://github.com/dart-lang/language/issues/2679 --- specification/dartLangSpec.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 13c7a0b65..91cdb95f7 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -8335,7 +8335,7 @@ \subsubsection{The Instantiation to Bound Algorithm} \TYPEDEF{} Inv = X \FUNCTION(X); \CLASS{} B{}> \{\} \\ -B b; // \comment{The raw B means} B{}>. +\LATE{} B b; // \comment{The raw B means} B{}>. \end{dartCode} \commentary{% From cfc52d43f7ee4e1a2eb797b752f8cf210f362d6b Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 9 Dec 2022 20:03:06 +0100 Subject: [PATCH 57/85] Specify erasure of intersection types for the semantics of await expressions --- specification/dartLangSpec.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 91cdb95f7..2493dadd3 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -27399,7 +27399,7 @@ \section*{Appendix: Algorithmic Subtyping} if $T_0$ is a type variable $X_0$ or a promoted type variables \code{$X_0$\,\&\,$S_0$} and $T_1$ is $X_0$ then \SubtypeNE{T_0}{T_1}. - + \commentary{% Note that this rule is admissible, and can be safely elided if desired.% } From b19dc4cdb0306139aebba63ab89f36ae11145cb0 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 20 Jan 2023 22:47:55 +0100 Subject: [PATCH 58/85] Typo --- specification/dartLangSpec.tex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 2493dadd3..0290c0e3d 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -20271,8 +20271,8 @@ \subsection{Return} \Case{Synchronous non-generator functions and factory constructors} Consider the case where $f$ is a synchronous non-generator function (\ref{functions}) -or a generative constructor -(\ref{generativeConstructors}). +or a factory constructor +(\ref{factories}). % % Returning without an object is only ok for "voidy" return types. It is a \Error{compile-time error} if $s$ is \code{\RETURN;}, From 022916b006625e364b8d175570ab0ccbf92e6c0a Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Thu, 2 Feb 2023 13:49:21 +0100 Subject: [PATCH 59/85] Introduce \DynamicError, marking all dynamic errors just like compile-time errors have been for a while --- specification/dart.sty | 10 ++-- specification/dartLangSpec.tex | 86 +++++++++++++++++++++------------- 2 files changed, 60 insertions(+), 36 deletions(-) diff --git a/specification/dart.sty b/specification/dart.sty index f80225e6e..bd808e758 100644 --- a/specification/dart.sty +++ b/specification/dart.sty @@ -175,7 +175,7 @@ % Used for defining occurrence of phrase, with customized index entry. \newcommand{\IndexCustom}[2]{% - \leavevmode\marginpar{\ensuremath{_{^\vartriangle}}}\emph{#1}\index{#2}} + \leavevmode\marginpar{\ensuremath{\vartriangle}}\emph{#1}\index{#2}} % Used for the defining occurrence of a local symbol. \newcommand{\DefineSymbol}[1]{% @@ -196,11 +196,15 @@ % Same appearance, but not adding an entry to the index. \newcommand{\NoIndex}[1]{% - \leavevmode\marginpar{\ensuremath{_{^\vartriangle}}}\emph{#1}} + \leavevmode\marginpar{\ensuremath{\vartriangle}}\emph{#1}} % Mark a compile-time error in the margin. \newcommand{\Error}[1]{% - \leavevmode\marginpar{\ensuremath{_{^\ominus}}}{#1}} + \leavevmode\marginpar{\ensuremath{\ominus}}{#1}} + +% Mark a dynamic error in the margin. +\newcommand{\DynamicError}[1]{% + \leavevmode\marginpar{\Lightning}{#1}} % Used to specify comma separated lists of similar symbols. \newcommand{\List}[3]{\ensuremath{{#1}_{#2},\,\ldots,\ {#1}_{#3}}} diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 0290c0e3d..ce1edbdba 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -12,6 +12,7 @@ \usepackage[T1]{fontenc} \usepackage{makeidx} \usepackage{enumitem} +\usepackage{marvosym} \makeindex \title{Dart Programming Language Specification\\ {6th edition draft}\\ @@ -102,9 +103,11 @@ % error anyway; change extension names to `typeIdentifier`, avoiding % built-in identifiers). % -% Feb 2023 +% Jan, Feb 2023 % - Change the specification of constant expressions of the form `e1 == e2` % to use primitive equality. +% - Introduce `\DynamicError{}`, thus marking every introduction (definition) +% of a dynamic error by a lightning symbol in the right margin. % % Dec 2022 % - Change the definition of the type function 'flatten' to resolve soundness @@ -1619,7 +1622,7 @@ \subsection{Implicitly Induced Getters and Setters} will bind \id{} to the object that the argument $x$ is bound to. An execution of the setter in a situation where the variable \id{} has been bound to an object -will incur a dynamic error. +will incur a \DynamicError{dynamic error}. \EndCase \LMHash{}% @@ -4212,7 +4215,7 @@ \subsubsection{Generative Constructors} % This can occur due to a failing implicit cast. unless the assigned object has a dynamic type which is not a subtype of the declared type of the instance variable \id, -in which case a dynamic error occurs. +in which case a \DynamicError{dynamic error} occurs. \commentary{% The above rule allows initializing formals to be used as optional parameters:% @@ -10568,7 +10571,7 @@ \subsubsection{Collection Literal Element Evaluation} or the given \code{value} does not have the type \code{Value}, but it cannot occur after the pair has been appended to $s$. \item - Otherwise, a dynamic error occurs. + Otherwise, a \DynamicError{dynamic error} occurs. \commentary{% This occurs when the target is an iterable respectively a map, @@ -10623,7 +10626,8 @@ \subsubsection{Collection Literal Element Evaluation} and if $\ell_2$ is not present then $\EvaluateElement{\ell} := \LiteralSequence{}$. % $o_b$ can have type \DYNAMIC. -If $o_b$ is neither \TRUE{} nor \FALSE{} then a dynamic error occurs. +If $o_b$ is neither \TRUE{} nor \FALSE{} +then a \DynamicError{dynamic error} occurs. \EndCase \LMHash{}% @@ -10854,7 +10858,7 @@ \subsubsection{Lists} An empty list has an empty set of indices. A non-empty list has the index set $\{0, \ldots, n - 1\}$ where $n$ is the size of the list. -It is a dynamic error to attempt to access a list +It is a \DynamicError{dynamic error} to attempt to access a list using an index that is not a member of its set of indices. \rationale{% @@ -10878,7 +10882,8 @@ \subsubsection{Lists} Only run-time list literals can be mutated after they are created. % This error can occur because being constant is a dynamic property. -Attempting to mutate a constant list literal will result in a dynamic error. +Attempting to mutate a constant list literal +will result in a \DynamicError{dynamic error}. \commentary{% % The following is true either directly or indirectly: There is a \CONST{} @@ -11647,7 +11652,7 @@ \subsubsection{Sets} \LMHash{}% A set may contain zero or more objects. Sets have a method which can be used to insert objects; -this will incur a dynamic error if the set is not modifiable. +this will incur a \DynamicError{dynamic error} if the set is not modifiable. Otherwise, when inserting an object $o_{\metavar{new}}$ into a set $s$, if an object $o_{\metavar{old}}$ exists in $s$ such that \code{$o_{\metavar{old}}$ == $o_{\metavar{new}}$} evaluates to \TRUE{} @@ -11687,7 +11692,8 @@ \subsubsection{Sets} and it is evaluated at run time. Only run-time set literals can be mutated after they are created. % This error can occur because being constant is a dynamic property, here. -Attempting to mutate a constant set literal will result in a dynamic error. +Attempting to mutate a constant set literal +will result in a \DynamicError{dynamic error}. \commentary{% % The following is true either directly or indirectly: There is a \CONST{} @@ -11893,7 +11899,8 @@ \subsubsection{Maps} and it is evaluated at run time. Only run-time map literals can be mutated after they are created. % This error can occur because being constant is a dynamic property, here. -Attempting to mutate a constant map literal will result in a dynamic error. +Attempting to mutate a constant map literal +will result in a \DynamicError{dynamic error}. \commentary{% % The following is true either directly or indirectly: There is a \CONST{} @@ -12034,7 +12041,8 @@ \subsection{Throw} } \LMHash{}% -If $v$ is the null object (\ref{null}), then a dynamic error occurs. +If $v$ is the null object (\ref{null}) +then a \DynamicError{dynamic error} occurs. Otherwise let $t$ be a stack trace corresponding to the current execution state, and the \THROW{} statement throws with $v$ as exception object and $t$ as stack trace (\ref{expressionEvaluation}). @@ -12647,7 +12655,7 @@ \subsubsection{New} % This error can occur because being-loaded is a dynamic property. If $T$ is a deferred type with prefix $p$, then if $p$ has not been successfully loaded, -a dynamic error occurs. +a \DynamicError{dynamic error} occurs. \EndCase \LMHash{}% @@ -13576,7 +13584,7 @@ \subsubsection{Binding Actuals to Formals} If $r = 0$ and $s > 0$ then if $f$ does not have default type arguments (\ref{instantiationToBound}) -then a dynamic error occurs. +then a \DynamicError{dynamic error} occurs. Otherwise replace the actual type argument list: Let $r$ be $s$ and let $t_i$ for $i \in 1 .. s$ be the result of instantiation to bound @@ -16645,7 +16653,8 @@ \subsection{Assignment} in which case $v$ has no initializer and is not definitely assigned, or a compile-time error would have occurred% }). - If $v$ has previously been bound to an object then a dynamic error occurs. + If $v$ has previously been bound to an object + then a \DynamicError{dynamic error} occurs. Otherwise, $v$ is bound to $o$, and then $a$ evaluates to $o$ (\ref{expressionEvaluation}). \item @@ -17197,7 +17206,8 @@ \subsection{Conditional} proceeds as follows: Evaluate $e_1$ to an object $o_1$. % This error can occur due to an implicit cast from \DYNAMIC. -It is a dynamic error if the run-time type of $o_1$ is not \code{bool}. +It is a \DynamicError{dynamic error} +if the run-time type of $o_1$ is not \code{bool}. If $o_1$ is the \TRUE{} object, then the value of $c$ is the result of evaluating the expression $e_2$. Otherwise, the value of $c$ is the result of evaluating the expression $e_3$. @@ -17265,11 +17275,13 @@ \subsection{Logical Boolean Expressions} \code{$e_1$\,\,||\,\,$e_2$} causes the evaluation of $e_1$ to an object $o_1$. % This error can occur due to an implicit downcast from \DYNAMIC. -It is a dynamic error if the run-time type of $o_1$ is not \code{bool}. +It is a \DynamicError{dynamic error} +if the run-time type of $o_1$ is not \code{bool}. If $o_1$ is \TRUE, the result of evaluating $b$ is \TRUE, otherwise $e_2$ is evaluated to an object $o_2$. % This error can occur due to an implicit downcast from \DYNAMIC. -It is a dynamic error if the run-time type of $o_2$ is not \code{bool}. +It is a \DynamicError{dynamic error} +if the run-time type of $o_2$ is not \code{bool}. Otherwise the result of evaluating $b$ is $o_2$. \LMHash{}% @@ -17277,11 +17289,13 @@ \subsection{Logical Boolean Expressions} \code{$e_1$\,\,\&\&\,\,$e_2$} causes the evaluation of $e_1$ to an object $o_1$. % This error can occur due to an implicit downcast from \DYNAMIC. -It is a dynamic error if the run-time type of $o_1$ is not \code{bool}. +It is a \DynamicError{dynamic error} +if the run-time type of $o_1$ is not \code{bool}. If $o_1$ is \FALSE, the result of evaluating $b$ is \FALSE, otherwise $e_2$ is evaluated to an object $o_2$. % This error can occur due to an implicit downcast from \DYNAMIC. -It is a dynamic error if the run-time type of $o_2$ is not \code{bool}. +It is a \DynamicError{dynamic error} +if the run-time type of $o_2$ is not \code{bool}. Otherwise the result of evaluating $b$ is $o_2$. @@ -17866,7 +17880,7 @@ \subsection{Postfix Expressions} \LMHash{}% $e$ is evaluated as follows: $e_1$ is evaluated to an object $o$. -If $o$ is the null object then a dynamic error occurs, +If $o$ is the null object then a \DynamicError{dynamic error} occurs, otherwise $e$ evaluates to $o$. \EndCase @@ -19204,7 +19218,7 @@ \subsection{Local Variable Declaration} then $v$ is bound to $o$. If an object $o$ is assigned to $v$ in a situation where $v$ is bound to an object $o'$ -then a dynamic error occurs +then a \DynamicError{dynamic error} occurs (\commentary{it does not matter whether $o$ is the same object as $o'$}). \commentary{% @@ -19365,7 +19379,8 @@ \subsection{If} proceeds as follows: Evaluate the expression $e$ to an object $o$. % This error can occur due to an implicit downcast from \DYNAMIC. -It is a dynamic error if the run-time type of $o$ is not \code{bool}. +It is a \DynamicError{dynamic error} +if the run-time type of $o$ is not \code{bool}. If $o$ is \TRUE, then execute the block statement $S_1$, otherwise execute the block statement $S_2$. @@ -19450,7 +19465,8 @@ \subsubsection{For Loop} \item The expression $[v'/v]c$ is evaluated to an object $o$. % This error can occur due to an implicit downcast from \DYNAMIC. - It is a dynamic error if the run-time type of $o$ is not \code{bool}. + It is a \DynamicError{dynamic error} + if the run-time type of $o$ is not \code{bool}. If $o$ is \FALSE, the for loop completes normally. Otherwise, execution continues at step \ref{beginIteration}. \item @@ -19663,7 +19679,8 @@ \subsection{While} \LMHash{}% The expression $e$ is evaluated to an object $o$. % This error can occur due to an implicit downcast from \DYNAMIC. -It is a dynamic error if the run-time type of $o$ is not \code{bool}. +It is a \DynamicError{dynamic error} +if the run-time type of $o$ is not \code{bool}. \LMHash{}% If $o$ is \FALSE, then execution of the while statement completes normally @@ -19714,7 +19731,8 @@ \subsection{Do} \LMHash{}% Then, the expression $e$ is evaluated to an object $o$. % This error can occur due to an implicit downcast from \DYNAMIC. -It is a dynamic error if the run-time type of $o$ is not \code{bool}. +It is a \DynamicError{dynamic error} +if the run-time type of $o$ is not \code{bool}. If $o$ is \FALSE, execution of the do statement completes normally (\ref{statementCompletion}). If $o$ is \TRUE, then the do statement is re-executed. @@ -20405,7 +20423,8 @@ \subsection{Return} \LMHash{}% The expression $e$ is evaluated to an object $o$. -A dynamic error occurs unless the dynamic type of $o$ is a subtype of +A \DynamicError{dynamic error} occurs +unless the dynamic type of $o$ is a subtype of the actual return type of $f$ (\ref{actualTypes}). Then the return statement $s$ completes returning $o$ @@ -20432,7 +20451,8 @@ \subsection{Return} let \code{v} be a fresh variable bound to $o$ and evaluate \code{\AWAIT{} v} to an object $r$; otherwise let $r$ be $o$. -A dynamic error occurs unless the dynamic type of $r$ +A \DynamicError{dynamic error} occurs +unless the dynamic type of $r$ is a subtype of the actual value of $T_v$ (\ref{actualTypes}). Then the return statement $s$ completes returning $r$ @@ -21329,21 +21349,21 @@ \subsubsection{Semantics of Imports} \NamespaceName{\metavar{import},i}, a corresponding getter named \id{} with the same function header as $g$. % This error can occur because being-loaded is a dynamic property. - Calling the getter results in a dynamic error. + Calling the getter results in a \DynamicError{dynamic error}. \item For every top level setter $s$ named \code{\id=} in \NamespaceName{\metavar{import},i}, a corresponding setter named \code{\id=} with the same function header as $s$. % This error can occur because being-loaded is a dynamic property. - Calling the setter results in a dynamic error that occurs before - the actual argument is evaluated. + Calling the setter results in a \DynamicError{dynamic error} + that occurs before the actual argument is evaluated. \item For every class, mixin, enum, and type alias declaration named \id{} in \NamespaceName{\metavar{import},i}, a corresponding getter named \id{} with return type \code{Type}. % This error can occur because being-loaded is a dynamic property. - Calling the getter results in a dynamic error. + Calling the getter results in a \DynamicError{dynamic error}. \end{itemize} \rationale{% @@ -21877,7 +21897,7 @@ \subsection{Scripts} that spawned $i$% }), or the null object if no such object was supplied. - A dynamic error occurs if + A \DynamicError{dynamic error} occurs if the run-time type of this object is not a subtype of the declared type of the corresponding parameter of \code{main}. \end{itemize} @@ -21900,7 +21920,7 @@ \subsection{Scripts} (\commentary{% the above rules ensure that the corresponding parameters are optional% }). -But the implementation must ensure that a dynamic error occurs +But the implementation must ensure that a \DynamicError{dynamic error} occurs if an actual argument does not have a run-time type which is a subtype of the declared type of the parameter. From f21de18f1364789097545ff3ca6d76be71f71b92 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Thu, 2 Feb 2023 14:19:15 +0100 Subject: [PATCH 60/85] Changed the error margin markers to use red text --- specification/dart.sty | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specification/dart.sty b/specification/dart.sty index bd808e758..bf3004759 100644 --- a/specification/dart.sty +++ b/specification/dart.sty @@ -200,11 +200,11 @@ % Mark a compile-time error in the margin. \newcommand{\Error}[1]{% - \leavevmode\marginpar{\ensuremath{\ominus}}{#1}} + \leavevmode\marginpar{\ensuremath{\textcolor{red}{\ominus}}}{#1}} % Mark a dynamic error in the margin. \newcommand{\DynamicError}[1]{% - \leavevmode\marginpar{\Lightning}{#1}} + \leavevmode\marginpar{\textcolor{red}{\Lightning}}{#1}} % Used to specify comma separated lists of similar symbols. \newcommand{\List}[3]{\ensuremath{{#1}_{#2},\,\ldots,\ {#1}_{#3}}} From db0a8aca83270e2d6616582da3dfabfc717f3563 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Wed, 31 May 2023 12:20:37 +0200 Subject: [PATCH 61/85] Correct dynamic type of tear-off with covariant parameter --- specification/dartLangSpec.tex | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index ce1edbdba..ab3a8c7b2 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -15663,7 +15663,7 @@ \subsubsection{Instance Method Closurization} \LMHash{}% For each parameter $p_j$, $j \in 1 .. n+k$, if $p_j$ is covariant (\ref{covariantParameters}) -then $T_j$ is the built-in class \code{Object}. +then $T_j$ is \DYNAMIC. %% No user code will see this, avoid downcast in body. \commentary{% This is concerned with the dynamic type of the function object obtained by @@ -15676,7 +15676,7 @@ \subsubsection{Instance Method Closurization} } \LMHash{}% -If $T$ is a non-generic class then for $j \in 1 .. n+k$, +Otherwise, if $T$ is a non-generic class then for $j \in 1 .. n+k$, $T_j$ is a type annotation that denotes the same type (\ref{typeType}) as that which is denoted by the type annotation on @@ -15819,7 +15819,7 @@ \subsubsection{Super Closurization} \LMHash{}% For each parameter $p_j$, $j \in 1 .. n+k$, if $p_j$ is covariant (\ref{covariantParameters}) -then $T_j$ is the built-in class \code{Object}. +then $T_j$ is \DYNAMIC. \commentary{% This is concerned with the dynamic type of the function object obtained by @@ -15832,7 +15832,7 @@ \subsubsection{Super Closurization} } \LMHash{}% -If $S$ is a non-generic class then for $j \in 1 .. n+k$, +Otherwise, if $S$ is a non-generic class then for $j \in 1 .. n+k$, $T_j$ is a type annotation that denotes the same type (\ref{typeType}) as that which is denoted by the type annotation on From 1508e4ecc5131d3d58b4a0bf6944d1bc8865f702 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Wed, 31 May 2023 12:38:48 +0200 Subject: [PATCH 62/85] Clarified tear-offs of methods with covariant parameters --- specification/dartLangSpec.tex | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index ab3a8c7b2..5f4f06ea4 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -15663,7 +15663,11 @@ \subsubsection{Instance Method Closurization} \LMHash{}% For each parameter $p_j$, $j \in 1 .. n+k$, if $p_j$ is covariant (\ref{covariantParameters}) -then $T_j$ is \DYNAMIC. %% No user code will see this, avoid downcast in body. +then $T_j$ is \code{Object?}. +The corresponding actual argument in the body is replaced by +\code{$p_j$\,\,\AS\,\,$T'_j$} +where $T'_j$ is the type which would be $T_j$ if $p_j$ had not been covariant +(\commentary{that is, it is computed as specified below}). \commentary{% This is concerned with the dynamic type of the function object obtained by @@ -15676,12 +15680,15 @@ \subsubsection{Instance Method Closurization} } \LMHash{}% -Otherwise, if $T$ is a non-generic class then for $j \in 1 .. n+k$, +Otherwise +(\commentary{when $p_j$ is not covariant}), +if $T$ is a non-generic class then for $j \in 1 .. n+k$, $T_j$ is a type annotation that denotes the same type (\ref{typeType}) as that which is denoted by the type annotation on the corresponding parameter declaration in $D$. -If that parameter declaration has no type annotation then $T_j$ is \DYNAMIC. +If that parameter declaration has no type annotation +then $T_j$ is \code{Object?}. \LMHash{}% Otherwise $T$ is a generic instantiation of a generic class $G$. @@ -15690,7 +15697,8 @@ \subsubsection{Instance Method Closurization} Then $T_j$ is a type annotation that denotes $[t''_1/X''_1, \ldots, t''_{s''}/X''_{s''}]S_j$, where $S_j$ is the type annotation of the corresponding parameter in $D$. -If that parameter declaration has no type annotation then $T_j$ is \DYNAMIC. +If that parameter declaration has no type annotation +then $T_j$ is \code{Object?}. \LMHash{}% There is one way in which @@ -15819,7 +15827,11 @@ \subsubsection{Super Closurization} \LMHash{}% For each parameter $p_j$, $j \in 1 .. n+k$, if $p_j$ is covariant (\ref{covariantParameters}) -then $T_j$ is \DYNAMIC. +then $T_j$ is \code{Object?}. +The corresponding actual argument in the body is replaced by +\code{$p_j$\,\,\AS\,\,$T'_j$} +where $T'_j$ is the type which would be $T_j$ if $p_j$ had not been covariant +(\commentary{that is, it is computed as specified below}). \commentary{% This is concerned with the dynamic type of the function object obtained by @@ -15832,12 +15844,15 @@ \subsubsection{Super Closurization} } \LMHash{}% -Otherwise, if $S$ is a non-generic class then for $j \in 1 .. n+k$, +Otherwise +(\commentary{when $p_j$ is not covariant}), +if $S$ is a non-generic class then for $j \in 1 .. n+k$, $T_j$ is a type annotation that denotes the same type (\ref{typeType}) as that which is denoted by the type annotation on the corresponding parameter declaration in $D$. -If that parameter declaration has no type annotation then $T_j$ is \DYNAMIC. +If that parameter declaration has no type annotation +then $T_j$ is \code{Object?}. \LMHash{}% Otherwise $S$ is a generic instantiation of a generic class $G$. @@ -15846,7 +15861,8 @@ \subsubsection{Super Closurization} Then $T_j$ is a type annotation that denotes $[t''_1/X''_1, \ldots, t''_{s''}/X''_{s''}]S_j$, where $S_j$ is the type annotation of the corresponding parameter in $D$. -If that parameter declaration has no type annotation then $T_j$ is \DYNAMIC. +If that parameter declaration has no type annotation +then $T_j$ is \code{Object?}. \LMHash{}% There is one way in which From 4bf89e6e5d7f910f9a86a38909f76e626f25aa96 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Wed, 31 May 2023 16:28:23 +0200 Subject: [PATCH 63/85] Correct a typo about closurization --- specification/dartLangSpec.tex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 5f4f06ea4..327883080 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -15693,7 +15693,7 @@ \subsubsection{Instance Method Closurization} \LMHash{}% Otherwise $T$ is a generic instantiation of a generic class $G$. Let $X''_1, \ldots, X''_{s''}$ be the formal type parameters of $G$, -and $t''_1, \ldots, t''_{s''}$ be the actual type arguments of $o$ at $T$. +and $t''_1, \ldots, t''_{s''}$ be the actual type arguments of $o$ at $G$. Then $T_j$ is a type annotation that denotes $[t''_1/X''_1, \ldots, t''_{s''}/X''_{s''}]S_j$, where $S_j$ is the type annotation of the corresponding parameter in $D$. @@ -15857,7 +15857,7 @@ \subsubsection{Super Closurization} \LMHash{}% Otherwise $S$ is a generic instantiation of a generic class $G$. Let $X''_1, \ldots, X''_{s''}$ be the formal type parameters of $G$, -and $t''_1, \ldots, t''_{s''}$ be the actual type arguments of $o$ at $S$. +and $t''_1, \ldots, t''_{s''}$ be the actual type arguments of $o$ at $G$. Then $T_j$ is a type annotation that denotes $[t''_1/X''_1, \ldots, t''_{s''}/X''_{s''}]S_j$, where $S_j$ is the type annotation of the corresponding parameter in $D$. From ea17e1776f069f48fa497795231060b2fbf978dc Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 16 Jun 2023 12:19:44 +0200 Subject: [PATCH 64/85] Correction: Add definition of normalizedType/topMergeType for member signatures --- specification/dartLangSpec.tex | 45 +++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 327883080..eedcca7dd 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -11692,7 +11692,7 @@ \subsubsection{Sets} and it is evaluated at run time. Only run-time set literals can be mutated after they are created. % This error can occur because being constant is a dynamic property, here. -Attempting to mutate a constant set literal +Attempting to mutate a constant set literal will result in a \DynamicError{dynamic error}. \commentary{% @@ -23538,6 +23538,34 @@ \subsection{Functions Dealing with Extreme Types} and then recursively taking \TopMergeTypeName{} of the rest. \commentary{The ordering of the arguments makes no difference.} +\LMHash{}% +We generalize \TopMergeTypeName{} such that +it can be applied to two or more member signatures rather than types. +The case with more than two member signatures is defined in the same way as +the case with more than two types. +With member signatures $m_1$ and $m_2$ +that are structurally identical modulo the choice of top types +and the occurrences of the modifier \COVARIANT, +\TopMergeType{$m_1$}{$m_2$} is a member signature $m$ such +that every pair of types $T_1$ and $T_2$ that occur in $m_1$ respectively $m_2$ +yields \TopMergeType{$T_1$}{$T_2$} in the result, +the modifier \COVARIANT{} occurs on every parameter in $m$ +where the corresponding parameter in $m_1$ or in $m_2$ has that modifier, +and all other non-type parts of $m_1$ and $m_2$ occur identically in the result. + +\commentary{% +For example, + +\noindent +\TopMergeType{\VOID\,\,m(List<\DYNAMIC>)} +{\DYNAMIC\,\,m(\COVARIANT\,\,List)} + +\noindent +is the member signature +\code{Object?\,\,m(\COVARIANT\,\,List)}.% +} + + \LMHash{}% The \IndexCustom{\IsTopTypeName}{isTopType@\IsTopTypeName} predicate is true for any type which is in @@ -23786,6 +23814,21 @@ \subsection{Type Normalization} \end{displaymath} } +\LMHash{}% +We generalize \NormalizedTypeOfName{} such that +it can be applied to a member signature rather than a type. +This maps a member signature $m$ to a member signature $m'$ such +that every type $T$ that occurs in $m$ is replaced by +\NormalizedTypeOf{$T$}, and all other parts of $m'$ are identical to +the corresponding part of $m$. + +\commentary{% +For example, +\NormalizedTypeOf{\VOID\,\,m(\COVARIANT\,\,FutureOr)} +is the member signature +\code{\VOID\,\,m(\COVARIANT\,\,Object)}.% +} + \subsection{The Canonical Syntax of Types} \LMLabel{theCanonicalSyntaxOfTypes} From 00391ca1cf20e5336e33dfdf845b7c7a98b9c59e Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Thu, 22 Jun 2023 16:25:49 +0200 Subject: [PATCH 65/85] Correction: Update dynamic type of covariant parameter to Object? --- specification/dartLangSpec.tex | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index eedcca7dd..ed9c677eb 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -5920,7 +5920,7 @@ \subsubsection{Correct Member Overrides} \item If $m$ and $m'$ are both methods or both setters: Let $F$ be the function type of $m$ - except that the parameter type is the built-in class \code{Object} + except that the parameter type is \code{Object?} for each parameter of $m$ which is covariant-by-declaration (\ref{covariantParameters}). Let $F'$ be the function type of $m'$. @@ -7913,7 +7913,7 @@ \subsection{Super-Bounded Types} \end{dartCode} \commentary{% -With this, \code{a.x} has static type \code{Object}, +With this, \code{a.x} has static type \code{Object?}, even though the upper bound on the type variable \code{X} is \code{num}.% } @@ -7940,7 +7940,7 @@ \subsection{Super-Bounded Types} (noting that all types must be regular-bounded when we do not have the notion of super-bounded types). So if we wish to allow a variable to hold any instance ``of type \code{C}'' -then that variable must use \code{Object} or another top type +then that variable must use \code{Object?} or another top type as its type annotation, which means that a member like \code{next} is not known to exist (which is what we mean by saying that the type is `less informative').% @@ -15152,7 +15152,7 @@ \subsubsection{Superinvocations} It is a \Error{compile-time error} if a method superinvocation occurs in a top-level function or variable initializer, in an instance variable initializer or initializer list, -in class \code{Object}, +in the built-in class \code{Object}, in a factory constructor, or in a static method or variable initializer. From 3cee2c7966b602776f3cff357566902dfbdc5b5d Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Thu, 29 Jun 2023 15:55:45 +0200 Subject: [PATCH 66/85] Typo --- specification/dartLangSpec.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index ed9c677eb..19961ceda 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -15335,7 +15335,7 @@ \subsection{Property Extraction} \LMHash{}% A \Error{compile-time error} occurs unless $C$ denotes a class, a mixin, or an extension that declares -a static member named $m$, +a static member named \id, which we will call the \IndexCustom{denoted member}{static property extraction!denoted member} of $i$. From 41c49401fbd078d7aa95e17982d64daa5ff2ab1d Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Wed, 5 Jul 2023 15:52:25 +0200 Subject: [PATCH 67/85] Add commentary to emphasize that `new`/`const` can be implicit, and `new/const C()` is still an error when `C` is super-bounded --- specification/dartLangSpec.tex | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 19961ceda..540ca2874 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -7872,7 +7872,9 @@ \subsection{Super-Bounded Types} \commentary{% This includes the case where the actual type arguments have been provided by type inference, which is assumed to have taken place already - (\ref{typeInference}).% + (\ref{typeInference}). + It also includes the case where \NEW{} or \CONST{} is added implicitly + (\ref{unqualifiedInvocation}, \ref{functionExpressionInvocation}).% } \item $T$ occurs in the \synt{constructorDesignation} of a redirecting factory constructor header From e9c2ef6b8c1ce3c4c63ecfb8a79177dffaf67ac6 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Thu, 6 Jul 2023 11:21:28 +0200 Subject: [PATCH 68/85] Add missing updates in section typeVoid --- specification/dartLangSpec.tex | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 540ca2874..2999427f2 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -25791,7 +25791,7 @@ \subsection{Type FutureOr} \end{itemize} -\subsection{Type Void} +\subsection{Type \VOID} \LMLabel{typeVoid} \LMHash{}% @@ -25821,22 +25821,10 @@ \subsection{Type Void} \commentary{% The type \VOID{} is a top type (\ref{superBoundedTypes}), -so \VOID{} and \code{Object} are subtypes of each other +so \VOID{} and \code{Object?} are subtypes of each other (\ref{subtypes}), which also implies that any object can be -the value of an expression of type \VOID. -% -Consequently, any instance of type \code{Type} which reifies the type \VOID{} -must compare equal (according to the \lit{==} operator \ref{equality}) -to any instance of \code{Type} which reifies the type \code{Object} -(\ref{dynamicTypeSystem}). -It is not guaranteed that \code{identical(\VOID, Object)} evaluates to -the \TRUE{} object. -In fact, it is not recommended that implementations strive to achieve this, -because it may be more important to ensure that diagnostic messages -(including stack traces and dynamic error messages) -preserve enough information to use the word `void' when referring to types -which are specified as such in source code.% +the value of an expression of type \VOID.% } \LMHash{}% @@ -25974,7 +25962,7 @@ \subsection{Type Void} } \begin{dartCode} -\FOR{} (Object x in <\VOID>[]) \{\} // \comment{Error.} +\FOR{} (Object? x in <\VOID>[]) \{\} // \comment{Error.} \AWAIT{} \FOR{} (int x \IN{} new Stream<\VOID{}>.empty()) \{\} // \comment{Error.} \FOR{} (\VOID{} x \IN{} <\VOID{}>[]) \{\ldots\} // \comment{OK.} \FOR (\VAR{} x \IN{} <\VOID{}>[]) \{\ldots\} // \comment{OK, type of x inferred.} From f7dbe69696a39d0e83f190d846817a5ab1825b7f Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Thu, 13 Jul 2023 13:44:31 +0200 Subject: [PATCH 69/85] Add missing error: default values must be type correct --- specification/dartLangSpec.tex | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 2999427f2..ebef44f33 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -2496,17 +2496,9 @@ \subsubsection{Optional and Named Formals} ::= \gnewline{} \REQUIRED? - \gnewline{} ((`=' | `:') )? + \gnewline{} (`:' )? \end{grammar} -\LMHash{}% -The form \syntax{ `:' } -is equivalent to the form -\syntax{ `=' }. -The colon-syntax is included only for backwards compatibility. -It is deprecated and will be removed in -a later version of the language specification. - \LMHash{}% It is a \Error{compile-time error} if an optional or named formal parameter has the modifier \CONST{} or the modifier \LATE. @@ -2521,6 +2513,11 @@ \subsubsection{Optional and Named Formals} It is a \Error{compile-time error} if a required named parameter has a default value. +\LMHash{}% +Assume that $p$ is an optional named parameter with default value $d$. +It is a \Error{compile-time error} +if the static type of $d$ is not assignable to the declared type of $p$. + \LMHash{}% It is a \Error{compile-time error} if the name of a named optional parameter begins with an `_' character. From 028ab0e0ffe8d17a4fd42aba156862f4c18b3284 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Thu, 13 Jul 2023 14:15:36 +0200 Subject: [PATCH 70/85] Add a paragraph explaining that the given section is about both optional and named parameters, not just parameters that are both --- specification/dartLangSpec.tex | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index ebef44f33..cb098c139 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -2279,7 +2279,8 @@ \subsection{Formal Parameters} \Index{formal parameter list}, which consists of a list of required positional parameters (\ref{requiredFormals}), -followed by any optional and/or named parameters (\ref{optionalFormals}). +followed by any optional and/or named parameters +(\ref{optionalFormalsAndNamedFormals}). The latter may be specified either as a list of optional positional parameters, or as a set of named parameters, @@ -2483,8 +2484,14 @@ \subsubsection{Required Formals} an instance method, instance setter, or instance operator. -\subsubsection{Optional and Named Formals} -\LMLabel{optionalFormals} +\subsubsection{Optional Formals and Named Formals} +\LMLabel{optionalFormalsAndNamedFormals} + +\LMHash{}% +This section is concerned with optional parameters, +positional or named, +and with required named parameters. +They are handled together because they share grammar rules. \LMHash{}% Optional formal parameters may be specified and provided with default values. @@ -2496,7 +2503,7 @@ \subsubsection{Optional and Named Formals} ::= \gnewline{} \REQUIRED? - \gnewline{} (`:' )? + \gnewline{} (`=' )? \end{grammar} \LMHash{}% @@ -2514,7 +2521,7 @@ \subsubsection{Optional and Named Formals} \Error{compile-time error} if a required named parameter has a default value. \LMHash{}% -Assume that $p$ is an optional named parameter with default value $d$. +Assume that $p$ is an optional parameter with default value $d$. It is a \Error{compile-time error} if the static type of $d$ is not assignable to the declared type of $p$. From 248780c668ab4fa13a7493f6f31d01c416586347 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 21 Jul 2023 17:17:53 +0200 Subject: [PATCH 71/85] Post-rebase error correction --- specification/dartLangSpec.tex | 75 ++++++++++++++-------------------- 1 file changed, 30 insertions(+), 45 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index cb098c139..def813130 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -26,13 +26,7 @@ % CHANGES % ======= % -% Significant changes to the specification. Note that the versions specified -% below indicate the current tool chain version when those changes were made. -% In practice, new features have always been integrated into the language -% specification (this document) a while after the change was accepted into -% the language and implemented. As of September 2022, the upcoming version of -% the language which is being specified is indicated by a version number in -% parentheses after the tool chain version. +% Significant changes to the specification. % % Note that the version numbers used below (up to 2.15) were associated with % the currently released language and tools at the time of the spec change, @@ -820,7 +814,7 @@ \section{Notation} Type inference of $e_j$ and the context type used for inference of $e_j$ are not relevant. It is generally assumed that type inference has occurred already -(\ref{overview}).% +(\ref{typeInference}).% } \LMHash{}% @@ -1436,13 +1430,6 @@ \section{Variables} That is, any kind of variable which is not a local variable.% } -\LMHash{}% -A \IndexCustom{non-local variable}{variable!non-local} -is a library variable, a class variable, or an instance variable. -\commentary{% -That is, any kind of variable which is not a local variable.% -} - \LMHash{}% A \IndexCustom{constant variable}{variable!constant} is a variable whose declaration includes the modifier \CONST. @@ -1480,15 +1467,12 @@ \section{Variables} \subsection{Implicitly Induced Getters and Setters} \LMLabel{implicitlyInducedGettersAndSetters} -%% TODO(eernst): When inference is specified, we should be able to conclude -%% that the cases with no declared type do not exist after type inference -%% (for instance `var x;` or `var x = e;`), and then we can replace all rules -%% about such cases by commentary saying that they may exist in the input, -%% but they are gone after type inference. -%% -%% At this time we rely on the assumption that type inference has already -%% occurred, which means that we can refer to the declared type of a variable -%% without mentioning type inference. +%% TODO(eernst): We treat type inference as a step that has already +%% taken place. We consider the types chosen by type inference to +%% be available as a kind of semantic attributes of the syntax. +%% That is, we can refer to the inferred type and say that the +%% given declaration has the inferred type. In this way we avoid +%% talking about an actual syntactic transformation. \LMHash{}% The following rules on implicitly induced getters and setters @@ -1534,7 +1518,8 @@ \subsection{Implicitly Induced Getters and Setters} \noindent implicitly induces a getter with the header that -contains \STATIC{} if{}f the declaration contains \STATIC{} and is followed by +contains \STATIC{} if{}f the declaration contains \STATIC, +and is followed by \code{$T$\,\,\GET\,\,\id}, where $T$ is obtained from type inference in the case where $e$ exists, @@ -1577,7 +1562,7 @@ \subsection{Implicitly Induced Getters and Setters} \code{\VOID\,\,\SET\,\,\id($T$\,\,$x$)}, whose execution sets the value of \id{} to the incoming argument $x$. The type $T$ is obtained from type inference -(\ref{overview}). +(\ref{typeInference}). \EndCase \LMHash{}% @@ -1590,10 +1575,10 @@ \subsection{Implicitly Induced Getters and Setters} \commentary{% Type inference has not yet been specified in this document -(\ref{overview}). -Note that type inference could change, e.g., -\code{\VAR\,\,x;} to \code{$T$\,\,x;}, -which would take us to an earlier case.% +(\ref{typeInference}). +Note that type inference could provide, e.g., +\code{\VAR\,\,x;} with an inferred type $T$, +which is then the declared type of the variable.% } \EndCase @@ -1602,7 +1587,7 @@ \subsection{Implicitly Induced Getters and Setters} A variable declaration of the form \code{\STATIC?\,\,\LATE\,\,\FINAL\,\,$T$\,\,\id;} implicitly induces a setter (\ref{setters}) with the header -\code{\VOID\,\,\SET\,\,\id(\DYNAMIC\,\,$x$)}. +\code{\VOID\,\,\SET\,\,\id($T$\,\,$x$)}. If this setter is executed in a situation where the variable \id{} has not been bound, it will bind \id{} to the object that $x$ is bound to. @@ -2000,9 +1985,6 @@ \subsection{Evaluation of Implicit Variable Getters} % Reduce whitespace after itemized list: This is just an end symbol. \vspace{-\baselineskip}\EndCase -% Reduce whitespace after itemized list: This is just an end symbol. -\vspace{-\baselineskip}\EndCase - \section{Functions} \LMLabel{functions} @@ -5188,12 +5170,11 @@ \subsubsection{Inheritance and Overriding} \item There is only one namespace for getters, setters, methods and constructors (\ref{scoping}). - A non-local variable $f$ introduces a getter $f$, - and a non-local variable $f$ - also introduces a setter + A non-local variable $f$ introduces a getter $f$. + A non-local variable $f$ also introduces a setter \code{$f$=} if it is not final and not constant, or it is late and final and has no initializing expression - \code{$f$=} (\ref{instanceVariables}, \ref{variables}). + (\ref{instanceVariables}, \ref{variables}). When we speak of members here, we mean accessible instance, static, or library variables, getters, setters, and methods @@ -8760,7 +8741,7 @@ \subsection{Constants} that is not qualified by a deferred prefix, is a potentially constant and constant expression. \commentary{% - For example, if class $C$ declares a constant class variable $v$, + For example, if class $C$ declares a constant class variable $v$, !!!TODO!!! \code{$C$.$v$} is a constant. The same is true if $C$ is accessed via a prefix $p$; \code{$p$.$C$.$v$} is a constant unless $p$ is a deferred prefix.% @@ -12480,7 +12461,7 @@ \subsection{This} \LMHash{}% The static type of \THIS{} is the interface of the -immediately enclosing class, enum, or mixin, if any. +immediately enclosing class, mixin, or enum, if any. The static type of \THIS{} is the \ON{} type of the enclosing extension, if any (\ref{extensions}). @@ -16548,7 +16529,7 @@ \subsection{Assignment} \LMHash{}% An assignment changes the value associated with a variable, -or invokes a setter. +or it invokes a setter. \begin{grammar} ::= `=' @@ -19023,7 +19004,8 @@ \subsection{Local Variable Declaration} \LMHash{}% The properties of being -\IndexCustom{initialized}{variable!initialized} or +\IndexCustom{initialized}{variable!initialized}, +\IndexCustom{final}{variable!final}, or \IndexCustom{constant}{variable!constant} apply to local variables with the same definitions as for other variables (\ref{variables}). @@ -19115,7 +19097,7 @@ \subsection{Local Variable Declaration} In every situation which is not covered by the previous paragraph, it is a compile-time error to assign to a local variable -which is \FINAL{} and not \LATE{} +which is final and not late (\ref{assignment}).% } @@ -22294,10 +22276,13 @@ \subsection{Dynamic Type System} } \LMHash{}% -An expression is a \emph{type literal} if it is an identifier, +An expression is a \Index{type literal} if it is an identifier, or a qualified identifier, which denotes a class, mixin, enum, or type alias declaration, or it is -an identifier denoting a type parameter of a generic class or function. +an identifier denoting a type parameter of a generic class or function, +or it is an identifier or qualified identifier which is a type literal +and which is followed by a list of actual type arguments +derived from \synt{typeArguments}. It is a \emph{constant type literal} if it does not denote a type parameter, and it is not qualified by a deferred prefix. \commentary{% From 4648a3c86756ef09d91bdab7c9fd417ce881529f Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 21 Jul 2023 17:32:56 +0200 Subject: [PATCH 72/85] Correct usages of "final variable" and "constant variable" --- specification/dartLangSpec.tex | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index def813130..27c23e94d 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -1463,7 +1463,6 @@ \section{Variables} is a variable whose declaration includes the modifier \FINAL. - \subsection{Implicitly Induced Getters and Setters} \LMLabel{implicitlyInducedGettersAndSetters} @@ -2373,8 +2372,8 @@ \subsection{Formal Parameters} the formal parameter scope of $f$. \LMHash{}% -It is a \Error{compile-time error} if a formal parameter -is declared as a constant variable (\ref{variables}). +It is a \Error{compile-time error} if a formal parameter declaration +has the modifier \CONST. % We put the following error here because it's shared among all kinds of % functions except one. It is a \Error{compile-time error} if an optional formal parameter @@ -8741,7 +8740,7 @@ \subsection{Constants} that is not qualified by a deferred prefix, is a potentially constant and constant expression. \commentary{% - For example, if class $C$ declares a constant class variable $v$, !!!TODO!!! + For example, if class $C$ declares a static constant variable $v$, \code{$C$.$v$} is a constant. The same is true if $C$ is accessed via a prefix $p$; \code{$p$.$C$.$v$} is a constant unless $p$ is a deferred prefix.% @@ -19215,7 +19214,7 @@ \subsection{Local Variable Declaration} Otherwise, the variable $v$ is bound to $o$. \LMHash{}% -Let $D$ be a \LATE{} and \FINAL{} local variable declaration +Let $D$ be a \LATE{} and final local variable declaration that declares a variable $v$. If an object $o$ is assigned to $v$ in a situation where $v$ is unbound @@ -19541,7 +19540,7 @@ \subsubsection{For-in} It follows that it is a compile-time error % The following error exists also in the case where \id{} is definitely % unassigned before the loop: The loop could run >1 time. -if $D$ is empty and \id{} is a final variable. +if $D$ is empty and \id{} is a final or constant variable. Also, it is a dynamic error if $e$ has type \DYNAMIC, but $e$ evaluates to an instance of a type which is not a subtype of \code{Iterable}.% From e5950a10951a5e9182aa5ec7c4e1e8df91d013d6 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Mon, 11 Sep 2023 15:38:59 +0200 Subject: [PATCH 73/85] Add missing declaration kind about getter/setter type error --- specification/dartLangSpec.tex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 27c23e94d..0e6147a0b 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -3876,7 +3876,8 @@ \subsection{Setters} \LMHash{}% It is a \Error{compile-time error} if a setter declares a return type other than \VOID. -It is a \Error{compile-time error} if a class has +%% TODO(eernst): Come Dart 3, add 'mixin class', later: 'extension type'. +It is a \Error{compile-time error} if a library, class, mixin, or extension has a setter named \code{$v$=} with argument type $T$ and a getter named $v$ with return type $S$, and $S$ is not a subtype of $T$. From b61f3a73aa0af7c0354a38070b30a7062c7d4dc9 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 29 Sep 2023 16:13:16 +0200 Subject: [PATCH 74/85] Remove obsolete reference to "mixin composition" from section Superclasses --- specification/dartLangSpec.tex | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 0e6147a0b..0f1bbf4b1 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -5034,16 +5034,23 @@ \subsection{Superclasses} %% superclass can be declared in that scope). \LMHash{}% -The superclass $S'$ of a class $C$ whose declaration has a with clause -\code{\WITH{} $M_1, \ldots,\ M_k$} -and an extends clause -\code{\EXTENDS{} $S$} -is the abstract class obtained by application of -mixin composition (\ref{mixins}) $M_k* \cdots * M_1$ to $S$. -The name $S'$ is a fresh identifier. +The superclass $S'$ of a class $C$ whose declaration has an extends clause +\code{\EXTENDS\,\,$S$} +and a with clause +\code{\WITH\,\,$M_1, \ldots,\ M_k$} +is the following class +(\ref{mixinClasses}), +where the name $N$ is a fresh identifier: + +\begin{normativeDartCode} +\ABSTRACT \CLASS{} $N$ = $S$ \WITH{} $M_1$, \ldots, $M_k$; +\end{normativeDartCode} + +\LMHash{}% If no \WITH{} clause is specified then the \EXTENDS{} clause of a class $C$ specifies its superclass. If no \EXTENDS{} clause is specified, then either: + \begin{itemize} \item $C$ is \code{Object}, which has no superclass. OR \item Class $C$ is deemed to have an \EXTENDS{} clause of the form From 02fc7714b0a3825adb5ffdf88f6bd9e00dbb2b17 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 29 Sep 2023 18:18:31 +0200 Subject: [PATCH 75/85] Add comment about default values of noSuchMethod forwarders being underspecified --- specification/dartLangSpec.tex | 3 +++ 1 file changed, 3 insertions(+) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 0f1bbf4b1..c9c283bcf 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -3516,6 +3516,9 @@ \subsubsection{The Method \code{noSuchMethod}} %% in order to be able to say this precisely. A noSuchMethod forwarder is a concrete member of $C$ with the signature taken from the interface of $C$, +%% TODO: #3331 - The default values should be obtained from all declarations +%% that gave rise to this member signature in $C$, and it should be an error +%% when there are no default values or there are >=2 distinct default values. and with the same default value for each optional parameter. It can be invoked in an ordinary invocation and in a superinvocation, and when $m$ is a method it can be closurized From 18e9f266709eb6b20aba0d207c1814797e90381a Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Wed, 10 Jan 2024 13:00:46 +0100 Subject: [PATCH 76/85] Correct rule about the interface of T? --- specification/dartLangSpec.tex | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index c9c283bcf..41a3a6b0d 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -23445,10 +23445,10 @@ \subsection{Type Nullability} \end{itemize} \LMHash{}% -Let $T$ be a potentially nullable type such that -\NormalizedTypeOf{$T$} is neither \DYNAMIC{} nor \VOID. -The \IndexCustom{interface}{interface!of potentially nullable type} -of $T$ is the interface of \code{Object}. +Let $T$ be a type such that \NormalizedTypeOf{$T$} is +neither \DYNAMIC{} nor \VOID. +The \IndexCustom{interface}{interface!of a nullable type} +of \code{$T$?} is the interface of \code{Object}. \commentary{% For example, even though \code{$e_1$.isEven} is allowed From 594c1b150cbd62c03caf762d66a0ebbb26a5f9da Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Wed, 10 Jan 2024 13:09:13 +0100 Subject: [PATCH 77/85] Correct rule about interfaces of bounded types --- specification/dartLangSpec.tex | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 41a3a6b0d..ed2b35f1c 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -5542,9 +5542,9 @@ \section{Interfaces} (\ref{propertyExtraction}), any type $T$ which is $T_0$ bounded (\ref{typesBoundedByTypes}), -where $T_0$ is a class with interface $I$, +where $T_0$ is a type with interface $I$, is also considered to have interface $I$. -Similarly, when $T$ is $T_0$ bounded where $T_0$ is a function type, +In particular, when $T$ is $T_0$ bounded where $T_0$ is a function type, $T$ is considered to have a method named \CALL{} with signature $m$, such that the function type of $m$ is $T_0$. @@ -23454,8 +23454,10 @@ \subsection{Type Nullability} For example, even though \code{$e_1$.isEven} is allowed when the type of $e_1$ is \code{int}, \code{$e_2$.isEven} is a compile-time error -when the type of $e_2$ is \code{int?}\ or -a type variable $X$ whose bound is \code{int?}.% +when the type of $e_2$ is \code{int?}. +It is also an error for a type variable $X$ whose bound is \code{int?}, +because they have the same interface +(\ref{interfaces}).% } From f25a150a9ceee8383046aab85cccdb04d7f69864 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Thu, 22 Feb 2024 13:34:41 +0100 Subject: [PATCH 78/85] Corrected many occurrences of "string" to "string literal" --- specification/dartLangSpec.tex | 43 ++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index ed2b35f1c..cf7f771e2 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -9573,7 +9573,8 @@ \subsection{Strings} \end{grammar} \LMHash{}% -A string can be a sequence of single line strings and multiline strings. +A string literal can be a sequence of single line strings +and multiline strings. \begin{grammar} ::= @@ -9625,24 +9626,25 @@ \subsection{Strings} \end{grammar} \LMHash{}% -A single line string is delimited by +A single line string literal is delimited by either matching single quotes or matching double quotes. \commentary{% -Hence, \code{'abc'} and \code{"abc"} are both legal strings, +Hence, \code{'abc'} and \code{"abc"} are both legal string literals, as are \code{'He said "To be or not to be" did he not?'} and \code{"He said 'To be or not to be' didn't he?"}. -However, \code{"This'} is not a valid string, nor is \code{'this"}.% +However, \code{"This'} is not a valid string literal, nor is \code{'this"}.% } \commentary{% -The grammar ensures that a single line string cannot span more than +The grammar ensures that a single line string literal cannot span more than one line of source code, unless it includes an interpolated expression that spans multiple lines.% } \LMHash{}% -Adjacent strings are implicitly concatenated to form a single string literal. +Adjacent string literals are implicitly concatenated +to form a single string literal. \commentary{% Here is an example:% @@ -9655,7 +9657,7 @@ \subsection{Strings} \rationale{% Dart also supports the operator + for string concatenation. -The + operator on Strings requires a String argument. +The + operator on \code{String}s requires a \code{String} argument. It does not coerce its argument into a string. This helps avoid puzzlers such as% } @@ -9680,17 +9682,17 @@ \subsection{Strings} String interpolation works well for most cases. The main situation where it is not fully satisfactory is for string literals that are too large to fit on a line. -Multiline strings can be useful, but in some cases, +Multiline string literals can be useful, but in some cases, we want to visually align the code. This can be expressed by writing -smaller strings separated by whitespace, as shown here:% +smaller string literals separated by whitespace, as shown here:% } \begin{dartCode} 'Imagine this is a very long string that does not fit on a line. What shall we do? ' 'Oh what shall we do? ' 'We shall split it into pieces ' -'like so'. +'like so.' \end{dartCode} \LMHash{}% @@ -9775,10 +9777,10 @@ \subsection{Strings} \end{grammar} \LMHash{}% -Multiline strings are delimited by either +Multiline string literals are delimited by either matching triples of single quotes or matching triples of double quotes. -If the first line of a multiline string consists solely of +If the first line of a multiline string literal consists solely of the whitespace characters defined by the production \synt{WHITESPACE} (\ref{lexicalRules}), possibly prefixed by \syntax{`\\'}, @@ -9786,11 +9788,12 @@ \subsection{Strings} including the line break at its end. \rationale{% -The idea is to ignore a whitespace-only first line of a multiline string, +The idea is to ignore a whitespace-only first line of +a multiline string literal, where whitespace is defined as tabs, spaces and the final line break. These can be represented directly, but since for most characters prefixing by backslash is -an identity in a non-raw string, +an identity in a non-raw string literal, we allow those forms as well.% } @@ -9808,7 +9811,7 @@ \subsection{Strings} } \LMHash{}% -Strings support escape sequences for special characters. +String literals support escape sequences for special characters. The escapes are: \begin{itemize} \item @@ -9854,12 +9857,12 @@ \subsection{Strings} \end{itemize} \LMHash{}% -Any string may be prefixed with the character \lit{r}, -indicating that it is a \Index{raw string}, +Any string literal may be prefixed with the character \lit{r}, +indicating that it is a \Index{raw string literal}, in which case no escapes or interpolations are recognized. \LMHash{}% -Line breaks in a multiline string are represented by +Line breaks in a multiline string literal are represented by the \synt{LINE\_BREAK} production. A line break introduces a single newline character (U+000A) into the string value. @@ -9908,12 +9911,12 @@ \subsubsection{String Interpolation} \commentary{% The reader will note that the expression inside the interpolation -could itself include strings, +could itself include string literals, which could again be interpolated recursively.% } \LMHash{}% -An unescaped \lit{\$} character in a string signifies +An unescaped \lit{\$} character in a string literal signifies the beginning of an interpolated expression. The \lit{\$} sign may be followed by either: \begin{itemize} From c3b8f68f3a2c2e8e16505b3caf88b7ace67c7d2f Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Tue, 30 Apr 2024 10:48:54 +0200 Subject: [PATCH 79/85] Update the "parameter name `_...`" error --- specification/dartLangSpec.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index cf7f771e2..848b3c373 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -2507,7 +2507,7 @@ \subsubsection{Optional Formals and Named Formals} if the static type of $d$ is not assignable to the declared type of $p$. \LMHash{}% -It is a \Error{compile-time error} if the name of a named optional parameter +It is a \Error{compile-time error} if the name of a named parameter begins with an `_' character. \rationale{% From 98ebb6946f46195e2ca6617bb8c832a3481fb370 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Mon, 17 Jun 2024 16:05:09 +0200 Subject: [PATCH 80/85] Restore the index marker, define a command for it --- specification/dart.sty | 7 +++++-- specification/dartLangSpec.tex | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/specification/dart.sty b/specification/dart.sty index bf3004759..f4829d3f1 100644 --- a/specification/dart.sty +++ b/specification/dart.sty @@ -173,9 +173,12 @@ \newcommand{\id}{\metavar{id}} \newcommand{\op}{\metavar{op}} +% Used in margin to indicate that a term is being defined here. +\newcommand{\IndexMarker}{\ensuremath{^\vartriangle}} + % Used for defining occurrence of phrase, with customized index entry. \newcommand{\IndexCustom}[2]{% - \leavevmode\marginpar{\ensuremath{\vartriangle}}\emph{#1}\index{#2}} + \leavevmode\marginpar{\IndexMarker}\emph{#1}\index{#2}} % Used for the defining occurrence of a local symbol. \newcommand{\DefineSymbol}[1]{% @@ -196,7 +199,7 @@ % Same appearance, but not adding an entry to the index. \newcommand{\NoIndex}[1]{% - \leavevmode\marginpar{\ensuremath{\vartriangle}}\emph{#1}} + \leavevmode\marginpar{\IndexMarker}\emph{#1}} % Mark a compile-time error in the margin. \newcommand{\Error}[1]{% diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 848b3c373..3c02228b2 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -831,11 +831,11 @@ \section{Notation} \LMHash{}% The document contains an index at the end. Each entry in the index refers to a page number, $p$. -On page $p$ there is a `$\diamond$' in the margin +On page $p$ there is a `\IndexMarker{}' in the margin at the definition of the given indexed phrase, and the phrase itself is shown using \emph{this typeface}. We have hereby introduced the -\Index{index marker $\diamond$} +\Index{index marker \IndexMarker{}} itself. \LMHash{}% From 09eed53b9ebb7ee73491f2ff697aab5dcfee0038 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Mon, 17 Jun 2024 16:27:12 +0200 Subject: [PATCH 81/85] Corrected the huge rebase operation (removed TODO material) --- specification/dartLangSpec.tex | 659 +-------------------------------- 1 file changed, 2 insertions(+), 657 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 3c02228b2..3fc18d155 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -14790,7 +14790,7 @@ \subsubsection{Ordinary Invocation} then the static type of the member is specified in Section~\ref{typeDynamic}. In this case, if $m$ is \code{hashCode} or \code{runtimeType} -then let $F$ be the return type of said getter; +then let \DefineSymbol{F} be the return type of said getter; if $m$ is \code{noSuchMethod} or \code{toString} then let $F$ be the type of said method. @@ -15420,7 +15420,7 @@ \subsubsection{Getter Invocation and Method Closurization} } \LMHash{}% -Let $T$ be the static type of $e$. +Let \DefineSymbol{T} be the static type of $e$. \LMHash{}% If $T$ is \DYNAMIC{} bounded @@ -26718,661 +26718,6 @@ \subsection{Operator Precedence} } -\section{Null safety} %% !!!TODO!!! -\LMLabel{} - -%% !!!At the end: Search Null, change to Never where appropriate -%% !!!Search all `TODO`.*null - -\subsubsection{Static errors} - -\paragraph{Nullability definitions} - -\paragraph{Errors and Warnings} - -[LATER] It is an error if the body of a method, function, getter, or -function expression with a potentially non-nullable return type may -complete normally. - -[LATER] For the purposes of errors and warnings, the null aware -operators \code{?.}, \code{?..}, and \code{?[]} are checked as if the -receiver of the operator had non-nullable type. More specifically, if -the type of the receiver of a null aware operator is $T$, then the -operator is checked as if the receiver had type \NonNullType{$T$} -(see definition below). - -[LATER] It is not a compile time error to write to a \FINAL{} -non-local or instance variable if that variable is declared \LATE{} -and does not have an initializer. For local variables, see the -section below. - -\subsubsection{Local variables and definite (un)assignment} - -As part of the null safety release, errors for local variables are specified to -take into account definite assignment and definite unassignment (see the -section on Definite Assignment below). We say that a variable is -\Index{potentially assigned} -if it is not definitely unassigned, and that a variable is -\Index{potentially unassigned} if it is not definitely assigned. - -In all cases in this section, errors that are described as occurring on reads of -a variable are intended to apply to all form of reads, including indirectly as -part of compound assignment operators, as well as via pre and post-fix -operators. Similarly, errors that are described as occurring on writes of a -variable are intended to apply to all form of writes. - -It is a compile time error to assign a value to a \FINAL, non-\LATE{} local -variable which is \Index{potentially assigned}. Thus, it is not a compile time -error to assign to a definitely unassigned \FINAL{} local variable. - -It is a compile time error to assign a value to a \FINAL, \LATE{} local variable -if it is definitely assigned. Thus, it is not a compile time error to -assign to a potentially unassigned \FINAL, \LATE{} local variable. - -\commentary{% -Note that a variable is always considered definitely assigned and not -definitely unassigned if it has an explicit initializer, or an implicit -initializer as part of a larger construct (e.g. the loop variable in a for-in -construct).% -} - -It is a compile time error to read a local variable when the variable is -definitely unassigned, unless the variable is non-\FINAL, and non-\LATE, -and has nullable type. - -It is a compile time error to read a local variable when the variable is -potentially unassigned, unless the variable is non-\FINAL{} and has nullable -type, or is \LATE. - -The errors specified above are summarized in the following table, where \code{int} is -used as an example of an arbitrary potentially non-nullable type, \code{int?}\ is -used as an example of an arbitrary nullable type, and $T$ is used to stand -for a type of any nullability. A variable which has an initializer (explicit or -implicit) is always considered definitely assigned, and is never considered -definitely unassigned. - -Read Behavior: - -\begin{verbatim} -| Declaration form | Def. Assigned | Neither | Def. Unassigned | -| ----------------- | ------------- | ----------------- | --------------- | -| var x; | Ok | Ok | Ok | -| final x; | Ok | Error | Error | -| int x; | Ok | Error | Error | -| int? x; | Ok | Ok | Ok | -| final T x; | Ok | Error | Error | -| late var x; | Ok | Ok | Error | -| late final x; | Ok | Ok | Error | -| late T x; | Ok | Ok | Error | -| late final T x; | Ok | Ok | Error | -\end{verbatim} - -Write Behavior: - -\begin{verbatim} -| Declaration form | Def. Assigned | Neither | Def. Unassigned | -| ----------------- | ------------- | ------------------- | --------------- | -| var x; | Ok | Ok | Ok | -| final x; | Error | Error | Ok | -| int x; | Ok | Ok | Ok | -| int? x; | Ok | Ok | Ok | -| final T x; | Error | Error | Ok | -| late var x; | Ok | Ok | Ok | -| late final x; | Error | Ok | Ok | -| late T x; | Ok | Ok | Ok | -| late final T x; | Error | Ok | Ok | -\end{verbatim} - -\subsubsection{Local variables and inference} - -Local variables with explicitly written types are given the declared types as -written. The declared type of the variable is considered a "type of interest" -in the sense defined in the flow analysis specification. If the variable has an -initializer (explicit or implicit) and is not \FINAL, then the declaration is -treated as an assignment for the purposes of promotion. - -\commentary{% -Treating the declared type of the variable as a "type of interest" implies that -if the variable has a nullable type, then the non-nullable version of that type -is also a type of interest. Treating the initialization as an assignment for -the purposes of promotion means that initializing a mutable variable declared at -type \code{$T$?}\ with a value of non-nullable type $T$ immediately promotes the -variable to the non-nullable type.% -} - -\begin{dartCode} -\VOID test() \{ - int? x = 3; // x is declared at \code{int?} - x.isEven; // Valid, x has been promoted to \code{int} - x = null; // Valid, demotes to the declared type. -\} -\end{dartCode} - -Local variables with no explicitly written type but with an initializer are -given an inferred type equal to the type of their initializer, unless that type -is \code{Null}, in which case the inferred type of the variable shall be \DYNAMIC. -The inferred type of the variable is considered a "type of interest" in the -sense defined in the flow analysis specification. In the case that the type of -the initializer is a promoted type variable \code{X \& T}, the inferred type of the -variable shall be \code{X}, but \code{X \& T} shall be considered as a type of interest and -the initialization treated as an assignment for the purposes of promotion. -Consequently, such a variable shall be treated as immediately promoted to \code{X \& -T}. - -\subsubsection{Expression typing} -\subsubsection{Super-bounded types} - -!!!TODO!!! - -It is permitted to invoke or tear-off a method, setter, getter, or operator that -is defined on \code{Object} on potentially nullable type. The type used for static -analysis of such an invocation or tear-off shall be the type declared on the -relevant member on \code{Object}. For example, given a receiver $o$ of type \code{$T$?}, -invoking an \code{Object} member on $o$ shall use the type of the member as declared -on \code{Object}, regardless of the type of the member as declared on $T$. - -\commentary{% -Note that -the type as declared on $T$ must be a subtype of the type on \code{Object}, and so -choosing the \code{Object} type is a sound choice. The opposite choice is not -sound.% -} - -\commentary{% -Note that evaluation of an expression $e$ of the form \code{$e_1$\,\,==\,\,$e_2$} is not an -invocation of \code{operator\,\,==}, it includes special treatment of null. The -precise rules are specified later in this section.% -} - -Calling a method (including an operator) or getter on a receiver of static type -\code{Never} is treated by static analysis as producing a result of type \code{Never}. -Tearing off a method from a receiver of static type \code{Never} produces a value of -type \code{Never}. Applying an expression of type \code{Never} in the function position -of a function call produces a result of type \code{Never}. - -The static type of a \code{\THROW\,\,$e$} expression is \code{Never}. - -Consider an expression $e$ of the form \code{$e_1$\,==\,$e_2$} where the static type of -$e_1$ is $T_1$ and the static type of $e_2$ is $T_2$. Let $S$ be the type of the -formal parameter of \code{operator\,\,==} in the interface of \NonNullType{$T_1$}. -It is a \Error{compile-time error} unless $T_2$ is assignable to \code{$S$?}. - -Similarly, consider an expression $e$ of the form \code{\SUPER\,==\,$e_2$} that occurs in a -class whose superclass is $C$, where the static type of $e_2$ is $T_2$. Let $S$ be -the formal parameter type of the concrete declaration of \code{operator\,\,==} found by -method lookup in $C$ -\commentary{(if that search succeeds, otherwise it is a compile-time error)}. -It is a \Error{compile-time error} unless $T_2$ is assignable to \code{$S$?}. - -\commentary{% -Even if the static type of $e_1$ is potentially nullable, the parameter type -of the \code{operator\,\,==} of the corresponding non-null type is taken into account, -because that instance method will not be invoked when $e_1$ is null. Similarly, -it is not a \Error{compile-time error} for the static type of $e_2$ to be potentially -nullable, even when the parameter type of said \code{operator\,\,==} is non-nullable. -This is again safe, because the instance method will not be invoked when $e_2$ -is null.% -} - -If no type is specified in a catch clause, then the default type of the error -variable is \code{Object}. - -\paragraph{Spread element typing} - -In a collection literal in Dart before null-safety, the inferred element -type of a spread element of the form \code{...?$e$} where $e$ has static type -\code{Null} is \code{Null}, and so are the inferred key type and value type. - -With null-safety, when the static type of $e$ is \code{Null} or a potentially -nullable subtype thereof, the inferred element, key, and value type -of \code{...?$e$} is \code{Never}. - -Similarly, when the static type of $e$ is a subtype of \code{Never}, -the element, key, and value type of \code{...$e$} and \code{...?$e$} is \code{Never}. - -\commentary{% -When the static type $S$ of $e$ is strictly non-nullable, such as when $S$ -is \code{Never}, \code{...?$e$} is a warning, but it may still occur.% -} - -\subsubsection{Instantiation to bound} - -The computation of instantiation to bound is changed to substitute \code{Never} for -type variables appearing in contravariant positions instead of \code{Null}. - -\subsubsection{Super-bounded types} - -Null safety requires three changes to the section 'Super-Bounded Types' in -the language specification. - -The definition of a top type is changed: $T$ is a top type if and only if -\code{Object?}\ is a subtype of $T$. Note that the helper predicate \IsTopTypeName{} -provides a syntactic characterization of the same concept. - -The definition of a super-bounded type is changed such that occurrences of -\code{Null} are replaced by types involving \code{Never}, and \code{Object} is replaced by -\code{Object?}. Moreover, top types in invariant positions and in positions that -have no variance (\commentary{unused type parameters in a type alias}) are given the -same treatment as top types in covariant positions. This causes one -sentence to change, with the following result: - -Let $T'$ be the result of replacing every occurrence in $T$ of a type $S$ -in a contravariant position where \code{$S$ <: Never} by \code{Object?}, and every -occurrence in $T$ of a top type in a position which is not contravariant by -\code{Never}. - -\subsubsection{Least and greatest closure} - -The definitions of least and greatest closure are changed in null safe libraries -to substitute \code{Never} in positions where previously \code{Null} would have been -substituted, and \code{Object?}\ in positions where previously \code{Object} or \DYNAMIC{} -would have been substituted. - -\subsubsection{Const type variable elimination} -\LMLabel{} - -If performing inference on a constant value of a generic class results in -inferred type arguments to the generic class which contain free type variables -from an enclosing generic class or method, the free type variables shall be -eliminated by taking the least closure of the inferred type with respect to the -free type variables. Note that free type variables which are explicitly used as -type arguments in constant generic instances are still considered erroneous. - -\begin{dartCode} -\CLASS\ G \{ - \VOID\ foo() \{ - \CONST\ List c = []; // Error - \CONST\ List d = []; // The list literal is inferred as [] - \} -\} -\end{dartCode} - -\subsubsection{Implicit conversions} - -!!!TODO!!! - -This section, line 4241 -in the language specification defines the notion of a _combined member -signature_. In Dart before null-safety it is based on the textually first -superinterface that has a most specific signature. With null-safety it -is changed such that the all the most specific signatures are merged. - -This is achieved by changing this paragraph, line 4373, -to the following: - -"Let $m_{\metavar{all}}$ be the result of applying \TopMergeTypeName{} to -the elements in $M_{\metavar{all}}$, ordered according to the interface -\List{I}{1}{k} that each signature came from." - -Moreover, the occurrence of $m_i$ in the next paragraph is -changed to $m_{\metavar{all}}$. - -\subsubsection{Implicit conversions} - -The implicit conversion of integer literals to double literals is performed when -the context type is \code{double} or \code{double?}. - -The implicit tear-off conversion which converts uses of instances of classes -with call methods to the tear-off of their \CALL{} method is performed when the -context type is a function type, or the nullable version of a function type. - -Implicit tear-off conversion is not performed on objects of nullable type, -regardless of the context type. For example: - -\begin{dartCode} -\CLASS\ C \{ - int call() \{\} -\} -\VOID\ main() \{ - int \FUNCTION()? c0 = \NEW\ C(); // Ok - int \FUNCTION()? c0 = (null \AS\ C?); // static error - int \FUNCTION() c1 = (null \AS\ C?); // static error -\} -\end{dartCode} - -\subsubsection{Constant Objects} - -!!!TODO!!! - -The definition of potentially constant expressions is extended to include type -casts and instance checks on potentially constant types, as follows. - -We change the following specification text: - -\Case{Spec change} -An expression of the form \code{$e$\,\,\AS\,\,$T$} is potentially constant -if $e$ is a potentially constant expression -and $T$ is a constant type expression, -and it is further constant if $e$ is constant. -\EndCase - -to - -\Case{Spec change} -An expression of the form \code{$e$\,\,\AS\,\,$T$} or -\code{$e$\,\,\IS\,\,$T$} is potentially constant -if $e$ is a potentially constant expression -and $T$ is a potentially constant type expression, -and it is further constant if $e$ is constant. -\EndCase - -where the definition of a "potentially constant type expression" is the same as -the current definition for a "constant type expression" with the addition that a -type variable is allowed as a "potentially constant type expression". - -This is motivated by the requirement to make downcasts explicit as part of the -NNBD release. Current constant evaluation is permitted to evaluate implicit -downcasts involving type variables. Without this change, it is difficult to -change such implicit downcasts to an explicit form. For example this class is -currently valid Dart code, but is invalid after the NNBD restriction on implicit -downcasts because of the implied downcast on the initialization of \code{w}: - - -\begin{dartCode} -\CONST\ num three = 3; - -\CLASS{} ConstantClass \{ - \FINAL\ T w; - \CONST\ ConstantClass() : w = three /* as T */; -\} - -\VOID\ main() \{ - print(\CONST\ ConstantClass()); -\} -\end{dartCode} - -With this change, the following is a valid migration of this code: - -\begin{dartCode} -\CONST\ num three = 3; - -\CLASS\ ConstantClass \{ - \FINAL\ T w; - \CONST\ ConstantClass() : w = three \AS\ T; -\} - -\VOID{} main() \{ - print(\CONST\ ConstantClass()); -\} -\end{dartCode} - -\subsubsection{Null promotion} -\LMLabel{} - -!!! - -The machinery of type promotion is extended to promote the type of -variables based on nullability checks subject to the same set of -restrictions as normal promotion. The relevant checks and the types -they are considered to promote to are as follows. - -A check of the form \code{$e$ == null} or of the form -\code{$e$\,\,\IS\,\,Null} where $e$ has static type $T$ promotes the -type of $e$ to \code{Null} in the \code{true} continuation, and to -\NonNullType{$T$} in the \code{false} continuation. - -A check of the form \code{$e$ != null} or of the form -\code{$e$\,\,\IS\,\,$T$} where $e$ has static type -\code{$T$?}\ promotes the type of $e$ to $T$ in the \code{true} -continuation, and to \code{Null} in the \code{false} continuation. - -The static type of an expression \code{$e$!} is \NonNullType{$T$} -where $T$ is the static type of $e$. - -\subsubsection{Null aware operator} -\LMLabel{nullShorteningTransformation} - -!!! - -The semantics of the null aware operator \code{?.} are defined via a source to source -translation of expressions into Dart code extended with a let binding construct. -The translation is defined using meta-level functions over syntax. We use the -notation \code{fn[x : Exp] : Exp => E} to define a meta-level function of type \code{Exp --> Exp} (that is, a function from expressions to expressions), and similarly -\code{fn[k : Exp -> Exp] : Exp => E} to define a meta-level function of type \code{Exp -> -Exp -> Exp}. Where obvious from context, we elide the parameter and return -types on the meta-level functions. The meta-variables \code{F} and \code{G} are used to -range over meta-level functions. Application of a meta-level function is -written as \code{F[p]} where \code{p} is the argument. - -The null-shorting translation of an expression \code{e} is meta-level function \code{F} of -type \code{(Exp -> Exp) -> Exp} which takes as an argument the continuation of \code{e} and -produces an expression semantically equivalent to \code{e} with all occurrences of -\code{?.} eliminated in favor of explicit sequencing using a \code{let} construct. - -Let \code{ID} be the identity function \code{fn[x : Exp] : Exp => x}. - -The expression translation of an expression \code{e} is the result of applying the -null-shorting translation of \code{e} to \code{ID}. That is, if \code{e} translates to \code{F}, -then \code{F[ID]} is the expression translation of \code{e}. - -We use \code{EXP(e)} as a shorthand for the expression translation of \code{e}. That is, -if the null-shorting translation of \code{e} is \code{F}, then \code{EXP(e)} is \code{F[ID]}. - -We extend the expression translation to argument lists in the obvious way, using -\code{ARGS(args)} to denote the result of applying the expression translation -pointwise to the arguments in the argument list \code{args}. - -We use three combinators to express the translation. - -The null-aware shorting combinator \code{SHORT} is defined as: -\begin{dartCode} - SHORT = fn[r : Exp, c : Exp -> Exp] => - fn[k : Exp -> Exp] : Exp => - let x = r in x == null ? null : k[c[x]] -\end{dartCode} - -where \code{x} is a fresh object level variable. The \code{SHORT} combinator is used to -give semantics to uses of the \code{?.} operator. It is parameterized over the -receiver of the conditional property access (\code{r}) and a meta-level function -(\code{c}) which given an object-level variable (\code{x}) bound to the result of -evaluating the receiver, produces the final expression. The result is -parameterized over the continuation of the expression being translated. The -continuation is only called in the case that the result of evaluating the -receiver is non-null. - -The shorting propagation combinator \code{PASSTHRU} is defined as: -\begin{dartCode} - PASSTHRU = fn[F : (Exp -> Exp) -> Exp, c : Exp -> Exp] => - fn[k : Exp -> Exp] : Exp => F[fn[x] => k[c[x]]] -\end{dartCode} - -The \code{PASSTHRU} combinator is used to give semantics to expression forms which -propagate null-shorting behavior. It is parameterized over the translation \code{F} -of the potentially null-shorting expression, and over a meta-level function \code{c} -which given an expression which denotes the value of the translated -null-shorting expression produces the final expression being translated. The -result is parameterized over the continuation of the expression being -translated, which is called unconditionally. - -The null-shorting termination combinator TERM is defined as: -\begin{dartCode} - TERM = fn[r : Exp] => fn[k : Exp -> Exp] : Exp => k[r] -\end{dartCode} - -The \code{TERM} combinator is used to give semantics to expressions which neither -short-circuit nor propagate null-shorting behavior. It is parameterized over -the translated expression, and simply passes on the expression to its -continuation. - -\begin{itemize} -\item A property access \code{$e$?.$f$} translates to: - \code{SHORT[EXP($e$), fn[x] => x.$f$]} -\item If $e$ translates to $F$ then \code{$e$.$f$} translates to: - \code{PASSTHRU[$F$, fn[x] => x.$f$]} -\item A null aware method call \code{$e$?.$m$(\metavar{args})} translates to: - \code{SHORT[EXP($e$), fn[x] => x.$m$(ARGS(\metavar{args}))]} -\item If $e$ translates to $F$ then \code{$e$.$m$(\metavar{args})} translates to: - \code{PASSTHRU[$F$, fn[x] => x.$m$(ARGS(\metavar{args}))]} -\item If $e$ translates to $F$ then \code{$e$(\metavar{args})} translates to: - \code{PASSTHRU[$F$, fn[x] => x(ARGS(\metavar{args}))]} -\item If $e_1$ translates to $F$ then \code{$e_1$?[$e_2$]} translates to: - \code{SHORT[EXP($e_1$), fn[x] => x[EXP($e_2$)]]} -\item If $e_1$ translates to $F$ then \code{$e_1$[$e_2$]} translates to: - \code{PASSTHRU[$F$, fn[x] => x[EXP($e_2$)]]} -\item If $e$ translates to $F$ then \code{$e$!} translates to: - \code{PASSTHRU[$F$, fn[x] => x!]} -\item The assignment \code{$e_1$?.$f$ = $e_2$} translates to: - \code{SHORT[EXP($e_1$), fn[x] => x.f = EXP($e_2$)]} -\item The other assignment operators are handled equivalently. -\item If $e_1$ translates to $F$ then \code{$e_1$.$f$ = $e_2$} translates to: - \code{PASSTHRU[$F$, fn[x] => x.$f$ = EXP($e_2$)]} -\item The other assignment operators are handled equivalently. -\item If $e_1$ translates to $F$ then \code{$e_1$?[$e_2$] = $e_3$} translates to: - \code{SHORT[EXP($e_1$), fn[x] => x[EXP($e_2$)] = EXP($e_3$)]} -\item The other assignment operators are handled equivalently. -\item If $e_1$ translates to $F$ then \code{$e_1$[$e_2$] = $e_3$} translates to: - \code{PASSTHRU[$F$, fn[x] => x[EXP($e_2$)] = EXP($e_3$)]} -\item The other assignment operators are handled equivalently. -\item A cascade expression \code{$e$..$s$} translates as follows, where $F$ is the - translation of $e$ and \code{x} and \code{y} are fresh object level variables: - \begin{dartCode} - fn[k : Exp -> Exp] : Exp => - $F$[fn[r : Exp] : Exp => let x = r in - let y = EXP(x.$s$) - in k[x] - ] - \end{dartCode} -\item A null-shorting cascade expression \code{$e$?..$s$} translates as follows, where \code{x} - and \code{y} are fresh object level variables. - \begin{dartCode} - fn[k : Exp -> Exp] : Exp => - let x = EXP($e$) in x == null ? null : let y = EXP(x.$s$) in k(x) - \end{dartCode} -\item All other expressions are translated compositionally using the \code{TERM} - combinator. Examples: - An identifier \code{x} translates to \code{TERM[x]} - A list literal \code{[$e_1$, \ldots, $e_n$]} translates to \code{TERM[ [EXP($e_1$), \ldots, EXP($e_n$)] ]} - A parenthesized expression \code{($e$)} translates to \code{TERM[(EXP($e$))]}. -\end{itemize} - -The language specification specifies that an invocation of any of several -operators is considered equivalent to a member access (this applies to -relational expressions, bitwise expressions, shift expressions, additive -expressions, multiplicative expressions, and unary expressions). - -\commentary{% -For example, \code{$a$ + $b$} is specified as equivalent to \code{$a$.plus($b$)}, -where \code{plus} is assumed to be a method with the same behavior as \code{+}. -Similarly, \code{-$e$} is equivalent to \code{$e$.unaryMinus()}.% -} - -This equivalence is not applicable in the above rules, so operators not -mentioned specifically in a rule are handled in the case for 'other' -expressions, not in the case for \code{$e$.m(\metavar{args})}. - -\commentary{% -This means that the null-shorting transformation stops at operators. -For instance, \code{$e$?.$f$ + $b$} is a compile-time error because \code{$e$?.$f$} can be null, it is -not an expression where both \code{.$f$} and \code{+ $b$} will be skipped if \code{$e$} is null. -Similarly, both \code{-$a$?.$f$} and \code{~$a$?.$f$} are errors, and do not null-short like -\code{$a$?.$f$.op()}.% -} - -\subsection{Other documents that need to be integrated} -\LMLabel{} - -!!!TODO!!! - -The requirement that the condition in a boolean conditional control expression -(e.g. the a conditional statement, conditional element, \code{while} loop, etc) be -assignable to \code{bool} is unchanged from pre null-safe Dart. The change in -assignability means that the static type of the condition may only be \DYNAMIC, -\code{Never}, or \code{bool}. An expression of type \code{Never} will -always diverge and an expression of type \code{bool} will never evaluate to a value -other than \code{true} or \code{false}, and hence no conversion is required in these -cases. A conditional expression of type \DYNAMIC{} may evaluated to any value, -and hence must be implicitly downcast to \code{bool}, after which no further check is -required. - -Given a boolean conditional expression $e$ where $e$ has type $S$, it is a -static error if $S$ is not assignable to \code{bool}. -Otherwise evaluation proceeds as follows: - -\begin{itemize} -\item First $e$ is implicitly cast to \code{bool} if required. - This cast may fail, and if so it is a dynamic error. -\item If the cast does not fail, then the result is known to be a non-null - boolean, and evaluation of the enclosing conditional proceeds as usual. -\end{itemize} - -\subsection{Migration features} - -The \TopMergeTypeName{} of two types $T$ and $S$ is the unique type $R$ defined -as: - -\begin{itemize} -\item \TopMergeType{Object?}{Object?} = \code{Object?} -\item \TopMergeType{\DYNAMIC}{\DYNAMIC} = \code{\DYNAMIC} -\item \TopMergeType{\VOID}{\VOID} = \code{\VOID} -\item \TopMergeType{Object?}{\VOID} = \code{Object?} - - And the reverse -\item \TopMergeType{\DYNAMIC}{\VOID} = \code{Object?} - - And the reverse -\item \TopMergeType{Object?}{\DYNAMIC} = \code{Object?} - - And the reverse -\item \TopMergeType{$T$?}{$S$?} = \code{\TopMergeType{$T$}{$S$}?} -\item And for all other types, recursively applying the transformation over the - structure of the type - - e.g. \TopMergeType{$C$<$T$>}{$C$<$S$>} = \code{$C$<\TopMergeType{$T$}{$S$}>} -\item When computing the \TopMergeTypeName{} of two method parameters at least one of - which is marked as covariant, the following algorithm is used to compute the - canonical parameter type. - - Given two corresponding parameters of type $T_1$ and $T_2$ where at least - one of the parameters has a \COVARIANT{} declaration: - if \code{$T_1$ <: $T_2$} and \code{$T_2$ <: $T_1$} then the result is \TopMergeType{$T_1$}{$T_2$}, - and it is covariant. - Otherwise, if \code{$T_1$ <: $T_2$} then the result is $T_2$ and it is covariant. - Otherwise the result is $T_1$ and it is covariant. -\end{itemize} - -In other words, \TopMergeTypeName{} takes two types which are structurally equal -except for the particular choice of top types, and -finds a single canonical type to represent them. -The \TopMergeTypeName{} of two types is not defined for -types which are not otherwise structurally equal. - -The \TopMergeTypeName{} of more than two types is defined by taking the -\TopMergeTypeName{} of the first two, and then recursively taking the -\TopMergeTypeName{} of the rest. - -If a class $C$ library implements the same generic class $I$ more -than once as \code{$I_0$, \ldots, $I_n$}, and at least one of the $I_i$ is not syntactically -equal to the others, then it is an error if \TopMergeType{$S_0$, \ldots}{$S_n$} is not -defined where $S_i$ is \NormalizedTypeOf{$I_i$}. Otherwise, $C$ is considered to -implement the canonical interface given by \TopMergeType{$S_0$, \ldots}{$S_n$}. This -determines the outcome of dynamic instance checks applied to instances of $C$, -as well as static subtype checks on expressions of type $C$. - -If a class $C$ in an opted-in library inherits a member $m$ with the same name -from multiple direct super-interfaces, let -\List{T}{0}{n} be the signatures of the inherited members. If there is exactly one -$T_i$ such that \code{$T_i$ <: $T_k$} for all $k \in 0 .. n$ then the signature -of $m$ is considered to be $T_i$. If there is more than one such $T_i$, then it -is an error if the \TopMergeTypeName{} of \List{S}{0}{n} does not exist, where $S_i$ -is \NormalizedTypeOf{$T_i$}. Otherwise, the signature of $m$ for the purposes of member -lookup is the \TopMergeTypeName{} of the $S_i$. - -!!!TODO!!! - -Check that the semantics of \IS{} is as specified here: -if $e$ evaluates to a value $v$ and $v$ has -runtime type $S$, an instance check \code{$e$ \IS\ $T$} is evaluated as follows: -Return \code{$S$ <: $T$}. - -Check that the semantics of \AS{} is as specified here: -if $e$ evaluates to a value $v$ and $v$ has -runtime type $S$, a cast \code{$e$ \AS\ $T$} is evaluated as follows: -if \code{$S$ <: $T$} then \code{$e$ as $T$} evaluates to $v$. -Otherwise a dynamic type error occurs. - -Other documents that need to be integrated, too: - -https://github.com/dart-lang/language/blob/master/resources/type-system/subtyping.md -https://github.com/dart-lang/language/blob/master/resources/type-system/upper-lower-bounds.md -%% !!!END TODO!!! - \section*{Appendix: Algorithmic Subtyping} \LMLabel{algorithmicSubtyping} From 4ba6e14f2f7137aa4d3b6739ac516785d33747bb Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Mon, 12 Aug 2024 15:01:56 +0200 Subject: [PATCH 82/85] Fix obsolete ref --- specification/dartLangSpec.tex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 3fc18d155..46ff072fa 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -22096,7 +22096,7 @@ \subsection{Static Types} A \synt{typeIdentifier} is an identifier which can be the name of a type, that is, it denotes an \synt{IDENTIFIER} which is not a \synt{BUILT\_IN\_IDENTIFIER} -(\ref{identifierReference}).% +(\ref{identifierExpression}).% } \commentary{% @@ -26605,7 +26605,7 @@ \subsubsection{Reserved Words} \LMHash{}% In the grammar, the rule for reserved words above must occur before the rule for \synt{BUILT\_IN\_IDENTIFIER} -(\ref{identifierReference}). +(\ref{identifierExpression}). \commentary{% This ensures that \synt{IDENTIFIER} and \synt{IDENTIFIER\_NO\_DOLLAR} do not From 5cd1a447480c37e9e80eca81b70ba6344c9fb457 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Mon, 12 Aug 2024 15:17:06 +0200 Subject: [PATCH 83/85] Introduce and use it --- specification/dartLangSpec.tex | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 46ff072fa..40eb36c5d 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -2888,14 +2888,18 @@ \section{Classes} ::= \ABSTRACT? \CLASS{} ? \gnewline{} ? ? - \gnewline{} `{' ( )* `}' + \gnewline{} \alt \ABSTRACT? \CLASS{} ::= (`,' )* - ::= `;' + ::= `;' \alt + ::= + + ::= `{' * `}' + ::= ? \alt \alt \STATIC? @@ -6051,7 +6055,7 @@ \subsection{Mixin Declaration} \begin{grammar} ::= \MIXIN{} ? \gnewline{} (\ON{} )? ? - \gnewline{} `\{' ( )* `\}' + \gnewline{} \end{grammar} %% TODO(eernst): Change this paragraph if we allow factory constructors. @@ -6306,7 +6310,7 @@ \section{Extensions} \begin{grammar} ::= \gnewline{} \EXTENSION{} ? ? \ON{} - \gnewline{} `\{' ( )* `\}' + \gnewline{} \end{grammar} \LMHash{}% @@ -22154,8 +22158,8 @@ \subsection{Static Types} ::= `[' `,'? `]' - ::= - `\{' (`,' )* `,'? `\}' + ::= \gnewline{} + `{' (`,' )* `,'? `}' ::= \REQUIRED? From a98a51d08ca5728151cf0c70f47a33d273d62784 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Mon, 12 Aug 2024 15:22:06 +0200 Subject: [PATCH 84/85] Add change log entry --- specification/dartLangSpec.tex | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/specification/dartLangSpec.tex b/specification/dartLangSpec.tex index 40eb36c5d..8c632e049 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -42,6 +42,11 @@ % is concerned with an instance member with the same basename, not a % static member. % +% Aug 2024 +% - Use everywhere, the distinction between class member +% syntax and mixin member syntax etc. was never actually introduced. The +% way they differ is expressed using (non-syntax) compile-time errors. +% % Jun 2024 % - Add missing references to section 'Type dynamic' at the points where the % static analysis of Object member invocations is specified. From bd6755e19cfa9f68b38b9b9114ab6bd26207be0a Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Thu, 17 Oct 2024 14:06:57 +0200 Subject: [PATCH 85/85] WIP --- specification/dart.sty | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/dart.sty b/specification/dart.sty index f4829d3f1..086e2420f 100644 --- a/specification/dart.sty +++ b/specification/dart.sty @@ -411,7 +411,7 @@ % the spacer argument. \newcommand{\RawFunctionTypeNamed}[9]{% \RawFunctionType{#1}{#2}{#3}{#4}{% - \FunctionTypeNamedParameters{#5}{#6}{#7}{#8}{r}}} + \FunctionTypeNamedParameters{#5}{#6}{#7}{#8}{#9}}} % A variant of \FunctionTypeNamed that uses the standard symbols, % that is, a function type with positional optional parameters which