Index: docs/language/dartLangSpec.tex |
diff --git a/docs/language/dartLangSpec.tex b/docs/language/dartLangSpec.tex |
index f11500bd23847384fee200dcf87b9e1b2c4703fa..572694935bba28384988c851844360672996b254 100644 |
--- a/docs/language/dartLangSpec.tex |
+++ b/docs/language/dartLangSpec.tex |
@@ -1,12 +1,19 @@ |
\documentclass{article} |
\usepackage{epsfig} |
+\usepackage{color} |
\usepackage{dart} |
\usepackage{bnf} |
\usepackage{hyperref} |
+\usepackage{lmodern} |
\newcommand{\code}[1]{{\sf #1}} |
\title{Dart Programming Language Specification \\ |
{\large Version 1.6}} |
%\author{The Dart Team} |
+ |
+% For information about Location Markers (and in particular the |
+% commands \LMHash and \LMLabel), see the long comment at the |
+% end of this file. |
+ |
\begin{document} |
\maketitle |
\tableofcontents |
@@ -19,22 +26,26 @@ |
% begin Ecma boilerplate |
\section{Scope} |
-\label{ecmaScope} |
+\LMLabel{ecmaScope} |
+\LMHash{} |
This Ecma standard specifies the syntax and semantics of the Dart programming language. It does not specify the APIs of the Dart libraries except where those library elements are essential to the correct functioning of the language itself (e.g., the existence of class \cd{Object} with methods such as \cd{noSuchMethod}, \cd{runtimeType}). |
\section{Conformance} |
-\label{ecmaConformance} |
+\LMLabel{ecmaConformance} |
+\LMHash{} |
A conforming implementation of the Dart programming language must provide and support all the APIs (libraries, types, functions, getters, setters, whether top-level, static, instance or local) mandated in this specification. |
+\LMHash{} |
A conforming implementation is permitted to provide additional APIs, but not additional syntax. |
% A claim of conformance with this Ecma Standard shall specify? |
\section{Normative References} |
-\label{ecmaNormativeReferences} |
+\LMLabel{ecmaNormativeReferences} |
+\LMHash{} |
The following referenced documents are indispensable for the application of this document. For dated references, only the edition cited applies. For undated references, the latest edition of the referenced document (including any amendments) applies. |
\begin{enumerate} |
@@ -46,15 +57,17 @@ Dart API Reference, https://api.dartlang.org/ |
\section{Terms and Definitions} |
-\label{ecmaTermsAndDefinitions} |
+\LMLabel{ecmaTermsAndDefinitions} |
+\LMHash{} |
Terms and definitions used in this specification are given in the body of the specification proper. Such terms are highlighted in italics when they are introduced, e.g., `we use the term {\em verbosity} to refer to the property of excess verbiage'. |
% End Ecma Boilerplate |
\section{Notation} |
-\label{notation} |
+\LMLabel{notation} |
+\LMHash{} |
We distinguish between normative and non-normative text. Normative text defines the rules of Dart. It is given in this font. At this time, non-normative text includes: |
\begin{itemize} |
\item[Rationale] Discussion of the motivation for language design decisions appears in italics. \rationale{Distinguishing normative from non-normative helps clarify what part of the text is binding and what part is merely expository.} |
@@ -62,6 +75,7 @@ We distinguish between normative and non-normative text. Normative text defines |
\item[Open questions] (\Q{in this font}). Open questions are points that are unsettled in the mind of the author(s) of the specification; expect them (the questions, not the authors; precision is important in a specification) to be eliminated in the final specification. \Q{Should the text at the end of the previous bullet be rationale or commentary?} |
\end{itemize} |
+\LMHash{} |
Reserved words and built-in identifiers (\ref{identifierReference}) appear in {\bf bold}. |
\commentary{ |
@@ -69,6 +83,7 @@ Examples would be \SWITCH{} or \CLASS{}. |
} |
+\LMHash{} |
Grammar productions are given in a common variant of EBNF. The left hand side of a production ends with a colon. On the right hand side, alternation is represented by vertical bars, and sequencing by spacing. As in PEGs, alternation gives priority to the left. Optional elements of a production are suffixed by a question mark like so: \code{anElephant?}. Appending a star to an element of a production means it may be repeated zero or more times. Appending a plus sign to a production means it occurs one or more times. Parentheses are used for grouping. Negation is represented by prefixing an element of a production with a tilde. Negation is similar to the not combinator of PEGs, but it consumes input if it matches. In the context of a lexical production it consumes a single character if there is one; otherwise, a single token if there is one. |
\commentary{ An example would be:} |
@@ -95,17 +110,23 @@ Grammar productions are given in a common variant of EBNF. The left hand side o |
% The alternative is to define ~X as anything but X, or to introduce an anthingBut(X) |
% combinator, such as !X |
+\LMHash{} |
Both syntactic and lexical productions are represented this way. Lexical productions are distinguished by their names. The names of lexical productions consist exclusively of upper case characters and underscores. As always, within grammatical productions, whitespace and comments between elements of the production are implicitly ignored unless stated otherwise. |
Punctuation tokens appear in quotes. |
+\LMHash{} |
Productions are embedded, as much as possible, in the discussion of the constructs they represent. |
+\LMHash{} |
A list $x_1, \ldots, x_n$ denotes any list of $n$ elements of the form $x_i, 1 \le i \le n$. Note that $n$ may be zero, in which case the list is empty. We use such lists extensively throughout this specification. |
+\LMHash{} |
The notation $[x_1, \ldots, x_n/y_1, \ldots, y_n]E$ denotes a copy of $E$ in which all occurrences of $y_i, 1 \le i \le n$ have been replaced with $x_i$. |
+\LMHash{} |
We sometimes abuse list or map literal syntax, writing $[o_1, \ldots, o_n]$ (respectively $\{k_1: o_1, \ldots, k_n: o_n\}$) where the $o_i$ and $k_i$ may be objects rather than expressions. The intent is to denote a list (respectively map) object whose elements are the $o_i$ (respectively, whose keys are the $k_i$ and values are the $o_i$). |
+\LMHash{} |
The specifications of operators often involve statements such as $x$ $op$ $y$ is equivalent to the method invocation $x.op(y)$. Such specifications should be understood as a shorthand for: |
\begin{itemize} |
\item |
@@ -115,8 +136,10 @@ The specifications of operators often involve statements such as $x$ $op$ $y$ is |
\rationale{This circumlocution is required because 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. |
} |
+\LMHash{} |
When the specification refers to the order given in the program, it means the order of the program source code text, scanning left-to-right and top-to-bottom. |
+\LMHash{} |
References to otherwise unspecified names of program entities (such as classes or functions) are interpreted as the names of members of the Dart core library. |
\commentary{ |
@@ -124,12 +147,15 @@ Examples would be the classes \code{Object} and \code{Type} representing the roo |
} |
\section{Overview} |
-\label{overview} |
+\LMLabel{overview} |
+\LMHash{} |
Dart is a class-based, single-inheritance, pure object-oriented programming language. Dart is optionally typed (\ref{types}) and supports reified generics. The run-time type of every object is represented as an instance of class \code{Type} which can be obtained by calling the getter \code{runtimeType} declared in class \code{Object}, the root of the Dart class hierarchy. |
+\LMHash{} |
Dart programs may be statically checked. The static checker will report some violations of the type rules, but such violations do not abort compilation or preclude execution. |
+\LMHash{} |
Dart programs may be executed in one of two modes: production mode or checked mode. In production mode, static type annotations (\ref{staticTypes}) have absolutely no effect on execution with the exception of reflection and structural type tests. |
\commentary{ |
@@ -138,6 +164,7 @@ Reflection, by definition, examines the program structure. If we provide reflect |
Type tests also examine the types in a program explicitly. Nevertheless, in most cases, these will not depend on type annotations. The exceptions to this rule are type tests involving function types. Function types are structural, and so depend on the types declared for their parameters and on their return types. |
} |
+\LMHash{} |
In checked mode, assignments are dynamically checked, and certain violations of the type system raise exceptions at run time. |
\commentary{ |
@@ -150,30 +177,36 @@ The coexistence between optional typing and reification is based on the followin |
\end{enumerate} |
} |
+\LMHash{} |
Dart programs are organized in a modular fashion into units called {\em libraries} (\ref{librariesAndScripts}). Libraries are units of encapsulation and may be mutually recursive. |
\commentary{However they are not first class. To get multiple copies of a library running simultaneously, one needs to spawn an isolate. |
} |
\subsection{Scoping} |
-\label{scoping} |
+\LMLabel{scoping} |
+\LMHash{} |
A {\em namespace} is a mapping of names denoting declarations to actual declarations. Let $NS$ be a namespace. We say that a name $n$ {\em is in }$NS$ if $n$ is a key of $NS$. We say a declaration $d$ {\em is in }$NS$ if a key of $NS$ maps to $d$. |
+\LMHash{} |
A scope $S_0$ induces a namespace $NS_0$ that maps the simple name of each variable, type or function declaration $d$ declared in $S_0$ to $d$. Labels are not included in the induced namespace of a scope; instead they have their own dedicated namespace. |
\commentary{It is therefore impossible, e.g., to define a class that declares a method and a field with the same name in Dart. Similarly one cannot declare a top-level function with the same name as a library variable or class. |
} |
+\LMHash{} |
It is a compile-time error if there is more than one entity with the same name declared in the same scope. |
\commentary{ |
In some cases, the name of the declaration differs from the identifier used to declare it. Setters have names that are distinct from the corresponding getters because they always have an = automatically added at the end, and unary minus has the special name unary-. |
} |
+\LMHash{} |
Dart is lexically scoped. Scopes may nest. A name or declaration $d$ is {\em available in scope} $S$ if $d$ is in the namespace induced by $S$ or if $d$ is available in the lexically enclosing scope of $S$. We say that a name or declaration $d$ is {\em in scope} if $d$ is available in the current scope. |
+\LMHash{} |
If a declaration $d$ named $n$ is in the namespace induced by a scope $S$, then $d$ {\em hides} any declaration named $n$ that is available in the lexically enclosing scope of $S$. |
\commentary { |
@@ -187,6 +220,7 @@ Naming conventions usually prevent such abuses. Nevertheless,the following progr |
\} |
\end{dartCode} |
+\LMHash{} |
Names may be introduced into a scope by declarations within the scope or by other mechanisms such as imports or inheritance. |
\rationale{ |
@@ -227,17 +261,20 @@ Good tooling should of course endeavor to inform programmers of such situations |
\subsection{Privacy} |
-\label{privacy} |
+\LMLabel{privacy} |
+\LMHash{} |
Dart supports two levels of privacy: {\em public} and {\em private}. A declaration is {\em private} iff its name is private, otherwise it is {\em public.} A name $q$ is private iff any one of the identifiers that comprise $q$ is private, otherwise it is {\em public.} An identifier is private iff it |
begins with an underscore (the \_ character) otherwise it is {\em public.} |
+\LMHash{} |
A declaration $m$ is {\em accessible to library $L$} if $m$ is declared in $L$ or if $m$ is public. |
\commentary{ |
This means private declarations may only be accessed within the library in which they are declared. |
} |
+\LMHash{} |
Privacy applies only to declarations within a library, not to library declarations themselves. |
\rationale{Libraries do not reference each other by name and so the idea of a private library is meaningless. |
@@ -250,16 +287,20 @@ Privacy is indicated by the name of a declaration - hence privacy and naming are |
\subsection{Concurrency} |
+\LMHash{} |
Dart code is always single threaded. There is no shared-state concurrency in Dart. Concurrency is supported via actor-like entities called {\em isolates}. |
+\LMHash{} |
An isolate is a unit of concurrency. It has its own memory and its own thread of control. Isolates communicate by message passing (\ref{sendingMessages}). No state is ever shared between isolates. Isolates are created by spawning (\ref{spawningAnIsolate}). |
\section{Errors and Warnings} |
-\label{errorsAndWarnings} |
+\LMLabel{errorsAndWarnings} |
+\LMHash{} |
This specification distinguishes between several kinds of errors. |
+\LMHash{} |
{\em Compile-time errors} are errors that preclude execution. A compile-time error must be reported by a Dart compiler before the erroneous code is executed. |
\rationale{A Dart implementation has considerable freedom as to when compilation takes place. Modern programming language implementations often interleave compilation and execution, so that compilation of a method may be delayed, e.g., until it is first invoked. Consequently, compile-time errors in a method $m$ may be reported as late as the time of $m$'s first invocation. |
@@ -269,6 +310,7 @@ As a web language, Dart is often loaded directly from source, with no intermedia |
In a development environment a compiler should of course report compilation errors eagerly so as to best serve the programmer. |
} |
+\LMHash{} |
If an uncaught compile-time error occurs within the code of a running isolate $A$, $A$ is immediately suspended. The only circumstance where a compile-time error could be caught would be via code run reflectively, where the mirror system can catch it. |
\rationale{Typically, once a compile-time error is thrown and $A$ is suspended, $A$ will then be terminated. However, this depends on the overall environment. |
@@ -276,18 +318,23 @@ A Dart engine runs in the context of an {\em embedder}, |
a program that interfaces between the engine and the surrounding computing environment. The embedder will often be a web browser, but need not be; it may be a C++ program on the server for example. When an isolate fails with a compile-time error as described above, control returns to the embedder, along with an exception describing the problem. This is necessary so that the embedder can clean up resources etc. It is then the embedder's decision whether to terminate the isolate or not. |
} |
+\LMHash{} |
{\em Static warnings} are those errors reported by the static checker. They have no effect on execution. Many, but not all, static warnings relate to types, in which case they are known as {\em static type warnings.} Static warnings must be provided by Dart compilers used during development such as those incorporated in IDEs or otherwise intended to be used by developers for developing code. Compilers that are part of runtime execution environments such as virtual machines should not issue static warnings. |
+\LMHash{} |
{\em Dynamic type errors} are type errors reported in checked mode. |
+\LMHash{} |
{\em Run-time errors} are exceptions raised during execution. Whenever we say that an exception $ex$ is {\em raised} or {\em thrown}, we mean that a throw expression (\ref{throw}) of the form: \code{\THROW{} $ex$;} was implicitly evaluated or that a rethrow statement (\ref{rethrow}) of the form \code{\RETHROW} was executed. When we say that {\em a} $C$ {\em is thrown}, where $C$ is a class, we mean that an instance of class $C$ is thrown. |
+\LMHash{} |
If an uncaught exception is thrown by a running isolate $A$, $A$ is immediately suspended. |
\section{Variables} |
-\label{variables} |
+\LMLabel{variables} |
+\LMHash{} |
Variables are storage locations in memory. |
\begin{grammar} |
@@ -325,19 +372,25 @@ Variables are storage locations in memory. |
\end{grammar} |
+\LMHash{} |
A variable that has not been initialized has the initial value \NULL{} (\ref{null}). |
+\LMHash{} |
A variable declared at the top-level of a library is referred to as either a {\em library variable} or simply a top-level variable. |
+\LMHash{} |
A {\em static variable} is a variable that is not associated with a particular instance, but rather with an entire library or class. Static variables include library variables and class variables. Class variables are variables whose declaration is immediately nested inside a class declaration and includes the modifier \STATIC{}. A library variable is implicitly static. It is a compile-time error to preface a top-level variable declaration with the built-in identifier (\ref{identifierReference}) \STATIC{}. |
+\LMHash{} |
Static variable declarations are initialized lazily. When a static variable $v$ is read, iff it has not yet been assigned, it is set to the result of evaluating its initializer. The precise rules are given in section \ref{evaluationOfImplicitVariableGetters}. |
\rationale{The lazy semantics are given because we do not want a language where one tends to define expensive initialization computations, causing long application startup times. This is especially crucial for Dart, which must support the coding of client applications. |
} |
+\LMHash{} |
A {\em final variable} is a variable whose binding is fixed upon initialization; a final variable $v$ will always refer to the same object after $v$ has been initialized. The declaration of a final variable must include the modifier \FINAL{}. |
+\LMHash{} |
It is a static warning if a final instance variable that has been initialized at its point of declaration is also initialized in a constructor. |
% It is a static warning if a final instance variable that has been initialized by means of an initializing formal of a constructor is also initialized elsewhere in the same constructor. |
It is a compile-time error if a local variable $v$ is final and $v$ is not initialized at its point of declaration. |
@@ -351,20 +404,27 @@ Attempting to assign to a final variable anywhere except in its declaration or i |
Taken as a whole, the rules ensure that any attempt to execute multiple assignments to a final variable will yield static warnings and repeated assignments will fail dynamically. |
} |
+\LMHash{} |
A {\em constant variable} is a variable whose declaration includes the modifier \CONST{}. A constant variable is always implicitly final. A constant variable must be initialized to a compile-time constant (\ref{constants}) or a compile-time error occurs. |
+\LMHash{} |
We say that a variable $v$ is {\em potentially mutated} in some scope $s$ if $v$ is not final or constant and an assignment to $v$ occurs in $s$. |
+\LMHash{} |
If a variable declaration does not explicitly specify a type, the type of the declared variable(s) is \DYNAMIC{}, the unknown type (\ref{typeDynamic}). |
+\LMHash{} |
A variable is {\em mutable} if it is not final. |
Static and instance variable declarations always induce implicit getters. If the variable is mutable it also introduces an implicit setter. |
The scope into which the implicit getters and setters are introduced depends on the kind of variable declaration involved. |
+\LMHash{} |
A library variable introduces a getter into the top level scope of the enclosing library. A static class variable introduces a static getter into the immediately enclosing class. An instance variable introduces an instance getter into the immediately enclosing class. |
+\LMHash{} |
A mutable library variable introduces a setter into the top level scope of the enclosing library. A mutable static class variable introduces a static setter into the immediately enclosing class. A mutable instance variable introduces an instance setter into the immediately enclosing class. |
+\LMHash{} |
Local variables are added to the innermost enclosing scope. They do not induce getters and setters. A local variable may only be referenced at a source code location that is after its initializer, if any, is complete, or a compile-time error occurs. The error may be reported either at the point where the premature reference occurs, or at the variable declaration. |
\rationale { |
@@ -422,8 +482,10 @@ As a rule, type annotations are ignored in production mode. However, we do |
% the grammar does not support local getters and setters. The local var discussion does not seem to mention getters and setters based semantics. It simply discusses the creation of the variable, not its access. Access is either assignment or identifiers. Identifiers ignore the getter story. |
+\LMHash{} |
The following rules apply to all static and instance variables. |
+\LMHash{} |
A variable declaration of one of the forms \code{$T$ $v$;}, \code{$T$ $v$ = $e$;} , \code{\CONST{} $T$ $v$ = $e$;}, \code{\FINAL{} $T$ $v$;} or \code{\FINAL{} $T$ $v$ = $e$;} always induces an implicit getter function (\ref{getters}) with signature |
$T$ \GET{} $v$ |
@@ -431,18 +493,21 @@ $T$ \GET{} $v$ |
whose invocation evaluates as described below (\ref{evaluationOfImplicitVariableGetters}). |
+\LMHash{} |
A variable declaration of one of the forms \code{\VAR{} $v$;}, \code{\VAR{} $v$ = $e$;} , \code{\CONST{} $v$ = $e$;}, \code{\FINAL{} $v$;} or \code{\FINAL{} $v$ = $e$;} always induces an implicit getter function with signature |
\GET{} $v$ |
whose invocation evaluates as described below (\ref{evaluationOfImplicitVariableGetters}). |
+\LMHash{} |
A non-final variable declaration of the form \code{{} $T$ $v$;} or the form \code{$T$ $v$ = $e$;} always induces an implicit setter function (\ref{setters}) with signature |
\VOID{} \SET{} $v=(T$ $x)$ |
whose execution sets the value of $v$ to the incoming argument $x$. |
+\LMHash{} |
A non-final variable declaration of the form \code{\VAR{} $v$;} or the form \code{\VAR{} $v$ = $e$;} always induces an implicit setter function with signature |
\SET{} $v=(x)$ |
@@ -451,8 +516,9 @@ whose execution sets the value of $v$ to the incoming argument $x$. |
\subsection{Evaluation of Implicit Variable Getters} |
-\label{evaluationOfImplicitVariableGetters} |
+\LMLabel{evaluationOfImplicitVariableGetters} |
+\LMHash{} |
Let $d$ be the declaration of a static or instance variable $v$. If $d$ is an instance variable, then the invocation of the implicit getter of $v$ evaluates to the value stored in $v$. |
If $d$ is a static or library variable then the implicit getter method of $v$ executes as follows: |
\begin{itemize} |
@@ -467,8 +533,9 @@ Otherwise |
\section{Functions} |
-\label{functions} |
+\LMLabel{functions} |
+\LMHash{} |
Functions abstract over executable actions. |
\begin{grammar} |
@@ -491,8 +558,10 @@ Functions abstract over executable actions. |
\end{grammar} |
+\LMHash{} |
Functions include function declarations (\ref{functionDeclarations}), methods (\ref{instanceMethods}, \ref{staticMethods}), getters (\ref{getters}), setters (\ref{setters}), constructors (\ref{constructors}) and function literals (\ref{functionExpressions}). |
+\LMHash{} |
All functions have a signature and a body. The signature describes the formal parameters of the function, and possibly its name and return type. A function body is either: |
\begin{itemize} |
\item A block statement (\ref{blocks}) containing the statements (\ref{statements}) executed by the function, optionally marked with one of the modifiers: \ASYNC, \ASYNC* or \SYNC*. In this case, if the last statement of a function is not a return statement (\ref{return}), the statement \code{\RETURN{};} is implicitly appended to the function body. |
@@ -506,12 +575,14 @@ OR |
\end{itemize} |
+\LMHash{} |
A function is {\em asynchronous} if its body is marked with the \ASYNC{} or \ASYNC* modifier. Otherwise the function is {\em synchronous}. A function is a {\em generator} if its body is marked with the \SYNC* or \ASYNC* modifier. |
\commentary{ |
Whether a function is synchronous or asynchronous is orthogonal to whether it is a generator or not. Generator functions are a sugar for functions that produce collections in a systematic way, by lazily applying a function that {\em generates} individual elements of a collection. Dart provides such a sugar in both the synchronous case, where one returns an iterable, and in the asynchronous case, where one returns a stream. Dart also allows both synchronous and asynchronous functions that produce a single value. |
} |
+\LMHash{} |
It is a compile-time error if an \ASYNC, \ASYNC* or \SYNC* modifier is attached to the body of a setter or constructor. |
\rationale{ |
@@ -524,14 +595,17 @@ One could allow modifiers for factories. A factory for \code{Future} could be mo |
\subsection{Function Declarations} |
-\label{functionDeclarations} |
+\LMLabel{functionDeclarations} |
+\LMHash{} |
A {\em function declaration} is a function that is neither a member of a class nor a function literal. Function declarations include {\em library functions}, which are function declarations |
%(including getters and setters) |
at the top level of a library, and {\em local functions}, which are function declarations declared inside other functions. Library functions are often referred to simply as top-level functions. |
+\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 body is empty for functions that are external. |
+\LMHash{} |
The scope of a library function is the scope of the enclosing library. The scope of a local function is described in section \ref{localFunctionDeclaration}. In both cases, the name of the function is in scope in its formal parameter scope (\ref{formalParameters}). |
%A function declaration of the form $T_0$ $id(T_1$ $a_1, \ldots, T_n$ $a_n, [T_{n+1}$ $x_{n+1} = d_1, \ldots, T_{n+k}$ $x_{n+k} = d_k])\{s\}$ is equivalent to a variable declaration of the form \code{\FINAL{} $F$ $id$ = $(T_1$ $a_1, \ldots, T_n$ $a_n, [T_{n+1}$ $x_{n+1} = d_1, \ldots, T_{n+k}$ $x_{n+k}= d_k])\{s\}$}, where $F$ is the function type alias (\ref{typedef}) \code{\TYPEDEF{} $T_0$ $F(T_1$ $a_1, \ldots, T_n$ $a_n, [T_{n+1}$ $x_{n+1}, \ldots, T_{n+k}$ $x_{n+k}])$}. Likewise, a function declaration of the form $id(T_1$ $a_1, \ldots, T_n$ $a_n, [T_{n+1}$ $x_{n+1} = d_1, \ldots, T_{n+k}$ $x_{n+k} = d_k])\{s\}$ is equivalent to a variable declaration of the form \code{\FINAL{} $F$ $id$ = $(T_1$ $a_1, \ldots, T_n$ $a_n, [T_{n+1}$ $x_{n+1} = d_1, \ldots, T_{n+k}$ $x_{n+k} = d_k])\{s\}$}, where $F$ is the function type alias \code{\TYPEDEF{} $F(T_1$ $a_1, \ldots, T_n$ $a_n, [T_{n+1}$ $x_{n+1}, \ldots, T_{n+k}$ $x_{n+k}])$}. |
@@ -552,18 +626,23 @@ The scope of a library function is the scope of the enclosing library. The scope |
%A function declaration of the form $T_0$ $id(T_1$ $a_1, \ldots, T_n$ $a_n, \{T_{n+1}$ $x_{n+1} : d_1, \ldots, T_{n+k}$ $x_{n+k} : d_k\})\{s\}$ is equivalent to a variable declaration of the form \code{\FINAL{} $F$ $id$ = $(T_1$ $a_1, \ldots, T_n$ $a_n, \{T_{n+1}$ $x_{n+1} : d_1, \ldots, T_{n+k}$ $x_{n+k} : d_k\})\{s\}$}, where $F$ is the function type alias (\ref{typedef}) \code{\TYPEDEF{} $T_0$ $F(T_1$ $a_1, \ldots, T_n$ $a_n, \{T_{n+1}$ $x_{n+1}, \ldots, T_{n+k}$ $x_{n+k}]\}$}. Likewise, a function declaration of the form $id(T_1$ $a_1, \ldots, T_n$ $a_n, \{T_{n+1}$ $x_{n+1} : d_1, \ldots, T_{n+k}$ $x_{n+k} : d_k\})\{s\}$ is equivalent to a variable declaration of the form \code{\FINAL{} $F$ $id$ = $(T_1$ $a_1, \ldots, T_n$ $a_n, \{T_{n+1}$ $x_{n+1} : d_1, \ldots, T_{n+k}$ $x_{n+k} : d_k\})\{s\}$}, where $F$ is the function type alias \code{\TYPEDEF{} $F(T_1$ $a_1, \ldots, T_n$ $a_n, \{T_{n+1}$ $x_{n+1}, \ldots, T_{n+k}$ $x_{n+k}\})$}. |
+\LMHash{} |
It is a compile-time error to preface a function declaration with the built-in identifier \STATIC{}. |
+\LMHash{} |
When we say that a function $f_1$ {\em forwards} to another function $f_2$, we mean that invoking $f_1$ causes $f_2$ to be executed with the same arguments and/or receiver as $f_1$, and returns the result of executing $f_2$ to the caller of $f_1$, unless $f_2$ throws an exception, in which case $f_1$ throws the same exception. Furthermore, we only use the term for synthetic functions introduced by the specification. |
\subsection{Formal Parameters} |
-\label{formalParameters} |
+\LMLabel{formalParameters} |
+\LMHash{} |
Every function includes a {\em 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, but not both. |
+\LMHash{} |
The formal parameter list of a function introduces a new scope known as the function's {\em formal parameter scope}. The formal parameter scope of a function $f$ is enclosed in the scope where $f$ is declared. Every formal parameter introduces a local variable into the formal parameter scope. However, the scope of a function's signature is the function's enclosing scope, not the formal parameter scope. |
+\LMHash{} |
The body of a function introduces a new scope known as the function's {\em body scope}. The body scope of a function $f$ is enclosed in the scope introduced by the formal parameter scope of $f$. |
@@ -572,6 +651,7 @@ The body of a function introduces a new scope known as the function's {\em body |
% The formal parameters of a function are processed in the enclosing scope of the function. |
% \commentary{this means that the parameters themselves may not be referenced within the formal parameter list.} |
+\LMHash{} |
It is a compile-time error if a formal parameter is declared as a constant variable (\ref{variables}). |
\begin{grammar} |
@@ -611,8 +691,9 @@ It is a compile-time error if a formal parameter is declared as a constant varia |
\subsubsection{Required Formals} |
-\label{requiredFormals} |
+\LMLabel{requiredFormals} |
+\LMHash{} |
A {\em required formal parameter} 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.% explain what the type is in this case? Where is this described in general? |
@@ -645,8 +726,9 @@ A {\em required formal parameter} may be specified in one of three ways: |
%\end{grammar} |
\subsubsection{Optional Formals} |
-\label{optionalFormals} |
+\LMLabel{optionalFormals} |
+\LMHash{} |
Optional parameters may be specified and provided with default values. |
\begin{grammar} |
@@ -659,8 +741,10 @@ Optional parameters may be specified and provided with default values. |
. |
\end{grammar} |
+\LMHash{} |
It is a compile-time error if the default value of an optional parameter is not a compile-time constant (\ref{constants}). If no default is explicitly specified for an optional parameter an implicit default of \NULL{} is provided. |
+\LMHash{} |
It is a compile-time error if the name of a named optional parameter begins with an `\_' character. |
\rationale{ |
@@ -669,16 +753,21 @@ If we allowed named parameters to begin with an underscore, they would be consid |
} |
\subsection{Type of a Function} |
-\label{typeOfAFunction} |
+\LMLabel{typeOfAFunction} |
+\LMHash{} |
If a function does not declare a return type explicitly, its return type is \DYNAMIC{} (\ref{typeDynamic}). |
+\LMHash{} |
Let $F$ be a function with required formal parameters $T_1$ $p_1 \ldots, T_n$ $p_n$, return type $T_0$ and no optional parameters. Then the type of $F$ is $(T_1 ,\ldots, T_n) \rightarrow T_0$. |
+\LMHash{} |
Let $F$ be a function with required formal parameters $T_1$ $p_1 \ldots, T_n$ $p_n$, return type $T_0$ and positional optional parameters $T_{n+1}$ $p_{n+1}, \ldots, T_{n+k}$ $ p_{n+k}$. Then the type of $F$ is $(T_1 ,\ldots, T_n, [T_{n+1}$ $p_{n+1}, \ldots, T_{n+k}$ $p_{n+k}]) \rightarrow T_0$. |
+\LMHash{} |
Let $F$ be a function with required formal parameters $T_1$ $p_1 \ldots, T_n$ $p_n$, return type $T_0$ and named optional parameters $T_{n+1}$ $p_{n+1}, \ldots, T_{n+k}$ $ p_{n+k}$. Then the type of $F$ is $(T_1 ,\ldots, T_n, \{T_{n+1}$ $p_{n+1}, \ldots, T_{n+k}$ $p_{n+k}\}) \rightarrow T_0$. |
+\LMHash{} |
The run time type of a function object always implements the class \cd{Function}. |
\commentary{ |
@@ -692,8 +781,9 @@ For example, consider that a closure produced via property extraction treats equ |
} |
\subsection{External Functions} |
-\label{externalFunctions} |
+\LMLabel{externalFunctions} |
+\LMHash{} |
An {\em external function} is a function whose body is provided separately from its declaration. An external function may be a top-level function (\ref{librariesAndScripts}), a method (\ref{instanceMethods}, \ref{staticMethods}), a getter (\ref{getters}), a setter (\ref{setters}) 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. |
\rationale{ |
@@ -704,13 +794,16 @@ External functions allow us to introduce type information for code that is not |
Examples of external functions might be foreign functions (defined in C, or Javascript etc.), primitives of the implementation (as defined by the Dart runtime), or code that was dynamically generated but whose interface is statically known. However, an abstract method is different from an external function, as it has {\em no} body. |
} |
+\LMHash{} |
An external function is connected to its body by an implementation specific mechanism. Attempting to invoke an external function that has not been connected to its body will raise a \code{NoSuchMethodError} or some subclass thereof. |
+\LMHash{} |
The actual syntax is given in sections \ref{classes} and \ref{librariesAndScripts} below. |
\section{Classes} |
-\label{classes} |
+\LMLabel{classes} |
+\LMHash{} |
A {\em class} defines the form and behavior of a set of objects which are its {\em instances}. Classes may be defined by class declarations as described below, or via mixin applications (\ref{mixinApplication}). |
\begin{grammar} |
@@ -761,6 +854,7 @@ metadata \ABSTRACT{}? \CLASS{} mixinApplicationClass |
\end{grammar} |
+\LMHash{} |
A class has constructors, instance members and static members. The instance members of a class are its instance methods, getters, setters and instance variables. The static members of a class are its static methods, getters, setters and static variables. The members of a class are its static and instance members. |
% A class has a static scope and an instance scope. The enclosing scope of the static scope of a non-generic class is the enclosing scope of the class declaration. The enclosing scope of the static scope of a generic class is the type parameter scope (\ref{}) of the generic class declaration. |
@@ -771,12 +865,14 @@ A class has constructors, instance members and static members. The instance mem |
%The enclosing scope of a static member declaration is the static scope of the class in which it is declared. |
+\LMHash{} |
Every class has a single superclass except class \code{Object} which has no superclass. A class may implement a number of interfaces |
%, either |
by declaring them in its implements clause (\ref{superinterfaces}). |
% or via interface injection declarations (\ref{interfaceInjection}) outside the class declaration |
+\LMHash{} |
An {\em abstract class} is |
%either |
a class that is explicitly declared with the \ABSTRACT{} modifier, either by means of a class declaration or via a type alias (\ref{typedef}) for a mixin application (\ref{mixinApplication}). A {\em concrete class} is a class that is not abstract. |
@@ -787,6 +883,7 @@ a class that is explicitly declared with the \ABSTRACT{} modifier, either by m |
We want different behavior for concrete classes and abstract classes. If $A$ is intended to be abstract, we want the static checker to warn about any attempt to instantiate $A$, and we do not want the checker to complain about unimplemented methods in $A$. In contrast, if $A$ is intended to be concrete, the checker should warn about all unimplemented methods, but allow clients to instantiate it freely. |
} |
+\LMHash{} |
The {\em interface of class $C$} is an implicit interface that declares instance members that correspond to the instance members declared by $C$, and whose direct superinterfaces are the direct superinterfaces of $C$ (\ref{superinterfaces}). When a class name appears as a type, that name denotes the interface of the class. |
% making an exception for the setters generated for final fields is tempting but problematic. |
@@ -794,6 +891,7 @@ The {\em interface of class $C$} is an implicit interface that declares instance |
% Maybe the final field hides the setter in scope? |
% I think the original rules were best. |
+\LMHash{} |
It is a compile-time error if a class declares two members of the same name. |
%, except that a getter and a setter may be declared with the same name provided both are instance members or both are static members. |
It is a compile-time error if a class has an instance member and a static member with the same name. |
@@ -819,27 +917,32 @@ It is a compile-time error if a class has an instance member and a static member |
\} |
\end{dartCode} |
+\LMHash{} |
It is a compile time error if a class $C$ declares a member with the same name as $C$. It is a compile time error if a generic class declares a type variable with the same name as the class or any of its members or constructors. |
\subsection{Instance Methods} |
-\label{instanceMethods} |
+\LMLabel{instanceMethods} |
+\LMHash{} |
Instance methods are functions (\ref{functions}) whose declarations are immediately contained within a class declaration and that are not declared \STATIC{}. The instance methods of a class $C$ are those instance methods declared by $C$ and the instance methods inherited by $C$ from its superclass. |
%make these warnings if possible |
+\LMHash{} |
It is a static warning if an instance method $m_1$ overrides (\ref{inheritanceAndOverriding}) an instance member $m_2$ and $m_1$ has a greater number of required parameters than $m_2$. It is a static warning if an instance method $m_1$ overrides an instance member $m_2$ and $m_1$ has fewer positional parameters than $m_2$. It is a static warning if an instance method $m_1$ overrides an instance member $m_2$ and $m_1$ does not declare all the named parameters declared by $m_2$. |
% not quite right. It should be ok to override a method that requires N parameters with one that requires M < N but accepts the others as optional. |
+\LMHash{} |
It is a static warning if an instance method $m_1$ overrides an instance member $m_2$ and the type of $m_1$ is not a subtype of the type of $m_2$. It is a static warning if an instance method $m_1$ overrides an instance member $m_2$, the signature of $m_2$ explicitly specifies a default value for a formal parameter $p$ and the signature of $m_1$ implies a different default value for $p$. It is a static warning if a class $C$ declares an instance method named $n$ and has a setter named $n=$. It is a static warning if a class $C$ declares an instance method named $n$ and an accessible static member named $n$ is declared in a superclass of $C$. |
% Works. If the name is public, no issue. If it's private, if a subclass has a conflicting inst var, it either is in the same lib and will be flagged, or is in another and is not an issue. |
\subsubsection{Operators} |
-\label{operators} |
+\LMLabel{operators} |
+\LMHash{} |
{\em Operators} are instance methods with special names. |
\begin{grammar} |
@@ -862,33 +965,41 @@ It is a static warning if an instance method $m_1$ overrides an instance member |
. |
\end{grammar} |
+\LMHash{} |
An operator declaration is identified using the built-in identifier (\ref{identifierReference}) \OPERATOR{}. |
+\LMHash{} |
The following names are allowed for user-defined operators: \code{$<$, $>$, $<$=, $>$=, ==, -, +, /, \~{}/, *, \%, $|$, \^{}, \&, $<<$, $>>$, []=, [], \~{}.} |
+\LMHash{} |
It is a compile-time error if the arity of the user-declared operator \code{[]=} is not 2. It is a compile-time error if the arity of a user-declared operator with one of the names: \code{ $<$, $>$, $<$=, $>$=, ==, -, +, \~{}/, /, *, \%, $|$, \^{}, \&, $<<$, $>>$, []} is not 1. It is a compile-time error if the arity of the user-declared operator \code{-} is not 0 or 1. |
\commentary{ |
The \code{-} operator is unique in that two overloaded versions are permitted. If the operator has no arguments, it denotes unary minus. If it has an argument, it denotes binary subtraction. |
} |
+\LMHash{} |
The name of the unary operator \code{-} is \code{unary-}. |
\rationale{ |
This device allows the two methods to be distinguished for purposes of method lookup, override and reflection. |
} |
+\LMHash{} |
It is a compile-time error if the arity of the user-declared operator \code{ \~{}} is not 0. |
+\LMHash{} |
It is a compile-time error to declare an optional parameter in an operator. |
+\LMHash{} |
It is a static warning if the return type of the user-declared operator \code{[]=} is explicitly declared and not \VOID{}. |
\subsection{Getters} |
-\label{getters} |
+\LMLabel{getters} |
+\LMHash{} |
Getters are functions (\ref{functions}) that are used to retrieve the values of object properties. |
\begin{grammar} |
@@ -899,29 +1010,36 @@ Getters are functions (\ref{functions}) that are used to retrieve the values of |
%\Q{Why does a getter have a formal parameter list at all?} |
+\LMHash{} |
If no return type is specified, the return type of the getter is \DYNAMIC{}. |
+\LMHash{} |
A getter definition that is prefixed with the \STATIC{} modifier defines a static getter. Otherwise, it defines an instance getter. The name of the getter is given by the identifier in the definition. The effect of a static getter declaration in class $C$ is to add an instance getter with the same name and signature to the \code{Type} object for class $C$ that forwards (\ref{functionDeclarations}) to the static getter. |
%It is a compile-time error if a getter`s formal parameter list is not empty. |
+\LMHash{} |
The instance getters of a class $C$ are those instance getters declared by $C$, either implicitly or explicitly, and the instance getters inherited by $C$ from its superclass. The static getters of a class $C$ are those static getters declared by $C$. |
+\LMHash{} |
It is a compile-time error if a class has both a getter and a method with the same name. This restriction holds regardless of whether the getter is defined explicitly or implicitly, or whether the getter or the method are inherited or not. |
\commentary{ |
This implies that a getter can never override a method, and a method can never override a getter or field. |
} |
+\LMHash{} |
It is a static warning if the return type of a getter is \VOID. |
It is a static warning if a getter $m_1$ overrides (\ref{inheritanceAndOverriding}) a getter |
$m_2$ and the type of $m_1$ is not a subtype of the type of $m_2$. |
+\LMHash{} |
It is a static warning if a class declares a static getter named $v$ and also has a non-static setter named $v=$. It is a static warning if a class $C$ declares an instance getter named $v$ and an accessible static member named $v$ or $v=$ is declared in a superclass of $C$. These warnings must be issued regardless of whether the getters or setters are declared explicitly or implicitly. |
\subsection{Setters} |
-\label{setters} |
+\LMLabel{setters} |
+\LMHash{} |
Setters are functions (\ref{functions}) that are used to set the values of object properties. |
% what about top level ones? Same for getters |
@@ -932,28 +1050,36 @@ Setters are functions (\ref{functions}) that are used to set the values of objec |
. |
\end{grammar} |
+\LMHash{} |
If no return type is specified, the return type of the setter is \DYNAMIC{}. |
+\LMHash{} |
A setter definition that is prefixed with the \STATIC{} modifier defines a static setter. Otherwise, it defines an instance setter. The name of a setter is obtained by appending the string `=' to the identifier given in its signature. The effect of a static setter declaration in class $C$ is to add an instance setter with the same name and signature to the \code{Type} object for class $C$ that forwards (\ref{functionDeclarations}) to the static setter. |
\commentary{Hence, a setter name can never conflict with, override or be overridden by a getter or method.} |
+\LMHash{} |
The instance setters of a class $C$ are those instance setters declared by $C$ either implicitly or explicitly, and the instance setters inherited by $C$ from its superclass. The static setters of a class $C$ are those static setters declared by $C$. |
+\LMHash{} |
It is a 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, but we'd have to specify the evaluation rules in that case.} |
%It is a compile-time error if a class has both a setter and a method with the same name. This restriction holds regardless of whether the setter is defined explicitly or implicitly, or whether the setter or the method are inherited or not. |
+\LMHash{} |
It is a static warning if a setter declares a return type other than \VOID{}. |
It is a static warning if a setter $m_1$ overrides (\ref{inheritanceAndOverriding}) a setter $m_2$ and the type of $m_1$ is not a subtype of the type of $m_2$. It is a static warning if a class has a setter named $v=$ with argument type $T$ and a getter named $v$ with return type $S$, and $T$ may not be assigned to $S$. |
+\LMHash{} |
It is a static warning if a class declares a static setter named $v=$ and also has a non-static member named $v$. It is a static warning if a class $C$ declares an instance setter named $v=$ and an accessible static member named $v=$ or $v$ is declared in a superclass of $C$. |
+\LMHash{} |
These warnings must be issued regardless of whether the getters or setters are declared explicitly or implicitly. |
\subsection{Abstract Instance Members} |
-\label{abstractInstanceMembers} |
+\LMLabel{abstractInstanceMembers} |
+\LMHash{} |
An {\em abstract method} (respectively, {\em abstract getter} or {\em abstract setter)} is an instance method, getter or setter that is not declared \EXTERNAL{} and does not provide an implementation. A {\em concrete method} (respectively, {\em concrete getter} or {\em concrete setter)} is an instance method, getter or setter that is not abstract. |
%The declaration of an abstract method is prefixed by the built-in identifier (\ref{identifierReference}) \ABSTRACT{}. |
@@ -977,6 +1103,7 @@ The purpose of an abstract method is to provide a declaration for purposes such |
} |
%always results in a run-time error. This must be \code{NoSuchMethodError} or an instance of a subclass of \code{NoSuchMethodError}, such as \code{AbstractMethodError}. |
+\LMHash{} |
It is a static warning if an abstract member is declared or inherited in a concrete class unless that member overrides a concrete one. |
\rationale { |
@@ -1001,10 +1128,12 @@ We wish to warn if one declares a concrete class with abstract members. However, |
\rationale{At run time, the concrete method \cd{one} declared in \cd{Base} will be executed, and no problem should arise. Therefore no warning should be issued and so we suppress warnings if a corresponding concrete member exists in the hierarchy. } |
\subsection{Instance Variables} |
-\label{instanceVariables} |
+\LMLabel{instanceVariables} |
+\LMHash{} |
Instance variables are variables whose declarations are immediately contained within a class declaration and that are not declared \STATIC{}. The instance variables of a class $C$ are those instance variables declared by $C$ 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. |
\rationale{ |
@@ -1048,10 +1177,12 @@ An instance getter for it can always be defined manually if desired. |
\subsection{Constructors} |
-\label{constructors} |
+\LMLabel{constructors} |
+\LMHash{} |
A {\em constructor} is a special function that is used in instance creation expressions (\ref{instanceCreation}) to produce objects. Constructors may be generative (\ref{generativeConstructors}) or they may be factories (\ref{factories}). |
+\LMHash{} |
A {\em 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 $id$ is the name of a member declared in the immediately enclosing class. It is a compile-time error if the name of a constructor is not a constructor name. |
@@ -1059,11 +1190,13 @@ A {\em constructor name} always begins with the name of its immediately enclosin |
% The enclosing scope of a generative constructor is the instance scope of the class in which it is declared (but what about redirecting?) |
+\LMHash{} |
Iff no constructor is specified for a class $C$, it implicitly has a default constructor \code{C() : \SUPER{}() \{\}}, unless $C$ is class \code{Object}. |
\subsubsection{Generative Constructors} |
-\label{generativeConstructors} |
+\LMLabel{generativeConstructors} |
+\LMHash{} |
A {\em generative constructor} consists of a constructor name, a constructor parameter list, and either a redirect clause or an initializer list and an optional body. |
\begin{grammar} |
@@ -1072,12 +1205,16 @@ A {\em generative constructor} consists of a constructor name, a constructor par |
. |
\end{grammar} |
+\LMHash{} |
A {\em constructor parameter list} is a parenthesized, comma-separated list of formal constructor parameters. A {\em formal constructor parameter} is either a formal parameter (\ref{formalParameters}) or an initializing formal. An {\em initializing formal} has the form \code{\THIS{}.id}, where \code{id} is the name of an instance variable of the immediately enclosing class. It is a compile-time error if \code{id} is not an instance variable of the immediately enclosing class. It is a compile-time error if an initializing formal is used by a function other than a non-redirecting generative constructor. |
+\LMHash{} |
If an explicit type is attached to the initializing formal, that is its static type. Otherwise, the type of an initializing formal named \code{id} is $T_{id}$, where $T_{id}$ is the type of the field named \code{id} in the immediately enclosing class. It is a static warning if the static type of \code{id} is not assignable to $T_{id}$. |
+\LMHash{} |
Using an initializing formal \code{\THIS{}.id} in a formal parameter list does not introduce a formal parameter name into the scope of the constructor. However, the initializing formal does effect the type of the constructor function exactly as if a formal parameter named \code{id} of the same type were introduced in the same position. |
+\LMHash{} |
Initializing formals are executed during the execution of generative constructors detailed below. Executing an initializing formal \code{\THIS{}.id} causes the field \code{id} of the immediately surrounding class to be assigned the value of the corresponding actual parameter, unless $id$ is a final variable that has already been initialized, in which case a runtime error occurs. |
@@ -1101,18 +1238,21 @@ class A \{ |
\} |
\end{dartCode} |
+\LMHash{} |
A {\em fresh instance} is an instance whose identity is distinct from any previously allocated instance of its class. A generative constructor always operates on a fresh instance of its immediately enclosing class. |
\commentary{ |
The above holds if the constructor is actually run, as it is by \NEW{}. If a constructor $c$ is referenced by \CONST{}, $c$ may not be run; instead, a canonical object may be looked up. See the section on instance creation (\ref{instanceCreation}). |
} |
+\LMHash{} |
If a generative constructor $c$ is not a redirecting constructor and no body is provided, then $c$ implicitly has an empty body \code{\{\}}. |
\paragraph{Redirecting Constructors} |
-\label{redirectingConstructors} |
+\LMLabel{redirectingConstructors} |
+\LMHash{} |
A generative constructor may be {\em redirecting}, in which case its only action is to invoke another generative constructor. A redirecting constructor has no body; instead, it has a redirect clause that specifies which constructor the invocation is redirected to, and with what arguments. |
\begin{grammar} |
@@ -1128,6 +1268,7 @@ A generative constructor may be {\em redirecting}, in which case its only action |
\paragraph{Initializer Lists} |
+\LMHash{} |
An initializer list begins with a colon, and consists of a comma-separated list of individual {\em initializers}. There are two kinds of initializers. |
\begin{itemize} |
\item A {\em superinitializer} identifies a {\em superconstructor} - that is, a specific constructor of the superclass. Execution of the superinitializer causes the initializer list of the superconstructor to be executed. |
@@ -1152,8 +1293,10 @@ An initializer list begins with a colon, and consists of a comma-separated list |
\end{grammar} |
+\LMHash{} |
Let $k$ be a generative constructor. Then $k$ may include at most one superinitializer in its initializer list or a 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 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 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 an initializer for a final variable $f$ whose declaration includes an initialization expression. |
+\LMHash{} |
Each final instance variable $f$ declared in the immediately enclosing class must have an initializer in $k$'s initializer list unless it has already been initialized by one of the following means: |
\begin{itemize} |
\item Initialization at the declaration of $f$. |
@@ -1166,21 +1309,26 @@ or a static warning occurs. It is a compile-time error if $k$'s initializer list |
\commentary{The initializer list may of course contain an initializer for any instance variable declared by the immediately surrounding class, even if it is not final. |
} |
+\LMHash{} |
It is a compile-time error if a generative constructor of class \code{Object} includes a superinitializer. |
+\LMHash{} |
Execution of a generative constructor $k$ is always done with respect to a set of bindings for its formal parameters and with \THIS{} bound to a fresh instance $i$ and the type parameters of the immediately enclosing class bound to a set of actual type arguments $V_1, \ldots , V_m$. |
\commentary{These bindings are usually determined by the instance creation expression that invoked the constructor (directly or indirectly). However, they may also be determined by a reflective call,. |
} |
+\LMHash{} |
If $k$ is redirecting then its redirect clause has the form |
\THIS{}$.g(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: a_{n+k})$ |
where $g$ identifies another generative constructor of the immediately surrounding class. Then execution of $k$ proceeds by evaluating the argument list $(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: a_{n+k})$, and then executing $g$ with respect to the bindings resulting from the evaluation of $(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: a_{n+k})$ and with \THIS{} bound to $i$ and the type parameters of the immediately enclosing class bound to $V_1, \ldots , V_m$. |
+\LMHash{} |
Otherwise, execution proceeds as follows: |
+\LMHash{} |
%First, a fresh instance (\ref{generativeConstructors}) $i$ of the immediately enclosing class is allocated. Next, the instance variable declarations of the immediately enclosing class are visited in the order they appear in the program text. For each such declaration $d$, if $d$ has the form \code{$finalConstVarOrType$ $v$ = $e$; } then the instance variable $v$ of $i$ is bound to the value of $e$ (which is necessarily a compile-time constant). |
%Next, a |
Any initializing formals declared in $k$'s parameter list are executed in the order they appear in the program text. |
@@ -1190,18 +1338,23 @@ Then, $k$'s initializers are executed in the order they appear in the program. |
\rationale {We could observe the order by side effecting external routines called. So we need to specify the order.} |
+\LMHash{} |
After all the initializers have completed, the body of $k$ is executed in a scope where \THIS{} is bound to $i$. Execution of the body begins with execution of the body of the superconstructor with \THIS{} bound to $i$, the type parameters of the immediately enclosing class bound to a set of actual type arguments $V_1, \ldots , V_m$ and the formal parameters bindings determined by the argument list of the superinitializer of $k$. |
\rationale{ |
This process ensures that no uninitialized final field is ever seen by code. Note that \THIS{} is not in scope on the right hand side of an initializer (see \ref{this}) so no instance method can execute during initialization: an instance method cannot be directly invoked, nor can \THIS{} be passed into any other code being invoked in the initializer. |
} |
+\LMHash{} |
Execution of an initializer of the form \code{\THIS{}.$v$ = $e$} proceeds as follows: |
+\LMHash{} |
First, the expression $e$ is evaluated to an object $o$. Then, the instance variable $v$ of the object denoted by \THIS{} is bound to $o$, unless $v$ is a final variable that has already been initialized, in which case a runtime error occurs. In checked mode, it is a dynamic type error if $o$ is not \NULL{} and the interface of the class of $o$ is not a subtype of the actual type of the field $v$. |
+\LMHash{} |
An initializer of the form \code{$v$ = $e$} is equivalent to an initializer of the form \code{\THIS{}.$v$ = $e$}. |
+\LMHash{} |
Execution of a superinitializer of the form |
\SUPER{}$(a_1, \ldots, a_n, x_{n+1}: a_{n+1}, \ldots, x_{n+k}: a_{n+k})$ |
@@ -1210,17 +1363,22 @@ Execution of a superinitializer of the form |
proceeds as follows: |
+\LMHash{} |
First, the argument list $(a_1, \ldots, a_n, x_{n+1}: a_{n+1}, \ldots, x_{n+k}: a_{n+k})$ is evaluated. |
+\LMHash{} |
Let $C$ be the class in which the superinitializer appears and let $S$ be the superclass of $C$. If $S$ is generic (\ref{generics}), let $U_1, , \ldots, U_m$ be the actual type arguments passed to $S$ in the superclass clause of $C$. |
+\LMHash{} |
Then, the initializer list of the constructor $S$ (respectively $S.id$) is executed with respect to the bindings that resulted from the evaluation of the argument list, with \THIS{} bound to the current binding of \THIS{}, and the type parameters (if any) of class $S$ bound to the current bindings of $U_1, , \ldots, U_m$. |
+\LMHash{} |
It is a compile-time error if class $S$ does not declare a generative constructor named $S$ (respectively $S.id$). |
\subsubsection{Factories} |
-\label{factories} |
+\LMLabel{factories} |
+\LMHash{} |
A {\em factory} is a constructor prefaced by the built-in identifier (\ref{identifierReference}) \FACTORY{}. |
\begin{grammar} |
@@ -1232,10 +1390,13 @@ A {\em factory} is a constructor prefaced by the built-in identifier (\ref{iden |
%The enclosing scope of a factory constructor is the static scope \ref{} of the class in which it is declared. |
+\LMHash{} |
The {\em return type} of a factory whose signature is of the form \FACTORY{} $M$ or the form \FACTORY{} $M.id$ is $M$ if $M$ is not a generic type; otherwise the return type is $M <T_1, \ldots, T_n>$ where $T_1, \ldots, T_n$ are the type parameters of the enclosing class |
+\LMHash{} |
It is a compile-time error if $M$ is not the name of the immediately enclosing class. |
+\LMHash{} |
In checked mode, it is a dynamic type error if a factory returns a non-null object whose type is not a subtype of its actual (\ref{actualTypeOfADeclaration}) return type. |
\rationale{It seems useless to allow a factory to return null. But it is more uniform to allow it, as the rules currently do.} |
@@ -1246,8 +1407,9 @@ Factories can produce instances that are not freshly allocated: they can come fr |
} |
\paragraph{Redirecting Factory Constructors} |
-\label{redirectingFactoryConstructors} |
+\LMLabel{redirectingFactoryConstructors} |
+\LMHash{} |
A {\em redirecting factory constructor} specifies a call to a constructor of another class that is to be used whenever the redirecting constructor is called. |
\begin{grammar} |
@@ -1256,16 +1418,19 @@ A {\em redirecting factory constructor} specifies a call to a constructor of ano |
. |
\end{grammar} |
+\LMHash{} |
Calling a redirecting factory constructor $k$ causes the constructor $k^\prime$ denoted by $type$ (respectively, $type.identifier$) to be called with the actual arguments passed to $k$, and returns the result of $k^\prime$ as the result of $k$. The resulting constructor call is governed by the same rules as an instance creation expression using \NEW{} (\ref{instanceCreation}). |
\commentary{ |
It follows that if $type$ or $type.id$ are not defined, or do not refer to a class or constructor, a dynamic error occurs, as with any other undefined constructor call. The same holds if $k$ is called with fewer required parameters or more positional parameters than $k^\prime$ expects, or if $k$ is called with a named parameter that is not declared by $k^\prime$. |
} |
+\LMHash{} |
It is a compile-time error if $k$ explicitly specifies a default value for an optional parameter.\commentary{ |
Default values specified in $k$ would be ignored, since it is the {\em actual} parameters that are passed to $k^\prime$. Hence, default values are disallowed. |
} |
+\LMHash{} |
It is a run-time error if a redirecting factory constructor redirects to itself, either directly or indirectly via a sequence of redirections. %does not redirect to a non-redirecting factory constructor or to a generative constructor in a finite number of steps. |
% Make this a runtime error so deferred loading works |
@@ -1275,6 +1440,7 @@ If a redirecting factory $F_1$ redirects to another redirecting factory $F_2$ an |
} |
+\LMHash{} |
It is a static warning if $type$ does not denote a class accessible in the current scope; if $type$ does denote such a class $C$ it is a static warning if the referenced constructor (be it $type$ or $type.id$) is not a constructor of $C$. |
\commentary{ |
@@ -1316,20 +1482,24 @@ At first glance, one might think that ordinary factory constructors could simply |
} |
%\end{dartCode} |
+\LMHash{} |
It is a compile-time error if $k$ is prefixed with the \CONST{} modifier but $k^\prime$ is not a constant constructor (\ref{constantConstructors}). |
+\LMHash{} |
It is a static warning if the function type of $k^\prime$ is not a subtype of the type of $k$. |
\commentary{ |
This implies that the resulting object conforms to the interface of the immediately enclosing class of $k$. |
} |
+\LMHash{} |
It is a static type warning if any of the type arguments to $k^\prime$ are not subtypes of the bounds of the corresponding formal type parameters of $type$. |
\subsubsection{Constant Constructors} |
-\label{constantConstructors} |
+\LMLabel{constantConstructors} |
+\LMHash{} |
A {\em constant constructor} may be used to create compile-time constant (\ref{constants}) objects. A constant constructor is prefixed by the reserved word \CONST{}. |
\begin{grammar} |
@@ -1345,22 +1515,27 @@ A {\em constant constructor} may be used to create compile-time constant (\ref{ |
\commentary{All the work of a constant constructor must be handled via its initializers.} |
+\LMHash{} |
It is a compile-time error if a constant constructor is declared by a class that has a non-final instance variable. |
\commentary{ |
The above refers to both locally declared and inherited instance variables. |
} |
+\LMHash{} |
It is a compile-time error if a constant constructor is declared by a class $C$ if any instance variable declared in $C$ is initialized with an expression that is not a constant expression. |
\commentary { |
A superclass of $C$ cannot declare such an initializer either, because it must necessarily declare constant constructor as well (unless it is \code{Object}, which declares no instance variables). |
} |
+\LMHash{} |
The superinitializer that appears, explicitly or implicitly, in the initializer list of a constant constructor must specify a constant constructor of the superclass of the immediately enclosing class or a compile-time error occurs. |
+\LMHash{} |
Any expression that appears within the initializer list of a constant constructor must be a potentially constant expression, or a compile-time error occurs. |
+\LMHash{} |
A {\em potentially constant expression} is an expression $e$ that would be a valid constant expression if all formal parameters of $e$'s immediately enclosing constant constructor were treated as compile-time constants that were guaranteed to evaluate to an integer, boolean or string value as required by their immediately enclosing superexpression. |
\commentary{ |
@@ -1424,15 +1599,18 @@ All of the illegal constructors of \code{D} above could not be sensibly invoked |
Careful readers will of course worry about cases where the actual arguments to \code{C()} are constants, but are not numeric. This is precluded by the following rule, combined with the rules for evaluating constant objects (\ref{const}). |
} |
+\LMHash{} |
When invoked from a constant object expression, a constant constructor must throw an exception if any of its actual parameters is a value that would prevent one of the potentially constant expressions within it from being a valid compile-time constant. |
%Discuss External Constructors in ne subsubsection here |
\subsection{Static Methods} |
-\label{staticMethods} |
+\LMLabel{staticMethods} |
+\LMHash{} |
{\em Static methods} are functions, other than getters or setters, whose declarations are immediately contained within a class declaration and that are declared \STATIC{}. The static methods of a class $C$ are those static methods declared by $C$. |
+\LMHash{} |
The effect of a static method declaration in class $C$ is to add an instance method with the same name and signature to the \code{Type} object for class $C$ that forwards (\ref{functionDeclarations}) to the static method. |
\rationale{ |
@@ -1441,6 +1619,7 @@ Inheritance of static methods has little utility in Dart. Static methods cannot |
Of course, the entire notion of static methods is debatable, but it is retained here because so many programmers are familiar with it. Dart static methods may be seen as functions of the enclosing library. |
} |
+\LMHash{} |
It is a static warning if a class $C$ declares a static method named $n$ and has a setter named $n=$. |
%It is a static warning if a class has a static method with the same name as a static member of one of its superclasses. |
@@ -1453,8 +1632,9 @@ It is a static warning if a class $C$ declares a static method named $n$ and has |
\subsection{Static Variables} |
-\label{staticVariables} |
+\LMLabel{staticVariables} |
+\LMHash{} |
{\em Static variables} are variables whose declarations are immediately contained within a class declaration and that are declared \STATIC{}. The static variables of a class $C$ are those static variables declared by $C$. |
%A static variable declaration of one of the forms \code{\STATIC{} $T$ $v$;}, \code{\STATIC{} $T$ $v$ = $e$;} , \code{\STATIC{} \CONST{} $T$ $v$ = $e$;} or \code{\STATIC{} \FINAL{} $T$ $v$ = $e$;} always induces an implicit static getter function (\ref{getters}) with signature |
@@ -1485,7 +1665,7 @@ It is a static warning if a class $C$ declares a static method named $n$ and has |
%Extrernal static functions, getters, setters |
%\subsubsection{Evaluation of Implicit Static Variable Getters} |
-%\label{evaluationOfStaticVariableGetters} |
+%\LMLabel{evaluationOfStaticVariableGetters} |
%Let $d$ be the declaration of a static variable $v$. The implicit getter method of $v$ executes as follows: |
%\begin{itemize} |
@@ -1498,14 +1678,16 @@ It is a static warning if a class $C$ declares a static method named $n$ and has |
\subsection{Superclasses} |
-\label{superclasses} |
+\LMLabel{superclasses} |
+\LMHash{} |
The superclass of a class $C$ that has a with clause \code{\WITH{} $M_1, \ldots, M_k$} and an extends clause \code{\EXTENDS{} S} is the application of mixin (\ref{mixins}) $M_k* \cdots * M_1$ to S. 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 \code{\EXTENDS{} Object}, and the rules above apply. |
\end{itemize} |
+\LMHash{} |
It is a compile-time error to specify an \EXTENDS{} clause for class \code{Object}. |
\begin{grammar} |
@@ -1519,6 +1701,7 @@ It is a compile-time error to specify an \EXTENDS{} clause for class \code{Objec |
%This means that in a generic class, the type parameters of the generic are available in the superclass clause. |
%} |
+\LMHash{} |
%It is a compile-time error if the \EXTENDS{} clause of a class $C$ includes a type expression that does not denote a class available in the lexical scope of $C$. |
It is a compile-time error if the \EXTENDS{} clause of a class $C$ specifies an enumerated type (\ref{enums}), a malformed type or a deferred type (\ref{staticTypes}) as a superclass. |
% too strict? Do we e want extends List<Undeclared> to work as List<dynamic>? |
@@ -1535,23 +1718,26 @@ class G$<$T$>$ extends T \{\} |
\end{dartCode} |
+\LMHash{} |
A class $S$ is {\em a superclass} of a class $C$ iff either: |
\begin{itemize} |
\item $S$ is the superclass of $C$, or |
\item $S$ is a superclass of a class $S^{\prime}$ and $S^{\prime}$ is a superclass of $C$. |
\end{itemize} |
+\LMHash{} |
It is a compile-time error if a class $C$ is a superclass of itself. |
\subsubsection{Inheritance and Overriding} |
- \label{inheritanceAndOverriding} |
+ \LMLabel{inheritanceAndOverriding} |
%A class $C$ {\em inherits} any accessible instance members of its superclass that are not overridden by members declared in $C$. |
+\LMHash{} |
Let $C$ be a class, let $A$ be a superclass of $C$, and let $S_1 \ldots S_k$ be superclasses of $C$ that are also subclasses of $A$. $C$ {\em inherits} all accessible instance members of $A$ that have not been overridden by a declaration in $C$ or in at least one of $S_1 \ldots S_k$. |
\rationale { |
@@ -1560,8 +1746,10 @@ to the library $L_1$ of $C$, whereas $S$ comes from a different library $L_2$, b |
the superclass chain of $S$ includes a class declared in $L_1$. |
} |
+\LMHash{} |
A class may override instance members that would otherwise have been inherited from its superclass. |
+\LMHash{} |
Let $C = S_0$ be a class declared in library $L$, and let $\{S_1 \ldots S_k\}$ be the set of all superclasses of $C$, where $S_i$ is the superclass of $S_{i-1}$ for $i \in 1 .. k$. Let $C$ declare a member $m$, and let $m^\prime$ be a member of $S_j, j \in 1 .. k$, that has the same name as $m$, such that $m^\prime$ is accessible to $L$. Then $m$ overrides $m^\prime$ if $m^\prime$ is not already overridden by a member of at least one of $S_1 \ldots S_{j-1}$ and neither $m$ nor $m^\prime$ are fields. |
%Let $C$ be a class declared in library $L$, with superclass $S$ and let $C$ declare an instance member $m$, and assume $S$ declares an instance member $m^\prime$ with the same name as $m$. Then $m$ {\em overrides} $m^\prime$ iff $m^\prime$ is accessible (\ref{privacy}) to $L$, $m$ has the same name as $m^\prime$ and neither $m$ nor $m^\prime$ are fields. |
@@ -1571,6 +1759,7 @@ Let $C = S_0$ be a class declared in library $L$, and let $\{S_1 \ldots S_k\}$ b |
\rationale{Again, a local definition of overriding would be preferable, but fails to account for library privacy. |
} |
+\LMHash{} |
Whether an override is legal or not is described elsewhere in this specification (see \ref{instanceMethods}, \ref{getters} and \ref{setters}). |
\commentary{For example getters may not legally override methods and vice versa. Setters never override methods or getters, and vice versa, because their names always differ. |
@@ -1584,6 +1773,7 @@ It is nevertheless convenient to define the override relation between members in |
Note that instance variables do not participate in the override relation, but the getters and setters they induce do. Also, getters don't override setters and vice versa. Finally, static members never override anything. |
} |
+\LMHash{} |
It is a static warning if a non-abstract class inherits an abstract method. |
\commentary { |
@@ -1631,9 +1821,10 @@ If two members override each other, it is a static warning if the overriding mem |
%Can we have abstract getters and setters? |
\subsection{ Superinterfaces} |
-\label{superinterfaces} |
+\LMLabel{superinterfaces} |
% what about rules about classes that fail to implement their interfaces? |
+\LMHash{} |
A class has a set of direct superinterfaces. This set includes the interface of its superclass and the interfaces specified in the the \IMPLEMENTS{} clause of the class. |
% and any superinterfaces specified by interface injection (\ref{interfaceInjection}). \Q{The latter needs to be worded carefully - when do interface injection clauses execute and in what scope?} |
@@ -1643,6 +1834,7 @@ A class has a set of direct superinterfaces. This set includes the interface of |
. |
\end{grammar} |
+\LMHash{} |
It is a compile-time error if the \IMPLEMENTS{} clause of a class $C$ specifies a type variable as a superinterface. It is a compile-time error if the \IMPLEMENTS{} clause of a class $C$ specifies an enumerated type (\ref{enums}), a malformed type or deferred type (\ref{staticTypes}) as a superinterface It is a compile-time error if the \IMPLEMENTS{} clause of a class $C$ specifies type \DYNAMIC{} as a superinterface. It is a compile-time error if the \IMPLEMENTS{} clause of a class $C$ specifies a type $T$ as a superinterface more than once. |
It is a compile-time error if the superclass of a class $C$ is specified as a superinterface of $C$. |
@@ -1650,8 +1842,10 @@ It is a compile-time error if the superclass of a class $C$ is specified as a su |
One might argue that it is harmless to repeat a type in the superinterface list, so why make it an error? The issue is not so much that the situation described in program source is erroneous, but that it is pointless. As such, it is an indication that the programmer may very well have meant to say something else - and that is a mistake that should be called to her or his attention. Nevertheless, we could simply issue a warning; and perhaps we should and will. That said, problems like these are local and easily corrected on the spot, so we feel justified in taking a harder line. |
} |
+\LMHash{} |
It is a compile-time error if the interface of a class $C$ is a superinterface of itself. |
+\LMHash{} |
Let $C$ be a concrete class that does not declare its own \code{noSuchMethod()} method. |
It is a static warning if the implicit interface of $C$ includes an instance member $m$ of type $F$ and $C$ does not declare or inherit a corresponding non-abstract instance member $m$ of type $F'$ such that $F' <: F$. |
@@ -1670,6 +1864,7 @@ In addition, it may be useful to suppress these warnings if \code{noSuchMethod} |
noSuchMethod(inv) =$>$ \SUPER.noSuchMethod(inv); |
\end{dartCode} |
+\LMHash{} |
It is a static warning if the implicit interface of a class $C$ includes an instance member $m$ of type $F$ and $C$ declares or inherits a corresponding instance member $m$ of type $F'$ if $F'$ is not a subtype of $F$. |
\rationale{ |
@@ -1704,23 +1899,27 @@ However, if a class does explicitly declare a member that conflicts with its sup |
\section{Interfaces} |
-\label{interfaces} |
+\LMLabel{interfaces} |
+\LMHash{} |
An {\em interface} defines how one may interact with an object. An interface has methods, getters and setters and a set of superinterfaces. |
\subsection{Superinterfaces} |
-\label{interfaceSuperinterfaces} |
+\LMLabel{interfaceSuperinterfaces} |
+\LMHash{} |
An interface has a set of direct superinterfaces. |
+\LMHash{} |
An interface $J$ is a superinterface of an interface $I$ iff either $J$ is a direct superinterface of $I$ or $J$ is a superinterface of a direct superinterface of $I$. |
\subsubsection{Inheritance and Overriding} |
-\label{interfaceInheritanceAndOverriding} |
+\LMLabel{interfaceInheritanceAndOverriding} |
+\LMHash{} |
Let $J$ be an interface and $K$ be a library. We define $inherited(J, K)$ to be the set of members $m$ such that all of the following hold: |
\begin{itemize} |
\item $m$ is accessible to $K$ and |
@@ -1732,6 +1931,7 @@ Let $J$ be an interface and $K$ be a library. We define $inherited(J, K)$ to be |
\item $m$ is not overridden by $J$. |
\end{itemize} |
+\LMHash{} |
Furthermore, we define $overrides(J, K)$ to be the set of members $m^\prime$ such that all of the following hold: |
\begin{itemize} |
\item $J$ is the implicit interface of a class $C$. |
@@ -1746,10 +1946,13 @@ Furthermore, we define $overrides(J, K)$ to be the set of members $m^\prime$ su |
\end{itemize} |
+\LMHash{} |
Let $I$ be the implicit interface of a class $C$ declared in library $L$. $I$ {\em inherits} all members of $inherited(I, L)$ and $I$ {\em overrides} $m^\prime$ if $m^\prime \in overrides(I, L)$. |
+\LMHash{} |
All the static warnings pertaining to the overriding of instance members given in section \ref{classes} above hold for overriding between interfaces as well. |
+\LMHash{} |
It is a static warning if $m$ is a method and $m^\prime$ is a getter, or if $m$ is a getter and $m^\prime$ is a method. |
@@ -1760,10 +1963,13 @@ It is a static warning if $m$ is a method and $m^\prime$ is a getter, or if $m$ |
% tighten definition? do we need chain as for classes? Definition for interface override? |
+\LMHash{} |
However, if the above rules would cause multiple members $m_1, \ldots, m_k$ with the same name $n$ to be inherited (because identically named members existed in several superinterfaces) then at most one member is inherited. |
+\LMHash{} |
If some but not all of the $m_i, 1 \le i \le k$ are getters none of the $m_i$ are inherited, and a static warning is issued. |
+\LMHash{} |
Otherwise, if the static types $T_1, \ldots, T_k$ of the members $m_1, \ldots, m_k$ are not identical, then there must be a member $m_x$ such that $T_x <: T_i, 1 \le x \le k$ for all $i \in 1..k$, or a static type warning occurs. The member that is inherited is $m_x$, if it exists; otherwise: |
let $numberOfPositionals(f)$ denote the number of positional parameters of a function $f$, and let $numberOfRequiredParams(f)$ denote the number of required parameters of a function $f$. Furthermore, let $s$ denote the set of all named parameters of the $m_1, \ldots, m_k$. Then let |
@@ -1771,6 +1977,7 @@ $h = max(numberOfPositionals(m_i)), $ |
$r = min(numberOfRequiredParams(m_i)), i \in 1..k$. |
+\LMHash{} |
Then $I$ has a method named $n$, with $r$ required parameters of type \DYNAMIC{}, $h$ positional parameters of type \DYNAMIC{}, named parameters $s$ of type \DYNAMIC{} and return type \DYNAMIC{}. |
@@ -1787,11 +1994,13 @@ The current solution is a tad complex, but is robust in the face of type annotat |
% Need warnings if overrider conflicts with overriddee either because signatures are incompatible or because done is a method and one is a getter or setter. |
\section{Mixins} |
-\label{mixins} |
+\LMLabel{mixins} |
+\LMHash{} |
A mixin describes the difference between a class and its superclass. A mixin is always derived from an existing class declaration. |
+\LMHash{} |
It is a compile-time error if a declared or derived mixin refers to \SUPER{}. It is a compile-time error if a declared or derived mixin explicitly declares a constructor. It is a compile-time error if a mixin is derived from a class whose superclass is not \code{Object}. |
\rationale{ |
@@ -1807,8 +2016,9 @@ Reasonable answers exist for all these issues, but their implementation is non-t |
} |
\subsection{Mixin Application} |
-\label{mixinApplication} |
+\LMLabel{mixinApplication} |
+\LMHash{} |
A mixin may be applied to a superclass, yielding a new class. Mixin application occurs when a mixin is mixed into a class declaration via its \WITH{} clause. The mixin application may be used to extend a class per section (\ref{classes}); alternately, a class may be defined as a mixin application as described in this section. It is a compile-time error if the \WITH{} clause of a mixin application $C$ includes a deferred type expression. |
@@ -1821,21 +2031,28 @@ A mixin may be applied to a superclass, yielding a new class. Mixin application |
. |
\end{grammar} |
+\LMHash{} |
A mixin application of the form \code{$S$ \WITH{} $M$;} defines a class $C$ with superclass $S$. |
+\LMHash{} |
A mixin application of the form \code{$S$ \WITH{} $M_1, \ldots, M_k$;} defines a class $C$ whose superclass is the application of the mixin composition (\ref{mixinComposition}) $M_{k-1} * \ldots * M_1$ to $S$. |
+\LMHash{} |
In both cases above, $C$ declares the same instance members as $M$ (respectively, $M_k$). If any of the instance fields of $M$ (respectively, $M_k$) have initializers, they are executed in the scope of $M$ (respectively, $M_k$) to initialize the corresponding fields of $C$. |
+\LMHash{} |
For each generative constructor named $q_i(T_{i1}$ $ a_{i1}, \ldots , T_{ik_i}$ $ a_{ik_i}), i \in 1..n$ of $S$, $C$ has an implicitly declared constructor named |
$q'_i = [C/S]q_i$ of the form |
$q'_i(a_{i1}, \ldots , a_{ik_i}):\SUPER(a_{i1}, \ldots , a_{ik_i});$. |
+\LMHash{} |
If the mixin application declares support for interfaces, the resulting class implements those interfaces. |
+\LMHash{} |
It is a compile-time error if $S$ is an enumerated type (\ref{enums}) or a malformed type. It is a compile-time error if $M$ (respectively, any of $M_1, \ldots, M_k$) is an enumerated type (\ref{enums}) or a malformed type. It is a compile time error if a well formed mixin cannot be derived from $M$ (respectively, from each of $M_1, \ldots, M_k$). |
+\LMHash{} |
Let $K$ be a class declaration with the same constructors, superclass and interfaces as $C$, and the instance members declared by $M$ (respectively $M_1, \ldots, M_k$). It is a static warning if the declaration of $K$ would cause a static warning. It is a compile-time error if the declaration of $K$ would cause a compile-time error. |
\commentary{ |
@@ -1843,17 +2060,19 @@ If, for example, $M$ declares an instance member $im$ whose type is at odds with |
} |
+\LMHash{} |
The effect of a class definition of the form \code{\CLASS{} $C$ = $M$; } or the form |
\code{\CLASS{} $C<T_1, \ldots, T_n>$ = $M$; } in library $L$ is to introduce the name $C$ into the scope of $L$, bound to the class (\ref{classes}) defined by the mixin application $M$. The name of the class is also set to $C$. Iff the class is prefixed by the built-in identifier \ABSTRACT{}, the class being defined is an abstract class. |
\subsection{Mixin Composition} |
-\label{mixinComposition} |
+\LMLabel{mixinComposition} |
\rationale{ |
Dart does not directly support mixin composition, but the concept is useful when defining how the superclass of a class with a mixin clause is created. |
} |
+\LMHash{} |
The {\em composition of two mixins}, $M_1<T_1 \ldots T_{k_{M_1}}>$ and $M_2<U_1 \ldots U_{k_{M_2}}>$, written $M_1<T_1 \ldots T_{k_{M_1}}> * M_2<U_1 \ldots U_{k_{M_2}}>$ defines an anonymous mixin such that for any class $S<V_1 \ldots V_{k_S}>$, the application of |
$M_1<T_1 \ldots T_{k_{M_1}}> * M_2<U_1 \ldots U_{k_{M_2}}>$ |
@@ -1878,6 +2097,7 @@ and $Id_1$ and $Id_2$ are unique identifiers that do not exist anywhere in the p |
The classes produced by mixin composition are regarded as abstract because they cannot be instantiated independently. They are only introduced as anonymous superclasses of ordinary class declarations and mixin applications. Consequently, no warning is given if a mixin composition includes abstract members, or incompletely implements an interface. |
} |
+\LMHash{} |
Mixin composition is associative. |
@@ -1887,8 +2107,9 @@ Note that any subset of $M_1$, $M_2$ and $S$ may or may not be generic. For any |
\section{Enums} |
-\label{enums} |
+\LMLabel{enums} |
+\LMHash{} |
An {\em enumerated type}, or {\em enum}, is used to represent a fixed number of constant values. |
\begin{grammar} |
@@ -1897,6 +2118,7 @@ metadata \ENUM{} id `\{' id [`,' id]* [`,'] `\}' |
. |
\end{grammar} |
+\LMHash{} |
The declaration of an enum of the form \code{metadata \ENUM{} E \{ id$_0$, \ldots id$_{n-1}$\};} |
has the same effect as a class declaration |
@@ -1917,8 +2139,9 @@ It is also a compile-time error to subclass, mix-in or implement an enum or to e |
} |
\section{Generics} |
-\label{generics} |
+\LMLabel{generics} |
+\LMHash{} |
A class declaration (\ref{classes}) or type alias (\ref{typedef}) |
$G$ may be {\em generic}, that is, $G$ may have formal type parameters declared. A generic declaration induces a family of declarations, one for each set of actual type parameters provided in the program. |
@@ -1931,8 +2154,10 @@ $G$ may be {\em generic}, that is, $G$ may have formal type parameters declared. |
. |
\end{grammar} |
+\LMHash{} |
A type parameter $T$ may be suffixed with an \EXTENDS{} clause that specifies the {\em upper bound} for $T$. If no \EXTENDS{} clause is present, the upper bound is \code{Object}. It is a static type warning if a type parameter is a supertype of its upper bound. The bounds of type variables are a form of type annotation and have no effect on execution in production mode. |
+\LMHash{} |
The type parameters of a generic $G$ are in scope in the bounds of all of the type parameters of $G$. The type parameters of a generic class declaration $G$ are also in scope in the \EXTENDS{} and \IMPLEMENTS{} clauses of $G$ (if these exist) and in the body of $G$. However, a type parameter is considered to be a malformed type when referenced by a static member. |
\rationale{ |
@@ -2008,7 +2233,7 @@ The normative versions of these are given in the appropriate sections of this s |
%\subsection{Interface Injection} |
-%\label{interfaceInjection} |
+%\LMLabel{interfaceInjection} |
%An {\em interface injection declaration} causes a pre-existing class $S$ to be considered a subinterface of another interface $I$. It is a static type warning if $S$ is not a structural subtype of $I$. However, the subinterface relations implied by the interface injection declaration are considered to hold by both the typechecker and the runtime, regardless. |
@@ -2038,8 +2263,9 @@ The normative versions of these are given in the appropriate sections of this s |
\section{Metadata} |
-\label{metadata} |
+\LMLabel{metadata} |
+\LMHash{} |
Dart supports metadata which is used to attach user defined annotations to program structures. |
\begin{grammar} |
@@ -2048,12 +2274,14 @@ Dart supports metadata which is used to attach user defined annotations to progr |
. |
\end{grammar} |
+\LMHash{} |
Metadata consists of a series of annotations, each of which begin with the character @, followed by a constant expression that starts with an identifier. It is a compile time error if the expression is not one of the following: |
\begin{itemize} |
\item A reference to a compile-time constant variable. |
\item A call to a constant constructor. |
\end{itemize} |
+\LMHash{} |
Metadata is associated with the abstract syntax tree of the program construct $p$ that immediately follows the metadata, assuming $p$ is not itself metadata or a comment. Metadata can be retrieved at runtime via a reflective call, provided the annotated program construct $p$ is accessible via reflection. |
\commentary{ |
@@ -2068,14 +2296,17 @@ It is important that no runtime overhead be incurred by the introduction of meta |
It is possible to associate metadata with constructs that may not be accessible via reflection, such as local variables (though it is conceivable that in the future, richer reflective libraries might provide access to these as well). This is not as useless as it might seem. As noted above, the data can be retrieved statically if source code is available. |
} |
+\LMHash{} |
Metadata can appear before a library, part header, class, typedef, type parameter, constructor, factory, function, field, parameter, or variable declaration and before an import, export or part directive. |
+\LMHash{} |
The constant expression given in an annotation is type checked and evaluated in the scope surrounding the declaration being annotated. |
\section{Expressions} |
-\label{expressions} |
+\LMLabel{expressions} |
+\LMHash{} |
An {\em expression} is a fragment of Dart code that can be evaluated at run time to yield a {\em value}, which is always an object. Every expression has an associated static type (\ref{staticTypes}). Every value has an associated dynamic type (\ref{dynamicTypeSystem}). |
@@ -2110,6 +2341,7 @@ An {\em expression} is a fragment of Dart code that can be evaluated at run time |
\end{grammar} |
+\LMHash{} |
An expression $e$ may always be enclosed in parentheses, but this never has any semantic effect on $e$. |
\commentary{ |
@@ -2117,8 +2349,9 @@ Sadly, it may have an effect on the surrounding expression. Given a class $C$ wi |
} |
\subsubsection{Object Identity} |
- \label{objectIdentity} |
+ \LMLabel{objectIdentity} |
+\LMHash{} |
The predefined Dart function \cd{identical()} is defined such that \code{identical($c_1$, $c_2$)} iff: |
\begin{itemize} |
\item $c_1$ evaluates to either \NULL{} or an instance of \code{bool} and \code{$c_1$ == $c_2$}, OR |
@@ -2152,10 +2385,12 @@ The rules for identity make it impossible for a Dart programmer to observe wheth |
\subsection{Constants} |
-\label{constants} |
+\LMLabel{constants} |
+\LMHash{} |
A {\em constant expression} is an expression whose value can never change, and that can be evaluated entirely at compile time. |
+\LMHash{} |
A constant expression is one of the following: |
\begin{itemize} |
\item A literal number (\ref{numbers}). |
@@ -2190,6 +2425,7 @@ A constant expression is one of the following: |
% designed so constants do not depend on check diode being on or not. |
+\LMHash{} |
It is a compile-time error if an expression is required to be a constant expression but its evaluation would raise an exception. |
% so, checked mode? analyzers? editor/development compilers? |
@@ -2232,6 +2468,7 @@ The treatment of \NULL{} merits some discussion. Consider \code{\NULL{} + 2}. T |
\end{enumerate} |
} |
+\LMHash{} |
It is a compile-time error if the value of a compile-time constant expression depends on itself. |
\commentary{ |
@@ -2261,8 +2498,9 @@ As an example, consider: |
\subsection{Null} |
-\label{null} |
+\LMLabel{null} |
+\LMHash{} |
The reserved word \NULL{} denotes the {\em null object}. |
%\Q{Any methods, such as \code{isNull}?} |
@@ -2272,9 +2510,11 @@ The reserved word \NULL{} denotes the {\em null object}. |
. |
\end{grammar} |
+\LMHash{} |
The null object is the sole instance of the built-in class \code{Null}. Attempting to instantiate \code{Null} causes a run-time error. It is a compile-time error for a class to attempt to extend or implement \code{Null}. |
Invoking a method on \NULL{} yields a \code{NoSuchMethodError} unless the method is explicitly implemented by class \code{Null}. |
+\LMHash{} |
The static type of \NULL{} is $\bot$. |
\rationale{The decision to use $\bot$ instead of \code{Null} allows \NULL{} to be be assigned everywhere without complaint by the static checker. |
@@ -2282,8 +2522,9 @@ The static type of \NULL{} is $\bot$. |
\subsection{Numbers} |
-\label{numbers} |
+\LMLabel{numbers} |
+\LMHash{} |
A {\em numeric literal} is either a decimal or hexadecimal integer of arbitrary size, or a decimal double. |
\begin{grammar} |
@@ -2309,24 +2550,30 @@ A {\em numeric literal} is either a decimal or hexadecimal integer of arbitrary |
. |
\end{grammar} |
+\LMHash{} |
If a numeric literal begins with the prefix `0x' or `0X', it denotes the hexadecimal integer represented by the part of the literal following `0x' (respectively `0X'). Otherwise, if the numeric literal does not include a decimal point it denotes a decimal integer. Otherwise, the numeric literal denotes a 64 bit double precision floating point number as specified by the IEEE 754 standard. |
+\LMHash{} |
In principle, the range of integers supported by a Dart implementations is unlimited. In practice, it is limited by available memory. Implementations may also be limited by other considerations. |
\commentary{ |
For example, implementations may choose to limit the range to facilitate efficient compilation to Javascript. These limitations should be relaxed as soon as technologically feasible. |
} |
+\LMHash{} |
It is a compile-time error for a class to attempt to extend or implement \code{int}. It is a compile-time error for a class to attempt to extend or implement \code{double}. It is a compile-time error for any type other than the types \code{int} and \code{double} to attempt to extend or implement \code{num}. |
+\LMHash{} |
An {\em integer literal} is either a hexadecimal integer literal or a decimal integer literal. Invoking the getter \code{runtimeType} on an integer literal returns the \code{Type} object that is the value of the expression \code{int}. The static type of an integer literal is \code{int}. |
+\LMHash{} |
A {\em literal double} is a numeric literal that is not an integer literal. Invoking the getter \code{runtimeType} on a literal double returns the \code{Type} object that is the value of the expression \code{double}. |
The static type of a literal double is \code{double}. |
\subsection{Booleans} |
-\label{booleans} |
+\LMLabel{booleans} |
+\LMHash{} |
The reserved words \TRUE{} and \FALSE{} denote objects that represent the boolean values true and false respectively. They are the {\em boolean literals}. |
\begin{grammar} |
@@ -2335,17 +2582,20 @@ The reserved words \TRUE{} and \FALSE{} denote objects that represent the boolea |
. |
\end{grammar} |
+\LMHash{} |
Both \TRUE{} and \FALSE{} implement the built-in class \code{bool}. It is a compile-time error for a class to attempt to extend or implement\code{ bool}. |
\commentary{ |
It follows that the two boolean literals are the only two instances of \code{bool}. |
} |
+\LMHash{} |
Invoking the getter \code{runtimeType} on a boolean literal returns the \code{Type} object that is the value of the expression \code{bool}. The static type of a boolean literal is \code{bool}. |
\subsubsection{Boolean Conversion} |
-\label{booleanConversion} |
+\LMLabel{booleanConversion} |
+\LMHash{} |
{\em Boolean conversion} maps any object $o$ into a boolean. Boolean conversion is defined by the function application |
\begin{dartCode} |
@@ -2368,8 +2618,9 @@ Dart also avoids the strange behaviors that can arise due to the interaction of |
} |
\subsection{Strings} |
-\label{strings} |
+\LMLabel{strings} |
+\LMHash{} |
A {\em string} is a sequence of UTF-16 code units. |
\rationale{ |
@@ -2381,6 +2632,7 @@ This decision was made for compatibility with web browsers and Javascript. Earli |
. |
\end{grammar} |
+\LMHash{} |
A string can be either a sequence of single line strings or a multiline string. |
\begin{grammar} |
@@ -2391,6 +2643,7 @@ A string can be either a sequence of single line strings or a multiline string. |
. |
\end{grammar} |
+\LMHash{} |
A single line string is delimited by either matching single quotes or matching double quotes. |
\commentary{ |
@@ -2400,6 +2653,7 @@ Hence, `abc' and ``abc'' are both legal strings, as are `He said ``To be or not |
\commentary{The grammar ensures that a single line string cannot span more than one line of source code, unless it includes an interpolated expression that spans multiple lines. |
} |
+\LMHash{} |
Adjacent |
%single line |
strings are implicitly concatenated to form a single string literal. |
@@ -2472,6 +2726,7 @@ print("A simple sum: 2 + 2 = \$\{2+2\}"); |
\end{grammar} |
+\LMHash{} |
Multiline strings 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 the whitespace characters defined by the production {\em WHITESPACE} \ref{lexicalRules}), possibly prefixed by $\backslash$, then that line is ignored, including the new line at its end. |
@@ -2479,6 +2734,7 @@ Multiline strings are delimited by either matching triples of single quotes or m |
The idea is to ignore whitespace, where whitespace is defined as tabs, spaces and newlines. These can be represented directly, but since for most characters prefixing by backslash is an identity, we allow those forms as well. |
} |
+\LMHash{} |
Strings support escape sequences for special characters. The escapes are: |
\begin{itemize} |
\item $\backslash$n for newline, equivalent to $\backslash$x0A. |
@@ -2496,8 +2752,10 @@ $\backslash$u\{$HEX\_DIGIT_1$ $HEX\_DIGIT_2$\}. |
\item Otherwise, $\backslash k$ indicates the character $k$ for any $k$ not in $\{n, r, f, b, t, v, x, u\}$. |
\end{itemize} |
+\LMHash{} |
Any string may be prefixed with the character `r', indicating that it is a {\em raw string}, in which case no escapes or interpolations are recognized. |
+\LMHash{} |
It is a compile-time error if a non-raw string literal contains a character sequence of the form $\backslash$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 a character sequence of the form $\backslash$u that is not followed by either a sequence of four hexadecimal digits, or by curly brace delimited sequence of hexadecimal digits. |
@@ -2528,11 +2786,13 @@ It is a compile-time error if a non-raw string literal contains a character sequ |
\end{grammar} |
+\LMHash{} |
All string literals implement the built-in class \code{String}. It is a compile-time error for a class to attempt to extend or implement \code{String}. Invoking the getter \code{runtimeType} on a string literal returns the \code{Type} object that is the value of the expression \code{String}. The static type of a string literal is \code{String}. |
\subsubsection{String Interpolation} |
-\label{stringInterpolation} |
+\LMLabel{stringInterpolation} |
+\LMHash{} |
It is possible to embed expressions within non-raw string literals, such that the these expressions are evaluated, and the resulting values are converted into strings and concatenated with the enclosing string. This process is known as {\em string interpolation}. |
\begin{grammar} |
@@ -2544,17 +2804,20 @@ It is possible to embed expressions within non-raw string literals, such that th |
\commentary{The reader will note that the expression inside the interpolation could itself include strings, which could again be interpolated recursively. |
} |
+\LMHash{} |
An unescaped \$ character in a string signifies the beginning of an interpolated expression. The \$ sign may be followed by either: |
\begin{itemize} |
\item A single identifier $id$ that must not contain the \$ character. |
\item An expression $e$ delimited by curly braces. |
\end{itemize} |
+\LMHash{} |
The form \code{\$id} is equivalent to the form \code{\$\{id\}}. An interpolated string \code{`$s_1$\$\{$e$\}$s_2$'} is equivalent to the concatenation of the strings \code{`$s_1$'}, \code{$e$.toString()} and \code{$`s_2$'}. Likewise an interpolated string \code{``$s_1$\$\{e\}$s_2$''} is equivalent to the concatenation of the strings \code{``$s_1$''}, \code{$e$.toString()} and \code{``$s_2$''}. |
\subsection{Symbols} |
-\label{symbols} |
+\LMLabel{symbols} |
+\LMHash{} |
A {\em symbol literal} denotes the name of a declaration in a Dart program. |
\begin{grammar} |
@@ -2562,9 +2825,11 @@ A {\em symbol literal} denotes the name of a declaration in a Dart program. |
`\#' (operator $|$ (identifier (`{\escapegrammar .}' identifier)*)) . |
\end{grammar} |
-A symbol literal \code{\#id} where \code{id} does not begin with an underscore (Ô\code{\_}Õ) is equivalent to the expression \code{\CONST{} Symbol(ÔidÕ)}. |
+\LMHash{} |
+A symbol literal \code{\#id} where \code{id} does not begin with an underscore (`\code{\_}') is equivalent to the expression \code{\CONST{} Symbol(`id')}. |
-A symbol literal \code{\#\_id} evaluates to the object that would be returned by the call \code{mirror.getPrivateSymbol(ÔidÕ)} where mirror is an instance of the class \code{LibraryMirror} defined in the library \code{dart:mirrors}, reflecting the current library. |
+\LMHash{} |
+A symbol literal \code{\#\_id} evaluates to the object that would be returned by the call \code{mirror.getPrivateSymbol(`id')} where mirror is an instance of the class \code{LibraryMirror} defined in the library \code{dart:mirrors}, reflecting the current library. |
\rationale{ |
One may well ask what is the motivation for introducing literal symbols? In some languages, symbols are canonicalized whereas strings are not. However literal strings are already canonicalized in Dart. Symbols are slightly easier to type compared to strings and their use can become strangely addictive, but this is not nearly sufficient justification for adding a literal form to the language. The primary motivation is related to the use of reflection and a web specific practice known as minification. |
@@ -2572,11 +2837,13 @@ One may well ask what is the motivation for introducing literal symbols? In some |
Minification compresses identifiers consistently throughout a program in order to reduce download size. This practice poses difficulties for reflective programs that refer to program declarations via strings. A string will refer to an identifier in the source, but the identifier will no longer be used in the minified code, and reflective code using these truing would fail. Therefore, Dart reflection uses objects of type \code{Symbol} rather than strings. Instances of \code{Symbol} are guaranteed to be stable with repeat to minification. Providing a literal form for symbols makes reflective code easier to read and write. The fact that symbols are easy to type and can often act as convenient substitutes for enums are secondary benefits. |
} |
+\LMHash{} |
The static type of a symbol literal is \code{Symbol}. |
\subsection{Lists} |
-\label{lists} |
+\LMLabel{lists} |
+\LMHash{} |
A {\em list literal} denotes a list, which is an integer indexed collection of objects. |
\begin{grammar} |
@@ -2585,28 +2852,33 @@ A {\em list literal} denotes a list, which is an integer indexed collection of o |
. |
\end{grammar} |
+\LMHash{} |
A list may contain zero or more objects. The number of elements in a list is its size. A list has an associated set of indices. 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 runtime error to attempt to access a list using an index that is not a member of its set of indices. |
+\LMHash{} |
If a list literal begins with the reserved word \CONST{}, it is a {\em constant list literal} which is a compile-time constant (\ref{constants}) and therefore evaluated at compile-time. Otherwise, it is a {\em run-time list literal} and it is evaluated at run-time. Only run-time list literals can be mutated |
after they are created. Attempting to mutate a constant list literal will result in a dynamic error. |
+\LMHash{} |
It is a compile-time error if an element of a constant list literal is not a compile-time constant. It is a compile-time error if the type argument of a constant list literal includes a type parameter. |
\rationale{The binding of a type parameter is not known at compile-time, so we cannot use type parameters inside compile-time constants.} |
+\LMHash{} |
The value of a constant list literal \CONST{} $<E>[e_1\ldots e_n]$ is an object $a$ whose class implements the built-in class $List<E>$. The $i$th element of $a$ is $v_{i+1}$, where $v_i$ is the value of the compile-time expression $e_i$. The value of a constant list literal \CONST{} $[e_1 \ldots e_n]$ is defined as the value of the constant list literal \CONST{}$ < \DYNAMIC{}>[e_1\ldots e_n]$. |
+\LMHash{} |
Let $list_1 =$ \CONST{} $<V>[e_{11} \ldots e_{1n}]$ and $list_2 =$ \CONST{} $<U>[e_{21} \ldots e_{2n}]$ be two constant list literals and let the elements of $list_1$ and $list_2$ evaluate to $o_{11} \ldots o_{1n}$ and $o_{21} \ldots o_{2n}$ respectively. Iff \code{identical($o_{1i}$, $o_{2i}$)} for $i \in 1.. n$ and $V = U$ then \code{identical($list_1$, $list_2$)}. |
\commentary{In other words, constant list literals are canonicalized.} |
+\LMHash{} |
A run-time list literal $<E>[e_1 \ldots e_n]$ is evaluated as follows: |
\begin{itemize} |
\item |
First, the expressions $e_1 \ldots e_n$ are evaluated in order they appear in the program, yielding objects $o_1 \ldots o_n$. |
\item A fresh instance (\ref{generativeConstructors}) $a$, of size $n$, whose class implements the built-in class $List<E>$ is allocated. |
\item |
- |
The operator \code{[]=} is invoked on $a$ with first argument $i$ and second argument |
%The $i$th element of $a$ is set to |
$o_{i+1}, 0 \le i < n$. |
@@ -2619,6 +2891,7 @@ The result of the evaluation is $a$. |
Note that this document does not specify an order in which the elements are set. This allows for parallel assignments into the list if an implementation so desires. The order can only be observed in checked mode (and may not be relied upon): if element $i$ is not a subtype of the element type of the list, a dynamic type error will occur when $a[i]$ is assigned $o_{i-1}$. |
} |
+\LMHash{} |
A runtime list literal $[e_1 \ldots e_n]$ is evaluated as $< \DYNAMIC{}>[e_1\ldots e_n]$. |
@@ -2627,6 +2900,7 @@ There is no restriction precluding nesting of list literals. It follows from the |
$<List<int>>[[1, 2, 3], [4, 5, 6]]$ is a list with type parameter $List<int>$, containing two lists with type parameter \DYNAMIC{}. |
} |
+\LMHash{} |
The static type of a list literal of the form \CONST{}$ <E>[e_1\ldots e_n]$ or the form $<E>[e_1 \ldots e_n]$ is $List<E>$. The static type a list literal of the form \CONST{} $[e_1 \ldots e_n$] or the form $[e_1\ldots e_n$] is $List< \DYNAMIC{}>$. |
\rationale{ |
@@ -2637,8 +2911,9 @@ It is tempting to assume that the type of the list literal would be computed bas |
% what about generics? |
\subsection{Maps} |
-\label{maps} |
+\LMLabel{maps} |
+\LMHash{} |
A {\em map literal} denotes a map object. |
\begin{grammar} |
@@ -2652,22 +2927,28 @@ A {\em map literal} denotes a map object. |
. |
\end{grammar} |
+\LMHash{} |
A {\em map literal} consists of zero or more entries. Each entry has a {\em key} and a {\em value}. Each key and each value is denoted by an expression. |
+\LMHash{} |
If a map literal begins with the reserved word \CONST{}, it is a {\em constant map literal} which is a compile-time constant (\ref{constants}) and therefore evaluated at compile-time. Otherwise, it is a {\em run-time map literal} and it is evaluated at run-time. Only run-time map literals can be mutated |
after they are created. Attempting to mutate a constant map literal will result in a dynamic error. |
+\LMHash{} |
It is a compile-time error if either a key or a value of an entry in a constant map literal is not a compile-time constant. It is a compile-time error if the key of an entry in a constant map literal is an instance of a class that implements the operator $==$ unless the key is a |
%symbol, |
string, an integer, a literal symbol or the result of invoking a constant constructor of class \cd{Symbol}. |
It is a compile-time error if the type arguments of a constant map literal include a type parameter. |
+\LMHash{} |
The value of a constant map literal \CONST{}$ <K, V>\{k_1:e_1\ldots k_n :e_n\}$ is an object $m$ whose class implements the built-in class $Map<K, V>$. The entries of $m$ are $u_i:v_i, i \in 1 .. n$, where $u_i$ is the value of the compile-time expression $k_i$ and $v_i$ is the value of the compile-time expression $e_i$. The value of a constant map literal \CONST{} $\{k_1:e_1\ldots k_n :e_n\}$ is defined as the value of a constant map literal \CONST{} $<\DYNAMIC{}, \DYNAMIC{}>\{k_1:e_1\ldots k_n :e_n\}$. |
+\LMHash{} |
Let $map_1 =$ \CONST{}$ <K, V>\{k_{11}:e_{11} \ldots k_{1n} :e_{1n}\}$ and $map_2 =$ \CONST{}$ <J, U>\{k_{21}:e_{21} \ldots k_{2n} :e_{2n}\}$ be two constant map literals. Let the keys of $map_1$ and $map_2$ evaluate to $s_{11} \ldots s_{1n}$ and $s_{21} \ldots s_{2n}$ respectively, and let the elements of $map_1$ and $map_2$ evaluate to $o_{11} \ldots o_{1n}$ and $o_{21} \ldots o_{2n}$ respectively. Iff \code{identical($o_{1i}$, $o_{2i}$)} and \code{identical($s_{1i}$, $s_{2i}$)} for $i \in 1.. n$, and $K = J, V = U$ then \code{identical($map_1$, $map_2$)}. |
\commentary{In other words, constant map literals are canonicalized.} |
+\LMHash{} |
A runtime map literal $<K, V>\{k_1:e_1\ldots k_n :e_n\}$ is evaluated as follows: |
\begin{itemize} |
\item |
@@ -2682,12 +2963,15 @@ The result of the evaluation is $m$. |
\end{itemize} |
+\LMHash{} |
A runtime map literal $\{k_1:e_1\ldots k_n :e_n\}$ is evaluated as |
$<\DYNAMIC{}, \DYNAMIC{}>\{k_1:e_1\ldots k_n :e_n\}$. |
+\LMHash{} |
Iff all the keys in a map literal are compile-time constants, it is a static warning if the values of any two keys in a map literal are equal. |
+\LMHash{} |
A map literal is ordered: iterating over the keys and/or values of the maps always happens in the |
order the keys appeared in the source code. |
@@ -2695,12 +2979,14 @@ A map literal is ordered: iterating over the keys and/or values of the maps alwa |
Of course, if a key repeats, the order is defined by first occurrence, but the value is defined by the last. |
} |
+\LMHash{} |
The static type of a map literal of the form \CONST{}$ <K, V>\{k_1:e_1\ldots k_n :e_n\}$ or the form $<K, V>\{k_1:e_1\ldots k_n :e_n\}$ is $Map<K, V>$. The static type a map literal of the form \CONST{}$\{k_1:e_1\ldots k_n :e_n\}$ or the form $\{k_1:e_1\ldots k_n :e_n\}$ is $Map<\DYNAMIC{}, \DYNAMIC{}>$. |
\subsection{Throw} |
-\label{throw} |
+\LMLabel{throw} |
+\LMHash{} |
The {\em throw expression} is used to raise an exception. |
\begin{grammar} |
@@ -2714,43 +3000,53 @@ The {\em throw expression} is used to raise an exception. |
\end{grammar} |
+\LMHash{} |
The {\em current exception} is the last exception raised and not subsequently caught at a given moment during runtime. |
+\LMHash{} |
Evaluation of a throw expression of the form \code{\THROW{} $e$;} proceeds as follows: |
+\LMHash{} |
The expression $e$ is evaluated yielding a value $v$. |
\commentary{ |
There is no requirement that the expression $e$ evaluate to a special kind of exception or error object. |
} |
+\LMHash{} |
If $e$ evaluates to \NULL{} (\ref{null}), then a \code{NullThrownError} is thrown. Otherwise the current exception is set to $v$ and the current return value (\ref{return}) becomes undefined. |
\rationale{The current exception and the current return value must never be simultaneously defined, as they represent mutually exclusive options for exiting the current function. |
} |
+\LMHash{} |
Let $f$ be the immediately enclosing function. |
+\LMHash{} |
If $f$ is synchronous (\ref{functions}), control is transferred to the nearest dynamically enclosing exception handler. |
\commentary{ |
If $f$ is marked \SYNC* then a dynamically enclosing exception handler encloses the call to \code{moveNext()} that initiated the evaluation of the throw expression. |
} |
+\LMHash{} |
If $f$ is asynchronous then if there is a dynamically enclosing exception handler $h$ (\ref{try}) introduced by the current activation, control is transferred to $h$, otherwise $f$ terminates. |
\rationale{ |
The rules for where a thrown exception will be handled must necessarily differ between the synchronous and asynchronous cases. Asynchronous functions cannot transfer control to an exception handler defined outside themselves. Asynchronous generators post exceptions to their stream. Other asynchronous functions report exceptions via their future. |
} |
+\LMHash{} |
If the object being thrown is an instance of class \code{Error} or a subclass thereof, its \code{stackTrace} getter will return the stack trace current at the point where the the object was first thrown. |
+\LMHash{} |
The static type of a throw expression is $\bot$. |
\subsection{ Function Expressions} |
-\label{functionExpressions} |
+\LMLabel{functionExpressions} |
+\LMHash{} |
A {\em function literal} is an object that encapsulates an executable unit of code. |
\begin{grammar} |
@@ -2759,6 +3055,7 @@ A {\em function literal} is an object that encapsulates an executable unit of co |
. |
\end{grammar} |
+\LMHash{} |
The class of a function literal implements the built-in class \code{Function}. |
%Invoking the getter \code{runtimeType} on a function literal returns the \code{Type} object that is the value of the expression \code{Function}. |
% not necessarily |
@@ -2766,6 +3063,7 @@ The class of a function literal implements the built-in class \code{Function}. |
%Q{Can anyone implement it? Then we should define things via call} |
+\LMHash{} |
The static type of a function literal of the form |
$(T_1$ $a_1, \ldots, T_n$ $a_n, [T_{n+1}$ $x_{n+1} = d_1, \ldots, T_{n+k}$ $x_{n+k} = d_k]) => e$ |
@@ -2773,11 +3071,13 @@ is |
$(T_1 \ldots, T_n, [T_{n+1}$ $x_{n+1}, \ldots, T_{n+k}$ $x_{n+k}]) \rightarrow T_0$, where $T_0$ is the static type of $e$. |
+\LMHash{} |
The static type of a function literal of the form |
$(T_1$ $a_1, \ldots, T_n$ $a_n, [T_{n+1}$ $x_{n+1} = d_1, \ldots, T_{n+k}$ $x_{n+k} = d_k])$ \ASYNC{} $=> e$ |
is $(T_1 \ldots, T_n, [T_{n+1}$ $x_{n+1}, \ldots, T_{n+k}$ $x_{n+k}]) \rightarrow Future<T_0>$, where $T_0$ is the static type of $e$. |
+\LMHash{} |
The static type of a function literal of the form |
$(T_1$ $a_1, \ldots, T_n$ $a_n, \{T_{n+1}$ $x_{n+1} : d_1, \ldots, T_{n+k}$ $x_{n+k} : d_k\}) => e$ |
@@ -2785,34 +3085,40 @@ is |
$(T_1 \ldots, T_n, \{T_{n+1}$ $x_{n+1}, \ldots, T_{n+k}$ $x_{n+k}\}) \rightarrow T_0$, where $T_0$ is the static type of $e$. |
+\LMHash{} |
The static type of a function literal of the form |
$(T_1$ $a_1, \ldots, T_n$ $a_n, \{T_{n+1}$ $x_{n+1} : d_1, \ldots, T_{n+k}$ $x_{n+k} : d_k\})$ \ASYNC{} $=> e$ |
is $(T_1 \ldots, T_n, \{T_{n+1}$ $x_{n+1}, \ldots, T_{n+k}$ $x_{n+k}\}) \rightarrow Future<T_0>$, where $T_0$ is the static type of $e$. |
+\LMHash{} |
The static type of a function literal of the form |
$(T_1$ $a_1, \ldots, T_n$ $a_n, [T_{n+1}$ $x_{n+1} = d_1, \ldots, T_{n+k}$ $x_{n+k}= d_k])\{s\}$ |
is $(T_1 \ldots, T_n, [T_{n+1}$ $x_{n+1}, \ldots, T_{n+k}$ $x_{n+k}]) \rightarrow \DYNAMIC{}$. |
+\LMHash{} |
The static type of a function literal of the form |
$(T_1$ $a_1, \ldots, T_n$ $a_n, [T_{n+1}$ $x_{n+1} = d_1, \ldots, T_{n+k}$ $x_{n+k}= d_k])$ $ \ASYNC{}$ $\{s\}$ |
is $(T_1 \ldots, T_n, [T_{n+1}$ $x_{n+1}, \ldots, T_{n+k}$ $x_{n+k}]) \rightarrow Future$. |
+\LMHash{} |
The static type of a function literal of the form |
$(T_1$ $a_1, \ldots, T_n$ $a_n, [T_{n+1}$ $x_{n+1} = d_1, \ldots, T_{n+k}$ $x_{n+k}= d_k])$ $ \ASYNC*{}$ $\{s\}$ |
is $(T_1 \ldots, T_n, [T_{n+1}$ $x_{n+1}, \ldots, T_{n+k}$ $x_{n+k}]) \rightarrow Stream$. |
+\LMHash{} |
The static type of a function literal of the form |
$(T_1$ $a_1, \ldots, T_n$ $a_n, [T_{n+1}$ $x_{n+1} = d_1, \ldots, T_{n+k}$ $x_{n+k}= d_k])$ $ \SYNC*{}$ $\{s\}$ |
is $(T_1 \ldots, T_n, [T_{n+1}$ $x_{n+1}, \ldots, T_{n+k}$ $x_{n+k}]) \rightarrow Iterable$. |
+\LMHash{} |
The static type of a function literal of the form |
$(T_1$ $a_1, \ldots, T_n$ $a_n, [T_{n+1}$ $x_{n+1} = d_1, \ldots, T_{n+k}$ $x_{n+k}= d_k])\{s\}$ |
@@ -2820,30 +3126,35 @@ $(T_1$ $a_1, \ldots, T_n$ $a_n, [T_{n+1}$ $x_{n+1} = d_1, \ldots, T_{n+k}$ $x_{ |
is $(T_1 \ldots, T_n, [T_{n+1}$ $x_{n+1}, \ldots, T_{n+k}$ $x_{n+k}]) \rightarrow \DYNAMIC{}$. |
+\LMHash{} |
The static type of a function literal of the form |
$(T_1$ $a_1, \ldots, T_n$ $a_n, \{T_{n+1}$ $x_{n+1} : d_1, \ldots, T_{n+k}$ $x_{n+k} : d_k\})$ $\ASYNC{}$ $\{s\}$ |
is $(T_1 \ldots, T_n, \{T_{n+1}$ $x_{n+1}, \ldots, T_{n+k}$ $x_{n+k}\}) \rightarrow Future{}$. |
+\LMHash{} |
The static type of a function literal of the form |
$(T_1$ $a_1, \ldots, T_n$ $a_n, \{T_{n+1}$ $x_{n+1} : d_1, \ldots, T_{n+k}$ $x_{n+k} : d_k\})$ $\ASYNC*{}$ $\{s\}$ |
is $(T_1 \ldots, T_n, \{T_{n+1}$ $x_{n+1}, \ldots, T_{n+k}$ $x_{n+k}\}) \rightarrow Stream{}$. |
+\LMHash{} |
The static type of a function literal of the form |
$(T_1$ $a_1, \ldots, T_n$ $a_n, \{T_{n+1}$ $x_{n+1} : d_1, \ldots, T_{n+k}$ $x_{n+k} : d_k\})$ $\SYNC*{}$ $\{s\}$ |
is $(T_1 \ldots, T_n, \{T_{n+1}$ $x_{n+1}, \ldots, T_{n+k}$ $x_{n+k}\}) \rightarrow Iterable{}$. |
+\LMHash{} |
In all of the above cases, whenever $T_i, 1 \le i \le n+k$, is not specified, it is considered to have been specified as \DYNAMIC{}. |
\subsection{ This} |
-\label{this} |
+\LMLabel{this} |
+\LMHash{} |
The reserved word \THIS{} denotes the target of the current instance member invocation. |
\begin{grammar} |
@@ -2852,23 +3163,27 @@ The reserved word \THIS{} denotes the target of the current instance member invo |
. |
\end{grammar} |
+\LMHash{} |
The static type of \THIS{} is the interface of the immediately enclosing class. |
\commentary{ |
We do not support self-types at this point. |
} |
+\LMHash{} |
It is a 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 initializer of an instance variable. |
\subsection{ Instance Creation} |
-\label{instanceCreation} |
+\LMLabel{instanceCreation} |
+\LMHash{} |
Instance creation expressions invoke constructors to produce instances. |
%It is a compile-time error if any of the type arguments to a constructor of a generic type invoked by a new expression or a constant object expression do not denote types in the enclosing lexical scope. |
%It is a compile-time error if a constructor of a non-generic type invoked by a new expression or a constant object expression is passed any type arguments. It is a compile-time error if a constructor of a generic type with $n$ type parameters invoked by a new expression or a constant object expression is passed $m$ type arguments where $m \ne n$, or if any of its type arguments is misconstructed (\ref{parameterizedTypes}). |
+\LMHash{} |
It is a static type warning if |
the type $T$ in an instance creation expression of one of the forms |
@@ -2880,6 +3195,7 @@ the type $T$ in an instance creation expression of one of the forms |
\CONST{} $T(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: a_{n+k})$ is malformed (\ref{dynamicTypeSystem}) or malbounded (\ref{parameterizedTypes}). |
+\LMHash{} |
It is a compile-time error if the type $T$ in an instance creation expression of one of the forms |
\NEW{} $T.id(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: a_{n+k})$, |
@@ -2895,8 +3211,9 @@ is an enumerated type (\ref{enums}). |
\subsubsection{ New} |
-\label{new} |
+\LMLabel{new} |
+\LMHash{} |
The {\em new expression} invokes a constructor (\ref{constructors}). |
\begin{grammar} |
@@ -2905,6 +3222,7 @@ The {\em new expression} invokes a constructor (\ref{constructors}). |
. |
\end{grammar} |
+\LMHash{} |
Let $e$ be a new expression of the form |
\NEW{} $T.id(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: a_{n+k})$ or the form |
@@ -2917,12 +3235,14 @@ Let $e$ be a new expression of the form |
%not a class accessible in the current scope, optionally followed by type arguments. |
+\LMHash{} |
If $T$ is a class or parameterized type accessible in the current scope then: |
\begin{itemize} |
\item |
If $e$ is of the form \NEW{} $T.id(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: a_{n+k})$ it is a static warning if $T.id$ is not the name of a constructor declared by the type $T$. If $e$ is of the form \NEW{} $T(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: a_{n+k})$ it is a static warning if the type $T$ does not declare a constructor with the same name as the declaration of $T$. |
\end{itemize} |
+\LMHash{} |
If $T$ is a parameterized type (\ref{parameterizedTypes}) $S<U_1, \ldots, U_m>$, let $R = S$. |
%It is a |
%compile-time CHANGED |
@@ -2931,46 +3251,60 @@ If $T$ is a parameterized type (\ref{parameterizedTypes}) $S<U_1, \ldots, U_m>$ |
If $T$ is not a parameterized type, let $R = T$. |
Furthermore, if $e$ is of the form \NEW{} $T.id(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: a_{n+k})$ then let $q$ be the constructor $T.id$, otherwise let $q$ be the constructor $T$. |
+\LMHash{} |
If $R$ is a generic with $l = m$ type parameters then |
\begin{itemize} |
\item If $T$ is not a parameterized type, then for $ i \in 1 .. l$, let $V_i = \DYNAMIC{}$. |
\item If $T$ is a parameterized type then let $V_i = U_i$ for $ i \in 1 .. m$. |
\end{itemize} |
+\LMHash{} |
If $R$ is a generic with $l \ne m$ type parameters then for $ i \in 1 .. l$, let $V_i = \DYNAMIC{}$. In any other case, let $V_i = U_i$ for $ i \in 1 .. m$. |
+\LMHash{} |
Evaluation of $e$ proceeds as follows: |
+\LMHash{} |
First, the argument list $(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: a_{n+k})$ is evaluated. |
+\LMHash{} |
If $T$ is a deferred type with prefix $p$, then if $p$ has not been successfully loaded, a dynamic error occurs. |
+\LMHash{} |
Then, if $q$ is a non-factory constructor of an abstract class then an \code{AbstractClassInstantiationError} is thrown. |
+\LMHash{} |
If $T$ is malformed or if $T$ is a type variable a dynamic error occurs. In checked mode, if $T$ or any of its superclasses is malbounded a dynamic error occurs. |
Otherwise, if $q$ is not defined or not accessible, a \code{NoSuchMethodError} is thrown. If $q$ has less than $n$ positional parameters or more than $n$ required parameters, or if $q$ lacks any of the keyword parameters $\{ x_{n+1}, \ldots, x_{n+k}\}$ a \code{NoSuchMethodError} is thrown. |
+\LMHash{} |
Otherwise, if $q$ is a generative constructor (\ref{generativeConstructors}), then: |
\commentary{Note that it this point we are assured that the number of actual type arguments match the number of formal type parameters.} |
+\LMHash{} |
A fresh instance (\ref{generativeConstructors}), $i$, of class $R$ is allocated. For each instance variable $f$ of $i$, if the variable declaration of $f$ has an initializer expression $e_f$, then $e_f$ is evaluated to an object $o_f$ and $f$ is bound to $o_f$. Otherwise $f$ is bound to \NULL{}. |
\commentary{ |
Observe that \THIS{} is not in scope in $e_f$. Hence, the initialization cannot depend on other properties of the object being instantiated. |
} |
+\LMHash{} |
Next, $q$ is executed with \THIS{} bound to $i$, the type parameters (if any) of $R$ bound to the actual type arguments $V_1, \ldots, V_l$ and the formal parameter bindings that resulted from the evaluation of the argument list. The result of the evaluation of $e$ is $i$. |
+\LMHash{} |
Otherwise, $q$ is a factory constructor (\ref{factories}). Then: |
+\LMHash{} |
If $q$ is a redirecting factory constructor of the form $T(p_1, \ldots, p_{n+k}) = c;$ or of the form $T.id(p_1, \ldots, p_{n+k}) = c;$ then the result of the evaluation of $e$ is equivalent to evaluating the expression |
$[V_1, \ldots, V_m/T_1, \ldots, T_m]($\code{\NEW{} $c(a_1, \ldots, a_n, x_{n+1}: a_{n+1}, \ldots, x_{n+k}: a_{n+k}))$}. If evaluation of $q$ causes $q$ to be re-evaluated cyclically, a runtime error occurs. |
+\LMHash{} |
Otherwise, the body of $q$ is executed with respect to the bindings that resulted from the evaluation of the argument list and the type parameters (if any) of $q$ bound to the actual type arguments $V_1, \ldots, V_l$ resulting in an object $i$. The result of the evaluation of $e$ is $i$. |
+\LMHash{} |
It is a static warning if $q$ is a constructor of an abstract class and $q$ is not a factory constructor. |
\commentary{The above gives precise meaning to the idea that instantiating an abstract class leads to a warning. |
@@ -2980,6 +3314,7 @@ A similar clause applies to constant object creation in the next section. |
\rationale{In particular, a factory constructor can be declared in an abstract class and used safely, as it will either produce a valid instance or lead to a warning inside its own declaration. |
} |
+\LMHash{} |
The static type of an instance creation expression of either the form |
\NEW{} $T.id(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: a_{n+k})$ |
@@ -2994,8 +3329,9 @@ It is a static warning if the static type of $a_i, 1 \le i \le n+ k$ may not be |
\subsubsection{ Const} |
-\label{const} |
+\LMLabel{const} |
+\LMHash{} |
A {\em constant object expression} invokes a constant constructor (\ref{constantConstructors}). |
\begin{grammar} |
@@ -3004,6 +3340,7 @@ A {\em constant object expression} invokes a constant constructor (\ref{constant |
. |
\end{grammar} |
+\LMHash{} |
Let $e$ be a constant object expression of the form |
\CONST{} $T.id(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: a_{n+k})$ |
@@ -3012,18 +3349,23 @@ or the form \CONST{} $T(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: |
\commentary{In particular, $T$ may not be a type variable.} |
+\LMHash{} |
If $T$ is a parameterized type, it is a compile-time error if $T$ includes a type variable among its type arguments. |
+\LMHash{} |
If $e$ is of the form \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 $T.id$ is not the name of a constant constructor declared by the type $T$. If $e$ is of the form \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 the type $T$ does not declare a constant constructor with the same name as the declaration of $T$. |
+\LMHash{} |
In all of the above cases, it is a compile-time error if $a_i, i\in 1 .. n + k$, is not a compile-time constant expression. |
%If $T$ is a parameterized type (\ref{parameterizedTypes}) $S<U_1, \ldots, U_m>$, let $R = S$. It is a compile-time error if $T$ is is malformed. If $T$ is not a parameterized type, let $R = T$. |
%Finally, |
% If $T$ is a generic with $l$ retype parameters, then for all $ i \in 1 .. l$, let $V_i = \DYNAMIC{}$. |
+\LMHash{} |
Evaluation of $e$ proceeds as follows: |
+\LMHash{} |
First, if $e$ is of the form |
\CONST{} $T.id(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: a_{n+k})$ |
@@ -3032,6 +3374,7 @@ then let $i$ be the value of the expression |
\NEW{} $T.id(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: a_{n+k})$. |
+\LMHash{} |
Otherwise, $e$ must be of the form |
\CONST{} $T(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: a_{n+k})$, |
@@ -3040,6 +3383,7 @@ in which case let $i$ be the result of evaluating |
\NEW{} $T(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: a_{n+k})$. |
+\LMHash{} |
Then: |
\begin{itemize} |
\item If during execution of the program, a constant object expression has already evaluated to an instance $j$ of class $R$ with type arguments $V_i, 1 \le i \le m$, then: |
@@ -3053,6 +3397,7 @@ Then: |
In other words, constant objects are canonicalized. In order to determine if an object is actually new, one has to compute it; then it can be compared to any cached instances. If an equivalent object exists in the cache, we throw away the newly created object and use the cached one. Objects are equivalent if they have identical fields and identical type arguments. Since the constructor cannot induce any side effects, the execution of the constructor is unobservable. The constructor need only be executed once per call site, at compile-time. |
} |
+\LMHash{} |
The static type of a constant object expression of either the form |
\CONST{} $T.id(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: a_{n+k})$ |
@@ -3063,6 +3408,7 @@ or the form |
is $T$. It is a static warning if the static type of $a_i, 1 \le i \le n+ k$ may not be assigned to the type of the corresponding formal parameter of the constructor $T.id$ (respectively $T$). |
+\LMHash{} |
It is a compile-time error if evaluation of a constant object results in an uncaught exception being thrown. |
\commentary{ |
@@ -3094,14 +3440,17 @@ Due to the rules governing constant constructors, evaluating the constructor \co |
} |
+\LMHash{} |
Given an instance creation expression of the form \CONST{} $q(a_1, \ldots , a_n)$ it is a static warning if $q$ is a constructor of an abstract class (\ref{abstractInstanceMembers}) but $q$ is not a factory constructor. |
\subsection{ Spawning an Isolate} |
-\label{spawningAnIsolate} |
+\LMLabel{spawningAnIsolate} |
+\LMHash{} |
Spawning an isolate is accomplished via what is syntactically an ordinary library call, invoking one of the functions \code{spawnUri()} or \code{spawnFunction()}defined in the \code{dart:isolate} library. However, such calls have the semantic effect of creating a new isolate with its own memory and thread of control. |
+\LMHash{} |
An isolate's memory is finite, as is the space available to its thread's call stack. It is possible for a running isolate to exhaust its memory or stack, resulting in a run-time error that cannot be effectively caught, which will force the isolate to be suspended. |
\commentary{ |
@@ -3111,12 +3460,15 @@ As discussed in section \ref{errorsAndWarnings}, the handling of a suspended iso |
\subsection{ Function Invocation} |
-\label{functionInvocation} |
+\LMLabel{functionInvocation} |
+\LMHash{} |
Function invocation occurs in the following cases: when a function expression (\ref{functionExpressions}) is invoked (\ref{functionExpressionInvocation}), when a method (\ref{methodInvocation}), getter (\ref{topLevelGetterInvocation}, \ref{propertyExtraction}) or setter (\ref{assignment}) is invoked or when a constructor is invoked (either via instance creation (\ref{instanceCreation}), constructor redirection (\ref{redirectingConstructors}) or super initialization). The various kinds of function invocation differ as to how the function to be invoked, $f$, is determined, as well as whether \THIS{} (\ref{this}) is bound. Once $f$ has been determined, the formal parameters of $f$ are bound to corresponding actual arguments. When the body of $f$ is executed it will be executed with the aforementioned bindings. |
+\LMHash{} |
If $f$ is marked \ASYNC{} (\ref{functions}), then a fresh instance (\ref{generativeConstructors}) $o$ implementing the built-in class \code{Future} is associated with the invocation and immediately returned to the caller. The body of $f$ is scheduled for execution at some future time. The future $o$ will complete when $f$ terminates. The value used to complete $o$ is the current return value (\ref{return}), if it is defined, and the current exception (\ref{throw}) otherwise. |
+\LMHash{} |
If $f$ is marked \ASYNC* (\ref{functions}), then a fresh instance $s$ implementing the built-in class \code{Stream} is associated with the invocation and immediately returned. When $s$ is listened to, execution of the body of $f$ will begin. When $f$ terminates: |
\begin{itemize} |
\item If the current return value is defined then, if $s$ has been canceled then its cancellation future is completed with \NULL{} (\ref{null}). |
@@ -3132,16 +3484,20 @@ If $f$ is marked \ASYNC* (\ref{functions}), then a fresh instance $s$ implementi |
When an asynchronous generator's stream has been canceled, cleanup will occur in the \FINALLY{} clauses (\ref{try}) inside the generator. We choose to direct any exceptions that occur at this time to the cancellation future rather than have them be lost. |
} |
+\LMHash{} |
If $f$ is asynchronous then, when $f$ terminates, any open stream subscriptions associated with any asynchronous for loops (\ref{asynchronousFor-in}) or yield-each statements (\ref{yieldEach}) executing within $f$ are canceled. |
\rationale{Such streams may be left open by for loops that were escaped when an exception was thrown within them for example. |
} |
+\LMHash{} |
If $f$ is marked \SYNC* (\ref{functions}), then a fresh instance $i$ implementing the built-in class \code{Iterable} is associated with the invocation and immediately returned. When iteration over the iterable is started, by getting an iterator $j$ from the iterable and calling \code{moveNext()} on it, execution of the body of $f$ will begin. When $f$ terminates, $j$ is positioned after its last element, so that its current value is \NULL{} and the current call to \code{moveNext()} on $j$ returns false, as will all further calls. |
+\LMHash{} |
If $f$ is synchronous and is not a generator (\ref{functions}) then execution of the body of $f$ begins immediately. When $f$ terminates the current return value is returned to the caller. |
+\LMHash{} |
Execution of $f$ terminates when the first of the following occurs: |
\begin{itemize} |
\item An exception is thrown and not caught within the current function activation. |
@@ -3153,8 +3509,9 @@ Execution of $f$ terminates when the first of the following occurs: |
\subsubsection{ Actual Argument List Evaluation} |
-\label{actualArguments} |
+\LMLabel{actualArguments} |
+\LMHash{} |
Function invocation involves evaluation of the list of actual arguments to the function and binding of the results to the function's formal parameters. |
\begin{grammar} |
@@ -3173,12 +3530,14 @@ Function invocation involves evaluation of the list of actual arguments to the f |
. |
\end{grammar} |
+\LMHash{} |
Evaluation of an actual argument list of the form |
$(a_1, \ldots, a_m, q_1: a_{m+1}, \ldots, q_l: a_{m+l})$ |
proceeds as follows: |
+\LMHash{} |
The arguments $a_1, \ldots, a_{m+l}$ are evaluated in the order they appear in the program, yielding objects $o_1, \ldots, o_{m+l}$. |
\commentary{Simply stated, an argument list consisting of $m$ positional arguments and $l$ named arguments is evaluated from left to right. |
@@ -3186,10 +3545,12 @@ The arguments $a_1, \ldots, a_{m+l}$ are evaluated in the order they appear in t |
\subsubsection{ Binding Actuals to Formals} |
-\label{bindingActualsToFormals} |
+\LMLabel{bindingActualsToFormals} |
+\LMHash{} |
Let $f$ be a function with $h$ required parameters, let $p_1 \ldots p_n$ be the positional parameters of $f$ and let $p_{h+1}, \ldots, p_{h+k}$ be the optional parameters declared by $f$. |
+\LMHash{} |
An evaluated actual argument list $o_1 \ldots o_{m+l}$ derived from an actual argument list of the form $(a_1, \ldots, a_m, q_1: a_{m+1}, \ldots, q_l: a_{m+l})$ is bound to the formal parameters of $f$ as follows: |
\commentary{ |
@@ -3201,25 +3562,31 @@ If $l > 0$, then it is necessarily the case that $n = h$, because a method canno |
} |
+\LMHash{} |
If $m < h$, or $m > n$, a \cd{NoSuchMethodError} is thrown. Furthermore, each $q_i, 1 \le i \le l$, must have a corresponding named parameter in the set $\{p_{n+1}, \ldots, p_{n +k}\}$ or a \cd{NoSuchMethodError} is thrown. 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$. All remaining formal parameters of $f$ are bound to their default values. |
\commentary{All of these remaining parameters are necessarily optional and thus have default values.} |
+\LMHash{} |
In checked mode, it is a dynamic type error if $o_i$ is not \NULL{} and the actual type (\ref{actualTypeOfADeclaration}) of $p_i$ is not a supertype of the type of $o_i, i \in 1.. m$. In checked mode, it is a dynamic type error if $o_{m+j}$ is not \NULL{} and the actual type (\ref{actualTypeOfADeclaration}) of $q_j$ is not a supertype of the type of $o_{m+j}, j \in 1.. l$. |
+\LMHash{} |
It is a compile-time error if $q_i = q_j$ for any $i \ne j$. |
+\LMHash{} |
Let $T_i$ be the static type of $a_i$, let $S_i$ be the type of $p_i, i \in 1 .. h+k$ and let $S_q$ be the type of the named parameter $q$ of $f$. It is a static warning if $T_j$ may not be assigned to $S_j, j \in 1..m$. It is a static warning if $m < h$ or if $m > n$. Furthermore, each $q_i, 1 \le i \le l$, must have a corresponding named parameter in the set $\{p_{n+1}, \ldots, p_{n +k}\}$ or a static warning occurs. It is a static warning if $T_{m+j}$ may not be assigned to $S_{q_j}, j \in 1 .. l$. |
\subsubsection{ Unqualified Invocation} |
-\label{unqualifiedInvocation} |
+\LMLabel{unqualifiedInvocation} |
+\LMHash{} |
An unqualified function invocation $i$ has the form |
$id(a_1, \ldots, a_n, x_{n+1}: a_{n+1}, \ldots, x_{n+k}: a_{n+k})$, |
where $id$ is an identifier. |
+\LMHash{} |
If there exists a lexically visible declaration named $id$, let $f_{id}$ be the innermost such declaration. Then: |
\begin{itemize} |
\item |
@@ -3235,8 +3602,10 @@ Otherwise, if $f_{id}$ is a static method of the enclosing class $C$, $i$ is equ |
%Unqualified access to static methods of superclasses is inconsistent with the idea that static methods are not inherited. It is not particularly necessary and may be restricted in future versions. |
%} |
+\LMHash{} |
Otherwise, if $i$ occurs inside a top level or static function (be it function, method, getter, or setter) or variable initializer, evaluation of $i$ causes a \cd{NoSuchMethodError} to be thrown. |
+\LMHash{} |
If $i$ does not occur inside a top level or static function, $i$ is equivalent to $\THIS{}.id(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: a_{n+k})$. |
% Should also say: |
@@ -3247,8 +3616,9 @@ If $i$ does not occur inside a top level or static function, $i$ is equivalent t |
\subsubsection{ Function Expression Invocation} |
-\label{functionExpressionInvocation} |
+\LMLabel{functionExpressionInvocation} |
+\LMHash{} |
A function expression invocation $i$ has the form |
$e_f(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: a_{n+k})$, |
@@ -3259,6 +3629,7 @@ where $e_f$ is an expression. If $e_f$ is an identifier $id$, then $id$ must nec |
\code{$a.b(x)$} is parsed as a method invocation of method \code{$b()$} on object \code{$a$}, not as an invocation of getter \code{$b$} on \code{$a$} followed by a function call \code{$(a.b)(x)$}. If a method or getter \code{$b$} exists, the two will be equivalent. However, if \code{$b$} is not defined on \code{$a$}, the resulting invocation of \code{noSuchMethod()} would differ. The \code{Invocation} passed to \code{noSuchMethod()} would describe a call to a method \code{$b$} with argument \code{$x$} in the former case, and a call to a getter \code{$b$} (with no arguments) in the latter. |
} |
+\LMHash{} |
Otherwise: |
A function expression invocation $e_f(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: a_{n+k})$ is equivalent to $e_f.call(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: a_{n+k})$. |
@@ -3267,6 +3638,7 @@ A function expression invocation $e_f(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldot |
The implication of this definition, and the other definitions involving the method \code{call()}, is that user defined types can be used as function values provided they define a \CALL{} method. The method \CALL{} is special in this regard. The signature of the \CALL{} method determines the signature used when using the object via the built-in invocation syntax. |
} |
+\LMHash{} |
It is a static warning if the static type $F$ of $e_f$ may not be assigned to a function type. If $F$ is not a function type, the static type of $i$ is \DYNAMIC{}. Otherwise |
the static type of $i$ is the declared return type of $F$. |
%\item Let $T_i$ be the static type of $a_i, i \in 1 .. n+k$. It is a static warning if $F$ is not a supertype of $(T_1, \ldots, T_n, [T_{n+1}$ $x_{n+1}, \ldots, T_{n+k}$ $x_{n+k}]) \to \bot$. |
@@ -3275,10 +3647,12 @@ the static type of $i$ is the declared return type of $F$. |
\subsection{ Lookup} |
\subsubsection{Method Lookup} |
-\label{methodLookup} |
+\LMLabel{methodLookup} |
+\LMHash{} |
The result of a lookup of a method $m$ in object $o$ with respect to library $L$ is the result of a lookup of method $m$ in class $C$ with respect to library $L$, where $C$ is the class of $o$. |
+\LMHash{} |
The result of a lookup of method $m$ in class $C$ with respect to library $L$ is: |
If $C$ declares a concrete instance method named $m$ that is accessible to $L$, then that method is the result of the lookup. Otherwise, if $C$ has a superclass $S$, then the result of the lookup is the result of looking up $m$ in $S$ with respect to $L$. Otherwise, we say that the method lookup has failed. |
@@ -3288,10 +3662,12 @@ The motivation for skipping abstract members during lookup is largely to allow s |
\subsubsection{ Getter and Setter Lookup} |
-\label{getterAndSetterLookup} |
+\LMLabel{getterAndSetterLookup} |
+\LMHash{} |
The result of a lookup of a getter (respectively setter) $m$ in object $o$ with respect to library $L$ is the result of looking up getter (respectively setter) $m$ in class $C$ with respect to $L$, where $C$ is the class of $o$. |
+\LMHash{} |
The result of a lookup of a getter (respectively setter) $m$ in class $C$ with respect to library $L$ is: |
If $C$ declares a concrete instance getter (respectively setter) named $m$ that is accessible to $L$, then that getter (respectively setter) is the result of the lookup. Otherwise, if $C$ has a superclass $S$, then the result of the lookup is the result of looking up getter (respectively setter) $m$ in $S$ with respect to $L$. Otherwise, we say that the lookup has failed. |
@@ -3301,52 +3677,64 @@ The motivation for skipping abstract members during lookup is largely to allow s |
\subsection{ Top level Getter Invocation} |
-\label{topLevelGetterInvocation} |
+\LMLabel{topLevelGetterInvocation} |
+\LMHash{} |
Evaluation of a top-level getter invocation $i$ of the form $m$, where $m$ is an identifier, 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. |
\commentary{ |
Note that the invocation is always defined. Per the rules for identifier references, an identifier will not be treated as a top-level getter invocation unless the getter $i$ is defined. |
} |
+\LMHash{} |
The static type of $i$ is the declared return type of $m$. |
\subsection{ Method Invocation} |
-\label{methodInvocation} |
+\LMLabel{methodInvocation} |
+\LMHash{} |
Method invocation can take several forms as specified below. |
\subsubsection{Ordinary Invocation} |
-\label{ordinaryInvocation} |
+\LMLabel{ordinaryInvocation} |
+\LMHash{} |
An ordinary method invocation $i$ has the form |
$o.m(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: a_{n+k})$. |
+\LMHash{} |
Evaluation of an ordinary method invocation $i$ of the form |
$o.m(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: a_{n+k})$ |
proceeds as follows: |
+\LMHash{} |
First, the expression $o$ is evaluated to a value $v_o$. Next, the argument list $(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: a_{n+k})$ is evaluated yielding actual argument objects $o_1, \ldots , o_{n+k}$. Let $f$ be the result of looking up (\ref{methodLookup}) method $m$ in $v_o$ with respect to the current library $L$. |
+\LMHash{} |
Let $p_1 \ldots p_h$ be the required parameters of $f$, let $p_1 \ldots p_m$ be the positional parameters of $f$ and let $p_{h+1}, \ldots, p_{h+l}$ be the optional parameters declared by $f$. |
\commentary{ |
We have an argument list consisting of $n$ positional arguments and $k$ named arguments. We have a function with $h$ required parameters and $l$ optional parameters. The number of positional arguments must be at least as large as the number of required parameters, and no larger than the number of positional parameters. All named arguments must have a corresponding named parameter. |
} |
+\LMHash{} |
If $n < h$, or $n > m$, the method lookup has failed. Furthermore, each $x_i, n+1 \le i \le n+k$, must have a corresponding named parameter in the set $\{p_{m+1}, \ldots, p_{h+l}\}$ or the method lookup also fails. If $v_o$ is an instance of \code{Type} but $o$ is not a constant type literal, then if $m$ is a method that forwards (\ref{functionDeclarations}) to a static method, method lookup fails. Otherwise method lookup has succeeded. |
+\LMHash{} |
If the method lookup succeeded, the body of $f$ is executed with respect to the bindings that resulted from the evaluation of the argument list, and with \THIS{} bound to $v_o$. The value of $i$ is the value returned after $f$ is executed. |
+\LMHash{} |
If the method lookup has failed, then let $g$ be the result of looking up getter (\ref{getterAndSetterLookup}) $m$ in $v_o$ with respect to $L$. |
f $v_o$ is an instance of \code{Type} but $o$ is not a constant type literal, then if $g$ is a getter that forwards to a static getter, getter lookup fails. |
If the getter lookup succeeded, let $v_g$ be the value of the getter invocation $o.m$. Then the value of $i$ is the result of invoking |
the static method \code{Function.apply()} with arguments $v.g, [o_1, \ldots , o_n], \{x_{n+1}: o_{n+1}, \ldots , x_{n+k}: o_{n+k}\}$. |
+\LMHash{} |
If getter lookup has also failed, then a new instance $im$ of the predefined class \code{Invocation} is created, such that : |
\begin{itemize} |
\item \code{im.isMethod} evaluates to \code{\TRUE{}}. |
@@ -3355,6 +3743,7 @@ If getter lookup has also failed, then a new instance $im$ of the predefined c |
\item \code{im.namedArguments} evaluates to an immutable map with the same keys and values as \code{\{$x_{n+1}: o_{n+1}, \ldots, x_{n+k} : o_{n+k}$\}}. |
\end{itemize} |
+\LMHash{} |
Then the method \code{noSuchMethod()} is looked up in $v_o$ and invoked with argument $im$, and the result of this invocation is the result of evaluating $i$. However, if the implementation found cannot be invoked with a single positional argument, the implementation of \code{noSuchMethod()} in class \code{Object} is invoked on $v_o$ with argument $im'$, where $im'$ is an instance of \code{Invocation} such that : |
\begin{itemize} |
\item \code{im.isMethod} evaluates to \code{\TRUE{}}. |
@@ -3378,6 +3767,7 @@ It is possible to bring about such a situation by overriding \code{noSuchMethod( |
\commentary{Notice that the wording carefully avoids re-evaluating the receiver $o$ and the arguments $a_i$. } |
+\LMHash{} |
Let $T$ be the static type of $o$. It is a static type warning if $T$ does not have an accessible (\ref{privacy}) instance member named $m$ unless either: |
\begin{itemize} |
\item |
@@ -3385,14 +3775,17 @@ $T$ or a superinterface of $T$ is annotated with an annotation denoting a consta |
\item $T$ is \code{Type}, $e$ is a constant type literal and the class corresponding to $e$ has a static getter named $m$. |
\end{itemize} |
+\LMHash{} |
If $T.m$ exists, it is a static type warning if the type $F$ of $T.m$ may not be assigned to a function type. If $T.m$ does not exist, or if $F$ is not a function type, the static type of $i$ is \DYNAMIC{}; otherwise the static type of $i$ is the declared return type of $F$. |
+\LMHash{} |
It is a compile-time error to invoke any of the methods of class \cd{Object} on a prefix object (\ref{imports}) or on a constant type literal that is immediately followed by the token `.'. |
\subsubsection{Cascaded Invocations} |
-\label{cascadedInvocations} |
+\LMLabel{cascadedInvocations} |
+\LMHash{} |
A {\em cascaded method invocation} has the form {\em e..suffix} |
where $e$ is an expression and {\em suffix} is a sequence of operator, method, getter or setter invocations. |
@@ -3406,27 +3799,35 @@ where $e$ is an expression and {\em suffix} is a sequence of operator, method, g |
. |
\end{grammar} |
+\LMHash{} |
A cascaded method invocation expression of the form {\em e..suffix} is equivalent to the expression \code{(t)\{t.{\em suffix}; \RETURN{} t;\}($e$)}. |
\subsubsection{Super Invocation} |
-\label{superInvocation} |
+\LMLabel{superInvocation} |
+\LMHash{} |
A super method invocation $i$ has the form |
$\SUPER{}.m(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: a_{n+k})$. |
+\LMHash{} |
Evaluation of $i$ proceeds as follows: |
+\LMHash{} |
First, the argument list $(a_1, \ldots , a_n, x_{n+1}: a_{n+1}, \ldots , x_{n+k}: a_{n+k})$ is evaluated yielding actual argument objects $o_1, \ldots , o_{n+k}$. Let $S$ be the superclass of the immediately enclosing class, and let $f$ be the result of looking up method (\ref{ordinaryInvocation}) $m$ in $S$ with respect to the current library $L$. |
Let $p_1 \ldots p_h$ be the required parameters of $f$, let $p_1 \ldots p_m$ be the positional parameters of $f$ and let $p_{h+1}, \ldots, p_{h+l}$ be the optional parameters declared by $f$. |
+\LMHash{} |
If $n < h$, or $n > m$, the method lookup has failed. Furthermore, each $x_i, n+1 \le i \le n+k$, must have a corresponding named parameter in the set $\{p_{m+1}, \ldots, p_{h+l}\}$ or the method lookup also fails. Otherwise method lookup has succeeded. |
+\LMHash{} |
If the method lookup succeeded, the body of $f$ is executed with respect to the bindings that resulted from the evaluation of the argument list, and with \THIS{} bound to the current value of \THIS{}. The value of $i$ is the value returned after $f$ is executed. |
+\LMHash{} |
If the method lookup has failed, then let $g$ be the result of looking up getter (\ref{getterAndSetterLookup}) $m$ in $S$ with respect to $L$. If the getter lookup succeeded, let $v_g$ be the value of the getter invocation $\SUPER{}.m$. Then the value of $i$ is the result of invoking |
the static method \code{Function.apply()} with arguments $v.g, [o_1, \ldots , o_n], \{x_{n+1}: o_{n+1}, \ldots , x_{n+k}: o_{n+k}\}$. |
+\LMHash{} |
If getter lookup has also failed, then a new instance $im$ of the predefined class \code{Invocation} is created, such that : |
\begin{itemize} |
\item \code{im.isMethod} evaluates to \code{\TRUE{}}. |
@@ -3445,8 +3846,10 @@ Then the method \code{noSuchMethod()} is looked up in $S$ and invoked on \THIS{} |
and the result of this latter invocation is the result of evaluating $i$. |
+\LMHash{} |
It is a compile-time error if a super method invocation occurs in a top-level function or variable initializer, in an instance variable initializer or initializer list, in class \code{Object}, in a factory constructor or in a static method or variable initializer. |
+\LMHash{} |
It is a static type warning if $S$ does not have an accessible (\ref{privacy}) instance member named $m$ unless $S$ or a superinterface of $S$ is annotated with an annotation denoting a constant identical to the constant \code{@proxy} defined in \code{dart:core}. If $S.m$ exists, it is a static type warning if the type $F$ of $S.m$ may not be assigned to a function type. If $S.m$ does not exist, or if $F$ is not a function type, the static type of $i$ is \DYNAMIC{}; otherwise the static type of $i$ is the declared return type of $F$. |
% The following is not needed because it is specified in 'Binding Actuals to Formals" |
%Let $T_i$ be the static type of $a_i, i \in 1 .. n+k$. It is a static warning if $F$ is not a supertype of $(T_1, \ldots, t_n, \{T_{n+1}$ $x_{n+1}, \ldots, T_{n+k}$ $x_{n+k}\}) \to \bot$. |
@@ -3455,9 +3858,9 @@ It is a static type warning if $S$ does not have an accessible (\ref{privacy}) i |
\subsubsection{Sending Messages} |
+\LMLabel{sendingMessages} |
-\label{sendingMessages} |
- |
+\LMHash{} |
Messages are the sole means of communication among isolates. Messages are sent by invoking specific methods in the Dart libraries; there is no specific syntax for sending a message. |
\commentary{In other words, the methods supporting sending messages embody primitives of Dart that are not accessible to ordinary code, much like the methods that spawn isolates. |
@@ -3466,8 +3869,9 @@ Messages are the sole means of communication among isolates. Messages are sent b |
\subsection{ Property Extraction} |
-\label{propertyExtraction} |
+\LMLabel{propertyExtraction} |
+\LMHash{} |
{\em Property extraction} allows for a member of an object to be concisely extracted from the object. |
A property extraction can be either: |
\begin{enumerate} |
@@ -3475,12 +3879,16 @@ A property extraction can be either: |
\item A {\em getter invocation} which returns the result of invoking of a getter method. |
\end{enumerate} |
+\LMHash{} |
Evaluation of a property extraction $i$ of the form $e.m$ proceeds as follows: |
+\LMHash{} |
First, the expression $e$ is evaluated to an object $o$. Let $f$ be the result of looking up (\ref{methodLookup}) method (\ref{instanceMethods}) $m$ in $o$ with respect to the current library $L$. If $o$ is an instance of \code{Type} but $e$ is not a constant type literal, then if $m$ is a method that forwards (\ref{functionDeclarations}) to a static method, method lookup fails. If method lookup succeeds and $f$ is a concrete method then $i$ evaluates to the closurization of $o.m$. |
+\LMHash{} |
Otherwise, $i$ is a getter invocation, and the getter function (\ref{getters}) $m$ is looked up (\ref{getterAndSetterLookup}) in $o$ with respect to $L$. If $o$ is an instance of \code{Type} but $e$ is not a constant type literal, then if $m$ is a getter that forwards to a static getter, getter lookup fails. Otherwise, the body of $m$ is executed with \THIS{} bound to $o$. The value of $i$ is the result returned by the call to the getter function. |
+\LMHash{} |
If the getter lookup has failed, then a new instance $im$ of the predefined class \code{Invocation} is created, such that : |
\begin{itemize} |
\item \code{im.isGetter} evaluates to \code{\TRUE{}}. |
@@ -3498,12 +3906,14 @@ Then the method \code{noSuchMethod()} is looked up in $o$ and invoked with argu |
and the result of this latter invocation is the result of evaluating $i$. |
+\LMHash{} |
It is a compile-time error if $m$ is a member of class \cd{Object} and $e$ is either a prefix object (\ref{imports}) or a constant type literal. |
\commentary { |
This precludes \code{int.toString} but not \code{(int).toString} because in the latter case, $e$ is a parenthesized expression. |
} |
+\LMHash{} |
Let $T$ be the static type of $e$. It is a static type warning if $T$ does not have a method or getter named $m$ unless either: |
\begin{itemize} |
\item |
@@ -3511,6 +3921,7 @@ $T$ or a superinterface of $T$ is annotated with an annotation denoting a consta |
\item $T$ is \code{Type}, $e$ is a constant type literal and the class corresponding to $e$ has a static method or getter named $m$. |
\end{itemize} |
+\LMHash{} |
If $i$ is a getter invocation, the static type of $i$ is: |
\begin{itemize} |
\item The declared return type of $T.m$, if $T.m$ exists. |
@@ -3518,14 +3929,19 @@ If $i$ is a getter invocation, the static type of $i$ is: |
\item The type \DYNAMIC{} otherwise. |
\end{itemize} |
+\LMHash{} |
If $i$ is a closurization, its static type is as described in section \ref{closurization}. |
+\LMHash{} |
Evaluation of a property extraction $i$ of the form $\SUPER.m$ proceeds as follows: |
+\LMHash{} |
Let $S$ be the superclass of the immediately enclosing class. Let $f$ be the result of looking up method $m$ in $S$ with respect to the current library $L$. If $f$ is a concrete method then $i$ evaluates to the closurization of $\SUPER.m$ with respect to superclass $S$(\ref{closurization}). |
+\LMHash{} |
Otherwise, $i$ is a getter invocation and the getter function $m$ is looked up in $S$ with respect to $L$, and its body 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. |
+\LMHash{} |
If the getter lookup has failed, then a new instance $im$ of the predefined class \code{Invocation} is created, such that : |
\begin{itemize} |
\item \code{im.isGetter} evaluates to \code{\TRUE{}}. |
@@ -3543,12 +3959,14 @@ Then the method \code{noSuchMethod()} is looked up in $S$ and invoked with argu |
and the result of this latter invocation is the result of evaluating $i$. |
+\LMHash{} |
It is a static type warning if $S$ does not have a method or getter named $m$. If $i$ is a getter invocation, the static type of $i$ is the declared return type of $S.m$, if $S.m$ exists and \DYNAMIC{} otherwise. If $i$ is a closurization, its static type is as described in section \ref{closurization}. |
\subsubsection{Closurization} |
-\label{closurization} |
+\LMLabel{closurization} |
+\LMHash{} |
The {\em closurization of $o.m$} is defined to be equivalent to: |
\begin{itemize} |
@@ -3598,6 +4016,7 @@ The special treatment of equality in this case facilitates the use of extracted |
+\LMHash{} |
The closurization of $\SUPER{}.m$ with respect to superclass $S$ is defined to be equivalent to: |
\begin{itemize} |
@@ -3622,6 +4041,7 @@ $(r_1, \ldots, r_n, [p_1 = d_1, \ldots , p_k = d_k])$\{ |
if $m$ has required parameters $r_1, \ldots, r_n$, and optional positional parameters $p_1, \ldots, p_k$ with defaults $d_1, \ldots, d_k$. |
\end{itemize} |
+\LMHash{} |
Except that: |
\begin{enumerate} |
\item iff \code{identical($o_1, o_2$)} then \cd{$o_1.m$ == $o_2.m$}. |
@@ -3631,8 +4051,9 @@ The static type of the property extraction is the static type of the method $S.m |
\subsection{ Assignment} |
-\label{assignment} |
+\LMLabel{assignment} |
+\LMHash{} |
An assignment changes the value associated with a mutable variable or property. |
\begin{grammar} |
@@ -3641,6 +4062,7 @@ An assignment changes the value associated with a mutable variable or property. |
. |
\end{grammar} |
+\LMHash{} |
Evaluation of an assignment $a$ of the form $v$ \code{=} $e$ proceeds as follows: |
@@ -3652,27 +4074,38 @@ Evaluation of an assignment $a$ of the form $v$ \code{=} $e$ proceeds as follows |
%Otherwise, |
+\LMHash{} |
Let $d$ be the innermost declaration whose name is $v$ or $v=$, if it exists. |
+\LMHash{} |
If $d$ is the declaration of a local variable, the expression $e$ is evaluated to an object $o$. Then, the variable $v$ is bound to $o$ unless $v$ is \FINAL{} or \CONST{}, in which case a dynamic error occurs. |
If no error occurs, the value of the assignment expression is $o$. |
+\LMHash{} |
If $d$ is the declaration of a library variable, top level getter or top level setter, the expression $e$ is evaluated to an object $o$. Then the setter $v=$ is invoked with its formal parameter bound to $o$. The value of the assignment expression is $o$. |
+\LMHash{} |
Otherwise, if $d$ is the declaration of a static variable, static getter or static setter in class $C$, then the assignment is equivalent to the assignment \code{$C.v$ = $e$}. |
+\LMHash{} |
Otherwise, If $a$ occurs inside a top level or static function (be it function, method, getter, or setter) or variable initializer, evaluation of $a$ causes $e$ to be evaluated, after which a \code{NoSuchMethodError} is thrown. |
+\LMHash{} |
Otherwise, the assignment is equivalent to the assignment \code{ \THIS{}.$v$ = $e$}. |
+\LMHash{} |
In checked mode, it is a dynamic type error if $o$ is not \NULL{} and the interface of the class of $o$ is not a subtype of the actual type (\ref{actualTypeOfADeclaration}) of $v$. |
+\LMHash{} |
It is a static type warning if the static type of $e$ may not be assigned to the static type of $v$. The static type of the expression $v$ \code{=} $e$ is the static type of $e$. |
+\LMHash{} |
Evaluation of an assignment of the form $e_1.v$ \code{=} $e_2$ proceeds as follows: |
+\LMHash{} |
The expression $e_1$ is evaluated to an object $o_1$. Then, the expression $e_2$ is evaluated to an object $o_2$. Then, the setter $v=$ is looked up (\ref{getterAndSetterLookup}) in $o_1$ with respect to the current library. If $o_1$ is an instance of \code{Type} but $e_1$ is not a constant type literal, then if $v=$ is a setter that forwards (\ref{functionDeclarations}) to a static setter, setter lookup fails. Otherwise, the body of $v=$ is executed with its formal parameter bound to $o_2$ and \THIS{} bound to $o_1$. |
+\LMHash{} |
If the setter lookup has failed, then a new instance $im$ of the predefined class \code{Invocation} is created, such that : |
\begin{itemize} |
\item \code{im.isSetter} evaluates to \code{\TRUE{}}. |
@@ -3681,6 +4114,7 @@ If the setter lookup has failed, then a new instance $im$ of the predefined cla |
\item \code{im.namedArguments} evaluates to the value of \code{\CONST{} \{\}}. |
\end{itemize} |
+\LMHash{} |
Then the method \code{noSuchMethod()} is looked up in $o_1$ and invoked with argument $im$. |
However, if the implementation found cannot be invoked with a single positional argument, the implementation of \code{noSuchMethod()} in class \code{Object} is invoked on $o_1$ with argument $im'$, where $im'$ is an instance of \code{Invocation} such that : |
\begin{itemize} |
@@ -3690,10 +4124,13 @@ However, if the implementation found cannot be invoked with a single positional |
\item \code{im.namedArguments} evaluates to the value of \code{\CONST{} \{\}}. |
\end{itemize} |
+\LMHash{} |
The value of the assignment expression is $o_2$ irrespective of whether setter lookup has failed or succeeded. |
+\LMHash{} |
In checked mode, it is a dynamic type error if $o_2$ is not \NULL{} and the interface of the class of $o_2$ is not a subtype of the actual type of $e_1.v$. |
+\LMHash{} |
Let $T$ be the static type of $e_1$. It is a static type warning if $T$ does not have an accessible instance setter named $v=$ unless either: |
\begin{itemize} |
\item $T$ or a superinterface of $T$ is annotated with an annotation denoting a constant identical to the constant \code{@proxy} defined in \code{dart:core}. Or |
@@ -3702,21 +4139,26 @@ Let $T$ be the static type of $e_1$. It is a static type warning if $T$ does not |
+\LMHash{} |
It is a static type warning if the static type of $e_2$ may not be assigned to the static type of the formal parameter of the setter $v=$. The static type of the expression $e_1.v$ \code{=} $e_2$ is the static type of $e_2$. |
+\LMHash{} |
Evaluation of an assignment of the form $e_1[e_2]$ \code{=} $e_3$ is equivalent to the evaluation of the expression \code{(a, i, e)\{a.[]=(i, e); \RETURN{} e; \} ($e_1, e_2, e_3$)}. The static type of the expression $e_1[e_2]$ \code{=} $e_3$ is the static type of $e_3$. |
% Should we add: It is a dynamic error if $e_1$ evaluates to an constant list or map. |
+\LMHash{} |
It is a static warning if an assignment of the form $v = e$ occurs inside a top level or static function (be it function, method, getter, or setter) or variable initializer and there is neither a local variable declaration with name $v$ nor setter declaration with name $v=$ in the lexical scope enclosing the assignment. |
+\LMHash{} |
It is a compile-time error to invoke any of the setters of class \cd{Object} on a prefix object (\ref{imports}) or on a constant type literal that is immediately followed by the token `.'. |
\subsubsection{Compound Assignment} |
-\label{compoundAssignment} |
+\LMLabel{compoundAssignment} |
+\LMHash{} |
A compound assignment of the form $v$ $op\code{=} e$ is equivalent to $v \code{=} v$ $op$ $e$. A compound assignment of the form $C.v$ $op \code{=} e$ is equivalent to $C.v \code{=} C.v$ $op$ $e$. A compound assignment of the form $e_1.v$ $op = e_2$ is equivalent to \code{((x) $=>$ x.v = x.v $op$ $e_2$)($e_1$)} where $x$ is a variable that is not used in $e_2$. A compound assignment of the form $e_1[e_2]$ $op\code{=} e_3$ is equivalent to |
\code{((a, i) $=>$ a[i] = a[i] $op$ $e_3$)($e_1, e_2$)} where $a$ and $i$ are a variables that are not used in $e_3$. |
@@ -3738,8 +4180,9 @@ A compound assignment of the form $v$ $op\code{=} e$ is equivalent to $v \code{= |
\subsection{ Conditional} |
-\label{conditional} |
+\LMLabel{conditional} |
+\LMHash{} |
A {\em conditional expression} evaluates one of two expressions based on a boolean condition. |
\begin{grammar} |
@@ -3748,10 +4191,13 @@ A {\em conditional expression} evaluates one of two expressions based on a boole |
. % the first branches could top level expressions, it seems, but certainly NOT the second |
\end{grammar} |
+\LMHash{} |
Evaluation of a conditional expression $c$ of the form $e_1 ? e_2 : e_3$ proceeds as follows: |
+\LMHash{} |
First, $e_1$ is evaluated to an object $o_1$. Then, $o_1$ is subjected to boolean conversion (\ref{booleanConversion}) producing an object $r$. If $r$ is \TRUE, 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 all of the following hold: |
\begin{itemize} |
\item $e_1$ shows that a variable $v$ has type $T$. |
@@ -3762,12 +4208,14 @@ If all of the following hold: |
then the type of $v$ is known to be $T$ in $e_2$. |
+\LMHash{} |
It is a static type warning 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$. |
\subsection{ Logical Boolean Expressions} |
-\label{logicalBooleanExpressions} |
+\LMLabel{logicalBooleanExpressions} |
+\LMHash{} |
The logical boolean expressions combine boolean objects using the boolean conjunction and disjunction operators. |
\begin{grammar} |
@@ -3782,12 +4230,16 @@ The logical boolean expressions combine boolean objects using the boolean conjun |
. |
\end{grammar} |
+\LMHash{} |
A {\em logical boolean expression} is either an equality expression (\ref{equality}), or an invocation of a logical boolean operator on 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$ which is then subjected to boolean conversion, yielding an object $o_1$; if $o_1$ is \TRUE, the result of evaluating $b$ is \TRUE, otherwise $e_2$ is evaluated to an object $o_2$, which is then subjected to boolean conversion (\ref{booleanConversion}) producing an object $r$, which is the value of $b$. |
+\LMHash{} |
Evaluation of a logical boolean expression $b$ of the form $e_1 \&\& e_2$ causes the evaluation of $e_1$ which is then subjected to boolean conversion, yielding an object $o_1$; if $o_1$ is not \TRUE, the result of evaluating $b$ is \FALSE, otherwise $e_2$ is evaluated to an object $o_2$, which is then subjected to boolean conversion producing an object $r$, which is the value of $b$. |
+\LMHash{} |
A logical boolean expression $b$ of the form $e_1 \&\& e_2$ shows that a variable $v$ has type |
$T$ if all of the following conditions hold: |
\begin{itemize} |
@@ -3796,6 +4248,7 @@ $T$ if all of the following conditions hold: |
\item The variable $v$ is not mutated in $e_2$ or within a closure. |
\end{itemize} |
+\LMHash{} |
Furthermore, if all of the following hold: |
\begin{itemize} |
\item $e_1$ shows that $v$ has type $T$. |
@@ -3804,12 +4257,14 @@ Furthermore, if all of the following hold: |
\end{itemize} |
then the type of $v$ is known to be $T$ in $e_2$. |
+\LMHash{} |
It is a static warning if the static type of $e_1$ may not be assigned to \cd{bool} or if the static type of $e_2$ may not be assigned to \cd{bool}. The static type of a logical boolean expression is \code{bool}. |
\subsection{ Equality} |
- \label{equality} |
+ \LMLabel{equality} |
+\LMHash{} |
Equality expressions test objects for equality. |
\begin{grammar} |
@@ -3822,9 +4277,11 @@ Equality expressions test objects for equality. |
. |
\end{grammar} |
+\LMHash{} |
An {\em equality expression} is either a relational expression (\ref{relationalExpressions}), or an invocation of an equality operator on either \SUPER{} or an expression $e_1$, with argument $e_2$. |
+\LMHash{} |
Evaluation of an equality expression $ee$ 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$. |
@@ -3834,6 +4291,7 @@ Evaluation of an equality expression $ee$ of the form \code{$e_1$ == $e_2$} proc |
\end{itemize} |
+\LMHash{} |
Evaluation of an equality expression $ee$ of the form \code{\SUPER{} == $e$} proceeds as follows: |
\begin{itemize} |
\item The expression $e$ is evaluated to an object $o$. |
@@ -3848,6 +4306,7 @@ Evaluation of an equality expression $ee$ of the form \code{\SUPER{} == $e$} pro |
Another implication is that there is never a need to use \code{identical()} to test against \NULL{}, nor should anyone ever worry about whether to write \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$)}. |
@@ -3855,12 +4314,14 @@ An equality expression of the form \code{$e_1$ != $e_2$} is equivalent to the 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} |
-\label{relationalExpressions} |
+\LMLabel{relationalExpressions} |
+\LMHash{} |
Relational expressions invoke the relational operators on objects. |
\begin{grammar} |
@@ -3876,13 +4337,16 @@ Relational expressions invoke the relational operators on objects. |
. |
\end{grammar} |
+\LMHash{} |
A {\em relational expression} is either a bitwise expression (\ref{bitwiseExpressions}), or an invocation of a relational operator on either \SUPER{} 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$)}. |
\subsection{ Bitwise Expressions} |
-\label{bitwiseExpressions} |
+\LMLabel{bitwiseExpressions} |
+\LMHash{} |
Bitwise expressions invoke the bitwise operators on objects. |
\begin{grammar} |
@@ -3904,8 +4368,10 @@ Bitwise expressions invoke the bitwise operators on objects. |
. |
\end{grammar} |
+\LMHash{} |
A {\em bitwise expression} is either a shift expression (\ref{shift}), or an invocation of a bitwise operator on either \SUPER{} or an expression $e_1$, with argument $e_2$. |
+\LMHash{} |
A bitwise expression of the form $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$)}. |
@@ -3915,8 +4381,9 @@ It should be obvious that the static type rules for these expressions are define |
\subsection{ Shift} |
-\label{shift} |
+\LMLabel{shift} |
+\LMHash{} |
Shift expressions invoke the shift operators on objects. |
\begin{grammar} |
@@ -3929,8 +4396,10 @@ Shift expressions invoke the shift operators on objects. |
. |
\end{grammar} |
+\LMHash{} |
A {\em shift expression} is either an additive expression (\ref{additiveExpressions}), or an invocation of a shift operator on either \SUPER{} or an expression $e_1$, 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$)}. |
\commentary{ |
@@ -3943,8 +4412,9 @@ The same holds for additive and multiplicative expressions. |
} |
\subsection{ Additive Expressions} |
-\label{additiveExpressions} |
+\LMLabel{additiveExpressions} |
+\LMHash{} |
Additive expressions invoke the addition operators on objects. |
\begin{grammar} |
@@ -3957,16 +4427,20 @@ Additive expressions invoke the addition operators on objects. |
. |
\end{grammar} |
+\LMHash{} |
An {\em additive expression} is either a multiplicative expression (\ref{multiplicativeExpressions}), or an invocation of an additive operator on either \SUPER{} or an expression $e_1$, 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$)}. |
+\LMHash{} |
The static type of an additive expression is usually determined by the signature given in the declaration of the operator used. However, invocations of the operators \cd{+} and \cd{-} of class \cd{int} are treated specially by the typechecker. The static type of an expression $e_1 + e_2$ where $e_1$ has static type \cd{int} is \cd{int} if the static type of $e_2$ is \cd{int}, and \cd{double} if the static type of $e_2$ is \cd{double}. The static type of an expression $e_1 - e_2$ where $e_1$ has static type \cd{int} is \cd{int} if the static type of $e_2$ is \cd{int}, and \cd{double} if the static type of $e_2$ is \cd{double}. |
\subsection{ Multiplicative Expressions} |
-\label{multiplicativeExpressions} |
+\LMLabel{multiplicativeExpressions} |
+\LMHash{} |
Multiplicative expressions invoke the multiplication operators on objects. |
\begin{grammar} |
@@ -3982,15 +4456,19 @@ Multiplicative expressions invoke the multiplication operators on objects. |
\end{grammar} |
+\LMHash{} |
A {\em multiplicative expression} is either a unary expression (\ref{unaryExpressions}), or an invocation of a multiplicative operator on either \SUPER{} or an expression $e_1$, 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$)}. |
+\LMHash{} |
The static type of an multiplicative expression is usually determined by the signature given in the declaration of the operator used. However, invocations of the operators \cd{*}, \cd{\%} and \cd{\~{}/} of class \cd{int} are treated specially by the typechecker. The static type of an expression $e_1 * e_2$ where $e_1$ has static type \cd{int} is \cd{int} if the static type of $e_2$ is \cd{int}, and \cd{double} if the static type of $e_2$ is \cd{double}. The static type of an expression $e_1 \% e_2$ where $e_1$ has static type \cd{int} is \cd{int} if the static type of $e_2$ is \cd{int}, and \cd{double} if the static type of $e_2$ is \cd{double}. The static type of an expression \cd{$e_1$ \~{}/ $e_2$} where $e_1$ has static type \cd{int} is \cd{int} if the static type of $e_2$ is \cd{int}. |
\subsection{ Unary Expressions} |
-\label{unaryExpressions} |
+\LMLabel{unaryExpressions} |
+\LMHash{} |
Unary expressions invoke unary operators on objects. |
\begin{grammar} |
@@ -4019,19 +4497,24 @@ Unary expressions invoke unary operators on objects. |
\end{grammar} |
+\LMHash{} |
A {\em unary expression} is either a postfix expression (\ref{postfixExpressions}), an await expression (\ref{awaitExpressions}) or an invocation of a prefix operator on an expression or an invocation of a unary operator on either \SUPER{} or an expression $e$. |
+\LMHash{} |
The expression $!e$ is equivalent to the expression $e?$ $ \FALSE{} :\TRUE{}$. |
+\LMHash{} |
Evaluation of an expression of the form \code{++$e$} is equivalent to \code{$e$ += 1}. Evaluation of an expression of the form \code{-{}-$e$} is equivalent to \code{$e$ -= 1}. |
%The expression $-e$ is equivalent to the method invocation \code{$e$.-()}. The expression \code{-\SUPER{}} is equivalent to the method invocation \code{\SUPER{}.-()}. |
+\LMHash{} |
An 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{superInvocation}) \code{\SUPER{}.$op()$}. |
\subsection{ Await Expressions} |
-\label{awaitExpressions} |
+\LMLabel{awaitExpressions} |
+\LMHash{} |
An {\em await expression} allows code to yield control until an asynchronous operation (\ref{functions}) completes. |
\begin{grammar} |
@@ -4039,11 +4522,14 @@ An {\em await expression} allows code to yield control until an asynchronous ope |
\AWAIT{} unaryExpression |
\end{grammar} |
+\LMHash{} |
Evaluation of an await expression $a$ of the form \AWAIT{} $e$ proceeds as follows: |
First, the expression $e$ is evaluated. Next: |
+\LMHash{} |
If $e$ raises an exception $x$, then an instance $f$ of class \code{Future} is allocated and later completed with $x$. Otherwise, if $e$ evaluates to an object $o$ that is not an instance of \code{Future}, then let $f$ be the result of calling \code{Future.value()} with $o$ as its argument; otherwise let $f$ be the result of evaluating $e$. |
+\LMHash{} |
Next, execution of the function $m$ immediately enclosing $a$ is suspended until after $f$ completes. The stream associated with the innermost enclosing asynchronous for loop (\ref{asynchronousFor-in}), if any, is paused. At some time after $f$ is completed, control returns to the current invocation. The stream associated with the innermost enclosing asynchronous for loop (\ref{asynchronousFor-in}), if any, is resumed. If $f$ has completed with an exception $x$, $a$ raises $x$. If $f$ completes with a value $v$, $a$ evaluates to $v$. |
%Otherwise, the value of $a$ is the value of $e$. If evaluation of $e$ raises an exception $x$, $a$ raises $x$. |
@@ -4060,6 +4546,7 @@ An await expression has no meaning in a synchronous function. If such a function |
It is not a static warning if the type of $e$ is not a subtype of \code{Future}. Tools may choose to give a hint in such cases. |
} |
+\LMHash{} |
Let $flatten(T) = flatten(S)$ if $T = Future<S>$, and $T$ otherwise. The static type of $a$ is $flatten(T)$ where $T$ is the static type of $e$. |
\rationale{ |
@@ -4070,8 +4557,9 @@ We collapse multiple layers of futures into one. If $e$ evaluates to a future $f |
\subsection{ Postfix Expressions} |
-\label{postfixExpressions} |
+\LMLabel{postfixExpressions} |
+\LMHash{} |
Postfix expressions invoke the postfix operators on objects. |
\begin{grammar} |
@@ -4093,45 +4581,55 @@ Postfix expressions invoke the postfix operators on objects. |
\end{grammar} |
+\LMHash{} |
A {\em postfix expression} is either a primary expression, a function, method or getter invocation, or an invocation of a postfix operator on an expression $e$. |
+\LMHash{} |
A postfix expression of the form \code{$v$++}, where $v$ is an identifier, is equivalent to \code{()\{var r = $v$; $v$ = r + 1; return r\}()}. |
\rationale{The above ensures that if $v$ is a field, the getter gets called exactly once. Likewise in the cases below. |
} |
+\LMHash{} |
A postfix expression of the form \code{$C.v$ ++} is equivalent to |
\code{()\{var r = $C.v$; $C.v$ = r + 1; return r\}()}. |
+\LMHash{} |
A postfix expression of the form \code{$e_1.v$++} is equivalent to |
\code{(x)\{var r = x.v; x.v = r + 1; \RETURN{} r\}($e_1$)}. |
+\LMHash{} |
A postfix expression of the form \code{$e_1[e_2]$++}, is equivalent to |
\code{(a, i)\{var r = a[i]; a[i] = r + 1; return r\}($e_1$, $e_2$)}. |
+\LMHash{} |
A postfix expression of the form \code{$v$-{}-}, where $v$ is an identifier, is equivalent to |
\code{()\{var r = $v$; $v$ = r - 1; return r\}()}. |
+\LMHash{} |
A postfix expression of the form \code{$C.v$-{}-} is equivalent to |
\code{()\{var r = $C.v$; $C.v$ = r - 1; return r\}()}. |
+\LMHash{} |
A postfix expression of the form \code{$e_1.v$-{}-} is equivalent to |
\code{(x)\{var r = x.v; x.v = r - 1; \RETURN{} r\}($e_1$)}. |
+\LMHash{} |
A postfix expression of the form \code{$e_1[e_2]$-{}-}, is equivalent to |
\code{(a, i)\{var r = a[i]; a[i] = r - 1; return r\}($e_1$, $e_2$)}. |
\subsection{ Assignable Expressions} |
-\label{assignableExpressions} |
+\LMLabel{assignableExpressions} |
+\LMHash{} |
Assignable expressions are expressions that can appear on the left hand side of an assignment. |
This section describes how to evaluate these expressions when they do not constitute the complete left hand side of an assignment. |
@@ -4154,6 +4652,7 @@ Of course, if assignable expressions always appeared {\em as} the left hand side |
\end{grammar} |
+\LMHash{} |
An {\em assignable expression} is either: |
\begin{itemize} |
\item An identifier. |
@@ -4162,21 +4661,27 @@ An {\em assignable expression} is either: |
\end{itemize} |
+\LMHash{} |
An assignable expression of the form $id$ is evaluated as an identifier expression (\ref{identifierReference}). |
%An assignable expression of the form $e.id(a_1, \ldots, a_n)$ is evaluated as a method invocation (\ref{methodInvocation}). |
+\LMHash{} |
An assignable expression of the form $e.id$ is evaluated as a property extraction (\ref{propertyExtraction}). |
+\LMHash{} |
An assignable expression of the form \code{$e_1$[$e_2$]} is evaluated as a method invocation of the operator method \code{[]} on $e_1$ with argument $e_2$. |
+\LMHash{} |
An assignable expression of the form \code{\SUPER{}.id} is evaluated as a property extraction. |
+\LMHash{} |
An assignable expression of the form \code{\SUPER{}[$e_2$]} is equivalent to the method invocation \code{\SUPER{}.[]($e_2$)}. |
\subsection{ Identifier Reference} |
-\label{identifierReference} |
+\LMLabel{identifierReference} |
+\LMHash{} |
An {\em identifier expression} consists of a single identifier; it provides access to an object via an unqualified name. |
\begin{grammar} |
@@ -4235,21 +4740,25 @@ An {\em identifier expression} consists of a single identifier; it provides acce |
. |
\end{grammar} |
+\LMHash{} |
A built-in identifier is one of the identifiers produced by the production {\em BUILT\_IN\_IDENTIFIER}. It is a compile-time error if a built-in identifier is used as the declared name of a prefix, class, type parameter or type alias. It is a compile-time error to use a built-in identifier other than \DYNAMIC{} as a type annotation or type parameter. |
\rationale{ |
Built-in identifiers are identifiers that are used as keywords in Dart, but are not reserved words in Javascript. To minimize incompatibilities when porting Javascript code to Dart, we do not make these into reserved words. A built-in identifier may not be used to name a class or type. In other words, they are treated as reserved words when used as types. This eliminates many confusing situations without causing compatibility problems. After all, a Javascript program has no type declarations or annotations so no clash can occur. Furthermore, types should begin with an uppercase letter (see the appendix) and so no clash should occur in any Dart user program anyway. |
} |
+\LMHash{} |
It is a compile-time error if any of the identifiers \ASYNC, \AWAIT{} or \YIELD{} is used as an identifier in a function body marked with either \ASYNC{}, \ASYNC* or \SYNC*. |
\rationale{ |
For compatibility reasons, new constructs cannot rely upon new reserved words or even built-in identifiers. However, the constructs above are only usable in contexts that require special markers introduced concurrently with these constructs, so no old code could use them. Hence the restriction, which treats these names as reserved words in a limited context. |
} |
+\LMHash{} |
Evaluation of an identifier expression $e$ of the form $id$ proceeds as follows: |
+\LMHash{} |
Let $d$ be the innermost declaration in the enclosing lexical scope whose name is $id$ or $id=$. If no such declaration exists in the lexical scope, let $d$ be the declaration of the inherited member named $id$ if it exists. |
%If no such member exists, let $d$ be the declaration of the static member name $id$ declared in a superclass of the current class, if it exists. |
@@ -4276,6 +4785,7 @@ Let $d$ be the innermost declaration in the enclosing lexical scope whose name i |
% This implies that referring to an undefined static getter by simple name is an error, whereas doing so by qualified name is only a warning. Same with assignments. Revise? |
\end{itemize} |
+\LMHash{} |
The static type of $e$ is determined as follows: |
\begin{itemize} |
@@ -4294,11 +4804,13 @@ The static type of $e$ is determined as follows: |
This prevents situations where one uses uncorrelated setters and getters. The intent is to prevent errors when a getter in a surrounding scope is used accidentally. |
} |
+\LMHash{} |
It is a static warning if an identifier expression $id$ occurs inside a top level or static function (be it function, method, getter, or setter) or variable initializer and there is no declaration $d$ with name $id$ in the lexical scope enclosing the expression. |
\subsection{ Type Test} |
-\label{typeTest} |
+\LMLabel{typeTest} |
+\LMHash{} |
The {\em is-expression} tests if an object is a member of a type. |
\begin{grammar} |
@@ -4312,8 +4824,10 @@ The {\em is-expression} tests if an object is a member of a type. |
. |
\end{grammar} |
+\LMHash{} |
Evaluation of the is-expression \code{$e$ \IS{} $T$} proceeds as follows: |
+\LMHash{} |
The expression $e$ is evaluated to a value $v$. Then, if $T$ is a malformed or deferred type (\ref{staticTypes}), a dynamic error occurs. Otherwise, if the interface of the class of $v$ is a subtype of $T$, the is-expression evaluates to \TRUE. Otherwise it evaluates to \FALSE. |
\commentary{It follows that \code{$e$ \IS{} Object} is always true. This makes sense in a language where everything is an object. |
@@ -4321,11 +4835,13 @@ The expression $e$ is evaluated to a value $v$. Then, if $T$ is a malformed or d |
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 a null value directly rather than via type tests. |
} |
+\LMHash{} |
The is-expression \code{$e$ \IS{}! $T$} is equivalent to \code{!($e$ \IS{} $T$)}. |
% Add flow dependent types |
+\LMHash{} |
Let $v$ be a local variable or a formal parameter. An is-expression of the form \code{$v$ \IS{} $T$} shows that $v$ has type $T$ iff $T$ is more specific than the type $S$ of the expression $v$ and both $T \ne \DYNAMIC{}$ and $S \ne \DYNAMIC{}$. |
\rationale{ |
@@ -4338,12 +4854,14 @@ It is pointless to deduce a weaker type than what is already known. Furthermore, |
We do not want to refine the type of a variable of type \DYNAMIC{}, as this could lead to more warnings rather than less. The opposite requirement, that $T \ne \DYNAMIC{}$ is a safeguard lest $S$ ever be $\bot$. |
} |
+\LMHash{} |
The static type of an is-expression is \code{bool}. |
\subsection{ Type Cast} |
-\label{typeCast} |
+\LMLabel{typeCast} |
+\LMHash{} |
The {\em cast expression} ensures that an object is a member of a type. |
\begin{grammar} |
@@ -4357,16 +4875,19 @@ The {\em cast expression} ensures that an object is a member of a type. |
. |
\end{grammar} |
+\LMHash{} |
Evaluation of the cast expression \code{$e$ \AS{} $T$} proceeds as follows: |
+\LMHash{} |
The expression $e$ is evaluated to a value $v$. Then, if $T$ is a malformed or deferred type (\ref{staticTypes}), a dynamic error occurs. Otherwise, if the interface of the class of $v$ is a subtype of $T$, the cast expression evaluates to $v$. Otherwise, if $v$ is \NULL{}, the cast expression evaluates to $v$. |
In all other cases, a \code{CastError} is thrown. |
+\LMHash{} |
The static type of a cast expression \code{$e$ \AS{} $T$} is $T$. |
\section{Statements} |
-\label{statements} |
+\LMLabel{statements} |
\begin{grammar} |
{\bf statements:} |
@@ -4399,21 +4920,26 @@ The static type of a cast expression \code{$e$ \AS{} $T$} is $T$. |
\end{grammar} |
\subsection{Blocks} |
- \label{blocks} |
+ \LMLabel{blocks} |
+\LMHash{} |
A {\em block statement} supports sequencing of code. |
+\LMHash{} |
Execution of a block statement $\{s_1, \ldots, s_n\}$ proceeds as follows: |
+\LMHash{} |
For $i \in 1 .. n, s_i$ is executed. |
+\LMHash{} |
A block statement introduces a new scope, which is nested in the lexically enclosing scope in which the block statement appears. |
\subsection{Expression Statements} |
- \label{expressionStatements} |
+ \LMLabel{expressionStatements} |
+\LMHash{} |
An {\em expression statement} consists of an expression other than a non-constant map literal (\ref{maps}) that has no explicit type arguments. |
\rationale{ |
@@ -4426,14 +4952,17 @@ The restriction on maps is designed to resolve an ambiguity in the grammar, whe |
. |
\end{grammar} |
+\LMHash{} |
Execution of an expression statement \code{$e$;} proceeds by evaluating $e$. |
+\LMHash{} |
It is a compile-time error if a non-constant map literal that has no explicit type arguments appears in a place where a statement is expected. |
\subsection{Local Variable Declaration} |
-\label{localVariableDeclaration} |
+\LMLabel{localVariableDeclaration} |
+\LMHash{} |
A {\em variable declaration statement }declares a new local variable. |
\begin{grammar} |
@@ -4442,10 +4971,13 @@ A {\em variable declaration statement }declares a new local variable. |
. |
\end{grammar} |
+\LMHash{} |
Executing a variable declaration statement of one of the forms \VAR{} $v = e;$, $T$ $v = e; $, \CONST{} $v = e;$, \CONST{} $T$ $v = e;$, \FINAL{} $v = e;$ or \FINAL{} $T$ $v = e;$ proceeds as follows: |
+\LMHash{} |
The expression $e$ is evaluated to an object $o$. Then, the variable $v$ is set to $o$. |
+\LMHash{} |
A variable declaration statement of the form \VAR{} $v;$ is equivalent to \VAR{} $v = \NULL{};$. A variable declaration statement of the form $T$ $v;$ is equivalent to $T$ $v = \NULL{};$. |
\commentary{ |
@@ -4470,9 +5002,9 @@ To do otherwise would undermine the optionally typed nature of Dart, causing typ |
\subsection{Local Function Declaration} |
+\LMLabel{localFunctionDeclaration} |
-\label{localFunctionDeclaration} |
- |
+\LMHash{} |
A function declaration statement declares a new local function (\ref{functionDeclarations}). |
\begin{grammar} |
@@ -4481,6 +5013,7 @@ A function declaration statement declares a new local function (\ref{functionDec |
. |
\end{grammar} |
+\LMHash{} |
A function declaration statement of one of the forms $id$ $signature$ $\{ statements \}$ or $T$ $id$ $signature$ $\{ statements \}$ causes a new function named $id$ to be added to the innermost enclosing scope. It is a compile-time error to reference a local function before its declaration. |
@@ -4524,8 +5057,9 @@ The rules for local functions differ slightly from those for local variables in |
% elaborate on function identity and equality, runtime type. Likewsie in function expressions (closures) and declarations |
\subsection{If} |
-\label{if} |
+\LMLabel{if} |
+\LMHash{} |
The {\em if statement} allows for conditional execution of statements. |
\begin{grammar} |
@@ -4536,6 +5070,7 @@ The {\em if statement} allows for conditional execution of statements. |
Execution of an if statement of the form \code {\IF{} (}$b$\code{)}$s_1$ \code{\ELSE{} } $s_2$ proceeds as follows: |
+\LMHash{} |
First, the expression $b$ is evaluated to an object $o$. Then, $o$ is subjected to boolean conversion (\ref{booleanConversion}), producing an object $r$. If $r$ is \TRUE{}, then the statement $\{s_1\}$ is executed, otherwise statement $\{s_2\}$ is executed. |
@@ -4559,8 +5094,10 @@ Execution of an if statement of the form \code {\IF{} (}$b$\code{)}$s_1$ \code{ |
Under reasonable scope rules such code is problematic. If we assume that \code{v} is declared in the scope of the method \code{main()}, then when \code{somePredicate} is false, \code{v} will be uninitialized when accessed. The cleanest approach would be to require a block following the test, rather than an arbitrary statement. However, this goes against long standing custom, undermining Dart's goal of familiarity. Instead, we choose to insert a block, introducing a scope, around the statement following the predicate (and similarly for \ELSE{} and loops). This will cause both a warning and a runtime error in the case above. Of course, if there is a declaration of \code{v} in the surrounding scope, programmers might still be surprised. We expect tools to highlight cases of shadowing to help avoid such situations. |
} |
+\LMHash{} |
It is a static type warning if the type of the expression $b$ may not be assigned to \code{bool}. |
+\LMHash{} |
If: |
\begin{itemize} |
\item $b$ shows that a variable $v$ has type $T$. |
@@ -4569,6 +5106,7 @@ If: |
\end{itemize} |
then the type of $v$ is known to be $T$ in $s_1$. |
+\LMHash{} |
An if statement of the form \code {\IF{} (}$b$\code{)}$s_1$ is equivalent to the if statement |
\code {\IF{} (}$b$\code{)}$s_1$ \code{\ELSE{} \{\}}. |
@@ -4576,8 +5114,9 @@ then the type of $v$ is known to be $T$ in $s_1$. |
\subsection{For} |
-\label{for} |
+\LMLabel{for} |
+\LMHash{} |
The {\em for statement} supports iteration. |
\begin{grammar} |
@@ -4595,16 +5134,20 @@ The {\em for statement} supports iteration. |
. |
\end{grammar} |
+\LMHash{} |
The for statement has three forms - the traditional for loop and two forms of the for-in statement - synchronous and asynchronous. |
\subsubsection{For Loop} |
-\label{forLoop} |
+\LMLabel{forLoop} |
+\LMHash{} |
Execution of a for statement of the form \code{ \FOR{} (\VAR{} $v = e_0$ ; $c$; $e$) $s$} proceeds as follows: |
+\LMHash{} |
If $c$ is empty then let $c^\prime$ be \TRUE{} otherwise let $c^\prime$ be $c$. |
+\LMHash{} |
First the variable declaration statement \VAR{} $v = e_0$ is executed. Then: |
\begin{enumerate} |
\item |
@@ -4630,6 +5173,7 @@ The definition above is intended to prevent the common error where users create |
Instead, each iteration has its own distinct variable. The first iteration uses the variable created by the initial declaration. The expression executed at the end of each iteration uses a fresh variable $v^{\prime\prime}$, bound to the value of the current iteration variable, and then modifies $v^{\prime\prime}$ as required for the next iteration. |
} |
+\LMHash{} |
It is a static warning if the static type of $c$ may not be assigned to \cd{bool}. |
%A for statement of the form \code{ \FOR{} ($d$ ; $c$; $e$) $s$} is equivalent to the the following code: |
@@ -4645,8 +5189,9 @@ It is a static warning if the static type of $c$ may not be assigned to \cd{bool |
%If $c$ is empty, it is interpreted as \TRUE{}. |
\subsubsection{For-in} |
-\label{for-in} |
+\LMLabel{for-in} |
+\LMHash{} |
A for statement of the form \code{ \FOR{} ($finalConstVarOrType?$ id \IN{} $e$) $s$} is equivalent to the following code: |
\begin{dartCode} |
@@ -4663,30 +5208,37 @@ Note that in fact, using a \CONST{} variable would give rise to a compile time |
} |
\subsubsection{Asynchronous For-in} |
-\label{asynchronousFor-in} |
+\LMLabel{asynchronousFor-in} |
+\LMHash{} |
A for-in statement may be asynchronous. The asynchronous form is designed to iterate over streams. An asynchronous for loop is distinguished by the keyword \AWAIT{} immediately preceding the keyword \FOR. |
+\LMHash{} |
Execution of a for-in statement of the form \code{\AWAIT{} \FOR{} (finalConstVarOrType? id \IN{} $e$) $s$} proceeds as follows: |
+\LMHash{} |
The expression $e$ is evaluated to an object $o$. It is a dynamic error if $o$ is not an instance of a class that implements \code{Stream}. Otherwise, the expression \code{\AWAIT{} $v_f$} (\ref{awaitExpressions}) is evaluated, where $v_f$ is a fresh variable whose value is a fresh instance (\ref{generativeConstructors}) $f$ implementing the built-in class \code{Future}. |
+\LMHash{} |
The stream $o$ is listened to, and on each data event in $o$ the statement $s$ is executed with \code{id} bound to the value of the current element of the stream. If $s$ raises an exception, or if $o$ raises an exception, then $f$ is completed with that exception. Otherwise, when all events in the stream $o$ have been processed, $f$ is completed with \NULL{} (\ref{null}). |
+\LMHash{} |
Let $u$ be the stream associated with the immediately enclosing asynchronous for loop or generator function (\ref{functions}), if any. If another event $e_u$ of $u$ occurs before execution of $s$ is complete, handling of $e_u$ must wait until $s$ is complete. |
\rationale{ |
The future $f$ and the corresponding \AWAIT{} expression ensure that execution suspends as an asynchronous for loop begins and resumes after the \FOR{} statement when it ends. They also ensure that the stream of any enclosing asynchronous \FOR{} loop is paused for the duration of this loop. |
} |
+\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} |
-\label{while} |
+\LMLabel{while} |
+\LMHash{} |
The while statement supports conditional iteration, where the condition is evaluated prior to the loop. |
\begin{grammar} |
@@ -4695,16 +5247,20 @@ The while statement supports conditional iteration, where the condition is evalu |
. |
\end{grammar} |
+\LMHash{} |
Execution of a while statement of the form \code{\WHILE{} ($e$) $s$;} proceeds as follows: |
+\LMHash{} |
The expression $e$ is evaluated to an object $o$. Then, $o$ is subjected to boolean conversion (\ref{booleanConversion}), producing an object $r$. If $r$ is \TRUE{}, then the statement $\{s\}$ is executed and then the while statement is re-executed recursively. If $r$ is \FALSE{}, execution of the while statement is complete. |
+\LMHash{} |
It is a static type warning if the static type of $e$ may not be assigned to \code{bool}. |
\subsection{Do} |
-\label{do} |
+\LMLabel{do} |
+\LMHash{} |
The do statement supports conditional iteration, where the condition is evaluated after the loop. |
\begin{grammar} |
@@ -4714,15 +5270,19 @@ The do statement supports conditional iteration, where the condition is evaluate |
\end{grammar} |
+\LMHash{} |
Execution of a do statement of the form \code{\DO{} $s$ \WHILE{} ($e$);} proceeds as follows: |
+\LMHash{} |
The statement $\{s\}$ is executed. Then, the expression $e$ is evaluated to an object $o$. Then, $o$ is subjected to boolean conversion (\ref{booleanConversion}), producing an object $r$. If $r$ is \FALSE{}, execution of the do statement is complete. If $r$ is \TRUE{}, then the do statement is re-executed recursively. |
+\LMHash{} |
It is a static type warning if the static type of $e$ may not be assigned to \code{bool}. |
\subsection{Switch} |
-\label{switch} |
+\LMLabel{switch} |
+\LMHash{} |
The {\em switch statement} supports dispatching control among a large number of cases. |
\begin{grammar} |
@@ -4740,6 +5300,7 @@ The {\em switch statement} supports dispatching control among a large number of |
. |
\end{grammar} |
+\LMHash{} |
Given a switch statement of the form |
\begin{dartCode} |
@@ -4771,6 +5332,7 @@ The {\em switch statement} supports dispatching control among a large number of |
\commentary{In other words, all the expressions in the cases evaluate to constants of the exact same user defined class or are of certain known types. Note that the values of the expressions are known at compile-time, and are independent of any static type annotations. |
} |
+\LMHash{} |
It is a compile-time error if the class $C$ has an implementation of the operator $==$ other than the one inherited from \code{Object} unless the value of the expression is a string, an integer, literal symbol or the result of invoking a constant constructor of class \cd{Symbol}. |
\rationale{ |
@@ -4782,6 +5344,7 @@ It is a compile-time error if the class $C$ has an implementation of the operato |
The \SWITCH{} statement should only be used in very limited situations (e.g., interpreters or scanners). |
} |
+\LMHash{} |
Execution of a switch statement of the form |
\begin{dartCode} |
@@ -4805,14 +5368,18 @@ or the form |
proceeds as follows: |
+\LMHash{} |
The statement \code{\VAR{} id = $e$;} is evaluated, where \code{id} is a variable whose name is distinct from any other variable in the program. In checked mode, it is a run time 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 executed if it exists. If \CASE{} $e_{1}: s_{1}$ does not exist, then if there is a \DEFAULT{} clause it is executed by executing $s_{n+1}$. |
+\LMHash{} |
A case clause introduces a new scope, nested in the lexically surrounding scope. The scope of a case clause ends immediately after the case clause's statement list. |
+\LMHash{} |
Execution of a \CASE{} clause \CASE{} $e_{k}: s_{k}$ of a switch statement |
\begin{dartCode} |
@@ -4826,11 +5393,13 @@ Execution of a \CASE{} clause \CASE{} $e_{k}: s_{k}$ of a switch statement |
proceeds as follows: |
+\LMHash{} |
The expression \code{$e_k$ == id} is evaluated to an object $o$ which is then subjected to boolean conversion yielding a value $v$. |
If $v$ is not \TRUE{} the following case, \CASE{} $e_{k+1}: s_{k+1}$ is executed if it exists. If \CASE{} $e_{k+1}: s_{k+1}$ does not exist, then the \DEFAULT{} clause is executed by executing $s_{n+1}$. |
If $v$ is \TRUE{}, let $h$ be the smallest number such that $h \ge k$ and $s_h$ is non-empty. If no such $h$ exists, let $h = n + 1$. The sequence of statements $s_h$ is then executed. |
If execution reaches the point after $s_h$ then a runtime error occurs, unless $h = n+1$. |
+\LMHash{} |
Execution of a \CASE{} clause \CASE{} $e_{k}: s_{k}$ of a switch statement |
\begin{dartCode} |
@@ -4843,6 +5412,7 @@ Execution of a \CASE{} clause \CASE{} $e_{k}: s_{k}$ of a switch statement |
proceeds as follows: |
+\LMHash{} |
The expression \code{$e_k$ == id} is evaluated to an object $o$ which is then subjected to boolean conversion yielding a value $v$. |
If $v$ is not \TRUE{} the following case, \CASE{} $e_{k+1}: s_{k+1}$ is executed if it exists. |
If $v$ is \TRUE{}, let $h$ be the smallest integer such that $h \ge k$ and $s_h$ is non-empty. The sequence of statements $s_h$ is executed if it exists. |
@@ -4853,6 +5423,7 @@ If execution reaches the point after $s_h$ then a runtime error occurs, unless |
In other words, there is no implicit fall-through between cases. The last case in a switch (default or otherwise) can `fall-through' to the end of the statement. |
} |
+\LMHash{} |
It is a static warning if the type of $e$ may not be assigned to the type of $e_k$. It is a static warning if the last statement of the statement sequence $s_k$ is not a \BREAK{}, \CONTINUE{}, \RETURN{} or \THROW{} statement. |
\rationale{ |
@@ -4871,6 +5442,7 @@ The sophistication of the analysis of fall-through is another issue. For now, we |
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. |
@@ -4884,9 +5456,10 @@ In other words, a warning will be issued if a switch statement over an enum is n |
\subsection{ Rethrow} |
-\label{rethrow} |
+\LMLabel{rethrow} |
+\LMHash{} |
The {\em rethrow statement} is used to re-raise an exception. |
\begin{grammar} |
@@ -4895,33 +5468,40 @@ The {\em rethrow statement} is used to re-raise an exception. |
. |
\end{grammar} |
+\LMHash{} |
Execution of a \code{\RETHROW{}} statement proceeds as follows: |
+\LMHash{} |
Let $f$ be the immediately enclosing function, and let \code{\ON{} $T$ \CATCH{} ($p_1$, $p_2$)} be the immediately enclosing catch clause (\ref{try}). |
\rationale{ |
A \RETHROW{} statement always appears inside a \CATCH{} clause, and any \CATCH{} clause is semantically equivalent to some \CATCH{} clause of the form \code{\ON{} $T$ \CATCH{} (p1, p2)}. So we can assume that the \RETHROW{} is enclosed in a \CATCH{} clause of that form. |
} |
+\LMHash{} |
The current exception (\ref{throw}) is set to $p_1$, the current return value (\ref{return}) becomes undefined, and the active stack trace (\ref{try}) is set to $p_2$. |
+\LMHash{} |
If $f$ is marked \ASYNC{} or \ASYNC* (\ref{functions}) and there is a dynamically enclosing exception handler (\ref{try}) $h$ introduced by the current activation, control is transferred to $h$, otherwise $f$ terminates. |
\rationale{ |
In the case of an asynchronous function, the dynamically enclosing exception handler is only relevant within the function. If an exception is not caught within the function, the exception value is channelled through a future or stream rather than propagating via exception handlers. |
} |
+\LMHash{} |
Otherwise, control is transferred to the innermost enclosing exception handler. |
\commentary{The change in control may result in multiple functions terminating if these functions do not catch the exception via a \CATCH{} or \FINALLY{} clause, both of which introduce a dynamically enclosing exception handler.} |
+\LMHash{} |
It is a compile-time error if a \code{\RETHROW{}} statement is not enclosed within an \ON-\CATCH{} clause. |
\subsection{ Try} |
-\label{try} |
+\LMLabel{try} |
+\LMHash{} |
The try statement supports the definition of exception handling code in a structured way. |
\begin{grammar} |
@@ -4942,6 +5522,7 @@ The try statement supports the definition of exception handling code in a struct |
. |
\end{grammar} |
+\LMHash{} |
A try statement consists of a block statement, followed by at least one of: |
\begin{enumerate} |
\item |
@@ -4954,23 +5535,28 @@ A \FINALLY{} clause, which consists of a block statement. |
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{} |
An \ON{}-\CATCH{} clause of the form \code{\ON{} $T$ \CATCH{} ($p_1, p_2$) $s$} {\em matches} an object $o$ if the type of $o$ is a subtype of $T$. If $T$ is a malformed or deferred type (\ref{staticTypes}), then performing a match causes a run time error. |
\commentary { |
It is of course a static warning if $T$ is a deferred or malformed type. |
} |
+\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$ and the static type of $p_2$ is \code{StackTrace}. |
+\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$} where $p_2$ is an identifier that does not occur anywhere else in the program. |
+\LMHash{} |
An \ON{}-\CATCH{} clause of the form \code{\CATCH{} ($p$) $s$} is equivalent to an \ON{}-\CATCH{} clause \code{\ON{} \DYNAMIC{} \CATCH{} ($p$) $s$}. 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$}. |
%If an explicit type is associated with of $p_2$, it is a static warning if that type is not \code{Object} or \DYNAMIC{}. |
+\LMHash{} |
The {\em active stack trace} is an object whose \code{toString()} method produces a string that is a record of exactly those function activations within the current isolate that had not completed execution at the point where the current exception (\ref{throw}) was thrown. |
%\begin{enumerate} |
%\item Started execution after the currently executing function. |
@@ -5001,19 +5587,24 @@ Note that we say nothing about the identity of the stack trace, or what notion o |
% A position can be represented via a Token. If we make that part of the core reflection facility, we can state this here. |
+\LMHash{} |
A try statement \TRY{} $s_1$ $on-catch_1 \ldots on-catch_n$ \FINALLY{} $s_f$ defines an exception handler $h$ that executes as follows: |
+\LMHash{} |
The \ON{}-\CATCH{} clauses are examined in order, starting with $catch_1$, until either an \ON{}-\CATCH{} clause that matches the current exception (\ref{throw}) is found, or the list of \ON{}-\CATCH{} clauses has been exhausted. If an \ON{}-\CATCH{} clause $on-catch_k$ is found, then $p_{k1}$ is bound to the current exception, $p_{k2}$, if declared, is bound to the active stack trace, and then $catch_k$ is executed. If no \ON{}-\CATCH{} clause is found, the \FINALLY{} clause is executed. Then, execution resumes at the end of the try statement. |
+\LMHash{} |
A finally clause \FINALLY{} $s$ defines an exception handler $h$ that executes as follows: |
+\LMHash{} |
Let $r$ be the current return value (\ref{return}). Then the current return value becomes undefined. Any open streams associated with any asynchronous for loops (\ref{asynchronousFor-in}) and yield-each (\ref{yieldEach}) statements executing within the dynamic scope of $h$ are canceled. |
\rationale{ |
Streams left open by for loops that were escaped for whatever reason would be canceled at function termination, but it is best to cancel them as soon as possible. |
} |
+\LMHash{} |
Then the \FINALLY{} clause is executed. Let $m$ be the immediately enclosing function. If $r$ is defined then the current return value is set to $r$ and then: |
\begin{itemize} |
\item |
@@ -5024,15 +5615,20 @@ Otherwise $m$ terminates. |
Otherwise, execution resumes at the end of the try statement. |
+\LMHash{} |
Execution of an \ON{}-\CATCH{} clause \code{\ON{} $T$ \CATCH{} ($p_1$, $p_2$)} $s$ of a try statement $t$ proceeds as follows: The statement $s$ is executed in the dynamic scope of the exception handler defined by the finally clause of $t$. Then, the current exception and active stack trace both become undefined. |
+\LMHash{} |
Execution of a \FINALLY{} clause \FINALLY{} $s$ of a try statement proceeds as follows: |
+\LMHash{} |
Let $x$ be the current exception and let $t$ be the active stack trace. Then the current exception and the active stack trace both become undefined. The statement $s$ is executed. Then, if $x$ is defined, it is rethrown as if by a rethrow statement (\ref{rethrow}) enclosed in a \CATCH{} clause of the form \code{\CATCH{} ($v_x$, $v_t$)} where $v_x$ and $v_t$ are fresh variables bound to $x$ and $t$ respectively. |
+\LMHash{} |
Execution of a try statement of the form \code{\TRY{} $s_1$ $on-catch_1 \ldots on-catch_n$ \FINALLY{} $s_f$;} proceeds as follows: |
+\LMHash{} |
The statement $s_1$ is executed in the dynamic scope of the exception handler defined by the try statement. Then, the \FINALLY{} clause is executed. |
\commentary{ |
@@ -5047,12 +5643,14 @@ If an exception is thrown during execution of an \ON{}-\CATCH{} clause, this wil |
If no exception was raised, the \FINALLY{} clause is also executed. Execution of the \FINALLY{} clause could also raise an exception, which will cause transfer of control to the next enclosing handler. |
} |
+\LMHash{} |
A try statement of the form \code{\TRY{} $s_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{} $\{\}$}. |
\subsection{ Return} |
-\label{return} |
+\LMLabel{return} |
+\LMHash{} |
The {\em return statement} returns a result to the caller of a synchronous function, completes the future associated with an asynchronous function or terminates the stream or iterable associated with a generator (\ref{functions}). |
@@ -5066,10 +5664,13 @@ The {\em return statement} returns a result to the caller of a synchronous funct |
Due to \FINALLY{} clauses, the precise behavior of \RETURN{} is a little more involved. Whether the value a return statement is supposed to return is actually returned depends on the behavior of any \FINALLY{} clauses in effect when executing the return. A \FINALLY{} clause may choose to return another value, or throw an exception, or even redirect control flow leading to other returns or throws. All a return statement really does is set a value that is intended to be returned when the function terminates. |
} |
+\LMHash{} |
The {\em current return value} is a unique value specific to a given function activation. It is undefined unless explicitly set in this specification. |
+\LMHash{} |
Executing a return statement \code{\RETURN{} $e$;} proceeds as follows: |
+\LMHash{} |
First the expression $e$ is evaluated, producing an object $o$. Next: |
\begin{itemize} |
\item |
@@ -5085,28 +5686,34 @@ In the simplest case, the immediately enclosing function is an ordinary, synchro |
The enclosing function cannot be marked as generator (i.e, \ASYNC* or \SYNC*), since generators are not allowed to contain a statement of the form \code{\RETURN{} $e$;} as discussed below. |
} |
+\LMHash{} |
Let $T$ be the static type of $e$ and let $f$ be the immediately enclosing function. |
+\LMHash{} |
It is a static type warning if the body of $f$ is marked \ASYNC{} and the type \code{Future$<$flatten(T)$>$} (\ref{awaitExpressions}) may not be assigned to the declared return type of $f$. Otherwise, it is a static type warning if $T$ may not be assigned to the declared return type of $f$. |
+\LMHash{} |
Let $S$ be the runtime type of $o$. In checked mode: |
\begin{itemize} |
\item If the body of $f$ is marked \ASYNC{} (\ref{functions}) it is a dynamic type error if $o$ is not \NULL{} (\ref{null}) and \code{Future$<$S$>$} is not a subtype of the actual return type (\ref{actualTypeOfADeclaration}) of $f$. |
\item Otherwise, it is a dynamic type error if $o$ is not \NULL{} and the runtime type of $o$ is not a subtype of the actual return type of $f$. |
\end{itemize} |
+\LMHash{} |
It is a compile-time error if a return statement of the form \code{\RETURN{} $e$;} appears in a generative constructor (\ref{generativeConstructors}). |
\rationale{ |
It is quite easy to forget to add the factory prefix for a constructor, accidentally converting a factory into a generative constructor. The static checker may detect a type mismatch in some, but not all, of these cases. The rule above helps catch such errors, which can otherwise be very hard to recognize. There is no real downside to it, as returning a value from a generative constructor is meaningless. |
} |
+\LMHash{} |
It is a 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 value returned by the function is the iterable or stream associated with it, and individual elements are added to that iterable using yield statements, and so returning a value makes no sense. |
} |
+\LMHash{} |
Let $f$ be the function immediately enclosing a return statement of the form \RETURN{}; It is a static warning $f$ is neither a generator nor a generative constructor and either: |
\begin{itemize} |
\item $f$ is synchronous and the return type of $f$ may not be assigned to \VOID{} (\ref{typeVoid}) or, |
@@ -5120,8 +5727,10 @@ Hence, a static warning will not be issued if $f$ has no declared return type, s |
\rationale{ An asynchronous non-generator always returns a future of some sort. If no expression is given, the future will be completed with \NULL{} and this motivates the requirement above.} \commentary{Leaving the return type of a function marked \ASYNC{} blank will be interpreted as \DYNAMIC{} as always, and cause no type error. Using \code{Future} or \code{Future$<$Object$>$} is acceptable as well, but any other type will cause a warning, since \NULL{} has no subtypes.} |
+\LMHash{} |
A return statement with no expression, \code{\RETURN;} is executed as follows: |
+\LMHash{} |
If the immediately enclosing function $f$ is a generator, then: |
\begin{itemize} |
\item |
@@ -5132,6 +5741,7 @@ Let $c$ be the \FINALLY{} clause of the innermost enclosing try-finally statemen |
Otherwise, execution of the current method terminates. |
\end{itemize} |
+\LMHash{} |
Otherwise the return statement is executed by executing the statement \code{\RETURN{} \NULL{};} if it occurs inside a method, getter, setter or factory; otherwise, the return statement necessarily occurs inside a generative constructor, in which case it is executed by executing \code{\RETURN{} \THIS{};}. |
\commentary{Despite the fact that \code{\RETURN{};} is executed as if by a \code{\RETURN{} $e$;}, it is important to understand that it is not a static warning to include a statement of the form \code{\RETURN{};} |
@@ -5146,14 +5756,16 @@ The motivation for formulating \code{\RETURN{};} in this way stems from the basi |
The question then becomes, what value should a return statement return when no return expression is given. In a generative constructor, it is obviously the object being constructed (\THIS{}). A void function is not expected to participate in an expression, which is why it is marked \VOID{} in the first place. Hence, this situation is a mistake which should be detected as soon as possible. The static rules help here, but if the code is executed, using \NULL{} leads to fast failure, which is desirable in this case. The same rationale applies for function bodies that do not contain a return statement at all. |
} |
+\LMHash{} |
It is a static warning if a function contains both one or more explicit return statements of the form \code{\RETURN;} and one or more return statements of the form \code{\RETURN{} $e$;}. |
\subsection{ Labels} |
-\label{labels} |
+\LMLabel{labels} |
+\LMHash{} |
A {\em label} is an identifier followed by a colon. A {\em labeled statement} is a statement prefixed by a label $L$. A {\em labeled case clause} is a case clause within a switch statement (\ref{switch}) prefixed by a label $L$. |
\rationale{The sole role of labels is to provide targets for the break (\ref{break}) and continue (\ref{continue}) statements.} |
@@ -5166,8 +5778,10 @@ A {\em label} is an identifier followed by a colon. A {\em labeled statement} is |
. |
\end{grammar} |
+\LMHash{} |
The semantics of a labeled statement $L: s$ are identical to those of the statement $s$. The namespace of labels is distinct from the one used for types, functions and variables. |
+\LMHash{} |
The scope of a label that labels a statement $s$ is $s$. The scope of a label that labels a case clause of a switch statement $s$ is $s$. |
\rationale{Labels should be avoided by programmers at all costs. The motivation for including labels in the language is primarily making Dart a better target for code generation. |
@@ -5175,8 +5789,9 @@ The scope of a label that labels a statement $s$ is $s$. The scope of a label th |
\subsection{ Break} |
-\label{break} |
+\LMLabel{break} |
+\LMHash{} |
The {\em break statement} consists of the reserved word \BREAK{} and an optional label (\ref{labels}). |
\begin{grammar} |
@@ -5185,15 +5800,18 @@ The {\em break statement} consists of the reserved word \BREAK{} and an optional |
. |
\end{grammar} |
+\LMHash{} |
Let $s_b$ be a \BREAK{} statement. If $s_b$ is of the form \code{\BREAK{} $L$;}, then let $s_E$ be the the innermost labeled statement with label $L$ enclosing $s_b$. If $s_b$ is of the form \code{\BREAK{};}, then let $s_E$ be the the innermost \DO{} (\ref{do}), \FOR{} (\ref{for}), \SWITCH{} (\ref{switch}) or \WHILE{} (\ref{while}) statement enclosing $s_b$. It is a compile-time error if no such statement $s_E$ exists within the innermost function in which $s_b$ occurs. Furthermore, let $s_1, \ldots, s_n$ be those \TRY{} statements that are both enclosed in $s_E$ and that enclose $s_b$, and that have a \FINALLY{} clause. Lastly, let $f_j$ be the \FINALLY{} clause of $s_j, 1 \le j \le n$. Executing $s_b$ first executes $f_1, \ldots, f_n$ in innermost-clause-first order and then terminates $s_E$. |
+\LMHash{} |
If $s_E$ is an asynchronous for loop (\ref{asynchronousFor-in}), its associated stream subscription is canceled. Furthermore, let $a_k$ be the set of asynchronous for loops and yield-each statements (\ref{yieldEach}) enclosing $s_b$ that are enclosed in $s_E , 1 \le k \le m$. The stream subscriptions associated with $a_j$ are canceled, $1 \le j \le m$. |
\subsection{ Continue} |
-\label{continue} |
+\LMLabel{continue} |
+\LMHash{} |
The {\em continue statement} consists of the reserved word \CONTINUE{} and an optional label (\ref{labels}). |
\begin{grammar} |
@@ -5202,20 +5820,23 @@ The {\em continue statement} consists of the reserved word \CONTINUE{} and an op |
. |
\end{grammar} |
+\LMHash{} |
Let $s_c$ be a \CONTINUE{} statement. If $s_c$ is of the form \code{\CONTINUE{} $L$;}, then let $s_E$ be the the innermost labeled \DO{} (\ref{do}), \FOR{} (\ref{for}) or \WHILE{} (\ref{while}) statement or case clause with label $L$ enclosing $s_c$. If $s_c$ is of the form \code{\CONTINUE{};} then let $s_E$ be the the innermost \DO{} (\ref{do}), \FOR{} (\ref{for}) or \WHILE{} (\ref{while}) statement enclosing $s_c$. It is a compile-time error if no such statement or case clause $s_E$ exists within the innermost function in which $s_c$ occurs. Furthermore, let $s_1, \ldots, s_n$ be those \TRY{} statements that are both enclosed in $s_E$ and that enclose $s_c$, and that have a \FINALLY{} clause. Lastly, let $f_j$ be the \FINALLY{} clause of $s_j, 1 \le j \le n$. Executing $s_c$ first executes $f_1, \ldots, f_n$ in innermost-clause-first order. Then, if $s_E$ is a case clause, control is transferred to the case clause. Otherwise, $s_E$ is necessarily a loop and execution resumes after the last statement in the loop body. |
\commentary{ |
In a while loop, that would be the boolean expression before the body. In a do loop, it would be the boolean expression after the body. In a for loop, it would be the increment clause. In other words, execution continues to the next iteration of the loop. |
} |
+\LMHash{} |
If $s_E$ is an asynchronous for loop (\ref{asynchronousFor-in}), let $a_k$ be the set of asynchronous for loops and yield-each statements (\ref{yieldEach}) enclosing $s_c$ that are enclosed in $s_E , 1 \le k \le m$. The stream subscriptions associated with $a_j$ are canceled, $1 \le j \le m$. |
\subsection{ Yield and Yield-Each} |
- \label{yieldAndYieldEach} |
+ \LMLabel{yieldAndYieldEach} |
\subsubsection{ Yield} |
- \label{yield} |
+ \LMLabel{yield} |
+\LMHash{} |
The {\em yield statement} adds an element to the result of a generator function (\ref{functions}). |
\begin{grammar} |
@@ -5224,18 +5845,23 @@ The {\em continue statement} consists of the reserved word \CONTINUE{} and an op |
. |
\end{grammar} |
+\LMHash{} |
Execution of a statement $s$ of the form \code{\YIELD{} $e$;} proceeds as follows: |
+\LMHash{} |
First, the expression $e$ is evaluated to an object $o$. If the enclosing function $m$ is marked \ASYNC* (\ref{functions}) and the stream $u$ associated with $m$ has been paused, then execution of $m$ is suspended until $u$ is resumed or canceled. |
+\LMHash{} |
Next, $o$ is added to the iterable or stream associated with the immediately enclosing function. |
+\LMHash{} |
If the enclosing function $m$ is marked \ASYNC* and the stream $u$ associated with $m$ has been canceled, then let $c$ be the \FINALLY{} clause (\ref{try}) of the innermost enclosing try-finally statement, if any. If $c$ is defined, let $h$ be the handler induced by $c$. If $h$ is defined, control is transferred to $h$. If $h$ is undefined, the immediately enclosing function terminates. |
\rationale{ |
The stream associated with an asynchronous generator could be canceled by any code with a reference to that stream at any point where the generator was passivated. Such a cancellation constitutes an irretrievable error for the generator. At this point, the only plausible action for the generator is to clean up after itself via its \FINALLY{} clauses. |
} |
+\LMHash{} |
If the enclosing function $m$ is marked \SYNC* (\ref{functions}) then: |
\begin{itemize} |
\item |
@@ -5244,8 +5870,10 @@ Execution of the function $m$ immediately enclosing $s$ is suspended until the m |
The current call to \code{moveNext()} returns \TRUE. |
\end{itemize} |
+\LMHash{} |
It is a compile-time error if a yield statement appears in a function that is not a generator function. |
+\LMHash{} |
Let $T$ be the static type of $e$ and let $f$ be the immediately enclosing function. It is a static type warning if either: |
\begin{itemize} |
\item |
@@ -5256,8 +5884,9 @@ Let $T$ be the static type of $e$ and let $f$ be the immediately enclosing funct |
\subsubsection{ Yield-Each} |
- \label{yieldEach} |
+ \LMLabel{yieldEach} |
+\LMHash{} |
The {\em yield-each statement} adds a series of values to the result of a generator function (\ref{functions}). |
\begin{grammar} |
@@ -5266,8 +5895,10 @@ Let $T$ be the static type of $e$ and let $f$ be the immediately enclosing funct |
. |
\end{grammar} |
+\LMHash{} |
Execution of a statement s of the form \code{\YIELD* $e$;} proceeds as follows: |
+\LMHash{} |
First, the expression $e$ is evaluated to an object $o$. If the immediately enclosing function $m$ is synchronous, then it is a dynamic error if the class of $o$ does not implement \code{Iterable}. If $m$ asynchronous, then it is a dynamic error if the class of $o$ does not implement \code{Stream}. Next, for each element $x$ of $o$: |
\begin{itemize} |
\item |
@@ -5278,6 +5909,7 @@ If $m$ is marked \ASYNC* (\ref{functions}) and the stream $u$ associated with $m |
If $m$ is marked \ASYNC* and the stream $u$ associated with $m$ has been canceled, then let $c$ be the \FINALLY{} clause (\ref{try}) of the innermost enclosing try-finally statement, if any. If $c$ is defined, let $h$ be the handler induced by $c$. If $h$ is defined, control is transferred to $h$. If $h$ is undefined, the immediately enclosing function terminates. |
\end{itemize} |
+\LMHash{} |
If the enclosing function is marked \SYNC* (\ref{functions}) then: |
\begin{itemize} |
\item |
@@ -5286,14 +5918,17 @@ Execution of the function $m$ immediately enclosing $s$ is suspended until the m |
The current call to \code{moveNext()} returns \TRUE. |
\end{itemize} |
+\LMHash{} |
It is a compile-time error if a yield-each statement appears in a function that is not a generator function. |
+\LMHash{} |
Let $T$ be the static type of $e$ and let $f$ be the immediately enclosing function. It is a static type warning if $T$ may not be assigned to the declared return type of $f$. |
\subsection{ Assert} |
-\label{assert} |
+\LMLabel{assert} |
+\LMHash{} |
An {\em assert statement} is used to disrupt normal execution if a given boolean condition does not hold. |
\begin{grammar} |
@@ -5302,14 +5937,17 @@ An {\em assert statement} is used to disrupt normal execution if a given boolean |
. |
\end{grammar} |
+\LMHash{} |
The assert statement has no effect in production mode. In checked mode, execution of an assert statement \code{\ASSERT{}($e$);} proceeds as follows: |
+\LMHash{} |
The conditional expression $e$ is evaluated to an object $o$. If the class of $o$ is a subtype of \code{Function} then let $r$ be the result of invoking $o$ with no arguments. Otherwise, let $r$ be $o$. |
It is a dynamic type error if $o$ is not of type \code{bool} or of type \code{Function}, or if $r$ is not of type \code{bool}. If $r$ is \FALSE{}, we say that the assertion failed. If $r$ is \TRUE{}, we say that the assertion succeeded. If the assertion succeeded, execution of the assert statement is complete. If the assertion failed, an \code{AssertionError} is thrown. |
%\Q{Might be cleaner to define it as \code{if (!$e$) \{\THROW{} \NEW{} AssertionError();\}} (in checked mode only). |
%What about an error message as part of the assert?} |
+\LMHash{} |
It is a static type warning if the type of $e$ may not be assigned to either \code{bool} or $() \rightarrow$ \code{bool}. |
\rationale{Why is this a statement, not a built in function call? Because it is handled magically so it has no effect and no overhead in production mode. Also, in the absence of final methods. one could not prevent it being overridden (though there is no real harm in that). It cannot be viewed as a function call that is being optimized away because the argument might have side effects. |
@@ -5339,10 +5977,12 @@ It is a dynamic type error if $o$ is not of type \code{bool} or of type \code{Fu |
%\rationale{therefore, we opt for the second option. Alternately, one could insist that assert be a reserved word, which may have an undesirable effect with respect to compatibility of Javascript code ported to Dart.} |
\section{Libraries and Scripts} |
-\label{librariesAndScripts} |
+\LMLabel{librariesAndScripts} |
+\LMHash{} |
A Dart program consists of one or more libraries, and may be built out of one or more {\em compilation units}. A compilation unit may be a library or a part (\ref{parts}). |
+\LMHash{} |
A library consists of (a possibly empty) set of imports, a set of exports, and a set of top-level declarations. A top-level declaration is either a class (\ref{classes}), a type alias declaration (\ref{typedef}), a function (\ref{functions}) or a variable declaration (\ref{variables}). The members of a library $L$ are those top level declarations given within $L$. |
\begin{grammar} |
@@ -5393,12 +6033,14 @@ A library consists of (a possibly empty) set of imports, a set of exports, and |
libraryExport |
\end{grammar} |
+\LMHash{} |
Libraries may be {\em explicitly named} or {\em implicitly named}. An explicitly named library begins with the word \LIBRARY{} (possibly prefaced with any applicable metadata annotations), followed by a qualified identifier that gives the name of the library. |
\commentary{ |
Technically, each dot and identifier is a separate token and so spaces between them are acceptable. However, the actual library name is the concatenation of the simple identifiers and dots and contains no spaces. |
} |
+\LMHash{} |
An implicitly named library has the empty string as its name. |
\rationale{ |
@@ -5409,19 +6051,23 @@ The name of a library is used to tie it to separately compiled parts of the libr |
Libraries intended for widespread use should avoid name collisions. Dart's \code{pub} package management system provides a mechanism for doing so. Each pub package is guaranteed a unique name, effectively enforcing a global namespace. |
} |
+\LMHash{} |
A library may optionally begin with a {\em script tag}. Script tags are intended for use with scripts (\ref{scripts}). A script tag can be used to identify the interpreter of the script to whatever computing environment the script is embedded in. The script tag must appear before any whitespace or comments. A script tag begins with the characters \#! and ends at the end of the line. Any characters that follow \#! in the script tag are ignored by the Dart implementation. |
+\LMHash{} |
Libraries are units of privacy. A private declaration declared within a library $L$ can only be accessed by code within $L$. Any attempt to access a private member declaration from outside $L$ will cause a method, getter or setter lookup failure. |
\commentary{Since top level privates are not imported, using the top level privates of another library is never possible. } |
+\LMHash{} |
The {\em public namespace} of library $L$ is the mapping that maps the simple name of each public top-level member $m$ of $L$ to $m$. |
The scope of a library $L$ consists of the names introduced by all top-level declarations declared in $L$, and the names added by $L$'s imports (\ref{imports}). |
\subsection{Imports} |
-\label{imports} |
+\LMLabel{imports} |
+\LMHash{} |
An {\em import} specifies a library to be used in the scope of another library. |
\begin{grammar} |
{\bf libraryImport:} |
@@ -5442,12 +6088,16 @@ An {\em import} specifies a library to be used in the scope of another library. |
\end{grammar} |
+\LMHash{} |
An import specifies a URI $x$ where the declaration of an imported library is to be found. |
+\LMHash{} |
Imports may be {\em deferred} or {\em immediate}. A deferred import is distinguished by the appearance of the built-in identifier \DEFERRED{} after the URI. Any import that is not deferred is immediate. |
+\LMHash{} |
It is a compile-time error if the specified URI of an immediate import does not refer to a library declaration. The interpretation of URIs is described in section \ref{uris} below. |
+\LMHash{} |
It is a static warning if the specified URI of a deferred import does not refer to a library declaration. |
\rationale{ |
@@ -5455,27 +6105,34 @@ It is a static warning if the specified URI of a deferred import does not refer |
} |
+\LMHash{} |
The {\em current library} is the library currently being compiled. The import modifies the namespace of the current library in a manner that is determined by the imported library and by the optional elements of the import. |
+\LMHash{} |
An immediate import directive $I$ may optionally include a prefix clause of the form \AS{} \code{Id} used to prefix names imported by $I$. A deferred import must include a prefix clause or a compile time error occurs. It is a compile-time error if a prefix used in a deferred import is used in another import clause. |
+\LMHash{} |
An import directive $I$ may optionally include a namespace combinator clauses used to restrict the set of names imported by $I$. Currently, two namespace combinators are supported: \HIDE{} and \SHOW{}. |
+\LMHash{} |
Let $I$ be an import directive that refers to a URI via the string $s_1$. Evaluation of $I$ proceeds as follows: |
+\LMHash{} |
If $I$ is a deferred import, no evaluation takes place. Instead, an mapping the name of the prefix, $p$ to a {\em deferred prefix object} is added to the scope of $L$. |
The deferred prefix object has the following methods: |
\begin{itemize} |
-\item \code{loadLibrary}. This method returns a future $f$. When called, the method causes an immediate import $IÕ$ to be executed at some future time, where $IÕ$ is is derived from $I$ by eliding the word \DEFERRED{} and adding a \HIDE{} \code{loadLibrary} combinator clause. When $IÕ$ executes without error, $f$ completes successfully. If $IÕ$ executes without error, we say that the call to \code{loadLibrary} has succeeded, otherwise we say the call has failed. |
+\item \code{loadLibrary}. This method returns a future $f$. When called, the method causes an immediate import $I'$ to be executed at some future time, where $I'$ is is derived from $I$ by eliding the word \DEFERRED{} and adding a \HIDE{} \code{loadLibrary} combinator clause. When $I'$ executes without error, $f$ completes successfully. If $I'$ executes without error, we say that the call to \code{loadLibrary} has succeeded, otherwise we say the call has failed. |
\item For every top level function $f$ named $id$ in $L$, a corresponding method named $id$ with the same signature as $f$. Calling the method results in a runtime error. |
\item For every top level getter $g$ named $id$ in $L$, a corresponding getter named $id$ with the same signature as $g$. Calling the method results in a runtime error. |
\item For every top level setter $s$ named $id$ in $L$, a corresponding setter named $id$ with the same signature as $s$. Calling the method results in a runtime error. |
\item For every type $T$ named $id$ in $L$, a corresponding getter named $id$ with return type \code{Type}. Calling the method results in a runtime error. |
\end{itemize} |
+\LMHash{} |
After a call succeeds, the name $p$ is mapped to a non-deferred prefix object as described below. In addition, the prefix object also supports the \code{loadLibrary} method, and so it is possible to call \code{loadLibrary} again. If a call fails, nothing happens, and one again has the option to call \code{loadLibrary} again. Whether a repeated call to \code{loadLibrary} succeeds will vary as described below. |
+\LMHash{} |
The effect of a repeated call to \code{$p$.loadLibrary} is as follows: |
\begin{itemize} |
\item |
@@ -5497,6 +6154,7 @@ In other words, one can retry a deferred load after a network failure or because |
We do not specify what value the future returned resolves to. |
} |
+\LMHash{} |
If $I$ is an immediate import then, first |
\begin{itemize} |
@@ -5507,6 +6165,7 @@ If the URI that is the value of $s_1$ has not yet been accessed by an import or |
\end{itemize} |
+\LMHash{} |
Let $NS_0$ be the exported namespace (\ref{exports}) of $B$. Then, for each combinator clause $C_i, i \in 1..n$ in $I$: |
\begin{itemize} |
\item If $C_i$ is of the form |
@@ -5526,6 +6185,7 @@ then let $NS_i = \HIDE{}([id_1, \ldots, id_k], NS_{i-1}$) |
where $hide(l, n)$ takes a list of identfiers $l$ and a namespace $n$, and produces a namespace that is identical to $n$ except that for each name $k$ in $l$, $k$ and $k=$ are undefined. |
\end{itemize} |
+\LMHash{} |
Next, if $I$ includes a prefix clause of the form \AS{} $p$, let $NS = NS_n \cup \{p: prefixObject(NS_n)\}$ where $prefixObject(NS_n)$ is a {\em prefix object} for the namespace $NS_n$, which is an object that has the following members: |
\begin{itemize} |
@@ -5535,12 +6195,14 @@ Next, if $I$ includes a prefix clause of the form \AS{} $p$, let $NS = NS_n \cu |
\item For every type $T$ named $id$ in $NS_n$, a corresponding getter named $id$ with return type \code{Type}, that, when invoked, returns the type object for $T$. |
\end{itemize} |
+\LMHash{} |
Otherwise, let $NS = NS_n$. |
It is a compile-time error if the current library declares a top-level member named $p$. |
% This is problematic, because it implies that p.T would be available even in a scope that declared p. We really need to think of p as a single object with properties p.T etc., except it isn't really that |
% either. After all, p isn't actually available as a stand alone name. |
+\LMHash{} |
Then, for each entry mapping key $k$ to declaration $d$ in $NS$, $d$ is made available in the top level scope of $L$ under the name $k$ unless either: |
\begin{itemize} |
\item |
@@ -5550,6 +6212,7 @@ a top-level declaration with the name $k$ exists in $L$, OR |
\rationale{The greatly increases the chance that a member can be added to a library without breaking its importers.} |
+\LMHash{} |
A {\em system library} is a library that is part of the Dart implementation. Any other library is a {\em non-system library}. If a name $N$ is referenced by a library $L$ and $N$ would be introduced into the top level scope of $L$ by |
imports of two libraries, $L_1$ and $L_2$, and the exported namespace of $L_1$ binds $N$ to a declaration originating in a system library: |
@@ -5560,11 +6223,12 @@ imports of two libraries, $L_1$ and $L_2$, and the exported namespace of $L_1$ b |
\end{itemize} |
\rationale { |
-Whereas normal conflicts are resolved at deployment time, the functionality of \code{dart:} libraries is injected into an application at run time, and may vary over time as browsers are upgraded. Thus, conflicts with \code{dart:} libraries can arise at runtime, outside the developerÕs control. To avoid breaking deployed applications in this way, conflicts with the \code{dart:} libraries are treated specially. |
+Whereas normal conflicts are resolved at deployment time, the functionality of \code{dart:} libraries is injected into an application at run time, and may vary over time as browsers are upgraded. Thus, conflicts with \code{dart:} libraries can arise at runtime, outside the developer's control. To avoid breaking deployed applications in this way, conflicts with the \code{dart:} libraries are treated specially. |
It is recommended that tools that deploy Dart code produce output in which all imports use show clauses to ensure that additions to the namespace of a library never impact deployed code. |
} |
+\LMHash{} |
If a name $N$ is referenced by a library $L$ and $N$ is introduced into the top level scope of $L$ by more than one import, and not all the imports denote the same declaration, then: |
\begin{itemize} |
\item A static warning occurs. |
@@ -5573,6 +6237,7 @@ If a name $N$ is referenced by a library $L$ and $N$ is introduced into the to |
\end{itemize} |
+\LMHash{} |
We say that the namespace $NS$ {\em has been imported into} $L$. |
\commentary{ |
@@ -5589,6 +6254,7 @@ In contrast a library comprises both imports and their usage; the library is und |
%On a related note, the provenance of the conflicting elements is not considered. An element that is imported via distinct paths may conflict with itself. This avoids variants of the well known "diamond" problem. |
} |
+\LMHash{} |
It is a static warning to import two different libraries with the same name. |
\commentary{ |
@@ -5600,6 +6266,7 @@ A widely disseminated library should be given a name that will not conflict with |
This prevents situations where removing a name from a library would cause breakage of a client library. |
} |
+\LMHash{} |
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';} |
@@ -5615,8 +6282,9 @@ It would be nice if there was nothing special about \code{dart:core}. However, i |
} |
\subsection{Exports} |
-\label{exports} |
+\LMLabel{exports} |
+\LMHash{} |
A library $L$ exports a namespace (\ref{scoping}), meaning that the declarations in the namespace are made available to other libraries if they choose to import $L$ (\ref{imports}). The namespace that $L$ exports is known as its {\em exported namespace}. |
\begin{grammar} |
@@ -5625,14 +6293,19 @@ A library $L$ exports a namespace (\ref{scoping}), meaning that the declarations |
. |
\end{grammar} |
+\LMHash{} |
An export specifies a URI $x$ where the declaration of an exported library is to be found. It is a compile-time error if the specified URI does not refer to a library declaration. |
+\LMHash{} |
We say that a name {\em is exported by a library} (or equivalently, that a library {\em exports a name}) if the name is in the library's exported namespace. We say that a declaration {\em is exported by a library} (or equivalently, that a library {\em exports a declaration}) if the declaration is in the library's exported namespace. |
+\LMHash{} |
A library always exports all names and all declarations in its public namespace. In addition, a library may choose to re-export additional libraries via {\em export directives}, often referred to simply as {\em exports}. |
+\LMHash{} |
Let $E$ be an export directive that refers to a URI via the string $s_1$. Evaluation of $E$ proceeds as follows: |
+\LMHash{} |
First, |
\begin{itemize} |
@@ -5642,6 +6315,7 @@ If the URI that is the value of $s_1$ has not yet been accessed by an import or |
\end{itemize} |
+\LMHash{} |
Let $NS_0$ be the exported namespace of $B$. Then, for each combinator clause $C_i, i \in 1..n$ in $E$: |
\begin{itemize} |
\item If $C_i$ is of the form \code{\SHOW{} $id_1, \ldots, id_k$} then let |
@@ -5652,9 +6326,11 @@ $NS_i = \SHOW{}([id_1, \ldots, id_k], NS_{i-1}$). |
then let $NS_i = \HIDE{}([id_1, \ldots, id_k], NS_{i-1}$). |
\end{itemize} |
+\LMHash{} |
For each |
entry mapping key $k$ to declaration $d$ in $NS_n$ an entry mapping $k$ to $d$ is added to the exported namespace of $L$ unless a top-level declaration with the name $k$ exists in $L$. |
+\LMHash{} |
If a name $N$ is referenced by a library $L$ and $N$ would be introduced into the exported namespace of $L$ by exports of two libraries, $L_1$ and $L_2$, and the exported namespace of $L_1$ binds $N$ to a declaration originating in a system library: |
%an export of a system library and an export of a non-system library: |
\begin{itemize} |
@@ -5666,17 +6342,21 @@ If a name $N$ is referenced by a library $L$ and $N$ would be introduced into th |
See the discussion in section \ref{imports} for the reasoning behind this rule. |
} |
+\LMHash{} |
We say that $L$ {\em re-exports library } $B$, and also that $L$ {\em re-exports namespace } $NS_n$. When no confusion can arise, we may simply state that $L$ {\em re-exports }$B$, or that $L$ {\em re-exports }$NS_n$. |
+\LMHash{} |
It is a compile-time error if a name $N$ is re-exported by a library $L$ and $N$ is introduced into the export namespace of $L$ by more than one export, unless all exports refer to same declaration for the name $N$. It is a static warning to export two different libraries with the same name. |
\subsection{Parts} |
-\label{parts} |
+\LMLabel{parts} |
+\LMHash{} |
A library may be divided into {\em parts}, each of which can be stored in a separate location. A library identifies its parts by listing them via \PART{} directives. |
+\LMHash{} |
A {\em part directive} specifies a URI where a Dart compilation unit that should be incorporated into the current library may be found. |
\begin{grammar} |
@@ -5692,22 +6372,27 @@ A {\em part directive} specifies a URI where a Dart compilation unit that should |
. |
\end{grammar} |
+\LMHash{} |
A {\em part header} begins with \PART{} \OF{} followed by the name of the library the part belongs to. A part declaration consists of a part header followed by a sequence of top-level declarations. |
+\LMHash{} |
Compiling a part directive of the form \code{\PART{} $s$;} causes the Dart system to attempt to compile the contents of 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 a valid part declaration. It is a static warning if the referenced part declaration $p$ names a library other than the current library as the library to which $p$ belongs. |
\subsection{Scripts} |
-\label{scripts} |
+\LMLabel{scripts} |
+\LMHash{} |
A {\em script} is a library whose exported namespace (\ref{exports}) includes a top-level function \code{main}. |
A script $S$ may be executed as follows: |
+\LMHash{} |
First, $S$ is compiled as a library as specified above. Then, the top-level function \code{main} that is in the exported namespace of $S$ is invoked. If \code{main} has no positional parameters, it is invoked with no arguments. Otherwise if \code{main} has exactly one positional parameter, it is invoked with a single actual argument whose runtime type implements \code{List$<$String$>$}. Otherwise \code{main} is invoked with the following two actual arguments: |
\begin{enumerate} |
\item An object whose runtime type implements \code{List$<$String$>$}. |
\item The initial message of the current isolate $i$ as determined by the invocation of \code{Isolate.spawnUri} that spawned $i$. |
\end{enumerate} |
+\LMHash{} |
It is a run time error if $S$ does not declare or import a top-level function \code{main}. It is a static warning if \code{main} has more than two required parameters. |
\commentary { |
@@ -5723,8 +6408,9 @@ A Dart program will typically be executed by executing a script. |
} |
\subsection{URIs} |
-\label{uris} |
+\LMLabel{uris} |
+\LMHash{} |
URIs are specified by means of string literals: |
\begin{grammar} |
@@ -5733,16 +6419,20 @@ URIs are specified by means of string literals: |
. |
\end{grammar} |
+\LMHash{} |
It is a compile-time error if the string literal $x$ that describes a URI is not a compile-time constant, or if $x$ involves string interpolation. |
+\LMHash{} |
This specification does not discuss the interpretation of URIs, with the following exceptions. |
\rationale{ |
The interpretation of URIs is mostly left to the surrounding computing environment. For example, if Dart is running in a web browser, that browser will likely interpret some URIs. While it might seem attractive to specify, say, that URIs are interpreted with respect to a standard such as IETF RFC 3986, in practice this will usually depend on the browser and cannot be relied upon. |
} |
+\LMHash{} |
A URI of the form \code{dart:$s$} is interpreted as a reference to a system library (\ref{imports}) $s$. |
+\LMHash{} |
A URI of the form \code{package:$s$} is interpreted as a URI of the form \code{packages/s} relative to an implementation specified location. |
\commentary{ |
@@ -5753,22 +6443,25 @@ This location will often be the location of the root library presented to the Da |
The intent is that, during development, Dart programmers can rely on a package manager to find elements of their program. Such package managers may provide a directory structure starting at a local directory \code{packages} where they place the required dart code (or links thereto). |
} |
+\LMHash{} |
Otherwise, any relative URI is interpreted as relative to the the location of the current library. All further interpretation of URIs is implementation dependent. |
\commentary{This means it is dependent on the embedder.} |
\section{Types} |
-\label{types} |
+\LMLabel{types} |
+\LMHash{} |
Dart supports optional typing based on interface types. |
\rationale{The type system is unsound, due to the covariance of generic types. This is a deliberate choice (and undoubtedly controversial). Experience has shown that sound type rules for generics fly in the face of programmer intuition. It is easy for tools to provide a sound type analysis if they choose, which may be useful for tasks like refactoring. |
} |
\subsection{Static Types} |
-\label{staticTypes} |
+\LMLabel{staticTypes} |
+\LMHash{} |
Static type annotations are used in variable declarations (\ref{variables}) (including formal parameters (\ref{formalParameters})), in the return types of functions (\ref{functions}) and in the bounds of type variables. Static type annotations are used during static checking and when running programs in checked mode. They have no effect whatsoever in production mode. |
\begin{grammar} |
@@ -5789,6 +6482,7 @@ Static type annotations are used in variable declarations (\ref{variables}) (inc |
. |
\end{grammar} |
+\LMHash{} |
A Dart implementation must provide a static checker that detects and reports exactly those situations this specification identifies as static warnings and only those situations. However: |
\begin{itemize} |
\item Running the static checker on a program $P$ is not required for compiling and running $P$. |
@@ -5800,6 +6494,7 @@ A Dart implementation must provide a static checker that detects and reports exa |
%\Q{Should we do something with respect to non-nullable types?} |
+\LMHash{} |
A type $T$ is {\em malformed} iff: |
\begin{itemize} |
\item $T$ has the form $id$ or the form $prefix.id$, and in the enclosing lexical scope, the name $id$ (respectively $prefix.id$) does not denote a type. |
@@ -5812,29 +6507,35 @@ A type $T$ is {\em malformed} iff: |
% \end{itemize} |
\end{itemize} |
+\LMHash{} |
Any use of a malformed type gives rise to a static warning. A malformed type is then interpreted as \DYNAMIC{} by the static type checker and the runtime unless explicitly specified otherwise. |
\rationale{ |
This ensures that the developer is spared a series of cascading warnings as the malformed type interacts with other types. |
} |
+\LMHash{} |
A type $T$ is {\em deferred} iff it is of the form $p.T$ where $p$ is a deferred prefix. |
It is a static warning to use a deferred type in a type annotation, type test, type cast or as a type parameter. However, all other static warnings must be issued under the assumption that all deferred libraries have successfully been loaded. |
\subsubsection{Type Promotion} |
-\label{typePromotion} |
+\LMLabel{typePromotion} |
+\LMHash{} |
The static type system ascribes a static type to every expression. In some cases, the types of local variables and formal parameters may be promoted from their declared types 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} |
-\label{dynamicTypeSystem} |
+\LMLabel{dynamicTypeSystem} |
+\LMHash{} |
A Dart implementation must support execution in both {\em production mode} and {\em checked mode}. Those dynamic checks specified as occurring specifically in checked mode must be performed iff the code is executed in checked mode. |
\commentary{ |
@@ -5848,6 +6549,7 @@ Clearly, if a deferred type has not yet been loaded, it is impossible to do a co |
%It is a run-time type error to access an undeclared type outside . |
+\LMHash{} |
%It is a dynamic type error if a malformed type is used in a subtype test. |
In checked mode, it is a dynamic type error if a deferred, malformed or malbounded (\ref{parameterizedTypes}) |
type is used in a subtype test. |
@@ -5929,11 +6631,12 @@ bool b = x is I; |
\subsection{Type Declarations} |
-\label{typeDeclarations} |
+\LMLabel{typeDeclarations} |
\subsubsection{Typedef} |
-\label{typedef} |
+\LMLabel{typedef} |
+\LMHash{} |
A {\em type alias} declares a name for a type expression. |
@@ -5957,8 +6660,10 @@ A {\em type alias} declares a name for a type expression. |
\end{grammar} |
+\LMHash{} |
The effect of a type alias of the form \code{\TYPEDEF{} $T$ $id (T_1$ $p_1, \ldots, T_n$ $p_n, [T_{n+1}$ $p_{n+1}, \ldots, T_{n+k}$ $p_{n+k}])$} declared in a library $L$ is is to introduce the name $id$ into the scope of $L$, bound to the function type $(T_1, \ldots, T_n, [T_{n+1}$ $p_{n+1}, \ldots, T_{n+k}$ $p_{n+k}]) \rightarrow T$. The effect of a type alias of the form \code{\TYPEDEF{} $T$ $id (T_1$ $p_1, \ldots, T_n$ $p_n, \{T_{n+1}$ $p_{n+1}, \ldots, T_{n+k}$ $p_{n+k}\})$} declared in a library $L$ is is to introduce the name $id$ into the scope of $L$, bound to the function type $(T_1, \ldots, T_n, \{T_{n+1}$ $p_{n+1}, \ldots, T_{n+k}$ $p_{n+k}\}) \rightarrow T$. . In either case, iff no return type is specified, it is taken to be \DYNAMIC{}. Likewise, if a type annotation is omitted on a formal parameter, it is taken to be \DYNAMIC{}. |
+\LMHash{} |
It is a compile-time error if any default values are specified in the signature of a function type alias. |
%A typedef may only refer to itself via the bounds of its generic parameters. |
Any self reference in a typedef, either directly, or recursively via another typedef, is a compile time error. |
@@ -5967,8 +6672,9 @@ Any self reference in a typedef, either directly, or recursively via another ty |
\subsection{Interface Types} |
-\label{interfaceTypes} |
+\LMLabel{interfaceTypes} |
+\LMHash{} |
The implicit interface of class $I$ is a direct supertype of the implicit interface of class $J$ iff: |
\begin{itemize} |
\item |
@@ -5991,6 +6697,7 @@ If $I$ is listed in the \EXTENDS{} clause of $J$% or an interface injection decl |
%\Q{Can wacky stuff happen with interface injection, e.g., a direct superinterface becomes indirect? What about side effects - loading order can affect type relationships. |
%} |
+\LMHash{} |
A type $T$ is more specific than a type $S$, written $T << S$, if one of the following conditions is met: |
\begin{itemize} |
\item $T$ is $S$. |
@@ -6006,6 +6713,7 @@ $T_i << S_i, 1 \le i \le n$ |
\item $T << U$ and $U << S$. |
\end{itemize} |
+\LMHash{} |
$<<$ is a partial order on types. |
$T$ is a subtype of $S$, written $T <: S$, iff $[\bot/\DYNAMIC{}]T << S$. |
@@ -6015,10 +6723,12 @@ $List <: List<String>$ and $List<int> <: List$, but $List<int>$ is not a subtype |
Although $<:$ is not a partial order on types, it does contain a partial order, namely $<<$. This means that, barring raw types, intuition about classical subtype rules does apply. |
} |
+\LMHash{} |
$S$ is a supertype of $T$, written $S :> T$, iff $T$ is a subtype of $S$. |
\commentary{The supertypes of an interface are its direct supertypes and their supertypes. } |
+\LMHash{} |
An interface type $T$ may be assigned to a type $S$, written $T \Longleftrightarrow S$, iff either $T <: S$ or $S <: T$. |
\rationale{This rule may surprise readers accustomed to conventional typechecking. The intent of the $\Longleftrightarrow$ relation is not to ensure that an assignment is correct. Instead, it aims to only flag assignments that are almost certain to be erroneous, without precluding assignments that may work. |
@@ -6027,8 +6737,9 @@ For example, assigning a value of static type Object to a variable with static t |
} |
\subsection{Function Types} |
-\label{functionTypes} |
+\LMLabel{functionTypes} |
+\LMHash{} |
Function types come in two variants: |
\begin{enumerate} |
\item |
@@ -6047,6 +6758,7 @@ The types of functions with named parameters. These have the general form $(T_1 |
%\item$ \forall i \in 1 .. n, T_i \Longleftrightarrow S_i$. |
%\end{enumerate} |
+\LMHash{} |
%A function type $(T_1, \ldots T_n, [T_{n+1} \ldots, T_{n+k}]) \rightarrow T$ is a subtype of the |
% the line below revises the rule to be more liberal |
A function type $(T_1, \ldots T_{k}, [T_{k+1} \ldots, T_{n+m}]) \rightarrow T$ is a subtype of the |
@@ -6061,6 +6773,7 @@ function type $(S_1, \ldots, S_{k+j}, [S_{k+j+1} \ldots, S_{n}]) \rightarrow S$, |
\end{enumerate} |
+\LMHash{} |
A function type $(T_1, \ldots T_n, \{T_{x_1}$ $x_1, \ldots, T_{x_k}$ $x_k\}) \rightarrow T$ is a subtype of the function type $(S_1, \ldots, S_n, \{S_{y_1}$ $y_1, \ldots, S_{y_m}$ $y_m\}) \rightarrow S$, if all of the following conditions are met: |
\begin{enumerate} |
\item Either |
@@ -6083,6 +6796,7 @@ A function type $(T_1, \ldots T_n, \{T_{x_1}$ $x_1, \ldots, T_{x_k}$ $x_k\}) \ri |
%The rules above need to be sanity checked, but the intent is that we view functions with rest parameters as having type $(T_1, ..., T_n, [\_{Tn+1}[] \_]) \rightarrow T$, where \_ is some magical identifier. Then the rules above may cover everything. |
% This is wrong - from the outside, the type takes an unbounded sequence of types, not a list. This can be modeled as $(T_1, \ldots, T_n, [T_{n+1}, \_ \ldots, T_{n+k} \_]) \rightarrow T$ for some finite $k$. |
+\LMHash{} |
In addition, the following subtype rules apply: |
@@ -6098,8 +6812,10 @@ $(T_1, \ldots, T_n) \rightarrow T <: (T_1, \ldots, T_n, []) \rightarrow T$. |
The naive reader might conclude that, since it is not legal to declare a function with an empty optional parameter list, these rules are pointless. However, they induce useful relationships between function types that declare no optional parameters and those that do. |
} |
+\LMHash{} |
A function type $T$ may be assigned to a function type $S$, written $T \Longleftrightarrow S$, iff $T <: S$. |
+\LMHash{} |
% ensure that Object and dynamic may be assign dot a function type |
A function is always an instance of some class that implements the class \code{Function} and implements a \CALL{} method with the same signature as the function. All function types are subtypes of \code{Function}. |
If a type $I$ includes an instance method named \CALL{}, and the type of \CALL{} is the function type $F$, then $I$ is considered to be more specific than $F$. It is a static warning if a concrete class implements \cd{Function} and does not have a concrete method named \CALL{} unless that class declares its own implementation of \cd{noSuchMethod()}. |
@@ -6109,6 +6825,7 @@ If a type $I$ includes an instance method named \CALL{}, and the type of \CALL{} |
%\commentary{Need to specify how a function values dynamic type is derived from its static signature.} |
+\LMHash{} |
A function type $(T_1, \ldots T_{k}, [T_{k+1} \ldots, T_{n+m}]) \rightarrow T$ is a more specific than the |
function type $(S_1, \ldots, S_{k+j}, [S_{k+j+1} \ldots, S_{n}]) \rightarrow S$, if all of the following conditions are met: |
\begin{enumerate} |
@@ -6121,6 +6838,7 @@ function type $(S_1, \ldots, S_{k+j}, [S_{k+j+1} \ldots, S_{n}]) \rightarrow S$, |
\end{enumerate} |
+\LMHash{} |
A function type $(T_1, \ldots T_n, \{T_{x_1}$ $x_1, \ldots, T_{x_k}$ $x_k\}) \rightarrow T$ is more specific than the function type $(S_1, \ldots, S_n, \{S_{y_1}$ $y_1, \ldots, S_{y_m}$ $y_m\}) \rightarrow S$, if all of the following conditions are met: |
\begin{enumerate} |
\item Either |
@@ -6134,19 +6852,23 @@ A function type $(T_1, \ldots T_n, \{T_{x_1}$ $x_1, \ldots, T_{x_k}$ $x_k\}) \ri |
\item For all $y_i \in \{y_1, \ldots, y_m\}, y_i = x_j \Rightarrow T_j << S_i$ |
\end{enumerate} |
+\LMHash{} |
Furthermore, if $F$ is a function type, $F << \code{Function}$. |
\subsection{Type \DYNAMIC{}} |
-\label{typeDynamic} |
+\LMLabel{typeDynamic} |
+\LMHash{} |
The type \DYNAMIC{} denotes the unknown type. |
+\LMHash{} |
If no static type annotation has been provided the type system assumes the declaration has the unknown type. If a generic type is used but type arguments are not provided, then the type arguments default to the unknown type. |
\commentary{This means that given a generic declaration $G<T_1, \ldots, T_n>$, the type $G$ is equivalent to $G< \DYNAMIC{}, \ldots, \DYNAMIC{}>$. |
} |
+\LMHash{} |
Type \DYNAMIC{} has methods for every possible identifier and arity, with every possible combination of named parameters. These methods all have \DYNAMIC{} as their return type, and their formal parameters all have type \DYNAMIC{}. |
Type \DYNAMIC{} has properties for every possible identifier. These properties all have type \DYNAMIC{}. |
@@ -6169,6 +6891,7 @@ This forces users to write type information in their client code just because th |
What of static checking? Surely we would want to flag (2) when users have explicitly asked for static typechecking? Yes, but the reality is that the Dart static checker is likely to be running in the background by default. Engineering teams typically desire a ``clean build'' free of warnings and so the checker is designed to be extremely charitable. Other tools can interpret the type information more aggressively and warn about violations of conventional (and sound) static type discipline. |
} |
+\LMHash{} |
The name \DYNAMIC{} denotes a \cd{Type} object even though \DYNAMIC{} is not a class. |
%\rationale { |
@@ -6176,8 +6899,9 @@ The name \DYNAMIC{} denotes a \cd{Type} object even though \DYNAMIC{} is not a c |
%} |
\subsection{Type Void} |
-\label{typeVoid} |
+\LMLabel{typeVoid} |
+\LMHash{} |
The special type \VOID{} may only be used as the return type of a function: it is a compile-time error to use \VOID{} in any other context. |
\commentary{ |
@@ -6212,15 +6936,18 @@ Type objects reify the runtime types of instances. No instance ever has type \VO |
\subsection{Parameterized Types} |
-\label{parameterizedTypes} |
+\LMLabel{parameterizedTypes} |
+\LMHash{} |
A {\em parameterized type} is an invocation of a generic type declaration. |
+\LMHash{} |
Let $T$ be a parameterized type $G<S_1, \ldots, S_n>$. If $G$ is not a generic type, the type arguments $S_i$, $1 \le i \le n$ are discarded. If $G$ has $m \ne n$ type parameters, $T$ is treated as as a parameterized type with $m$ arguments, all of which are \DYNAMIC{}. |
\commentary{In short, any arity mismatch results in all type arguments being dropped, and replaced with the correct number of type arguments, all set to \DYNAMIC{}. Of course, a static warning will be issued. |
} |
+\LMHash{} |
Otherwise, let |
$T_i$ be the type parameters of $G$ and let $B_i$ be the bound of $T_i, i \in 1.. n$,. $T$ is {\em malbounded} iff either $S_i$ is malbounded or $S_i$ is not a subtype of $[S_1, \ldots, S_n/T_1, \ldots, T_n]B_i, i \in 1.. n$. |
@@ -6228,8 +6955,10 @@ Otherwise, let |
Note, that, in checked mode, it is a dynamic type error if a malbounded type is used in a type test as specified in \ref{dynamicTypeSystem}. |
} |
+\LMHash{} |
Any use of a malbounded type gives rise to a static warning. |
+\LMHash{} |
If $S$ is the static type of a member $m$ of $G$, then the static type of the member $m$ of $G<A_1, \ldots, A_n>$ is $[A_1, \ldots, A_n/T_1, \ldots, T_n]S$ where $T_1, \ldots, T_n$ are the formal type parameters of $G$. Let $B_i$, be the bounds of $T_i, 1 \le i \le n$. It is a static type warning if $A_i$ is not a subtype of $[A_1, \ldots, A_n/T_1, \ldots, T_n]B_i, i \in 1..n$. It is a static type warning if $G$ is not a generic type with exactly $n$ type parameters. |
@@ -6237,14 +6966,16 @@ If $S$ is the static type of a member $m$ of $G$, then the static type of the me |
\subsubsection{Actual Type of Declaration} |
-\label{actualTypeOfADeclaration} |
+\LMLabel{actualTypeOfADeclaration} |
+\LMHash{} |
A type $T$ {\em depends on a type parameter} $U$ iff: |
\begin{itemize} |
\item $T$ is $U$. |
\item $T$ is a parameterized type, and one of the type arguments of $T$ depends on $U$. |
\end{itemize} |
+\LMHash{} |
Let $T$ be the declared type of a declaration $d$, as it appears in the program source. The {\em actual type} of $d$ is |
\begin{itemize} |
@@ -6253,20 +6984,24 @@ Let $T$ be the declared type of a declaration $d$, as it appears in the program |
\end{itemize} |
\subsubsection{Least Upper Bounds} |
-\label{leastUpperBounds} |
+\LMLabel{leastUpperBounds} |
+\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$ %, and $k=max(depth(T_1), \ldots, depth(T_m)), T_i \in S, i \in 1..m$, |
where $depth(T)$ is the number of steps in the longest inheritance path from $T$ to \code{Object}. Let $q$ be the largest number such that $S_q$ has cardinality one. The least upper bound of $I$ and $J$ is the sole element of $S_q$. |
+\LMHash{} |
The least upper bound of \DYNAMIC{} and any type $T$ is \DYNAMIC{}. |
The least upper bound of \VOID{} and any type $T \ne \DYNAMIC{}$ is \VOID{}. |
Let $U$ be a type variable with upper bound $B$. The least upper bound of $U$ and a type $T$ is the least upper bound of $B$ and $T$. |
+\LMHash{} |
The least upper bound relation is symmetric and reflexive. |
% Function types |
+\LMHash{} |
The least upper bound of a function type and an interface type $T$ is the least upper bound of \cd{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 \cd{Function}. Otherwise: |
\begin{itemize} |
@@ -6309,16 +7044,18 @@ where $L_i$ is the least upper bound of $T_i$ and $S_i, i \in 0..r$ |
\section{Reference} |
-\label{reference} |
+\LMLabel{reference} |
\subsection{Lexical Rules} |
-\label{lexicalRules} |
+\LMLabel{lexicalRules} |
+\LMHash{} |
Dart source text is represented as a sequence of Unicode code points. This sequence is first converted into a sequence of tokens according to the lexical rules given in this specification. At any point in the tokenization process, the longest possible token is recognized. |
\subsubsection{Reserved Words} |
-\label{reservedWords} |
+\LMLabel{reservedWords} |
+\LMHash{} |
A {\em reserved word} may not be used as an identifier; it is a compile-time error if a reserved word is used where an identifier is expected. |
\ASSERT{}, \BREAK{}, \CASE{}, \CATCH{}, \CLASS{}, \CONST{}, \CONTINUE{}, \DEFAULT{}, \DO{}, \ELSE{}, \ENUM{}, \EXTENDS{}, \FALSE{}, \FINAL{}, \FINALLY{}, \FOR{}, \IF{}, \IN{}, \IS{}, \NEW{}, \NULL{}, \RETHROW, \RETURN{}, \SUPER{}, \SWITCH{}, \THIS{}, \THROW{}, \TRUE{}, \TRY{}, \VAR{}, \VOID{}, \WHILE{}, \WITH{}. |
@@ -6342,8 +7079,9 @@ A {\em reserved word} may not be used as an identifier; it is a compile-time err |
\end{grammar} |
\subsubsection{Comments} |
-\label{comments} |
+\LMLabel{comments} |
+\LMHash{} |
{\em Comments} are sections of program text that are used for documentation. |
\begin{grammar}{\bf SINGLE\_LINE\_COMMENT:} |
@@ -6355,16 +7093,22 @@ A {\em reserved word} may not be used as an identifier; it is a compile-time err |
. |
\end{grammar} |
+\LMHash{} |
Dart supports both single-line and multi-line comments. A {\em single line comment} begins with the token \code{//}. Everything between \code{//} and the end of line must be ignored by the Dart compiler unless the comment is a documentation comment. . |
+\LMHash{} |
A {\em multi-line comment} begins with the token \code{/*} and ends with the token \code{*/}. Everything between \code{/}* and \code{*}/ must be ignored by the Dart compiler unless the comment is a documentation comment. Comments may nest. |
+\LMHash{} |
{\em Documentation comments} are comments that begin with the tokens \code{///} or \code{/**}. Documentation comments are intended to be processed by a tool that produces human readable documentation. |
+\LMHash{} |
The scope of a documentation comment always excludes the imported namespace of the enclosing library. Only names declared in the enclosing library are considered in scope within a documentation comment. |
+\LMHash{} |
The scope of a documentation comment immediately preceding the declaration of a class $C$ is the instance scope of $C$, excluding any names introduced via the import namespace of the enclosing library. |
+\LMHash{} |
The scope of a documentation comment immediately preceding the declaration of a function $f$ is the scope in force at the very beginning of the body of $f$, excluding any names introduced via the import namespace of the enclosing library. |
@@ -6373,8 +7117,9 @@ The scope of a documentation comment immediately preceding the declaration of a |
%\subsection{Grammar} |
\subsection{Operator Precedence} |
-\label{operatorPrecedence} |
+\LMLabel{operatorPrecedence} |
+\LMHash{} |
Operator precedence is given implicitly by the grammar. |
\commentary{The following non-normative table may be helpful |
@@ -6435,3 +7180,156 @@ The following naming conventions are customary in Dart programs. |
\end{document} |
+ |
+[Text after \end{document} is ignored, hence we do not need "%"] |
+---------------------------------------------------------------------- |
+ |
+* On Location Markers |
+ |
+This is a description of location markers, giving some information |
+about the underlying motivation and rationale, the actual |
+implementation, and the relevant tool support. |
+ |
+** What is a Location Marker? |
+ |
+In order to support more fine-grained update propagation from this |
+language specification to artifacts that depend on it, location |
+markers have been added. The idea is that each logical unit (section, |
+subsection, etc) and each paragraph containing normative text should |
+be addressable using these markers, such that source code (compilers |
+and other tools, tests, etc.) can contain location markers, and the |
+corresponding location in the spec may be looked up using standard |
+document viewer search features. |
+ |
+An SHA1 hash value of the text is associated with each location |
+marker, such that changes in the text will incur changes in this hash |
+value. Consequently, source code in tools/tests that depend on |
+specific parts of the spec may be flagged for revision by checking |
+whether these hash values have changed: If a given test T depends on |
+a paragraph with hash value V in the spec, and the search for V fails |
+in a new version of the spec, then that paragraph has changed and T |
+should be revisited and possible revised. |
+ |
+As a result, the search for parts of source code and similar artifacts |
+in likely need of updates because of spec changes can be performed |
+mechanically, which should help ensure that the conformance of all |
+artifacts depending on this spec is maintained more easily, and hence |
+more consistently. Note that it makes no difference whether the need |
+for an update has arisen in a very recent version of the spec or it |
+has existed for a long time, because the hash value just remains |
+different as long as the text is different from what it was when the |
+location marker was harvested from the spec. |
+ |
+** LaTeX Commands Supporting Location Markers |
+ |
+Concretely, this is based on the commands \LMHash and \LMLabel. |
+\LMHash{V} is used to add the text V in the margin, intended to mark |
+a paragraph of normative text with the SHA1 hash value of the text, V. |
+\LMLabel{L} has the effect of \label{L}, and moreover it shows the |
+text sec:L in the margin. In order to indicate a dependency on a |
+section or subsection an \LMLabel location marker is used, and in |
+order to indicate a dependency on a specific paragraph, the hash value |
+of that paragraph is used. |
+ |
+In this file, each normative paragraph has had the command \LMHash{} |
+added at the beginning, such that each of these paragraphs can be |
+decorated with their hash value. Similarly, all \section{}s, |
+\subsection{}s, \subsubsection{}s, and \paragraph{}s have had |
+their \label commands changed to \LMLabel, such that they are |
+decorated with logical names. |
+ |
+** Rationale |
+ |
+The design of location markers was proposed by Erik Ernst and |
+developed through discussions with several others, in particular Gilad |
+Bracha and Lars Bak. Some discussions along the way that gave rise to |
+the given design are outlined below. |
+ |
+The basic idea is that a hash value based on the actual text will |
+serve well to identify a piece of text, because it will change |
+whenever the text changes, and it remains the same if the text is |
+moved to a different location; in other words, it characterizes the |
+text itself, independently of the rest of the document. Hence: |
+ |
+ - references to specific paragraphs in the spec are easy to create: |
+ copy the marker and paste it into the source code (but see below |
+ why this uses an extra indirection as far as possible) |
+ |
+ - such references would be robust in the sense that they depend on |
+ the actual text alone, i.e., they would not be invalidated by |
+ updates to section numbers, relocation of the paragraph, or |
+ updates to text in different paragraphs; as Lars mentioned, we |
+ should use a "stripped" version of the text, removing comments, |
+ normalizing white space, etc., which would make the refs even more |
+ robust in case of "inessential" changes |
+ |
+ - artifacts depending on a given part of the spec that was |
+ changed could easily be pointed out: After an update to a |
+ part of the spec, that artifact would hold a marker associated |
+ with a hash value which does not any more occur in the spec, |
+ maintainers of the artifact would then receive a notification |
+ ("test1773 depends on a part of the spec that was updated"). |
+ Nice tool support would show them the paragraph in the most recent |
+ version of the spec as well as the old version that the artifact |
+ used to depend on, and a comparison of the two would help |
+ clarifying what needs fixing because of this change, if anything. |
+ |
+However, there is a conflict in this scenario: Lars pointed out that |
+it is very inconvenient to have to create a lot of revision control |
+commits (e.g., new versions of tests), just because a large number of |
+artifacts depend on a specific hash value that changed, if that change |
+has no real impact on each of those artifacts. The obvious solution |
+to this problem would be to use symbolic names and keep the actual |
+hash values out of the primary artifacts. |
+ |
+This approach has been used for \section{}s, \subsection{}s, etc., by |
+using their labels as location markers. For instance, dependency on |
+\subsubsection{New} would be marked as a dependency on 'sec:new', |
+which will (most likely) exist with the same label in the spec for a |
+long time. To detect a need for updates, the hash value associated |
+with \subsubsection{New} from the date of the latest check of this |
+kind to the dependent artifact should be compared with the current |
+hash value for the same \subsubsection{}: The artifact should be |
+revisited iff those hash values differ. As an easy approximation to |
+this scheme, the hash values for all location markers would be |
+computed for each spec update, and the location markers that have new |
+hash values should cause revisits to all artifacts depending on that |
+location marker. |
+ |
+The symbolic location markers on larger units like \section{} |
+etc. enable location marking in a hierarchical fashion: Dependencies |
+on a \subsubsection{} or on a \section{} can be chosen according to |
+the actual needs with each dependent artifact. In general, fine |
+granularity helps avoiding false positives, where an update somewhere |
+in a large unit will flag too many dependent artifacts for revisits. |
+In contrast, coarse granularity enables other artifacts to declare the |
+actual dependencies when small units would be impractical because the |
+artifact depends on so many of them. But there is a problem at the |
+bottom of this hierarchy, namely with paragraphs. |
+ |
+It would be very inconvenient to have to invent a logical name for |
+every paragraph. Similarly, using a simple paragraph numbering would |
+be unstable (add one new paragraph in the beginning of a section, and |
+all the rest have new numbers, creating a massive flood of false |
+update alerts, or, even worse, corrupting the declared dependencies in |
+artifacts because they point to the wrong paragraphs). |
+ |
+Hence, paragraphs have no other label than their actual hash value. |
+Artifacts that depend on very specific elements in the spec may |
+declare so by using an actual hash value for a given paragraph, and in |
+return they pay in terms of potential updates to the marker when that |
+paragraph changes, even in cases where the actual change makes no |
+difference for that particular artifact. This choice of granularity |
+vs. stability is up to the creator of each artifact. |
+ |
+** Maintenance of this document |
+ |
+The invariant that each normative paragraph is associated with a line |
+containing the text \LMHash{} should be maintained. Extra occurrences |
+of \LMHash{} can be added if needed, e.g., in order to make |
+individual \item{}s in itemized lists addressable. Each \LM.. command |
+must occur on a separate line. \LMHash{} must occur immediately |
+before the associated paragraph, and \LMLabel must occur immediately |
+after the associated \section{}, \subsection{} etc. |
+ |
+---------------------------------------------------------------------- |