Google Dart Language Specification VI – Statemets, scripts and types

< PREV: Google Dart Language Specification III

Google Dart Language Specification IV – Statemets, scripts and types

[The current version (0.05) was released November 14, 2011. See the specification’s change log (section 1.2) for a list of differences between versions

Statements

statements:
statement*
;

statement:
label* nonLabelledStatement
;

nonLabelledStatement:
block
| initializedVariableDeclaration ‘;’
| forStatement
| whileStatement
| doStatement
| switchStatement
| ifStatement
| tryStatement
| breakStatement
| continueStatement
| returnStatement
| throwStatement
| expressionStatement
| assertStatement
| functionSignature functionBody
;

Blocks

A block statement supports sequencing of code.

Execution of a block statement {s1 … sn} proceeds as follows:

For i = 1 .. n, si is executed.

Expression Statements

An expression statement consists of an expression.

expressionStatement:
expression? ‘;’

Execution of an expression statement e; proceeds by evaluating e.

Variable Declaration

A variable declaration statement declares a new local variable.

A variable declaration statement T id; or T id = e; introduces a new variable id with static type T into the innermost enclosing scope. A variable declaration statement var id; or var id = e; introduces a new variable named id with static type Dynamic into the innermost enclosing scope.
In all cases, iff the variable declaration is prefixed with the final modifier, the variable is marked as final.

Executing a variable declaration statement T id = e; is equivalent to evaluating the assignment expression id = e, except that the assignment is considered legal even if the variable is final.

However, it is still illegal to assign to a final variable from within its initializer.

A variable declaration statement of the form T id; is equivalent to T id = null;.

This holds regardless of the type T. For example, int i; does not cause i to be initialized to zero. Instead, i is initialized to null, just as if we had written var i; or Object i; or Collection<String> i;
To do otherwise would undermine the optionally typed nature of Dart, causing type annotations to modify program behavior.

If

The if statement allows for conditional execution of statements.

ifStatement:
if ‘(‘ expression ‘)’ statement (else statement)?
;

Execution of an if statement of the form if(b) s1 else s2 proceeds as follows:

First, the expression b is evaluated to an object o. In checked mode, it is a dynamic type error if o is not of type bool. Otherwise, o is then subjected to boolean conversion, producing an object r. If r is true, then the statement s1 is executed, otherwise statement s2 is executed.

It is a static type warning if the type of the expression b may not be assigned to bool.

An if statement of the form if (b) s1 is equivalent to the if statement if(b) s1 else {}.

For

The for statement supports iteration.

forStatement:
for ‘(‘ forLoopParts ‘)’ statement
;

forLoopParts:
forInitializerStatement expression? ‘;’ expressionList?
| declaredIdentifier in expression
| identifier in expression
;

forInitializerStatement:
initializedVariableDeclaration ‘;’
| expression? ‘;’
;

The for statement has two forms – the traditional for loop and the foreach statement.

For Loop

Execution of a for statement of the form for (var v = e0 ; c e) s proceeds as follows:

If c is empty let c’ be true, otherwise let c’ be c.

First the variable declaration statement var v = e0 is executed. Then:

  1. If this is the first iteration of the for loop, let v’ be v. Otherwise, let v’ be the variable v’’ created in the previous execution of step 4.
  2. The expression [v’/v]c is evaluated and subjected to boolean conversion. If the result is false, the for loop completes. Otherwise, execution continues at step 3.
  3. The statement [v’/v]s is executed.
  4. Let v’’ be a fresh variable. v’’ is bound to the value of v’.
  5. The expression [v’’/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’’, bound on the value of the current iteration variable, and then modifies $v’’ as required for the next iteration.

Foreach

A for statement of the form for (finalVarOrType id in e) s is equivalent to the the following code:

var n0 = e.iterator();
while (n0.hasNext()) {
finalVarOrType id = n0.next();
s
}
where n0 is an identifier that does not occur anywhere in the program.

While

The while statement supports conditional iteration, where the condition is evaluated prior to the loop.

whileStatement:
while ‘(‘ expression ‘)’ statement
;

Execution of a while statement of the form while (e) s; proceeds as follows:

The expression e is evaluated to an object o. In checked mode, it is a dynamic type error if o is not of type bool. Otherwise, o is subjected to boolean conversion, producing an object r. If r is true, then s is executed and then the while statement is re-executed recursively. If r is false, execution of the while statement is complete.

It is a static type warning if the type of e may not be assigned to bool.

Do

The do statement supports conditional iteration, where the condition is evaluated after the loop.

doStatement:
do statement while ‘(‘ expression ‘)’ ‘;’
;

Execution of a do statement of the form do s while (e); proceeds as follows:

The statement s is executed. Then, the expression e is evaluated to an object o. In checked mode, it is a dynamic type error if o is not of type bool. Otherwise, o is then subjected to boolean conversion, producing an object r. If r is false, execution of the do statement is complete. If r is true, then the do statement is re-executed recursively.

It is a static type warning if the type of e not be assigned to bool.

Switch

The switch statement supports dispatching control among a large number of cases.

switchStatement:
switch ‘(‘ expression ‘)’ ‘{‘ switchCase* defaultCase? ‘}’
;

switchCase:
label? (case expression ‘:’)+ statements
;

defaultCase:
label? (case expression ‘:’)* default ‘:’ statements
;

Execution of a switch statement switch (e) { case e1: s1 … case en: sn default sn+1} proceeds as follows:

The statement var n = e; is evaluated, where n is a variable whose name is distinct from any other variable in the program. Next, the case clause case e1: s1 is executed if it exists. If case e1: s1 does not exist, then the default clause is executed by executing sn+1.

Execution of a case clause case ek: sk of a switch statement switch (e) { case e1: s1 … case en: sn default sn+1} proceeds as follows:

The expression n == ek is evaluated to a value v.

If v is false, or if sk is empty, the following case, case ek+1: sk+1 is executed if it exists. If case ek+1: sk+1 does not exist, then the default clause is executed by executing sn+1.
If v is true, the statement sequence sk is executed.

A switch statement switch (e) { case e1: s1 … case en: sn} is equivalent to the switch statement switch (e) { case e1: s1 … case en: sn default }

It is a static warning if the type of e is may not be assigned to the type of ek for all 1 <= k <= n.

Try

The try statement supports the definition of exception handling code in a structured way.

tryStatement:
try block (catchPart+ finallyPart? | finallyPart)
;

catchPart:
catch ‘(‘ simpleFormalParameter (‘,’ simpleFormalParameter)? ‘)’ block
;

finallyPart:
finally block
;

A try statement consists of a block statement, followed by at least one of:

  1. A set of catch clauses, each of which specifies one or two exception parameters and a block statement.
  2. A finally clause, which consists of a block statement.

A catch clause of one of the forms catch (T1 p1, T p2) s or catch (T1 p1, p2) matches an object o if o is null or if the type of o is a subtype of T. It is a compile-time error if T1 does not denote a type available in the lexical scope of the catch clause.

A catch clause of one of the forms catch (p1, T p2) s or catch (p1, p2) always matches an object o.

The definition below is an attempt to characterize exception handling without resorting to a normal/abrupt completion formulation. It has the advantage that one need not specify abrupt completion behavior for every compound statement. On the other hand, it is new different and needs more thought.

A try statement try s1 catch1 … catchn finally sf defines an exception handler h that executes as follows:

The catch clauses are examined in order, starting with catch1, until either a catch clause that matches the current exception is found, or the list of catch clauses has been exhausted. If a catch clause catchk is found, then pk1 is bound to the current exception, pk2 is bound to the current stack trace, and then catchk is executed. If no 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 by executing the finally clause. Then, execution resumes at the end of the try statement.

Execution of a catch clause catch (p1, p2) s of a try statement t proceeds as follows: The statement s is executed in the dynamic scope of the exception handler defined by the finally clause of t. Then, the current exception and current stack trace both become undefined.

Execution of a finally clause finally s of a try statement proceeds as follows:

The statement s is executed. Then, if the current exception is defined, control is transferred to the nearest dynamically enclosing exception handler.

Execution of a try statement of the form try s1 catch1 … catchn finally sf proceeds as follows:

The statement s1 is executed in the dynamic scope of the exception handler defined by the try statement. Then, the finally clause is executed.

Whether any of the catch clauses is executed depends on whether a matching exception has been raised by s1 (see the specification of the throw statement).

If s1 has raised an exception, it will transfer control to the try statement’s handler, which will examine the catch clauses in order for a match as specified above. If no matches are found, the handler will execute the finally clause.

If a matching catch was found, it will execute first, and then the finally clause will be executed.

If an exception is raised during execution of a catch clause, this will transfer control to the handler for the finally clause, causing the finally clause to execute in this case as well.

If no exception was raised, the finally clause is also executed. Execution of the finally clause could also raise an exception, which will cause transfer of control to the next enclosing handler.

Return

The return statement returns a result to the caller of a function.

returnStatement:
return expression? ‘;’
;

Executing a return statement

return e;

first causes evaluation of the expression e, producing an object o. Next, control is transferred to the caller of the current function activation, and the object o is provided to the caller as the result of the function call.

It is a static type warning if the type of e may not be assigned to the declared return type of the immediately enclosing function. It is a compile-time error if a return statement of the form return e; appears in a generative constructor.

It is quite easy to forget to add the factory prefix for a constructor, accidentally converting a factory into a generative constructor. The static checker may detect a type mismatch in some, but not all, of these cases. The rule above helps catch such errors, which can otherwise be very hard to recognize. There is no real downside to it, as returning a value from a generative constructor is meaningless.

Let f be the function immediately enclosing a return statement of the form return; It is a static warning if both of the following conditions hold:

  • f is not a generative constructor.
  • The return type of f may not be assigned to void.

Hence, a static warning will not be issued if f has no declared return type, since the return type would be Dynamic and Dynamic may be assigned to void. However, any function that declares a return type must return an expression explicitly.

This helps catch situations where users forget to return a value in a return statement.

A return statement with no expression, return; is executed by executing the statement return null; if it occurs inside a method, getter, setter or factory; otherwise, the return statement necessarily occurs inside a generative constructor, in which case it is executed by executing return this;.

Despite the fact that return; is executed as if by a return e;, it is important to understand that it is not a static warning to include a statement of the form return; in a generative constructor. The rules relate only to the specific syntactic form return e;.

The motivation for formulating return; in this way stems from the basic requirement that all function invocations indeed return a value. Function invocations 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). In void functions we use null. 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.

Labels

A label is an identifier followed by a colon. A labeled statement is a statement prefixed by a label L. A labeled case clause is a case clause within a switch statement prefixed by a label L.
The sole role of labels is to provide targets for the break and continue statements.

label:
identifier ‘:’
;

The semantics of a labeled statement L: s are identical to those of the statement s. The namespace of labels is distinct from the one used for types, functions and variables.

The scope of a label that labels a statement s is s. The scope of a label that labels a case clause of a switch statement s is s.

Labels should be avoided by programmers at all costs. The motivation for including labels in the language is primarily making Dart a better target for code generation.

Break

The break statement consists of the reserved word break and an optional label.

breakStatement:
break identifier? ‘;’
;

Let sb be a break statement. If sb is of the form break L; then let sE be the the innermost labeled statement with label L enclosing sb. If sb is of the form break; then let sE be the the innermost do, for, switch or while statement enclosing sb. It is a compile-time error if no such statement sE exists within the innermost function in which sb occurs. Furthermore, let s1… sn be those try statements that are both enclosed in sE and that enclose sb, and that have a finally clause. Lastly, let fj be the finally clause of sj, 1 <= j <= n. Executing sb first executes f1 … fn in innermost-clause-first order and then terminates sE.

Continue

The continue statement consists of the reserved word continue and an optional label.

continueStatement:
continue identifier? ‘;’
;

Let sc be a continue statement. If sc is of the form continue L; then let sE be the the innermost labeled statement or case clause with label L that encloses sc. If sc is of the form continue; then let sE be the the innermost do, for or while statement enclosing sc. It is a compile-time error if no such statement or case clause sE exists within the innermost function in which sc occurs. Furthermore, let s1… sn be those try statements that are both enclosed in sE and that enclose sc, and that have a finally clause. Lastly, let fj be the finally clause of sj, 1 <= j <= n. Executing sc first executes f1 … fn in innermost-clause-first order and then transfers control to sE.

Throw

The throw statement is used to raise or re-raise an exception.

throwStatement:
throw expression? ‘;’
;

The current exception is the last unhandled exception thrown. The current stack trace is a record of all the function activations within the current isolate that had not completed execution at the point where the current exception was thrown. For each such function activation, the current stack trace includes the name of the function, the bindings of all its formal parameters, local variables and this, and the position at which the function was executing.

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 in the exception.

Execution of a throw statement of the form throw e ; proceeds as follows:
The expression e is evaluated yielding a value v. Then, control is transferred to the nearest dynamically enclosing exception handler, with the current exception set to v and the current stack trace set to the series of activations that led to execution of the current function.

There is no requirement that the expression e evaluate to a special kind of exception or error object.

Execution of a statement of the form throw; proceeds as follows:
Control is transferred to the nearest dynamically enclosing exception handler.

No change is made to the current stack trace or the current exception.

It is a compile-time error if a statement of the form throw; is not enclosed within a catch clause.

Assert

An assert statement is used to disrupt normal execution if a given boolean condition does not hold.

assertStatement:
assert ‘(‘ conditionalExpression ‘)’ ‘;’
;

The assert statement has no effect in production mode. In checked mode, execution of an assert statement assert(e); proceeds as follows:

The conditional expression e is evaluated to an object o. If the class of o is a subtype of Function then let r be the result of invoking o with no arguments. Otherwise, let r be o. It is a dynamic error if o is not of type bool or of type Function, or if r is not of type bool. If r is false, we say that the assertion failed. If r is true, we say that the assertion succeeded. If the assertion succeeded, execution of the assert statement is complete. If the assertion failed, an AssertionError is thrown.

It is a static type warning if the type of e may not be assigned to either bool or () → bool

Why is this a statement, not a built in function call? Because it is handled magically so it has no effect and no overhead in production mode. Also, in the absence of final methods. one could not prevent it being overridden (though there is no real harm in that). Overall, perhaps it could be defined as a function, and the overhead issue could be viewed as an optimization.

Libraries and Scripts

A library consists of (a possibly empty) set of imports, and a set of top level declarations. A top level declaration is either a class, an interface, a type declaration, a function or a variable declaration.

topLevelDefinition:
classDefinition
| interfaceDefinition
| functionTypeAlias
| functionSignature functionBody
| returnType? getOrSet identifier formalParameterList functionBody
| final type? staticFinalDeclarationList ‘;’
| variableDeclaration ‘;’
;

getOrSet:
get
| set
;

libraryDefinition:
scriptTag? libraryName import* include* resource* topLevelDefinition*
;

scriptTag:
“#!” (~NEWLINE)* NEWLINE
;

libraryName:
“#” “library” “(” stringLiteral “)” “;”
;

A library may optionally begin with a script tag, which can be used to identify the interpreter of the script to whatever computing environment the script is embedded in. A script tag begins with the characters #! and ends at the end of the line. Any characters after #! are ignored by the Dart implementation.

Libraries are units of privacy. A private declaration declared within a library L can only be accessed by code within L. Any attempt to access a private member declaration from outside L will cause a run-time error. Since top level privates are not imported, using them is a compile time error and not an issue here.

The scope of a library L consists of the names introduced of all top level declarations declared in L, and the names of the names added by L’s imports.

Libraries may include extralinguistic resources (e.g., audio, video or graphics files)

resource:
“#” “resource” “(” stringLiteral “)” “;”
;

Imports

An import specifies a library to be used in the scope of another library.

import:
“#” “import” “(” stringLiteral (“,” “prefix:” stringLiteral)? “)” “;”
;

An import provides a URI where the declaration of the imported library is to be found. The effect of an import of library B with prefix P within the declaration of library A is:

  • If P is the empty string, each non-private top level declaration d of B is added to the scope of A.
  • Otherwise, each non-private top level declaration d of B is added to the scope of A under the name P.d, as is the name P.

Imports assume a global namespace of libraries (at least per isolate). They also assume the library is in control, rather than the other way around.

It is a compile-time error if a name N is introduced into the library scope of a library A, and either:

  • N is declared by A, OR
  • Another import introduces N into the scope of A.

This implies that it is a compile-time error for a library to import itself, as the names of its members will be duplicated.

The current library is the library currently being compiled.

Compiling an import directive of the form #import(s1, prefix: s2); proceeds as follows:

  • If the contents of the URI that is value of s1 have not yet been compiled in the current isolate then they are compiled to yield a library B. It is a compile-time error if s1 does not denote a URI that contains the source code for a Dart library.
  • Otherwise, the contents of URI denoted by s1 have been compiled into a library B within the current library.
  • Then, the library B is imported into the current library with prefix p, where p is the value of s2.

Compiling an import directive of the form #import(s) is equivalent to compiling the directive #import(s, prefix: “”);.

It is a compile-time error to import two or more libraries that define the same name. It is a compile-time error if either s1 or s2 is not a compile-time constant. It is a compile-time error if the value of s2 is not a valid identifier.

Includes

An include directive specifies a URI where a Dart compilation unit that should be incorporated into the current library may be found.

include:
“#” “source” “(” stringLiteral “)” “;”
;

compilationUnit:
topLevelDefinition* EOF
;

A compilation unit is a sequence of top level declarations.

Compiling an include directive of the form #source(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 compilation unit.

It is a compile-time error if s is not a compile-time constant.

Scripts

A script is a library with a top level function main().

scriptDefinition:
scriptTag? libraryName? import* include* resource* topLevelDefinition*
;

A script S may be executed as follows:

First, S is compiled as a library as specified above. Then, the top level function main() that is in scope in S is invoked with no arguments. It is a run time error if S does not declare or import a top level function main().

The names of scripts are optional, in the interests of interactive, informal use. However, any script of long term value should be given a name as a matter of good practice. Named scripts are composable: they can be used as libraries by other scripts and libraries.

Types

Dart supports optional typing based on interface types.
The type system is unsound, due to the covariance of generic types. This is a deliberate choice (and undoubtedly controversial). Experience has shown that sound type rules for generics fly in the face of programmer intuition. It is easy for tools to provide a sound type analysis if they choose, which may be useful for tasks like refactoring.

Static Types

Static type annotations are used in variable declarations (including formal parameters) and in the return types of functions. Static type annotations are used during static checking and when running programs in checked mode. They have no effect whatsoever in production mode.

type:
qualified typeArguments?
;

typeArguments:
‘<‘ typeList ‘>’
;

typeList:
type (‘,’ type)*
;

A Dart implementation must provide a static checker that detects and reports exactly those situations this specification identifies as static warnings. However:

  • Running the static checker on a program P is not required for compiling and running P.
  • Running the static checker on a program P must not prevent successful compilation of P nor may it prevent the execution of P, regardless of whether any static warnings occur.

Nothing precludes additional tools that implement alternative static analyses (e.g., interpreting the existing type annotations in a sound manner such as either non-variant generics, or inferring declaration based variance from the actual declarations). However, using these tools does not preclude successful compilation and execution of Dart code.

Should we do something with respect to non-nullable types?

Dynamic Type System

A Dart implementation must support execution in both production mode and checked mode. Those dynamic checks specified as occurring specifically in checked mode must be performed iff the code is executed in checked mode.

Type Declarations

Typedef

A type alias declares a name for a type expression.

functionTypeAlias:
typedef functionPrefix typeParameters? formalParameterList ‘;’
;

functionPrefix:
returnType? identifier
;

The effect of a type alias of the form typedef T id (T1 p1, .., Tn pn, [Tn+1 pn+1, …, Tn+k pn+k]) declared in a library L is is to introduce the name id into the scope of L, bound to the function type (T1, .., Tn, [ Tn+1 pn+1:, …, Tn+k pn+k]) → T. If no return type is specified, it is taken to be Dynamic. Likewise, if a type annotation is omitted on a formal parameter, it is taken to be Dynamic.

Currently, type aliases are restricted to function types. It is a compile-time error if any default values are specified in the signature of a function type alias.

Interface Types

An interface I is a direct supertype of an interface J iff:

  • If I is Object, and J has no extends clause.
  • if I is listed in the extends clause of J.

The supertypes of an interface are its direct supertypes and their supertypes.

A type T is more specific than a type S, written T ≪ S, if one of the following conditions is met:

  1. Reflexivity: T is S.
  2. T is bottom.
  3. S is Dynamic.
  4. Direct supertype: S is a direct supertype of T.
  5. T is a type variable and S is the upper bound of T.
  6. Covariance: T is of the form I<T1, …, Tn> and S is of the form I<S1, …, Sn> and Ti ≪ Si , 1 <= i <= n.
  7. Transitivity: T ≪ U and U ≪ S.

≪ is a partial order on types.
T is a subtype of S, written T <: S, iff [bottom/Dynamic]T ≪ S.
Note that <: is not a partial order on types, it is only binary relation on types. This is because <: is not transitive. If it was, the subtype rule would have a cycle. For example:
List <: List<String> and List<int> <: List, but List<int> is not a subtype of List<String>.
Although <: is not a partial order on types, it does contain a partial order, namely ≪. This means that, barring raw types, intuition about classical subtype rules does apply.

S is a supertype of T, written S :> T, iff T is a subtype of S.

A type T may be assigned to a type S, written T ⇔ S, iff either T <: S or S <: T.
This rule may surprise readers accustomed to conventional typechecking. The intent of the ⇔ relation is not to ensure that an assignment is correct. Instead, it aims to only flag assignments that are almost certain to be erroneous, without precluding assignments that may work.

For example, assigning a value of static type Object to a variable with static type String, while not guaranteed to be correct, might be fine if the runtime value happens to be a string.

Function Types

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. For all i , 1 <= i <= n, Ti ⇔ Si.
  3. k >= m and xi = yi, 1 <= i <= m. It is necessary, but not sufficient, that the optional arguments of the subtype be a subset of those of the supertype. We cannot treat them as just sets, because optional arguments can be invoked positionally, so the order matters.
  4. For all y in {y1, …, ym} Sy ⇔ Ty.

We write (T1, …, Tn) → T as a shorthand for the type (T1, …, Tn, []) → T.

All functions implement the interface Function, so all function types are a subtype of Function.

Type Dynamic

The built-in identifier Dynamic denotes the unknown type.

If no static type annotation has been provided the type system assumes the declaration has the type Dynamic. If a generic type is used but the corresponding type arguments are not provided, then the missing type arguments default to the unknown type.

This means that given a generic declaration G<T1, …, Tn>, the type G is equivalent to G<Dynamic, …, Dynamic>.

Type Dynamic has methods for every possible identifier and arity, with every possible combination of named parameters. These methods all have Dynamic as their return type, and their formal parameters all have type Dynamic.
Type Dynamic has properties for every possible identifier. These properties all have type Dynamic.

From a usability perspective, we want to ensure that the checker does not issue errors everywhere an unknown type is used. The definitions above ensure that no secondary errors are reported when accessing an unknown type.

The current rules say that missing type arguments are treated as if they were the type Dynamic. An alternative is to consider them as meaning Object. This would lead to earlier error detection in checked mode, and more aggressive errors during static typechecking. For example:

(1) typedAPI(G<String> g){…}
(2) typedAPI(new G());

Under the alternative rules, (2) would cause a runtime error in checked mode. This seems desirable from the perspective of error localization. However, when a Dynamic error is raised 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 programmers, 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.

Type Void

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)
  • bottom <: void (as bottom is a subtype of all types).
  • void <: Dynamic (as Dynamic is a supertype of all types)

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 (or a dynamic type error in checked mode).

