\documentclass[makeidx]{article} \usepackage{xspace} \usepackage{epsfig} \usepackage{xcolor} \usepackage{syntax} \usepackage[fleqn]{amsmath} \usepackage{amssymb} \usepackage{semantic} \usepackage{dart} \usepackage{hyperref} \usepackage{lmodern} \usepackage[T1]{fontenc} \usepackage{makeidx} \makeindex \title{Dart Programming Language Specification\\ {6th edition draft}\\ {\large Version 2.15-dev}} \author{} % For information about Location Markers (and in particular the % commands \LMHash and \LMLabel), see the long comment at the % end of this file. % CHANGES % ======= % % Significant changes to the specification. % % 2.15 % - Allow generic instantiation of expressions with a generic function type % (until now, it was an error unless the expression denoted a declaration). % - Allow generic instantiation of the call method of a function object. % - Clarify that TypeLiteral.extensionMethod() is an error, in line with the % error for int.toString(). % - Add support for function closurization for callable objects, in the sense % that o is desugared as o.call when the context type is a function type. % - Clarify the treatment of covariant parameters in the interface of a class % that inherits an implementation where those parameters are not covariant. % - Adjust and clarify simple string interpolation (to allow '$this', which % is already implemented and useful). % - Add several lexical rules about identifiers, clarifying different kinds. % - Clarify the conflicts between extension members and Object instance % members. % - Correct to include metadata. % % 2.14 % - Add constraint on type of parameter which is covariant-by-declaration in % the case where the method is inherited (that case was omitted by mistake). % - Clarify symbol equality and identity. % % 2.12 - 2.13 (there was no 2.11) % - Revert the CL where certain null safety features were removed (to enable % publishing a stable version of the specification). % - Add rule that a top-level pair of declarations with the same basename % is a compile-time error except when it is a getter/setter pair. % - Change grammar to enable non-function type aliases. Correct rule for % invoking F.staticMethod() where F is a type alias. % - Add missing error for cyclic redirecting generative constructor. % % 2.8 - 2.10 % - Change several warnings to compile-time errors, matching the actual % behavior of tools. % - Eliminate error for library name conflicts in imports and exports. % - Clarify the specification of initializing formals. Fix error: Add % missing ? at the end for function typed initializing formal. % - Adjust specification of JS number semantics. % - Revise and clarify the sections Imports and Exports. % - Bug fix: Change to omit the single identifier, then add it % back when is used, as resp. . % - Clarify that a function-type bounded receiver has a .call method. % - Merge the static' and instance' scope of a class, yielding a single % class' scope. % - Add specification of >> in JavaScript compiled code, in appendix. % - Integrate the specification of extension methods into this document. % - Specify identifier references denoting extension members. % - Remove a few null safety features, to enable publishing a stable % version of the specification which is purely about Dart 2.10. % - Reorganize specification of type aliases to be in section Type Aliases' % (this used to be both there, and in 'Generics'). % - Clarify the cyclicity error for type aliases ("F is not allowed to depend % on itself). % - Add the error for a type alias$F$used in an instance creation etc., when %$F$expands to a type variable. % - Correct lexical lookup rules to include implicit extension method % invocation. % % 2.7 % - Rename non-terminals <...Definition> to <...Declaration> (e.g., it is % 'class declaration' everywhere, so  is inconsistent). % - Clarify that and are the % start symbols of the grammar. % - Clarify the notion of being noSuchMethod forwarded': m is indeed % noSuchMethod forwarded if an implementation of m is inherited, but % it does not have the required signature. % - Clarify static checks on yield and yield* to explicitly ensure that % assignability is enforced per element. % - Update whitelist for expressions of type void to include await e, % consistent with decision in SDK issue #33415. % - Re-adjust yield* rules: Per-element type checks are not supported, % the given Iterable/Stream must have a safe element type. % - Clarify that an expression of type X extends T can be invoked when % T is a function type, plus other similar cases. % - Specify actual type arguments passed to a generic function which is invoked % with no type arguments (so it must be a dynamic invocation). % % 2.6 % - Specify static analysis of a "callable object" invocation (where % the callee is an instance of a class that has a call method). % - Specify that string literals cannot use string % interpolation; specify that it is a dynamic error for loadLibrary % to load a different library than the one which was used for % compile-time checks. % - Specify that a constant expression e.length can only invoke an instance % getter (in particular, it cannot execute/tear-off an extension member); % similar changes were made for several operators. % - Specify the type arguments of the fields of an Invocation received by % noSuchMethod, when invoked in response to a failed instance member % invocation. % % 2.4 % - Clarify the section Exports'. % - Update grammar rules for , to support static late final % variables with no initializer; for several top-level declarations, % to correct the existence and placement of ; for % , to simplify the grammar (preserving the % derivable terms); for , to allow top-level final % and const variables with no type, and to allow late final top-level % variables, to allow late on a top-level variable declaration; and % adding to allow required parameters. % - Make lexical identifier lookups use the rules for 'Identifier Reference' % consistently; that is, always look up id as well as id=, and commit % to the kind of declaration found by that lookup. % - Specify the signature of the call method of a function object. % - Add the rule that it is an error for a type variable of a class to occur % in a non-covariant position in a superinterface. % - Correct several grammar rules, including: added (to % avoid semicolon in ), adjusted . % - Revise section on cascades. Now uses compositional grammar, and % specifies static type, compile-time errors, and includes ?... % - Correct the grammar and lexical rules for string literals. % - Change specification of await to be type-safe, avoiding cases like: % FutureOr> ffs = Future.value(s); % Future fs = await ffs; % % 2.3 % - Add requirement that the iterator of a for-in statement must have % type Iterator. % - Clarify which constructors are covered by the section 'Constant % Constructors' and removed confusing redundancy in definiton of % potentially constant expressions. % - Integrate the feature specification of collection literal elements % (aka UI-as-code). % % 2.2 % - Specify whether the values of literal expressions override Object.==. % - Allow Type objects as case expressions and const map keys. % - Introduce set literals. % - Specify that a getter/setter and a method with the same basename is % an error, also in the case where a class obtains both from its % superinterfaces. % - Specify the Dart 2.0 rule that you cannot implement, extend or mix-in % Function. % - Generalize specification of type aliases such that they can denote any % type, not just function types. % - Clarify that 'Constant Constructors' is concerned with non-redirecting % generative constructors only. % % 2.1 % - Remove 64-bit constraint on integer literals compiled to JavaScript numbers. % - Allow integer literals in a double context to evaluate to a double value. % - Specify dynamic error for a failing downcast in redirecting factory % constructor invocation. % - Specify that type arguments passed in a redirecting factory constructor % declaration must be taken into account during static checks. % - Disallow any expression statement starting with {, not just % those that are map literals. % - Define a notion of lookup that is needed for superinvocations, adjust % specification of superinvocations accordingly. % - Specify that it is a dynamic error to initialize a non-static variable % with an object that does not have the declared type (e.g., a failed cast). % - Specify for constructor initializers that target variable must exist and % the initializing expression must have a type which is assignable to its % type. % - Specify for superinitializers that the target constructor must exist and % the argument part must have a matching shape and pass type and value % arguments satisfying the relevant constraints. % - Reword rules about abstract methods and inheritance to use 'class % interface'. % - Specify that it is an error for a concrete class with no non-trivial % \code{noSuchMethod} to not have a concrete declaration for some member % in its interface, or to have one which is not a correct override. % - Use \ref{bindingActualsToFormals} in 3 locations, eliminating 2 extra % copies of nearly the same text. % - Add figure in \ref{bindingActualsToFormals} for improved readability. % - Introduce a notion of lookup which is needed for superinvocations. % - Use new lookup concept to simplify specification of getter, setter, method % lookup. % - Introduce several Case markers in order to improve % readability. % - Reorganize several sections to specify static analysis first and then % dynamic semantics; clarify many details along the way. The sections are: % \ref{variables}, \ref{new}, \ref{const}, \ref{bindingActualsToFormals}, % \ref{unqualifiedInvocation}, \ref{functionExpressionInvocation}, % \ref{superInvocations}, \ref{assignment}, \ref{compoundAssignment}, % \ref{localVariableDeclaration}, and \ref{return}. % - Corrected error involving multiple uses of the same part in the same % program such that it takes exports into account. % - Eliminate all references to checked and production mode, Dart 2 does % not have modes. % - Integrate feature specification on noSuchMethod forwarders. % - Specify that bound satisfaction in generic type alias type parameters % must imply bound satisfaction everywhere in the body. % - Specify that super-bounded generic type alias applications must trigger % a well-boundedness check on all types occurring in the denoted type. % - Corrected corner case of rules for generation of noSuchMethod forwarders. % - Integrate feature specification on parameters that are % covariant-by-declaration. % - Integrate feature specification on parameters that are % covariant-by-class. % - Correct section 'Type of a function', allowing for adjustments needed % for rules related to covariant parameters. % - Specified the dynamic type of function objects in several contexts, such % that the special treatment of covariant parameters can be mentioned. % - Specified what it means for an override relation to be correct, thus % adding the parts that are not captured by a function type subtype check. % - Introduced the notion of member signatures, specified that they are the % kind of entity that a class interface contains. % - Corrected super-boundedness check to take variance into account at the % top level. % % 2.0 % - Don't allow functions as assert test values. % - Start running "async" functions synchronously. % - It is a static warning and dynamic error to assign to a final local. % - Specify what "is equivalent to" means. % - Remove @proxy. % - Don't specify the exact object used for empty positionalArguments and % namedArguments on Invocation. % - Remove the, now unnecessary, handling of invalid overrides of noSuchMethod. % - Add >>> as overridable operator. % - If initializing formal has type annotation, require subtype of field type. % - Constant == operations now also allowed if just one operand is null. % - Make flatten not be recursive. % - Disallow implementing two instantiations of the same generic interface. % - Update "FutureOr" specification for Dart 2.0. % - Require that a top-level "main" declaration is a valid script-entry % function declaration. % - State that the return type of a setter or []= is void when not specified. % - Clarify that "noSuchMethod" must be implemented, not just redeclared % abstractly, to eliminate certain diagnostic messages. % - Add generic functions and methods to the language. % - Don't cause warning if a non-system library import shadows a system library. % - Update mixin application forwarding constructors to correctly handle % optional parameters and const constructors. % - Specify call for Dart 2 (no function type given to enclosing class). % - Clarify that an identifier reference denoting a top-level, static, or % local function evaluates to the closurization of that declaration. % - Make mixin and interface built-in identifiers. % - Make async *not* a reserved word inside async functions. % - Add 'Class Member Conflicts', simplifying and adjusting rules about % member declaration conflicts beyond "n declared twice in one scope". % - Specify that integer literals are limited to signed 64-bit values, % and that the int class is intended as signed 64-bit integer, but % that platforms may differ. % - Specify variance and super-bounded types. % - Introduce subterm' and immediate subterm'. % - Introduce top type'. % - Specify configurable imports. % - Specify the dynamic type of the Iterable/Future/Stream returned from % invocations of functions marked sync*/async/async*. % - Add appendix listing the major differences between 64-bit integers % and JavaScript integers. % - Remove appendix on naming conventions. % - Make it explicit that "dynamic" is exported from dart:core. % - Remove "boolean conversion". It's just an error to not be a bool. % - Adjust cyclic subtype prevention rule for type variables. % - Clarify that it is an error to use FutureOr as a superinterface etc. % - Eliminate the notion of static warnings, all program faults are now errors. % - It is no longer an error for a getter to have return type void. % - Specify that each redirection of a constructor is checked, statically and % dynamically. % - Specify that it is an error for a superinitializer to occur anywhere else % than at the end of an initializer list. % - Update the potentially/compile-time constant expression definitions % so that "potentially constant" depends only on the grammar, not the types % of sub-expressions. % - Make == recognize null and make && and || short-circuit in constant % expressions. % - Add as and is expressions as constant expressions % - Make ^, | and & operations on bool constant operations. % - Integrate subtyping.md. This introduces the Dart 2 rules for subtyping, % which in particular means that the notion of being a more specific type % is eliminated, and function types are made contravariant in their % parameter types. % - Integrate instantiation to bound. This introduces the notions of raw % types, the raw-depends relation, and simple bounds; and it specifies % the algorithm which is used to expand a raw type (e.g., C) to a % parameterized type (e.g., C). % - Integrate invalid_returns.md. This replaces the rules about when it is % an error to have return; or return e; in a function. % - Integrate generalized-void.md. Introduces syntactic support for using % void in many new locations, including variable type annotations and % actual type arguments; also adds errors for using values of type void. % - Integrate implicit_creation.md, specifying how some constant expressions % can be written without const, and all occurrences of new can be % omitted. % % 1.15 % - Change how language specification describes control flow. % - Object initialization now specifies initialization order correctly. % - Specifies that leaving an await-for loop must wait for the subscription % to be canceled. % - An await-for loop only pauses the subscription if it does something async. % - Assert statements allows a "message" operand and a trailing comma. % - The Null type is now considered a subtype of all types in most cases. % - Specify what NEWLINE means in multiline strings. % - Specified the FutureOf type. % - Asserts can occur in initializer lists. % % 1.14 % - The call "C()" where "C" is a class name, is a now compile-time error. % - Changed description of rewrites that depended on a function literal. % In many cases, the rewrite wasn't safe for asynchronous code. % - Removed generalized tear-offs. % - Allow "rethrow" to also end a switch case. Allow braces around switch cases. % - Allow using = as default-value separator for named parameters. % - Make it a compile-time error if a library includes the same part twice. % - Now more specific about the return types of sync*/async/async* functions % in relation to return statements. % - Allow Unicode surrogate values in String literals. % - Make an initializing formal's value accessible in the initializer list. % - Allow any expression in assert statements (was only conditionalExpression). % - Allow trailing commas in argument and parameter lists. % % 1.11 - ECMA 408 - 4th Edition % - Specify that potentially constant expressions must be valid expressions % if the parameters are non-constant. % - Make "??" a compile-time constant operator. % - Having multiple unnamed libraries no longer causes warnings. % - Specify null-aware operators for static methods. % % 1.10 % - Allow mixins to have super-classes and super-calls. % - Specify static type checking for the implicit for-in iterator variable. % - Specify static types for a number of expressions where it was missing. % - Make calls on the exact type "Function" not cause warnings. % - Specify null-aware behavior of "e?.v++" and similar expressions. % - Specify that package: URIs are treated in an implementation dependent way. % - Require warning if for-in is used on object with no "iterator" member. % % 1.9 - ECMA-408 - 3rd Edition % \begin{document} \maketitle \tableofcontents \newpage \pagestyle{myheadings} \markright{Dart Programming Language Specification} % begin Ecma boilerplate \section{Scope} \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 \code{Object} with methods such as \code{noSuchMethod}, \code{runtimeType}). \section{Conformance} \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, except for experimental features. \section{Normative References} \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} \item The Unicode Standard, Version 5.0, as amended by Unicode 5.1.0, or successor. \item Dart API Reference, https://api.dartlang.org/ \end{enumerate} \section{Terms and Definitions} \LMLabel{ecmaTermsAndDefinitions} \LMHash{}% Terms and definitions used in this specification are given in the body of the specification proper. % End Ecma Boilerplate \section{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.% } \item[Commentary] Comments such as \commentary{% The careful reader will have noticed that the name Dart has four characters% }'' serve to illustrate or clarify the specification, but are redundant with the normative text. \commentary{% The difference between commentary and rationale can be subtle.% } \rationale{% Commentary is more general than rationale, and may include illustrative examples or clarifications.% } \end{itemize} \LMHash{}% Reserved words and built-in identifiers (\ref{identifierReference}) appear in {\bf bold}. \commentary{% 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 \lit{::=}'. 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:% } \begin{grammar}\color{commentaryColor} ::= \alt \alt \alt * \alt + \alt ? \alt ( ) \alt \gtilde \alt aTerminal' \alt \end{grammar} \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 \Index{term} is a syntactic construct. It may be considered to be a piece of text which is derivable in the grammar, and it may be considered to be a tree created by such a derivation. An \Index{immediate subterm} of a given term$t$is a syntactic construct which corresponds to an immediate subtree of$t$considered as a derivation tree. A \Index{subterm} of a given term$t$is$t$, or an immediate subterm of$t$, or a subterm of an immediate subterm of$t$. \LMHash{}% A list \DefineSymbol{x_1, \ldots, x_n} denotes any list of$n$elements of the form$x_i, 1 \le i \le n$. Note that$n$may be zero, in which case the list is empty. We use such lists extensively throughout this specification. \BlindDefineSymbol{j, y_j, x_j}% \LMHash{}% For$j \in 1 .. n$, let$y_j$be an atomic syntactic entity (like an identifier),$x_j$a composite syntactic entity (like an expression or a type), and \DefineSymbol{E} again a composite syntactic entity. The notation \IndexCustom{$[x_1/y_1, \ldots, x_n/y_n]E$}{[x1/y1, ..., xn/yn]E@$[x/y\ldots]E$} then denotes a copy of$E$in which each occurrence of$y_i, 1 \le i \le n$has been replaced by$x_i$. \LMHash{}% This operation is also known as \Index{substitution}, and it is the variant that avoids capture. That is, when$E$contains a construct that introduces$y_i$into a nested scope for some$i \in 1 .. n$, the substitution will not replace$y_i$in that scope. Conversely, if such a replacement would put an identifier \id{} (a subterm of$x_i$) into a scope where \id{} is declared, the relevant declarations in$E$are systematically renamed to fresh names. \commentary{% In short, capture freedom ensures that the meaning'' of each identifier is preserved during substitution.% } \LMHash{}% We sometimes abuse list or map literal syntax, writing \code{[\List{o}{1}{n}]} (respectively \code{\{$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{}% \BlindDefineSymbol{x, op, y}% The specifications of operators often involve statements such as \code{$x$\metavar{op}$y$} is equivalent to the method invocation \IndexCustom{\rm\code{$x$.\metavar{op}($y$)}}{% x.op(y)@\code{$x$.\metavar{op}($y$)}}. Such specifications should be understood as a shorthand for: \begin{itemize} \item$xopy$is equivalent to the method invocation \code{$x$.\metavar{op'}($y$)}, assuming the class of$x$actually declared a non-operator method named$op'$defining the same function as the operator$op$. \end{itemize} \rationale{% This circumlocution is required because {\rm\code{$x$.\metavar{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{}% When the specification refers to a \IndexCustom{fresh variable}{variable!fresh}, it means a local variable with a name that doesn't occur anywhere in the current program. When the specification introduces a fresh variable bound to an object, the fresh variable is implicitly bound in a surrounding scope. \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{% Examples would be the classes \code{Object} and \code{Type} representing, respectively, the root of the class hierarchy and the reification of run-time types. % It would be possible to declare, e.g., a local variable named \code{Object}, so it is generally incorrect to assume that the name \code{Object} will actually resolve to said core class. However, we will generally omit mentioning this, for brevity.% } %% TODO(eernst): We need to get rid of the concept of is equivalent to, %% cf. language issue https://github.com/dart-lang/language/issues/227. %% In this CL the phrase treated as has been introduced in a few places, %% and the above-mentioned issue 227 will give rise to a complete revision %% of this aspect of this document. In particular, the next paragraph will %% be deleted. \LMHash{}% When the specification says that one piece of syntax \Index{is equivalent to} another piece of syntax, it means that it is equivalent in all ways, and the former syntax should generate the same compile-time errors and have the same run-time behavior as the latter, if any. \commentary{% Error messages, if any, should always refer to the original syntax.% } If execution or evaluation of a construct is said to be equivalent to execution or evaluation of another construct, then only the run-time behavior is equivalent, and compile-time errors apply only for the original syntax. \LMHash{}% \BlindDefineSymbol{s, s'}% When the specification says that one piece of syntax$s$is \Index{treated as} another piece of syntax$s'$, it means that the static analysis of$s$is the static analysis of$s'$(\commentary{in particular, exactly the same compile-time errors occur}). Moreover, if$s$has no compile-time errors then the behavior of$s$at run time is exactly the behavior of$s'$. \rationale{% Error \emph{messages}, if any, should always refer to the original syntax$s$.% } \commentary{% In short, whenever$s$is treated as$s'$, the reader should immediately switch to the section about$s'$in order to get any further information about the static analysis and dynamic semantics of$s$.% } \rationale{% The notion of being treated as' is similar to the notion of syntactic sugar: $s$is treated as$s'$'' could as well have been worded $s$is desugared into$s'$''. Of course, it should then actually be called semantic sugar'', because the applicability of the transformation and the construction of$s'$may rely on information from static analysis. The point is that we only specify the static analysis and dynamic semantics of a core language which is a subset of Dart (just slightly smaller than Dart), and desugaring transforms any given Dart program to a program in that core language. This helps keeping the language specification consistent and comprehensible, because it shows directly that some language features are introducing essential semantics, and others are better described as mere abbreviations of existing constructs.% } \LMHash{}% The specification uses one syntactic construct, the \IndexCustom{\LET{} expression}{let expression@\LET{} expression}, which is not derivable in the grammar (\commentary{that is, no Dart source code contains such an expression}). This expression is helpful in specifying certain syntactic forms that are treated as other syntactic forms, because it allows for introducing and initializing one or more fresh variables, and using them in an expression. \commentary{% That is, a \LET{} expression is only introduced as a tool to define the evaluation semantics of an expression in terms of other expressions containing \LET{} expressions.% } \LMHash{}% The syntax of a \LET{} expression is as follows: \begin{grammar} ::= \LET{} \IN{} \end{grammar} \LMHash{}% \BlindDefineSymbol{e_{\metavar{let}}, e_j, v_j, k}% Let$e_{\metavar{let}}$be a \LET{} expression of the form \LetMany{$v_1$}{$e_1$}{$v_k$}{$e_k$}{$e$}. It is tacitly assumed that$v_j$is a fresh variable,$j \in 1 .. k$, unless something is stated to the contrary. \LMHash{}%$e_{\metavar{let}}$contains$k$nested scopes, \DefineSymbol{\List{S}{1}{k}}. The enclosing scope for$S_1$is the current scope for$e_{\metavar{let}}$, and the enclosing scope for$S_j$is$S_{j-1}$,$j \in 2 .. k$. The current scope of$e_1$is the current scope of$e_{\metavar{let}}$, the current scope of$e_j$is$S_{j-1}$,$j \in 2 .. k$, and the current scope of$e$is$S_k$. For$j \in 1 .. k$,$v_j$introduces a final, local variable into$S_j$, with the static type of$e_j$as its declared type. \commentary{% Type inference of$e_j$and the context type used for inference of$e_j$are not relevant, it is assumed that type inference has occurred already (\ref{overview}).% } \LMHash{}% Evaluation of$e_{\metavar{let}}$proceeds by evaluating$e_j$to an object$o_j$and binding$v_j$to$o_j$, where$j \in 1 .. k$, in that order. Finally,$e$is evaluated to an object$o$and then$e_{\metavar{let}}$evaluates to$o$. \LMHash{}% The right margin of each page in this document is used to indicate referenced entities. \LMHash{}% The document contains an index at the end. Each entry in the index refers to a page number,$p$. On page$p$there is a $\diamond$' in the margin at the definition of the given indexed phrase, and the phrase itself is shown using \emph{this typeface}. We have hereby introduced the \Index{index marker$\diamond$} itself. \LMHash{}% The right margin also contains symbols. Whenever a symbol \BlindDefineSymbol{\textcolor{commentaryColor}{C}}% \BlindDefineSymbol{\textcolor{commentaryColor}{x_j}}% (\commentary{say,$C$or$x_j$}) is introduced and used in more than a few lines of text, it is shown in the margin. \commentary{% The point is that it is easy to find the definition of a symbol by scanning back in the text until that symbol occurs in the margin. To avoid useless verbosity, some symbols are not mentioned in the margin. For instance, we may introduce \List{e}{1}{k}, but only show$e_j$and$k$in the margin. Note that it may be necessary to look at a few lines of text above the $\diamond$' or symbol, because the margin markers can be pushed down one line when there is more than one marker for a single line.% } \section{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. Programs with compile-time errors do not have a specified dynamic semantics. This specification makes no attempt to answer additional questions about a library or program at the point where it is known to have a compile-time error. \commentary{% However, tools may choose to support execution of some programs with errors. For instance, a compiler may compile certain constructs with errors such that a dynamic error will be raised if an attempt is made to execute such a construct, or an IDE integrated runtime may support opening an editor window when such a construct is executed, allowing developers to correct the error. It is expected that such features would amount to a natural extension of the dynamic semantics of Dart as specified here, but, as mentioned, this specification makes no attempt to specify exactly what that means.% } \LMHash{}% As specified in this document, dynamic checks are guaranteed to be performed in certain situations, and certain violations of the type system throw exceptions at run time. \commentary{% An implementation is free to omit such checks whenever they are guaranteed to succeed, e.g., based on results from the static analysis.% } \commentary{% The coexistence between optional typing and reification is based on the following: \begin{enumerate} \item Reified type information reflects the types of objects at run time and may always be queried by dynamic typechecking constructs (the analogs of instanceOf, casts, typecase etc.\ in other languages). Reified type information includes access to instances of class \code{Type} representing types, the run-time type (aka class) of an object, and the actual values of type parameters to constructors and generic function invocations. \item Type annotations declare the types of variables and functions (including methods and constructors). \item %% TODO(eernst): Change when integrating instantiate-to-bounds.md. Type annotations may be omitted, in which case they are generally filled in with the type \DYNAMIC{} (\ref{typeDynamic}). \end{enumerate}% } %% TODO(eernst): Update when we add inference. \commentary{% Dart as implemented includes extensive support for inference of omitted types. This specification makes the assumption that inference has taken place, and hence inferred types are considered to be present in the program already. However, in some cases no information is available to infer an omitted type annotation, and hence this specification still needs to specify how to deal with that. A future version of this specification will also specify type inference.% } \LMHash{}% Dart programs are organized in a modular fashion into units called \NoIndex{libraries} (\ref{librariesAndScripts}). 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.% } \LMHash{}% A dart program execution may occur with assertions enabled or disabled. The method used to enable or disable assertions is implementation specific. \subsection{Scoping} \LMLabel{scoping} \LMHash{}% A \IndexCustom{compile-time namespace}{namespace!compile-time} is a partial function that maps names to namespace values. Compile-time namespaces are used much more frequently than run-time namespaces (\commentary{defined later in this section}), so when the word \Index{namespace} is used alone, it means compile-time namespace. A \Index{name} is a lexical token which is an \synt{IDENTIFIER}, an \synt{IDENTIFIER} followed by \lit{=}, or an \synt{operator}, or \code{unary-}; and a \Index{namespace value} is a declaration, a namespace, or the special value \ConflictValue{} (\ref{conflictMergingOfNamespaces}). \LMHash{}% If$\Namespace{}{n} = V$then we say that \NamespaceName{} \IndexCustom{maps}{namespace!maps a key to a value} the \IndexCustom{key}{namespace!key}$n$to the \IndexCustom{value}{namespace!value}$V$, and that \NamespaceName{} \IndexCustom{has the binding}{namespace!has a binding}$n\mapsto{}V$. \commentary{% The fact that \NamespaceName{} is a partial function just means that each name is mapped to at most one namespace value. That is, if \NamespaceName{} has the bindings$n\mapsto{}V_1$and$n\mapsto{}V_2$then$V_1 = V_2$.% } \LMHash{}% Let \NamespaceName{} be a namespace. We say that a name$n$\Index{is in} \NamespaceName{} if$n$is a key of \NamespaceName. We say a declaration$d$\NoIndex{is in} \NamespaceName{} if a key of \NamespaceName{} is mapped to$d$. \LMHash{}% A scope$S_0$has an associated namespace \NamespaceName{0}. The bindings of \NamespaceName{0} is specified in this document by saying that a given declaration \BlindDefineSymbol{D, n}$D$named$n$\IndexCustom{introduces}{declaration!introduces an entity into a scope} a specific entity \DefineSymbol{V} into$S_0$, which means that the binding$n\mapsto{}V$is added to \NamespaceName{0}. \commentary{% In some cases, the name of the declaration differs from the identifier that occurs in the declaration syntax used to declare it. Setters have names that are distinct from the corresponding getters because they always have an \lit{=} automatically added at the end, and the unary minus operator has the special name \code{unary-}.% } \commentary{% It is typically the case that$V$is the declaration$D$itself, but there are exceptions. For example, a variable declaration introduces an implicitly induced getter declaration, and in some cases also an implicitly induced setter declaration into the given scope.% } \commentary{% Note that labels (\ref{labels}) are not included in the namespace of a scope. They are resolved lexically rather then being looked up in a namespace.% } \LMHash{}% It is a compile-time error if there is more than one entity with the same name declared in the same scope. \commentary{% It is therefore impossible, e.g., to define a class that declares a method and a getter with the same name in Dart. Similarly one cannot declare a top-level function with the same name as a library variable or a class which is declared in the same library.% } \LMHash{}% We introduce the notion of a \Index{run-time namespace}. This is a partial function from names to run-time entities, in particular storage locations and functions. Each run-time namespace corresponds to a namespace with the same keys, but with values that correspond to the semantics of the namespace values. \rationale{% A namespace typically maps a name to a declaration, and it can be used statically to figure out what that name refers to. For example, a variable is associated with an actual storage location at run time. We introduce the notion of a run-time namespace based on a namespace, such that the dynamic semantics can access run-time entities like that storage location. The same code may be executed multiple times with the same run-time namespace, or with different run-time namespaces for each execution. E.g., local variables declared inside a function are specific to each invocation of the function, and instance variables are specific to an object.% } \LMHash{}% Dart is lexically scoped. Scopes may nest. A name or declaration$d$is \Index{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 \Index{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$\Index{hides} any declaration named$n$that is available in the lexically enclosing scope of$S$. \commentary{% A consequence of these rules is that it is possible to hide a type with a method or variable. Naming conventions usually prevent such abuses. Nevertheless, the following program is legal:% } \begin{dartCode} \CLASS{} HighlyStrung \{ String() => "?"; \} \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{% The interaction of lexical scoping and inheritance is a subtle one. Ultimately, the question is whether lexical scoping takes precedence over inheritance or vice versa. Dart chooses the former. Allowing inherited names to take precedence over locally declared names could create unexpected situations as code evolves. Specifically, the behavior of code in a subclass could silently change if a new name is introduced in a superclass. Consider:% } \begin{dartCode} \LIBRARY{} L1; \CLASS{} S \{\} \LIBRARY{} L2; \IMPORT{} L1.dart'; foo() => 42; \CLASS{} C \EXTENDS{} S\{ bar() => foo();\} \end{dartCode} \rationale{% Now assume a method \code{foo()} is added to \code{S}.% } \begin{dartCode} \LIBRARY{} L1; \CLASS{} S \{foo() => 91;\} \end{dartCode} \rationale{% If inheritance took precedence over the lexical scope, the behavior of \code{C} would change in an unexpected way. Neither the author of \code{S} nor the author of \code{C} are necessarily aware of this. In Dart, if there is a lexically visible method \code{foo()}, it will always be called. Now consider the opposite scenario. We start with a version of \code{S} that contains \code{foo()}, but do not declare \code{foo()} in library \code{L2}. Again, there is a change in behavior---but the author of \code{L2} is the one who introduced the discrepancy that effects their code, and the new code is lexically visible. Both these factors make it more likely that the problem will be detected. These considerations become even more important if one introduces constructs such as nested classes, which might be considered in future versions of the language. Good tooling should of course endeavor to inform programmers of such situations (discreetly). For example, an identifier that is both inherited and lexically visible could be highlighted (via underlining or colorization). Better yet, tight integration of source control with language aware tools would detect such changes when they occur.% } \subsection{Privacy} \LMLabel{privacy} \LMHash{}% Dart supports two levels of \Index{privacy}: public and private. A declaration is \IndexCustom{private}{private!declaration} if{}f its name is private, otherwise it is \IndexCustom{public}{public!declaration}. A name$q$is \IndexCustom{private}{private!name} if{}f any one of the identifiers that comprise$q$is private, otherwise it is \IndexCustom{public}{public!name}. An identifier is \IndexCustom{private}{private!identifier} if{}f it begins with an underscore (the \_ character) otherwise it is \IndexCustom{public}{public!identifier}. \LMHash{}% A declaration$m$is \Index{accessible to a 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.% } \rationale{% Privacy applies only to declarations within a library, not to the library declaration as a whole. This is because libraries do not reference each other by name, and so the idea of a private library is meaningless (\ref{imports}). Thus, if the name of a library begins with an underscore, it has no effect on the accessibility of the library or its members.% } \rationale{% Privacy is, at this point, a static notion tied to a particular piece of code (a library). It is designed to support software engineering concerns rather than security concerns. Untrusted code should always run in an another isolate. Privacy is indicated by the name of a declaration---hence privacy and naming are not orthogonal. This has the advantage that both humans and machines can recognize access to private declarations at the point of use without knowledge of the context from which the declaration is derived.% } \subsection{Concurrency} \LMLabel{concurrency} \LMHash{}% Dart code is always single threaded. There is no shared-state concurrency in Dart. Concurrency is supported via actor-like entities called \Index{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} \LMLabel{errorsAndWarnings} \LMHash{}% This specification distinguishes between several kinds of errors. \LMHash{}% \IndexCustom{Compile-time errors}{compile-time error} 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. Dart is often loaded directly from source, with no intermediate binary representation. In the interests of rapid loading, Dart implementations may choose to avoid full parsing of method bodies, for example. This can be done by tokenizing the input and checking for balanced curly braces on method body entry. In such an implementation, even syntax errors will be detected only when the method needs to be executed, at which time it will be compiled (JITed). In a development environment a compiler should of course report compilation errors eagerly so as to best serve the programmer. A Dart development environment might choose to support error eliminating program transformations, e.g., replacing an erroneous expression by the invocation of a debugger. It is outside the scope of this document to specify how such transformations work, and where they may be applied.% } \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. A Dart engine runs in the context of a \Index{runtime}, a program that interfaces between the engine and the surrounding computing environment. The runtime may be, for instance, a C++ program on the server. When an isolate fails with a compile-time error as described above, control returns to the runtime, along with an exception describing the problem. This is necessary so that the runtime can clean up resources etc. It is then the runtime's decision whether to terminate the isolate or not.% } \LMHash{}% \IndexCustom{Static warnings}{static warning} are situations that do not preclude execution, but which are unlikely to be intended, and likely to cause bugs or inconveniences. A static warning must be reported by a Dart compiler before the associated code is executed. \LMHash{}% When this specification says that a \Index{dynamic error} occurs, it means that a corresponding error object is thrown. When it says that a \Index{dynamic type error} occurs, it represents a failed type check at run time, and the object which is thrown implements \code{TypeError}. \LMHash{}% Whenever we say that an exception$ex$is \IndexCustom{thrown}{throwing an exception}, it acts like an expression had thrown (\ref{statementCompletion}) with$ex$as exception object and with a stack trace corresponding to the current system state. When we say that a$C$\IndexCustom{is thrown}{throwing a class}, 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} \LMLabel{variables} \LMHash{}% Variables are storage locations in memory. \begin{grammar} ::= \LATE? \FINAL{} ? \alt \CONST{} ? \alt \LATE? ::= \VAR{} \alt ::= \gnewline{} (=' )? (,' )* ::= (=' )? ::= (,' )* \end{grammar} \LMHash{}% An \synt{initializedVariableDeclaration} that declares two or more variables is equivalent to multiple variable declarations declaring the same set of variable names, in the same order, with the same initialization, type, and modifiers. \commentary{% For example, \code{\VAR{} x, y;} is equivalent to \code{\VAR{} x; \VAR{} y;} and \code{\STATIC{} \FINAL{} String s1, s2 = "foo";} is equivalent to \code{\STATIC{} \FINAL{} String s1; \STATIC{} \FINAL{} String s2 = "foo";}.% } \LMHash{}% It is possible for a variable declaration to include the modifier \COVARIANT. The effect of doing this with an instance variable is described elsewhere (\ref{instanceVariables}). It is a compile-time error for the declaration of a variable which is not an instance variable to include the modifier \COVARIANT. \LMHash{}% In a variable declaration of one of the forms \code{$Nv$;} \code{$Nv$=$e$;} where$N$is derived from \syntax{ }, we say that$v$is the \Index{declaring occurrence} of the identifier. For every identifier which is not a declaring occurrence, we say that it is an \Index{referencing occurrence}. We also abbreviate that to say that an identifier is a \Index{declaring identifier} respectively an \Index{referencing identifier}. \commentary{% In an expression of the form \code{$e$.\id} it is possible that$e$has static type \DYNAMIC{} and \id{} cannot be associated with any specific declaration named \id{} at compile-time, but in this situation \id{} is still a referencing identifier.% } \LMHash{}% An \Index{initializing variable declaration} is a variable declaration whose declaring identifier is immediately followed by \code{=}' and an \Index{initializing expression}. \LMHash{}% A variable declared at the top-level of a library is referred to as either a \IndexCustom{library variable}{variable!library} or a \IndexCustom{top-level variable}{variable!top-level}. \LMHash{}% A \IndexCustom{static variable}{variable!static} 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{}% A \IndexCustom{constant variable}{variable!constant} is a variable whose declaration includes the modifier \CONST. A constant variable must be initialized to a constant expression (\ref{constants}) or a compile-time error occurs. \commentary{% An initializing expression of a constant variable occurs in a constant context (\ref{constantContexts}), which means that \CONST{} modifiers need not be specified explicitly.% } \LMHash{}% A \IndexCustom{final variable}{variable!final} 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. A variable is final if{}f its declaration includes the modifier \FINAL{} or the modifier \CONST. \LMHash{}% A \IndexCustom{mutable variable}{variable!mutable} is a variable which is not final. %% Note that the following relies on the assumption that inference has %% already taken place, including member signature inference. For instance, %% if var x; is an instance variable declaration that overrides T get x; %% then we treat var x; as if it had been T x;. \LMHash{}% The following rules on implicitly induced getters and setters apply to all static and instance variables. \LMHash{}% A variable declaration of one of the forms \code{$Tv$;} \code{$Tv$=$e$;} \code{\CONST{}$Tv$=$e$;} \code{\FINAL{}$Tv$;} or \code{\FINAL{}$Tv$=$e$;} induces an implicit getter function (\ref{getters}) with signature \code{$T$\GET{}$v$} whose invocation evaluates as described below (\ref{evaluationOfImplicitVariableGetters}). In these cases the static type of$v$is$T$. \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$;} induces an implicit getter function with signature \code{\DYNAMIC{} \GET{}$v$} whose invocation evaluates as described below (\ref{evaluationOfImplicitVariableGetters}). %% TODO[inference]: We assume inference has taken place, i.e., inferred types %% are written explicitly. Does this mean that the initialized variants %% cannot exist (not even for $e$ of type dynamic?). We probably don't %% want to start talking about a grammar before inference and another one %% after inference. In these cases, the static type of$v$is \DYNAMIC{} (\ref{typeDynamic}). \LMHash{}% A mutable variable declaration of the form \code{{}$Tv$;} or \code{$Tv$=$e$;} induces an implicit setter function (\ref{setters}) with signature \code{\VOID{} \SET{}$v$=($Tx$)} whose execution sets the value of$v$to the incoming argument$x$. \LMHash{}% A mutable variable declaration of the form \code{\VAR{}$v$;} or \code{\VAR{}$v$=$e$;} induces an implicit setter function with signature \code{\VOID{} \SET{}$v$=(\DYNAMIC{}$x$)} whose execution sets the value of$v$to the incoming argument$x$. \LMHash{}% 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 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 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{}% Let$v$be variable declared in an initializing variable declaration, and let$e$be the associated initializing expression. It is a compile-time error if the static type of$e$is not assignable to the declared type of$v$. It is a compile-time error if a final instance variable whose declaration has an initializer expression is also initialized by a constructor, either by an initializing formal or an initializer list entry. \commentary{% It is a compile-time error if a final instance variable that has been initialized by means of an initializing formal of a constructor$k$is also initialized in the initializer list of$k$(\ref{initializerLists}). %% TODO(eernst): Not quite true, because of special lookup for assignment! A static final variable$v$does not induce a setter, so unless a setter named \code{$v$=} is in scope it is a compile-time error to assign to$v$. Similarly, assignment to a final instance variable$v$is a compile-time error, unless a setter named \code{$v$=} is in scope, or the receiver has type \DYNAMIC.$v$can be initialized in its declaration or in initializer lists, but initialization and assignment is not the same thing. When the receiver has type \DYNAMIC{} such an assignment is not a compile-time error, % This error can occur because the receiver is dynamic. but if there is no setter it will cause a dynamic error.% } \LMHash{}% A variable that has no initializing expression has the null object (\ref{null}) as its initial value. Otherwise, variable initialization proceeds as follows: \LMHash{}% Static variable declarations with an initializing expression are initialized lazily (\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.% } \commentary{% Initialization of an instance variable with no initializing expression takes place during constructor execution (\ref{initializerLists}).% } \LMHash{}% Initialization of an instance variable$v$with an initializing expression$e$proceeds as follows:$e$is evaluated to an object$o$and the variable$v$is bound to$o$. \commentary{% It is specified elsewhere when this initialization occurs, and in which environment (p.\,\pageref{executionOfGenerativeConstructors}, \ref{localVariableDeclaration}, \ref{bindingActualsToFormals}).% } \commentary{% If the initializing expression throws then access to the uninitialized variable is prevented, because the instance creation that caused this initialization to take place will throw.% } \LMHash{}% % This error can occur due to implicit casts, and % for instance variables also when a setter is called dynamically. It is a dynamic type error if the dynamic type of$o$is not a subtype of the actual type of the variable$v$(\ref{actualTypes}). \subsection{Evaluation of Implicit Variable Getters} \LMLabel{evaluationOfImplicitVariableGetters} \LMHash{}% \BlindDefineSymbol{d, v}% 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 object stored in$v$. If$d$is a static variable (\commentary{which can be a library variable}) then the implicit getter method of$v$executes as follows: \begin{itemize} \item {\bf Non-constant variable declaration with initializer}. If$d$is of one of the forms \code{\VAR{}$v$=$e$;}, \code{$Tv$=$e$;}, \code{\FINAL{}$v$=$e$;}, \code{\FINAL{}$Tv$=$e$;}, \code{\STATIC{}$v$=$e$;}, \code{\STATIC{}$Tv$=$e$; }, \code{\STATIC{} \FINAL{}$v$=$e$; } or \code{\STATIC{} \FINAL{}$Tv$=$e$;} and no object has yet been stored into$v$then the initializing expression$e$is evaluated. If, during the evaluation of$e$, the getter for$v$is invoked, a \code{CyclicInitializationError} is thrown. If the evaluation of$e$throws an exception$e$and stack trace$s$, the null object (\ref{null}) is stored into$v$; the execution of the getter then throws$e$and stack trace$s$. Otherwise, the evaluation of$e$succeeded yielding an object$o$; then$o$is stored into$v$and the execution of the getter completes by returning$o$. Otherwise, (\commentary{when an object$o$has been stored in$v$}) execution of the getter completes by returning$o$. \item {\bf Constant variable declaration}. If$d$is of one of the forms \code{\CONST{}$v$=$e$;}, \code{\CONST{}$Tv$=$e$;}, \code{\STATIC{} \CONST{}$v$=$e$;} or \code{\STATIC{} \CONST{}$Tv$=$e$;} the result of the getter is the value of the constant expression$e$. \commentary{% Note that a constant expression cannot depend on itself, so no cyclic references can occur.% } \item {\bf Variable declaration without initializer}. The result of executing the getter method is the object stored in$v$. \commentary{This may be the initial value, that is, the null object.} \end{itemize} \section{Functions} \LMLabel{functions} \LMHash{}% Functions abstract over executable actions. \begin{grammar} ::= \gnewline{} ? ::= ? ::= \ASYNC? =>' ;' \alt (\ASYNC{} *'? | \SYNC{} *')? ::= {' }' \end{grammar} \LMHash{}% Functions can be introduced by function declarations (\ref{functionDeclarations}), method declarations (\ref{instanceMethods}, \ref{staticMethods}), getter declarations (\ref{getters}), setter declarations (\ref{setters}), and constructor declarations (\ref{constructors}); and they can be introduced by function literals (\ref{functionExpressions}). \LMHash{}% A function is \IndexCustom{asynchronous}{function!asynchronous} if its body is marked with the \ASYNC{} or \code{\ASYNC*} modifier. Otherwise the function is \IndexCustom{synchronous}{function!synchronous}. A function is a \IndexCustom{generator}{function!generator} if its body is marked with the \code{\SYNC*} or \code{\ASYNC*} modifier. Further details about these concepts are given below. \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 \emph{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{}% Each declaration that introduces a function has a signature that specifies its return type, name, and formal parameter part, except that the return type may be omitted, and getters never have a formal parameter part. Function literals have a formal parameter part, but no return type and no name. The formal parameter part optionally specifies the formal type parameter list of the function, and it always specifies its formal parameter list. 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, \code{\ASYNC*} or \code{\SYNC*}. % Unless it is statically known that the body of the function cannot complete normally (\commentary{that is, it cannot reach the end and fall through''}, cf.~\ref{statementCompletion}), it is a compile-time error if the addition of \code{\RETURN;} at the end of the body would be a compile-time error. \commentary{% For instance, it is an error if the return type of a synchronous function is \code{int}, and the body may complete normally. The precise rules are given in section~\ref{return}.% } \commentary{% Because Dart supports dynamic function invocations, we cannot guarantee that a function that does not return an object will not be used in the context of an expression. Therefore, every function must either throw or return an object. A function body that ends without doing a throw or return will cause the function to return the null object (\ref{null}), as will a \RETURN{} without an expression. For generator functions, the situation is more subtle. See further discussion in section~\ref{return}.% } OR \item of the form \code{=>$e$} or the form \code{\ASYNC{} =>$e$}, which both return the value of the expression$e$as if by a \code{return$e$}. \commentary{% The other modifiers do not apply here, because they apply only to generators, discussed below. Generators are not allowed to explicitly return anything, objects are added to the generated stream or iterable using \YIELD{} or \YIELD*.% } Let$T$be the declared return type of the function that has this body. It is a compile-time error if one of the following conditions hold: \begin{itemize} \item The function is synchronous,$T$is not \VOID, and it would have been a compile-time error to declare the function with the body \code{\{ \RETURN{}$e$; \}} rather than \code{=>$e$}. \commentary{% In particular,$e$can have \emph{any} type when the return type is \VOID.% } \rationale{% This enables concise declarations of \VOID{} functions. It is reasonably easy to understand such a function, because the return type is textually near to the returned expression$e$. In contrast, \code{\RETURN{}$e$;} in a block body is only allowed for an$e$with one of a few specific static types, because it is less likely that the developer understands that the returned object will not be used (\ref{return}).% } \item The function is asynchronous, \flatten{T} is not \VOID, and it would have been a compile-time error to declare the function with the body \code{\ASYNC{} \{ \RETURN{}$e$; \}} rather than \code{\ASYNC{} =>$e$}. \commentary{% In particular,$e$can have \emph{any} type when the flattened return type is \VOID,% } \rationale{% and the rationale is similar to the synchronous case.% } \end{itemize} \end{itemize} \LMHash{}% It is a compile-time error if an \ASYNC, \code{\ASYNC*} or \code{\SYNC*} modifier is attached to the body of a setter or constructor. \rationale{% An asynchronous setter would be of little use, since setters can only be used in the context of an assignment (\ref{assignment}), and an assignment expression always evaluates to the value of the assignment's right hand side. If the setter actually did its work asynchronously, one might imagine that one would return a future that resolved to the assignment's right hand side after the setter did its work. An asynchronous constructor would, by definition, never return an instance of the class it purports to construct, but instead return a future. Calling such a beast via \NEW{} would be very confusing. If you need to produce an object asynchronously, use a method. One could allow modifiers for factories. A factory for \code{Future} could be modified by \ASYNC, a factory for \code{Stream} could be modified by \code{\ASYNC*}, and a factory for \code{Iterable} could be modified by \code{\SYNC*}. No other scenario makes sense because the object returned by the factory would be of the wrong type. This situation is very unusual so it is not worth making an exception to the general rule for constructors in order to allow it.% } \LMHash{}% It is a compile-time error if the declared return type of a function marked \ASYNC{} is not a supertype of \code{Future<$T$>} for some type$T$. It is a compile-time error if the declared return type of a function marked \code{\SYNC*} is not a supertype of \code{Iterable<$T$>} for some type$T$. It is a compile-time error if the declared return type of a function marked \code{\ASYNC*} is not a supertype of \code{Stream<$T$>} for some type$T$. It is a compile-time error if the declared return type of a function marked \code{\SYNC*} or \code{\ASYNC*} is \VOID. \LMHash{}% We define the notion of the \IndexCustom{element type of a generator}{function!generator!element type} as follows: % If the function$f$is a synchronous generator whose declared return type implements \code{Iterable<$U$>} for some$U$(\ref{interfaceSuperinterfaces}) then the element type of$f$is$U$. % If the function$f$is an asynchronous generator whose declared return type implements \code{Stream<$U$>} for some$U$then the element type of$f$is$U$. % Otherwise, if the function$f$is a generator (\commentary{synchronous or asynchronous}) then the element type of$f$is \DYNAMIC. \commentary{% %% TODO(eernst): Come nnbd, change a top type' to \DYNAMIC. In the latter case the return type is a top type, because the declaration of$f$would otherwise be a compile-time error. This implies that there is no information about the type of elements that the generator will yield.% } \subsection{Function Declarations} \LMLabel{functionDeclarations} \LMHash{}% A \Index{function declaration} is a function that is neither a member of a class nor a function literal. Function declarations include exactly the following: \IndexCustom{library functions}{function!library}, which are function declarations %(including getters and setters) at the top level of a library, and \IndexCustom{local functions}{function!local}, 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}). \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$\Index{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} \LMLabel{formalParameters} \LMHash{}% Every non-getter function declaration includes a \Index{formal parameter list}, which consists of a list of required positional parameters (\ref{requiredFormals}), followed by any optional parameters (\ref{optionalFormals}). The optional parameters may be specified either as a set of named parameters or as a list of positional parameters, but not both. \LMHash{}% Some function declarations include a \Index{formal type parameter list} (\ref{functions}), in which case we say that it is a \IndexCustom{generic function}{function!generic}. A \IndexCustom{non-generic function}{function!non-generic} is a function which is not generic. \LMHash{}% The \Index{formal parameter part} of a function declaration consists of the formal type parameter list, if any, and the formal parameter list. \commentary{% The following kinds of functions cannot be generic: Getters, setters, operators, and constructors.% } \LMHash{}% The formal type parameter list of a function declaration introduces a new scope known as the function's \IndexCustom{type parameter scope}{scope!type parameter}. The type parameter scope of a generic function$f$is enclosed in the scope where$f$is declared. Every formal type parameter introduces a type into the type parameter scope. \LMHash{}% If it exists, the type parameter scope of a function$f$is the current scope for the signature of$f$, and for the formal type parameter list itself; otherwise the scope where$f$is declared is the current scope for the signature of$f$. \commentary{% This means that formal type parameters are in scope in the bounds of parameter declarations, allowing for so-called F-bounded type parameters like \noindent \code{class C{}> \{ \ldots\ \}}, \noindent and the formal type parameters are in scope for each other, allowing dependencies like \code{class D \{ \ldots\ \}}.% } \LMHash{}% The formal parameter list of a function declaration introduces a new scope known as the function's \IndexCustom{formal parameter scope}{scope!formal parameter}. The formal parameter scope of a non-generic function$f$is enclosed in the scope where$f$is declared. The formal parameter scope of a generic function$f$is enclosed in the type parameter scope of$f$. Every formal parameter introduces a local variable into the formal parameter scope. The current scope for the function's signature is the scope that encloses the formal parameter scope. \commentary{% This means that in a generic function declaration, the return type and parameter type annotations can use the formal type parameters, but the formal parameters are not in scope in the signature.% } \LMHash{}% The body of a function declaration introduces a new scope known as the function's \IndexCustom{body scope}{scope!function body}. The body scope of a function$f$is enclosed in the scope introduced by the formal parameter scope of$f$. \LMHash{}% It is a compile-time error if a formal parameter is declared as a constant variable (\ref{variables}). \begin{grammar} ::= (' )' \alt (' ,'? )' \alt (' ,' )' \alt (' )' ::= \gnewline{} (,' )* ::= \alt ::= \gnewline{} [' (,' )* ,'? ]' ::= \gnewline{} {' (,' )* ,'? }' \end{grammar} \LMHash{}% Formal parameter lists allow an optional trailing comma after the last parameter (\syntax{,'?}). A parameter list with such a trailing comma is equivalent in all ways to the same parameter list without the trailing comma. All parameter lists in this specification are shown without a trailing comma, but the rules and semantics apply equally to the corresponding parameter list with a trailing comma. \subsubsection{Required Formals} \LMLabel{requiredFormals} \LMHash{}% A \Index{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. \item As an initializing formal, which is only valid as a parameter to a generative constructor (\ref{generativeConstructors}). \item Via an ordinary variable declaration (\ref{variables}). \end{itemize} \begin{grammar} ::= \gnewline{} ::= \alt \alt ::= \gnewline{} \COVARIANT? ? ?'? ::= \alt \COVARIANT? ::= \COVARIANT? ::= \gnewline{} ? \THIS{} .' ( ?'?)? \end{grammar} \LMHash{}% It is a compile-time error if a formal parameter has the modifier \CONST{} or the modifier \LATE. It is a compile-time error if \VAR{} occurs as the first token of a \synt{fieldFormalParameter}. \LMHash{}% It is a compile-time error if a parameter derived from \synt{fieldFormalParameter} occurs as a parameter of a function which is not a non-redirecting generative constructor. \commentary{% A \synt{fieldFormalParameter} declares an initializing formal, which is described elsewhere (\ref{generativeConstructors}).% } \LMHash{}% It is possible to include the modifier \COVARIANT{} in some forms of parameter declarations. The effect of doing this is described in a separate section (\ref{covariantParameters}). \commentary{% Note that the non-terminal \synt{normalFormalParameter} is also used in the grammar rules for optional parameters, which means that such parameters can also be covariant.% } \LMHash{}% It is a compile-time error if the modifier \COVARIANT{} occurs on a parameter of a function which is not an instance method, instance setter, or instance operator. \subsubsection{Optional Formals} \LMLabel{optionalFormals} \LMHash{}% Optional parameters may be specified and provided with default values. \begin{grammar} ::= (=' )? ::= \gnewline{} \REQUIRED? \gnewline{} ((=' | :') )? \end{grammar} The form \syntax{ :' } is equivalent to the form \syntax{ =' }. The colon-syntax is included only for backwards compatibility. It is deprecated and will be removed in a later version of the language specification. \LMHash{}% It is a compile-time error if the default value of an optional parameter is not a constant expression (\ref{constants}). If no default is explicitly specified for an optional parameter an implicit default of \NULL{} is provided. \LMHash{}% It is a compile-time error if the name of a named optional parameter begins with an _' character. \rationale{% The need for this restriction is a direct consequence of the fact that naming and privacy are not orthogonal. If we allowed named parameters to begin with an underscore, they would be considered private and inaccessible to callers from outside the library where it was defined. If a method outside the library overrode a method with a private optional name, it would not be a subtype of the original method. The static checker would of course flag such situations, but the consequence would be that adding a private named formal would break clients outside the library in a way they could not easily correct.% } \subsubsection{Covariant Parameters} \LMLabel{covariantParameters} \LMHash{}% Dart allows formal parameters of instance methods, including setters and operators, to be declared \COVARIANT. \commentary{% The syntax for doing this is specified in an earlier section (\ref{requiredFormals}).% } \LMHash{}% It is a compile-time error if the modifier \COVARIANT{} occurs in the declaration of a formal parameter of a function which is not an instance method, an instance setter, or an operator. \commentary{% As specified below, a parameter can also be covariant for other reasons. The overall effect of having a covariant parameter$p$in the signature of a given method$m$is to allow the type of$p$to be overridden covariantly, which means that the type required at run time for a given actual argument may be a proper subtype of the type which is known at compile time at the call site.% } \rationale{% This mechanism allows developers to explicitly request that a compile-time guarantee which is otherwise supported (namely: that an actual argument whose static type satisfies the requirement will also do so at run time) is replaced by dynamic type checks. In return for accepting these dynamic type checks, developers can use covariant parameters to express software designs where the dynamic type checks are known (or at least trusted) to succeed, based on reasoning that the static type analysis does not capture.% } \LMHash{}% \BlindDefineSymbol{m, X_j, s}% Let$m$be a method signature with formal type parameters \List{X}{1}{s}, positional formal parameters \List{p}{1}{n}, \BlindDefineSymbol{p_j, n, q_j, k}% and named formal parameters \List{q}{1}{k}. \BlindDefineSymbol{m', X'_j}% Let$m'$be a method signature with formal type parameters \List{X'\!}{1}{s}, positional formal parameters \List{p'\!}{1}{n'}, \BlindDefineSymbol{p'_j, n', q'_j, k'}% and named formal parameters \List{q'\!}{1}{k'}. % Assume that$j \in 1 .. n'$, and$j \leq n$; we say that$p'_j$is the parameter in$m'$that \IndexCustom{corresponds}{parameter corresponds to parameter} to the formal parameter$p_j$in$m$. Assume that$j \in 1 .. k'$and$l \in 1 .. k$; we say that$q'_j$is the parameter in$m'$that \NoIndex{corresponds} to the formal parameter$q_l$in$m$if$q'_j = q_l$. % Similarly, we say that the formal type parameter$X'_j$from$m'$\NoIndex{corresponds} to the formal type parameter$X_j$from$m$, for all$j \in 1 .. s$. \commentary{% This includes the case where$m$respectively$m'$has optional positional parameters, in which case$k = 0$respectively$k' = 0$must hold, but we can have$n \not= n'$. The case where the numbers of formal type parameters differ is not relevant.% } % Being covariant is a property of a parameter of the interface of a class; % this means that we only talk about the originating keyword \COVARIANT{} % and the class that contains the relevant declaration when we detect for % the first time that a given parameter is covariant. From that point and on % it is "carried" along the subtype links associated with class interfaces, % such that we can get it inductively from an indirect superinterface just % by checking whether the direct superinterfaces "have" a method signature % with the relevant name and a corresponding parameter, and then checking % that parameter. The same approach is applicable for covariant-by-class. \LMHash{}% \BlindDefineSymbol{C, m, p}% Let$C$be a class that declares a method$m$which has a parameter$p$whose declaration has the modifier \COVARIANT; in this case we say that the parameter$p$is \IndexCustom{covariant-by-declaration}{parameter!covariant-by-declaration}. % In this case the interface of$C$has the method signature$m$, and that signature has the parameter$p$; we also say that the parameter$p$in this method signature is \NoIndex{covariant-by-declaration}. % Finally, the parameter$p$of the method signature$m$of the interface of a class$C$is \NoIndex{covariant-by-declaration} if a direct superinterface of$C$has an accessible method signature$m'$with the same name as$m$, which has a parameter$p'$that corresponds to$p$, such that$p'$is covariant-by-declaration. \LMHash{}% Assume that \BlindDefineSymbol{C, X_j, B_j, s}$C$is a generic class with formal type parameter declarations \code{$X_1\ \EXTENDS\ B_1 \ldots,\ X_s\ \EXTENDS\ B_s$}, \BlindDefineSymbol{m, p, T}% let$m$be a declaration of an instance method in$C$(which can be a method, a setter, or an operator), let$p$be a parameter declared by$m$, and let$T$be the declared type of$p$. % The parameter$p$is \IndexCustom{covariant-by-class}{parameter!covariant-by-class} if, for any$j \in 1 .. s$,$X_j$occurs in a covariant or an invariant position in$T$. % In this case the interface of$C$also has the method signature$m$, and that signature has the parameter$p$; we also say that the parameter$p$in this method signature is \NoIndex{covariant-by-class}. Finally, the parameter$p$of the method signature$m$of the interface of the class$C$is \NoIndex{covariant-by-class} if a direct superinterface of$C$has an accessible method signature$m'$with the same name as$m$, which has a parameter$p'$that corresponds to$p$, such that$p'$is covariant-by-class. \LMHash{}% A formal parameter$p$is \IndexCustom{covariant}{parameter!covariant} if$p$is covariant-by-declaration or$p$is covariant-by-class. \commentary{% It is possible for a parameter to be simultaneously covariant-by-declaration and covariant-by-class. Note that a parameter may be covariant-by-declaration or covariant-by-class based on a declaration in any direct or indirect superinterface, including any superclass: The definitions above propagate these properties to an interface from each of its direct superinterfaces, but they will in turn receive the property from their direct superinterfaces, and so on.% } \subsection{Type of a Function} \LMLabel{typeOfAFunction} \LMHash{}% This section specifies the static type which is ascribed to the function denoted by a function declaration, and the dynamic type of the corresponding function object. \LMHash{}% In this specification, the notation used to denote the type of a function, that is, a \Index{function type}, follows the syntax of the language, except that \EXTENDS{} is abbreviated to \FunctionTypeExtends. This means that every function type is of one of the forms \FunctionTypePositionalStd{T_0} \FunctionTypeNamedStd{T_0} \noindent where$T_0$is the return type,$X_j$are the formal type parameters with bounds$B_j$,$j \in 1 .. s$,$T_j$are the formal parameter types for$j \in 1 .. n + k$, and$x_{n+j}$are the names of named parameters for$j \in 1 .. k$. Non-generic function types are covered by the case$s = 0$, where the type parameter declaration list \code{<\ldots{}>} as a whole is omitted. % Similarly, the optional brackets \code{[]} and \code{\{\}} are omitted when there are no optional parameters. % We promise that the two forms always get the same treatment for k=0. \commentary{% Both forms with optionals cover function types with no optionals when$k = 0$, and every rule in this specification is such that any of the two forms may be used without ambiguity to determine the treatment of function types with no optionals.% } \LMHash{}% If a function declaration does not declare a return type explicitly, its return type is \DYNAMIC{} (\ref{typeDynamic}), unless it is a constructor, in which case it is not considered to have a return type, or it is a setter or operator \code{[]=}, in which case its return type is \VOID. \LMHash{}% A function declaration may declare formal type parameters. The type of the function includes the names of the type parameters and for each type parameter the upper bound, which is considered to be the built-in class \code{Object} if no bound is specified. When consistent renaming of type parameters can make two function types identical, they are considered to be the same type. \commentary{% It is convenient to include the formal type parameter names in function types because they are needed in order to express such things as relations among different type parameters, F-bounds, and the types of formal parameters. However, we do not wish to distinguish between two function types if they have the same structure and only differ in the choice of names. This treatment of names is also known as alpha-equivalence.% } \LMHash{}% In the following three paragraphs, if the number \DefineSymbol{s} of formal type parameters is zero then the type parameter list in the function type is omitted. \LMHash{}% Let$F$be a function with type parameters \TypeParametersStd, required formal parameter types \List{T}{1}{n}, return type$T_0$, and no optional parameters. Then the static type of$F$is \FunctionTypeAllRequiredStd{T_0}. \LMHash{}% Let$F$be a function with type parameters \TypeParametersStd, required formal parameter types \List{T}{1}{n}, return type$T_0$and positional optional parameter types \List{T}{n+1}{n+k}. Then the static type of$F$is \FunctionTypePositionalStd{T_0}. \LMHash{}% Let$F$be a function with type parameters \TypeParametersStd, required formal parameter types \List{T}{1}{n}, return type$T_0$, and named parameters \PairList{T}{x}{n+1}{n+k}, where$x_{n+j}$,$j \in 1 .. k$may or may not have a default value. Then the static type of$F$is \FunctionTypeNamedStd{T_0}. \LMHash{}% Let$T$be the static type of a function declaration$F$. Let$u$be the run-time type of a function object$o$obtained by function closurization (\ref{functionClosurization}) or instance method closurization (\ref{instanceMethodClosurization}) applied to$F$, and let$t$be the actual type corresponding to$T$at the occasion where$o$was created (\ref{actualTypes}). \commentary{%$T$may contain free type variables, but$t$contains their actual values.% } The following must then hold:$u$is a class that implements the built-in class \FUNCTION;$u$is a subtype of$t$; and$u$is not a subtype of any function type which is a proper subtype of$t$. \commentary{% If we had omitted the last requirement then \code{f \IS{} int\,\FUNCTION([int])} could evaluate to \TRUE{} with the declaration \code{\VOID{} f()\,\{\}}, which is obviously not the intention.% } \rationale{% It is up to the implementation to choose an appropriate representation for function objects. For example, consider that a function object produced via property extraction treats equality differently from other function objects, and is therefore likely a different class. Implementations may also use different classes for function objects based on arity and or type. Arity may be implicitly affected by whether a function is an instance method (with an implicit receiver parameter) or not. The variations are manifold and, e.g., one cannot assume that any two distinct function objects will necessarily have the same run-time type.% } \subsection{External Functions} \LMLabel{externalFunctions} \LMHash{}% An \IndexCustom{external function}{function!external} 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{% External functions allow us to introduce type information for code that is not statically known to the Dart compiler.% } \commentary{% Examples of external functions might be foreign functions (defined in C, or Javascript etc.), primitives of the implementation (as defined by the Dart run-time system), 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 \emph{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 throw a \code{NoSuchMethodError} or some subclass thereof. \LMHash{}% An implementation specific compile-time error can be raised at an \EXTERNAL{} function declaration. \commentary{% Such errors are intended to indicate that every invocation of that function would throw, e.g., because it is known that it will not be connected to a body.% } \LMHash{}% The actual syntax is given in sections \ref{classes} and \ref{librariesAndScripts} below. \section{Classes} \LMLabel{classes} \LMHash{}% A \Index{class} defines the form and behavior of a set of objects which are its \IndexCustom{instances}{instance}. Classes may be defined by class declarations as described below, or via mixin applications (\ref{mixinApplication}). \begin{grammar} ::= \ABSTRACT? \CLASS{} ? \gnewline{} ? ? \gnewline{} {' ( )* }' \alt \ABSTRACT? \CLASS{} ::= (,' )* ::= ;' \alt ::= ? \alt \alt \STATIC? \alt \STATIC? \alt \STATIC? \alt ::= \EXTERNAL{} \alt \EXTERNAL{} \alt \EXTERNAL{} \alt (\EXTERNAL{} \STATIC?)? \alt (\EXTERNAL{} \STATIC?)? \alt (\EXTERNAL{} \STATIC?)? \alt \EXTERNAL? \alt \STATIC{} \CONST{} ? \alt \STATIC{} \FINAL{} ? \alt \STATIC{} \LATE{} \FINAL{} ? \alt \STATIC{} \LATE? \alt \COVARIANT{} \LATE{} \FINAL{} ? \alt \COVARIANT{} \LATE? \alt \LATE? \FINAL{} ? \alt \LATE? \alt \alt ( | )? \alt ( | )? ::= \gnewline{} (,' )* ::= =' \end{grammar} \LMHash{}% It is possible to include the modifier \COVARIANT{} in some forms of declarations. The effect of doing this is described elsewhere (\ref{covariantParameters}). \LMHash{}% A class has constructors, instance members and static members. The \IndexCustom{instance members}{members!instance} of a class are its instance methods, getters, setters and instance variables. The \IndexCustom{static members}{members!static} of a class are its static methods, getters, setters and class variables. The \IndexCustom{members}{members} of a class are its static and instance members. \LMHash{}% A class declaration introduces two scopes: \begin{itemize} \item A \IndexCustom{type-parameter scope}{scope!type parameter}, which is empty if the class is not generic (\ref{generics}). The enclosing scope of the type-parameter scope of a class declaration is the library scope of the current library. \item A \IndexCustom{body scope}{scope!class body}. The enclosing scope of the body scope of a class declaration is the type parameter scope of the class declaration. \end{itemize} \LMHash{}% The current scope of an instance member declaration, a static member declaration, or a constructor declaration is the body scope of the class in which it is declared. \LMHash{}% The current instance (\commentary{and hence its members}) can only be accessed at specific locations in a class: We say that a location$\ell$\IndexCustom{has access to \THIS}{has access to this@has access to \THIS} if{}f$\ell$is inside the body of a declaration of an instance member or a generative constructor, or in the initializing expression of a \LATE{} instance variable declaration. \commentary{% Note that an initializing expression for a non-\LATE{} instance variable does not have access to \THIS, and neither does any part of a declaration marked \STATIC.% } \LMHash{}% Every class has a single superclass except class \code{Object} which has no superclass. A class may implement a number of interfaces by declaring them in its implements clause (\ref{superinterfaces}). \LMHash{}% An \IndexCustom{abstract class declaration}{class declaration!abstract} is a class declaration that is explicitly declared with the \ABSTRACT{} modifier. A \IndexCustom{concrete class declaration}{class declaration!concrete} is a class declaration that is not abstract. An \IndexCustom{abstract class}{class!abstract} is a class whose declaration is abstract, and a \IndexCustom{concrete class}{class!concrete} is a class whose declaration is concrete. \rationale{% 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.% } \commentary{% The interface of a class$C$is an implicit interface that declares instance member signatures that correspond to the instance members declared by$C$, and whose direct superinterfaces are the direct superinterfaces of$C$(\ref{interfaces}, \ref{superinterfaces}).% } \LMHash{}% When a class name appears as a type, that name denotes the interface of the class. \LMHash{}% It is a compile-time error if a class named$C$declares a member with basename (\ref{classMemberConflicts})$C$. If a generic class named$G$declares a type variable named$X$, it is a compile-time error if$X$is equal to$G$, or if$G$has a member whose basename is$X$, or if$G$has a constructor named \code{$G$.$X$}. \commentary{% Here are simple examples, that illustrate the difference between has a member'' and declares a member''. For example, \code{B} \IndexCustom{declares}{declares member} one member named \code{f}, but it \IndexCustom{has}{has member} two such members. The rules of inheritance determine what members a class has.% } \begin{dartCode} \CLASS{} A \{ \VAR{} i = 0; \VAR{} j; f(x) => 3; \} \\ \CLASS{} B \EXTENDS{} A \{ int i = 1; // \comment{getter i and setter i= override versions from A} \STATIC{} j; // \comment{compile-time error: static getter \& setter conflict} // \comment{with instance getter \& setter} \\ // \comment{compile-time error: static method conflicts with instance method} \STATIC{} f(x) => 3; \} \end{dartCode} \subsection{Fully Implementing an Interface} \LMLabel{fullyImplementingAnInterface} % Note that rules here and in \ref{instanceMethods} overlap, but they are % both needed: This section is concerned with concrete methods, including % inherited ones, and \ref{instanceMethods} is concerned with instance % members declared in$C$, including both concrete and abstract ones. \LMHash{}% % The use of concrete member' below may seem redundant, because a class % does not inherit abstract members from its superclass, but this % underscores the fact that even when an abstract declaration of$m$is % declared in$C$,$C$does not "have" an$m$which will suffice here. A concrete class must fully implement its interface. \BlindDefineSymbol{C, I, m}% Let$C$be a concrete class declared in library$L$, with interface$I$. Assume that$I$has a member signature$m$which is accessible to$L$. It is a compile-time error if$C$does not have a concrete member with the same name as$m$and accessible to$L$, unless$C$has a non-trivial \code{noSuchMethod} (\ref{theMethodNoSuchMethod}). \LMHash{}% Each concrete member must have a suitable signature: Assume that$C$has a concrete member with the same name as$m$and accessible to$L$, and let \DefineSymbol{m''} be its member signature. \commentary{% The concrete member may be declared in$C$or inherited from a superclass.% } Let \DefineSymbol{m'} be the member signature which is obtained from$m''$by adding, if not present already, the modifier \COVARIANT{} (\ref{covariantParameters}) to each parameter$p$in$m''$where the corresponding parameter in$m$has the modifier \COVARIANT. It is a compile-time error if$m'$is not a correct override of$m$(\ref{correctMemberOverrides}), unless that concrete member is a \code{noSuchMethod} forwarder (\ref{theMethodNoSuchMethod}). \commentary{% Consider a concrete class \code{C}, and assume that \code{C} declares or inherits a member implementation with the same name for every member signature in its interface. It is still an error if one or more of those member implementations has parameters or types such that they do not satisfy the corresponding member signature in the interface. For this check, any missing \COVARIANT{} modifiers are implicitly added to the signature of an inherited member (this is how we get$m'$from$m''$). When the modifier \COVARIANT{} is added to one or more parameters (which will only happen when the concrete member is inherited), an implementation may choose to implicitly induce a forwarding method with the same signature as$m'$, in order to perform the required dynamic type check, and then invoke the inherited method.% } \LMHash{}% It is an implementation specific choice whether or not an implicitly induced forwarding method is used when the modifier \COVARIANT{} is added to one or more parameters in$m'$. \commentary{% This is true in spite of the fact that such forwarding methods can be observed. E.g., we can compare the run-time type of a tearoff of the method from a receiver of type \code{C} to the run-time type of a tearoff of the super-method from a location in the body of \code{C}.% } \LMHash{}% With or without a forwarding method, the member signature in the interface of$C$is$m$. \commentary{% The forwarding method does not change the interface of \code{C}, it is an implementation detail. In particular, this holds even in the case where an explicit declaration of the forwarding method would have changed the interface of \code{C}, because$m'$is a subtype of$m$. When a class has a non-trivial \code{noSuchMethod}, the class may leave some members unimplemented, and the class is allowed to have a \code{noSuchMethod} forwarder which does not satisfy the class interface (in which case it will be overridden by another \code{noSuchMethod} forwarder). Here is an example:% } \begin{dartCode} \CLASS\ B \{ \VOID\ m(int i) \{\} // \comment{Signature$m''$: \VOID\ m(int).} \} \\ \ABSTRACT\ \CLASS\ I \{ \VOID\ m(\COVARIANT\ num n); // \comment{Signature: \VOID\ m(\COVARIANT\ num).} \} \\ \CLASS\ C \EXTENDS\ B \IMPLEMENTS\ I \{ // \comment{Signature$m$: \VOID\ m(\COVARIANT\ num).} // // \comment{To check that this class fully implements its interface,} // \comment{check that$m'$, that is, \VOID\ m(\COVARIANT\ int),} // \comment{correctly overrides$m$: OK!} \} \end{dartCode} \LMHash{}% Parameters that are covariant-by-declaration must also satisfy the following constraint: Assume that the parameter$p$of$m'$has the modifier \COVARIANT. Assume that a direct or indirect superinterface of$C$has a method signature$m_s$with the same name as$m'$and accessible to$L$, such that$m_s$has a parameter$p_s$that corresponds to$p$. In this situation, a compile-time error occurs if the type of$p$is not a subtype and not a supertype of the type of$p_s$. \commentary{% This ensures that an inherited method satisfies the same constraint for each formal parameter which is covariant-by-declaration as the constraint which is specified for a declaration in$C$(\ref{instanceMethods}).% } \subsection{Instance Methods} \LMLabel{instanceMethods} \LMHash{}% \IndexCustom{Instance methods}{method!instance} are functions (\ref{functions}) whose declarations are immediately contained within a class declaration and that are not declared \STATIC. The \Index{instance methods of a class}$C$are the instance methods declared by$C$and the instance methods inherited by$C$from its superclass (\ref{inheritanceAndOverriding}). \LMHash{}% \BlindDefineSymbol{C, D, m}% Consider a class$C$and an instance member declaration$D$in$C$, with member signature$m$(\ref{interfaces}). It is a compile-time error if$D$overrides a declaration % Note that$m'$is accessible, due to the definition of overrides'. with member signature$m'$from a direct superinterface of$C$(\ref{interfaceInheritanceAndOverriding}), unless$m$is a correct member override of$m'$(\ref{correctMemberOverrides}). \commentary{% This is not the only kind of conflict that may exist: An instance member declaration$D$may conflict with another declaration$D'$, even in the case where they do not have the same name or they are not the same kind of declaration. E.g.,$D$could be an instance getter and$D'$a static setter (\ref{classMemberConflicts}).% } \LMHash{}% For each parameter$p$of$m$where \COVARIANT{} is present, it is a compile-time error if there exists a direct or indirect superinterface of$C$which has an accessible method signature$m''$with the same name as$m$, such that$m''$has a parameter$p''$that corresponds to$p$(\ref{covariantParameters}), unless the type of$p$is a subtype or a supertype of the type of$p''$. \commentary{% This means that a parameter which is covariant-by-declaration can have a type which is a supertype or a subtype of the type of a corresponding parameter in a superinterface, but the two types cannot be unrelated. Note that this requirement must be satisfied for each direct or indirect superinterface separately, because that relationship is not transitive.% } \rationale{% The superinterface may be the statically known type of the receiver, so this means that we relax the potential typing relationship between the statically known type of a parameter and the type which is actually required at run time to the subtype-or-supertype relationship, rather than the strict supertype relationship which applies to a parameter which is not covariant. It should be noted that it is not statically known at the call site whether any given parameter is covariant, because the covariance could be introduced in a proper subtype of the statically known type of the receiver. We chose to give priority to flexibility rather than safety here, because the whole point of covariant parameters is that developers can make the choice to increase the flexibility in a trade-off where some static type safety is lost.% } \subsubsection{Operators} \LMLabel{operators} \LMHash{}% \IndexCustom{Operators}{operators} are instance methods with special names, except for operator \lit{[]} which is an instance getter and operator \lit{[]=} which is an instance setter. \begin{grammar} ::= \gnewline{} ? \OPERATOR{} ::= ~' \alt \alt []' \alt []=' ::= \alt \alt \alt \alt ==' \alt \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: \lit{<}, \lit{>}, \lit{<=}, \lit{>=}, \lit{==}, \lit{-}, \lit{+}, \lit{/}, \lit{\gtilde/}, \lit{*}, \lit{\%}, \lit{|}, \lit{\^}, \lit{\&}, \lit{\ltlt}, \lit{\gtgtgt}, \lit{\gtgt}, \lit{[]=}, \lit{[]}, \lit{\gtilde}. \LMHash{}% It is a compile-time error if the arity of the user-declared operator \lit{[]=} is not 2. It is a compile-time error if the arity of a user-declared operator with one of the names: \lit{<}, \lit{>}, \lit{<=}, \lit{>=}, \lit{==}, \lit{-}, \lit{+}, \lit{\gtilde/}, \lit{/}, \lit{*}, \lit{\%}, \lit{|}, \lit{\^}, \lit{\&}, \lit{\ltlt}, \lit{\gtgtgt}, \lit{\gtgt}, \lit{[]} is not 1. It is a compile-time error if the arity of the user-declared operator \lit{-} is not 0 or 1. \commentary{% The \lit{-} 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 \lit{-} 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 \lit{\gtilde} is not 0. \LMHash{}% It is a compile-time error to declare an optional parameter in an operator. \LMHash{}% It is a compile-time error if a user-declared operator \lit{[]=} declares a return type other than \VOID. \commentary{% If no return type is specified for a user-declared operator \lit{[]=}, its return type is \VOID{} (\ref{typeOfAFunction}).% } \rationale{% The return type is \VOID{} because a return statement in an implementation of operator \lit{[]=} does not return an object. Consider a non-throwing evaluation of an expression$e$of the form \code{$e_1$[$e_2$] =$e_3$}, and assume that the evaluation of$e_3$yields an object$o$.$e$will then evaluate to$o$, and even if the executed body of operator \lit{[]=} completes with an object$o'$, that is, if$o'$is returned it is simply ignored. The rationale for this behavior is that assignments should be guaranteed to evaluate to the assigned object.% } \subsubsection{The Method \code{noSuchMethod}} \LMLabel{theMethodNoSuchMethod} \LMHash{}% The method \code{noSuchMethod} is invoked implicitly during execution in situations where one or more member lookups fail (\ref{ordinaryInvocation}, \ref{getterAccessAndMethodExtraction}, \ref{assignment}). \commentary{% We may think of \code{noSuchMethod} as a backup which kicks in when an invocation of a member$m$is attempted, but there is no member named$m$, or it exists, but the given invocation has an argument list shape that does not fit the declaration of$m$(passing fewer positional arguments than required or more than supported, or passing named arguments with names not declared by$m$). % The next sentence covers both function objects and instances of % a class with a method named \CALL, because we would have a % compile-time error invoking \CALL{} with a wrongly shaped argument % list unless the type is \DYNAMIC{} or \FUNCTION. This can only occur for an ordinary method invocation when the receiver has static type \DYNAMIC, or for a function invocation when the invoked function has static type \FUNCTION{} or \DYNAMIC. % The method \code{noSuchMethod} can also be invoked in other ways, e.g., it can be called explicitly like any other method, and it can be invoked from a \code{noSuchMethod} forwarder, as explained below.% } \LMHash{}% We say that a class$C$\Index{has a non-trivial \code{noSuchMethod}} if$C\$ has a concrete member named \code{noSuchMethod} which is distinct from the one declared in the built-in class \code{Object}. \commentary{% Note that it must be a method that accepts one positional argument, in order to correctly override \code{noSuchMethod} in \code{Object}. For instance, it can have signature \code{noSuchMethod(Invocation i)} or \code{noSuchMethod(Object i, [String s])}, but not