Ecma 408 PDF
Ecma 408 PDF
Dart Programming
Language Specification
Reference number
ECMA-123:2009
"COPYRIGHT NOTICE
© 2015 Ecma International
This document may be copied, published and distributed to others, and certain derivative works of it
may be prepared, copied, published, and distributed, in whole or in part, provided that the above
copyright notice and this Copyright License and Disclaimer are included on all such copies and
derivative works. The only derivative works that are permissible under this Copyright License and
Disclaimer are:
(i) works which incorporate all or portion of this document for the purpose of providing commentary or
explanation (such as an annotated version of the document),
(ii) works which incorporate all or portion of this document for the purpose of incorporating features
that provide accessibility,
(iii) translations of this document into languages other than English and into different formats and
(iv) works by making use of this specification in standard conformant products by implementing (e.g.
by copy and paste wholly or partly) the functionality therein.
However, the content of this document itself may not be modified in any way, including by removing the
copyright notice or references to Ecma International, except as required to translate it into languages
other than English or into a different format.
The official version of an Ecma International document is the English language version on the Ecma
International website. In the event of discrepancies between a translated version and the official
version, the official version shall govern.
The limited permissions granted above are perpetual and will not be revoked by Ecma International or
its successors or assigns.
This document and the information contained herein is provided on an "AS IS" basis and ECMA
INTERNATIONAL DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE
ANY OWNERSHIP RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR
A PARTICULAR PURPOSE."
Contents
1 Scope 6
2 Conformance 6
3 Normative References 6
5 Notation 6
6 Overview 8
6.1 Scoping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
6.2 Privacy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
6.3 Concurrency . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
8 Variables 12
8.1 Evaluation of Implicit Variable Getters . . . . . . . . . . . . . . . 16
9 Functions 16
9.1 Function Declarations . . . . . . . . . . . . . . . . . . . . . . . . 18
9.2 Formal Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . 19
9.2.1 Required Formals . . . . . . . . . . . . . . . . . . . . . . . 20
9.2.2 Optional Formals . . . . . . . . . . . . . . . . . . . . . . . 20
9.3 Type of a Function . . . . . . . . . . . . . . . . . . . . . . . . . . 21
9.4 External Functions . . . . . . . . . . . . . . . . . . . . . . . . . . 22
1
10 Classes 22
10.1 Instance Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
10.1.1 Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
10.2 Getters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
10.3 Setters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
10.4 Abstract Instance Members . . . . . . . . . . . . . . . . . . . . . 27
10.5 Instance Variables . . . . . . . . . . . . . . . . . . . . . . . . . . 28
10.6 Constructors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
10.6.1 Generative Constructors . . . . . . . . . . . . . . . . . . . 29
10.6.2 Factories . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
10.6.3 Constant Constructors . . . . . . . . . . . . . . . . . . . . 34
10.7 Static Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
10.8 Static Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
10.9 Superclasses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
10.9.1 Inheritance and Overriding . . . . . . . . . . . . . . . . . 37
10.10 Superinterfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
11 Interfaces 41
11.1 Superinterfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
11.1.1 Inheritance and Overriding . . . . . . . . . . . . . . . . . 41
12 Mixins 43
12.1 Mixin Application . . . . . . . . . . . . . . . . . . . . . . . . . . 43
12.2 Mixin Composition . . . . . . . . . . . . . . . . . . . . . . . . . . 44
13 Enums 45
14 Generics 45
15 Metadata 46
16 Expressions 47
16.0.1 Object Identity . . . . . . . . . . . . . . . . . . . . . . . . 48
16.1 Constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
16.2 Null . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
16.3 Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
16.4 Booleans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
16.4.1 Boolean Conversion . . . . . . . . . . . . . . . . . . . . . 54
16.5 Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
16.5.1 String Interpolation . . . . . . . . . . . . . . . . . . . . . 58
16.6 Symbols . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
16.7 Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
16.8 Maps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
16.9 Throw . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
16.10 Function Expressions . . . . . . . . . . . . . . . . . . . . . . . . 63
16.11 This . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
16.12 Instance Creation . . . . . . . . . . . . . . . . . . . . . . . . . . 65
2
16.12.1 New . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
16.12.2 Const . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
16.13 Spawning an Isolate . . . . . . . . . . . . . . . . . . . . . . . . . 69
16.14 Function Invocation . . . . . . . . . . . . . . . . . . . . . . . . . 69
16.14.1 Actual Argument List Evaluation . . . . . . . . . . . . . 71
16.14.2 Binding Actuals to Formals . . . . . . . . . . . . . . . . . 71
16.14.3 Unqualified Invocation . . . . . . . . . . . . . . . . . . . 72
16.14.4 Function Expression Invocation . . . . . . . . . . . . . . 73
16.15 Lookup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
16.15.1 Method Lookup . . . . . . . . . . . . . . . . . . . . . . . 73
16.15.2 Getter and Setter Lookup . . . . . . . . . . . . . . . . . 74
16.16 Top level Getter Invocation . . . . . . . . . . . . . . . . . . . . . 74
16.17 Method Invocation . . . . . . . . . . . . . . . . . . . . . . . . . . 74
16.17.1 Ordinary Invocation . . . . . . . . . . . . . . . . . . . . . 74
16.17.2 Cascaded Invocations . . . . . . . . . . . . . . . . . . . . 76
16.17.3 Super Invocation . . . . . . . . . . . . . . . . . . . . . . . 77
16.17.4 Sending Messages . . . . . . . . . . . . . . . . . . . . . . . 78
16.18 Property Extraction . . . . . . . . . . . . . . . . . . . . . . . . . 79
16.18.1 Getter Access and Method Extraction . . . . . . . . . . . 79
16.18.2 Super Getter Access and Method Closurization . . . . . . 81
16.18.3 General Closurization . . . . . . . . . . . . . . . . . . . . 82
16.18.4 Named Constructor Extraction . . . . . . . . . . . . . . . 83
16.18.5 Anonymous Constructor Extraction . . . . . . . . . . . . 83
16.18.6 General Super Property Extraction . . . . . . . . . . . . . 84
16.18.7 Ordinary Member Closurization . . . . . . . . . . . . . . 84
16.18.8 Named Constructor Closurization . . . . . . . . . . . . . . 85
16.18.9 Anonymous Constructor Closurization . . . . . . . . . . . 86
16.18.10Super Closurization . . . . . . . . . . . . . . . . . . . . . 86
16.19 Assignment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
16.19.1 Compound Assignment . . . . . . . . . . . . . . . . . . . 90
16.20 Conditional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
16.21If-null Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . 92
16.22 Logical Boolean Expressions . . . . . . . . . . . . . . . . . . . . 92
16.23 Equality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
16.24 Relational Expressions . . . . . . . . . . . . . . . . . . . . . . . 94
16.25 Bitwise Expressions . . . . . . . . . . . . . . . . . . . . . . . . . 95
16.26 Shift . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
16.27 Additive Expressions . . . . . . . . . . . . . . . . . . . . . . . . 96
16.28 Multiplicative Expressions . . . . . . . . . . . . . . . . . . . . . 97
16.29 Unary Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . 98
16.30 Await Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . 99
16.31 Postfix Expressions . . . . . . . . . . . . . . . . . . . . . . . . . 99
16.32 Assignable Expressions . . . . . . . . . . . . . . . . . . . . . . . 101
16.33 Identifier Reference . . . . . . . . . . . . . . . . . . . . . . . . . 102
16.34 Type Test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
16.35 Type Cast . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
3
17 Statements 106
17.1 Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
17.2 Expression Statements . . . . . . . . . . . . . . . . . . . . . . . . 107
17.3 Local Variable Declaration . . . . . . . . . . . . . . . . . . . . . . 108
17.4 Local Function Declaration . . . . . . . . . . . . . . . . . . . . . 108
17.5 If . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
17.6 For . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
17.6.1 For Loop . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
17.6.2 For-in . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
17.6.3 Asynchronous For-in . . . . . . . . . . . . . . . . . . . . . 111
17.7 While . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
17.8 Do . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
17.9 Switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
17.10 Rethrow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
17.11 Try . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
17.12 Return . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
17.13 Labels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
17.14 Break . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
17.15 Continue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
17.16 Yield and Yield-Each . . . . . . . . . . . . . . . . . . . . . . . . 124
17.16.1 Yield . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
17.16.2 Yield-Each . . . . . . . . . . . . . . . . . . . . . . . . . . 125
17.17 Assert . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
19 Types 136
19.1 Static Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
19.1.1 Type Promotion . . . . . . . . . . . . . . . . . . . . . . . 138
19.2 Dynamic Type System . . . . . . . . . . . . . . . . . . . . . . . . 138
19.3 Type Declarations . . . . . . . . . . . . . . . . . . . . . . . . . . 139
19.3.1 Typedef . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
19.4 Interface Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
19.5 Function Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
19.6 Type dynamic . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
19.7 Type Void . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
19.8 Parameterized Types . . . . . . . . . . . . . . . . . . . . . . . . . 145
19.8.1 Actual Type of Declaration . . . . . . . . . . . . . . . . . 145
19.8.2 Least Upper Bounds . . . . . . . . . . . . . . . . . . . . . 145
4
20 Reference 146
20.1 Lexical Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
20.1.1 Reserved Words . . . . . . . . . . . . . . . . . . . . . . . 147
20.1.2 Comments . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
20.2 Operator Precedence . . . . . . . . . . . . . . . . . . . . . . . . . 148
5
Dart Programming Language Specification 6
1 Scope ecmaScope
This Ecma standard specifies the syntax and semantics of the Dart program-
ming 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 Object with methods such as noSuchMethod,
runtimeType).
2 Conformance ecmaConformance
Terms and definitions used in this specification are given in the body of
the specification proper. Such terms are highlighted in italics when they are
introduced, e.g., ‘we use the term verbosity to refer to the property of excess
verbiage’.
5 Notation notation
Rationale Discussion of the motivation for language design decisions appears in ital-
ics. Distinguishing normative from non-normative helps clarify what part
of the text is binding and what part is merely expository.
Commentary Comments such as “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. The difference between commentary
and rationale can be subtle. Commentary is more general than rationale,
and may include illustrative examples or clarifications.
Open questions (in this font). Open questions are points that are unsettled in the mind
of the author(s) of the specification; expect them (the questions, not the
authors; precision is important in a specification) to be eliminated in the
final specification. Should the text at the end of the previous bullet
be rationale or commentary?
Reserved words and built-in identifiers (16.33) appear in bold.
Examples would be switch or class.
Grammar productions are given in a common variant of EBNF. The left
hand side of a production ends with a colon. On the right hand side, alternation
is represented by vertical bars, and sequencing by spacing. As in PEGs, alter-
nation gives priority to the left. Optional elements of a production are suffixed
by a question mark like so: 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.
An example would be:
AProduction:
AnAlternative |
AnotherAlternative |
OneThing After Another |
ZeroOrMoreThings* |
OneOrMoreThings+ |
AnOptionalThing? |
(Some Grouped Things) |
˜NotAThing |
A LEXICAL THING
;
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
Dart Programming Language Specification 8
6 Overview overview
4. Checked mode utilizes static type annotations and dynamic type information
aggressively yet selectively to provide early error detection during development.
Dart programs are organized in a modular fashion into units called libraries
(18). Libraries are units of encapsulation and may be mutually recursive.
However they are not first class. To get multiple copies of a library running
simultaneously, one needs to spawn an isolate.
In some cases, the name of the declaration differs from the identifier used to
declare it. Setters have names that are distinct from the corresponding getters
because they always have an = automatically added at the end, and unary minus
has the special name unary-.
Dart is lexically scoped. Scopes may nest. A name or declaration d is
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 in
scope if d is available in the current scope.
If a declaration d named n is in the namespace induced by a scope S, then d
hides any declaration named n that is available in the lexically enclosing scope
of S.
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:
class HighlyStrung {
String() => ”?”;
}
Names may be introduced into a scope by declarations within the scope or
by other mechanisms such as imports or inheritance.
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 can
create unexpected situations as code evolves. Specifically, the behavior of code
in a subclass can change without warning if a new name is introduced in a
superclass. Consider:
library L1;
class S {}
library L2;
import ‘L1.dart’;
foo() => 42;
class C extends S{ bar() => foo();}
Now assume a method foo() is added to S.
library L1;
class S {foo() => 91;}
If inheritance took precedence over the lexical scope, the behavior of C would
change in an unexpected way. Neither the author of S nor the author of C are
necessarily aware of this. In Dart, if there is a lexically visible method foo(), it
will always be called.
Now consider the opposite scenario. We start with a version of S that con-
tains foo(), but do not declare foo() in library L2. Again, there is a change in
behavior - but the author of 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 con-
structs such as nested classes, which might be considered in future versions of
Dart Programming Language Specification 11
the language.
Good tooling should of course endeavor to inform programmers of such situ-
ations (discreetly). For example, an identifier that is both inherited and lexically
visible could be highlighted (via underlining or colorization). Better yet, tight in-
tegration of source control with language aware tools would detect such changes
when they occur.
error must be reported by a Dart compiler before the erroneous code is executed.
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.
As a web language, Dart is often loaded directly from source, with no inter-
mediate binary representation. In the interests of rapid loading, Dart implemen-
tations 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.
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.
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 an embedder, a program that interfaces between the
engine and the surrounding computing environment. The embedder will often
be a web browser, but need not be; it may be a C++ program on the server for
example. When an isolate fails with a compile-time error as described above,
control returns to the embedder, along with an exception describing the problem.
This is necessary so that the embedder can clean up resources etc. It is then the
embedder’s decision whether to terminate the isolate or not.
Static warnings are those errors reported by the static checker. They have no
effect on execution. Many, but not all, static warnings relate to types, in which
case they are known as static type warnings. Static warnings must be provided
by Dart compilers used during development such as those incorporated in IDEs
or otherwise intended to be used by developers for developing code. Compilers
that are part of runtime execution environments such as virtual machines should
not issue static warnings.
Dynamic type errors are type errors reported in checked mode.
Run-time errors are exceptions raised during execution. Whenever we say
that an exception ex is raised or thrown, we mean that a throw expression (16.9)
of the form: throw ex; was implicitly evaluated or that a rethrow statement
(17.10) of the form rethrow was executed. When we say that a C is thrown,
where C is a class, we mean that an instance of class C is thrown.
If an uncaught exception is thrown by a running isolate A, A is immediately
suspended.
8 Variables variables
Dart Programming Language Specification 13
variableDeclaration:
declaredIdentifier (‘, ’ identifier)*
;
declaredIdentifier:
metadata finalConstVarOrType identifier
;
finalConstVarOrType:
final type? |
const type? |
varOrType
;
varOrType:
var |
type
;
initializedVariableDeclaration:
declaredIdentifier (‘=’ expression)? (‘, ’ initializedIdentifier)*
;
initializedIdentifier:
identifier (‘=’ expression)?
;
initializedIdentifierList:
initializedIdentifier (‘, ’ initializedIdentifier)*
;
A variable that has not been initialized has the initial value null (16.2).
A variable declared at the top-level of a library is referred to as either a
library variable or simply a top-level variable.
A static variable is a variable that is not associated with a particular in-
stance, 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 (16.33) static.
Static variable declarations are initialized lazily. When a static variable v
Dart Programming Language Specification 14
is read, iff it has not yet been assigned, it is set to the result of evaluating its
initializer. The precise rules are given in section 8.1.
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.
A final variable is a variable whose binding is fixed upon initialization; a
final variable v will always refer to the same object after v has been initialized.
The declaration of a final variable must include the modifier final.
It is a static warning if a final instance variable that has been initialized
at its point of declaration is also initialized in a constructor. It is a compile-
time error if a local variable v is final and v is not initialized at its point of
declaration.
A library or static variable is guaranteed to have an initializer at its declaration
by the grammar.
Attempting to assign to a final variable anywhere except in its declaration or in
a constructor header will cause a runtime error to be thrown as discussed below.
The assignment will also give rise to a static warning. Any repeated assignment to
a final variable will also lead to a runtime error.
Taken as a whole, the rules ensure that any attempt to execute multiple assign-
ments to a final variable will yield static warnings and repeated assignments will fail
dynamically.
A constant variable is a variable whose declaration includes the modifier
const. A constant variable is always implicitly final. A constant variable must
be initialized to a compile-time constant (16.1) or a compile-time error occurs.
We say that a variable v is potentially mutated in some scope s if v is not
final or constant and an assignment to v occurs in s.
If a variable declaration does not explicitly specify a type, the type of the
declared variable(s) is dynamic, the unknown type (19.6).
A variable is mutable if it is not final. Static and instance variable declara-
tions always induce implicit getters. If the variable is mutable it also introduces
an implicit setter. The scope into which the implicit getters and setters are
introduced depends on the kind of variable declaration involved.
A library variable introduces a getter into the top level scope of the enclosing
library. A static class variable introduces a static getter into the immediately
enclosing class. An instance variable introduces an instance getter into the
immediately enclosing class.
A mutable library variable introduces a setter into the top level scope of
the enclosing library. A mutable static class variable introduces a static setter
into the immediately enclosing class. A mutable instance variable introduces an
instance setter into the immediately enclosing class.
Local variables are added to the innermost enclosing scope. They do not
induce getters and setters. A local variable may only be referenced at a source
code location that is after its initializer, if any, is complete, or a compile-time
error occurs. The error may be reported either at the point where the premature
reference occurs, or at the variable declaration.
Dart Programming Language Specification 15
Inside perverse(), C denotes a local variable. The type C is hidden by the vari-
able of the same name. The attempt to instantiate C causes a compile-time error be-
cause it references a local variable prior to its declaration. Similarly, for the decla-
ration of aC (even though it is only a type annotation).
As a rule, type annotations are ignored in production mode. However, we do
not want to allow programs to compile legally in one mode and not an-
other, and in this extremely odd situation, that consideration takes precedence.
The following rules apply to all static and instance variables.
A variable declaration of one of the forms T v;, T v = e; , const T v =
Dart Programming Language Specification 16
9 Functions functions
functionSignature:
metadata returnType? identifier formalParameterList
;
returnType:
void |
type
;
functionBody:
async? ‘=>’ expression ‘;’ |
(async | async* | sync*)? block
;
block:
‘{’ statements ‘}’
;
• of the form => e which is equivalent to a body of the form {return e;}
or the form async => e which is equivalent to a body of the form async
{return e;}. The other modifiers do not apply here, because they apply
only to generators, discussed below, and generators do not allow the form
return e; values are added to the generated stream or iterable using yield
instead.
A function is asynchronous if its body is marked with the async or async*
Dart Programming Language Specification 18
scope of a local function is described in section 17.4. In both cases, the name
of the function is in scope in its formal parameter scope (9.2).
It is a compile-time error to preface a function declaration with the built-in
identifier static.
When we say that a function f1 forwards to another function f2 , we mean
that invoking f1 causes f2 to be executed with the same arguments and/or
receiver as f1 , and returns the result of executing f2 to the caller of f1 , unless f2
throws an exception, in which case f1 throws the same exception. Furthermore,
we only use the term for synthetic functions introduced by the specification.
formalParameterList:
‘(’ ‘)’ |
‘(’ normalFormalParameters ( ‘, ’ optionalFormalParameters)? ‘)’
|
‘(’ optionalFormalParameters ‘)’
;
normalFormalParameters:
normalFormalParameter (‘, ’ normalFormalParameter)*
;
optionalFormalParameters:
optionalPositionalFormalParameters |
namedFormalParameters
;
Dart Programming Language Specification 20
optionalPositionalFormalParameters:
‘[’ defaultFormalParameter (‘, ’ defaultFormalParameter)* ‘]’
;
namedFormalParameters:
‘{’ defaultNamedParameter (‘, ’ defaultNamedParameter)* ‘}’
;
normalFormalParameter:
functionSignature |
fieldFormalParameter |
simpleFormalParameter
;
simpleFormalParameter:
declaredIdentifier |
metadata identifier
;
fieldFormalParameter:
metadata finalConstVarOrType? this ‘.’ identifier formalParam-
eterList?
;
defaultFormalParameter:
normalFormalParameter (’=’ expression)?
;
Dart Programming Language Specification 21
defaultNamedParameter:
normalFormalParameter ( ‘:’ expression)?
;
If a function does not declare a return type explicitly, its return type is
dynamic (19.6), unless it is a constructor function, in which case its return
type is the immediately enclosing class.
Let F be a function with required formal parameters T1 p1 . . . , Tn pn , return
type T0 and no optional parameters. Then the type of F is (T1 , . . . , Tn ) → T0 .
Let F be a function with required formal parameters T1 p1 . . . , Tn pn , return
type T0 and positional optional parameters Tn+1 pn+1 , . . . , Tn+k pn+k . Then
the type of F is (T1 , . . . , Tn , [Tn+1 pn+1 , . . . , Tn+k pn+k ]) → T0 .
Let F be a function with required formal parameters T1 p1 . . . , Tn pn , return
type T0 and named optional parameters Tn+1 pn+1 , . . . , Tn+k pn+k . Then the
type of F is (T1 , . . . , Tn , {Tn+1 pn+1 , . . . , Tn+k pn+k }) → T0 .
The run time type of a function object always implements the class Function.
One cannot assume, based on the above, that given a function f, f.runtimeType
will actually be Function, or that any two distinct function objects necessarily have
the same runtime type.
It is up to the implementation to choose an appropriate representation for
functions. For example, consider that a closure produced via property extraction
treats equality different from ordinary closures, and is therefore likely a different
class. Implementations may also use different classes for functions 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 so this specification only guarantees that function objects are
instances of some class that is considered to implement Function.
Dart Programming Language Specification 22
10 Classes classes
A class defines the form and behavior of a set of objects which are its
instances. Classes may be defined by class declarations as described below, or
via mixin applications (12.1).
classDefinition:
metadata abstract? class identifier typeParameters? (superclass
mixins?)? interfaces?
‘{’ (metadata classMemberDefinition)* ‘}’ |
mixins:
with typeList
;
classMemberDefinition:
declaration ‘;’ |
methodSignature functionBody
;
methodSignature:
constructorSignature initializers? |
Dart Programming Language Specification 23
factoryConstructorSignature |
static? functionSignature |
static? getterSignature |
static? setterSignature |
operatorSignature
;
declaration:
constantConstructorSignature (redirection | initializers)? |
constructorSignature (redirection | initializers)? |
external constantConstructorSignature |
external constructorSignature |
((external static ?))? getterSignature |
((external static?))? setterSignature |
external? operatorSignature |
((external static?))? functionSignature |
static (final | const) type? staticFinalDeclarationList |
final type? initializedIdentifierList |
static? (var | type) initializedIdentifierList
;
staticFinalDeclarationList:
staticFinalDeclaration (‘, ’ staticFinalDeclaration)*
;
staticFinalDeclaration:
identifier ‘=’ expression
;
A class has constructors, instance members and static members. The in-
stance members of a class are its instance methods, getters, setters and instance
variables. The static members of a class are its static methods, getters, setters
and static variables. The members of a class are its static and instance members.
A class has several scopes:
• A type-parameter scope, which is empty if the class is not generic (14).
The enclosing scope of the type-parameter scope of a class is the enclosing
scope of the class declaration.
• A static scope. The enclosing scope of the static scope of a class is the
type parameter scope (14) of the class.
• An instance scope. The enclosing scope of a class’ instance scope is the
class’ static scope.
Dart Programming Language Specification 24
Instance methods are functions (9) whose declarations are immediately con-
tained within a class declaration and that are not declared static. The instance
Dart Programming Language Specification 25
methods of a class C are those instance methods declared by C and the instance
methods inherited by C from its superclass.
It is a static warning if an instance method m1 overrides (10.9.1) an instance
member m2 and m1 has a greater number of required parameters than m2 . It
is a static warning if an instance method m1 overrides an instance member m2
and m1 has fewer positional parameters than m2 . It is a static warning if an
instance method m1 overrides an instance member m2 and m1 does not declare
all the named parameters declared by m2 .
It is a static warning if an instance method m1 overrides an instance member
m2 and the type of m1 is not a subtype of the type of m2 . It is a static warning
if an instance method m1 overrides an instance member m2 , the signature of m2
explicitly specifies a default value for a formal parameter p and the signature
of m1 implies a different default value for p. It is a static warning if a class C
declares an instance method named n and has a setter named n =. It is a static
warning if a class C declares an instance method named n and an accessible
static member named n is declared in a superclass of C.
operatorSignature:
returnType? operator operator formalParameterList
;
operator:
‘˜’ |
binaryOperator |
‘[’ ‘]’ |
‘[’ ‘]’ ‘=’
;
binaryOperator:
multiplicativeOperator |
additiveOperator |
shiftOperator |
relationalOperator |
‘==’ |
bitwiseOperator
;
Getters are functions (9) that are used to retrieve the values of object
properties.
getterSignature:
returnType? get identifier
;
Setters are functions (9) that are used to set the values of object properties.
setterSignature:
returnType? set identifier formalParameterList
;
constructorSignature:
identifier (‘.’ identifier)? formalParameterList
;
the type of the field named id in the immediately enclosing class. It is a static
warning if the static type of id is not assignable to Tid .
Using an initializing formal this.id in a formal parameter list does not in-
troduce a formal parameter name into the scope of the constructor. However,
the initializing formal does effect the type of the constructor function exactly as
if a formal parameter named id of the same type were introduced in the same
position.
Initializing formals are executed during the execution of generative con-
structors detailed below. Executing an initializing formal this.id causes the field
id of the immediately surrounding class to be assigned the value of the corre-
sponding actual parameter, unless id is a final variable that has already been
initialized, in which case a runtime error occurs.
The above rule allows initializing formals to be used as optional parameters:
class A {
int x;
A([this.x]);
}
is legal, and has the same effect as
class A {
int x;
A([int x]): this.x = x;
}
A fresh instance is an instance whose identity is distinct from any previously
allocated instance of its class. A generative constructor always operates on a
fresh instance of its immediately enclosing class.
The above holds if the constructor is actually run, as it is by new. If a constructor
c is referenced by const, c may not be run; instead, a canonical object may be looked
up. See the section on instance creation (16.12).
If a generative constructor c is not a redirecting constructor and no body is
provided, then c implicitly has an empty body {}. redirectingConstructors
Redirecting Constructors
A generative constructor may be redirecting, in which case its only action is
to invoke another generative constructor. A redirecting constructor has no body;
instead, it has a redirect clause that specifies which constructor the invocation
is redirected to, and with what arguments.
redirection:
‘:’ this (‘.’ identifier)? arguments
;
initializerLists
Initializer Lists
An initializer list begins with a colon, and consists of a comma-separated
list of individual initializers. There are two kinds of initializers.
• A superinitializer identifies a superconstructor - that is, a specific con-
structor of the superclass. Execution of the superinitializer causes the
initializer list of the superconstructor to be executed.
Dart Programming Language Specification 31
superCallOrFieldInitializer:
super arguments |
super ‘.’ identifier arguments |
fieldInitializer
;
fieldInitializer:
(this ‘.’)? identifier ‘=’ conditionalExpression cascadeSection*
;
factoryConstructorSignature:
factory identifier (‘.’ identifier)? formalParameterList
;
The return type of a factory whose signature is of the form factory M or the
form factory M.id is M if M is not a generic type; otherwise the return type
is M < T1 , . . . , Tn > where T1 , . . . , Tn are the type parameters of the enclosing
class
It is a compile-time error if M is not the name of the immediately enclosing
class.
In checked mode, it is a dynamic type error if a factory returns a non-null
object whose type is not a subtype of its actual (19.8.1) return type.
It seems useless to allow a factory to return null. But it is more uniform to
allow it, as the rules currently do.
Factories address classic weaknesses associated with constructors in other
languages. Factories can produce instances that are not freshly allocated: they
can come from a cache. Likewise, factories can return instances of different
classes. redirectingFactoryConstructors
redirectingFactoryConstructorSignature:
const? factory identifier (‘.’ identifier)? formalParameterList
‘=’ type (‘.’ identifier)?
;
constantConstructorSignature:
const qualified formalParameterList
;
All the work of a constant constructor must be handled via its initializers.
It is a compile-time error if a constant constructor is declared by a class
that has a non-final instance variable.
The above refers to both locally declared and inherited instance variables.
It is a compile-time error if a constant constructor is declared by a class C
if any instance variable declared in C is initialized with an expression that is
not a constant expression.
Dart Programming Language Specification 35
assume that p evaluates to an integer. A similar argument holds for p and q in the
assignment to z.
However, the following constructors are disallowed:
class D {
final w;
const D.makeList(p): w = const [p]; // compile-time error
const D.makeMap(p): w = const {“help”: q}; // compile-time error
const D.makeC(p): w = const C(p, 12); // compile-time error
}
The problem is not that the assignments to w are not potentially constant; they
are. However, all these run afoul of the rules for constant lists (16.7), maps (16.8)
and objects (16.12.2), all of which independently require their subexpressions to be
constant expressions.
All of the illegal constructors of D above could not be sensibly invoked via
new, because an expression that must be constant cannot depend on a formal
parameter, which may or may not be constant. In contrast, the legal examples
make sense regardless of whether the constructor is invoked via const or via
new.
Careful readers will of course worry about cases where the actual arguments
to C() are constants, but are not numeric. This is precluded by the following
rule, combined with the rules for evaluating constant objects (16.12.2).
When invoked from a constant object expression, a constant constructor
must throw an exception if any of its actual parameters is a value that would
prevent one of the potentially constant expressions within it from being a valid
compile-time constant.
Static methods are functions, other than getters or setters, whose declara-
tions are immediately contained within a class declaration and that are declared
static. The static methods of a class C are those static methods declared by
C.
The effect of a static method declaration in class C is to add an instance
method with the same name and signature to the Type object for class C that
forwards (9.1) to the static method.
Inheritance of static methods has little utility in Dart. Static methods cannot
be overridden. Any required static function can be obtained from its declaring
library, and there is no need to bring it into scope via inheritance. Experience
shows that developers are confused by the idea of inherited methods that are not
instance methods.
Of course, the entire notion of static methods is debatable, but it is retained
here because so many programmers are familiar with it. Dart static methods
may be seen as functions of the enclosing library.
It is a static warning if a class C declares a static method named n and has
a setter named n =.
Dart Programming Language Specification 37
superclass:
extends type
;
The scope of the extends and with clauses of a class C is the type-
parameter scope of C.
It is a compile-time error if the extends clause of a class C specifies an
enumerated type (13), a malformed type or a deferred type (19.1) as a superclass.
The type parameters of a generic class are available in the lexical scope of
the superclass clause, potentially shadowing classes in the surrounding scope. The
following code is therefore illegal and should cause a compile-time error:
class T {}
/* Compilation error: Attempt to subclass a type parameter */
class G<T> extends T {}
A class S is a superclass of a class C iff either:
• S is the superclass of C, or
1. There is only one namespace for getters, setters, methods and constructors
(6.1). A field f introduces a getter f and a non-final field f also introduces a
setter f = (10.5, 10.8). When we speak of members here, we mean accessible
fields, getters, setters and methods (10).
2. You cannot have two members with the same name in the same class - be
they declared or inherited (6.1, 10).
A class has a set of direct superinterfaces. This set includes the interface of
its superclass and the interfaces specified in the the implements clause of the
class.
interfaces:
implements typeList
;
11 Interfaces interfaces
An interface defines how one may interact with an object. An interface has
methods, getters and setters and a set of superinterfaces.
– A declares a member m0 or
– m0 is a member of inherited(A, K).
Let I be the implicit interface of a class C declared in library L. I inherits
all members of inherited(I, L) and I overrides m0 if m0 ∈ overrides(I, L).
All the static warnings pertaining to the overriding of instance members
given in section 10 above hold for overriding between interfaces as well.
It is a static warning if m is a method and m0 is a getter, or if m is a getter
and m0 is a method.
However, if the above rules would cause multiple members m1 , . . . , mk with
the same name n to be inherited (because identically named members existed
in several superinterfaces) then at most one member is inherited.
If some but not all of the mi , 1 ≤ i ≤ k are getters none of the mi are
inherited, and a static warning is issued.
Otherwise, if the static types T1 , . . . , Tk of the members m1 , . . . , mk are not
identical, then there must be a member mx such that Tx <: Ti , 1 ≤ x ≤ k for
all i ∈ 1..k, or a static type warning occurs. The member that is inherited is
mx , if it exists; otherwise: let numberOf P ositionals(f ) denote the number of
positional parameters of a function f , and let numberOf RequiredP arams(f )
denote the number of required parameters of a function f . Furthermore, let s
denote the set of all named parameters of the m1 , . . . , mk . Then let
h = max(numberOf P ositionals(mi )),
r = min(numberOf RequiredP arams(mi )), i ∈ 1..k.
Then I has a method named n, with r required parameters of type dy-
namic, h positional parameters of type dynamic, named parameters s of type
dynamic and return type dynamic.
The only situation where the runtime would be concerned with this would be
during reflection, if a mirror attempted to obtain the signature of an interface
member.
The current solution is a tad complex, but is robust in the face of type an-
notation changes. Alternatives: (a) No member is inherited in case of conflict.
(b) The first m is selected (based on order of superinterface list) (c) Inherited
member chosen at random.
Dart Programming Language Specification 43
12 Mixins mixins
A mixin describes the difference between a class and its superclass. A mixin
is always derived from an existing class declaration.
It is a compile-time error if a declared or derived mixin explicitly declares
a constructor.
This restriction is temporary. We expect to remove it in later versions of
Dart.
The restriction on constructors simplifies the construction of mixin applica-
tions because the process of creating instances is simpler.
mixinApplication:
type mixins interfaces?
;
Dart does not directly support mixin composition, but the concept is useful
when defining how the superclass of a class with a mixin clause is created.
The composition of two mixins, M1 < T1 . . . TkM1 > and M2 < U1 . . . UkM2 >,
written M1 < T1 . . . TkM1 > ∗M2 < U1 . . . UkM2 > defines an anonymous mixin
such that for any class S < V1 . . . VkS >, the application of
M1 < T1 . . . TkM1 > ∗M2 < U1 . . . UkM2 >
to S < V1 . . . VkS > is equivalent to
abstract class Id1 < T1 . . . TkM1 , U1 . . . UkM2 , V1 . . . VkS > =
Id2 < U1 . . . UkM2 , V1 . . . VkS > with M1 < T1 . . . TkM1 >;
where Id2 denotes
abstract class Id2 < U1 . . . UkM2 , V1 . . . VkS > =
S < V1 . . . VkS > with M2 < U1 . . . UkM2 >;
and Id1 and Id2 are unique identifiers that do not exist anywhere in the
program.
The classes produced by mixin composition are regarded as abstract because
they cannot be instantiated independently. They are only introduced as anony-
mous superclasses of ordinary class declarations and mixin applications. Conse-
Dart Programming Language Specification 45
13 Enums enums
enumType:
metadata enum id ‘{’ id [‘, ’ id]* [‘, ’] ‘}’
;
14 Generics generics
A class declaration (10) or type alias (19.3.1) G may be generic, that is,
G may have formal type parameters declared. A generic declaration induces a
family of declarations, one for each set of actual type parameters provided in
the program.
typeParameter:
metadata identifier (extends type)?
;
typeParameters:
‘<’ typeParameter (‘,’ typeParameter)* ‘>’
Dart Programming Language Specification 46
15 Metadata metadata
metadata:
(‘@’ qualified (‘.’ identifier)? (arguments)?)*
;
16 Expressions expressions
expression:
assignableExpression assignmentOperator expression |
conditionalExpression cascadeSection* |
throwExpression
;
Dart Programming Language Specification 48
expressionWithoutCascade:
assignableExpression assignmentOperator expressionWithoutCas-
cade |
conditionalExpression |
throwExpressionWithoutCascade
;
expressionList:
expression (‘, ’ expression)*
;
primary:
thisExpression |
super unconditionalAssignableSelector |
functionExpression |
literal |
identifier |
newExpression |
new type ‘#’ (‘.’ identifier)? |
constObjectExpression |
‘(’ expression ‘)’
;
• c1 and c2 are constant lists that are defined to be identical in the specifi-
cation of literal list expressions (16.7), OR
• c1 and c2 are constant maps that are defined to be identical in the speci-
fication of literal map expressions (16.8), OR
• c1 and c2 are constant objects of the same class C and each member field
of c1 is identical to the corresponding field of c2 . OR
• c1 and c2 are the same object.
The definition of identity for doubles differs from that of equality in that a NaN
is identical to itself, and that negative and positive zero are distinct.
The definition of equality for doubles is dictated by the IEEE 754 standard,
which posits that NaNs do not obey the law of reflexivity. Given that hardware
implements these rules, it is necessary to support them for reasons of efficiency.
The definition of identity is not constrained in the same way. Instead, it
assumes that bit-identical doubles are identical.
The rules for identity make it impossible for a Dart programmer to observe
whether a boolean or numerical value is boxed or unboxed.
• An expression of the form e1 ?e2 :e3 where e1 , e2 and e3 are constant ex-
pressions and e1 evaluates to a boolean value.
• An expression of the form e.length where e is a constant expression that
evaluates to a string value.
Dart Programming Language Specification 51
literal:
nullLiteral |
booleanLiteral |
numericLiteral |
stringLiteral |
symbolLiteral |
mapLiteral |
listLiteral
;
nullLiteral:
null
;
The null object is the sole instance of the built-in class Null. Attempting to
instantiate Null causes a run-time error. It is a compile-time error for a class to
attempt to extend, mix in or implement Null. Invoking a method on null yields
a NoSuchMethodError unless the method is explicitly implemented by class Null.
The static type of null is ⊥.
The decision to use ⊥ instead of Null allows null to be be assigned everywhere
without complaint by the static checker.
numericLiteral:
NUMBER |
HEX NUMBER
;
Dart Programming Language Specification 53
NUMBER:
DIGIT+ (‘.’ DIGIT+)? EXPONENT? |
‘.’ DIGIT+ EXPONENT?
;
EXPONENT:
(‘e’ | ‘E’) (’+’ | ‘-‘)? DIGIT+
;
HEX NUMBER:
‘0x’ HEX DIGIT+ |
‘0X’ HEX DIGIT+
;
HEX DIGIT:
‘a’..’f’ |
‘A’..’F’ |
DIGIT
;
If a numeric literal begins with the prefix ‘0x’ or ‘0X’, it denotes the hex-
adecimal integer represented by the part of the literal following ‘0x’ (respectively
‘0X’). Otherwise, if the numeric literal does not include a decimal point it de-
notes a decimal integer. Otherwise, the numeric literal denotes a 64 bit double
precision floating point number as specified by the IEEE 754 standard.
In principle, the range of integers supported by a Dart implementations is
unlimited. In practice, it is limited by available memory. Implementations may
also be limited by other considerations.
For example, implementations may choose to limit the range to facilitate ef-
ficient compilation to Javascript. These limitations should be relaxed as soon as
technologically feasible.
It is a compile-time error for a class to attempt to extend, mix in or imple-
ment int. It is a compile-time error for a class to attempt to extend, mix in or
implement double. It is a compile-time error for any type other than the types
int and double to attempt to extend, mix in or implement num.
An integer literal is either a hexadecimal integer literal or a decimal integer
literal. Invoking the getter runtimeType on an integer literal returns the Type
object that is the value of the expression int. The static type of an integer literal
is int.
A literal double is a numeric literal that is not an integer literal. Invoking
the getter runtimeType on a literal double returns the Type object that is the
value of the expression double. The static type of a literal double is double.
The reserved words true and false denote objects that represent the boolean
values true and false respectively. They are the boolean literals.
booleanLiteral:
true |
false
;
Both true and false implement the built-in class bool. It is a compile-time
error for a class to attempt to extend, mix in or implement bool.
It follows that the two boolean literals are the only two instances of bool.
Invoking the getter runtimeType on a boolean literal returns the Type object
that is the value of the expression bool. The static type of a boolean literal is
bool.
stringLiteral:
(multilineString | singleLineString)+
;
singleLineString:
‘”’ stringContentDQ* ‘”’ |
‘’’ stringContentSQ* ‘’’ |
‘r’ ‘’’ (˜( ‘’’ | NEWLINE ))* ‘’’ |
‘r’ ‘”’ (˜( ‘”’ | NEWLINE ))* ‘”’
;
’Imagine this is a very long string that does not fit on a line. What shall we do? ’
’Oh what shall we do? ’
’We shall split it into pieces ’
’like so’.
multilineString:
‘"""’ stringContentTDQ* ‘"""’ |
‘’’’’ stringContentTSQ* ‘’’’’ |
‘r’ ‘"""’ (˜ ‘"""’)* ‘"""’ |
‘r’ ‘’’’’ (˜ ‘’’’’)* ‘’’’’
;
ESCAPE SEQUENCE:
‘\ n’ |
‘\ r’ |
‘\ f’ |
‘\ b’ |
‘\ t’ |
‘\ v’ |
‘\ x’ HEX DIGIT HEX DIGIT |
‘\ u’ HEX DIGIT HEX DIGIT HEX DIGIT HEX DIGIT |
‘\ u{’ HEX DIGIT SEQUENCE ‘}’
;
stringContentDQ:
˜( ‘\’ | ‘"’ | ‘$’ | NEWLINE ) |
‘\’ ˜( NEWLINE ) |
stringInterpolation
;
stringContentSQ:
˜( ‘\’ | ‘’’ | ‘$’ | NEWLINE ) |
‘\’ ˜( NEWLINE ) |
stringInterpolation
;
stringContentTDQ:
˜( ‘\’ | ‘"""’ | ‘$’) |
stringInterpolation
;
stringContentTSQ:
˜( ‘\’ | ‘’’’’ | ‘$’) |
stringInterpolation
Dart Programming Language Specification 58
NEWLINE:
\n|
\r
;
stringInterpolation:
‘$’ IDENTIFIER NO DOLLAR |
‘$’ ‘{’ expression ‘}’
;
The reader will note that the expression inside the interpolation could itself
include strings, which could again be interpolated recursively.
An unescaped $ character in a string signifies the beginning of an interpo-
lated expression. The $ sign may be followed by either:
• A single identifier id that must not contain the $ character.
• An expression e delimited by curly braces.
The form $id is equivalent to the form ${id}. An interpolated string
‘s1 ${e}s2 ’ is equivalent to the concatenation of the strings ‘s1 ’, e.toString()
and ‘s2 ’. Likewise an interpolated string “s1 ${e}s2 ” is equivalent to the con-
catenation of the strings “s1 ”, e.toString() and “s2 ”.
symbolLiteral:
‘#’ (operator | (identifier (‘.’ identifier)*))
;
Dart Programming Language Specification 59
A list may contain zero or more objects. The number of elements in a list
is its size. A list has an associated set of indices. An empty list has an empty
set of indices. A non-empty list has the index set {0 . . . n − 1} where n is the
size of the list. It is a runtime error to attempt to access a list using an index
that is not a member of its set of indices.
If a list literal begins with the reserved word const, it is a constant list literal
which is a compile-time constant (16.1) and therefore evaluated at compile-time.
Otherwise, it is a run-time list literal and it is evaluated at run-time. Only run-
time list literals can be mutated after they are created. Attempting to mutate
a constant list literal will result in a dynamic error.
It is a compile-time error if an element of a constant list literal is not a
compile-time constant. It is a compile-time error if the type argument of a
constant list literal includes a type parameter. The binding of a type parameter
is not known at compile-time, so we cannot use type parameters inside compile-
time constants.
Dart Programming Language Specification 60
The value of a constant list literal const < E > [e1 . . . en ] is an object a
whose class implements the built-in class List < E >. The ith element of a is
vi+1 , where vi is the value of the compile-time expression ei . The value of a
constant list literal const [e1 . . . en ] is defined as the value of the constant list
literal const< dynamic > [e1 . . . en ].
Let list1 = const < V > [e11 . . . e1n ] and list2 = const < U > [e21 . . . e2n ]
be two constant list literals and let the elements of list1 and list2 evaluate to
o11 . . . o1n and o21 . . . o2n respectively. Iff identical(o1i , o2i ) for i ∈ 1..n and
V = U then identical(list1 , list2 ).
In other words, constant list literals are canonicalized.
A run-time list literal < E > [e1 . . . en ] is evaluated as follows:
• The operator []= is invoked on a with first argument i and second argu-
ment oi+1 , 0 ≤ i < n.
• The result of the evaluation is a.
Note that this document does not specify an order in which the elements are
set. This allows for parallel assignments into the list if an implementation so desires.
The order can only be observed in checked mode (and may not be relied upon): if
element i is not a subtype of the element type of the list, a dynamic type error will
occur when a[i] is assigned oi−1 .
A runtime list literal [e1 . . . en ] is evaluated as < dynamic > [e1 . . . en ].
There is no restriction precluding nesting of list literals. It follows from the
rules above that < List < int >> [[1, 2, 3], [4, 5, 6]] is a list with type parameter
List < int >, containing two lists with type parameter dynamic.
The static type of a list literal of the form const< E > [e1 . . . en ] or the
form < E > [e1 . . . en ] is List < E >. The static type a list literal of the form
const [e1 . . . en ] or the form [e1 . . . en ] is List < dynamic >.
It is tempting to assume that the type of the list literal would be computed
based on the types of its elements. However, for mutable lists this may be unwar-
ranted. Even for constant lists, we found this behavior to be problematic. Since
compile-time is often actually runtime, the runtime system must be able to per-
form a complex least upper bound computation to determine a reasonably precise
type. It is better to leave this task to a tool in the IDE. It is also much more
uniform (and therefore predictable and understandable) to insist that whenever
types are unspecified they are assumed to be the unknown type dynamic.
mapLiteral:
const? typeArguments? ‘{’ (mapLiteralEntry (‘, ’ mapLitera-
lEntry)* ‘, ’?)? ‘}’
;
mapLiteralEntry:
expression ‘:’ expression
;
A map literal consists of zero or more entries. Each entry has a key and a
value. Each key and each value is denoted by an expression.
If a map literal begins with the reserved word const, it is a constant map lit-
eral which is a compile-time constant (16.1) and therefore evaluated at compile-
time. Otherwise, it is a run-time map literal and it is evaluated at run-time.
Only run-time map literals can be mutated after they are created. Attempting
to mutate a constant map literal will result in a dynamic error.
It is a compile-time error if either a key or a value of an entry in a constant
map literal is not a compile-time constant. It is a compile-time error if the key
of an entry in a constant map literal is an instance of a class that implements
the operator == unless the key is a string, an integer, a literal symbol or the
result of invoking a constant constructor of class Symbol. It is a compile-time
error if the type arguments of a constant map literal include a type parameter.
The value of a constant map literal const< K, V > {k1 : e1 . . . kn : en } is an
object m whose class implements the built-in class M ap < K, V >. The entries
of m are ui : vi , i ∈ 1..n, where ui is the value of the compile-time expression ki
and vi is the value of the compile-time expression ei . The value of a constant
map literal const {k1 : e1 . . . kn : en } is defined as the value of a constant map
literal const < dynamic, dynamic > {k1 : e1 . . . kn : en }.
Let map1 = const< K, V > {k11 : e11 . . . k1n : e1n } and map2 = const<
J, U > {k21 : e21 . . . k2n : e2n } be two constant map literals. Let the keys of
map1 and map2 evaluate to s11 . . . s1n and s21 . . . s2n respectively, and let the
elements of map1 and map2 evaluate to o11 . . . o1n and o21 . . . o2n respectively.
Iff identical(o1i , o2i ) and identical(s1i , s2i ) for i ∈ 1..n, and K = J, V = U then
identical(map1 , map2 ).
In other words, constant map literals are canonicalized.
A runtime map literal < K, V > {k1 : e1 . . . kn : en } is evaluated as follows:
• First, the expression ki is evaluated yielding object ui , the ei is vaulted
yielding object oi , for i ∈ 1..n in left to right order, yielding objects
u1 , o1 . . . un , on .
• A fresh instance (10.6.1) m whose class implements the built-in class
M ap < K, V > is allocated.
• The operator []= is invoked on m with first argument ui and second ar-
gument oi , i ∈ 1..n.
Dart Programming Language Specification 62
throwExpression:
throw expression
;
throwExpressionWithoutCascade:
throw expressionWithoutCascade
;
The current exception is the last exception raised and not subsequently
caught at a given moment during runtime.
Evaluation of a throw expression of the form throw e; proceeds as follows:
The expression e is evaluated yielding a value v.
There is no requirement that the expression e evaluate to a special kind of
exception or error object.
If e evaluates to null (16.2), then a NullThrownError is thrown. Otherwise
the current exception is set to v and the current return value (17.12) becomes
undefined.
The current exception and the current return value must never be simul-
taneously defined, as they represent mutually exclusive options for exiting the
current function.
Let f be the immediately enclosing function.
If f is synchronous (9), control is transferred to the nearest dynamically
enclosing exception handler.
If f is marked sync* then a dynamically enclosing exception handler encloses
the call to moveNext() that initiated the evaluation of the throw expression.
If f is asynchronous then if there is a dynamically enclosing exception
Dart Programming Language Specification 63
functionExpression:
formalParameterList functionBody
;
Here, a naive definition of f latten diverges; there is not even a fixed point.
A more sophisticated definition of f latten is possible, but the existing rule deals
with most realistic examples while remaining relatively simple to understand.
The static type of a function literal of the form
(T1 a1 , . . . , Tn an , {Tn+1 xn+1 : d1 , . . . , Tn+k xn+k : dk }) => e is
(T1 . . . , Tn , {Tn+1 xn+1 , . . . , Tn+k xn+k }) → T0 , where T0 is the static type
of e.
The static type of a function literal of the form
(T1 a1 , . . . , Tn an , {Tn+1 xn+1 : d1 , . . . , Tn+k xn+k : dk }) async => e
is (T1 . . . , Tn , {Tn+1 xn+1 , . . . , Tn+k xn+k }) → F uture < f latten(T0 ) >,
where T0 is the static type of e.
The static type of a function literal of the form
(T1 a1 , . . . , Tn an , [Tn+1 xn+1 = d1 , . . . , Tn+k xn+k = dk ]){s}
is (T1 . . . , Tn , [Tn+1 xn+1 , . . . , Tn+k xn+k ]) → dynamic.
The static type of a function literal of the form
(T1 a1 , . . . , Tn an , [Tn+1 xn+1 = d1 , . . . , Tn+k xn+k = dk ]) async {s} is
(T1 . . . , Tn , [Tn+1 xn+1 , . . . , Tn+k xn+k ]) → F uture.
The static type of a function literal of the form
(T1 a1 , . . . , Tn an , [Tn+1 xn+1 = d1 , . . . , Tn+k xn+k = dk ]) async ∗ {s} is
(T1 . . . , Tn , [Tn+1 xn+1 , . . . , Tn+k xn+k ]) → Stream.
The static type of a function literal of the form
(T1 a1 , . . . , Tn an , [Tn+1 xn+1 = d1 , . . . , Tn+k xn+k = dk ]) sync ∗ {s} is
(T1 . . . , Tn , [Tn+1 xn+1 , . . . , Tn+k xn+k ]) → Iterable.
The static type of a function literal of the form
(T1 a1 , . . . , Tn an , [Tn+1 xn+1 = d1 , . . . , Tn+k xn+k = dk ]){s}
is (T1 . . . , Tn , [Tn+1 xn+1 , . . . , Tn+k xn+k ]) → dynamic.
The static type of a function literal of the form
(T1 a1 , . . . , Tn an , {Tn+1 xn+1 : d1 , . . . , Tn+k xn+k : dk }) async {s}
is (T1 . . . , Tn , {Tn+1 xn+1 , . . . , Tn+k xn+k }) → F uture.
The static type of a function literal of the form
(T1 a1 , . . . , Tn an , {Tn+1 xn+1 : d1 , . . . , Tn+k xn+k : dk }) async ∗ {s}
is (T1 . . . , Tn , {Tn+1 xn+1 , . . . , Tn+k xn+k }) → Stream.
The static type of a function literal of the form
(T1 a1 , . . . , Tn an , {Tn+1 xn+1 : d1 , . . . , Tn+k xn+k : dk }) sync ∗ {s}
is (T1 . . . , Tn , {Tn+1 xn+1 , . . . , Tn+k xn+k }) → Iterable.
In all of the above cases, whenever Ti , 1 ≤ i ≤ n + k, is not specified, it is
considered to have been specified as dynamic.
The reserved word this denotes the target of the current instance member
invocation.
thisExpression:
this
Dart Programming Language Specification 65
The static type of this is the interface of the immediately enclosing class.
We do not support self-types at this point.
It is a compile-time error if this appears, implicitly or explicitly, in a top-
level function or variable initializer, in a factory constructor, or in a static
method or variable initializer, or in the initializer of an instance variable.
newExpression:
new type (‘.’ identifier)? arguments
;
constObjectExpression:
const type (’.’ identifier)? arguments
;
arguments:
‘(’ argumentList? ‘)’
;
argumentList:
namedArgument (‘, ’ namedArgument)* |
expressionList (‘, ’ namedArgument)*
;
namedArgument:
label expression
;
by o.m(a1 , . . . , an , xn+1 : an+1 , . . . , xn+k : an+k ) are also generated in the case
of o?.m(a1 , . . . , an , xn+1 : an+1 , . . . , xn+k : an+k ).
An unconditional ordinary method invocation i has the form
o.m(a1 , . . . , an , xn+1 : an+1 , . . . , xn+k : an+k ).
Evaluation of an unconditional ordinary method invocation i of the form
o.m(a1 , . . . , an , xn+1 : an+1 , . . . , xn+k : an+k )
proceeds as follows:
First, the expression o is evaluated to a value vo . Next, the argument list
(a1 , . . . , an , xn+1 : an+1 , . . . , xn+k : an+k ) is evaluated yielding actual argument
objects o1 , . . . , on+k . Let f be the result of looking up (16.15.1) method m in
vo with respect to the current library L.
Let p1 . . . ph be the required parameters of f , let p1 . . . pm be the positional
parameters of f and let ph+1 , . . . , ph+l be the optional parameters declared by
f.
We have an argument list consisting of n positional arguments and k named
arguments. We have a function with h required parameters and l optional parame-
ters. The number of positional arguments must be at least as large as the number
of required parameters, and no larger than the number of positional parameters. All
named arguments must have a corresponding named parameter.
If n < h, or n > m, the method lookup has failed. Furthermore, each
xi , n + 1 ≤ i ≤ n + k, must have a corresponding named parameter in the set
{pm+1 , . . . , ph+l } or the method lookup also fails. If vo is an instance of Type
but o is not a constant type literal, then if m is a method that forwards (9.1) to
a static method, method lookup fails. Otherwise method lookup has succeeded.
If the method lookup succeeded, the body of f is executed with respect to
the bindings that resulted from the evaluation of the argument list, and with
this bound to vo . The value of i is the value returned after f is executed.
If the method lookup has failed, then let g be the result of looking up getter
(16.15.2) m in vo with respect to L. If vo is an instance of Type but o is not a
constant type literal, then if g is a getter that forwards to a static getter, getter
lookup fails. If the getter lookup succeeded, let vg be the value of the getter invo-
cation o.m. Then the value of i is the result of invoking the static method Func-
tion.apply() with arguments v.g, [o1 , . . . , on ], {xn+1 : on+1 , . . . , xn+k : on+k }.
If getter lookup has also failed, then a new instance im of the predefined
class Invocation is created, such that :
• im.isMethod evaluates to true.
• im.memberName evaluates to the symbol m.
• im.positionalArguments evaluates to an immutable list with the same values
as [o1 , . . . , on ].
• im.namedArguments evaluates to an immutable map with the same keys
and values as {xn+1 : on+1 , . . . , xn+k : on+k }.
Then the method noSuchMethod() is looked up in vo and invoked with
argument im, and the result of this invocation is the result of evaluating i.
Dart Programming Language Specification 76
cascadeSection:
‘..’ (cascadeSelector arguments*) (assignableSelector arguments*)*
(assignmentOperator expressionWithoutCascade)?
;
cascadeSelector:
‘[’ expression ‘]’ |
identifier
;
If getter lookup has also failed, then a new instance im of the predefined
class Invocation is created, such that :
• im.isMethod evaluates to true.
• im.memberName evaluates to the symbol m.
• im.positionalArguments evaluates to an immutable list with the same values
as [o1 , . . . , on ].
• im.namedArguments evaluates to an immutable map with the same keys
and values as {xn+1 : on+1 , . . . , xn+k : on+k }.
Then the method noSuchMethod() is looked up in Sdynamic and invoked on this
with argument im, and the result of this invocation is the result of evaluating i.
However, if the implementation found cannot be invoked with a single positional
argument, the implementation of noSuchMethod() in class Object is invoked on
this with argument im0 , where im0 is an instance of Invocation such that :
• im’.isMethod evaluates to true.
• im’.memberName evaluates to #noSuchMethod.
• im’.positionalArguments evaluates to an immutable list whose sole element
is im.
• im’.namedArguments evaluates to the value of const {}.
and the result of this latter invocation is the result of evaluating i.
It is a compile-time error if a super method invocation occurs in a top-level
function or variable initializer, in an instance variable initializer or initializer
list, in class Object, in a factory constructor or in a static method or variable
initializer.
Let Sstatic be the superclass of the immediately enclosing class. It is a static
type warning if Sstatic does not have an accessible (6.2) instance member named
m unless Sstatic or a superinterface of Sstatic is annotated with an annotation
denoting a constant identical to the constant @proxy defined in dart:core. If
Sstatic .m exists, it is a static type warning if the type F of Sstatic .m may not
be assigned to a function type. If Sstatic .m does not exist, or if F is not a
function type, the static type of i is dynamic; otherwise the static type of i is
the declared return type of F .
}
if f is named m and has required parameters r1 , . . . , rn , and optional
positional parameters p1 , . . . , pk with defaults d1 , . . . , dk .
Except that iff identical(o1 , o2 ) then o1 #m == o2 #m, o1 .m == o2 .m, o1 #m
== o2 .m and o1 .m == o2 #m.
The closurization of getter f on object o is defined to be equivalent to
(){return u.m;} if f is named m, except that iff identical(o1 , o2 ) then o1 #m ==
o2 #m.
The closurization of setter f on object o is defined to be equivalent to
(a){return u.m = a;} if f is named m =, except that iff identical(o1 , o2 ) then
o1 #m = == o2 #m =.
There is no guarantee that identical(o1 .m, o2 .m). Dart implementations are not
required to canonicalize these or any other closures.
The special treatment of equality in this case facilitates the use of extracted
property functions in APIs where callbacks such as event listeners must often be
registered and later unregistered. A common example is the DOM API in web
browsers.
Observations:
One cannot closurize a constructor, getter or a setter via the dot based syntax.
One must use the # based form. One can tell whether one implemented a property
via a method or via a field/getter, which means that one has to plan ahead as to
what construct to use, and that choice is reflected in the interface of the class.
• (r1 , . . . , rn , {p1 : d1 , . . . , pk : dk }) {
return new T.m(r1 , . . . , rn , p1 : p1 , . . . , pk : pk );
}
if f is a named constructor with name m that has required parameters
r1 , . . . , rn , and named parameters p1 , . . . , pk with defaults d1 , . . . , dk .
• (r1 , . . . , rn , [p1 = d1 , . . . , pk = dk ]){
return new T.m(r1 , . . . , rn , p1 , . . . , pk );
}
if f is a named constructor with name m that has required parame-
ters r1 , . . . , rn , and optional positional parameters p1 , . . . , pk with defaults
d1 , . . . , dk .
Except that iff identical(T1 , T2 ) then new T1 #m == new T2 #m.
The above implies that for non-parameterized types, one can rely on the equality
of closures resulting from closurization on the “same” type. For parameterized types,
one cannot, since there is no requirement to canonicalize them.
Dart Programming Language Specification 86
• (a){return super op a;} if f is named op and op is one of <, >, <=, >=,
==, -, +, /, ˜/, *, %, |, ˆ, &, <<, >>.
• (r1 , . . . , rn , {p1 : d1 , . . . , pk : dk }) {
return super.m(r1 , . . . , rn , p1 : p1 , . . . , pk : pk );
}
if f is named m and has required parameters r1 , . . . , rn , and named pa-
rameters p1 , . . . , pk with defaults d1 , . . . , dk .
Except that iff two closurizations were created by code declared in the same
class with identical bindings of this then super1 #m == super2 #m, super1 .m
== super2 .m, super1 #m == super2 .m and super1 .m == super2 #m.
The closurization of getter f with respect to superclass S is defined to be
equivalent to (){return super.m;} if f is named m, except that iff two closur-
izations were created by code declared in the same class with identical bindings
of this then super1 #m == super2 #m.
The closurization of setter f with respect to superclass S is defined to be
equivalent to (a){return super.m = a;} if f is named m =, except that iff two
closurizations were created by code declared in the same class with identical
bindings of this then super1 #m = == super2 #m =.
assignmentOperator:
‘=’ |
compoundAssignmentOperator
;
of a is the static type of e2 . Let T be the static type of e1 and let y be a fresh
variable of type T . Exactly the same static warnings that would be caused by
y.v = e2 are also generated in the case of e1 ?.v = e2 .
Evaluation of an assignment of the form e1 .v = e2 proceeds as follows:
The expression e1 is evaluated to an object o1 . Then, the expression e2 is
evaluated to an object o2 . Then, the setter v = is looked up (16.15.2) in o1 with
respect to the current library. If o1 is an instance of Type but e1 is not a constant
type literal, then if v = is a setter that forwards (9.1) to a static setter, setter
lookup fails. Otherwise, the body of v = is executed with its formal parameter
bound to o2 and this bound to o1 .
If the setter lookup has failed, then a new instance im of the predefined
class Invocation is created, such that :
• im.isSetter evaluates to true.
• im.memberName evaluates to the symbol v=.
• im.positionalArguments evaluates to an immutable list with the same values
as [o2 ].
• im.namedArguments evaluates to the value of const {}.
Then the method noSuchMethod() is looked up in o1 and invoked with
argument im. However, if the implementation found cannot be invoked with
a single positional argument, the implementation of noSuchMethod() in class
Object is invoked on o1 with argument im0 , where im0 is an instance of Invocation
such that :
• im’.isMethod evaluates to true.
• im’.memberName evaluates to #noSuchMethod.
static warnings that would be caused by z.v = e2 are also generated in the case
of e1 .v ??= e2 .
The static type of a compound assignment of the form e1 [e2 ] ??= e3 is the
least upper bound of the static type of e1 [e2 ] and the static type of e3 . Exactly
the same static warnings that would be caused by e1 [e2 ] = e3 are also generated
in the case of e1 [e2 ] ??= e3 .
The static type of a compound assignment of the form super.v ??= e is
the least upper bound of the static type of super.v and the static type of e.
Exactly the same static warnings that would be caused by super.v = e are also
generated in the case of super.v ??= e.
For any other valid operator op, a compound assignment of the form v
op =e is equivalent to v=v op e. A compound assignment of the form C.v op=e
is equivalent to C.v=C.v op e. A compound assignment of the form e1 .v op = e2
is equivalent to ((x) => x.v = x.v op e2 )(e1 ) where x is a variable that is not
used in e2 . A compound assignment of the form e1 [e2 ] op=e3 is equivalent to
((a, i) => a[i] = a[i] op e3 )(e1 , e2 ) where a and i are a variables that are not
used in e3 .
Evaluation of a compound assignment of the form e1 ?.v op = e2 is equivalent
to ((x) => x?.v = x.v op e2 )(e1 ) where x is a variable that is not used in e2 .
The static type of e1 ?.v op = e2 is the static type of e1 .v op e2 . Exactly the
same static warnings that would be caused by e1 .v op = e2 are also generated
in the case of e1 ?.v op = e2 .
A compound assignment of the form C?.v op = e2 is equivalent to the
expression C.v op = e2 .
compoundAssignmentOperator:
‘*=’ |
‘/=’ |
‘˜/=’ |
‘%=’ |
‘+=’ |
‘-=’ |
‘<<=’ |
‘>>=’ |
‘&=’ |
‘ˆ=’ |
‘|=’ |
‘??=’ |
conditionalExpression:
ifNullExpression (‘?’ expressionWithoutCascade ‘:’ expression-
WithoutCascade)?
;
ifNullExpression:
logicalOrExpression (‘??’ logicalOrExpression)*
The logical boolean expressions combine boolean objects using the boolean
conjunction and disjunction operators.
logicalOrExpression:
logicalAndExpression (‘||’ logicalAndExpression)*
;
Dart Programming Language Specification 93
logicalAndExpression:
equalityExpression (‘&&’ equalityExpression)*
;
equalityExpression:
relationalExpression (equalityOperator relationalExpression)? |
super equalityOperator relationalExpression
;
Dart Programming Language Specification 94
equalityOperator:
‘==’ |
‘!=’
;
relationalExpression:
bitwiseOrExpression (typeTest | typeCast | relationalOperator bit-
wiseOrExpression)? |
super relationalOperator bitwiseOrExpression
Dart Programming Language Specification 95
relationalOperator:
‘>=’ |
‘>’ |
‘<=’ |
‘<’
;
bitwiseOrExpression:
bitwiseXorExpression (‘|’ bitwiseXorExpression)* |
super (‘|’ bitwiseXorExpression)+
;
bitwiseXorExpression:
bitwiseAndExpression (‘ˆ’ bitwiseAndExpression)* |
super (‘ˆ’ bitwiseAndExpression)+
;
bitwiseAndExpression:
shiftExpression (‘&’ shiftExpression)* |
super (‘&’ shiftExpression)+
;
bitwiseOperator:
‘&’ |
‘ˆ’ |
‘|’
;
shiftExpression:
additiveExpression (shiftOperator additiveExpression)* |
super (shiftOperator additiveExpression)+
;
shiftOperator:
‘<<’ |
‘>>’
;
additiveExpression:
multiplicativeExpression (additiveOperator multiplicativeExpres-
sion)* |
super (additiveOperator multiplicativeExpression)+
;
additiveOperator:
‘+’ |
‘-’
Dart Programming Language Specification 97
multiplicativeExpression:
unaryExpression (multiplicativeOperator unaryExpression)* |
super (multiplicativeOperator unaryExpression)+
;
multiplicativeOperator:
‘*’ |
‘/’ |
‘%’ |
‘˜/’
;
of e2 is int, and double if the static type of e2 is double. The static type of an
expression e1 ˜/ e2 where e1 has static type int is int if the static type of e2 is
int.
prefixOperator:
minusOperator |
negationOperator |
tildeOperator
;
minusOperator:
‘-’ |
negationOperator:
‘!’ |
tildeOperator:
‘˜’
;
awaitExpression:
await unaryExpression
postfixExpression:
assignableExpression postfixOperator |
primary (selector* | ( ‘#’ ( (identifier ‘=’?) | operator)))
;
postfixOperator:
incrementOperator
;
selector:
assignableSelector |
Dart Programming Language Specification 100
arguments
;
incrementOperator:
‘++’ |
‘--’
;
Assignable expressions are expressions that can appear on the left hand
side of an assignment. This section describes how to evaluate these expressions
when they do not constitute the complete left hand side of an assignment.
Of course, if assignable expressions always appeared as the left hand side,
one would have no need for their value, and the rules for evaluating them would
be unnecessary. However, assignable expressions can be subexpressions of other
expressions and therefore must be evaluated.
assignableExpression:
primary (arguments* assignableSelector)+ |
super unconditionalAssignableSelector |
identifier
;
unconditionalAssignableSelector:
‘[’ expression ‘]’ |
‘.’ identifier
;
assignableSelector:
unconditionalAssignableSelector |
‘?.’ identifier
;
• An identifier.
• An invocation (possibly conditional) of a getter (10.2) or list access oper-
ator on an expression e.
• An invocation of a getter or list access operator on super.
identifier:
IDENTIFIER
;
IDENTIFIER NO DOLLAR:
IDENTIFIER START NO DOLLAR IDENTIFIER PART NO DOLLAR*
;
IDENTIFIER:
IDENTIFIER START IDENTIFIER PART*
;
BUILT IN IDENTIFIER:
abstract |
as |
deferred |
dynamic |
export |
external |
factory |
get |
implements |
import |
library |
operator |
part |
set |
static |
typedef
;
IDENTIFIER START:
IDENTIFIER START NO DOLLAR |
Dart Programming Language Specification 103
‘$’
;
IDENTIFIER PART:
IDENTIFIER START |
DIGIT
;
qualified:
identifier (‘.’ identifier)?
;
Note that if one declares a setter, we bind to the corresponding getter even if it
does not exist.
This prevents situations where one uses uncorrelated setters and getters.
The intent is to prevent errors when a getter in a surrounding scope is used
accidentally.
It is a static warning if an identifier expression id occurs inside a top level or
static function (be it function, method, getter, or setter) or variable initializer
and there is no declaration d with name id in the lexical scope enclosing the
expression.
typeTest:
isOperator type
;
isOperator:
is ‘!’?
;
typeCast:
asOperator type
;
asOperator:
as
;
17 Statements statements
Dart Programming Language Specification 107
statements:
statement*
;
statement:
label* nonLabelledStatement
;
nonLabelledStatement:
block |
localVariableDeclaration |
forStatement |
whileStatement |
doStatement |
switchStatement |
ifStatement |
rethrowStatement |
tryStatement |
breakStatement |
continueStatement |
returnStatement |
yieldStatement |
yieldEachStatement |
expressionStatement |
assertStatement |
localFunctionDeclaration
;
expressionStatement:
expression? ‘;’
Dart Programming Language Specification 108
localVariableDeclaration:
initializedVariableDeclaration ’;’
;
localFunctionDeclaration:
functionSignature functionBody
;
f(3); // illegal
f(x) => x > 0? x*f(x-1): 1; // recursion is legal
g1(x) => h(x, 1); // error: h is not declared yet
h(x, n) => x > 1? h(x-1, n*x): n; // again, recursion is fine
g2(x) => h(x, 1); // legal
p1(x) => q(x,x); // illegal
q1(a, b) => a > 0 ? p1(a-1): b; // fine
q2(a, b) => a > 0 ? p2(a-1): b; // illegal
p1(x) => q2(x,x); // fine
}
There is no way to write a pair of mutually recursive local functions, because
one always has to come before the other is declared. These cases are quite rare,
and can always be managed by defining a pair of variables first, then assigning them
appropriate closures:
top2() { // a top level function
var p, q;
p = (x) => q(x,x);
q = (a, b) => a > 0 ? p(a-1): b;
}
The rules for local functions differ slightly from those for local variables in
that a function can be accessed within its declaration but a variable can only
be accessed after its declaration. This is because recursive functions are use-
ful whereas recursively defined variables are almost always errors. It therefore
makes sense to harmonize the rules for local functions with those for functions
in general rather than with the rules for local variables.
17.5 If if
ifStatement:
if ‘(’ expression ‘)’ statement ( else statement)?
;
forStatement:
await? for ‘(’ forLoopParts ‘)’ statement
;
forLoopParts:
forInitializerStatement expression? ‘;’ expressionList? |
declaredIdentifier in expression |
identifier in expression
;
forInitializerStatement:
localVariableDeclaration |
expression? ‘;’
;
The for statement has three forms - the traditional for loop and two forms
of the for-in statement - synchronous and asynchronous.
Dart Programming Language Specification 111
1. If this is the first iteration of the for loop, let v 0 be v. Otherwise, let v 0 be
the variable v 00 created in the previous execution of step 4.
2. The expression [v 0 /v]c is evaluated and subjected to boolean conversion
(16.4). If the result is false, the for loop completes. Otherwise, execution
continues at step 3.
3. The statement [v 0 /v]{s} is executed.
4. Let v 00 be a fresh variable. v 00 is bound to the value of v 0 .
5. The expression [v 00 /v]e is evaluated, and the process recurses at step 1.
The definition above is intended to prevent the common error where users
create a closure inside a for loop, intending to close over the current binding
of the loop variable, and find (usually after a painful process of debugging and
learning) that all the created closures have captured the same value - the one
current in the last iteration executed.
Instead, each iteration has its own distinct variable. The first iteration uses
the variable created by the initial declaration. The expression executed at the
end of each iteration uses a fresh variable v 00 , bound to the value of the current
iteration variable, and then modifies v 00 as required for the next iteration.
It is a static warning if the static type of c may not be assigned to bool.
17.8 Do do
doStatement:
do statement while ‘(’ expression ‘)’ ‘;’
;
switchCase:
label* case expression ‘:’ statements
;
defaultCase:
label* default ‘:’ statements
;
ignore the warning and run their code, a run time error will prevent the program
from misbehaving in hard-to-debug ways (at least with respect to this issue).
The sophistication of the analysis of fall-through is another issue. For now,
we have opted for a very straightforward syntactic requirement. There are obvi-
ously situations where code does not fall through, and yet does not conform to
these simple rules, e.g.:
switch (x) {
case 1: try { . . . return;} finally { . . . return;}
}
Very elaborate code in a case clause is probably bad style in any case, and
such code can always be refactored.
It is a static warning if all of the following conditions hold:
• The switch statement does not have a default clause.
• The static type of e is an enumerated typed with elements id1 , . . . , idn .
• The sets {e1 , . . . , ek } and {id1 , . . . , idn } are not the same.
In other words, a warning will be issued if a switch statement over an enum is
not exhaustive.
rethrowStatement:
rethrow ‘;’
;
dler.
The change in control may result in multiple functions terminating if these
functions do not catch the exception via a catch or finally clause, both of which
introduce a dynamically enclosing exception handler.
It is a compile-time error if a rethrow statement is not enclosed within an
on-catch clause.
tryStatement:
try block (onPart+ finallyPart? | finallyPart)
;
onPart:
catchPart block |
on type catchPart? block
;
catchPart:
catch ‘(’ identifier (‘, ’ identifier)? ‘)’
;
finallyPart:
finally block
;
scope CS in which final local variables specified by p1 and p2 are defined. The
statement s is enclosed within CS. The static type of p1 is T and the static
type of p2 is StackTrace.
An on-catch clause of the form on T catch (p1 ) s is equivalent to an on-
catch clause on T catch (p1 , p2 ) s where p2 is an identifier that does not occur
anywhere else in the program.
An on-catch clause of the form catch (p) s is equivalent to an on-catch
clause on dynamic catch (p) s. An on-catch clause of the form catch (p1 , p2 )
s is equivalent to an on-catch clause on dynamic catch (p1 , p2 ) s.
The active stack trace is an object whose toString() method produces a string
that is a record of exactly those function activations within the current isolate
that had not completed execution at the point where the current exception
(16.9) was thrown.
This implies that no synthetic function activations may be added to the trace,
nor may any source level activations be omitted. This means, for example, that
any inlining of functions done as an optimization must not be visible in the trace.
Similarly, any synthetic routines used by the implementation must not appear in the
trace.
Nothing is said about how any native function calls may be represented in the
trace.
Note that we say nothing about the identity of the stack trace, or what notion
of equality is defined for stack traces.
The term position should not be interpreted as a line number, but rather as
a precise position - the exact character index of the expression that raised the
exception.
A try statement try s1 on − catch1 . . . on − catchn finally sf defines an
exception handler h that executes as follows:
The on-catch clauses are examined in order, starting with catch1 , until
either an on-catch clause that matches the current exception (16.9) is found,
or the list of on-catch clauses has been exhausted. If an on-catch clause
on−catchk is found, then pk1 is bound to the current exception, pk2 , if declared,
is bound to the active stack trace, and then catchk is executed. If no on-catch
clause is found, the finally clause is executed. Then, execution resumes at the
end of the try statement.
A finally clause finally s defines an exception handler h that executes as
follows:
Let r be the current return value (17.12). Then the current return value
becomes undefined. Any open streams associated with any asynchronous for
loops (17.6.3) and yield-each (17.16.2) statements executing within the dynamic
scope of h are canceled, in the order of their nesting, innermost first.
Streams left open by for loops that were escaped for whatever reason would
be canceled at function termination, but it is best to cancel them as soon as
possible.
Then the finally clause is executed. Let m be the immediately enclosing
function. If r is defined then the current return value is set to r and then:
Dart Programming Language Specification 119
returnStatement:
return expression? ‘;’
;
Dart Programming Language Specification 120
Due to finally clauses, the precise behavior of return is a little more involved.
Whether the value a return statement is supposed to return is actually returned
depends on the behavior of any finally clauses in effect when executing the return.
A finally clause may choose to return another value, or throw an exception, or even
redirect control flow leading to other returns or throws. All a return statement really
does is set a value that is intended to be returned when the function terminates.
The current return value is a unique value specific to a given function
activation. It is undefined unless explicitly set in this specification.
Executing a return statement return e; proceeds as follows:
First the expression e is evaluated, producing an object o. Next:
• The current return value is set to o and the current exception (16.9) and
active stack trace (17.11) become undefined.
• Let c be the finally clause of the innermost enclosing try-finally statement
(17.11), if any. If c is defined, let h be the handler induced by c. If h is
defined, control is transferred to h.
The motivation for formulating return; in this way stems from the basic
requirement that all function invocations indeed return a value. Function in-
vocations are expressions, and we cannot rely on a mandatory typechecker to
always prohibit use of void functions in expressions. Hence, a return statement
must always return a value, even if no expression is specified.
The question then becomes, what value should a return statement return when
no return expression is given. In a generative constructor, it is obviously the
object being constructed (this). A void function is not expected to participate
in an expression, which is why it is marked void in the first place. Hence, this
situation is a mistake which should be detected as soon as possible. The static
rules help here, but if the code is executed, using null leads to fast failure, which
is desirable in this case. The same rationale applies for function bodies that do
not contain a return statement at all.
It is a static warning if a function contains both one or more explicit return
statements of the form return; and one or more return statements of the form
return e;.
label:
identifier ‘:’
;
The break statement consists of the reserved word break and an optional
label (17.13).
breakStatement:
break identifier? ‘;’
Dart Programming Language Specification 123
continueStatement:
continue identifier? ‘;’
;
• the body of f is marked async* and the type Stream<T> may not be
assigned to the declared return type of f .
• the body of f is marked sync* and the type Iterable<T> may not be
assigned to the declared return type of f .
yieldEachStatement:
yield* expression ‘;’
;
assertStatement:
assert ‘(’ conditionalExpression ‘)’ ‘;’
;
A Dart program consists of one or more libraries, and may be built out of
one or more compilation units. A compilation unit may be a library or a part
(18.3).
A library consists of (a possibly empty) set of imports, a set of exports, and
a set of top-level declarations. A top-level declaration is either a class (10), a
type alias declaration (19.3.1), a function (9) or a variable declaration (8). The
members of a library L are those top level declarations given within L.
topLevelDefinition:
classDefinition |
enumType |
typeAlias |
external? functionSignature ‘;’ |
external? getterSignature ‘;’ |
external? setterSignature ‘;’ |
functionSignature functionBody |
returnType? get identifier functionBody |
returnType? set identifier formalParameterList functionBody |
(final | const) type? staticFinalDeclarationList ‘;’ |
variableDeclaration ‘;’
;
getOrSet:
get |
set
;
libraryDefinition:
scriptTag? libraryName? importOrExport* partDirective* topLevelDef-
inition*
;
scriptTag:
‘#!’ (˜NEWLINE)* NEWLINE
;
libraryName:
metadata library identifier (‘.’ identifier)* ‘;’
;
importOrExport:
libraryImport |
libraryExport
Dart Programming Language Specification 128
importSpecification:
import uri (as identifier)? combinator* ‘;’ |
import uri deferred as identifier combinator* ‘;’
;
combinator:
show identifierList |
Dart Programming Language Specification 129
hide identifierList
;
identifierList:
identifier (, identifier)*
In other words, one can retry a deferred load after a network failure or because
a file is absent, but once one finds some content and loads it, one can no longer
reload.
We do not specify what value the future returned resolves to.
If I is an immediate import then, first
• If the URI that is the value of s1 has not yet been accessed by an import
or export (18.2) directive in the current isolate then the contents of the
URI are compiled to yield a library B. Because libraries may have mutually
recursive imports, care must be taken to avoid an infinite regress.
• Otherwise, the contents of the URI denoted by s1 have been compiled into
a library B within the current isolate.
Dart Programming Language Specification 131
• For every top level getter with the same name and signature as g named
id in N Sn , a corresponding getter that forwards to g.
• For every top level setter s with the same name and signature as named
id in N Sn , a corresponding setter that forwards to s.
The greatly increases the chance that a member can be added to a library
without breaking its importers.
A system library is a library that is part of the Dart implementation. Any
other library is a non-system library. If a name N is referenced by a library
L and N would be introduced into the top level scope of L by imports of two
libraries, L1 and L2 , and the exported namespace of L1 binds N to a declaration
originating in a system library:
A widely disseminated library should be given a name that will not conflict
with other such libraries. The preferred mechanism for this is using pub, the Dart
package manager, which provides a global namespace for libraries, and conventions
that leverage that namespace.
Note that no errors or warnings are given if one hides or shows a name that is
not in a namespace. This prevents situations where removing a name from a
library would cause breakage of a client library.
The dart core library dart:core is implicitly imported into every dart library
other than itself via an import clause of the form
import ‘dart:core’;
unless the importing library explicitly imports dart:core.
Any import of dart:core, even if restricted via show, hide or as, preempts the
automatic import.
It would be nice if there was nothing special about dart:core. However, its use
is pervasive, which leads to the decision to import it automatically. However,
some library L may wish to define entities with names used by dart:core (which
it can easily do, as the names declared by a library take precedence). Other
libraries may wish to use L and may want to use members of L that conflict
with the core library without having to use a prefix and without encountering
warnings. The above rule makes this possible, essentially canceling dart:core’s
special treatment by means of yet another special rule.
libraryExport:
metadata export uri combinator* ‘;’
;
• If the URI that is the value of s1 has not yet been accessed by an import
or export directive in the current isolate then the contents of the URI are
compiled to yield a library B.
• Otherwise, the contents of the URI denoted by s1 have been compiled into
a library B within the current isolate.
See the discussion in section 18.1 for the reasoning behind this rule.
We say that L re-exports library B, and also that L re-exports namespace
N Sn . When no confusion can arise, we may simply state that L re-exports B,
or that L re-exports N Sn .
It is a compile-time error if a name N is re-exported by a library L and N
is introduced into the export namespace of L by more than one export, unless
all exports refer to same declaration for the name N . It is a static warning
to export two different libraries with the same name unless their name is the
empty string.
A library may be divided into parts, each of which can be stored in a separate
location. A library identifies its parts by listing them via part directives.
A part directive specifies a URI where a Dart compilation unit that should
be incorporated into the current library may be found.
partDirective:
metadata part uri ‘;’
Dart Programming Language Specification 135
partHeader:
metadata part of identifier (‘.’ identifier)* ‘;’
;
partDeclaration:
partHeader topLevelDefinition* EOF
;
A part header begins with part of followed by the name of the library
the part belongs to. A part declaration consists of a part header followed by a
sequence of top-level declarations.
Compiling a part directive of the form part s; causes the Dart system to
attempt to compile the contents of the URI that is the value of s. The top-level
declarations at that URI are then compiled by the Dart compiler in the scope of
the current library. It is a compile-time error if the contents of the URI are not a
valid part declaration. It is a static warning if the referenced part declaration p
names a library other than the current library as the library to which p belongs.
uri:
stringLiteral
;
19 Types types
type:
typeName typeArguments?
;
typeName:
qualified
;
typeArguments:
’<’ typeList ’>’
;
typeList:
type (’, ’ type)*
;
type is then interpreted as dynamic by the static type checker and the runtime
unless explicitly specified otherwise.
This ensures that the developer is spared a series of cascading warnings as
the malformed type interacts with other types.
A type T is deferred iff it is of the form p.T where p is a deferred prefix. It
is a static warning to use a deferred type in a type annotation, type test, type
cast or as a type parameter. However, all other static warnings must be issued
under the assumption that all deferred libraries have successfully been loaded.
The static type system ascribes a static type to every expression. In some
cases, the types of local variables and formal parameters may be promoted from
their declared types based on control flow.
We say that a variable v is known to have type T whenever we allow the
type of v to be promoted. The exact circumstances when type promotion is
allowed are given in the relevant sections of the specification (16.22, 16.20 and
17.5).
Type promotion for a variable v is allowed only when we can deduce that
such promotion is valid based on an analysis of certain boolean expressions. In
such cases, we say that the boolean expression b shows that v has type T . As
a rule, for all variables v and types T , a boolean expression does not show that
v has type T . Those situations where an expression does show that a variable
has a type are mentioned explicitly in the relevant sections of this specification
(16.34 and 16.22).
typeAlias:
metadata typedef typeAliasBody
;
typeAliasBody:
functionTypeAlias
;
functionTypeAlias:
functionPrefix typeParameters? formalParameterList ’;’
;
functionPrefix:
returnType? identifier
;
2. The types of functions with named parameters. These have the general
form (T1 , . . . , Tn , {Tx1 x1 . . . , Txk xk }) → T .
A function type (T1 , . . . Tk , [Tk+1 . . . , Tn+m ]) → T is a subtype of the func-
tion type (S1 , . . . , Sk+j , [Sk+j+1 . . . , Sn ]) → S, if all of the following conditions
are met:
1. Either
• S is void, Or
• T ⇐⇒ S.
2. ∀i ∈ 1..n, Ti ⇐⇒ Si .
A function type (T1 , . . . Tn , {Tx1 x1 , . . . , Txk xk }) → T is a subtype of the
function type (S1 , . . . , Sn , {Sy1 y1 , . . . , Sym ym }) → S, if all of the following
conditions are met:
1. Either
• S is void, Or
• T ⇐⇒ S.
2. ∀i ∈ 1..n, Ti ⇐⇒ Si .
3. k ≥ m and yi ∈ {x1 , . . . , xk }, i ∈ 1..m.
4. For all yi ∈ {y1 , . . . , ym }, yi = xj ⇒ Tj ⇐⇒ Si
In addition, the following subtype rules apply:
(T1 , . . . , Tn , []) → T <: (T1 , . . . , Tn ) → T .
(T1 , . . . , Tn ) → T <: (T1 , . . . , Tn , {}) → T .
(T1 , . . . , Tn , {}) → T <: (T1 , . . . , Tn ) → T .
(T1 , . . . , Tn ) → T <: (T1 , . . . , Tn , []) → T .
The naive reader might conclude that, since it is not legal to declare a func-
tion with an empty optional parameter list, these rules are pointless. However,
they induce useful relationships between function types that declare no optional
parameters and those that do.
A function type T may be assigned to a function type S, written T ⇐⇒ S,
iff T <: S.
A function is always an instance of some class that implements the class
Function and implements a call method with the same signature as the function.
All function types are subtypes of Function. If a type I includes an instance
method named call, and the type of call is the function type F , then I is
considered to be more specific than F . It is a static warning if a concrete
class implements Function and does not have a concrete method named call
unless that class has an implementation of noSuchMethod() distinct from the
one declared in class Object.
A function type (T1 , . . . Tk , [Tk+1 . . . , Tn+m ]) → T is more specific than
the function type (S1 , . . . , Sk+j , [Sk+j+1 . . . , Sn ]) → S, if all of the following
conditions are met:
Dart Programming Language Specification 143
1. Either
• S is void, Or
• T << S.
2. ∀i ∈ 1..n, Ti << Si .
• S is void, Or
• T << S.
2. ∀i ∈ 1..n, Ti << Si .
3. k ≥ m and yi ∈ {x1 , . . . , xk }, i ∈ 1..m.
when a dynamic error is thrown at (2), the only way to keep running is rewriting
(2) into
(3) typedAPI(new G<String>());
This forces users to write type information in their client code just because
they are calling a typed API. We do not want to impose this on Dart program-
mers, some of which may be blissfully unaware of types in general, and genericity
in particular.
What of static checking? Surely we would want to flag (2) when users have
explicitly asked for static typechecking? Yes, but the reality is that the Dart static
checker is likely to be running in the background by default. Engineering teams
typically desire a “clean build” free of warnings and so the checker is designed
to be extremely charitable. Other tools can interpret the type information more
aggressively and warn about violations of conventional (and sound) static type
discipline.
The name dynamic denotes a Type object even though dynamic is not a
class.
The special type void may only be used as the return type of a function:
it is a compile-time error to use void in any other context.
For example, as a type argument, or as the type of a variable or parameter
Void is not an interface type.
The only subtype relations that pertain to void are therefore:
• void <: void (by reflexivity)
• ⊥ <: void (as bottom is a subtype of all types).
• void <: dynamic (as dynamic is a supertype of all types)
The analogous rules also hold for the << relation for similar reasons.
Hence, the static checker will issue warnings if one attempts to access a member
of the result of a void method invocation (even for members of null, such as ==).
Likewise, passing the result of a void method as a parameter or assigning it to a
variable will cause a warning unless the variable/formal parameter has type dynamic.
On the other hand, it is possible to return the result of a void method from
within a void method. One can also return null; or a value of type dynamic.
Returning any other result will cause a type warning. In checked mode, a dynamic
type error would arise if a non-null object was returned from a void method (since
no object has runtime type dynamic).
The name void does not denote a Type object.
It is syntacticly illegal to use void as an expression, and it would make no
sense to do so. Type objects reify the runtime types of instances. No instance
ever has type void.
Dart Programming Language Specification 145
upper bound of void and any type T 6= dynamic is void. The least upper
bound of ⊥ and any type T is T . Let U be a type variable with upper bound
B. The least upper bound of U and a type T 6= ⊥ is the least upper bound of
B and T .
The least upper bound relation is symmetric and reflexive.
The least upper bound of a function type and an interface type T is the
least upper bound of Function and T . Let F and G be function types. If F and
G differ in their number of required parameters, then the least upper bound of
F and G is Function. Otherwise:
• If
F = (T1 . . . Tr , [Tr+1 , . . . , Tn ]) −→ T0 ,
G = (S1 . . . Sr , [Sr+1 , . . . , Sk ]) −→ S0
where k ≤ n then the least upper bound of F and G is
(L1 . . . Lr , [Lr+1 , . . . , Lk ]) −→ L0
where Li is the least upper bound of Ti and Si , i ∈ 0..k.
• If
F = (T1 . . . Tr , [Tr+1 , . . . , Tn ]) −→ T0 ,
G = (S1 . . . Sr , {. . .}) −→ S0
then the least upper bound of F and G is
(L1 . . . Lr ) −→ L0
where Li is the least upper bound of Ti and Si , i ∈ 0..r.
• If
F = (T1 . . . Tr , {Tr+1 pr+1 , . . . , Tf pf }) −→ T0 ,
G = (S1 . . . Sr , {Sr+1 qr+1 , . . . , Sg qg }) −→ S0
then let {xm , . . . xn } = {pr+1 , . . . , pf } ∩ {qr+1 , . . . , qg } and let Xj be the
least upper bound of the types of xj in F and G, j ∈ m..n. Then the least
upper bound of F and G is
(L1 . . . Lr , {Xm xm , . . . , Xn xn }) −→ L0
where Li is the least upper bound of Ti and Si , i ∈ 0..r
20 Reference reference
LETTER:
‘a’ .. ‘z’ |
‘A’ ..‘Z’
;
DIGIT:
‘0’ .. ‘9’
;
WHITESPACE:
(‘\t’ | ‘ ’ | NEWLINE)+
;
Comments are sections of program text that are used for documentation.
Dart supports both single-line and multi-line comments. A single line com-
ment begins with the token //. Everything between // and the end of line
must be ignored by the Dart compiler unless the comment is a documentation
comment. .
A multi-line comment begins with the token /* and ends with the token */.
Everything between /* and */ must be ignored by the Dart compiler unless the
comment is a documentation comment. Comments may nest.
Documentation comments are comments that begin with the tokens ///
or /**. Documentation comments are intended to be processed by a tool that
produces human readable documentation.
The scope of a documentation comment immediately preceding the decla-
Dart Programming Language Specification 148
• The names of type variables are short (preferably single letter). Examples: T,
S, K, V , E.
• The names of libraries or library prefixes never use upper case letters. If they
consist of multiple words, those words are separated by underscores. Example:
my favorite library.
© Ecma International 2015