| \documentclass{article} |
| \usepackage{epsfig} |
| \usepackage{color} |
| \usepackage{dart} |
| \usepackage{bnf} |
| \usepackage{hyperref} |
| \usepackage{lmodern} |
| \usepackage[T1]{fontenc} |
| \title{Dart Programming Language Specification\\ |
| {5th edition draft}\\ |
| {\large Version 2.0.0-dev}} |
| |
| % For information about Location Markers (and in particular the |
| % commands \LMHash and \LMLabel), see the long comment at the |
| % end of this file. |
| |
| % CHANGES |
| % ======= |
| % Significant changes to the specification. |
| % |
| % 2.0 |
| % - Don't allow functions as assert test values. |
| % - Start running "async" functions synchronously. |
| % - It is a static warning and dynamic error to assign to a final local. |
| % - Specify what "is equivalent to" means. |
| % - Remove @proxy. |
| % - Don't specify the exact object used for empty positionalArguments and |
| % namedArguments on Invocation. |
| % - Remove the, now unnecessary, handling of invalid overrides of noSuchMethod. |
| % - Add >>> as overridable operator. |
| % - If initializing formal has type annotation, require subtype of field type. |
| % - Constant `==` operations now also allowed if just one operand is null. |
| % - Make flatten not be recursive. |
| % - Disallow implementing two instantiations of the same generic interface. |
| % - Update "FutureOr" specification for Dart 2.0. |
| % - Require that a top-level "main" declaration is a valid script-entry |
| % function declaration. |
| % - State that the return type of a setter or []= is void when not specified. |
| % - Clarify that "noSuchMethod" must be implemented, not just redeclared |
| % abstractly, to eliminate certain diagnostic messages. |
| % - Add generic functions and methods to the language. |
| % - Don't cause warning if a non-system library import shadows a system library. |
| % - Update mixin application forwarding constructors to correctly handle |
| % optional parameters and const constructors. |
| % - Specify `call` for Dart 2 (no function type given to enclosing class). |
| % - Clarify that an identifier reference denoting a top-level, static, or |
| % local function evaluates to the closurization of that declaration. |
| % - Make `mixin` and `interface` built-in identifiers. |
| % - Make `async` *not* a reserved word inside async functions. |
| % - Add 'Class Member Conflicts', simplifying and adjusting rules about |
| % member declaration conflicts beyond "`n` declared twice in one scope". |
| % - Specify that integer literals are limited to signed 64-bit values, |
| % and that the `int` class is intended as signed 64-bit integer, but |
| % that platforms may differ. |
| % - Specify variance and super-bounded types. |
| % - Introduce `subterm` and `immediate subterm`. |
| % - Introduce `top type`. |
| % - Specify configurable imports. |
| % - Specify the dynamic type of the Iterable/Future/Stream returned from |
| % invocations of functions marked sync*/async/async*. |
| % - Add appendix listing the major differences between 64-bit integers |
| % and JavaScript integers. |
| % - Remove appendix on naming conventions. |
| % |
| % 1.15 |
| % - Change how language specification describes control flow. |
| % - Object initialization now specifies initialization order correctly. |
| % - Specifies that leaving an await-for loop must wait for the subscription |
| % to be canceled. |
| % - An await-for loop only pauses the subscription if it does something async. |
| % - Assert statements allows a "message" operand and a trailing comma. |
| % - The Null type is now considered a subtype of all types in most cases. |
| % - Specify what NEWLINE means in multiline strings. |
| % - Specified the FutureOf type. |
| % - Asserts can occur in initializer lists. |
| % |
| % 1.14 |
| % - The call "C()" where "C" is a class name, is a now compile-time error. |
| % - Changed description of rewrites that depended on a function literal. |
| % In many cases, the rewrite wasn't safe for asynchronous code. |
| % - Removed generalized tear-offs. |
| % - Allow "rethrow" to also end a switch case. Allow braces around switch cases. |
| % - Allow using '=' as default-value separator for named parameters. |
| % - Make it a compile-time error if a library includes the same part twice. |
| % - Now more specific about the return types of sync*/async/async* functions |
| % in relation to return statements. |
| % - Allow Unicode surrogate values in String literals. |
| % - Make an initializing formal's value accessible in the initializer list. |
| % - Allow any expression in assert statements (was only conditionalExpression). |
| % - Allow trailing commas in argument and parameter lists. |
| % |
| % 1.11 - ECMA 408 - 4th Edition |
| % - Specify that potentially constant expressions must be valid expressions |
| % if the parameters are non-constant. |
| % - Make "??" a compile-time constant operator. |
| % - Having multiple unnamed libraries no longer causes warnings. |
| % - Specify null-aware operators for static methods. |
| % |
| % 1.10 |
| % - Allow mixins to have super-classes and super-calls. |
| % - Specify static type checking for the implicit for-in iterator variable. |
| % - Specify static types for a number of expressions where it was missing. |
| % - Make calls on the exact type "Function" not cause warnings. |
| % - Specify null-aware behavior of "e?.v++" and similar expressions. |
| % - Specify that `package:` URIs are treated in an implementation dependent way. |
| % - Require warning if for-in is used on object with no "iterator" member. |
| % |
| % 1.9 - ECMA-408 - 3rd Edition |
| % |
| |
| \begin{document} |
| \maketitle |
| \tableofcontents |
| |
| \newpage |
| |
| \pagestyle{myheadings} |
| \markright{Dart Programming Language Specification} |
| |
| |
| % begin Ecma boilerplate |
| \section{Scope} |
| \LMLabel{ecmaScope} |
| |
| \LMHash{} |
| This Ecma standard specifies the syntax and semantics of the Dart programming language. |
| It does not specify the APIs of the Dart libraries except where those library elements are essential to the correct functioning of the language itself (e.g., the existence of class \code{Object} with methods such as \code{noSuchMethod}, \code{runtimeType}). |
| |
| |
| \section{Conformance} |
| \LMLabel{ecmaConformance} |
| |
| \LMHash{} |
| A conforming implementation of the Dart programming language must provide and support all the APIs (libraries, types, functions, getters, setters, whether top-level, static, instance or local) mandated in this specification. |
| |
| \LMHash{} |
| A conforming implementation is permitted to provide additional APIs, but not additional syntax, except for experimental features in support of null-aware cascades that are likely to be introduced in the next revision of this specification. |
| |
| |
| \section{Normative References} |
| \LMLabel{ecmaNormativeReferences} |
| |
| \LMHash{} |
| The following referenced documents are indispensable for the application of this document. |
| For dated references, only the edition cited applies. |
| For undated references, the latest edition of the referenced document (including any amendments) applies. |
| |
| \begin{enumerate} |
| \item |
| The Unicode Standard, Version 5.0, as amended by Unicode 5.1.0, or successor. |
| \item |
| Dart API Reference, https://api.dartlang.org/ |
| \end{enumerate} |
| |
| |
| \section{Terms and Definitions} |
| \LMLabel{ecmaTermsAndDefinitions} |
| |
| \LMHash{} |
| Terms and definitions used in this specification are given in the body of the specification proper. |
| Such terms are highlighted in italics when they are introduced, e.g., `we use the term {\em verbosity} to refer to the property of excess verbiage'. |
| % End Ecma Boilerplate |
| |
| |
| \section{Notation} |
| \LMLabel{notation} |
| |
| \LMHash{} |
| We distinguish between normative and non-normative text. |
| Normative text defines the rules of Dart. |
| It is given in this font. |
| At this time, non-normative text includes: |
| \begin{itemize} |
| \item[Rationale] |
| Discussion of the motivation for language design decisions appears in italics. |
| \rationale{ |
| Distinguishing normative from non-normative helps clarify what part of the text is binding and what part is merely expository. |
| } |
| \item[Commentary] |
| Comments such as ``\commentary{The careful reader will have noticed that the name Dart has four characters}'' serve to illustrate or clarify the specification, but are redundant with the normative text. |
| \commentary{ |
| The difference between commentary and rationale can be subtle. |
| } |
| \rationale{ |
| Commentary is more general than rationale, and may include illustrative examples or clarifications. |
| } |
| \item[Open questions] (\Q{in this font}). |
| Open questions are points that are unsettled in the mind of the author(s) of the specification; expect them (the questions, not the authors; precision is important in a specification) to be eliminated in the final specification. |
| \Q{Should the text at the end of the previous bullet be rationale or commentary?} |
| \end{itemize} |
| |
| \LMHash{} |
| Reserved words and built-in identifiers (\ref{identifierReference}) appear in {\bf bold}. |
| |
| \commentary{ |
| Examples would be \SWITCH{} or \CLASS{}. |
| } |
| |
| \LMHash{} |
| Grammar productions are given in a common variant of EBNF. |
| The left hand side of a production ends with a colon. |
| On the right hand side, alternation is represented by vertical bars, and sequencing by spacing. |
| As in PEGs, alternation gives priority to the left. |
| Optional elements of a production are suffixed by a question mark like so: \code{anElephant?}. |
| Appending a star to an element of a production means it may be repeated zero or more times. |
| Appending a plus sign to a production means it occurs one or more times. |
| Parentheses are used for grouping. |
| Negation is represented by prefixing an element of a production with a tilde. |
| Negation is similar to the not combinator of PEGs, but it consumes input if it matches. |
| In the context of a lexical production it consumes a single character if there is one; otherwise, a single token if there is one. |
| |
| \commentary{ |
| An example would be: |
| } |
| |
| \begin{grammar} |
| {\sf |
| {\bf AProduction:}AnAlternative; |
| AnotherAlternative; |
| OneThing After Another; |
| ZeroOrMoreThings*; |
| OneOrMoreThings+; |
| AnOptionalThing?; |
| (Some Grouped Things); |
| \~{}NotAThing; |
| A\_LEXICAL\_THING |
| . |
| } |
| \end{grammar} |
| |
| \LMHash{} |
| Both syntactic and lexical productions are represented this way. |
| Lexical productions are distinguished by their names. |
| The names of lexical productions consist exclusively of upper case characters and underscores. |
| As always, within grammatical productions, whitespace and comments between elements of the production are implicitly ignored unless stated otherwise. |
| Punctuation tokens appear in quotes. |
| |
| \LMHash{} |
| Productions are embedded, as much as possible, in the discussion of the constructs they represent. |
| |
| \LMHash{} |
| A {\em term} is a syntactic construct. |
| It may be considered to be a piece of text which is derivable in the grammar, |
| and it may be considered to be a tree created by such a derivation. |
| An {\em immediate subterm} of a given term $t$ is a syntactic construct |
| which corresponds to an immediate subtree of $t$ considered as a derivation tree. |
| A {\em subterm} of a given term $t$ is $t$, |
| or an immediate subterm of $t$, |
| or a subterm of an immediate subterm of $t$. |
| |
| \LMHash{} |
| A list $x_1, \ldots, x_n$ denotes any list of $n$ elements of the form $x_i, 1 \le i \le n$. |
| Note that $n$ may be zero, in which case the list is empty. |
| We use such lists extensively throughout this specification. |
| |
| \LMHash{} |
| For $j \in 1 .. n$, |
| let $y_j$ be an atomic syntactic entity (like an identifier), |
| $x_j$ a composite syntactic entity (like an expression or a type), |
| and $E$ again a composite syntactic entity. |
| The notation $[x_1/y_1, \ldots, x_n/y_n]E$ then denotes a copy of $E$ |
| in which each occurrence of $y_i, 1 \le i \le n$ has been replaced by $x_i$. |
| |
| \LMHash{} |
| This operation is also known as substitution, and it is the variant that avoids capture. |
| That is, when $E$ contains a construct that introduces $y_i$ into a nested scope for some $i \in 1 .. n$, |
| the substitution will not replace $y_i$ in that scope. |
| Conversely, if such a replacement would put an identifier \id{} (a subterm of $x_i$) into a scope where \id{} is declared, |
| the relevant declarations in $E$ are systematically renamed to fresh names. |
| |
| \commentary{ |
| In short, capture freedom ensures that the ``meaning'' of each identifier is preserved during substitution. |
| } |
| |
| \LMHash{} |
| We sometimes abuse list or map literal syntax, writing $[o_1, \ldots, o_n]$ (respectively $\{k_1: o_1, \ldots, k_n: o_n\}$) where the $o_i$ and $k_i$ may be objects rather than expressions. |
| The intent is to denote a list (respectively map) object whose elements are the $o_i$ (respectively, whose keys are the $k_i$ and values are the $o_i$). |
| |
| \LMHash{} |
| The specifications of operators often involve statements such as $x$ $op$ $y$ is equivalent to the method invocation $x.op(y)$. |
| Such specifications should be understood as a shorthand for: |
| \begin{itemize} |
| \item |
| $x$ $op$ $y$ is equivalent to the method invocation $x.op'(y)$, assuming the class of $x$ actually declared a non-operator method named $op'$ defining the same function as the operator $op$. |
| \end{itemize} |
| |
| \rationale{ |
| This circumlocution is required because x.op(y), where op is an operator, is not legal syntax. |
| However, it is painfully verbose, and we prefer to state this rule once here, and use a concise and clear notation across the specification. |
| } |
| |
| \LMHash{} |
| When the specification refers to the order given in the program, it means the order of the program source code text, scanning left-to-right and top-to-bottom. |
| |
| \LMHash{} |
| When the specification refers to a {\em fresh variable}, it means a variable with a name that doesn't occur anywhere in the current program. |
| When the specification introduces a fresh variable bound to a value, the fresh variable is implicitly bound in a surrounding scope. |
| |
| \LMHash{} |
| References to otherwise unspecified names of program entities (such as classes or functions) are interpreted as the names of members of the Dart core library. |
| |
| \commentary{ |
| Examples would be the classes \code{Object} and \code{Type} representing the root of the class hierarchy and the reification of run-time types respectively. |
| } |
| |
| \LMHash{} |
| When the specification says that one piece of syntax {\em is equivalent to} another piece of syntax, it means that it is equivalent in all ways, and the former syntax should generate the same static warnings and have the same run-time behavior as the latter. |
| \commentary{ |
| Error messages, if any, should always refer to the original syntax. |
| } |
| If execution or evaluation of a construct is said to be equivalent to execution or evaluation of another construct, then only the run-time behavior is equivalent, and only the static warnings or errors mentioned for the original syntax applies. |
| |
| |
| \section{Overview} |
| \LMLabel{overview} |
| |
| \LMHash{} |
| Dart is a class-based, single-inheritance, pure object-oriented programming language. |
| Dart is optionally typed (\ref{types}) and supports reified generics. |
| The run-time type of every object is represented as an instance of class \code{Type} which can be obtained by calling the getter \code{runtimeType} declared in class \code{Object}, the root of the Dart class hierarchy. |
| |
| \LMHash{} |
| Dart programs may be statically checked. |
| The static checker will report some violations of the type rules, but such violations do not abort compilation or preclude execution. |
| |
| \LMHash{} |
| Dart programs may be executed in one of two modes: production mode or checked mode. |
| In production mode, static type annotations (\ref{staticTypes}) have absolutely no effect on execution with the exception of reflection and structural type tests. |
| |
| \commentary{ |
| Reflection, by definition, examines the program structure. |
| If we provide reflective access to the type of a declaration, or to source code, it will inevitably produce results that depend on the types used in the underlying code. |
| |
| Type tests also examine the types in a program explicitly. |
| Nevertheless, in most cases, these will not depend on type annotations. |
| The exceptions to this rule are type tests involving function types. |
| Function types are structural, and so depend on the types declared for their parameters and on their return types. |
| } |
| |
| \LMHash{} |
| In checked mode, assignments are dynamically checked, and certain violations of the type system throw exceptions at run time. |
| |
| \commentary{ |
| The coexistence between optional typing and reification is based on the following: |
| \begin{enumerate} |
| \item Reified type information reflects the types of objects at run time and may always be queried by dynamic typechecking constructs (the analogs of instanceOf, casts, typecase etc.\ in other languages). |
| Reified type information includes class declarations, the run-time type (aka class) of an object, and type arguments to constructors. |
| \item Static type annotations determine the types of variables and function declarations (including methods and constructors). |
| \item Production mode respects optional typing. |
| Static type annotations do not affect run-time behavior. |
| \item Checked mode utilizes static type annotations and dynamic type information aggressively yet selectively to provide early error detection during development. |
| \end{enumerate} |
| } |
| |
| \LMHash{} |
| Dart programs are organized in a modular fashion into units called {\em libraries} (\ref{librariesAndScripts}). |
| Libraries are units of encapsulation and may be mutually recursive. |
| |
| \commentary{ |
| However they are not first class. |
| To get multiple copies of a library running simultaneously, one needs to spawn an isolate. |
| } |
| |
| |
| \subsection{Scoping} |
| \LMLabel{scoping} |
| |
| \LMHash{} |
| A {\em namespace} is a mapping of names denoting declarations to actual declarations. |
| Let $NS$ be a namespace. |
| We say that a name $n$ {\em is in }$NS$ if $n$ is a key of $NS$. |
| We say a declaration $d$ {\em is in }$NS$ if a key of $NS$ maps to $d$. |
| |
| \LMHash{} |
| A scope $S_0$ induces a namespace $NS_0$ that maps the simple name of each variable, type or function declaration $d$ declared in $S_0$ to $d$. |
| Labels are not included in the induced namespace of a scope; instead they have their own dedicated namespace. |
| |
| \commentary{ |
| It is therefore impossible, e.g., to define a class that declares a method and a getter with the same name in Dart. |
| Similarly one cannot declare a top-level function with the same name as a library variable or a class. |
| } |
| |
| \LMHash{} |
| It is a compile-time error if there is more than one entity with the same name declared in the same scope. |
| |
| \commentary{ |
| In some cases, the name of the declaration differs from the identifier used to declare it. |
| Setters have names that are distinct from the corresponding getters because they always have an = automatically added at the end, and unary minus has the special name unary-. |
| } |
| |
| \LMHash{} |
| Dart is lexically scoped. |
| Scopes may nest. |
| A name or declaration $d$ is {\em available in scope} $S$ if $d$ is in the namespace induced by $S$ or if $d$ is available in the lexically enclosing scope of $S$. |
| We say that a name or declaration $d$ is {\em in scope} if $d$ is available in the current scope. |
| |
| \LMHash{} |
| If a declaration $d$ named $n$ is in the namespace induced by a scope $S$, then $d$ {\em hides} any declaration named $n$ that is available in the lexically enclosing scope of $S$. |
| |
| \commentary{ |
| A consequence of these rules is that it is possible to hide a type with a method or variable. |
| Naming conventions usually prevent such abuses. |
| Nevertheless, the following program is legal: |
| } |
| |
| \begin{dartCode} |
| \CLASS{} HighlyStrung \{ |
| String() => "?"; |
| \} |
| \end{dartCode} |
| |
| \LMHash{} |
| Names may be introduced into a scope by declarations within the scope or by other mechanisms such as imports or inheritance. |
| |
| \rationale{ |
| The interaction of lexical scoping and inheritance is a subtle one. |
| Ultimately, the question is whether lexical scoping takes precedence over inheritance or vice versa. |
| Dart chooses the former. |
| |
| Allowing inherited names to take precedence over locally declared names 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: |
| } |
| |
| \begin{dartCode} |
| \LIBRARY{} L1; |
| \CLASS{} S \{\} |
| |
| \LIBRARY{} L2; |
| \IMPORT{} `L1.dart'; |
| foo() => 42; |
| \CLASS{} C \EXTENDS{} S\{ bar() => foo();\} |
| \end{dartCode} |
| |
| \rationale{ |
| Now assume a method \code{foo()} is added to \code{S}. |
| } |
| |
| \begin{dartCode} |
| \LIBRARY{} L1; |
| \CLASS{} S \{foo() => 91;\} |
| \end{dartCode} |
| |
| \rationale{ |
| If inheritance took precedence over the lexical scope, the behavior of \code{C} would change in an unexpected way. |
| Neither the author of \code{S} nor the author of \code{C} are necessarily aware of this. |
| In Dart, if there is a lexically visible method \code{foo()}, it will always be called. |
| |
| Now consider the opposite scenario. |
| We start with a version of \code{S} that contains \code{foo()}, but do not declare \code{foo()} in library \code{L2}. |
| Again, there is a change in behavior - but the author of \code{L2} is the one who introduced the discrepancy that effects their code, and the new code is lexically visible. |
| Both these factors make it more likely that the problem will be detected. |
| |
| These considerations become even more important if one introduces constructs such as nested classes, which might be considered in future versions of the language. |
| |
| Good tooling should of course endeavor to inform programmers of such situations (discreetly). |
| For example, an identifier that is both inherited and lexically visible could be highlighted (via underlining or colorization). |
| Better yet, tight integration of source control with language aware tools would detect such changes when they occur. |
| } |
| |
| |
| \subsection{Privacy} |
| \LMLabel{privacy} |
| |
| \LMHash{} |
| Dart supports two levels of privacy: {\em public} and {\em private}. |
| A declaration is {\em private} if{}f its name is private, otherwise it is {\em public}. |
| A name $q$ is private if{}f any one of the identifiers that comprise $q$ is private, otherwise it is {\em public}. |
| An identifier is private if{}f it begins with an underscore (the \_ character) otherwise it is {\em public}. |
| |
| \LMHash{} |
| A declaration $m$ is {\em accessible to library $L$} if $m$ is declared in $L$ or if $m$ is public. |
| |
| \commentary{ |
| This means private declarations may only be accessed within the library in which they are declared. |
| } |
| |
| \LMHash{} |
| Privacy applies only to declarations within a library, not to library declarations themselves. |
| |
| \rationale{ |
| Libraries do not reference each other by name and so the idea of a private library is meaningless. |
| Thus, if the name of a library begins with an underscore, it has no effect on the accessibility of the library or its members. |
| } |
| |
| \rationale{ |
| Privacy is, at this point, a static notion tied to a particular piece of code (a library). |
| It is designed to support software engineering concerns rather than security concerns. |
| Untrusted code should always run in an another isolate. |
| It is possible that libraries will become first class objects and privacy will be a dynamic notion tied to a library instance. |
| |
| Privacy is indicated by the name of a declaration - hence privacy and naming are not orthogonal. |
| This has the advantage that both humans and machines can recognize access to private declarations at the point of use without knowledge of the context from which the declaration is derived. |
| } |
| |
| |
| \subsection{Concurrency} |
| \LMLabel{concurrency} |
| |
| \LMHash{} |
| Dart code is always single threaded. |
| There is no shared-state concurrency in Dart. |
| Concurrency is supported via actor-like entities called {\em isolates}. |
| |
| \LMHash{} |
| An isolate is a unit of concurrency. |
| It has its own memory and its own thread of control. |
| Isolates communicate by message passing (\ref{sendingMessages}). |
| No state is ever shared between isolates. |
| Isolates are created by spawning (\ref{spawningAnIsolate}). |
| |
| |
| \section{Errors and Warnings} |
| \LMLabel{errorsAndWarnings} |
| |
| \LMHash{} |
| This specification distinguishes between several kinds of errors. |
| |
| \LMHash{} |
| {\em Compile-time errors} are errors that preclude execution. |
| A compile-time error must be reported by a Dart compiler before the erroneous code is executed. |
| |
| \rationale{ |
| A Dart implementation has considerable freedom as to when compilation takes place. |
| Modern programming language implementations often interleave compilation and execution, so that compilation of a method may be delayed, e.g., until it is first invoked. |
| Consequently, compile-time errors in a method $m$ may be reported as late as the time of $m$'s first invocation. |
| |
| As a web language, Dart is often loaded directly from source, with no intermediate binary representation. |
| In the interests of rapid loading, Dart implementations may choose to avoid full parsing of method bodies, for example. |
| This can be done by tokenizing the input and checking for balanced curly braces on method body entry. |
| In such an implementation, even syntax errors will be detected only when the method needs to be executed, at which time it will be compiled (JITed). |
| |
| In a development environment a compiler should of course report compilation errors eagerly so as to best serve the programmer. |
| } |
| |
| \LMHash{} |
| If an uncaught compile-time error occurs within the code of a running isolate $A$, $A$ is immediately suspended. |
| The only circumstance where a compile-time error could be caught would be via code run reflectively, where the mirror system can catch it. |
| |
| \rationale{ |
| Typically, once a compile-time error is thrown and $A$ is suspended, $A$ will then be terminated. |
| However, this depends on the overall environment. |
| A Dart engine runs in the context of an {\em embedder}, |
| a program that interfaces between the engine and the surrounding computing environment. |
| The embedder will often be a web browser, but need not be; it may be a C++ program on the server for example. |
| When an isolate fails with a compile-time error as described above, control returns to the embedder, along with an exception describing the problem. |
| This is necessary so that the embedder can clean up resources etc. |
| It is then the embedder's decision whether to terminate the isolate or not. |
| } |
| |
| \LMHash{} |
| {\em Static warnings} are those errors reported by the static checker. |
| They have no effect on execution. |
| Many, but not all, static warnings relate to types, in which case they are known as {\em static type warnings}. |
| Static warnings must be provided by Dart compilers used during development such as those incorporated in IDEs or otherwise intended to be used by developers for developing code. |
| Compilers that are part of run-time execution environments such as virtual machines should not issue static warnings. |
| |
| \LMHash{} |
| {\em Dynamic type errors} are type errors reported in checked mode. |
| |
| \LMHash{} |
| {\em Run-time errors} are exceptions thrown during execution. |
| Whenever we say that an exception $ex$ is {\em thrown}, it acts like an expression had {\em thrown} (\ref{completion}) with $ex$ as exception object and with a stack trace corresponding to the current system state. |
| When we say that {\em a} $C$ {\em is thrown}, where $C$ is a class, we mean that an instance of class $C$ is thrown. |
| |
| \LMHash{} |
| If an uncaught exception is thrown by a running isolate $A$, $A$ is immediately suspended. |
| |
| |
| \section{Variables} |
| \LMLabel{variables} |
| |
| \LMHash{} |
| Variables are storage locations in memory. |
| |
| \begin{grammar} |
| {\bf variableDeclaration:}declaredIdentifier (`,' identifier)* |
| . |
| |
| {\bf declaredIdentifier:}metadata finalConstVarOrType identifier |
| . |
| |
| {\bf finalConstVarOrType:}\FINAL{} type?; |
| \CONST{} type?; |
| varOrType |
| . |
| |
| {\bf varOrType:}\VAR{}; |
| type |
| . |
| |
| {\bf initializedVariableDeclaration:}declaredIdentifier (`=' expression)? (`,' initializedIdentifier)* |
| . |
| |
| {\bf initializedIdentifier:}identifier (`=' expression)? |
| . |
| |
| {\bf initializedIdentifierList:}initializedIdentifier (`,' initializedIdentifier)* |
| . |
| \end{grammar} |
| |
| \LMHash{} |
| A variable that has not been initialized has the null object (\ref{null}) as its initial value. |
| |
| \LMHash{} |
| A variable declared at the top-level of a library is referred to as either a {\em library variable} or simply a top-level variable. |
| |
| \LMHash{} |
| A {\em static variable} is a variable that is not associated with a particular instance, but rather with an entire library or class. |
| Static variables include library variables and class variables. |
| Class variables are variables whose declaration is immediately nested inside a class declaration and includes the modifier \STATIC{}. |
| A library variable is implicitly static. |
| It is a compile-time error to preface a top-level variable declaration with the built-in identifier (\ref{identifierReference}) \STATIC{}. |
| |
| \LMHash{} |
| Static variable declarations are initialized lazily. |
| When a static variable $v$ is read, if{}f it has not yet been assigned, it is set to the result of evaluating its initializer. |
| The precise rules are given in section \ref{evaluationOfImplicitVariableGetters}. |
| |
| \rationale{ |
| The lazy semantics are given because we do not want a language where one tends to define expensive initialization computations, causing long application startup times. |
| This is especially crucial for Dart, which must support the coding of client applications. |
| } |
| |
| \LMHash{} |
| A {\em final variable} is a variable whose binding is fixed upon initialization; a final variable $v$ will always refer to the same object after $v$ has been initialized. |
| The declaration of a final variable must include the modifier \FINAL{}. |
| |
| \LMHash{} |
| It is a static warning if a final instance variable that has been initialized at its point of declaration is also initialized in a constructor. |
| % It is a static warning if a final instance variable that has been initialized by means of an initializing formal of a constructor is also initialized elsewhere in the same constructor. |
| It is a compile-time error if a local variable $v$ is final and $v$ is not initialized at its point of declaration. |
| It is a static warning and a dynamic error to assign to a final local variable. |
| |
| \commentary{ |
| 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 run-time 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 run-time error. |
| |
| Taken as a whole, the rules ensure that any attempt to execute multiple assignments to a final variable will yield static warnings and repeated assignments will fail dynamically. |
| } |
| |
| \LMHash{} |
| A {\em constant variable} is a variable whose declaration includes the modifier \CONST{}. |
| A constant variable is always implicitly final. |
| A constant variable must be initialized to a compile-time constant (\ref{constants}) or a compile-time error occurs. |
| |
| \LMHash{} |
| We say that a variable $v$ is {\em potentially mutated} in some scope $s$ if $v$ is not final or constant and an assignment to $v$ occurs in $s$. |
| |
| \LMHash{} |
| If a variable declaration does not explicitly specify a type, the type of the declared variable(s) is \DYNAMIC{}, the unknown type (\ref{typeDynamic}). |
| |
| \LMHash{} |
| A variable is {\em mutable} if it is not final. |
| Static and instance variable declarations always induce implicit getters. |
| If the variable is mutable it also introduces an implicit setter. |
| The scope into which the implicit getters and setters are introduced depends on the kind of variable declaration involved. |
| |
| \LMHash{} |
| A library variable introduces a getter into the top level scope of the enclosing library. |
| A static class variable introduces a static getter into the immediately enclosing class. |
| An instance variable introduces an instance getter into the immediately enclosing class. |
| |
| \LMHash{} |
| A mutable library variable introduces a setter into the top level scope of the enclosing library. |
| A mutable static class variable introduces a static setter into the immediately enclosing class. |
| A mutable instance variable introduces an instance setter into the immediately enclosing class. |
| |
| \LMHash{} |
| Local variables are added to the innermost enclosing scope. |
| They do not induce getters and setters. |
| A local variable may only be referenced at a source code location that is after its initializer, if any, is complete, or a compile-time error occurs. |
| The error may be reported either at the point where the premature reference occurs, or at the variable declaration. |
| |
| \rationale{ |
| We allow the error to be reported at the declaration to allow implementations to avoid an extra processing phase. |
| } |
| |
| \commentary{ |
| The example below illustrates the expected behavior. |
| A variable $x$ is declared at the library level, and another $x$ is declared inside the function $f$. |
| } |
| |
| \begin{dartCode} |
| \VAR{} x = 0; |
| |
| f(y) \{ |
| \VAR{} z = x; // compile-time error |
| if (y) \{ |
| x = x + 1; // two compile-time errors |
| print(x); // compile-time error |
| \} |
| \VAR{} x = x++; // compile-time error |
| print(x); |
| \} |
| \end{dartCode} |
| |
| \commentary{ |
| The declaration inside $f$ hides the enclosing one. |
| So all references to $x$ inside $f$ refer to the inner declaration of $x$. |
| However, many of these references are illegal, because they appear before the declaration. |
| The assignment to $z$ is one such case. |
| The assignment to $x$ in the \IF{} statement suffers from multiple problems. |
| The right hand side reads $x$ before its declaration, and the left hand side assigns to $x$ before its declaration. |
| Each of these are, independently, compile-time errors. |
| The print statement inside the \IF{} is also illegal. |
| |
| The inner declaration of $x$ is itself erroneous because its right hand side attempts to read $x$ before the declaration has terminated. |
| The left hand side is not, technically, a reference or an assignment but a declaration and so is legal. |
| The last print statement is perfectly legal as well. |
| } |
| |
| \commentary{ |
| As another example \code{\VAR{} x = 3, y = x;} is legal, because \code{x} is referenced after its initializer. |
| |
| A particularly perverse example involves a local variable name shadowing a type. |
| This is possible because Dart has a single namespace for types, functions and variables. |
| } |
| |
| \begin{dartCode} |
| \CLASS{} C \{\} |
| perverse() \{ |
| \VAR{} v = \NEW{} C(); // compile-time error |
| C aC; // compile-time error |
| \VAR{} C = 10; |
| \} |
| \end{dartCode} |
| |
| \commentary{ |
| Inside \code{perverse()}, \code{C} denotes a local variable. |
| The type \code{C} is hidden by the variable of the same name. |
| The attempt to instantiate \code{C} causes a compile-time error because it references a local variable prior to its declaration. |
| Similarly, for the declaration of \code{aC} (even though it is only a type annotation). |
| } |
| |
| \rationale{ |
| 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 another, and in this extremely odd situation, that consideration takes precedence. |
| } |
| |
| % the grammar does not support local getters and setters. |
| % The local var discussion does not seem to mention getters and setters based semantics. |
| % It simply discusses the creation of the variable, not its access. |
| % Access is either assignment or identifiers. |
| % Identifiers ignore the getter story. |
| |
| \LMHash{} |
| The following rules apply to all static and instance variables. |
| |
| \LMHash{} |
| A variable declaration of one of the forms \code{$T$ $v$;}, \code{$T$ $v$ = $e$;} \code{\CONST{} $T$ $v$ = $e$;}, \code{\FINAL{} $T$ $v$;} or \code{\FINAL{} $T$ $v$ = $e$;} always induces an implicit getter function (\ref{getters}) with signature |
| |
| $T$ \GET{} $v$ |
| |
| whose invocation evaluates as described below (\ref{evaluationOfImplicitVariableGetters}). |
| |
| \LMHash{} |
| A variable declaration of one of the forms \code{\VAR{} $v$;}, \code{\VAR{} $v$ = $e$;}, \code{\CONST{} $v$ = $e$;}, \code{\FINAL{} $v$;} or \code{\FINAL{} $v$ = $e$;} always induces an implicit getter function with signature |
| |
| \GET{} $v$ |
| |
| whose invocation evaluates as described below (\ref{evaluationOfImplicitVariableGetters}). |
| |
| \LMHash{} |
| A non-final variable declaration of the form \code{{} $T$ $v$;} or the form \code{$T$ $v$ = $e$;} always induces an implicit setter function (\ref{setters}) with signature |
| |
| \VOID{} \SET{} $v=(T$ $x)$ |
| |
| whose execution sets the value of $v$ to the incoming argument $x$. |
| |
| \LMHash{} |
| A non-final variable declaration of the form \code{\VAR{} $v$;} or the form \code{\VAR{} $v$ = $e$;} always induces an implicit setter function with signature |
| |
| \SET{} $v=(x)$ |
| |
| whose execution sets the value of $v$ to the incoming argument $x$. |
| |
| |
| \subsection{Evaluation of Implicit Variable Getters} |
| \LMLabel{evaluationOfImplicitVariableGetters} |
| |
| \LMHash{} |
| Let $d$ be the declaration of a static or instance variable $v$. |
| If $d$ is an instance variable, then the invocation of the implicit getter of $v$ evaluates to the value stored in $v$. |
| If $d$ is a static or library variable then the implicit getter method of $v$ executes as follows: |
| \begin{itemize} |
| \item {\bf Non-constant variable declaration with initializer}. |
| If $d$ is of one of the forms \code{\VAR{} $v$ = $e$;}, \code{$T$ $v$ = $e$;}, \code{\FINAL{} $v$ = $e$;}, \code{\FINAL{} $T$ $v$ = $e$;}, \code{\STATIC{} $v$ = $e$;}, \code{\STATIC{} $T$ $v$ = $e$; }, \code{\STATIC{} \FINAL{} $v$ = $e$; } or \code{\STATIC{} \FINAL{} $T$ $v$ = $e$;} and no value has yet been stored into $v$ then the initializer expression $e$ is evaluated. |
| If, during the evaluation of $e$, the getter for $v$ is invoked, a \code{CyclicInitializationError} is thrown. |
| If the evaluation succeeded yielding an object $o$, let $r$ be $o$, otherwise let $r$ be the null object (\ref{null}). |
| In any case, $r$ is stored into $v$. |
| The result of executing the getter is $r$. |
| \item {\bf Constant variable declaration}. |
| If $d$ is of one of the forms \code{\CONST{} $v$ = $e$;}, \code{\CONST{} $T$ $v$ = $e$;}, \code{\STATIC{} \CONST{} $v$ = $e$;} or \code{\STATIC{} \CONST{} $T$ $v$ = $e$;} the result of the getter is the value of the compile-time constant $e$. |
| \commentary{ |
| Note that a compile-time constant cannot depend on itself, so no cyclic references can occur. |
| } |
| Otherwise |
| \item {\bf Variable declaration without initializer}. |
| The result of executing the getter method is the value stored in $v$. |
| \end{itemize} |
| |
| |
| \section{Functions} |
| \LMLabel{functions} |
| |
| \LMHash{} |
| Functions abstract over executable actions. |
| |
| \begin{grammar} |
| {\bf functionSignature:}metadata returnType? identifier formalParameterPart |
| . |
| |
| {\bf formalParameterPart:}typeParameters? formalParameterList |
| . |
| |
| {\bf returnType:}\VOID{}; |
| type |
| . |
| |
| {\bf functionBody:}\ASYNC{}? `={\escapegrammar \gt}' expression `{\escapegrammar ;}'; |
| (\ASYNC{} $|$ \ASYNC* $|$ \SYNC*)? block |
| . |
| |
| {\bf block:}`\{' statements `\}' |
| . |
| \end{grammar} |
| |
| \LMHash{} |
| Functions can be introduced by function declarations (\ref{functionDeclarations}), |
| method declarations (\ref{instanceMethods}, \ref{staticMethods}), |
| getter declarations (\ref{getters}), |
| setter declarations (\ref{setters}), |
| and constructor declarations (\ref{constructors}); |
| and they can be introduced by function literals (\ref{functionExpressions}). |
| |
| \LMHash{} |
| Each declaration that introduces a function has a signature that specifies its return type, name, and formal parameter part, |
| except that the return type may be omitted, and getters never have a formal parameter part. |
| Function literals have a formal parameter part, but no return type and no name. |
| The formal parameter part optionally specifies the formal type parameter list of the function, |
| and it always specifies its formal parameter list. |
| A function body is either: |
| \begin{itemize} |
| \item A block statement (\ref{blocks}) containing the statements (\ref{statements}) executed by the function, optionally marked with one of the modifiers: \ASYNC, \ASYNC* or \SYNC*. |
| |
| \commentary{ |
| Because Dart is optionally typed, we cannot guarantee that a function that does not return a value will not be used in the context of an expression. |
| Therefore, every function must return a value. |
| A function body that ends without doing a throw or return will cause the function to return the null object (\ref{null}), as will a \RETURN{} without an expression. |
| For generator functions, the situation is more subtle. |
| See further discussion in section \ref{return}. |
| } |
| |
| OR |
| \item of the form \code{=> $e$} or the form \code{\ASYNC{} => $e$}, which both return the value of the expression $e$ as if by a \code{return $e$}. |
| \commentary{ |
| The other modifiers do not apply here, because they apply only to generators, discussed below, and generators do not allow to return a value, values are added to the generated stream or iterable using \YIELD{} instead. |
| } |
| Let $R$ be the static type of $e$ |
| and let $T$ be the actual return type (\ref{actualTypeOfADeclaration}) |
| of the function that has this body. |
| It is a static warning if $T$ is not \VOID{} and either |
| the function is synchronous and the static type of $R$ is not assignable to $T$, |
| or the function is asynchronous and \code{Future<$flatten(R)$>} |
| is not assignable to $T$. |
| |
| \end{itemize} |
| |
| \LMHash{} |
| A function is {\em asynchronous} if its body is marked with the \ASYNC{} or \ASYNC* modifier. |
| Otherwise the function is {\em synchronous}. |
| A function is a {\em generator} if its body is marked with the \SYNC* or \ASYNC* modifier. |
| |
| \commentary{ |
| Whether a function is synchronous or asynchronous is orthogonal to whether it is a generator or not. |
| Generator functions are a sugar for functions that produce collections in a systematic way, by lazily applying a function that {\em generates} individual elements of a collection. |
| Dart provides such a sugar in both the synchronous case, where one returns an iterable, and in the asynchronous case, where one returns a stream. |
| Dart also allows both synchronous and asynchronous functions that produce a single value. |
| } |
| |
| \LMHash{} |
| It is a compile-time error if an \ASYNC, \ASYNC* or \SYNC* modifier is attached to the body of a setter or constructor. |
| |
| \rationale{ |
| An asynchronous setter would be of little use, since setters can only be used in the context of an assignment (\ref{assignment}), and an assignment expression always evaluates to the value of the assignment's right hand side. |
| If the setter actually did its work asynchronously, one might imagine that one would return a future that resolved to the assignment's right hand side after the setter did its work. |
| However, this would require dynamic tests at every assignment, and so would be prohibitively expensive. |
| |
| An asynchronous constructor would, by definition, never return an instance of the class it purports to construct, but instead return a future. |
| Calling such a beast via \NEW{} would be very confusing. |
| If you need to produce an object asynchronously, use a method. |
| |
| One could allow modifiers for factories. |
| A factory for \code{Future} could be modified by \ASYNC{}, a factory for \code{Stream} could be modified by \ASYNC* and a factory for \code{Iterable} could be modified by \SYNC*. |
| No other scenario makes sense because the object returned by the factory would be of the wrong type. |
| This situation is very unusual so it is not worth making an exception to the general rule for constructors in order to allow it. |
| } |
| \LMHash{} |
| It is a static warning if the declared return type of a function marked \ASYNC{} is not a supertype of \code{Future<\mbox{$T$}>} for some type $T$. |
| It is a static warning if the declared return type of a function marked \SYNC* is not a supertype of \code{Iterable<\mbox{$T$}>} for some type $T$. |
| It is a static warning if the declared return type of a function marked \ASYNC* is not a supertype of \code{Stream<\mbox{$T$}>} for some type $T$. |
| |
| |
| \subsection{Function Declarations} |
| \LMLabel{functionDeclarations} |
| |
| \LMHash{} |
| A {\em function declaration} is a function that is neither a member of a class nor a function literal. |
| Function declarations include {\em library functions}, which are function declarations |
| %(including getters and setters) |
| at the top level of a library, and {\em local functions}, which are function declarations declared inside other functions. |
| Library functions are often referred to simply as top-level functions. |
| |
| \LMHash{} |
| A function declaration consists of an identifier indicating the function's name, possibly prefaced by a return type. |
| The function name is followed by a signature and body. |
| For getters, the signature is empty. |
| The body is empty for functions that are external. |
| |
| \LMHash{} |
| The scope of a library function is the scope of the enclosing library. |
| The scope of a local function is described in section \ref{localFunctionDeclaration}. |
| In both cases, the name of the function is in scope in its formal parameter scope (\ref{formalParameters}). |
| |
| \LMHash{} |
| It is a compile-time error to preface a function declaration with the built-in identifier \STATIC{}. |
| |
| \LMHash{} |
| When we say that a function $f_1$ {\em forwards} to another function $f_2$, we mean that invoking $f_1$ causes $f_2$ to be executed with the same arguments and/or receiver as $f_1$, and returns the result of executing $f_2$ to the caller of $f_1$, unless $f_2$ throws an exception, in which case $f_1$ throws the same exception. |
| Furthermore, we only use the term for synthetic functions introduced by the specification. |
| |
| |
| \subsection{Formal Parameters} |
| \LMLabel{formalParameters} |
| |
| \LMHash{} |
| Every non-getter function declaration includes a {\em formal parameter list}, |
| which consists of a list of required positional parameters (\ref{requiredFormals}), |
| followed by any optional parameters (\ref{optionalFormals}). |
| The optional parameters may be specified either as a set of named parameters or as a list of positional parameters, but not both. |
| |
| \LMHash{} |
| Some function declarations include a {\em formal type parameter list} (\ref{functions}), |
| in which case we say that it is a {\em generic function}. |
| A {\em non-generic function} is a function which is not generic. |
| |
| \LMHash{} |
| The {\em formal parameter part} of a function declaration consists of the formal type parameter list, if any, and the formal parameter list. |
| |
| \commentary{ |
| The following kinds of functions cannot be generic: |
| Getters, setters, operators, and constructors. |
| } |
| |
| \LMHash{} |
| The formal type parameter list of a function declaration introduces a new scope known as the function's {\em type parameter scope}. |
| The type parameter scope of a generic function $f$ is enclosed in the scope where $f$ is declared. |
| Every formal type parameter introduces a type into the type parameter scope. |
| |
| \LMHash{} |
| If it exists, the type parameter scope of a function $f$ is the current scope for the signature of $f$, and for the formal type parameter list itself; |
| otherwise the scope where $f$ is declared is the current scope for the signature of $f$. |
| |
| \commentary{ |
| This means that formal type parameters are in scope in the bounds of parameter declarations, |
| allowing for so-called F-bounded type parameters like |
| |
| \code{class C<X \EXTENDS{} Comparable<X>{}> \{ \ldots{} \}}, |
| |
| \noindent |
| and the formal type parameters are in scope for each other, allowing dependencies like |
| \code{class D<X \EXTENDS{} Y, Y> \{ \ldots{} \}}. |
| } |
| |
| \LMHash{} |
| The formal parameter list of a function declaration introduces a new scope known as the function's {\em formal parameter scope}. |
| The formal parameter scope of a non-generic function $f$ is enclosed in the scope where $f$ is declared. |
| The formal parameter scope of a generic function $f$ is enclosed in the type parameter scope of $f$. |
| Every formal parameter introduces a local variable into the formal parameter scope. |
| The current scope for the function's signature is the scope that encloses the formal parameter scope. |
| |
| \commentary{ |
| This means that in a generic function declaration, |
| the return type and parameter type annotations can use the formal type parameters, |
| but the formal parameters are not in scope in the signature. |
| } |
| |
| \LMHash{} |
| The body of a function declaration introduces a new scope known as the function's {\em body scope}. |
| The body scope of a function $f$ is enclosed in the scope introduced by the formal parameter scope of $f$. |
| |
| %The formal parameter scope of a function maps the name of each formal parameter $p$ to the value $p$ is bound to. |
| |
| % The formal parameters of a function are processed in the enclosing scope of the function. |
| % \commentary{this means that the parameters themselves may not be referenced within the formal parameter list.} |
| |
| \LMHash{} |
| It is a compile-time error if a formal parameter is declared as a constant variable (\ref{variables}). |
| |
| \begin{grammar} |
| {\bf formalParameterList:}`(' `)'; |
| `(' normalFormalParameters \gcomma{}? `)'; |
| `(' normalFormalParameters \gcomma{} optionalFormalParameters `)'; |
| `(' optionalFormalParameters `)' |
| . |
| |
| {\bf normalFormalParameters:}normalFormalParameter (\gcomma{} normalFormalParameter)* |
| . |
| |
| {\bf optionalFormalParameters:}optionalPositionalFormalParameters; |
| namedFormalParameters |
| . |
| |
| {\bf optionalPositionalFormalParameters:}`[' defaultFormalParameter (\gcomma{} defaultFormalParameter)* \gcomma{}? `]' |
| . |
| |
| {\bf namedFormalParameters:}`\{' defaultNamedParameter (\gcomma{} defaultNamedParameter)* \gcomma{}? `\}' |
| . |
| \end{grammar} |
| |
| Formal parameter lists allow an optional trailing comma after the last parameter ($`,\!'?$). |
| A parameter list with such a trailing comma is equivalent in all ways to the same parameter list without the trailing comma. |
| All parameter lists in this specification are shown without a trailing comma, but the rules and semantics apply equally to the corresponding parameter list with a trailing comma. |
| |
| |
| \subsubsection{Required Formals} |
| \LMLabel{requiredFormals} |
| |
| \LMHash{} |
| A {\em required formal parameter} may be specified in one of three ways: |
| \begin{itemize} |
| \item By means of a function signature that names the parameter and describes its type as a function type (\ref{functionTypes}). |
| It is a compile-time error if any default values are specified in the signature of such a function type.% explain what the type is in this case? Where is this described in general? |
| \item As an initializing formal, which is only valid as a parameter to a generative constructor (\ref{generativeConstructors}). % do we need to say this, or anything more? |
| \item Via an ordinary variable declaration (\ref{variables}). |
| \end{itemize} |
| |
| \begin{grammar} |
| {\bf normalFormalParameter:}functionFormalParameter; |
| fieldFormalParameter; |
| simpleFormalParameter |
| . |
| |
| {\bf functionFormalParameter:}metadata \COVARIANT{}? returnType? identifier |
| \gnewline{} formalParameterPart |
| . |
| |
| {\bf simpleFormalParameter:}metadata \COVARIANT{}? finalConstVarOrType? identifier; |
| . |
| |
| {\bf fieldFormalParameter:}metadata finalConstVarOrType? \THIS{} `{\escapegrammar .}' identifier |
| \gnewline{} formalParameterPart? |
| . |
| \end{grammar} |
| |
| \LMHash{} |
| It is possible to include the modifier \COVARIANT{} in some forms of parameter declarations. |
| This modifier has no effect. |
| |
| \rationale{ |
| The modifier \COVARIANT{} is used in strong mode. |
| The modifier is allowed here even though it has no effect, such that source code can be used in both contexts. |
| } |
| |
| \LMHash{} |
| It is a compile-time error if the modifier \COVARIANT{} occurs on a parameter of a function which is not an instance method, instance setter, or instance operator. |
| |
| |
| \subsubsection{Optional Formals} |
| \LMLabel{optionalFormals} |
| |
| \LMHash{} |
| Optional parameters may be specified and provided with default values. |
| |
| \begin{grammar} |
| {\bf defaultFormalParameter:}normalFormalParameter (`=' expression)? |
| . |
| |
| {\bf defaultNamedParameter:}normalFormalParameter (`=' expression)?; |
| normalFormalParameter ( `{\escapegrammar :}' expression)? |
| . |
| \end{grammar} |
| |
| A {\bf defaultNamedParameter} of the form: |
| \begin{code} |
| normalFormalParameter : expression |
| \end{code} |
| is equivalent to one of the form: |
| \begin{code} |
| normalFormalParameter = expression |
| \end{code} |
| The colon-syntax is included only for backwards compatibility. |
| It is deprecated and will be removed in a later version of the language specification. |
| |
| \LMHash{} |
| It is a compile-time error if the default value of an optional parameter is not a compile-time constant (\ref{constants}). |
| If no default is explicitly specified for an optional parameter an implicit default of \NULL{} is provided. |
| |
| \LMHash{} |
| It is a compile-time error if the name of a named optional parameter begins with an `\_' character. |
| |
| \rationale{ |
| The need for this restriction is a direct consequence of the fact that naming and privacy are not orthogonal. |
| If we allowed named parameters to begin with an underscore, they would be considered private and inaccessible to callers from outside the library where it was defined. |
| If a method outside the library overrode a method with a private optional name, it would not be a subtype of the original method. |
| The static checker would of course flag such situations, but the consequence would be that adding a private named formal would break clients outside the library in a way they could not easily correct. |
| } |
| |
| |
| \subsection{Type of a Function} |
| \LMLabel{typeOfAFunction} |
| |
| \LMHash{} |
| If a function declaration does not declare a return type explicitly, its return type is \DYNAMIC{} (\ref{typeDynamic}), |
| unless it is a constructor function, in which case its return type is the immediately enclosing class, |
| or it is a setter or operator \code{[]=}, in which case its return type is \VOID{}. |
| |
| \LMHash{} |
| A function declaration may declare formal type parameters. |
| The type of the function includes the names of the type parameters and their upper bounds. |
| When consistent renaming of type parameters can make two function types identical, |
| they are considered to be the same type. |
| |
| \commentary{ |
| It is convenient to include the type parameter names in function types because they are needed in order to express such things as relations among different type parameters, and F-bounds. |
| However, we do not wish to distinguish two function types if they have the same structure and only differ in the choice of names. |
| This treatment of names is also known as alpha-equivalence. |
| } |
| |
| \LMHash{} |
| In the following three paragraphs, |
| if the number $m$ of formal type parameters is zero then the type parameter list in the function type should be omitted. |
| |
| \LMHash{} |
| Let $F$ be a function with |
| formal type parameters $X_1\ B_1, \ldots,\ X_m\ B_m$, |
| required formal parameters $T_1\ p_1, \ldots,\ T_n\ p_n$, |
| return type $T_0$ |
| and no optional parameters. |
| Then the type of $F$ is |
| \code{<$X_1\ B_1, \ldots,\ X_m\ B_m$>($T_1, \ldots,\ T_n$) $ \rightarrow T_0$}. |
| |
| \LMHash{} |
| Let $F$ be a function with |
| formal type parameters $X_1\ B_1, \ldots,\ X_m\ B_m$, |
| required formal parameters $T_1\ p_1, \ldots,\ T_n\ p_n$, |
| return type $T_0$ |
| and positional optional parameters $T_{n+1}\ p_{n+1}, \ldots,\ T_{n+k}\ p_{n+k}$. |
| Then the type of $F$ is |
| |
| \code{<$X_1\ B_1, \ldots,\ X_m\ B_m$>($T_1, \ldots,\ T_n, $ [$T_{n+1}\ p_{n+1}, \ldots,\ T_{n+k}\ p_{n+k}$]) $ \rightarrow T_0$}. |
| |
| \LMHash{} |
| Let $F$ be a function with |
| formal type parameters $X_1\ B_1, \ldots,\ X_m\ B_m$, |
| required formal parameters $T_1\ p_1, \ldots,\ T_n\ p_n$, |
| return type $T_0$ |
| and named optional parameters $T_{n+1}\ p_{n+1}, \ldots,\ T_{n+k}\ p_{n+k}$. |
| Then the type of $F$ is |
| |
| \code{<$X_1\ B_1, \ldots,\ X_m\ B_m$>($T_1, \ldots,\ T_n, $ \{$T_{n+1}\ p_{n+1}, \ldots,\ T_{n+k}\ p_{n+k}$\}) $ \rightarrow T_0$}. |
| |
| \LMHash{} |
| The run-time type of a function object always implements the class \FUNCTION{}. |
| |
| \commentary{ |
| One cannot assume, based on the above, that given a function \code{f}, \code{f.runtimeType} will actually be \FUNCTION{}, or that any two distinct function objects necessarily have the same run-time type. |
| } |
| |
| \rationale{ |
| It is up to the implementation to choose an appropriate representation for function objects. |
| For example, consider that a function object produced via property extraction treats equality differently from other function objects, and is therefore likely a different class. |
| Implementations may also use different classes for function objects based on arity and or type. |
| Arity may be implicitly affected by whether a function is an instance method (with an implicit receiver parameter) or not. |
| The variations are manifold, and so this specification only guarantees that function objects are instances of some class that implements \FUNCTION{}. |
| } |
| |
| |
| \subsection{External Functions} |
| \LMLabel{externalFunctions} |
| |
| \LMHash{} |
| An {\em external function} is a function whose body is provided separately from its declaration. |
| An external function may be a top-level function (\ref{librariesAndScripts}), a method (\ref{instanceMethods}, \ref{staticMethods}), a getter (\ref{getters}), a setter (\ref{setters}) or a non-redirecting constructor (\ref{generativeConstructors}, \ref{factories}). |
| External functions are introduced via the built-in identifier \EXTERNAL{} (\ref{identifierReference}) followed by the function signature. |
| |
| \rationale{ |
| External functions allow us to introduce type information for code that is not statically known to the Dart compiler. |
| } |
| |
| \commentary{ |
| Examples of external functions might be foreign functions (defined in C, or Javascript etc.), primitives of the implementation (as defined by the Dart run-time system), or code that was dynamically generated but whose interface is statically known. |
| However, an abstract method is different from an external function, as it has {\em no} body. |
| } |
| |
| \LMHash{} |
| An external function is connected to its body by an implementation specific mechanism. |
| Attempting to invoke an external function that has not been connected to its body will throw a \code{NoSuchMethodError} or some subclass thereof. |
| |
| \LMHash{} |
| The actual syntax is given in sections \ref{classes} and \ref{librariesAndScripts} below. |
| |
| |
| \section{Classes} |
| \LMLabel{classes} |
| |
| \LMHash{} |
| A {\em class} defines the form and behavior of a set of objects which are its {\em instances}. |
| Classes may be defined by class declarations as described below, or via mixin applications (\ref{mixinApplication}). |
| |
| \begin{grammar} |
| {\bf classDefinition:}metadata \ABSTRACT{}? \CLASS{} identifier typeParameters? |
| \gnewline{} (superclass mixins?)? interfaces? |
| \gnewline{} `\{' (metadata classMemberDefinition)* `\}'; |
| metadata \ABSTRACT{}? \CLASS{} mixinApplicationClass |
| . |
| |
| {\bf mixins:}\WITH{} typeList |
| . |
| |
| {\bf classMemberDefinition:}declaration `{\escapegrammar ;}' ; |
| methodSignature functionBody |
| . |
| |
| {\bf methodSignature:}constructorSignature initializers?; |
| factoryConstructorSignature; |
| % This doesn't work: `static @override foo() => 42`, but `functionSignature` starts with metadata. https://github.com/dart-lang/sdk/issues/29614 |
| \STATIC{}? functionSignature; |
| \STATIC{}? getterSignature; |
| \STATIC{}? setterSignature; |
| operatorSignature |
| . |
| |
| {\bf declaration:}constantConstructorSignature (redirection $|$ initializers)?; |
| constructorSignature (redirection $|$ initializers)?; |
| \EXTERNAL{} constantConstructorSignature; |
| \EXTERNAL{} constructorSignature; |
| ((\EXTERNAL{} \STATIC{}?))? getterSignature; |
| ((\EXTERNAL{} \STATIC{}?))? setterSignature; |
| \EXTERNAL{}? operatorSignature; |
| % This doesn't work: `static @override foo() => 42`, but `functionSignature` starts with metadata. https://github.com/dart-lang/sdk/issues/29614 |
| ((\EXTERNAL{} \STATIC{}?))? functionSignature; |
| \STATIC{} (\FINAL{} $|$ \CONST{}) type? staticFinalDeclarationList; |
| % \CONST{} type? staticFinalDeclarationList; |
| \FINAL{} type? initializedIdentifierList; |
| (\STATIC{} $|$ \COVARIANT{})? (\VAR{} $|$ type) initializedIdentifierList |
| . |
| |
| {\bf staticFinalDeclarationList:}staticFinalDeclaration (\gcomma{} staticFinalDeclaration)* |
| . |
| |
| {\bf staticFinalDeclaration:}identifier `=' expression |
| . |
| \end{grammar} |
| |
| \LMHash{} |
| It is possible to include the modifier \COVARIANT{} in some forms of declarations. |
| This modifier has no effect. |
| |
| \rationale{ |
| The modifier \COVARIANT{} is used in strong mode. |
| The modifier is allowed here even though it has no effect, such that source code can be used in both contexts. |
| } |
| |
| \LMHash{} |
| A class has constructors, instance members and static members. |
| The instance members of a class are its instance methods, getters, setters and instance variables. |
| The static members of a class are its static methods, getters, setters and static variables. |
| The members of a class are its static and instance members. |
| |
| \LMHash{} |
| A class has several scopes: |
| \begin{itemize} |
| \item A {\em type-parameter scope}, which is empty if the class is not generic (\ref{generics}). |
| The enclosing scope of the type-parameter scope of a class is the enclosing scope of the class declaration. |
| \item A {\em static scope}. |
| The enclosing scope of the static scope of a class is the type parameter scope (\ref{generics}) of the class. |
| \item An {\em instance scope}. |
| The enclosing scope of a class' instance scope is the class' static scope. |
| \end{itemize} |
| |
| \LMHash{} |
| The enclosing scope of an instance member declaration is the instance scope of the class in which it is declared. |
| |
| \LMHash{} |
| The enclosing scope of a static member declaration is the static scope of the class in which it is declared. |
| |
| \LMHash{} |
| Every class has a single superclass except class \code{Object} which has no superclass. |
| A class may implement a number of interfaces by declaring them in its implements clause (\ref{superinterfaces}). |
| |
| \LMHash{} |
| An {\em abstract class} is a class that is explicitly declared with the \ABSTRACT{} modifier, either by means of a class declaration or via a type alias (\ref{typedef}) for a mixin application (\ref{mixinApplication}). |
| A {\em concrete class} is a class that is not abstract. |
| |
| \rationale{ |
| We want different behavior for concrete classes and abstract classes. |
| If $A$ is intended to be abstract, we want the static checker to warn about any attempt to instantiate $A$, and we do not want the checker to complain about unimplemented methods in $A$. |
| In contrast, if $A$ is intended to be concrete, the checker should warn about all unimplemented methods, but allow clients to instantiate it freely. |
| } |
| |
| \LMHash{} |
| The {\em interface of class $C$} is an implicit interface that declares instance members that correspond to the instance members declared by $C$, and whose direct superinterfaces are the direct superinterfaces of $C$ (\ref{superinterfaces}). |
| When a class name appears as a type, that name denotes the interface of the class. |
| |
| % making an exception for the setters generated for final fields is tempting but problematic. |
| % If a super type defines a setter, it will be overridden yet have no impact on the interface. |
| % Maybe the final field hides the setter in scope? |
| % I think the original rules were best. |
| |
| \commentary{ |
| It is a compile-time error if a class declares two members of the same name, |
| either because it declares the same name twice in the same scope (\ref{scoping}), |
| or because it declares a static member and an instance member with the same name |
| (\ref{classMemberConflicts}). |
| } |
| |
| \commentary{ |
| Here are simple examples, that illustrate the difference between ``has a member'' and ``declares a member''. |
| For example, \code{B} {\em declares} one member named \code{f}, but {\em has} two such members. |
| The rules of inheritance determine what members a class has. |
| } |
| |
| \begin{dartCode} |
| \CLASS{} A \{ |
| \VAR{} i = 0; |
| \VAR{} j; |
| f(x) => 3; |
| \} |
| |
| \CLASS{} B \EXTENDS{} A \{ |
| int i = 1; // getter i and setter i= override versions from A |
| \STATIC{} j; // compile-time error: static getter \& setter conflict with |
| // instance getter \& setter |
| |
| /* compile-time error: static method conflicts with instance method */ |
| \STATIC{} f(x) => 3; |
| \} |
| \end{dartCode} |
| |
| \LMHash{} |
| It is a compile-time error if a class $C$ declares a member with the same name as $C$. |
| It is a compile-time error if a generic class declares a type variable with the same name as the class or any of its members or constructors. |
| |
| |
| \subsection{Instance Methods} |
| \LMLabel{instanceMethods} |
| |
| \LMHash{} |
| Instance methods are functions (\ref{functions}) whose declarations are immediately contained within a class declaration and that are not declared \STATIC{}. |
| The instance methods of a class $C$ are those instance methods declared by $C$ and the instance methods inherited by $C$ from its superclass. |
| |
| %make these warnings if possible |
| |
| \LMHash{} |
| It is a static warning if an instance method $m_1$ overrides (\ref{inheritanceAndOverriding}) an instance member $m_2$ and $m_1$ has a greater number of required parameters than $m_2$. |
| It is a static warning if an instance method $m_1$ overrides an instance member $m_2$ and $m_1$ has fewer positional parameters than $m_2$. |
| It is a static warning if an instance method $m_1$ overrides an instance member $m_2$ and $m_1$ does not declare all the named parameters declared by $m_2$. |
| |
| % not quite right. It should be ok to override a method that requires N parameters with one that requires M < N but accepts the others as optional. |
| |
| \LMHash{} |
| It is a static warning if an instance method $m_1$ overrides an instance member $m_2$ and the type of $m_1$ is not a subtype of the type of $m_2$. |
| It is a static warning if an instance method $m_1$ overrides an instance member $m_2$, the signature of $m_2$ explicitly specifies a default value for a formal parameter $p$, and the signature of $m_1$ implies a different default value for $p$. |
| |
| \commentary{ |
| A method declaration may conflict with other declarations |
| (\ref{classMemberConflicts}). |
| } |
| |
| |
| \subsubsection{Operators} |
| \LMLabel{operators} |
| |
| \LMHash{} |
| {\em Operators} are instance methods with special names. |
| |
| \begin{grammar} |
| {\bf operatorSignature:}returnType? \OPERATOR{} operator formalParameterList |
| . |
| |
| {\bf operator:}`\~{}'; |
| binaryOperator; |
| `[]' ; |
| `[]=' |
| . |
| |
| {\bf binaryOperator:}multiplicativeOperator; |
| additiveOperator; |
| shiftOperator; |
| relationalOperator; |
| `=='; |
| bitwiseOperator |
| . |
| \end{grammar} |
| |
| \LMHash{} |
| An operator declaration is identified using the built-in identifier (\ref{identifierReference}) \OPERATOR{}. |
| |
| \LMHash{} |
| The following names are allowed for user-defined operators: \code{<, >, <=, >=, ==, -, +, /, \~{}/, *, \%, $|$, \^{}, \&, $<<$, $>>$, $>>>$, []=, [], \~{}}. |
| |
| \LMHash{} |
| It is a compile-time error if the arity of the user-declared operator \code{[]=} is not 2. |
| It is a compile-time error if the arity of a user-declared operator with one of the names: \code{<, >, <=, >=, ==, -, +, \~{}/, /, *, \%, $|$, \^{}, \&, $<<$, $>>$, $>>>$, []} is not 1. |
| It is a compile-time error if the arity of the user-declared operator \code{-} is not 0 or 1. |
| |
| \commentary{ |
| The \code{-} operator is unique in that two overloaded versions are permitted. |
| If the operator has no arguments, it denotes unary minus. |
| If it has an argument, it denotes binary subtraction. |
| } |
| |
| \LMHash{} |
| The name of the unary operator \code{-} is \code{unary-}. |
| |
| \rationale{ |
| This device allows the two methods to be distinguished for purposes of method lookup, override and reflection. |
| } |
| |
| \LMHash{} |
| It is a compile-time error if the arity of the user-declared operator \code{\~{}} is not 0. |
| |
| \LMHash{} |
| It is a compile-time error to declare an optional parameter in an operator. |
| |
| \LMHash{} |
| It is a static warning if the return type of the user-declared operator \code{[]=} is explicitly declared and not \VOID{}. |
| |
| \commentary{ |
| If no return type is specified for a user-declared operator \code{[]=}, its return type is \VOID{} (\ref{typeOfAFunction}). |
| } |
| |
| % add rationale: return in []= methods will have no effect, a the expression always returns its second argument (the RHS of the assignment, for consistency with assignment in general). So it's best to enforce this by declaring the method to be void, even though the expression that uses it returns an object with the type of the RHS, as described in \ref{assignment}. |
| |
| |
| \subsection{Getters} |
| \LMLabel{getters} |
| |
| \LMHash{} |
| Getters are functions (\ref{functions}) that are used to retrieve the values of object properties. |
| |
| \begin{grammar} |
| {\bf getterSignature:}returnType? \GET{} identifier |
| . |
| \end{grammar} |
| |
| \LMHash{} |
| If no return type is specified, the return type of the getter is \DYNAMIC{}. |
| |
| \LMHash{} |
| A getter definition that is prefixed with the \STATIC{} modifier defines a static getter. |
| Otherwise, it defines an instance getter. |
| The name of the getter is given by the identifier in the definition. |
| The effect of a static getter declaration in class $C$ is to add an instance getter with the same name and signature to the \code{Type} object for class $C$ that forwards (\ref{functionDeclarations}) to the static getter. |
| |
| \LMHash{} |
| The instance getters of a class $C$ are those instance getters declared by $C$, either implicitly or explicitly, and the instance getters inherited by $C$ from its superclass. |
| The static getters of a class $C$ are those static getters declared by $C$. |
| |
| \commentary{ |
| A getter declaration may conflict with other declarations |
| (\ref{classMemberConflicts}). |
| In particular, a getter can never override a method, |
| and a method can never override a getter or an instance variable. |
| } |
| |
| \LMHash{} |
| It is a static warning if the return type of a getter is \VOID. |
| It is a static warning if a getter $m_1$ overrides (\ref{inheritanceAndOverriding}) a getter |
| $m_2$ and the type of $m_1$ is not a subtype of the type of $m_2$. |
| |
| |
| \subsection{Setters} |
| \LMLabel{setters} |
| |
| \LMHash{} |
| Setters are functions (\ref{functions}) that are used to set the values of object properties. |
| |
| \begin{grammar} |
| {\bf setterSignature:}returnType? \SET{} identifier formalParameterList |
| . |
| \end{grammar} |
| |
| \commentary{ |
| If no return type is specified, the return type of the setter is \VOID{} (\ref{typeOfAFunction}). |
| } |
| |
| \LMHash{} |
| A setter definition that is prefixed with the \STATIC{} modifier defines a static setter. |
| Otherwise, it defines an instance setter. |
| The name of a setter is obtained by appending the string `=' to the identifier given in its signature. |
| The effect of a static setter declaration in class $C$ is to add an instance setter with the same name and signature to the \code{Type} object for class $C$ that forwards (\ref{functionDeclarations}) to the static setter. |
| |
| \commentary{ |
| Hence, a setter name can never conflict with, override or be overridden by a getter or method. |
| } |
| |
| \LMHash{} |
| The instance setters of a class $C$ are those instance setters declared by $C$ either implicitly or explicitly, and the instance setters inherited by $C$ from its superclass. |
| The static setters of a class $C$ are those static setters declared by $C$. |
| |
| \LMHash{} |
| It is a compile-time error if a setter's formal parameter list does not consist of exactly one required formal parameter $p$. |
| \rationale{ |
| We could enforce this via the grammar, but we'd have to specify the evaluation rules in that case. |
| } |
| |
| %It is a compile-time error if a class has both a setter and a method with the same name. This restriction holds regardless of whether the setter is defined explicitly or implicitly, or whether the setter or the method are inherited or not. |
| |
| \LMHash{} |
| It is a static warning if a setter declares a return type other than \VOID{}. |
| It is a static warning if a setter $m_1$ overrides (\ref{inheritanceAndOverriding}) a setter $m_2$ and the type of $m_1$ is not a subtype of the type of $m_2$. |
| It is a static warning if a class has a setter named $v=$ with argument type $T$ and a getter named $v$ with return type $S$, and $T$ may not be assigned to $S$. |
| |
| \commentary{ |
| A setter declaration may conflict with other declarations |
| (\ref{classMemberConflicts}). |
| } |
| |
| |
| \subsection{Abstract Instance Members} |
| \LMLabel{abstractInstanceMembers} |
| |
| \LMHash{} |
| An {\em abstract method} (respectively, {\em abstract getter} or {\em abstract setter)} is an instance method, getter or setter that is not declared \EXTERNAL{} and does not provide an implementation. |
| A {\em concrete method} (respectively, {\em concrete getter} or {\em concrete setter)} is an instance method, getter or setter that is not abstract. |
| |
| \rationale{ |
| Earlier versions of Dart required that abstract members be identified by prefixing them with the modifier \ABSTRACT{}. |
| The elimination of this requirement is motivated by the desire to use abstract classes as interfaces. |
| Every Dart class induces an implicit interface. |
| |
| Using an abstract class instead of an interface has important advantages. |
| An abstract class can provide default implementations; it can also provide static methods, obviating the need for service classes such as \code{Collections} or \code{Lists}, whose entire purpose is to group utilities related to a given type. |
| |
| Eliminating the requirement for an explicit modifier on members makes abstract classes more concise, making abstract classes an attractive substitute for interface declarations. |
| } |
| |
| \commentary{ |
| Invoking an abstract method, getter or setter results in an invocation of \code{noSuchMethod} exactly as if the declaration did not exist, unless a suitable member $a$ is available in a superclass, in which case $a$ is invoked. |
| The normative specification for this appears under the definitions of lookup for methods, getters and setters. |
| } |
| |
| % so does an abstract method override a method in a superclass or not? Does the superclass method get inherited or not? This generally makes the spec inconsistent, as there is no simple answer. |
| % For example - if we say it does not override, then the superclass member is inherited, in which case the rules for warning break down, and also there is question of whether there are two definitions of the same name. |
| % But if we do override, method lookup rules break down. So several things need revisiting. |
| |
| \rationale{ |
| The purpose of an abstract method is to provide a declaration for purposes such as type checking and reflection. |
| In classes used as mixins, it is often useful to introduce such declarations for methods that the mixin expects will be provided by the superclass the mixin is applied to. |
| } |
| %always results in a run-time error. This must be \code{NoSuchMethodError} or an instance of a subclass of \code{NoSuchMethodError}, such as \code{AbstractMethodError}. |
| |
| \LMHash{} |
| It is a static warning if an abstract member $m$ is declared or inherited in a concrete class $C$ unless: |
| \begin{itemize} |
| \item $m$ overrides a concrete member, or |
| \item $C$ has a concrete \code{noSuchMethod()} method distinct from the one declared in class \code{Object}. |
| \end{itemize} |
| |
| \rationale{ |
| We wish to warn if one declares a concrete class with abstract members. |
| However, code like the following should work without warnings: |
| } |
| |
| \begin{dartCode} |
| class Base \{ |
| int get one => 1; |
| \} |
| |
| abstract class Mix \{ |
| int get one; |
| int get two => one + one; |
| \} |
| |
| class C extends Base with Mix \{ \} |
| \end{dartCode} |
| |
| \rationale{ |
| At run time, the concrete method \code{one} declared in \code{Base} will be executed, and no problem should arise. |
| Therefore no warning should be issued and so we suppress warnings if a corresponding concrete member exists in the hierarchy. |
| } |
| |
| |
| \subsection{Instance Variables} |
| \LMLabel{instanceVariables} |
| |
| \LMHash{} |
| Instance variables are variables whose declarations are immediately contained within a class declaration and that are not declared \STATIC{}. |
| The instance variables of a class $C$ are those instance variables declared by $C$ and the instance variables inherited by $C$ from its superclass. |
| |
| \LMHash{} |
| It is a compile-time error if an instance variable is declared to be constant. |
| |
| \rationale{ |
| The notion of a constant instance variable is subtle and confusing to programmers. |
| An instance variable is intended to vary per instance. |
| A constant instance variable would have the same value for all instances, and as such is already a dubious idea. |
| |
| The language could interpret const instance variable declarations as instance getters that return a constant. |
| However, a constant instance variable could not be treated as a true compile-time constant, as its getter would be subject to overriding. |
| |
| Given that the value does not depend on the instance, it is better to use a static class variable. |
| An instance getter for it can always be defined manually if desired. |
| } |
| |
| %An instance variable declaration of one of the forms \code{$T$ $v$;}, \code{\FINAL{} $T$ $v$;} , \code{$T$ $v$ = $e$;}, \code{\CONST{} $T$ $v$ = $e$;} or \code{\FINAL{} $T$ $v$ = $e$;} always induces an implicit getter function (\ref{getters}) with signature |
| |
| %$T$ \GET{} $v$ |
| |
| %whose invocation evaluates to the value stored in $v$. |
| |
| %An instance variable declaration of one of the forms \code{\VAR{} $v$;}, \code{\FINAL{} $v$;}, \code{\VAR{} $v$ = $e$;}, \code{\CONST{} $v$ = $e$;} or \code{\FINAL{} $v$ = $e$;} always induces an implicit getter function with signature |
| |
| %\GET{} $v$ |
| |
| %whose invocation evaluates to the value stored in $v$. |
| |
| %\commentary{Getters are introduced for all instance and static variables (\ref{staticVariables}), regardless of whether they are const/final or not.} |
| |
| %A non-final instance variable declaration of the form \code{$T$ $v$;} or the form \code{$T$ $v$ = $e$;} always induces an implicit setter function (\ref{setters}) with signature |
| |
| %\VOID{} \SET{} $v=(T$ $x)$ |
| |
| %whose execution sets the value of $v$ to the incoming argument $x$. |
| |
| %A non-final instance variable declaration of the form \code{\VAR{} $v$;} or the form \code{\VAR{} $v$ = $e$;} always induces an implicit setter function with signature |
| |
| %\SET{} $v=(x)$ |
| |
| %whose execution sets the value of $v$ to the incoming argument $x$. |
| |
| % It is a compile-time error/warning if a class $C$ declares a final instance variable $v$ and $C$ inherits a setter $v=$. |
| |
| |
| \subsection{Constructors} |
| \LMLabel{constructors} |
| |
| \LMHash{} |
| A {\em constructor} is a special function that is used in instance creation expressions (\ref{instanceCreation}) to obtain objects, typically by creating or initializing them. |
| Constructors may be generative (\ref{generativeConstructors}) or they may be factories (\ref{factories}). |
| |
| \LMHash{} |
| A {\em constructor name} always begins with the name of its immediately enclosing class, and may optionally be followed by a dot and an identifier \id. |
| It is a compile-time error if the name of a constructor is not a constructor name. |
| |
| \commentary{ |
| A constructor declaration may conflict with static member declarations |
| (\ref{classMemberConflicts}). |
| } |
| |
| % In what scope do constructors go? The simple names of named constructors go in the static scope of the class. Unnamed ones go nowhere, but we use the class name to refer to them; the class name could also in the static scope of the class as well to prevent weird errors, or we could ban it explicitly and avoiding duplication. Similarly, the instance scope could contain the constructor names and class name, or we could have special rules to prevent collisions between instance members and constructors or the class. |
| |
| % The enclosing scope of a generative constructor is the instance scope of the class in which it is declared (but what about redirecting?) |
| |
| \LMHash{} |
| If{}f no constructor is specified for a class $C$, it implicitly has a default constructor \code{C() : \SUPER{}() \{\}}, unless $C$ is class \code{Object}. |
| |
| |
| \subsubsection{Generative Constructors} |
| \LMLabel{generativeConstructors} |
| |
| \LMHash{} |
| A {\em generative constructor} consists of a constructor name, a constructor parameter list, and either a redirect clause or an initializer list and an optional body. |
| |
| \begin{grammar} |
| {\bf constructorSignature:}identifier (`{\escapegrammar .}' identifier)? formalParameterList |
| . |
| \end{grammar} |
| |
| \LMHash{} |
| A {\em constructor parameter list} is a parenthesized, comma-separated list of formal constructor parameters. |
| A {\em formal constructor parameter} is either a formal parameter (\ref{formalParameters}) or an initializing formal. |
| An {\em initializing formal} has the form \code{\THIS{}.\id}, where \id{} is the name of an instance variable of the immediately enclosing class. |
| It is a compile-time error if \id{} is not an instance variable of the immediately enclosing class. |
| It is a compile-time error if an initializing formal is used by a function other than a non-redirecting generative constructor. |
| |
| \LMHash{} |
| If an explicit type is attached to the initializing formal, that is its static type. |
| Otherwise, the type of an initializing formal named \id{} is $T_{id}$, where $T_{id}$ is the type of the instance variable named \id{} in the immediately enclosing class. |
| It is a static warning if the static type of \id{} is not a subtype of $T_{id}$. |
| |
| \LMHash{} |
| Initializing formals constitute an exception to the rule that every formal parameter introduces a local variable into the formal parameter scope (\ref{formalParameters}). |
| When the formal parameter list of a non-redirecting generative constructor contains any initializing formals, a new scope is introduced, the {\em formal parameter initializer scope}, which is the current scope of the initializer list of the constructor, and which is enclosed in the scope where the constructor is declared. |
| Each initializing formal in the formal parameter list introduces a final local variable into the formal parameter initializer scope, but not into the formal parameter scope; every other formal parameter introduces a local variable into both the formal parameter scope and the formal parameter initializer scope. |
| |
| \commentary{ |
| This means that formal parameters, including initializing formals, must have distinct names, and that initializing formals are in scope for the initializer list, but they are not in scope for the body of the constructor. |
| When a formal parameter introduces a local variable into two scopes, it is still one variable and hence one storage location. |
| The type of the constructor is defined in terms of its formal parameters, including the initializing formals. |
| } |
| |
| \LMHash{} |
| Initializing formals are executed during the execution of generative constructors detailed below. |
| Executing an initializing formal \code{\THIS{}.\id} causes the instance variable \id{} of the immediately surrounding class to be assigned the value of the corresponding actual parameter, |
| unless \id{} is a final variable that has already been initialized, in which case a run-time error occurs. |
| |
| \commentary{ |
| The above rule allows initializing formals to be used as optional parameters: |
| } |
| |
| \begin{dartCode} |
| class A \{ |
| int x; |
| A([this.x]); |
| \} |
| \end{dartCode} |
| |
| \commentary{ |
| is legal, and has the same effect as |
| } |
| |
| \begin{dartCode} |
| class A \{ |
| int x; |
| A([int x]): this.x = x; |
| \} |
| \end{dartCode} |
| |
| \LMHash{} |
| A {\em fresh instance} is an instance whose identity is distinct from any previously allocated instance of its class. |
| A generative constructor always operates on a fresh instance of its immediately enclosing class. |
| |
| \commentary{ |
| The above holds if the constructor is actually run, as it is by \NEW{}. |
| If a constructor $c$ is referenced by \CONST{}, $c$ may not be run; instead, a canonical object may be looked up. |
| See the section on instance creation (\ref{instanceCreation}). |
| } |
| |
| \LMHash{} |
| If a generative constructor $c$ is not a redirecting constructor and no body is provided, then $c$ implicitly has an empty body \code{\{\}}. |
| |
| |
| \paragraph{Redirecting Constructors} |
| \LMLabel{redirectingConstructors} |
| |
| \LMHash{} |
| A generative constructor may be {\em redirecting}, in which case its only action is to invoke another generative constructor. |
| A redirecting constructor has no body; instead, it has a redirect clause that specifies which constructor the invocation is redirected to, and with what arguments. |
| |
| \begin{grammar} |
| {\bf redirection:}`{\escapegrammar :}' \THIS{} (`{\escapegrammar .}' identifier)? arguments |
| . |
| \end{grammar} |
| |
| |
| \paragraph{Initializer Lists} |
| \LMLabel{initializerLists} |
| |
| \LMHash{} |
| An initializer list begins with a colon, and consists of a comma-separated list of individual {\em initializers}. |
| There are two kinds of initializers. |
| \begin{itemize} |
| \item A {\em superinitializer} identifies a {\em superconstructor} - that is, a specific constructor of the superclass. |
| Execution of the superinitializer causes the initializer list of the superconstructor to be executed. |
| \item An {\em instance variable initializer} assigns a value to an individual instance variable. |
| \end{itemize} |
| |
| \begin{grammar} |
| {\bf initializers:}`{\escapegrammar :}' initializerListEntry (\gcomma{} initializerListEntry)* |
| . |
| |
| {\bf initializerListEntry:}\SUPER{} arguments; |
| \SUPER{} `{\escapegrammar .}' identifier arguments; |
| fieldInitializer; |
| assertion |
| . |
| |
| {\bf fieldInitializer:}(\THIS{} `{\escapegrammar .}')? identifier `=' conditionalExpression cascadeSection* |
| . |
| \end{grammar} |
| |
| \LMHash{} |
| Let $k$ be a generative constructor. |
| Then $k$ may include at most one superinitializer in its initializer list or a compile-time error occurs. |
| If no superinitializer is provided, an implicit superinitializer of the form \SUPER{}() is added at the end of $k$'s initializer list, |
| unless the enclosing class is class \code{Object}. |
| It is a compile-time error if more than one initializer corresponding to a given instance variable appears in $k$'s initializer list. |
| It is a compile-time error if $k$'s initializer list contains an initializer for a variable that is initialized by means of an initializing formal of $k$. |
| It is a compile-time error if $k$'s initializer list contains an initializer for a final variable $f$ whose declaration includes an initialization expression. |
| It is a compile-time error if $k$ includes an initializing formal for a final variable $f$ whose declaration includes an initialization expression. |
| |
| \LMHash{} |
| Each final instance variable $f$ declared in the immediately enclosing class must have an initializer in $k$'s initializer list unless it has already been initialized by one of the following means: |
| \begin{itemize} |
| \item Initialization at the declaration of $f$. |
| \item Initialization by means of an initializing formal of $k$. |
| \end{itemize} |
| |
| or a static warning occurs. |
| It is a compile-time error if $k$'s initializer list contains an initializer for a variable that is not an instance variable declared in the immediately surrounding class. |
| |
| \commentary{ |
| The initializer list may of course contain an initializer for any instance variable declared by the immediately surrounding class, even if it is not final. |
| } |
| |
| \LMHash{} |
| It is a compile-time error if a generative constructor of class \code{Object} includes a superinitializer. |
| |
| \LMHash{} |
| Execution of a generative constructor $k$ of type $T$ to initialize a fresh instance $i$ |
| is always done with respect to a set of bindings for its formal parameters |
| and the type parameters of the immediately enclosing class bound to a set of actual type arguments of $T$, $V_1, \ldots, V_m$. |
| |
| \commentary{ |
| These bindings are usually determined by the instance creation expression that invoked the constructor (directly or indirectly). |
| However, they may also be determined by a reflective call. |
| } |
| |
| \LMHash{} |
| If $k$ is redirecting then its redirect clause has the form |
| |
| \code{\THIS{}.$g$($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} |
| |
| where $g$ identifies another generative constructor of the immediately surrounding class. |
| Then execution of $k$ to initialize $i$ proceeds by evaluating the argument list |
| \code{($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}, |
| and then executing $g$ to initialize $i$ with respect to the bindings resulting from the evaluation of |
| \code{($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} |
| and with \THIS{} bound to $i$ and the type parameters of the immediately enclosing class bound to $V_1, \ldots, V_m$. |
| |
| \LMHash{} |
| Otherwise, execution proceeds as follows: |
| |
| \LMHash{} |
| The instance variable declarations of the immediately enclosing class are visited in the order they appear in the program text. |
| For each such declaration $d$, if $d$ has the form \code{$finalConstVarOrType$ $v$ = $e$; } |
| then $e$ is evaluated to an object $o$ |
| and the instance variable $v$ of $i$ is bound to $o$. |
| |
| \LMHash{} |
| Any initializing formals declared in $k$'s parameter list are executed in the order they appear in the program text. |
| % In fact, this order is unobservable; this could be done any time prior to running the body, since |
| % these only effect \THIS{}. |
| Then, the initializers of $k$'s initializer list are executed to initialize $i$ |
| in the order they appear in the program. |
| |
| \rationale{ |
| We could observe the order by side effecting external routines called. |
| So we need to specify the order. |
| } |
| |
| \LMHash{} |
| Then if any instance variable of $i$ declared by the immediately enclosing class |
| is not yet bound to a value, |
| all such variables are initialized with the null object (\ref{null}). |
| |
| \LMHash{} |
| Then, unless the enclosing class is \code{Object}, the explicitly specified or |
| implicitly added superinitializer (\ref{initializerLists}) is executed to |
| further initialize $i$. |
| |
| \commentary{ |
| The super constructor call can be written anywhere |
| in the initializer list of $k$, |
| but the actual call always happens after all initializers have been processed. |
| It is not equivalent to moving the super call to the end of the initializer list |
| because the argument expressions may have visible side effects |
| which must happen in the order the expressions occur in the program text. |
| } |
| |
| \LMHash{} |
| After the superinitializer has completed, the body of $k$ is executed in a scope where \THIS{} is bound to $i$. |
| |
| \rationale{ |
| This process ensures that no uninitialized final instance variable is ever seen by code. |
| Note that \THIS{} is not in scope on the right hand side of an initializer (see \ref{this}) so no instance method can execute during initialization: |
| an instance method cannot be directly invoked, nor can \THIS{} be passed into any other code being invoked in the initializer. |
| } |
| |
| \LMHash{} |
| During the execution of a generative constructor to initialize an instance $i$, |
| execution of an initializer of the form \code{\THIS{}.$v$ = $e$} |
| proceeds as follows: |
| |
| \LMHash{} |
| First, the expression $e$ is evaluated to an object $o$. |
| Then, the instance variable $v$ of $i$ is bound to $o$. |
| In checked mode, it is a dynamic type error if $o$ is not the null object (\ref{null}) and the interface of the class of $o$ is not a subtype of the actual type of the instance variable $v$. |
| |
| \LMHash{} |
| An initializer of the form \code{$v$ = $e$} is equivalent to an initializer of the form \code{\THIS{}.$v$ = $e$}. |
| |
| \LMHash{} |
| Execution of an initializer that is an assertion proceeds by executing the assertion (\ref{assert}). |
| |
| \LMHash{} |
| Execution of a superinitializer of the form |
| |
| \code{\SUPER{}($a_1, \ldots,\ a_n,\ x_{n+1}: a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} |
| |
| (respectively |
| \code{\SUPER{}.\id($a_1, \ldots,\ a_n,\ x_{n+1}: a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}) |
| |
| proceeds as follows: |
| |
| \LMHash{} |
| First, the argument list |
| \code{($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)} |
| is evaluated. |
| |
| \LMHash{} |
| Then, after the remainder of the initializer list of $k$ has been executed, |
| the superconstructor is executed as follows: |
| |
| \LMHash{} |
| Let $C$ be the class in which the superinitializer appears and let $S$ be the superclass of $C$. |
| If $S$ is generic (\ref{generics}), let $U_1, \ldots, U_m$ be the actual type arguments passed to $S$ in the superclass clause of $C$. |
| |
| \LMHash{} |
| The generative constructor $S$ (respectively \code{$S$.\id}) of $S$ is executed |
| to initialize $i$ with respect to the bindings that resulted from the evaluation of |
| \code{($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}, |
| and the type parameters (if any) of class $S$ bound to $U_1, \ldots, U_m$. |
| |
| \LMHash{} |
| It is a compile-time error if class $S$ does not declare a generative constructor named $S$ (respectively \code{$S$.\id}). |
| |
| |
| \subsubsection{Factories} |
| \LMLabel{factories} |
| |
| \LMHash{} |
| A {\em factory} is a constructor prefaced by the built-in identifier (\ref{identifierReference}) \FACTORY{}. |
| |
| \begin{grammar} |
| {\bf factoryConstructorSignature:}\FACTORY{} identifier (`{\escapegrammar .}' identifier)? formalParameterList |
| . |
| \end{grammar} |
| |
| %The enclosing scope of a factory constructor is the static scope \ref{} of the class in which it is declared. |
| |
| \LMHash{} |
| The {\em return type} of a factory whose signature is of the form \FACTORY{} $M$ or the form \FACTORY{} \code{$M$.\id} is $M$ if $M$ is not a generic type; |
| otherwise the return type is \code{$M$<$T_1, \ldots,\ T_n$>} where $T_1, \ldots, T_n$ are the type parameters of the enclosing class. |
| |
| \LMHash{} |
| It is a compile-time error if $M$ is not the name of the immediately enclosing class. |
| |
| \LMHash{} |
| In checked mode, it is a dynamic type error if a factory returns a non-null object whose type is not a subtype of its actual (\ref{actualTypeOfADeclaration}) return type. |
| |
| \rationale{ |
| It seems useless to allow a factory to return the null object (\ref{null}). |
| But it is more uniform to allow it, as the rules currently do. |
| } |
| |
| \rationale{ |
| Factories address classic weaknesses associated with constructors in other languages. |
| Factories can produce instances that are not freshly allocated: they can come from a cache. |
| Likewise, factories can return instances of different classes. |
| } |
| |
| |
| \paragraph{Redirecting Factory Constructors} |
| \LMLabel{redirectingFactoryConstructors} |
| |
| \LMHash{} |
| A {\em redirecting factory constructor} specifies a call to a constructor of another class that is to be used whenever the redirecting constructor is called. |
| |
| \begin{grammar} |
| {\bf redirectingFactoryConstructorSignature:}\CONST{}? \FACTORY{} identifier (`{\escapegrammar .}' identifier)? formalParameterList |
| \gnewline{} `=' type (`{\escapegrammar .}' identifier)? |
| . |
| \end{grammar} |
| |
| \LMHash{} |
| Calling a redirecting factory constructor $k$ causes the constructor $k'$ denoted by $type$ (respectively, $type.identifier$) to be called with the actual arguments passed to $k$, and returns the result of $k'$ as the result of $k$. |
| The resulting constructor call is governed by the same rules as an instance creation expression using \NEW{} (\ref{instanceCreation}). |
| |
| \commentary{ |
| It follows that if \metavar{type} or \code{\metavar{type}.\id} are not defined, or do not refer to a class or constructor, a dynamic error occurs, as with any other undefined constructor call. |
| The same holds if $k$ is called with fewer required parameters or more positional parameters than $k'$ expects, or if $k$ is called with a named parameter that is not declared by $k'$. |
| } |
| |
| \LMHash{} |
| It is a compile-time error if $k$ explicitly specifies a default value for an optional parameter. |
| \commentary{ |
| Default values specified in $k$ would be ignored, since it is the {\em actual} parameters that are passed to $k'$. |
| Hence, default values are disallowed. |
| } |
| |
| \LMHash{} |
| It is a run-time error if a redirecting factory constructor redirects to itself, either directly or indirectly via a sequence of redirections. %does not redirect to a non-redirecting factory constructor or to a generative constructor in a finite number of steps. |
| |
| % Make this a run-time error so deferred loading works |
| |
| \rationale{ |
| If a redirecting factory $F_1$ redirects to another redirecting factory $F_2$ and $F_2$ then redirects to $F_1$, then both $F_1$ and $F_2$ are ill-defined. |
| Such cycles are therefore illegal. |
| } |
| |
| \LMHash{} |
| It is a static warning if $type$ does not denote a class accessible in the current scope; if $type$ does denote such a class $C$ it is a static warning if the referenced constructor (be it \metavar{type} or \code{\metavar{type}.\id}) is not a constructor of $C$. |
| |
| \commentary{ |
| Note that it is not possible to modify the arguments being passed to $k'$. |
| } |
| % but we have the same issue with other redirecting constructors, no?) |
| \rationale{ |
| At first glance, one might think that ordinary factory constructors could simply create instances of other classes and return them, and that redirecting factories are unnecessary. |
| However, redirecting factories have several advantages: |
| \begin{itemize} |
| \item An abstract class may provide a constant constructor that utilizes the constant constructor of another class. |
| \item A redirecting factory constructors avoids the need for forwarders to repeat the default values for formal parameters in their signatures. |
| %\item A generic factory class that aggregates factory constructors for types it does not implement can still have its type arguments passed correctly. |
| \end{itemize} |
| |
| %An example of the latter point: |
| %} |
| |
| %\begin{dartCode} |
| %\CLASS{} W<T> \IMPLEMENTS{} A<T> { W(w) {...} ...} |
| %\CLASS{} X<T> \IMPLEMENTS{} A<T> { X(x) {...} ...} |
| %\CLASS{} Y<T> \IMPLEMENTS{} A<T> { Y(y) {...} ...} |
| %\CLASS{} Z<T> \IMPLEMENTS{} A<T> { Z(z) {...} ...} |
| |
| %\CLASS{} F<T> { // note that F does not implement A |
| % \STATIC{} F<T> idw(w) => \NEW{} W<T>(w); // illegal - T not in scope in idw |
| % \FACTORY{} F.idx(x) => \NEW{} X<T>(x); |
| % \FACTORY{} F.idy(y) => \NEW{} Y<T>(y); |
| % \STATIC{} F idz(z) => \NEW{} Z(z); // does not capture the type argument |
| %} |
| |
| %\CLASS{} A<T>{ |
| % \FACTORY{} A.idw(w) => F<T>.idw(w); |
| %// illegal - cannot pass type parameter to static method |
| % \FACTORY{} A.idx(x) $=> \NEW{} $F<T>.idx(x); // works, but allocates a gratuitous instance of F |
| % \FACTORY{} A.idy(y) = Y<T>; // works |
| % \FACTORY{} A.idz(z) => F.idz(z); // wrong - returns Z<Dynamic>; no way to pass type argument |
| } |
| %\end{dartCode} |
| |
| \LMHash{} |
| It is a compile-time error if $k$ is prefixed with the \CONST{} modifier but $k'$ is not a constant constructor (\ref{constantConstructors}). |
| |
| \LMHash{} |
| It is a static warning if the function type of $k'$ is not a subtype of the type of $k$. |
| |
| \commentary{ |
| This implies that the resulting object conforms to the interface of the immediately enclosing class of $k$. |
| } |
| |
| \LMHash{} |
| It is a static type warning if any of the type arguments to $k'$ are not subtypes of the bounds of the corresponding formal type parameters of $type$. |
| |
| |
| \subsubsection{Constant Constructors} |
| \LMLabel{constantConstructors} |
| |
| \LMHash{} |
| A {\em constant constructor} may be used to create compile-time constant (\ref{constants}) objects. |
| A constant constructor is prefixed by the reserved word \CONST{}. |
| |
| \begin{grammar} |
| {\bf constantConstructorSignature:}\CONST{} qualified formalParameterList |
| . |
| \end{grammar} |
| |
| %\commentary{Spell out subtleties: a constant constructor call within the initializer of a constant constructor is treated as a ordinary constructor call (a new), because the arguments cannot be assumed constant anymore. In practice, this means two versions are compiled and analyzed. One for new and one for const.} |
| |
| \commentary{ |
| All the work of a constant constructor must be handled via its initializers. |
| } |
| |
| \LMHash{} |
| It is a compile-time error if a constant constructor is declared by a class that has a non-final instance variable. |
| |
| \commentary{ |
| The above refers to both locally declared and inherited instance variables. |
| } |
| |
| \LMHash{} |
| It is a compile-time error if a constant constructor is declared by a class $C$ if any instance variable declared in $C$ is initialized with an expression that is not a constant expression. |
| |
| \commentary{ |
| A superclass of $C$ cannot declare such an initializer either, because it must necessarily declare constant constructor as well (unless it is \code{Object}, which declares no instance variables). |
| } |
| |
| \LMHash{} |
| The superinitializer that appears, explicitly or implicitly, in the initializer list of a constant constructor must specify a constant constructor of the superclass of the immediately enclosing class or a compile-time error occurs. |
| |
| \LMHash{} |
| Any expression that appears within the initializer list of a constant constructor must be a potentially constant expression, or a compile-time error occurs. |
| |
| \LMHash{} |
| A {\em potentially constant expression} is an expression $e$ that would be a valid constant expression if all formal parameters of $e$'s immediately enclosing constant constructor were treated as compile-time constants that were guaranteed to evaluate to an integer, boolean or string value as required by their immediately enclosing superexpression, and where $e$ is also a valid expression if all the formal parameters are treated as non-constant variables. |
| |
| \commentary{ |
| Note that a parameter that is not used in a superexpression that is restricted to certain types can be a constant of any type. |
| For example |
| } |
| |
| \begin{dartCode} |
| \CLASS{} A \{ |
| \FINAL{} m; |
| \CONST{} A(this.m); |
| \} |
| \end{dartCode} |
| |
| \commentary{ |
| can be instantiated via \code{\CONST{} A(\CONST []);} |
| } |
| |
| \commentary{ |
| The difference between a potentially constant expression and a compile-time constant expression (\ref{const}) deserves some explanation. |
| |
| The key issue is whether one treats the formal parameters of a constructor as compile-time constants. |
| |
| If a constant constructor is invoked from a constant object expression, the actual arguments will be required to be compile-time constants. |
| Therefore, if we were assured that constant constructors were always invoked from constant object expressions, we could assume that the formal parameters of a constructor were compile-time constants. |
| |
| However, constant constructors can also be invoked from ordinary instance creation expressions (\ref{new}), and so the above assumption is not generally valid. |
| |
| Nevertheless, the use of the formal parameters of a constant constructor within the constructor is of considerable utility. |
| The concept of potentially constant expressions is introduced to facilitate limited use of such formal parameters. |
| Specifically, we allow the usage of the formal parameters of a constant constructor for expressions that involve built-in operators, but not for constant objects, lists and maps. |
| This allows for constructors such as: |
| } |
| |
| \begin{dartCode} |
| \CLASS{} C \{ |
| \FINAL{} x; \FINAL{} y; \FINAL{} z; |
| \CONST{} C(p, q): x = q, y = p + 100, z = p + q; |
| \} |
| \end{dartCode} |
| |
| \commentary{ |
| The assignment to \code{x} is allowed under the assumption that \code{q} is a compile-time constant (even though \code{q} is not, in general a compile-time constant). |
| The assignment to \code{y} is similar, but raises additional questions. |
| In this case, the superexpression of \code{p} is \code{p + 100}, and it requires that \code{p} be a numeric compile-time constant for the entire expression to be considered constant. |
| The wording of the specification allows us to assume that \code{p} evaluates to an integer. |
| A similar argument holds for \code{p} and \code{q} in the assignment to \code{z}. |
| |
| However, the following constructors are disallowed: |
| } |
| |
| \begin{dartCode} |
| \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 |
| \} |
| \end{dartCode} |
| |
| \commentary{ |
| The problem is not that the assignments to \code{w} are not potentially constant; they are. |
| However, all these run afoul of the rules for constant lists (\ref{lists}), maps (\ref{maps}) and objects (\ref{const}), all of which independently require their subexpressions to be constant expressions. |
| } |
| |
| \rationale{ |
| All of the illegal constructors of \code{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 \code{C()} are constants, but are not numeric. |
| This is precluded by the following rule, combined with the rules for evaluating constant objects (\ref{const}). |
| } |
| |
| \LMHash{} |
| When invoked from a constant object expression, a constant constructor must throw an exception if any of its actual parameters is a value that would prevent one of the potentially constant expressions within it from being a valid compile-time constant. |
| |
| %Discuss External Constructors in ne subsubsection here |
| |
| |
| \subsection{Static Methods} |
| \LMLabel{staticMethods} |
| |
| \LMHash{} |
| {\em Static methods} are functions, other than getters or setters, whose declarations are immediately contained within a class declaration and that are declared \STATIC{}. |
| The static methods of a class $C$ are those static methods declared by $C$. |
| |
| \LMHash{} |
| The effect of a static method declaration in class $C$ is to add an instance method with the same name and signature to the \code{Type} object for class $C$ that forwards (\ref{functionDeclarations}) to the static method. |
| |
| \rationale{ |
| 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. |
| } |
| |
| \commentary{ |
| Static method declarations may conflict with other declarations |
| (\ref{classMemberConflicts}). |
| } |
| |
| |
| \subsection{Static Variables} |
| \LMLabel{staticVariables} |
| |
| \LMHash{} |
| {\em Static variables} are variables whose declarations are immediately contained within a class declaration and that are declared \STATIC{}. |
| The static variables of a class $C$ are those static variables declared by $C$. |
| |
| %A static variable declaration of one of the forms \code{\STATIC{} $T$ $v$;}, \code{\STATIC{} $T$ $v$ = $e$;}, \code{\STATIC{} \CONST{} $T$ $v$ = $e$;} or \code{\STATIC{} \FINAL{} $T$ $v$ = $e$;} always induces an implicit static getter function (\ref{getters}) with signature |
| |
| %\STATIC{} $T$ \GET{} $v$ |
| |
| %whose invocation evaluates as described below (\ref{evaluationOfStaticVariableGetters}).%to the value stored in $v$. |
| |
| %A static variable declaration of one of the forms \code{\STATIC{} \VAR{} $v$;}, \code{\STATIC{} \VAR{} $v$ = $e$;}, \code{\STATIC{} \CONST{} $v$ = $e$;} or \code{\STATIC{} \FINAL{} $v$ = $e$;} always induces an implicit static getter function with signature |
| |
| %\STATIC{} \GET{} $v$ |
| |
| %whose invocation evaluates as described below (\ref{evaluationOfStaticVariableGetters}).%to the value stored in $v$. |
| |
| %A non-final static variable declaration of the form \code{\STATIC{} $T$ $v$;} or the form \code{\STATIC{} $T$ $v$ = $e$;} always induces an implicit static setter function (\ref{setters}) with signature |
| |
| %\STATIC{} \VOID{} \SET{} $v=(T$ $x)$ |
| |
| %whose execution sets the value of $v$ to the incoming argument $x$. |
| |
| %A static variable declaration of the form \code{\STATIC{} \VAR{} $v$;} or the form \code{\STATIC{} \VAR{} $v$ = $e$;} always induces an implicit static setter function with signature |
| |
| %\STATIC{} \SET{} $v=(x)$ |
| |
| %whose execution sets the value of $v$ to the incoming argument $x$. |
| |
| %Extrernal static functions, getters, setters |
| |
| %\subsubsection{Evaluation of Implicit Static Variable Getters} |
| %\LMLabel{evaluationOfStaticVariableGetters} |
| |
| %Let $d$ be the declaration of a static variable $v$. The implicit getter method of $v$ executes as follows: |
| %\begin{itemize} |
| %\item If $d$ is of one of the forms \code{\STATIC{} \VAR{} $v$ = $e$;} , \code{\STATIC{} $T$ $v$ = $e$; }, \code{\STATIC{} \FINAL{} $v$ = $e$; } or \code{\STATIC{} \FINAL{} $T$ $v$ = $e$;} and no value has yet been stored into $v$ then the initializer expression $e$ is evaluated. If, during the evaluation of $e$, the getter for $v$ is referenced, a \code{CyclicInitializationError} is thrown. If the evaluation succeeded yielding an object $o$, let $r$ be $o$, otherwise let $r$ be the null object (\ref{null}). In any case, $r$ is stored into $v$. The result of executing the getter is $r$. |
| %\item If $d$ is of one of the forms \code{\STATIC{} \CONST{} $v$ = $e$; } or \code{\STATIC{} \CONST{} $T$ $v$ = $e$;} the result of the getter is the value of the compile-time constant $e$. |
| %Otherwise |
| %\item The result of executing the getter method is the value stored in $v$. |
| %\end{itemize} |
| |
| |
| \subsection{Superclasses} |
| \LMLabel{superclasses} |
| |
| \LMHash{} |
| The superclass $S^\prime$ of a class $C$ that has a with clause \code{\WITH{} $M_1, \ldots,\ M_k$} and an extends clause \code{\EXTENDS{} $S$} is the application of mixin composition (\ref{mixins}) $M_k* \cdots * M_1$ to $S$. The name $S^\prime$ is a fresh identifier. |
| If no \WITH{} clause is specified then the \EXTENDS{} clause of a class $C$ specifies its superclass. |
| If no \EXTENDS{} clause is specified, then either: |
| \begin{itemize} |
| \item $C$ is \code{Object}, which has no superclass. OR |
| \item Class $C$ is deemed to have an \EXTENDS{} clause of the form \code{\EXTENDS{} Object}, and the rules above apply. |
| \end{itemize} |
| |
| \LMHash{} |
| It is a compile-time error to specify an \EXTENDS{} clause for class \code{Object}. |
| |
| \begin{grammar} |
| {\bf superclass:}\EXTENDS{} type |
| . |
| \end{grammar} |
| |
| %The superclass clause of a class C is processed within the enclosing scope of the static scope of C. |
| %\commentary{ |
| %This means that in a generic class, the type parameters of the generic are available in the superclass clause. |
| %} |
| |
| \LMHash{} |
| The scope of the \EXTENDS{} and \WITH{} clauses of a class $C$ is the type-parameter scope of $C$. |
| |
| \LMHash{} |
| It is a compile-time error if the \EXTENDS{} clause of a class $C$ specifies a type variable (\ref{generics}), a type alias (\ref{typedef}), an enumerated type (\ref{enums}), a malformed type, or a deferred type (\ref{staticTypes}) as a superclass. |
| It is a compile-time error if the \EXTENDS{} clause of a class $C$ specifies type \DYNAMIC{} as a superinterface. |
| |
| \commentary{ |
| 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: |
| } |
| |
| \begin{dartCode} |
| class T \{\} |
| |
| /* Compilation error: Attempt to subclass a type parameter */ |
| class G<T> extends T \{\} |
| |
| \end{dartCode} |
| |
| \LMHash{} |
| A class $S$ is {\em a superclass} of a class $C$ if{}f either: |
| \begin{itemize} |
| \item $S$ is the superclass of $C$, or |
| \item $S$ is a superclass of a class $S'$, |
| and $S'$ is the superclass of $C$. |
| \end{itemize} |
| |
| \LMHash{} |
| It is a compile-time error if a class $C$ is a superclass of itself. |
| |
| |
| \subsubsection{Inheritance and Overriding} |
| \LMLabel{inheritanceAndOverriding} |
| |
| %A class $C$ {\em inherits} any accessible instance members of its superclass that are not overridden by members declared in $C$. |
| |
| \LMHash{} |
| Let $C$ be a class, let $A$ be a superclass of $C$, and let $S_1, \ldots, S_k$ be superclasses of $C$ that are also subclasses of $A$. |
| $C$ {\em inherits} all accessible instance members of $A$ that have not been overridden by a declaration in $C$ or in at least one of $S_1, \ldots, S_k$. |
| |
| \rationale{ |
| It would be more attractive to give a purely local definition of inheritance, that depended only on the members of the direct superclass $S$. |
| However, a class $C$ can inherit a member $m$ that is not a member of its superclass $S$. |
| This can occur when the member $m$ is private to the library $L_1$ of $C$, |
| whereas $S$ comes from a different library $L_2$, |
| but the superclass chain of $S$ includes a class declared in $L_1$. |
| } |
| |
| \LMHash{} |
| A class may override instance members that would otherwise have been inherited from its superclass. |
| |
| \LMHash{} |
| Let $C = S_0$ be a class declared in library $L$, and let $\{S_1, \ldots, S_k\}$ be the set of all superclasses of $C$, where $S_i$ is the superclass of $S_{i-1}$ for $i \in 1 .. k$. |
| Let $C$ declare a member $m$, and let $m'$ be a member of $S_j, j \in 1 .. k$, that has the same name as $m$, such that $m'$ is accessible to $L$. |
| Then $m$ overrides $m'$ if $m'$ is not already overridden by a member of at least one of $S_1, \ldots, S_{j-1}$ and neither $m$ nor $m'$ are instance variables. |
| |
| %Let $C$ be a class declared in library $L$, with superclass $S$ and let $C$ declare an instance member $m$, and assume $S$ declares an instance member $m'$ with the same name as $m$. Then $m$ {\em overrides} $m'$ if{}f $m'$ is accessible (\ref{privacy}) to $L$, $m$ has the same name as $m'$ and neither $m$ nor $m'$ are fields. |
| |
| \commentary{ |
| Instance variables never override each other. |
| The getters and setters induced by instance variables do. |
| } |
| |
| \rationale{ |
| Again, a local definition of overriding would be preferable, but fails to account for library privacy. |
| } |
| |
| \LMHash{} |
| Whether an override is legal or not is described elsewhere in this specification (see \ref{instanceMethods}, \ref{getters} and \ref{setters}). |
| |
| \commentary{ |
| For example getters may not legally override methods and vice versa. |
| Setters never override methods or getters, and vice versa, because their names always differ. |
| } |
| |
| \rationale{ |
| It is nevertheless convenient to define the override relation between members in this way, so that we can concisely describe the illegal cases. |
| } |
| |
| \commentary{ |
| Note that instance variables do not participate in the override relation, but the getters and setters they induce do. |
| Also, getters don't override setters and vice versa. |
| Finally, static members never override anything. |
| } |
| |
| \LMHash{} |
| It is a static warning if a concrete class inherits an abstract method. |
| |
| \commentary{ |
| For convenience, here is a summary of the relevant rules. |
| Remember that this is not normative. |
| The controlling language is in the relevant sections of the specification. |
| |
| \begin{enumerate} |
| |
| \item There is only one namespace for getters, setters, methods and constructors (\ref{scoping}). |
| An instance or static variable $f$ introduces a getter $f$ and a non-final instance or static variable $f$ also introduces a setter $f=$ (\ref{instanceVariables}, \ref{staticVariables}). |
| When we speak of members here, we mean accessible instance or static variables, getters, setters, and methods (\ref{classes}). |
| \item You cannot have two members with the same name in the same class - be they declared or inherited (\ref{scoping}, \ref{classes}). |
| \item Static members are never inherited. |
| \item It is a warning if you have an static member named $m$ in your class or any superclass (even though it is not inherited) and an instance member of the same name (\ref{instanceMethods}, \ref{getters}, \ref{setters}). |
| \item It is a warning if you have a static setter $v=$, and an instance member $v$ (\ref{setters}). |
| \item It is a warning if you have a static getter $v$ and an instance setter $v=$ (\ref{getters}). |
| \item If you define an instance member named $m$, and your superclass has an instance member of the same name, they override each other. |
| This may or may not be legal. |
| \item \label{typeSigAssignable} |
| If two members override each other, it is a static warning if their type signatures are not assignable to each other (\ref{instanceMethods}, \ref{getters}, \ref{setters}) (and since these are function types, this means the same as "subtypes of each other"). |
| \item \label{requiredParams} |
| If two members override each other, it is a static warning if the overriding member has more required parameters than the overridden one (\ref{instanceMethods}). |
| \item \label{optionalPositionals} |
| If two members override each other, it is a static warning if the overriding member has fewer positional parameters than the overridden one (\ref{instanceMethods}). |
| \item \label{namedParams} |
| If two members override each other, it is a static warning if the overriding member does not have all the named parameters that the overridden one has (\ref{instanceMethods}). |
| \item Setters, getters and operators never have optional parameters of any kind; it's a compile-time error (\ref{operators}, \ref{getters}, \ref{setters}). |
| \item It is a compile-time error if a member has the same name as its enclosing class (\ref{classes}). |
| \item A class has an implicit interface (\ref{classes}). |
| \item Superinterface members are not inherited by a class, but are inherited by its implicit interface. |
| Interfaces have their own inheritance rules (\ref{interfaceInheritanceAndOverriding}). |
| \item A member is abstract if it has no body and is not labeled \EXTERNAL{} (\ref{abstractInstanceMembers}, \ref{externalFunctions}). |
| \item A class is abstract if{}f it is explicitly labeled \ABSTRACT{}.% or if it declares (not just inherits) an abstract member (\ref{classes}). |
| \item It is a static warning if a concrete class has an abstract member (declared or inherited). |
| \item It is a static warning and a dynamic error to call a non-factory constructor of an abstract class (\ref{new}). |
| \item If a class defines an instance member named $m$, and any of its superinterfaces have a member named $m$, the interface of the class overrides $m$. |
| \item An interface inherits all members of its superinterfaces that are not overridden and not members of multiple superinterfaces. |
| \item If multiple superinterfaces of an interface define a member with the same name $m$, then at most one member is inherited. |
| That member (if it exists) is the one whose type is a subtype of all the others. |
| If there is no such member, then: |
| \begin{itemize} |
| \item A static warning is given. |
| \item If possible the interface gets a member named $m$ that has the minimum number of required parameters among all the members in the superinterfaces, the maximal number of positionals, and the superset of named parameters. |
| The types of these are all \DYNAMIC{}. |
| If this is impossible then no member $m$ appears in the interface. |
| \end{itemize} (\ref{interfaceInheritanceAndOverriding}) |
| \item Rule \ref{typeSigAssignable} applies to interfaces as well as classes (\ref{interfaceInheritanceAndOverriding}). |
| \item It is a static warning if a concrete class does not have an implementation for a method in any of its superinterfaces unless it has a concrete \code{noSuchMethod} method (\ref{superinterfaces}) distinct from the one in class \code{Object}. |
| \item The identifier of a named constructor cannot be the same as the name of a member declared (as opposed to inherited) in the same class (\ref{constructors}). |
| \end{enumerate} |
| } |
| |
| %Can we have abstract getters and setters? |
| |
| |
| \subsection{Superinterfaces} |
| \LMLabel{superinterfaces} |
| % what about rules about classes that fail to implement their interfaces? |
| |
| \LMHash{} |
| A class has a set of direct superinterfaces. |
| This set includes the interface of its superclass and the interfaces specified in the \IMPLEMENTS{} clause of the class. |
| |
| \begin{grammar} |
| {\bf interfaces:}\IMPLEMENTS{} typeList |
| . |
| \end{grammar} |
| |
| \LMHash{} |
| The scope of the \IMPLEMENTS{} clause of a class $C$ is the type-parameter scope of $C$. |
| |
| \LMHash{} |
| It is a compile-time error if the \IMPLEMENTS{} clause of a class $C$ specifies a type variable (\ref{generics}), a type alias (\ref{typedef}), an enumerated type (\ref{enums}), a malformed type, or a deferred type (\ref{staticTypes}) as a superinterface. |
| It is a compile-time error if the \IMPLEMENTS{} clause of a class $C$ specifies type \DYNAMIC{} as a superinterface. |
| It is a compile-time error if the \IMPLEMENTS{} clause of a class $C$ specifies a type $T$ as a superinterface more than once. |
| It is a compile-time error if the superclass of a class $C$ is specified as a superinterface of $C$. |
| It is a compile-time error if a class $C$ has two superinterfaces that are different instantiations of the same generic class. \commentary{For example, a class may not have both `List<int>` and `List<num>` as superinterfaces.} |
| % If we need to allow multiple instantiations, they'll need to have a most |
| % specific one, and then we can add the following clause |
| %, unless it implements one that is a subtype of all the other. \commentary{This ensures that each class implements one {\em most specific} version of a generic class' interface.} |
| |
| \rationale{ |
| One might argue that it is harmless to repeat a type in the superinterface list, so why make it an error? The issue is not so much that the situation described in program source is erroneous, but that it is pointless. |
| As such, it is an indication that the programmer may very well have meant to say something else - and that is a mistake that should be called to her or his attention. |
| Nevertheless, we could simply issue a warning; and perhaps we should and will. |
| That said, problems like these are local and easily corrected on the spot, so we feel justified in taking a harder line. |
| } |
| |
| \LMHash{} |
| It is a compile-time error if the interface of a class $C$ is a superinterface of itself. |
| |
| \LMHash{} |
| Let $C$ be a concrete class that does not have a concrete \code{noSuchMethod()} method distinct from the one declared in class \code{Object}. |
| It is a static warning if the implicit interface of $C$ includes an instance member $m$ of type $F$ and $C$ does not declare or inherit a corresponding concrete instance member $m$ of type $F'$ such that $F' <: F$. |
| |
| \commentary{ |
| A class does not inherit members from its superinterfaces. |
| However, its implicit interface does. |
| } |
| |
| \rationale{ |
| We choose to issue these warnings only for concrete classes; an abstract class might legitimately be designed with the expectation that concrete subclasses will implement part of the interface. |
| We also disable these warnings if a concrete \code{noSuchMethod()} declaration is present or inherited from any class other than \code{Object}. |
| In such cases, the supported interface is going to be implemented via \code{noSuchMethod()} and no actual declarations of the implemented interface's members are needed. |
| This allows proxy classes for specific types to be implemented without provoking type warnings. |
| } |
| |
| \LMHash{} |
| It is a static warning if the implicit interface of a class $C$ includes an instance member $m$ of type $F$ and $C$ declares or inherits a corresponding instance member $m$ of type $F'$ if $F'$ is not a subtype of $F$. |
| |
| \rationale{ |
| However, if a class does explicitly declare a member that conflicts with its superinterface, this always yields a static warning. |
| } |
| %It is a static warning if an imported superinterface of a class $C$ declares private members. |
| |
| % Should we ignore unimplemented private members? |
| |
| |
| \subsection{Class Member Conflicts} |
| \LMLabel{classMemberConflicts} |
| |
| Some pairs of class member declarations cannot coexist, |
| even though they do not both introduce the same name into the same scope. |
| This section specifies these errors. |
| |
| The {\em basename} of a getter or method named $n$ is $n$; |
| the basename of a setter named \code{$n$=} is $n$. |
| |
| Let $C$ be a class. |
| It is a compile-time error if $C$ declares a |
| \begin{itemize} |
| \item constructor named \code{$C$.$n$} and a static member with basename $n$. |
| \item getter or a setter with basename $n$, and has a method named $n$. |
| \item method named $n$, and has a getter or a setter with basename $n$. |
| \item static member with basename $n$, and has an instance member with basename $n$. |
| \end{itemize} |
| These errors occur when the getters or setters are defined explicitly |
| as well as when they are induced by variable declarations. |
| |
| |
| \section{Interfaces} |
| \LMLabel{interfaces} |
| |
| \LMHash{} |
| An {\em interface} defines how one may interact with an object. |
| An interface has methods, getters and setters and a set of superinterfaces. |
| |
| |
| \subsection{Superinterfaces} |
| \LMLabel{interfaceSuperinterfaces} |
| |
| \LMHash{} |
| An interface has a set of direct superinterfaces. |
| |
| \LMHash{} |
| An interface $J$ is a superinterface of an interface $I$ if{}f either $J$ is a direct superinterface of $I$ or $J$ is a superinterface of a direct superinterface of $I$. |
| |
| |
| \subsubsection{Inheritance and Overriding} |
| \LMLabel{interfaceInheritanceAndOverriding} |
| |
| \LMHash{} |
| Let $J$ be an interface and $K$ be a library. |
| We define $inherited(J, K)$ to be the set of members $m$ such that all of the following hold: |
| \begin{itemize} |
| \item $m$ is accessible to $K$ and |
| \item $A$ is a direct superinterface of $J$ and either |
| \begin{itemize} |
| \item $A$ declares a member $m$ or |
| \item $m$ is a member of $inherited(A, K)$. |
| \end{itemize} |
| \item $m$ is not overridden by $J$. |
| \end{itemize} |
| |
| \LMHash{} |
| Furthermore, we define $overrides(J, K)$ to be the set of members $m'$ such that all of the following hold: |
| \begin{itemize} |
| \item $J$ is the implicit interface of a class $C$. |
| \item $C$ declares a member $m$. |
| \item $m'$ has the same name as $m$. |
| \item $m'$ is accessible to $K$. |
| \item $A$ is a direct superinterface of $J$ and either |
| \begin{itemize} |
| \item $A$ declares a member $m'$ or |
| \item $m'$ is a member of $inherited(A, K)$. |
| \end{itemize} |
| \end{itemize} |
| |
| \LMHash{} |
| Let $I$ be the implicit interface of a class $C$ declared in library $L$. |
| $I$ {\em inherits} all members of $inherited(I, L)$ and $I$ {\em overrides} $m'$ if $m' \in overrides(I, L)$. |
| |
| \LMHash{} |
| All the static warnings pertaining to the overriding of instance members given in section \ref{classes} above hold for overriding between interfaces as well. |
| |
| \LMHash{} |
| It is a static warning if $m$ is a method and $m'$ is a getter, or if $m$ is a getter and $m'$ is a method. |
| |
| %Let $I = S_0$ be the implicit interface of a class $C$ declared in library $L$, and let $\{S_1, \ldots, S_k\}$ be the set of all superinterfaces of $I$. |
| |
| %Let $I$ be the implicit interface of a class $C$. $I$ inherits any instance members of its superinterfaces that are not overridden by members declared in $C$. |
| |
| % tighten definition? do we need chain as for classes? Definition for interface override? |
| |
| \LMHash{} |
| However, if the above rules would cause multiple members $m_1, \ldots, m_k$ with the same name $n$ to be inherited (because identically named members existed in several superinterfaces) then at most one member is inherited. |
| |
| \LMHash{} |
| If some but not all of the $m_i, 1 \le i \le k$ are getters none of the $m_i$ are inherited, and a static warning is issued. |
| |
| \LMHash{} |
| Otherwise, if the static types $T_1, \ldots, T_k$ of the members $m_1, \ldots, m_k$ are not identical then there must be an $x \in 1 .. k$ such that $T_x <: T_i$ for all $i \in 1 .. k$, |
| or a static type warning occurs. |
| The member that is inherited is $m_x$, if it exists; otherwise: |
| let $numberOfPositionals(f)$ denote the number of positional parameters of a function $f$, |
| and let $numberOfRequiredParams(f)$ denote the number of required parameters of a function $f$. |
| Furthermore, let $s$ denote the set of all named parameters of the $m_1, \ldots, m_k$. |
| Then let |
| |
| $h = max(numberOfPositionals(m_i)), $ |
| |
| $r = min(numberOfRequiredParams(m_i)), i \in 1 .. k$. |
| |
| \LMHash{} |
| Then $I$ has a method named $n$, with $r$ required parameters of type \DYNAMIC{}, $h$ positional parameters of type \DYNAMIC{}, named parameters $s$ of type \DYNAMIC{} and return type \DYNAMIC{}. |
| |
| \commentary{ |
| The only situation where the run-time system would be concerned with this would be during reflection, if a mirror attempted to obtain the signature of an interface member. |
| } |
| |
| \rationale{ |
| The current solution is a tad complex, but is robust in the face of type annotation 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. |
| |
| (a) means that the presence of an inherited member of an interface varies depending on type signatures. |
| (b) is sensitive to irrelevant details of the declaration, and |
| (c) is liable to give unpredictable results between implementations or even between different compilation sessions. |
| } |
| |
| % Need warnings if overrider conflicts with overriddee either because signatures are incompatible or because done is a method and one is a getter or setter. |
| |
| |
| \section{Mixins} |
| \LMLabel{mixins} |
| |
| \LMHash{} |
| A mixin describes the difference between a class and its superclass. |
| A mixin is always derived from an existing class declaration. |
| |
| \LMHash{} |
| It is a compile-time error to derive a mixin from a class which explicitly declares a generative constructor. |
| It is a compile-time error to derive a mixin from a class which has a superclass other than \code{Object}. |
| |
| \rationale{ |
| This restriction is temporary. |
| We expect to remove it in later versions of Dart. |
| |
| The restriction on constructors simplifies the construction of mixin applications because the process of creating instances is simpler. |
| } |
| |
| |
| \subsection{Mixin Application} |
| \LMLabel{mixinApplication} |
| |
| \LMHash{} |
| A mixin may be applied to a superclass, yielding a new class. |
| Mixin application occurs when one or more mixins are mixed into a class declaration via its \WITH{} clause. |
| The mixin application may be used to extend a class per section (\ref{classes}); alternatively, a class may be defined as a mixin application as described in this section. |
| It is a compile-time error if the \WITH{} clause of a mixin application $C$ includes a type variable (\ref{generics}), a type alias (\ref{typedef}), an enumerated type (\ref{enums}), a malformed type, or a deferred type (\ref{staticTypes}). |
| |
| \begin{grammar} |
| {\bf mixinApplicationClass:}identifier typeParameters? `=' mixinApplication `{\escapegrammar ;}' |
| . |
| |
| {\bf mixinApplication:}type mixins interfaces? |
| . |
| \end{grammar} |
| |
| \LMHash{} |
| A mixin application of the form \code{$S$ \WITH{} $M$;} for the name $N$ defines a class $C$ with superclass $S$ and name $N$. |
| |
| \LMHash{} |
| A mixin application of the form \code{$S$ \WITH{} $M_1,\ \ldots, M_k$;} for the name $N$ defines a class $C$ whose superclass is the application of the mixin composition (\ref{mixinComposition}) $M_{k-1} * \ldots * M_1$ to $S$ of a name that is a fresh identifer, and whose name is $N$. |
| \rationale{The name of the resulting class is necessary because it is part of the names of the introduced constructors.} |
| |
| \LMHash{} |
| In both cases above, $C$ declares the same instance members as $M$ (respectively, $M_k$), |
| and it does not declare any static members. |
| If any of the instance variables of $M$ (respectively, $M_k$) have initializers, |
| they are executed in the instance scope of $M$ (respectively, $M_k$) |
| to initialize the corresponding instance variables of $C$. |
| |
| \LMHash{} |
| Let $L_C$ be the library containing the mixin application. |
| \commentary{That is, the library containing the clause \code{$S$ \WITH{} $M$} |
| or the clause \code{$S_0$ \WITH{} $M_1$, \ldots,\ $M_k$, $M$}.} |
| |
| Let $N_C$ be the name of the mixin application class $C$, |
| let $S$ be the superclass of $C$, and let $S_N$ be the name of $S$. |
| |
| For each generative constructor of the form \code{$S_q$($T_{1}$ $a_{1}$, $\ldots$, $T_{k}$ $a_{k}$)} of $S$ that is accessible to $L_C$, $C$ has an implicitly declared constructor of the form |
| \begin{dartCode} |
| $C_q$($T_{1}$ $a_{1}$, \ldots, $T_{k}$ $a_{k}$):$\SUPER_q$($a_{1}$, $\ldots$, $a_{k}$); |
| \end{dartCode} |
| |
| \noindent |
| where $C_q$ is obtained from $S_q$ by replacing occurrences of $S_N$, |
| which denote the superclass, by $N_C$, and $\SUPER_q$ is obtained from $S_q$ by |
| replacing occurrences of $S_N$ which denote the superclass by \SUPER{}. |
| If $S_q$ is a generative const constructor, and $M$ does not declare any |
| fields, $C_q$ is also a const constructor. |
| |
| \LMHash{} |
| For each generative constructor of the form \code{$S_q$($T_{1}$ $a_{1}$, \ldots , $T_{k}$ $a_{k}$, [$T_{k+1}$ $a_{k+1}$ = $d_1$, \ldots , $T_{k+p}$ $a_{k+p}$ = $d_p$])} of $S$ that is accessible to $L_C$, $C$ has an implicitly declared constructor of the form |
| \begin{dartCode} |
| $C_q$($T_{1}$ $a_{1}$, \ldots , $T_{k}$ $a_{k}$, [$T_{k+1}$ $a_{k+1}$ = $d'_{1}$, \ldots , $T_{k+p}$ $a_{k+p}$ = $d'_p$]) |
| : $\SUPER_q$($a_{1}$, \ldots , $a_{k}$, $a_{k+1}$, \ldots, $a_p$); |
| \end{dartCode} |
| |
| \noindent |
| where $C_q$ is obtained from $S_q$ by replacing occurrences of $S_N$, |
| which denote the superclass, by $N_C$, |
| $\SUPER_q$ is obtained from $S_q$ by replacing occurrences of $S_N$ |
| which denote the superclass by \SUPER{}, |
| and $d'_i$, $i \in 1..p$, is a compile-time constant expression evaluating |
| to the same value as $d_i$. |
| If $S_q$ is a generative const constructor, and $M$ does not declare any |
| fields, $C_q$ is also a const constructor. |
| |
| \LMHash{} |
| For each generative constructor of the form \code{$S_q$($T_{1}$ $a_{1}$, \ldots , $T_{k}$ $a_{k}$, \{$T_{k+1}$ $a_{k+1}$ = $d_1$, \ldots , $T_{k+n}$ $a_{k+n}$ = $d_n$\})} of $S$ that is accessible to $L_C$, $C$ has an implicitly declared constructor of the form |
| \begin{dartCode} |
| $C_q$($T_{1}$ $a_{1}$, \ldots , $T_{k}$ $a_{k}$, \{$T_{k+1}$ $a_{k+1}$ = $d'_1$, \ldots , $T_{k+n}$ $a_{k+n}$ = $d'_n$\}) |
| : $\SUPER_q$($a_{1}$, \ldots , $a_{k}$, $a_{k+1}$: $a_{k+1}$, \ldots, $a_p$: $a_p$); |
| \end{dartCode} |
| |
| \noindent |
| where $C_q$ is obtained from $S_q$ by replacing occurrences of $S_N$ |
| which denote the superclass by $N_C$, |
| $\SUPER_q$ is obtained from $S_q$ by replacing occurrences of $S_N$ |
| which denote the superclass by \SUPER{}, |
| and $d'_i$, $i \in 1..n$, is a compile-time constant expression evaluating to the same value as $d_i$. |
| If $S_q$ is a generative const constructor, and $M$ does not declare any |
| fields, $C_q$ is also a const constructor. |
| |
| \LMHash{} |
| If the mixin application class declares interfaces, the resulting class also implements those interfaces. |
| |
| \LMHash{} |
| It is a compile-time error if $S$ is an enumerated type (\ref{enums}) or a malformed type. |
| It is a compile-time error if $M$ (respectively, any of $M_1, \ldots, M_k$) is an enumerated type (\ref{enums}) or a malformed type. |
| It is a compile-time error if a well formed mixin cannot be derived from $M$ (respectively, from each of $M_1, \ldots, M_k$). |
| |
| \LMHash{} |
| Let $K$ be a class declaration with the same constructors, superclass and interfaces as $C$, and the instance members declared by $M$ (respectively $M_1, \ldots, M_k$). |
| It is a static warning if the declaration of $K$ would cause a static warning. |
| It is a compile-time error if the declaration of $K$ would cause a compile-time error. |
| |
| \commentary{ |
| If, for example, $M$ declares an instance member $im$ whose type is at odds with the type of a member of the same name in $S$, this will result in a static warning just as if we had defined $K$ by means of an ordinary class declaration extending $S$, with a body that included $im$. |
| } |
| |
| \LMHash{} |
| The effect of a class definition of the form \code{\CLASS{} $C$ = $M$; } or the form \code{\CLASS{} $C<T_1, \ldots,\ T_n>$ = $M$; } in library $L$ is to introduce the name $C$ into the scope of $L$, bound to the class (\ref{classes}) defined by the mixin application $M$ for the name $C$. |
| The name of the class is also set to $C$. |
| If{}f the class is prefixed by the built-in identifier \ABSTRACT{}, the class being defined is an abstract class. |
| |
| \LMHash{} |
| Let $M_A$ be a mixin derived from a class $M$ with direct superclass $S_{static}$, e.g., as defined by the class declaration \code{class M extends S$_{static}$ \{ \ldots \}}. |
| |
| \LMHash{} |
| Let $A$ be an application of $M_A$. |
| It is a static warning if the superclass of $A$ is not a subtype of $S_{static}$. |
| |
| \LMHash{} |
| Let $C$ be a class declaration that includes $M_A$ in a with clause. |
| It is a static warning if $C$ does not implement, directly or indirectly, all the direct superinterfaces of $M$. |
| |
| |
| \subsection{Mixin Composition} |
| \LMLabel{mixinComposition} |
| |
| \rationale{ |
| Dart does not directly support mixin composition, but the concept is useful when defining how the superclass of a class with a mixin clause is created. |
| } |
| |
| \LMHash{} |
| The {\em composition of two mixins}, $M_1<T_1, \ldots, T_{k_{M_1}}>$ and $M_2<U_1, \ldots, U_{k_{M_2}}>$, written $M_1<T_1, \ldots, T_{k_{M_1}}> * M_2<U_1, \ldots, U_{k_{M_2}}>$ defines an anonymous mixin such that for any class $S<V_1, \ldots, V_{k_S}>$, the application of |
| |
| $M_1<T_1, \ldots, T_{k_{M_1}}> * M_2<U_1, \ldots, U_{k_{M_2}}>$ |
| |
| to $S<V_1, \ldots, V_{k_S}>$ for the name $C$ is equivalent to |
| |
| \begin{dartCode} |
| \ABSTRACT{} \CLASS{} $C<T_1, \ldots, T_{k_{M_1}}, U_1, \ldots, U_{k_{M_2}}, V_1, \ldots, V_{k_S}> = $ |
| $Id_2<U_1, \ldots, U_{k_{M_2}}, V_1 \ldots V_{k_S}>$ \WITH{} $M_1 <T_1, \ldots, T_{k_{M_1}}>$; |
| \end{dartCode} |
| |
| where $Id_2$ denotes |
| |
| \begin{dartCode} |
| \ABSTRACT{} \CLASS{} $Id_2<U_1, \ldots, U_{k_{M_2}}, V_1, \ldots, V_{k_S}> =$ |
| $S<V_1, \ldots, V_{k_S}>$ \WITH{} $M_2<U_1, \ldots, U_{k_{M_2}}>$; |
| \end{dartCode} |
| |
| and $Id_2$ is a unique identifier that does not exist anywhere in the program. |
| |
| \rationale{ |
| The intermediate classes produced by mixin composition are regarded as abstract because they cannot be instantiated independently. |
| They are only introduced as anonymous superclasses of ordinary class declarations and mixin applications. |
| Consequently, no warning is given if a mixin composition includes abstract members, or incompletely implements an interface. |
| } |
| |
| \LMHash{} |
| Mixin composition is associative. |
| |
| \commentary{ |
| Note that any subset of $M_1$, $M_2$ and $S$ may or may not be generic. |
| For any non-generic declaration, the corresponding type parameters may be elided, and if no type parameters remain in the derived declarations $C$ and/or $Id_2$ then the those declarations need not be generic either. |
| } |
| |
| |
| \section{Enums} |
| \LMLabel{enums} |
| |
| \LMHash{} |
| An {\em enumerated type}, or {\em enum}, is used to represent a fixed number of constant values. |
| |
| \begin{grammar} |
| {\bf enumType:}metadata \ENUM{} identifier |
| \gnewline{} `\{' enumEntry (\gcomma{} enumEntry)* (\gcomma{})? `\}' |
| . |
| |
| {\bf enumEntry:}metadata identifier |
| . |
| \end{grammar} |
| |
| \LMHash{} |
| The declaration of an enum of the form |
| \code{$m$ \ENUM{} E \{$m_0\,\,id_0, \ldots,\ m_{n-1}\,\,id_{n-1}$\}} |
| has the same effect as a class declaration |
| |
| \begin{dartCode} |
| $m$ \CLASS{} E \{ |
| \FINAL{} int index; |
| \CONST{} E(\THIS{}.index); |
| $m_0$ \STATIC{} \CONST{} E id$_0$ = \CONST{} E(0); |
| $\ldots$ |
| $m_{n-1}$ \STATIC{} \CONST{} E id$_{n-1}$ = const E(n - 1); |
| \STATIC{} \CONST{} List<E> values = const <E>[id$_0, \ldots, $ id$_{n-1}$]; |
| String toString() => \{ 0: `E.id$_0$', $\ldots$, n-1: `E.id$_{n-1}$'\}[index] |
| \} |
| \end{dartCode} |
| |
| \commentary{ |
| It is also a compile-time error to subclass, mix-in or implement an enum or to explicitly instantiate an enum. |
| These restrictions are given in normative form in sections \ref{superclasses}, \ref{superinterfaces}, \ref{mixinApplication} and \ref{instanceCreation} as appropriate. |
| } |
| |
| |
| \section{Generics} |
| \LMLabel{generics} |
| |
| \LMHash{} |
| A class declaration (\ref{classes}), type alias (\ref{typedef}), or function (\ref{functions}) $G$ may be {\em generic}, that is, $G$ may have formal type parameters declared. |
| |
| \LMHash{} |
| When an entity in this specification is described as generic, |
| and the special case is considered where the number of type arguments is zero, |
| the type argument list should be omitted. |
| |
| \commentary{ |
| This allows non-generic cases to be included implicitly as special cases. |
| For example, an invocation of a non-generic function arises as the special case where the function takes zero type arguments, and zero type arguments are passed. |
| In this situation some operations are also omitted (have no effect), e.g., |
| operations where formal type parameters are replaced by actual type arguments. |
| } |
| |
| \LMHash{} |
| A {\em generic class declaration} introduces a generic class into the enclosing library scope. |
| A {\em generic class} is a mapping that accepts a list of actual type arguments and maps them to a class. |
| Consider a generic class declaration $G$ named \code{C} with formal type parameter declarations |
| $X_1\ \EXTENDS\ B_1, \ldots,\ X_m\ \EXTENDS\ B_m$, |
| and a parameterized type $T$ of the form \code{C<$T_1, \ldots,\ T_l$>}. |
| |
| \LMHash{} |
| It is a static warning if $m \not= l$. |
| It is a static warning if $T$ is not well-bounded |
| (\ref{superBoundedTypes}). |
| |
| \commentary{ |
| That is, if the number of type arguments is wrong, |
| or one or more of the upper bounds has been violated. |
| } |
| |
| \LMHash{} |
| Otherwise, said parameterized type \code{C<$T_1, \ldots,\ T_m$>} denotes an application of the generic class declared by $G$ to the type arguments $T_1, \ldots, T_m$. |
| This yields a class $C'$ whose members are equivalent to those of a class declaration which is obtained from the declaration of $G$ by replacing each occurrence of $X_j$ by $T_j$. |
| \commentary{ |
| |
| % TODO(eernst): make sure this list of properties is complete. |
| Other properties of $C'$ such as the subtype relationships are specified elsewhere (\ref{interfaceTypes}). |
| } |
| |
| \LMHash{} |
| A {\em generic type alias} introduces a mapping from actual type argument lists to types. |
| Consider a generic type alias declaration $G$ named \code{F} with formal type parameter declarations |
| $X_1\ \EXTENDS\ B_1, \ldots,\ X_m\ \EXTENDS\ B_m$, |
| and right hand side $T$, |
| and the parameterized type $S$ of the form \code{F<$T_1, \ldots,\ T_l$>}. |
| |
| \LMHash{} |
| It is a static warning if $m \not= l$. |
| It is a static warning if $S$ is not well-bounded |
| (\ref{superBoundedTypes}). |
| |
| \commentary{ |
| That is, if the number of type arguments is wrong, |
| or one or more of the upper bounds has been violated. |
| } |
| |
| \LMHash{} |
| Otherwise, said parameterized type |
| \code{F<$T_1, \ldots,\ T_m$>} |
| denotes an application of the mapping denoted by $G$ to the type arguments |
| $T_1, \ldots, T_m$. |
| This yields the type |
| $[T_1/X_1, \ldots, T_m/X_m]T$. |
| |
| \commentary{ |
| A generic type alias does not correspond to any entities at run time, |
| it is only an alias for an existing type. |
| Hence, we may consider it as syntactic sugar which is eliminated before the program runs. |
| } |
| |
| \LMHash{} |
| A {\em generic type} is a type which is introduced by |
| a generic class declaration or a generic type alias, |
| or it is the type \code{FutureOr}. |
| |
| \LMHash{} |
| A {\em generic function declaration} introduces a generic function (\ref{formalParameters}) into the enclosing scope. |
| Consider a function invocation expression of the form |
| \code{f<$T_1, \ldots,\ T_l$>(\ldots)}, |
| where the static type of \code{f} is a generic function type with formal type parameters |
| $X_1\ \EXTENDS\ B_1, \ldots,\ X_m\ \EXTENDS\ B_m$. |
| |
| \LMHash{} |
| It is a static warning if $m \not= l$. |
| It is a static warning if there exists a $j$ |
| such that $T_j$ is not a subtype of $[T_1/X_1, \ldots, T_m/X_m]B_j$. |
| |
| \commentary{ |
| That is, if the number of type arguments is wrong, |
| or if the $j$th actual type argument is not a subtype of the corresponding bound, |
| where each formal type parameter has been replaced by the corresponding actual type argument. |
| } |
| |
| \begin{grammar} |
| {\bf typeParameter:}metadata identifier (\EXTENDS{} type)? |
| . |
| |
| {\bf typeParameters:}`<' typeParameter (\gcomma{} typeParameter)* `>' |
| . |
| \end{grammar} |
| |
| \LMHash{} |
| A type parameter $T$ may be suffixed with an \EXTENDS{} clause that specifies the {\em upper bound} for $T$. |
| If no \EXTENDS{} clause is present, the upper bound is \code{Object}. |
| It is a static type warning if a type parameter is a supertype of its upper bound. |
| The bounds of type variables are a form of type annotation and have no effect on execution in production mode. |
| |
| \LMHash{} |
| Type parameters are declared in the type parameter scope of a class or function. |
| The type parameters of a generic $G$ are in scope in the bounds of all of the type parameters of $G$. |
| The type parameters of a generic class declaration $G$ are also in scope in the \EXTENDS{} and \IMPLEMENTS{} clauses of $G$ (if these exist) and in the body of $G$. |
| However, a type parameter of a generic class is considered to be a malformed type when referenced by a static member. |
| |
| \commentary{ |
| The scopes associated with the type parameters of a generic function are described in (\ref{formalParameters}). |
| } |
| |
| \rationale{ |
| The restriction on static members is necessary since a type variable has no meaning in the context of a static member, |
| because statics are shared among all generic instantiations of a generic class. |
| However, a type variable may be referenced from an instance initializer, |
| even though \THIS{} is not available. |
| } |
| |
| \commentary{ |
| Because type parameters are in scope in their bounds, we support F-bounded quantification (if you don't know what that is, don't ask). |
| This enables typechecking code such as: |
| } |
| |
| \begin{dartCode} |
| \CLASS{} Ordered<T> \{ |
| operator > (T x); |
| \} |
| |
| \CLASS{} Sorter<T \EXTENDS{} Ordered<T$>>$ \{ |
| sort(List<T> l) {... l[n] < l[n+1] ...} |
| \} |
| |
| \end{dartCode} |
| |
| \commentary{ |
| Even where type parameters are in scope there are numerous restrictions at this time: |
| \begin{itemize} |
| \item A type parameter cannot be used to name a constructor in an instance creation expression (\ref{instanceCreation}). |
| \item A type parameter cannot be used as a superclass or superinterface (\ref{superclasses}, \ref{superinterfaces}, \ref{interfaceSuperinterfaces}). |
| \item A type parameter cannot be used as a generic type. |
| \end{itemize} |
| |
| The normative versions of these are given in the appropriate sections of this specification. |
| Some of these restrictions may be lifted in the future. |
| } |
| |
| %The {\em induced type set}, $S$, of a parameterized type $T$ is the set consisting of |
| %\begin{itemize} |
| %\item The supertypes of any type in $S$. |
| %\item The type arguments of any parameterized type in $S$. |
| %\end{itemize} |
| |
| %Let $P$ be the generic instantiation of a generic type with its own type parameters. It is a compile-time error if the induced type set of $P$ is not finite. |
| |
| %\rationale {A typical recursive type declaration such as} |
| |
| %\begin{dartCode} |
| %\CLASS{} B<S> \{\} |
| %\CLASS{} D<T> \EXTENDS{} B<D<T$>>$ \{\} |
| %\end{dartCode} |
| |
| %\rationale{ |
| %poses no problem under this rule. The generic instantiation \code{D<T>} has an induced |
| %set consisting of: \code{B<D<T$>>$, Object, D<T>, T}. However, the following variant |
| %} |
| |
| %\begin{dartCode} |
| %\CLASS{} B<S> \{\} |
| %\CLASS{} D<T> \EXTENDS{} B<D<D<T$>>>$ \{\} |
| %\end{dartCode} |
| |
| %\rationale{ |
| %is disallowed. Consider again the generic instantiation \code{D<T>}. It leads to the |
| %superclass \code{B<D<D<T$>>>$}, and so adds \code{D<D$<$T$>>$} to the induced set. The latter in turn leads to \code{B<D<D<D<T$>>>>$} and \code{D<D<D<T$>>>$} |
| %and so on ad infinitum.} |
| |
| %\commentary{ |
| %The above requirement does not preclude the use of arbitrary recursive types in the body of a generic class. } |
| %A generic has a type parameter scope. The enclosing scope of a type parameter scope of a generic G is the enclosing scope of G. |
| |
| %class T {...} |
| |
| %class G<T> extends T; |
| |
| %By current rules, this is illegal. Make sure we preserve this. |
| |
| |
| \subsection{Variance} |
| \LMLabel{variance} |
| |
| \LMHash{} |
| We say that a type $S$ {\em occurs covariantly} in a type $T$ if{}f |
| $S$ occurs in a covariant position in $T$, |
| but not in a contravariant position, |
| and not in an invariant position. |
| |
| \LMHash{} |
| We say that a type $S$ {\em occurs contravariantly} in a type $T$ if{}f |
| $S$ occurs in a contravariant position in $T$, |
| but not in a covariant position, |
| and not in an invariant position. |
| |
| \LMHash{} |
| We say that a type $S$ {\em occurs invariantly} in a type $T$ if{}f |
| $S$ occurs in an invariant position in $T$, |
| or $S$ occurs in a covariant position as well as a contravariant position. |
| |
| \LMHash{} |
| We say that a type $S$ occurs {\em in a covariant position} in a type $T$ |
| if{}f one of the following conditions is true: |
| |
| \begin{itemize} |
| \item $T$ is $S$ |
| |
| \item $T$ is of the form \code{$G$<$S_1,\ \ldots,\ S_n$>} |
| where $G$ denotes a generic class |
| and $S$ occurs in a covariant position in $S_j$ for some $j \in 1 .. n$. |
| |
| \item $T$ is of the form |
| \code{$S_0$ \FUNCTION<$X_1\ \EXTENDS\ B_1, \ldots$>($S_1\ x_1, \ldots$)} |
| where the type parameter list may be omitted, |
| and $S$ occurs in a covariant position in $S_0$. |
| |
| \item $T$ is of the form |
| |
| \code{$S_0$ \FUNCTION<$X_1\ \EXTENDS\ B_1, \ldots$>} |
| |
| \code{\quad($S_1\ x_1, \ldots,\ S_k\ x_k, |
| $ [$S_{k+1}\ x_{k+1} = d_{k+1}, \ldots,\ S_n\ x_n = d_n$])} |
| |
| \noindent |
| or of the form |
| |
| \code{$S_0$ \FUNCTION<$X_1\ \EXTENDS\ B_1, \ldots$>} |
| |
| \code{\quad($S_1\ x_1, \ldots,\ S_k\ x_k, |
| $ \{$S_{k+1}\ x_{k+1} = d_{k+1}, \ldots,\ S_n\ x_n = d_n$\})} |
| |
| \noindent |
|