Parameterized Types

A parameterized type is an invocation of a generic type declaration.

Let p = G<A1, …, An> be a parameterized type.

It is a static type warning if G is not an accessible generic type declaration with n type parameters. It is a static type warning if Ai, 1 <= i <= n does not denote a type in the enclosing lexical scope.

If S is the static type of of a member m of G, then the static type of the member m of G<A1, …, An> is [A1, …, An/T1, …, Tn]S where T1, …, Tn are the formal type parameters of G. Let Bi be the bounds of Ti, 1 <= i <= n. It is a static type warning if Ai is not a subtype of [A1, …, An/T1, …, Tn]Bi, 1 <= i <= n.

Actual Type of a Declaration

A type T depends on a type variable U iff:

  • T is U.
  • T is a parameterized type, and one of the type arguments of T depends on U.

Let T be the declared type of a declaration d, as it appears in the program source. The actual type of d is

  • [A1, …, An/U1, …, Un]T if d depends on type variables U1, …, Un, and Ai is the actual value of Ui, 1 <= i <= n.
  • T otherwise.

Least Upper Bounds

Given two interfaces I and J, let SI be the set of superinterfaces of I, let SJ be the set of superinterfaces of J and let S = (I SI ) (J SJ ). Furthermore, we define Sn = {T | T S depth(T) =n} for any finite n, and k=max(depth(T1), …, depth(Tm)), Ti S, 1 <= i <= m, where depth(T) is the number of steps in the shortest inheritance path from T to Object. Let q be the smallest number such that Sq has cardinality one. The least upper bound of I and J is the sole element of Sq.

