diff --git a/specification/dart.sty b/specification/dart.sty index 6c856874b..086e2420f 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 @@ -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,11 +199,15 @@ % 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]{% - \leavevmode\marginpar{\ensuremath{_{^\ominus}}}{#1}} + \leavevmode\marginpar{\ensuremath{\textcolor{red}{\ominus}}}{#1}} + +% Mark a dynamic error in the margin. +\newcommand{\DynamicError}[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}}} @@ -402,9 +409,9 @@ % 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}}} + \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 @@ -466,17 +473,14 @@ \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}}} \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 8615bc9f7..0462cad55 100644 --- a/specification/dartLangSpec.tex +++ b/specification/dartLangSpec.tex @@ -7,10 +7,12 @@ \usepackage{amssymb} \usepackage{semantic} \usepackage{dart} -\usepackage{hyperref} +\usepackage[hidelinks]{hyperref} \usepackage{lmodern} \usepackage[T1]{fontenc} \usepackage{makeidx} +\usepackage{enumitem} +\usepackage{marvosym} \makeindex \title{Dart Programming Language Specification\\ {6th edition draft}\\ @@ -24,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, @@ -46,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. @@ -101,9 +102,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 @@ -130,6 +133,9 @@ % members. % - 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 @@ -277,9 +283,9 @@ % - Specify that it is an error for a concrete class with no non-trivial % \code{noSuchMethod} to not have a concrete declaration for some member % in its interface, or to have one which is not a correct override. -% - Use \ref{bindingActualsToFormals} in 3 locations, eliminating 2 extra +% - Use \ref{bindingFormalsToActuals} in 3 locations, eliminating 2 extra % copies of nearly the same text. -% - Add figure in \ref{bindingActualsToFormals} for improved readability. +% - Add figure in \ref{bindingFormalsToActuals} for improved readability. % - Introduce a notion of lookup which is needed for superinvocations. % - Use new lookup concept to simplify specification of getter, setter, method % lookup. @@ -287,7 +293,7 @@ % readability. % - Reorganize several sections to specify static analysis first and then % dynamic semantics; clarify many details along the way. The sections are: -% \ref{variables}, \ref{new}, \ref{const}, \ref{bindingActualsToFormals}, +% \ref{variables}, \ref{new}, \ref{const}, \ref{bindingFormalsToActuals}, % \ref{unqualifiedInvocation}, \ref{functionExpressionInvocation}, % \ref{superInvocations}, \ref{assignment}, \ref{compoundAssignment}, % \ref{localVariableDeclaration}, and \ref{return}. @@ -540,7 +546,7 @@ \section{Notation} \LMHash{}% Reserved words and built-in identifiers -(\ref{identifierReference}) +(\ref{identifierExpression}) appear in {\bf bold}. \commentary{% @@ -608,6 +614,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$. @@ -651,21 +665,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.% } @@ -805,7 +819,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{}% @@ -822,11 +836,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{}% @@ -911,24 +925,14 @@ \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 + (\ref{typeInference}), + or using the type \DYNAMIC{} (\ref{typeDynamic}). \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}). @@ -1020,8 +1024,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 @@ -1439,10 +1443,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{% @@ -1457,19 +1463,20 @@ \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} -%% 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 @@ -1515,7 +1522,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, @@ -1525,6 +1533,17 @@ \subsection{Implicitly Induced Getters and Setters} In these cases, the declared type of \id{} is $T$. \EndCase +\commentary{% +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 +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 @@ -1544,13 +1563,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{typeInference}). \EndCase \LMHash{}% @@ -1563,10 +1579,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 @@ -1595,7 +1611,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{}% @@ -1715,7 +1731,7 @@ \subsection{Implicitly Induced Getters and Setters} and in which environment (p.\,\pageref{executionOfGenerativeConstructors}, \ref{localVariableDeclaration}, -\ref{bindingActualsToFormals}).% +\ref{bindingFormalsToActuals}).% } \commentary{% @@ -1899,7 +1915,49 @@ \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 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 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, + 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, + and the variable is then bound to 11. + 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$, @@ -1939,7 +1997,7 @@ \section{Functions} Functions abstract over executable actions. \begin{grammar} - ::= \gnewline{} + ::= \gnewline{} ? ::= ? @@ -1982,7 +2040,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. @@ -2002,7 +2061,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{% @@ -2037,12 +2096,12 @@ \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 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.% @@ -2051,16 +2110,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 @@ -2073,7 +2132,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. @@ -2104,16 +2163,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{}% @@ -2171,8 +2230,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{}% @@ -2184,7 +2243,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{}% @@ -2206,11 +2265,41 @@ \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{optionalFormalsAndNamedFormals}). +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}), @@ -2220,7 +2309,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{% @@ -2238,10 +2329,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 @@ -2258,7 +2350,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 @@ -2267,14 +2359,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{}% @@ -2285,54 +2377,42 @@ \subsection{Formal Parameters} the formal parameter scope of $f$. \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. +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 +(\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 \Error{compile-time error} if any default values are specified + in the signature of such a function type. \end{itemize} \begin{grammar} @@ -2356,13 +2436,13 @@ \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 \VAR{} occurs as +It is a \Error{compile-time error} if a formal parameter has +the modifier \CONST{} or the modifier \LATE. +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. @@ -2385,40 +2465,54 @@ \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. -\subsubsection{Optional 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 parameters may be specified and provided with default values. +Optional formal parameters may be specified and provided with default values. +A named formal parameter can be optional, +but if the modifier \REQUIRED{} is present then it is required. \begin{grammar} ::= (`=' )? ::= \gnewline{} \REQUIRED? - \gnewline{} ((`=' | `:') )? + \gnewline{} (`=' )? \end{grammar} -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. +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 +\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 default value of an optional parameter is -not a constant expression (\ref{constants}). -If no default is explicitly specified for an optional parameter -an implicit default of \NULL{} is provided. +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$. \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 parameter begins with an `_' character. \rationale{% @@ -2449,14 +2543,14 @@ \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. \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 @@ -2611,7 +2705,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{}>} @@ -2652,7 +2747,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{}% @@ -2683,8 +2778,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}. @@ -2745,8 +2841,8 @@ \subsection{External Functions} or a non-redirecting constructor (\ref{generativeConstructors}, \ref{factories}). External functions are introduced via the built-in identifier \EXTERNAL{} -(\ref{identifierReference}) -followed by the function signature. +(\ref{identifierExpression}) +followed by the function header. \rationale{% External functions allow us to introduce type information for code @@ -2770,7 +2866,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{% @@ -2797,28 +2893,32 @@ \section{Classes} ::= \ABSTRACT? \CLASS{} ? \gnewline{} ? ? - \gnewline{} `{' ( )* `}' + \gnewline{} \alt \ABSTRACT? \CLASS{} ::= (`,' )* - ::= `;' - \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{} ? @@ -2827,9 +2927,9 @@ \section{Classes} \alt \COVARIANT{} \LATE? \alt \LATE? \FINAL{} ? \alt \LATE? - \alt - \alt ( | )? - \alt ( | )? + \alt + \alt ( | )? + \alt ( | )? ::= \gnewline{} (`,' )* @@ -2888,6 +2988,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. @@ -2929,10 +3031,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$}. @@ -2981,7 +3083,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}). @@ -2999,7 +3101,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}). @@ -3082,7 +3184,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{% @@ -3111,7 +3213,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$ @@ -3130,7 +3232,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$ @@ -3176,7 +3278,7 @@ \subsubsection{Operators} and operator \lit{[]=} which is an instance setter. \begin{grammar} - ::= \gnewline{} + ::= \gnewline{} ? \OPERATOR{} ::= `~' @@ -3194,7 +3296,7 @@ \subsubsection{Operators} \LMHash{}% An operator declaration is identified using the built-in identifier -(\ref{identifierReference}) +(\ref{identifierExpression}) \OPERATOR. \LMHash{}% @@ -3221,9 +3323,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{>}, @@ -3244,7 +3346,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. @@ -3264,15 +3366,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{% @@ -3343,7 +3446,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 @@ -3356,15 +3459,15 @@ \subsubsection{The Method \code{noSuchMethod}} because the actual argument fails to satisfy a type check, but that situation will give rise to a dynamic type error rather than a repeated attempt to invoke \code{noSuchMethod} -(\ref{bindingActualsToFormals}). +(\ref{bindingFormalsToActuals}). 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} \CLASS{} A \{ - noSuchMethod(\COVARIANT{} Null n) => n; + noSuchMethod(\COVARIANT{} Never n) => n; \} \\ \VOID{} main() \{ @@ -3411,7 +3514,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. @@ -3422,6 +3525,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 @@ -3557,11 +3663,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{}% @@ -3698,7 +3804,7 @@ \subsection{Getters} to retrieve the values of object properties. \begin{grammar} - ::= ? \GET{} + ::= ? \GET{} \end{grammar} \LMHash{}% @@ -3734,10 +3840,10 @@ \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{} + ::= ? \SET{} \end{grammar} \commentary{% @@ -3746,15 +3852,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 signature. +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{}% @@ -3767,7 +3878,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, @@ -3775,11 +3886,18 @@ \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. +%% 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$ 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 @@ -3825,7 +3943,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.% @@ -3878,7 +3996,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 @@ -3898,6 +4017,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 \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{}% It is possible for the declaration of an instance variable to include the modifier \COVARIANT{} @@ -3907,6 +4041,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 @@ -3930,7 +4069,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{}% @@ -3973,11 +4112,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} @@ -3990,13 +4131,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.% } @@ -4017,11 +4158,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. @@ -4033,7 +4174,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{}% @@ -4072,7 +4213,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:% @@ -4080,7 +4221,7 @@ \subsubsection{Generative Constructors} \begin{dartCode} class A \{ - int x; + int? x; A([this.x]); \} \end{dartCode} @@ -4091,8 +4232,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} @@ -4155,19 +4296,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}. -It is a compile-time error if the static argument list type +\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{% @@ -4197,23 +4338,24 @@ \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}). \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 @@ -4290,27 +4432,27 @@ \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{}% 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$. -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}, +as specified in Section~\ref{bindingFormalsToActuals}, as if \code{\SUPER} respectively \code{\SUPER.\id} had had the function type of the denoted constructor, %% TODO(eernst): The following is very imprecise, it just serves to remember @@ -4324,38 +4466,44 @@ \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. \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 \Error{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{}% -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. @@ -4366,7 +4514,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. @@ -4391,18 +4539,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$ @@ -4440,11 +4588,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 @@ -4491,17 +4645,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$. @@ -4511,9 +4665,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$, @@ -4526,40 +4680,43 @@ \subsubsection{Factories} \LMHash{}% A \IndexCustom{factory}{constructor!factory} is a constructor prefaced by the built-in identifier -(\ref{identifierReference}) +(\ref{identifierExpression}) \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$>} -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. \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. @@ -4578,7 +4735,7 @@ \subsubsection{Factories} whenever the redirecting constructor is called. \begin{grammar} - ::= \gnewline{} + ::= \gnewline{} \CONST? \FACTORY{} `=' \gnewline{} @@ -4613,14 +4770,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} @@ -4632,7 +4789,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{}% @@ -4642,7 +4799,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{% @@ -4669,7 +4826,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{% @@ -4679,7 +4836,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$. @@ -4704,7 +4862,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{}% @@ -4713,7 +4871,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{% @@ -4763,7 +4921,7 @@ \subsubsection{Constant Constructors} A constant constructor is prefixed by the reserved word \CONST. \begin{grammar} - ::= \gnewline{} + ::= \gnewline{} \CONST{} \end{grammar} @@ -4785,8 +4943,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.% @@ -4795,7 +4957,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. @@ -4812,21 +4974,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. @@ -4884,16 +5046,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 @@ -4901,7 +5070,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} @@ -4916,21 +5085,11 @@ \subsection{Superclasses} the type-parameter scope of $C$. \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}).% -} +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 +(\ref{classBuildingTypes}). \commentary{% The type parameters of a generic class are available in @@ -4958,7 +5117,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} @@ -5030,12 +5189,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 @@ -5123,18 +5281,17 @@ \subsection{Superinterfaces} the type-parameter scope of $C$. \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 -%% TODO(eernst): Refer to nnbd notion of 'same type'. -the \IMPLEMENTS{} clause of a class $C$ specifies the same type $T$. -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$. +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 \Error{compile-time error} if two elements in said type list +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, @@ -5147,7 +5304,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 @@ -5157,7 +5314,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. @@ -5169,7 +5326,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{% @@ -5195,20 +5352,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. @@ -5265,6 +5422,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}); @@ -5308,10 +5466,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} @@ -5327,12 +5485,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 @@ -5344,10 +5503,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.% @@ -5395,10 +5550,10 @@ \section{Interfaces} and property extractions (\ref{propertyExtraction}), any type $T$ which is $T_0$ bounded -(\ref{bindingActualsToFormals}), -where $T_0$ is a class with interface $I$, +(\ref{typesBoundedByTypes}), +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$. @@ -5418,7 +5573,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} @@ -5449,7 +5604,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 @@ -5465,7 +5620,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.% } @@ -5476,8 +5631,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 @@ -5488,12 +5643,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{}% @@ -5512,23 +5662,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: @@ -5540,8 +5692,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} @@ -5563,7 +5716,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{}% @@ -5577,7 +5730,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.% } @@ -5586,7 +5739,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, @@ -5594,7 +5747,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, @@ -5602,39 +5755,34 @@ \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.% +We need to compute one member signature from \Mall.% } \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. +Let \mall{} be the result of applying \TopMergeTypeName{} to +\NormalizedTypeOfName{} of the elements in \Mall. \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.% +Note that these types are mutual subtypes, +and hence the normalized forms are structurally identical, +modulo different choices of top type in some locations.% } -} - -\LMHash{}% 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 @@ -5655,7 +5803,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}) @@ -5740,7 +5888,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{}% @@ -5753,7 +5902,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. @@ -5775,7 +5924,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'$. @@ -5789,14 +5938,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{% @@ -5818,7 +5959,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}. @@ -5841,17 +5982,15 @@ \subsection{Mixin Classes} \end{grammar} \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} +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 +(\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. @@ -5863,8 +6002,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{}% @@ -5897,7 +6037,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 @@ -5913,31 +6053,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. @@ -5945,11 +6060,18 @@ \subsection{Mixin Declaration} \begin{grammar} ::= \MIXIN{} ? \gnewline{} (\ON{} )? ? - \gnewline{} `\{' ( )* `\}' + \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 +\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}), +unless the variable has a \LATE, \ABSTRACT, or \EXTERNAL{} modifier. \LMHash{}% A mixin declaration with no \code{\ON} clause is equivalent @@ -5966,43 +6088,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 \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 +(\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. -It is a compile-time error for the mixin declaration if the $M_S$ -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% -}. +\LMHash{}% +In this declaration, $M_{\metavar{super}}$ is a fresh name. +It is a \Error{compile-time error} for the mixin declaration if the $M_S$ +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 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> @@ -6011,24 +6134,66 @@ \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 +\Error{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 \Error{compile-time error} if +the type of $v$ is potentially non-nullable +(\ref{typeNullability}). \subsection{Mixin Application} @@ -6039,16 +6204,17 @@ \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$. -It is a compile-time error if any of \metavar{members} contains a +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 \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 @@ -6149,7 +6315,7 @@ \section{Extensions} \begin{grammar} ::= \gnewline{} \EXTENSION{} ? ? \ON{} - \gnewline{} `\{' ( )* `\}' + \gnewline{} \end{grammar} \LMHash{}% @@ -6207,7 +6373,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. @@ -6284,7 +6450,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}. @@ -6342,7 +6508,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'. @@ -6404,13 +6570,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{} @@ -6418,7 +6584,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}). @@ -6451,7 +6617,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. @@ -6519,8 +6685,8 @@ \subsection{Explicit Invocation of an Instance Member of an Extension} Let $m$ be the member of $E$ that has the name $n$. Evaluation of $i$ proceeds by evaluating $e$ to an object $o$, -evaluating and binding the actual arguments to the formal parameters -(\ref{bindingActualsToFormals}), +evaluating the actual arguments and binding the formal parameters to them +(\ref{bindingFormalsToActuals}), and finally executing $m$ in a binding environment where \List{X}{1}{k} are bound to \List{t}{1}{k}, \THIS{} is bound to $o$, @@ -6860,7 +7026,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{% @@ -6958,7 +7124,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. @@ -6982,6 +7149,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} @@ -6990,23 +7158,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{}% @@ -7087,7 +7256,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.% } @@ -7131,7 +7300,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}). @@ -7139,7 +7308,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 @@ -7154,7 +7323,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}). @@ -7163,11 +7332,11 @@ \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 -\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 @@ -7178,7 +7347,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{% @@ -7244,7 +7413,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} @@ -7290,8 +7459,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{}% @@ -7328,8 +7497,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{% @@ -7349,8 +7518,9 @@ \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}. -It is a compile-time error if a type parameter is a supertype of its upper bound +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. \commentary{% @@ -7620,10 +7790,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, @@ -7634,8 +7804,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.% @@ -7643,15 +7813,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 @@ -7659,8 +7828,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: @@ -7668,15 +7837,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} @@ -7687,24 +7858,33 @@ \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. +Any use of a type which is not well-bounded is a \Error{compile-time error}. \LMHash{}% -Any use of a type $T$ which is not well-bounded is a 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 +\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 - signature - (\ref{redirectingFactoryConstructors}). -\item $T$ is an immediate subterm of an \EXTENDS{} clause of a class + \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}). + 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 + (\ref{redirectingFactoryConstructors}). +\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? @@ -7732,13 +7912,14 @@ \subsection{Super-Bounded Types} \begin{dartCode} \CLASS{} A \{ X x; + A(this.x); \} \\ -A a; +A a; \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}.% } @@ -7765,7 +7946,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').% @@ -7814,16 +7995,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.% @@ -7847,12 +8020,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{% @@ -7876,7 +8044,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$.% @@ -7925,7 +8093,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.% } @@ -7948,15 +8116,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 @@ -8022,7 +8184,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. @@ -8044,7 +8206,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, @@ -8063,6 +8225,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. @@ -8080,6 +8243,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; @@ -8093,6 +8257,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}$. @@ -8100,10 +8265,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}$>}: @@ -8130,7 +8297,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.] @@ -8145,7 +8312,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.] @@ -8177,7 +8344,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{% @@ -8191,7 +8358,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}). @@ -8200,8 +8367,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} @@ -8209,34 +8376,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{}% @@ -8249,6 +8390,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{% @@ -8300,7 +8443,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. @@ -8491,12 +8634,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$}. @@ -8506,14 +8654,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{% @@ -8568,14 +8720,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. @@ -8594,44 +8745,40 @@ \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}) 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 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.% } - \item A simple or qualified identifier denoting a class, 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 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}) @@ -8643,15 +8790,13 @@ \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}) 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 @@ -8663,30 +8808,29 @@ \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 A constant list literal (\ref{lists}), \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. - \item A constant set literal (\ref{sets}), \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, + 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. - \item A constant map literal (\ref{maps}), \code{\CONST{} <$K$, $V$>\{$k_1$: $v_1$, \ldots, $k_n$: $v_n$\}}, or @@ -8695,13 +8839,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 @@ -8711,12 +8853,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. @@ -8726,13 +8866,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$ @@ -8743,7 +8881,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$ @@ -8754,7 +8891,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 @@ -8762,8 +8898,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 @@ -8772,8 +8908,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. @@ -8782,8 +8918,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 @@ -8791,15 +8927,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$} @@ -8809,8 +8944,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 @@ -8818,74 +8953,43 @@ \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 + (\ref{constantTypes}), 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 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 $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$} is equivalent to \code{!($e$\,\,is\,\,$T$)} in every way, including whether it's potentially constant or constant. \end{itemize} -\LMHash{}% -A -\Index{constant type expression} -is one of: -\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. -\item - A type of the form \code{FutureOr<$T$>} - where $T$ is a 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})} - (where $R$ and \code{<\metavar{typeParameters}>} may be omitted) - and where $R$, \metavar{typeParameters} and \metavar{argumentTypes} - (if present) contain only constant type expressions. -\item - The type \VOID. -\item - The type \DYNAMIC. -\end{itemize} - % Being potentially constant is entirely structural, not type based, % but the program still has to satisfy strong-mode typing. @@ -8895,19 +8999,16 @@ \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 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{% @@ -8923,6 +9024,122 @@ \subsection{Constants} \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$ + 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 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 + 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, + %% but that is not covered by the current rules: `X` is a type variable, + %% 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 + respectively a constant type + if $R$, \metavar{typeParameters}, and \metavar{parameterTypes} + (if present) contain only potentially constant types + respectively constant types. +\item + The type \VOID{} is a potentially constant and constant type. +\item + The type \DYNAMIC{} is a potentially constant and constant type. +\item + A type variable is a potentially constant type. +\end{itemize} + + +\subsubsection{Type Variable Elimination in Constants} +\LMLabel{typeVariableEliminationInConstants} + +\LMHash{}% +\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{}% +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{% +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\ 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} @@ -9156,8 +9373,11 @@ \subsection{Null} 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 extends the \code{Object} class -and declares no methods except those also declared by \code{Object}. +%% Deep weasel talk here: The clean model would be to have a sealed top class +%% `Any`, with `Null` and `Object` as the immediate subtypes. The only +%% special rule we would then need would be that `Any <: Object?`. +The \code{Null} class implements the same member signatures +as the \code{Object} class. \commentary{% The null object has primitive equality @@ -9168,6 +9388,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} @@ -9209,9 +9433,16 @@ \subsection{Numbers} is either a hexadecimal integer literal or a decimal integer literal. \LMHash{}% +%% 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, 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 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}. @@ -9235,14 +9466,16 @@ \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 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} @@ -9253,7 +9486,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{}% @@ -9264,7 +9498,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{% @@ -9278,11 +9512,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}. @@ -9295,22 +9529,31 @@ \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}. -It is a compile-time error for a class to +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}. \commentary{% @@ -9342,7 +9585,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} ::= @@ -9394,24 +9638,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:% @@ -9424,7 +9669,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% } @@ -9449,17 +9694,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{}% @@ -9544,10 +9789,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{`\\'}, @@ -9555,11 +9800,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.% } @@ -9577,7 +9823,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 @@ -9605,9 +9851,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}} @@ -9623,21 +9869,21 @@ \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. \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. @@ -9650,7 +9896,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. @@ -9677,12 +9923,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} @@ -9699,12 +9945,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$. @@ -9745,8 +9993,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. @@ -9900,6 +10148,14 @@ \subsection{Collection Literals} how to obtain zero or more entities, of the form \synt{ifElement} or \synt{forElement}. +\LMHash{}% +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{% Terms derived from \synt{element}, and the ability to build collections from them, @@ -10076,7 +10332,7 @@ \subsection{Collection Literals} %% Same text again. -\subsubsection{Type Promotion} +\subsubsection{Collection Literal Type Promotion} \LMLabel{collectionLiteralTypePromotion} \LMHash{}% @@ -10245,10 +10501,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 @@ -10330,7 +10583,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, @@ -10385,12 +10638,13 @@ \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{}% \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. @@ -10414,7 +10668,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, @@ -10422,6 +10677,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 @@ -10433,7 +10689,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$, @@ -10464,20 +10720,14 @@ \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): 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$ -%% 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 \NonNullType{$S'$}. \begin{itemize} \item @@ -10488,10 +10738,11 @@ \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 compile-time error occurs. + Otherwise, a \Error{compile-time error} occurs. \end{itemize} \vspace{-5mm} \EndCase @@ -10503,7 +10754,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$. @@ -10511,24 +10762,26 @@ \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{}% \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 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$)\,\,\{\}}, +\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$.% } @@ -10554,6 +10807,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$. @@ -10562,8 +10816,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? @@ -10602,9 +10857,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$. @@ -10615,7 +10870,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{% @@ -10639,7 +10894,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{} @@ -10652,12 +10908,12 @@ \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}). +is not a constant type +(\ref{constantTypes}). \rationale{% The binding of a formal type parameter of an enclosing class or function @@ -10697,6 +10953,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}]} @@ -10739,7 +11002,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 \synt{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, @@ -10750,15 +11051,15 @@ \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. -%% TODO(eernst): Define `greatest closure' of a context type -%% when we define `context type'. +\BlindDefineSymbol{S}% 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 @@ -10774,12 +11075,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{}% @@ -10792,7 +11088,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}) @@ -10804,7 +11100,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. @@ -10846,6 +11142,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} @@ -10889,6 +11186,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$, @@ -10899,6 +11197,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$>}, @@ -10928,6 +11227,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, @@ -10954,6 +11254,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} @@ -10993,6 +11294,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 @@ -11005,6 +11307,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$>}$. @@ -11039,19 +11342,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 compile-time error occurs. + 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 \NonNullType{$S'$}. +Next: \begin{itemize} \item @@ -11066,15 +11373,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 compile-time error occurs. + 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 \NonNullType{$S'$}. +Next: \begin{itemize} \item @@ -11093,21 +11408,24 @@ \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 compile-time error occurs. + Otherwise, a \Error{compile-time error} occurs. \end{itemize} \vspace{-5mm} \EndCase \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 @@ -11118,6 +11436,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. @@ -11128,6 +11447,7 @@ \subsubsection{Set and Map Literal Inference} a \DYNAMIC{} spread in either branch does not cause the error to occur.% } +\LMHash{}% Then: \begin{itemize} @@ -11135,7 +11455,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)$ @@ -11143,32 +11464,35 @@ \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 \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. -The same compile-time errors occur for $\ell$ as +\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, @@ -11197,6 +11521,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$. @@ -11208,8 +11533,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$ @@ -11230,13 +11556,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. @@ -11259,15 +11586,16 @@ \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 compile-time error occurs. + Otherwise, a \Error{compile-time error} occurs. \commentary{In this case the literal cannot be disambiguated.} \end{itemize} \end{itemize} @@ -11327,16 +11655,16 @@ \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$. \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{} @@ -11376,7 +11704,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{} @@ -11389,20 +11718,21 @@ \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}). +is not a constant type +(\ref{constantTypes}). \rationale{% The binding of a formal type parameter of an enclosing class or function @@ -11434,9 +11764,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 @@ -11449,6 +11780,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: @@ -11512,9 +11850,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 @@ -11573,7 +11911,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{} @@ -11586,19 +11925,20 @@ \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}). +is not a constant type +(\ref{constantTypes}). \rationale{% The binding of a formal type parameter of an enclosing class or function @@ -11643,6 +11983,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}\}} @@ -11684,8 +12031,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 \Error{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{}% @@ -11694,18 +12048,20 @@ \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{}% -If $v$ is the null object (\ref{null}), then a \code{NullThrownError} is thrown. +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}). \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}. @@ -11715,9 +12071,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} @@ -11746,17 +12099,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} @@ -11939,7 +12286,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$. @@ -11975,7 +12322,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$. @@ -12073,7 +12420,7 @@ \subsection{Function Expressions} \noindent is %% TODO(eernst): Adjust to take type inference into account. -\FunctionTypeNamedStdCr{\code{Future}}. +\FunctionTypeNamedStd{\code{Future}}. \EndCase \LMHash{}% @@ -12089,7 +12436,7 @@ \subsection{Function Expressions} \noindent is %% TODO(eernst): Adjust to take type inference into account. -\FunctionTypeNamedStdCr{\code{Stream}}. +\FunctionTypeNamedStd{\code{Stream}}. \EndCase \LMHash{}% @@ -12105,7 +12452,7 @@ \subsection{Function Expressions} \noindent is %% TODO(eernst): Adjust to take type inference into account. -\FunctionTypeNamedStdCr{\code{Iterable}}. +\FunctionTypeNamedStd{\code{Iterable}}. \EndCase \LMHash{}% @@ -12139,7 +12486,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}). @@ -12151,7 +12498,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. @@ -12174,20 +12521,20 @@ \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 -\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}). @@ -12207,14 +12554,14 @@ \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 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{% @@ -12234,13 +12581,13 @@ \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}$)} - it is a compile-time error if \code{$R$.\id} is not the name of + \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}$)} - it is a compile-time error if $R$ is not the name of + \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} @@ -12248,15 +12595,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, @@ -12266,7 +12613,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'$ @@ -12281,7 +12628,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{% @@ -12298,13 +12645,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$.} @@ -12320,7 +12667,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{}% @@ -12353,10 +12700,10 @@ \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}$)} +\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$. @@ -12372,15 +12719,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.% @@ -12403,24 +12753,24 @@ \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 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{}% @@ -12434,19 +12784,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 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 + \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}$)} - it is a compile-time error if $R$ is not the name of + \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} @@ -12458,18 +12809,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}). @@ -12478,7 +12829,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{% @@ -12493,11 +12844,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'$, @@ -12512,9 +12863,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.% } @@ -12550,7 +12901,15 @@ \subsubsection{Const} } \LMHash{}% -It is a compile-time error if evaluation of a constant object +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. \commentary{% @@ -12571,7 +12930,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} @@ -12710,19 +13069,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 false, + $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 false. + 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{}% @@ -12779,6 +13141,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 @@ -12852,22 +13215,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{} @@ -12887,23 +13254,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 @@ -12911,7 +13279,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 @@ -12970,7 +13338,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{}% @@ -12990,8 +13358,8 @@ \subsubsection{Actual Argument List Evaluation} } -\subsubsection{Binding Actuals to Formals} -\LMLabel{bindingActualsToFormals} +\subsubsection{Binding Formals to Actuals} +\LMLabel{bindingFormalsToActuals} \commentary{% In the following, the non-generic case is covered implicitly: @@ -13003,7 +13371,11 @@ \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. +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, @@ -13038,44 +13410,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 +Otherwise, it is a \Error{compile-time error} if the static type of $f$ is not function-type bounded. +\LMHash{}% +A \Error{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; @@ -13083,11 +13430,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$, @@ -13098,7 +13445,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.% } @@ -13129,7 +13477,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> @@ -13137,21 +13485,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} @@ -13164,29 +13512,36 @@ \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$. -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 \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 + $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{}% 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{% @@ -13222,26 +13577,26 @@ \subsubsection{Binding Actuals to Formals} and let $p_{h+1}, \ldots, p_{h+k}$ be the optional parameters declared by $f$. \LMHash{}% -An evaluated actual argument part +With a given 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: +the formal type parameters and formal parameters of $f$ are bound as follows: \LMHash{}% % Passing a wrong number of actual type arguments. 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 @@ -13258,14 +13613,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{% @@ -13281,16 +13636,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_{m+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$. +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} @@ -13300,7 +13651,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. @@ -13335,7 +13686,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, @@ -13376,11 +13727,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 compile-time error occurs. +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 @@ -13416,7 +13769,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. @@ -13429,7 +13782,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 @@ -13446,7 +13800,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 @@ -13464,7 +13818,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{% @@ -13482,17 +13836,17 @@ \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 \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 -in Section~\ref{bindingActualsToFormals}, +in Section~\ref{bindingFormalsToActuals}, using $F$ as the static type of the invoked function, and the static type of $i$ is as specified there. @@ -13500,7 +13854,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$. @@ -13508,11 +13862,11 @@ \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 -as specified in Section~\ref{bindingActualsToFormals}, +is evaluated by binding formals to actuals +as specified in Section~\ref{bindingFormalsToActuals}, and executing the body of $f$ with those bindings; the returned result is then the result of evaluating $i$. @@ -13523,7 +13877,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. @@ -13540,12 +13894,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{}% @@ -13571,7 +13925,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 @@ -13594,7 +13948,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'$. @@ -13610,13 +13964,13 @@ \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{}% -An invocation of $o$ with a given argument list will bind actuals to formals +An invocation of $o$ with a given argument list will bind formals to actuals in the same way as an invocation of $f$ -(\ref{bindingActualsToFormals}), +(\ref{bindingFormalsToActuals}), and then execute the body of $f$ in the captured scope amended with the bound parameter scope, yielding the same completion @@ -13627,13 +13981,22 @@ \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 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} @@ -13657,7 +14020,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.% } @@ -13678,7 +14042,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}'' @@ -13686,9 +14050,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}. @@ -13702,7 +14066,7 @@ \subsection{Generic Function Instantiation} \ref{initializerLists}, \ref{new}, \ref{const}, -\ref{bindingActualsToFormals}, +\ref{bindingFormalsToActuals}, \ref{assignment}, \ref{localVariableDeclaration}, \ref{switch}, @@ -13713,7 +14077,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}. @@ -13724,7 +14090,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{}% @@ -13741,8 +14107,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 @@ -13756,12 +14120,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 @@ -13775,18 +14141,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.% } @@ -13824,7 +14193,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$} @@ -13892,11 +14261,11 @@ \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, +Per the rules for identifier expressions, an identifier will not be treated as a top-level getter invocation unless the getter $i$ is defined.% } @@ -13926,11 +14295,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} @@ -13942,8 +14309,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{[]} \\ @@ -14051,11 +14417,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$} \\ @@ -14076,11 +14440,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} \\ @@ -14123,36 +14485,100 @@ \subsection{Member Invocations} \end{figure} \LMHash{}% -A composite member invocation is an abbreviated form -whose meaning is reduced to simple member invocations -as shown in Fig.~\ref{fig:desugarCompositeMemberInvocations}. -This step is known as a \Index{desugaring} transformation, -and we say that the resulting expression has been \Index{desugared}. -Fig.~\ref{fig:desugarCompositeMemberInvocations} contains -several occurrences of $r'$ -which is the -\IndexCustom{replacement receiver}{method invocation!replacement receiver} -of the composite method invocation. -The meaning of each occurrence of $r'$ is determined as follows: - -\LMHash{}% -When the receiver $r$ is an extension application -(\ref{explicitExtensionInvocations}) -of the form \code{$E$<\List{T}{1}{k}>($e_r$)} -(\commentary{where $k = 0$ means that the type argument list is absent}): -Let $v_r$ be a fresh variable bound to the value of $e_r$ -and with the same static type as $e_r$, -then $r'$ is \code{$E$<\List{T}{1}{k}>($v_r$)} when it occurs -as the receiver of a member invocation, -and otherwise $r'$ is $v_r$. +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{}% -When $r$ is not an extension application, -$r'$ is a fresh variable bound to the value of $r$, -with the same static type as $r$. +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{% -This corresponds to an extra outermost \LET{} in each rule +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 +as shown in Fig.~\ref{fig:desugarCompositeMemberInvocations}. +This step is known as a \Index{desugaring} transformation, +and we say that the resulting expression has been \Index{desugared}. +Fig.~\ref{fig:desugarCompositeMemberInvocations} contains +several occurrences of $r'$ +which is the +\IndexCustom{replacement receiver}{method invocation!replacement receiver} +of the composite method invocation. +The meaning of each occurrence of $r'$ is determined as follows: + +\LMHash{}% +When the receiver $r$ is an extension application +(\ref{explicitExtensionInvocations}) +of the form \code{$E$<\List{T}{1}{k}>($e_r$)} +(\commentary{where $k = 0$ means that the type argument list is absent}): +Let $v_r$ be a fresh variable bound to the value of $e_r$ +and with the same static type as $e_r$, +then $r'$ is \code{$E$<\List{T}{1}{k}>($v_r$)} when it occurs +as the receiver of a member invocation, +and otherwise $r'$ is $v_r$. + +\LMHash{}% +When $r$ is not an extension application, +$r'$ is a fresh variable bound to the value of $r$, +with the same static type as $r$. + +\commentary{% +This corresponds to an extra outermost \LET{} in each rule in Fig.~\ref{fig:desugarCompositeMemberInvocations} where $r'$ occurs, and an explicit distinction between the two forms of $r$, @@ -14179,55 +14605,62 @@ \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 -\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 +\code{$e$?.$m$<\TypeArgumentListStd>(\ArgumentListStd)}. \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{}% -The static type of $i$ is the same as the static type of - -\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 - -\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}$)} +A warning occurs if $e$ is a type literal, +and $i$ is then treated as \noindent -are also generated in the case of $i$. +\code{% +$e$.$m$<\TypeArgumentListStd>(\ArgumentListStd)}. \LMHash{}% -Evaluation of $i$ proceeds as follows: +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$}: -\LMHash{}% -If $e$ is a type literal or denotes an extension, $i$ is treated as +\noindent +\code{% +$v$.$m$<\TypeArgumentListStd>(\ArgumentListStd)}. \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 $T$ be the static type of $i'$. +The static type of $i$ is then \code{$T$?}. \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$. +The dynamic semantics of $i$ is determined by the semantics of other constructs, +because all conditional ordinary method invocations are eliminated during +the null-shorting transformation +(\ref{nullShorting}). \EndCase \LMHash{}% \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}$)}, -where $C$ is a type literal, or $C$ denotes an extension. +\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. \commentary{% Non-generic invocations arise as the special case @@ -14235,27 +14668,37 @@ \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 \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 \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 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 -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. +as specified in Section~\ref{bindingFormalsToActuals}, +considering $F$ to be the static type of the function to call. +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: @@ -14263,8 +14706,8 @@ \subsubsection{Ordinary Invocation} \LMHash{}% If the denoted member of $i$ is a static method, let $f$ be the function declared by that member. -The binding of actual arguments to formal parameters is performed -as specified in Section~\ref{bindingActualsToFormals}. +The binding of formal parameters to actual arguments is performed +as specified in Section~\ref{bindingFormalsToActuals}. The body of $f$ is then executed with respect to the bindings that resulted from the evaluation of the argument part. The value of $i$ is the object returned by the execution of $f$'s body. @@ -14274,15 +14717,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. @@ -14292,11 +14738,11 @@ \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 -(\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 @@ -14305,7 +14751,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: @@ -14318,23 +14764,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, @@ -14349,7 +14802,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. @@ -14359,6 +14812,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$, @@ -14378,14 +14832,14 @@ \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. \LMHash{}% Otherwise, the static analysis of $i$ is performed -as specified in Section~\ref{bindingActualsToFormals}, +as specified in Section~\ref{bindingFormalsToActuals}, considering $F$ to be the static type of the function to call, and the static type of $i$ is as specified there, except that invocations of methods named \code{remainder} @@ -14448,8 +14902,8 @@ \subsubsection{Ordinary Invocation} \end{itemize} \LMHash{}% -It is a 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.% } @@ -14464,6 +14918,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()}), @@ -14485,10 +14940,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}) @@ -14496,8 +14954,8 @@ \subsubsection{Ordinary Invocation} \LMHash{}% If the method lookup succeeded, -the binding of actual arguments to formal parameters is performed -as specified in Section~\ref{bindingActualsToFormals}. +the binding of formal parameters to actual arguments is performed +as specified in Section~\ref{bindingFormalsToActuals}. The body of $f$ is then executed with respect to the bindings that resulted from the evaluation of the argument list, and with \THIS{} bound to $o$. @@ -14515,9 +14973,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: @@ -14532,11 +14992,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{}% @@ -14625,7 +15086,15 @@ \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 if the static type of $e$ is non-nullable +(\ref{typeNullability}). + +\LMHash{}% +Otherwise, the static analysis and dynamic semantics of +a cascaded member access is specified in terms of the following desugaring step. \LMHash{}% @@ -14640,6 +15109,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} @@ -14669,7 +15151,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 @@ -14679,10 +15161,10 @@ \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}, +in the built-in class \code{Object}, in a factory constructor, or in a static method or variable initializer. @@ -14705,12 +15187,12 @@ \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}), the static analysis of $i$ is performed -as specified in Section~\ref{bindingActualsToFormals}, +as specified in Section~\ref{bindingFormalsToActuals}, considering the function to have static type $F$, and the static type of $i$ is as specified there. @@ -14728,13 +15210,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$.% @@ -14769,9 +15251,9 @@ \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}$)} -as specified in Section~\ref{bindingActualsToFormals}, +Otherwise perform the binding of formal parameters to actual arguments for +\code{$f$<\TypeArgumentListStd>(\ArgumentListStd)} +as specified in Section~\ref{bindingFormalsToActuals}, and execute the body of $f$ with said bindings plus a binding of \THIS{} to $o$. The result returned by $f$ is then the result of evaluating $i$. @@ -14806,7 +15288,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} @@ -14822,48 +15304,50 @@ \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. \LMHash{}% -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}. - -\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}. +A warning occurs if $e$ is a type literal, +and $i$ is then treated as \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$. +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{}% +The dynamic semantics of $i$ is determined by the semantics of other constructs, +because all conditional property extraction expressions are eliminated during +the null shorting transformation +(\ref{nullShorting}). \EndCase \LMHash{}% \Case{Static} +\BlindDefineSymbol{i, C, \id}% Let \id{} be an identifier; 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 +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$. @@ -14871,7 +15355,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} @@ -14893,17 +15377,17 @@ \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{}% \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{% @@ -14912,19 +15396,21 @@ \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 -\subsubsection{Getter Access and Method Extraction} +\subsubsection{Getter Invocation and Method Closurization} \LMLabel{getterAccessAndMethodExtraction} \LMHash{}% 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. @@ -14946,11 +15432,11 @@ \subsubsection{Getter Access and Method Extraction} } \LMHash{}% -Let $T$ be the static type of $e$. +Let \DefineSymbol{T} be the static type of $e$. \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 @@ -14959,11 +15445,11 @@ \subsubsection{Getter Access and Method Extraction} 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: @@ -14978,8 +15464,8 @@ \subsubsection{Getter Access and Method Extraction} 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} @@ -15022,7 +15508,7 @@ \subsubsection{Getter Access and Method Extraction} (\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, @@ -15061,7 +15547,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: @@ -15101,7 +15587,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 @@ -15133,38 +15619,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}>($\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 -\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{}% @@ -15194,7 +15675,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 the built-in class \code{Object}. +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 @@ -15207,21 +15692,25 @@ \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 +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$. 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$. -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 @@ -15231,7 +15720,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{% @@ -15242,10 +15731,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.% } @@ -15300,7 +15791,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 @@ -15348,7 +15839,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 the built-in class \code{Object}. +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 @@ -15361,21 +15856,25 @@ \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 +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$. 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$. -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 @@ -15386,7 +15885,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. @@ -15416,7 +15915,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:} @@ -15427,7 +15928,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() \{ @@ -15438,8 +15939,8 @@ \subsubsection{Generic Method Instantiation} \commentary{% \noindent -The function object which is stored in \code{f} at the end of \code{main} -has dynamic type \code{int\,\,\FUNCTION(int,\,[List])}, +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}'' to the denoted generic instance method, @@ -15485,7 +15986,7 @@ \subsubsection{Generic Method Instantiation} \ref{initializerLists}, \ref{new}, \ref{const}, -\ref{bindingActualsToFormals}, +\ref{bindingFormalsToActuals}, \ref{assignment}, \ref{localVariableDeclaration}, \ref{switch}, @@ -15496,7 +15997,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}. @@ -15588,7 +16091,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{}% @@ -15623,8 +16126,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$. % @@ -15645,12 +16147,414 @@ \subsubsection{Generic Method Instantiation} } +\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 +\Index{null shorting}. +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, +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 is used during 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} is a base-level expression.} + +\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 $e$ +into a meta-level function \metaCode{F} of type +\metaCode{(Exp\,\,$\rightarrow$\,\,Exp)\,\,$\rightarrow$\,\,Exp}, +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. + +\LMHash{}% +Let +\IndexCustom{\metaCode{ID}}{null shorting!\metaCode{ID}} +be the identity function +\metaCode{fn[x:\,\,Exp]:\,\,Exp\,\,=>\,\,x}. + +\LMHash{}% +The +\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(\textcolor{normativeColor}{$e$})}}{% + null shorting!\metaCode{EXP(\textcolor{normativeColor}{$e$})}} +as a shorthand for +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(\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 \metavar{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 (\metaCode{k}). +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{\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 +\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 + \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} \LMHash{}% An assignment changes the value associated with a variable, -or invokes a setter. +or it invokes a setter. \begin{grammar} ::= `=' @@ -15681,17 +16585,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 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$ - which is not a local variable, - it is guaranteed to be a setter - (\commentary{that may be explicit or induced implicitly by a variable}) - because other declarations do not have a name + 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 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$}. @@ -15701,8 +16631,8 @@ \subsection{Assignment} proceeds as specified elsewhere.% } - Otherwise, a 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 @@ -15731,37 +16661,59 @@ \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 \DynamicError{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=} @@ -15779,7 +16731,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$ @@ -15788,7 +16740,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: @@ -15799,20 +16750,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 @@ -15821,9 +16785,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$. @@ -15878,9 +16842,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$. @@ -15906,9 +16870,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 @@ -15917,12 +16880,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$. @@ -15944,11 +16908,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$. @@ -15965,11 +16931,18 @@ \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$. +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$} @@ -15981,58 +16954,78 @@ \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$. -The static type of $a$ is the least upper bound of -the static type of \code{$C$.$v$} and the static type of $e$. +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 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$.$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$. -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$. +\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 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$.$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 []}'. -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$. +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 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 @@ -16049,148 +17042,176 @@ \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$. -The static type of $a$ is the least upper bound of -the static type of \code{\SUPER.$v$} and the static type of $e$. +\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 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.$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. -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$. +\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{}% -Evaluation of a compound assignment $a$ of the form \code{$e_1$?.$v$ ??= $e_2$} +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 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 +\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$}. -\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{$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{$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$}. +\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{$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} \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} @@ -16199,34 +17220,25 @@ \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 standard upper bound +(\ref{standardUpperBoundsAndStandardLowerBounds}) +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$. -% This error can occur due to implicit casts and null. -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 +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 \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$. - -\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 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} @@ -16241,7 +17253,16 @@ \subsection{If-null Expressions} \end{grammar} \LMHash{}% -Evaluation of 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.} +The static type of $e$ is the standard upper bound +(\ref{standardUpperBoundsAndStandardLowerBounds}) +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$} proceeds as follows: \LMHash{}% @@ -16250,10 +17271,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} @@ -16276,66 +17293,39 @@ \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$. -% This error can occur due to implicit casts and null. -It is a dynamic error if the run-time type of $o_1$ is not \code{bool}. +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 \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 implicit casts and null. -It is a dynamic error if the run-time type of $o_2$ is not \code{bool}. +% This error can occur due to an implicit downcast from \DYNAMIC. +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{}% -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. -It is a dynamic error if the run-time type of $o_1$ is not \code{bool}. +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 \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 implicit casts and null. -It is a dynamic error if the run-time type of $o_2$ is not \code{bool}. +% This error can occur due to an implicit downcast from \DYNAMIC. +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{}% -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 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} @@ -16359,31 +17349,74 @@ \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 \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{}% +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 If either \THIS{} or $o$ is the null object (\ref{null}), - then $ee$ 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 + 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} @@ -16400,20 +17433,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} @@ -16439,10 +17458,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} @@ -16475,10 +17494,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 @@ -16513,10 +17532,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 @@ -16552,10 +17571,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 @@ -16621,13 +17640,13 @@ \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 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} @@ -16722,7 +17741,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{}% @@ -16730,7 +17749,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 @@ -16740,10 +17759,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} @@ -16834,7 +17853,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.% } @@ -16867,11 +17886,31 @@ \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 non-null assertion; 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{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 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 \DynamicError{dynamic error} occurs, +otherwise $e$ evaluates to $o$. +\EndCase \LMHash{}% \Case{Constructor Invocations} @@ -16879,7 +17918,7 @@ \subsection{Postfix Expressions} \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 @@ -16903,15 +17942,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$. @@ -16937,16 +17976,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$. @@ -16965,16 +18005,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$. @@ -16995,19 +18035,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$. @@ -17024,24 +18064,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 @@ -17132,8 +18183,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{}% @@ -17193,7 +18243,7 @@ \subsection{Lexical Lookup} } \LMHash{}% -\BlindDefineSymbol{n, id}% +\BlindDefineSymbol{n, \id}% Consider the situation where a name $n$ has basename \id{} (\ref{classMemberConflicts}) @@ -17253,7 +18303,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}). @@ -17270,12 +18320,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 @@ -17331,7 +18381,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, @@ -17373,8 +18423,8 @@ \subsection{Lexical Lookup} } -\subsection{Identifier Reference} -\LMLabel{identifierReference} +\subsection{Identifier Expression} +\LMLabel{identifierExpression} \LMHash{}% An \Index{identifier expression} consists of a single identifier; @@ -17452,11 +18502,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.% } @@ -17471,9 +18521,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 @@ -17484,10 +18535,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{} @@ -17502,15 +18555,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$. @@ -17527,13 +18579,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 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: + \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 @@ -17550,8 +18625,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} @@ -17559,7 +18634,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. @@ -17607,7 +18682,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$ @@ -17685,7 +18761,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} ::= @@ -17694,33 +18770,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 whenever +$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$. % @@ -17740,7 +18827,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. @@ -17757,9 +18844,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} @@ -17774,18 +18858,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} @@ -17946,7 +19029,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}). @@ -18038,7 +19122,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}).% } @@ -18155,26 +19239,33 @@ \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 +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 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{% +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} @@ -18184,7 +19275,7 @@ \subsection{Local Function Declaration} (\ref{functionDeclarations}). \begin{grammar} - ::= + ::= \end{grammar} \LMHash{}% @@ -18193,7 +19284,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, @@ -18251,21 +19343,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% @@ -18273,17 +19370,16 @@ \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} 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. @@ -18300,37 +19396,22 @@ \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: - -\LMHash{}% -First, the expression $b$ 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 \TRUE, then the block statement $s_1$ is executed, -otherwise the block statement $s_2$ is executed. - -\LMHash{}% -It is a 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} +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{}% -An if statement of the form \code{\IF{} ($e$) $s$} is equivalent to -the if statement \code{\IF{} ($e$) $s$ \ELSE{} \{\}}. +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 \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$. \subsection{For} @@ -18360,6 +19441,40 @@ \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 \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}. + +\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 \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 +%% 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: @@ -18378,8 +19493,9 @@ \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. - It is a dynamic error if the run-time type of $o$ is not \code{bool}. + % This error can occur due to an implicit downcast from \DYNAMIC. + 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 @@ -18415,31 +19531,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: +\subsubsection{For-in} +\LMLabel{for-in} -%\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{?}\ and +consider a \FOR{} statement $S_{\metavar{for}}$ of the form +\code{\FOR\ ($D$\,\,\id\,\,\IN\,\,$e$)\ $S$}. +A \Error{compile-time error} occurs if $D$ contains +the modifier \CONST{} or the modifier \LATE. \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, +Otherwise, $S_{\metavar{for}}$ is treated as the following code, where $\id_1$ and $\id_2$ are fresh identifiers: \begin{normativeDartCode} @@ -18452,21 +19556,20 @@ \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>}. +It is a \Error{compile-time error} if $T$ is not assignable to +\code{Iterable}. \commentary{% 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<\DYNAMIC>}.% +which is not a subtype of \code{Iterable}.% } @@ -18480,17 +19583,33 @@ \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 \Error{compile-time error} occurs if $D$ contains +the modifier \CONST{} or the modifier \LATE. +It is a \Error{compile-time error} if an asynchronous for-in statement appears +inside a synchronous function (\ref{functions}). +It is a \Error{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$. -% 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 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{}% @@ -18570,18 +19689,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} @@ -18600,8 +19707,9 @@ \subsection{While} \LMHash{}% The expression $e$ 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}. +% This error can occur due to an implicit downcast from \DYNAMIC. +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 @@ -18623,7 +19731,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}. @@ -18651,14 +19759,15 @@ \subsection{Do} \LMHash{}% Then, the expression $e$ 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}. +% This error can occur due to an implicit downcast from \DYNAMIC. +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. \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}. @@ -18746,9 +19855,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{% +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{% -The \SWITCH{} statement should only be used in -very limited situations (e.g., interpreters or scanners).% +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{}% @@ -18763,6 +19911,7 @@ \subsection{Switch} \} \end{normativeDartCode} +\noindent or the form \begin{normativeDartCode} @@ -18773,29 +19922,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$) \{ @@ -18806,23 +19956,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$) \{ @@ -18832,81 +19985,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} @@ -18990,8 +20082,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: @@ -19037,137 +20129,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 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} @@ -19186,39 +20276,74 @@ \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 factory constructor +(\ref{factories}). % % 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$;}, -$T$ is neither \VOID, \DYNAMIC, nor \code{Null}, +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$. \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 @@ -19234,54 +20359,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. -It is a compile-time error if $s$ is \code{\RETURN;}, -unless \flatten{T} -(\ref{functionExpressions}) +% Returning without an object is only ok with a "voidy" future value type. +It is a \Error{compile-time error} if $s$ is \code{\RETURN;}, +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 \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$;}, -\flatten{T} is neither \VOID, \DYNAMIC, nor \code{Null}, -and \flatten{S} is \VOID. +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$;}, -\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 \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$. +\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.% @@ -19294,8 +20409,8 @@ \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. +It is a \Error{compile-time error} if a return statement of +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 @@ -19307,8 +20422,8 @@ \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 +It is a \Error{compile-time error} if a return statement of +the form \code{\RETURN\,\,$e$;} appears in a generative constructor (\ref{generativeConstructors}). \rationale{% @@ -19324,65 +20439,63 @@ \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 \DynamicError{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.% +} +\EndCase \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 \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$ +(\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}). @@ -19440,10 +20553,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 @@ -19470,13 +20584,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. @@ -19503,9 +20617,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}). @@ -19599,7 +20713,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{}% @@ -19607,10 +20721,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{}% @@ -19626,7 +20740,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 @@ -19661,7 +20775,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 @@ -19707,7 +20821,7 @@ \subsection{Assert} \begin{grammar} ::= `;' - ::= \ASSERT{} `(' (`,' )? `,'? `)' + ::= \ASSERT{} `(' (`,' )? `,'? `)' \end{grammar} \LMHash{}% @@ -19734,7 +20848,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 @@ -19749,7 +20863,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{% @@ -19787,12 +20901,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? `;' @@ -19813,6 +20927,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 @@ -19893,7 +21010,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. @@ -19926,7 +21043,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{}% @@ -19959,9 +21076,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{}% @@ -19972,7 +21089,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';} @@ -20249,7 +21366,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. @@ -20259,22 +21376,23 @@ \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. + 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 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. + 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{% @@ -20291,7 +21409,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, @@ -20404,7 +21523,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{}% @@ -20447,7 +21566,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}). @@ -20731,13 +21850,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{}% @@ -20751,7 +21870,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{% @@ -20769,51 +21888,74 @@ \subsection{Scripts} \LMLabel{scripts} \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. +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}. -A script $S$ is executed as follows: +\LMHash{}% +A \Index{script} is a library that exports a declaration named \code{main}. +A script $L$ is executed as follows: \LMHash{}% -First, $S$ is compiled as a library as specified above. +First, $L$ 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, +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{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} + +\begin{itemize} +\item + An object whose run-time type is a subtype of \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 \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} + +\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{}% 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{}% +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 \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. \LMHash{}% -It is a 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.} +A Dart program will typically be executed by executing a script. +The procedure whereby this script is chosen is implementation specific. \subsection{URIs} @@ -20833,7 +21975,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. @@ -20922,7 +22064,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 @@ -20965,7 +22108,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{% @@ -21023,8 +22166,8 @@ \subsection{Static Types} ::= `[' `,'? `]' - ::= - `\{' (`,' )* `,'? `\}' + ::= \gnewline{} + `{' (`,' )* `,'? `}' ::= \REQUIRED? @@ -21056,7 +22199,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, @@ -21087,12 +22230,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. @@ -21105,34 +22249,6 @@ \subsection{Static Types} % objects in constructor args - these cannot represent parameterized types. -\subsubsection{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} @@ -21169,7 +22285,7 @@ \subsection{Dynamic Type System} \ref{redirectingFactoryConstructors}, \ref{lists}, \ref{new}, -\ref{bindingActualsToFormals}, +\ref{bindingFormalsToActuals}, \ref{ordinaryInvocation}, \ref{assignment}, \ref{typeCast}, @@ -21185,45 +22301,13 @@ \subsection{Dynamic Type System} } \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, +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{% @@ -21275,8 +22359,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 @@ -21292,15 +22377,15 @@ \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{}% 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{}% @@ -21360,7 +22445,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. @@ -21395,7 +22480,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 @@ -21407,9 +22492,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{}% @@ -21565,14 +22649,14 @@ \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:} \begin{dartCode} -\CLASS{} C \{\} +\CLASS{} C \{\} \TYPEDEF{} F = C; \TYPEDEF{} G = Z; \\ @@ -21633,128 +22717,188 @@ \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.% +} % Subtype Rule Numbering \newcommand{\SrnReflexivity}{1} -\newcommand{\SrnTop}{2} -\newcommand{\SrnBottom}{3} -\newcommand{\SrnNull}{4} -\newcommand{\SrnLeftTypeAlias}{5} -\newcommand{\SrnRightTypeAlias}{6} -\newcommand{\SrnLeftFutureOr}{7} -\newcommand{\SrnTypeVariableReflexivityA}{8} -\newcommand{\SrnRightPromotedVariable}{9} -\newcommand{\SrnRightFutureOrA}{10} -\newcommand{\SrnRightFutureOrB}{11} -\newcommand{\SrnLeftPromotedVariable}{12} -\newcommand{\SrnLeftVariableBound}{13} -\newcommand{\SrnRightFunction}{14} -\newcommand{\SrnPositionalFunctionType}{15} -\newcommand{\SrnNamedFunctionType}{16} -\newcommand{\SrnCovariance}{17} -\newcommand{\SrnSuperinterface}{18} +\newcommand{\SrnRightTop}{2} +\newcommand{\SrnLeftTop}{3} +\newcommand{\SrnBottom}{4} +%\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{\SrnLeftPromotedVariableOne}{10} +\newcommand{\SrnRightPromotedVariable}{11} +\newcommand{\SrnRightFutureOrA}{12} +\newcommand{\SrnRightFutureOrB}{13} +\newcommand{\SrnRightNullableOne}{14} +\newcommand{\SrnRightNullableTwo}{15} +\newcommand{\SrnLeftPromotedVariableTwo}{16} +\newcommand{\SrnLeftVariableBound}{17} +\newcommand{\SrnRightFunction}{18} +\newcommand{\SrnPositionalFunctionType}{19} +\newcommand{\SrnNamedFunctionType}{20} +\newcommand{\SrnCovariance}{21} +\newcommand{\SrnNominal}{22} \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 + % the omission. + % ------------------------------------------------ Right Object 1 + % Not needed unless algorithmic: Instance of + % \SrnLeftVariableBound. + % \RuleRaw{\SrnRightObjectOne}{% + % \code{$X$\,\EXTENDS\,$B$} & \SubtypeStd{B}{\code{Object}}% + % }{X}{\code{Object}} + % ------------------------------------------------ Right Object 2 + % Not needed unless algorithmic: Instance of + % \SrnLeftPromotedVariableTwo. + % \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}{S}{S} - \Axiom{\SrnBottom}{Left Bottom}{\bot}{T} + % ------------------------------------------------ Reflexivity + \Axiom{\SrnReflexivity}{T}{T} + % ------------------------------------------------ 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{\SrnTop}{Right Top}{T \in \{\code{Object}, \DYNAMIC, \VOID\}}{S}{T} - \RuleRaw{\SrnNull}{Left Null}{T \not= \bot}{\code{Null}}{T} + % ------------------------------------------------ Right Top + \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 + \Rule{\SrnNullTwo}{\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}{% + % ------------------------------------------------ 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{\SrnLeftVariableBound}{Left Variable Bound}{\Delta(X)}{T}{X}{T} + % ------------------------------------------------ Right FutureOr B + \Rule{\SrnRightFutureOrB}{S}{T}{S}{% + \code{FutureOr<$T$>}} + % ------------------------------------------------ Right Nullable 2 + \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} - \Axiom{\SrnTypeVariableReflexivityA}{Left Promoted Variable A}{X \& S}{X} - \Rule{\SrnRightFutureOrA}{Right FutureOr A}{S}{\code{Future<$T$>}}{% - S}{\code{FutureOr<$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 Nullable + \RuleTwo{\SrnLeftNullable}{S}{T}{\code{Null}}{T}{% + \code{$S$?}}{T} + % ------------------------------------------------ Left Promoted Variable A + \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{\SrnLeftPromotedVariableTwo}{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}{% + % ------------------------------------------------ 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 - \RuleRawRaw{\SrnNamedFunctionType}{Named Function Types}{% + % ------------------------------------------------ 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}\,\} \\ - \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 - \RuleRaw{\SrnCovariance}{Covariance}{% - \code{\CLASS{} $C$<\TypeParametersNoBounds{X}{s}>\,\ldots\,\{\}} & - \forall j \in 1 .. s\!:\;\SubtypeStd{S_j}{T_j}}{% + % ------------------------------------------------ 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.} & + \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 - \RuleRaw{\SrnSuperinterface}{Superinterface}{% - \code{\CLASS{} $C$<\TypeParametersNoBounds{X}{s}>\,\ldots\,\{\}}\\ + % ------------------------------------------------ Superinterface + \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}}{% \code{$C$<\List{S}{1}{s}>}\;}{\;T} @@ -21793,6 +22937,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} @@ -21804,9 +22951,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. +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 @@ -21819,41 +22966,35 @@ \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). - -However, the wording `denoting the same type' above covers -additional situations as well: -For instance, we may 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{}% -Every \synt{typeName} used in a type mentioned in this section is assumed to -have no compile-time error and denote a type. +(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.% +} + +\LMHash{}% +In section~\ref{subtypes} and its subsections, +all designations of types are considered to be the same +if{}f they have the same canonical syntax +(\ref{theCanonicalSyntaxOfTypes}). + +\commentary{% +Note that the canonical syntax also implies +transitive expansion of all type aliases +(\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 section~\ref{subtypes} and its subsections +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 @@ -21901,9 +23042,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, @@ -21914,7 +23057,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 @@ -21941,48 +23085,18 @@ \subsubsection{Subtype Rules} } -\subsubsection{Being a subtype} +\subsubsection{Being a Subtype} \LMLabel{beingASubtype} \LMHash{}% 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. -\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{} -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 @@ -21995,28 +23109,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 @@ -22034,23 +23134,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} @@ -22091,8 +23180,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{\SrnTop}{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 @@ -22101,42 +23190,48 @@ \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{\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$. + \code{Never} is a subtype of every type. +\Item{\SrnRightObjectFour}{Right 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} + \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} @@ -22173,8 +23268,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$, @@ -22193,11 +23289,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$, @@ -22223,58 +23321,2158 @@ \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} -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{% -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.% } -\subsection{Function Types} -\LMLabel{functionTypes} +\subsection{Type Nullability} +\LMLabel{typeNullability} \LMHash{}% -\IndexCustom{Function types}{type!function} -come in two variants: -\begin{enumerate} -\item - The types of functions that only have positional parameters. - These have the general form - \FunctionTypePositionalStd{T}. -\item - The types of functions with named parameters. - These have the general form - \FunctionTypeNamedStd{T}. -\end{enumerate} +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{% -Note that the non-generic case is covered by having $s = 0$, -in which case the type parameter declarations are omitted -(\ref{generics}). -The case with no optional parameters is covered by having $k = 0$; +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. +\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 given 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{}% +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 +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?}. +It is also an error for a type variable $X$ whose bound is \code{int?}, +because they have the same interface +(\ref{interfaces}).% +} + + +\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. +In each of these function definitions, +the first applicable case must be used. + +\LMHash{}% +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. +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{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. + + \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. +When \TopMergeTypeName{} is used in this specification, +each case where \TopMergeTypeName{} is undefined +is handled explicitly.% +} + +\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, \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>} +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. +\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 +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} + +\noindent +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}}. +\item \DefEquals{\IsObjectType{FutureOr<$T$>}}{\IsObjectType{$T$}}. +\item \DefEquals{\IsObjectType{$T$}}{\metavar{false}}, otherwise. +\end{itemize} + +\noindent +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}}. +\item \DefEquals{\IsBottomType{$X$\,\,\&\,\,$T$}}{\IsBottomType{$T$}}. +\item \DefEquals{\IsBottomType{$X$\,\,\EXTENDS\,\,$T$}}{\IsBottomType{$T$}}. +\item \DefEquals{\IsBottomType{$T$}}{\metavar{false}}, otherwise. +\end{itemize} + +\noindent +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}}. +\item \DefEquals{\IsNullType{$T$?}}{\IsNullType{$T$} \vee \IsBottomType{$T$}}. +\item \DefEquals{\IsNullType{$T$}}{\metavar{false}}, otherwise. +\end{itemize} + +\noindent +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}}. +\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} + +\noindent +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.% +} + +\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} + + +\subsection{Type Normalization} +\LMLabel{typeNormalization} + +\LMHash{}% +This section specifies a function that normalizes Dart types. +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{}% +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 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, +but \code{List} and \code{\VOID\,\,\FUNCTION()} are not atomic.% +} + +\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. + +\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$). + +In particular, \SubtypeNE{S}{T} and \SubtypeNE{T}{S} holds if and only if +\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{}% +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'}). +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: + +\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}{r} + + \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} +} + +\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} + +\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)}. + +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{% +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, +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. +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{}% +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 +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 + 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 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{% + 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{% +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 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{}% +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 +\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. + +\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 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{\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. + +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.% +} + + +\subsection{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$ 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. +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$, +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}.% +} + +\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 +(\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, 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. + +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 +consistently renamed to fresh names whenever necessary, +such that no type variables are captured due to accidental name clashes. +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 +\Index{standard upper bound} +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 + \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_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$}}{% + \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{$T_3$?}},\\ + where $T_3$ is \UpperBoundType{$T_1$}{$T_2$}. +\item + \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{$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$}}{% + \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 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$}, + 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}$)}}. + + \commentary{% + This case includes non-generic function types by allowing $m$ to be zero.% + } +\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}_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 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}). + \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 \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: + + \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$ 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 + \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 + \DefEqualsNewline{\UpperBoundType{$S$ \FUNCTION<\ldots>(\ldots)}{$T_2$}}{% + \UpperBoundType{Object}{$T_2$}}. +\item + \DefEqualsNewline{\UpperBoundType{$T_1$}{$S$ \FUNCTION<\ldots>(\ldots)}}{% + \UpperBoundType{$T_1$}{Object}}. +\item + \DefEquals{\UpperBoundType{FutureOr<$T_1$>}{FutureOr<$T_2$>}}{% + \code{FutureOr<$T_3$>}}, + where $T_3$ = \UpperBoundType{$T_1$}{$T_2$}. +\item + \DefEquals{\UpperBoundType{Future<$T_1$>}{FutureOr<$T_2$>}}{% + \code{FutureOr<$T_3$>}}, + where $T_3$ = \UpperBoundType{$T_1$}{$T_2$}. +\item + \DefEquals{\UpperBoundType{FutureOr<$T_1$>}{Future<$T_2$>}}{% + \code{FutureOr<$T_3$>}}, + where $T_3$ = \UpperBoundType{$T_1$}{$T_2$}. +\item + \DefEquals{\UpperBoundType{$T_1$}{FutureOr<$T_2$>}}{% + \code{FutureOr<$T_3$>}}, + where $T_3$ = \UpperBoundType{$T_1$}{$T_2$}. +\item + \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}. + + \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{}% +The function \LowerBoundType{$T_1$}{$T_2$} is defined as follows. + +\begin{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}$)} + + \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 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 + 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}$)}}. + + \commentary{% + This case includes non-generic function types by allowing $m$ to be zero.% + } +\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}_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 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$)}}, + + \noindent + where: + + \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} + + \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} + +\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.% +} + + +\subsubsection{The Standard Upper Bound of Distinct Interface Types} +\LMLabel{dartOneStandardUpperBound} + +\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.% +} + +\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{}>}.% +} + +\LMHash{}% +We define the auxiliary function \NominalTypeDepthName{} +on interface types as follows: + +\begin{itemize} +\item + % 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$. + Then \DefEquals{\NominalTypeDepth{$T$}}{d + 1}, + where $d$ is + $\metavar{max}\,\{\;\NominalTypeDepth{$S$}\;|\;S\;\in M\;\}$. +\end{itemize} + +\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)$. + +\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$. + + +\subsubsection{Standard Upper Bound Issues} +\LMLabel{standardUpperBoundIssues} + +%% 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 +does not guarantee termination. +Here is a counterexample: +} + +\begin{dartCode} +\VOID{} foo, S \EXTENDS{} List>(T x, S y) \{ + \VAR{} a = (x == y) ? x : y; +\} +\end{dartCode} + +\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.% +} + +\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} + +\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}{r} + + \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}{r} + + \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 +(\ref{typeInference}).% +} + +\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} + +\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: + +\begin{itemize} +\item $T$ is \VOID{} (\ref{typeVoid}). +\item $T$ is \DYNAMIC{} (\ref{typeDynamic}). +\item + $T$ is \code{Never}, \code{Null}, \code{num}, \code{int}, \code{double}, + \code{bool}, or \code{String}. +\item $T$ is an enumerated type (\ref{enums}). +% TODO(eernst): Come newer versions, add these: +% \item $T$ is a record type (\ref{???}) or the type \RECORD. +% \item $T$ is a sealed class. +\item $T$ is a function type (\ref{functionTypes}) or the type \FUNCTION. +\item $T$ is \code{$S$?} for some type $S$. +\item $T$ is \code{FutureOr<$S$>} for some type $S$ (\ref{typeFutureOr}). +% \item $T$ is an extension type (\ref{???}). +\item $T$ is a type variable (\ref{generics}). +\item $T$ is an intersection type (\ref{intersectionTypes}). +\item $T$ is a deferred type (\ref{staticTypes}). +\item $T$ is a type alias whose transitive alias expansion + (\ref{typedef}) does not denote a class building type. +\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, 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}>}, +where $C$ denotes a class different from \code{Never} and \code{Null}, +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}, \code{Null}, +any function type, any type variable, any intersection type, +and any type of the form \code{$T$?}. + +Conversely, built-in classes like, e.g., +\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$>}, +for any $S$ and $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 never occurs as or in +an actual type argument in a parameterized 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{?}.% +} + +\LMHash{}% +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 Never} +\LMLabel{typeNever} + +\LMHash{}% +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{% +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, \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. +Similarly, a variable of type \code{Object?\,\,\FUNCTION(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{% +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, +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, +and similarly for any arguments passed to an expression of type \code{Never} +which is invoked as a function.% +} + + +\subsection{Type Null} +\LMLabel{typeNull} + +\LMHash{}% +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}). + +\commentary{% +In other words, \code{Null} is a singleton type. +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 +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}).% +} + +\LMHash{}% +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}. + +\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 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 +the property of being optional.% +} + + +\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}). +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. +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 type $T$, +we say that $o$ is a \Index{reified type}, +and we say that $o$ \IndexCustom{reifies}{type!reifies} $T$. + +\LMHash{}% +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$, +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 have the same canonical syntax +(\ref{standardUpperBoundsAndStandardLowerBounds}). + +\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 $T_1$ respectively $T_2$, +and let $o_3$ be an object which is not a reified type. +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. +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 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{$v_1$\,\,==\,\,$v_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} + +\LMHash{}% +\IndexCustom{Function types}{type!function} +come in two variants: +\begin{enumerate} +\item + The types of functions that only have positional parameters. + These have the general form + \FunctionTypePositionalStd{T}. +\item + The types of functions with named parameters. + These have the general form + \FunctionTypeNamedStd{T}. +\end{enumerate} + +\commentary{% +Note that the non-generic case is covered by having $s = 0$, +in which case the type parameter declarations are omitted +(\ref{generics}). +The case with no optional parameters is covered by having $k = 0$; note that all rules involving function types of the two kinds coincide in this case.% } @@ -22298,14 +25496,14 @@ \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}).% } -\subsection{Type \FUNCTION} +\subsection{Type Function} \LMLabel{functionType} \LMHash{}% @@ -22319,31 +25517,34 @@ \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.% } -\subsection{Type \DYNAMIC} +\subsection{Type dynamic} \LMLabel{typeDynamic} \LMHash{}% @@ -22482,7 +25683,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}. @@ -22556,7 +25757,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$>}.% @@ -22565,7 +25766,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}. @@ -22609,7 +25810,7 @@ \subsection{Type FutureOr} \end{itemize} -\subsection{Type Void} +\subsection{Type \VOID} \LMLabel{typeVoid} \LMHash{}% @@ -22639,28 +25840,17 @@ \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 true. -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{}% 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} @@ -22690,6 +25880,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{% @@ -22699,20 +25890,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$;}, @@ -22780,13 +25964,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$, @@ -22797,7 +25981,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.} @@ -22845,22 +26029,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)} \} @@ -22877,25 +26061,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{% @@ -22916,7 +26100,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 @@ -22964,7 +26148,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 @@ -22979,7 +26163,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. @@ -23003,7 +26187,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} @@ -23057,108 +26242,335 @@ \subsubsection{Actual Types} } -\subsubsection{Least Upper Bounds} -\LMLabel{leastUpperBounds} +\section{Type Inference} +\LMLabel{typeInference} -% 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{}% +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{}% -% 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$. +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{}% -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$. +This specification does not fully specify the flow analysis in Dart, +but a future version of it will do so.\!\footnote{% +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 +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 least upper bound operation is commutative and idempotent, -but it is not associative. +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} -% Function types +\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}. -%% TODO(eernst): This section should use the new syntax for function types -%% (\FunctionTypePositionalStd{} etc.); these updates are made by CL 84908. +\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{}% -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: +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 If +\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} -\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 +\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$. -\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$} +\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. -\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$} +\subsection{Type Promotion} +\LMLabel{typePromotion} -\noindent -where $L_i$ is the least upper bound of $T_i$ and $S_i, i \in 0 .. k$. -\item If +\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$. -\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$}, +\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'. -\noindent -\code{$G = $ <$X_1\ B_1, \ldots,\ X_s\ B_s$>($S_1, \ldots,\ S_r,\ $\{ \ldots{} \}) $ \rightarrow S_0$} +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?}.% +} -\noindent -then the least upper bound of $F$ and $G$ is +\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$. -\noindent -\code{<$X_1\ B_1, \ldots,\ X_s\ B_s$>($L_1, \ldots,\ L_r$) $ \rightarrow L_0$} +\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.% +} + +\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$. +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}% +}). -\noindent -where $L_i$ is the least upper bound of $T_i$ and $S_i, i \in 0 .. r$. -\item If +\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. -\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$}, +\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:% +} -\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$} +\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} -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 +\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$}. -\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{% +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} promoted to \code{$X$\,\&\,\,$T$}.% +} -where $L_i$ is the least upper bound of $T_i$ and $S_i, i \in 0 .. r$ -\end{itemize} +\LMHash{}% +We now mention the locations where promotion may occur +independently of the control flow. \commentary{% -Note that the non-generic case is covered by using $s = 0$, -in which case the type parameter declarations are omitted (\ref{generics}).% +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. + +%% 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'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$} +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.% } @@ -23183,7 +26595,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{% @@ -23208,7 +26620,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 @@ -23324,156 +26736,238 @@ \subsection{Operator Precedence} \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 \SrnTop--\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} -\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, \SrnTop--\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 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{}f \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{$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}. + + \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 $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 + \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 $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 + \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$])} + + 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$. + \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$. + \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}