OLD | NEW |
(Empty) | |
| 1 # Feature: Generic Method Syntax |
| 2 |
| 3 **This document** is an informal specification of the support for generic |
| 4 methods and functions which has been implemented in `dart2js` with option |
| 5 `--generic-method-syntax`, starting with commit |
| 6 [acc5f59](https://github.com/dart-lang/sdk/commit/acc5f59a99d5d8747459c935e6360a
c325606cc6). |
| 7 In SDK 1.21 this feature is available by default (i.e., also without the |
| 8 option) in the virtual machine and the analyzer, as well as in `dart2js`. |
| 9 |
| 10 The **motivation for** having this **feature** is that it enables partial |
| 11 support for generic methods and functions, thus providing a bridge between |
| 12 not having generic methods and having full support for generic methods. In |
| 13 particular, code declaring and using generic methods may be type checked and |
| 14 compiled in strong mode, and the same code will now be acceptable in |
| 15 standard (non-strong) mode as well. The semantics is different in certain |
| 16 cases, but standard mode analysis will emit diagnostic messages (e.g., |
| 17 errors) for that. |
| 18 |
| 19 In this document, the word **routine** will be used when referring to |
| 20 something which can be a method, a top level function, a local function, or |
| 21 a function literal expression. |
| 22 |
| 23 With **this feature** it is possible to compile code where generic methods |
| 24 and functions are declared, implemented, and invoked. The runtime semantics |
| 25 does not include reification of type arguments. Evaluations of the runtime |
| 26 value of a routine type parameter is a runtime error or yields `dynamic`, |
| 27 depending on the context. No type checking takes place at usages of a method |
| 28 or function type parameter in the body, and no type checking regarding |
| 29 explicitly specified or omitted type arguments takes place at call sites. |
| 30 |
| 31 In short, generic methods and functions are supported syntactically, and the |
| 32 runtime semantics prevents dynamic usages of the type argument values, but |
| 33 it allows all usages where that dynamic value is not required. For instance, |
| 34 a generic routine type parameter, `T`, cannot be used in an expression like |
| 35 `x is T`, but it can be used as a type annotation. In a context where other |
| 36 tools may perform type checking, this allows for a similar level of |
| 37 expressive power as do language designs where type arguments are erased at |
| 38 compile time. |
| 39 |
| 40 The **motivation for** this **document** is that it serves as an informal |
| 41 specification for the implementation of support for the generic method |
| 42 syntax feature in all Dart tools. |
| 43 |
| 44 ## Syntax |
| 45 |
| 46 The syntactic elements which are added or modified in order to support this |
| 47 feature are as follows, based on grammar rules given in the Dart Language |
| 48 Specification (Aug 19, 2015). |
| 49 |
| 50 ``` |
| 51 formalParameterPart: |
| 52 typeParameters? formalParameterList |
| 53 functionSignature: |
| 54 metadata returnType? identifier formalParameterPart |
| 55 typeParameter: |
| 56 metadata identifier ('extends' type)? |
| 57 functionExpression: |
| 58 formalParameterPart functionBody |
| 59 fieldFormalParameter: |
| 60 metadata finalConstVarOrType? 'this' '.' identifier |
| 61 formalParameterPart? |
| 62 argumentPart: |
| 63 typeArguments? arguments |
| 64 selector: |
| 65 assignableSelector | argumentPart |
| 66 assignableExpression: |
| 67 primary (argumentPart* assignableSelector)+ | |
| 68 'super' unconditionalAssignableSelector | |
| 69 identifier |
| 70 cascadeSection: |
| 71 '..' (cascadeSelector argumentPart*) |
| 72 (assignableSelector argumentPart*)* |
| 73 (assignmentOperator expressionWithoutCascade)? |
| 74 ``` |
| 75 |
| 76 In a [draft specification](https://codereview.chromium.org/1177073002) of |
| 77 generic methods from June 2015, the number of grammar changes is |
| 78 significantly higher, but that form can be obtained via renaming. |
| 79 |
| 80 This extension to the grammar gives rise to an **ambiguity** where the |
| 81 same tokens may be angle brackets of a type argument list as well as |
| 82 relational operators. For instance, `foo(a<b,c>(d))`[^1] may be parsed as |
| 83 a `postfixExpression` on the form `primary arguments` where the arguments |
| 84 are two relational expressions (`a<b` and `c>(d)`), and it may also be |
| 85 parsed such that there is a single argument which is an invocation of a |
| 86 generic function (`a<b,c>(d)`). The ambiguity is resolved in **favor** of |
| 87 the latter. |
| 88 |
| 89 This implies that an expression like `foo(a<b,2>(d))` will be rejected |
| 90 because it is parsed such that `foo` gets one argument which must be a |
| 91 generic function invocation, but `2` cannot parse correctly as a |
| 92 `type`. This is a breaking change, because the same expression used to parse |
| 93 correctly as an invocation of `foo` with two arguments. |
| 94 |
| 95 The **reason** why the generic function invocation is favored over the |
| 96 relational expressions is that it is considered to be a rare exception that |
| 97 this ambiguity arises: It requires a balanced set of angle brackets followed |
| 98 by a left parenthesis, which is already an unusual form. On top of that, the |
| 99 style guide recommendation to use named parameters for boolean arguments |
| 100 helps making this situation even less common. |
| 101 |
| 102 If it does occur then there is an easy **workaround**: an extra set of |
| 103 parentheses (as in `foo(a<b,(2>(d)))`) will resolve the ambiguity in the |
| 104 direction of relational expressions; or we might simply be able to remove |
| 105 the parentheses around the last expression (as in `foo(a<b,2>d)`), which |
| 106 will also eliminate the ambiguity. |
| 107 |
| 108 _It should be noted that parsing techniques like recursive descent seem to |
| 109 conflict with this approach to disambiguation: Determining whether the |
| 110 remaining input starts with a balanced expression on the form `<` .. `>` |
| 111 seems to imply a need for an unbounded lookahead. However, if some type of |
| 112 "diet" parsing is used and various kinds of bracket tokens are matched up |
| 113 during the lexical analysis then it takes only a simple O(1) check in the |
| 114 parser to perform the required check._ |
| 115 |
| 116 ## Scope of the Mechanism |
| 117 |
| 118 With the syntax in place, it is obvious that certain potential extensions |
| 119 have **not** been **included**. |
| 120 |
| 121 For instance, constructors, setters, getters, and operators cannot be |
| 122 declared as generic: The syntax for passing actual type arguments at |
| 123 invocation sites for setters, getters, and operators is likely to be |
| 124 unwieldy and confusing, and for constructors there is a need to find |
| 125 a way to distinguish between type arguments for the new instance and |
| 126 type arguments for the constructor itself. However, there are plans |
| 127 to add support for generic constructors. |
| 128 |
| 129 This informal specification specifies a dynamic semantics where the values |
| 130 of **actual type arguments are not reified** at run time. A future |
| 131 extension of this mechanism may add this reification, such that dynamic |
| 132 type tests and type casts involving routine type variables will be |
| 133 supported. |
| 134 |
| 135 ## Resolution and Type Checking |
| 136 |
| 137 In order to be useful, the support for generic methods and functions must be |
| 138 sufficiently complete and consistent to **avoid spurious** diagnostic |
| 139 **messages**. In particular, even though no regular type checks take place |
| 140 at usages of routine type parameters in the body where they are in scope, |
| 141 those type parameters should be resolved. If they had been ignored then any |
| 142 usage of a routine type parameter `X` would give rise to a `Cannot resolve |
| 143 type X` error message, or the usage might resolve to other declarations of |
| 144 `X` in enclosing scopes such as a class type parameter, both of which is |
| 145 unacceptable. |
| 146 |
| 147 In `dart2js` resolution, the desired behavior has been achieved by adding a |
| 148 new type parameter **scope** and putting the type parameters into that |
| 149 scope, giving each of them the bound `dynamic`. The type parameter scope is |
| 150 the current scope during resolution of the routine signature and the type |
| 151 parameter bounds, it encloses the formal parameter scope of the routine, and |
| 152 the formal parameter scope in turn encloses the body scope. |
| 153 |
| 154 This implies that every usage of a routine type parameter is treated during |
| 155 **type checking** as if it had been an alias for the type dynamic. |
| 156 |
| 157 Static checks for **invocations** of methods or functions where type |
| 158 arguments are passed are omitted entirely: The type arguments are parsed, |
| 159 but no checks are applied to certify that the given routine accepts type |
| 160 arguments, and no checks are applied for bound violations. Similarly, no |
| 161 checks are performed for invocations where no type arguments are passed, |
| 162 whether or not the given routine is statically known to accept type |
| 163 arguments. |
| 164 |
| 165 Certain usages of a routine type parameter `X` give rise to **errors**: It |
| 166 is a compile-time error if `X` is used as a type literal expression (e.g., |
| 167 `foo(X)`), or in an expression on the form `e is X` or `e is! X`, or in a |
| 168 try/catch statement like `.. on T catch ..`. |
| 169 |
| 170 It could be argued that it should be a warning or an error if a routine type |
| 171 parameter `X` is used in an expression on the form `e as X`. The blind |
| 172 success of this test at runtime may introduce bugs into correct programs in |
| 173 situations where the type constraint is violated; in particular, this could |
| 174 cause "wrong" objects to propagate through local variables and parameters |
| 175 and even into data structures (say, when a `List<T>` is actually a |
| 176 `List<dynamic>`, because `T` is not present at runtime when the list is |
| 177 created). However, considering that these type constraint violations are |
| 178 expected to be rare, and considering that it is common to require that |
| 179 programs compile without warnings, we have chosen to omit this warning. A |
| 180 tool is still free to emit a hint, or in some other way indicate that there |
| 181 is an issue. |
| 182 |
| 183 ## Dynamic semantics |
| 184 |
| 185 If a routine invocation specifies actual type arguments, e.g., `int` in the |
| 186 **invocation** `f<int>(42)`, those type arguments will not be evaluated at |
| 187 runtime, and they will not be passed to the routine in the |
| 188 invocation. Similarly, no type arguments are ever passed to a generic |
| 189 routine due to call-site inference. This corresponds to the fact that the |
| 190 type arguments have no runtime representation. |
| 191 |
| 192 When the body of a generic **routine** is **executed**, usages of the formal |
| 193 type parameters will either result in a run-time error, or they will yield |
| 194 the type dynamic, following the treatment of malformed types in |
| 195 Dart. There are the following cases: |
| 196 |
| 197 When `X` is a routine type parameter, the evaluation of `e is X`, `e is! X`, |
| 198 and `X` used as an expression proceeds as if `X` had been a malformed type, |
| 199 producing a dynamic error; the evaluation of `e as X` has the same outcome |
| 200 as the evaluation of `e`. |
| 201 |
| 202 Note that the forms containing `is` are compile-time errors, which means |
| 203 that compilers may reject the program or offer ways to compile the program |
| 204 with a different runtime semantics for these expressions. The rationale for |
| 205 `dart2js` allowing the construct and compiling it to a run time error is |
| 206 that (1) this allows more programs using generic methods to be compiled, |
| 207 and (2) an `is` expression that blindly returns `true` every time (or |
| 208 `false` every time) may silently introduce a bug into an otherwise correct |
| 209 program, so the expression must fail if it is ever evaluated. |
| 210 |
| 211 When `X` is a routine type parameter which is passed as a type argument to a |
| 212 generic class instantiation `G`, it is again treated like a malformed type, |
| 213 i.e., it is considered to denote the type dynamic. |
| 214 |
| 215 This may be surprising, so let us consider a couple of examples: When `X` is |
| 216 a routine type parameter, `42 is X` raises a dynamic error, `<int>[42] is |
| 217 List<X>` yields the value `true`, and `42 as X` yields `42`, no matter |
| 218 whether the syntax for the invocation of the routine included an actual type |
| 219 argument, and, if so, no matter which value the actual type argument would |
| 220 have had at the invocation. |
| 221 |
| 222 Object construction is similar: When `X` is a routine type parameter which |
| 223 is a passed as a type argument in a constructor invocation, the actual |
| 224 value of the type type argument will be the type dynamic, as it would have |
| 225 been with a malformed type. |
| 226 |
| 227 In **checked mode**, when `X` is a routine type parameter, no checked mode |
| 228 checks will ever fail for initialization or assignment to a local variable |
| 229 or parameter whose type annotation is `X`, and if the type annotation is a |
| 230 generic type `G` that contains `X`, checked mode checks will succeed or |
| 231 fail as if `X` had been the type dynamic. Note that this differs from the |
| 232 treatment of malformed types. |
| 233 |
| 234 ## Changes |
| 235 |
| 236 2017-Jan-04: Changed 'static error' to 'compile-time error', which is the |
| 237 phrase that the language specification uses. |
| 238 |
| 239 ## Notes |
| 240 |
| 241 [^1]: These expressions violate the common style in Dart with respect to |
| 242 spacing and capitalization. That is because the ambiguity implies |
| 243 conflicting requirements, and we do not want to bias the appearance in |
| 244 one of the two directions. |
OLD | NEW |