Reference

Lexical Rules

Dart source text is represented as a sequence of Unicode code points normalized to Unicode Normalization Form C.

Reserved Words

break, case, catch, class, const, continue, default, do, else, extends, false, final, finally, for, if, in, is, new, null, return, super, switch, this, throw, true, try, var, void, while.

LETTER:
‘a’..’z’
| ‘A’..’Z’
;

DIGIT:
‘0’..’9′
;

WHITESPACE:
(‘\t’ | ‘ ‘ | NEWLINE)+
;

Comments

Comments are sections of program text that are used for documentation.

SINGLE_LINE_COMMENT:
‘//’ ~(NEWLINE)* (NEWLINE)?
;

MULTI_LINE_COMMENT:
‘/*’ (MULTI_LINE_COMMENT | ~ ‘*/’)* ‘*/’
;

Dart supports both single-line and multi-line comments. A single line comment begins with the token //. Everything between // and the end of line must be ignored by the Dart compiler.

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 multi-line comments that begin with the tokens /**. Inside a documentation comment, the Dart compiler ignores all text unless it is enclosed in brackets.

Operator Precedence

Operator precedence is given implicitly by the grammar.

By Dartlang.org

< Prev – Google Dart Language Specification III

< Prev – Google Dart Language Specification II

< Prev – Google Dart Language Specification I

:: Official Link Dartlang.org

2 thoughts on “Google Dart Language Specification VI – Statemets, scripts and types

  1. Pingback: Google Dart Language Specification « Moises Belchin

  2. Pingback: Google Dart Language Specification II – Variables, Functions and Classes « Moises Belchin

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s