Chromium Code Reviews| 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'|'super') type)? | |
|
Lasse Reichstein Nielsen
2017/04/26 09:15:31
Remove `super`. We are not planning on adding it r
eernst
2017/05/19 18:49:36
Done.
| |
| 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 same | |
| 81 tokens may be angle brackets of a type argument list as well as relational | |
| 82 operators. For instance, `foo(a<b,c>(d))`[^1] may be parsed as a | |
| 83 `postfixExpression` on the form `primary arguments` where the arguments are | |
| 84 two relational expressions (`a<b` and `c>(d)`), and it may also be parsed | |
| 85 such that there is a single argument which is an invocation of a generic | |
| 86 function (`a<b,c>(d)`). The ambiguity is resolved in **favor** of the | |
| 87 **generic function invocation** whenever the `primary` is followed by a | |
| 88 balanced pair of angle brackets where the next token after the final `>` is | |
| 89 a left parenthesis (in short, we are "looking at `< .. >(`"). | |
|
Lasse Reichstein Nielsen
2017/04/26 09:15:31
Drop the "whenever". *The ambiguity* is always res
eernst
2017/05/19 18:49:35
That was not the approach that I specified, I took
Lasse Reichstein Nielsen
2017/06/01 07:56:59
I understand the lure of the O(1) shortcut (given
| |
| 90 | |
| 91 This implies that an expression like `foo(a<b,2>(d))` will be rejected | |
|
Lasse Reichstein Nielsen
2017/04/26 09:15:31
What does "rejeceted" mean?
In any case, remove en
eernst
2017/05/19 18:49:35
It is a syntax error.
Lasse Reichstein Nielsen
2017/06/01 07:57:00
I think it is possible - because the preferred int
| |
| 92 because it is parsed such that `foo` gets one argument which must be a | |
| 93 generic function invocation, but `2` cannot parse correctly as a | |
| 94 `type`. This is a breaking change, because the same expression used to parse | |
| 95 correctly as an invocation of `foo` with two arguments. | |
| 96 | |
| 97 The **reason** why the generic function invocation is favored over the | |
| 98 relational expressions is that it is considered to be a rare exception that | |
| 99 this ambiguity arises: It requires a balanced set of angle brackets followed | |
| 100 by a left parenthesis, which is already an unusual form. On top of that, the | |
| 101 style guide recommendation to use named parameters for boolean arguments | |
| 102 helps making this situation even less common. | |
| 103 | |
| 104 If it does occur then there is an easy **workaround**: an extra set of | |
| 105 parentheses (as in `foo(a<b,(2>(d)))`) will resolve the ambiguity in the | |
| 106 direction of relational expressions; or we might simply be able to remove | |
| 107 the parentheses around the last expression (as in `foo(a<b,2>d)`), which | |
| 108 will also eliminate the ambiguity. | |
| 109 | |
| 110 It should be noted that parsing techniques like recursive descent seem to | |
| 111 conflict with this approach to disambiguation: Determining whether the | |
| 112 remaining input starts with a balanced expression on the form `<` .. `>` | |
| 113 seems to imply a need for an unbounded lookahead. However, if some type of | |
| 114 "diet" parsing is used and various kinds of bracket tokens are matched up | |
| 115 during the lexical analysis then it takes only a simple O(1) check in the | |
| 116 parser to perform the required check. | |
|
Lasse Reichstein Nielsen
2017/04/26 09:15:31
Drop this section, or rewrite it to suggest, as an
eernst
2017/05/19 18:49:35
This should be commentary; changed the font to ita
| |
| 117 | |
| 118 ## Scope of the Mechanism | |
| 119 | |
| 120 With the syntax in place, it is obvious that certain potential extensions | |
| 121 have **not** been **included**. | |
| 122 | |
| 123 For instance, constructors, setters, getters, and operators cannot be | |
| 124 declared as generic. Actual type arguments cannot be passed at invocation | |
| 125 sites for setters, getters, and operators, and for constructors there is a | |
| 126 need to find a way to distinguish between type arguments for the new | |
| 127 instance and type arguments for the constructor itself. It is possible that | |
|
Lasse Reichstein Nielsen
2017/04/26 09:15:31
The second sentence seems to assume that construct
eernst
2017/05/19 18:49:36
The second sentence is intended to explain why con
Lasse Reichstein Nielsen
2017/06/01 07:56:59
The primary reason we will not have generic getter
| |
| 128 some of these restrictions will be lifted in a full-fledged version of this | |
| 129 extension. | |
|
Lasse Reichstein Nielsen
2017/04/26 09:15:31
Not really, no.
Setters and getters will not be g
eernst
2017/05/19 18:49:35
This is exactly like the spec when it says that th
| |
| 130 | |
| 131 Conversely, the inclusion of lower bounds for type parameters (using the | |
| 132 keyword `super`) serves to demonstrate that lower bounds fit well into the | |
| 133 syntax. There is no guarantee that a final version of generic methods will | |
| 134 support lower bounds, and it is not required that an implementation must | |
| 135 support them. | |
|
Lasse Reichstein Nielsen
2017/04/26 09:15:31
Drop mentions of lower bounds.
eernst
2017/05/19 18:49:36
Done.
| |
| 136 | |
| 137 In general, the support for generic methods offered by this feature is not | |
| 138 intended to be complete, it is **intended** to allow **for** **experiments** | |
| 139 such that a final version of the generic method support can be designed | |
| 140 well, **and** it is intended to allow for the **subset of usages** where | |
| 141 reification is not required. | |
|
Lasse Reichstein Nielsen
2017/04/26 09:15:31
This disagrees with the paragraph above saying tha
eernst
2017/05/19 18:49:36
Done.
I inserted a shorted paragraph explaining t
| |
| 142 | |
| 143 ## Resolution and Type Checking | |
| 144 | |
| 145 In order to be useful, the support for generic methods and functions must be | |
| 146 sufficiently complete and consistent to **avoid spurious** diagnostic | |
| 147 **messages**. In particular, even though no regular type checks take place | |
| 148 at usages of routine type parameters in the body where they are in scope, | |
| 149 those type parameters should be resolved. If they had been ignored then any | |
| 150 usage of a routine type parameter `X` would give rise to a `Cannot resolve | |
| 151 type X` error message, or the usage might resolve to other declarations of | |
| 152 `X` in enclosing scopes such as a class type parameter, both of which is | |
| 153 unacceptable. | |
| 154 | |
| 155 In `dart2js` resolution, the desired behavior has been achieved by adding a | |
| 156 new type parameter **scope** and putting the type parameters into that | |
| 157 scope, giving each of them the bound `dynamic`. The type parameter scope is | |
| 158 the current scope during resolution of the routine signature and the type | |
| 159 parameter bounds, it encloses the formal parameter scope of the routine, and | |
| 160 the formal parameter scope in turn encloses the body scope. | |
| 161 | |
| 162 This implies that every usage of a routine type parameter is treated during | |
| 163 **type checking** as if it had been an alias for the type `dynamic`. | |
| 164 | |
| 165 Static checks for **invocations** of methods or functions where type | |
| 166 arguments are passed are omitted entirely: The type arguments are parsed, | |
| 167 but no checks are applied to certify that the given routine accepts type | |
| 168 arguments, and no checks are applied for bound violations. Similarly, no | |
| 169 checks are performed for invocations where no type arguments are passed, | |
| 170 whether or not the given routine is statically known to accept type | |
| 171 arguments. | |
| 172 | |
| 173 Certain usages of a routine type parameter `X` give rise to **errors**: It | |
| 174 is a compile-time error if `X` is used as a type literal (e.g., `foo(X)`), | |
| 175 or in an expression on the form `e is X` or `e is! X`. | |
| 176 | |
| 177 It could be argued that it should be a warning or an error if a routine type | |
| 178 parameter `X` is used in an expression on the form `e as X`. The blind | |
| 179 success of this test at runtime may introduce bugs into correct programs in | |
| 180 situations where the type constraint is violated; in particular, this could | |
| 181 cause "wrong" objects to propagate through local variables and parameters | |
| 182 and even into data structures (say, when a `List<T>` is actually a | |
| 183 `List<dynamic>`, because `T` is not present at runtime when the list is | |
| 184 created). However, considering that these type constraint violations are | |
| 185 expected to be rare, and considering that it is common to require that | |
| 186 programs compile without warnings, we have chosen to omit this warning. A | |
| 187 tool is still free to emit a hint, or in some other way indicate that there | |
| 188 is an issue. | |
| 189 | |
| 190 ## Dynamic semantics | |
| 191 | |
| 192 If a routine invocation specifies actual type arguments, e.g., `int` in the | |
| 193 **invocation** `f<int>(42)`, those type arguments will not be evaluated at | |
| 194 runtime, and they will not be passed to the routine in the | |
| 195 invocation. Similarly, no type arguments are ever passed to a generic | |
| 196 routine due to call-site inference. This corresponds to the fact that the | |
| 197 type arguments have no runtime representation. | |
| 198 | |
| 199 When the body of a generic **routine** is **executed**, usages of the formal | |
| 200 type parameters will either result in a run-time error, or they will yield | |
| 201 the value yielded by an evaluation of `dynamic`, following the treatment of | |
|
Lasse Reichstein Nielsen
2017/04/26 09:15:31
if `dynamic` can be shadowed, this it's correct to
eernst
2017/05/19 18:49:35
Incorrect?
Lasse Reichstein Nielsen
2017/06/01 07:56:59
Yes "incorrect". Read what I mean :)
I agree that
| |
| 202 malformed types in Dart. There are the following cases: | |
| 203 | |
| 204 When `X` is a routine type parameter, the evaluation of `e is X`, `e is! X`, | |
| 205 and `X` used as a type literal proceeds as if `X` had been a malformed type, | |
|
Lasse Reichstein Nielsen
2017/04/26 09:15:31
a type literal -> an expression
I'm not sure exact
eernst
2017/05/19 18:49:36
Done.
| |
| 206 producing a dynamic error if the type test itself is reached; the evaluation | |
| 207 of `e as X` has the same outcome as the evaluation of `e`. | |
| 208 | |
| 209 Note that the forms containing `is` are compile-time errors, which means | |
| 210 that implementations are free to reject the program, or to compile the | |
| 211 program with a different runtime semantics for these expressions. The | |
|
Lasse Reichstein Nielsen
2017/04/26 09:15:31
If it's specified as a compile-time error, then co
eernst
2017/05/19 18:49:35
If we insist that the language specification must
Lasse Reichstein Nielsen
2017/06/01 07:56:59
I'm not saying that compilers can't choose to impl
| |
| 212 rationale for `dart2js` to allow the construct and compile it to a run time | |
| 213 error is that (1) this allows more programs using generic methods to be | |
| 214 compiled, and (2) an `is` expression that blindly returns `true` every time | |
| 215 (or `false` every time) may silently introduce a bug into an otherwise | |
| 216 correct program, so the expression must fail if it is ever evaluated. | |
| 217 | |
| 218 When `X` is a routine type parameter which is passed as a type argument to a | |
| 219 generic class instantiation `G` which is itself used in `e is G`, `e is! G`, | |
|
Lasse Reichstein Nielsen
2017/04/26 09:15:31
Isn't this just saying that in a generic class ins
eernst
2017/05/19 18:49:35
Right, this can be compressed. Did that.
| |
| 220 `e as G`, and `G` used as a type literal, evaluation again proceeds as if | |
| 221 `X` were a malformed type, which in this case means that it is treated like | |
| 222 `dynamic`. | |
| 223 | |
| 224 This may be surprising, so let us consider a couple of examples: When `X` is | |
| 225 a routine type parameter, `42 is X` raises a dynamic error, `<int>[42] is | |
| 226 List<X>` yields the value `true`, and `42 as X` yields `42`, no matter | |
| 227 whether the syntax for the invocation of the routine included an actual type | |
| 228 argument, and, if so, no matter which value the actual type argument would | |
| 229 have had at the invocation. | |
| 230 | |
| 231 Object construction is similar: When `X` is a routine type parameter which | |
| 232 is a passed as a type argument to a constructor invocation, the value passed | |
| 233 to the constructor will be the value yielded by an evaluation of `dynamic`. | |
|
Lasse Reichstein Nielsen
2017/04/26 09:15:31
Drop "the value yielded by an evaluation of `dynam
eernst
2017/05/19 18:49:36
Adjusted the sentence in approximately that manner
| |
| 234 | |
| 235 In **checked mode**, when `X` is a routine type parameter which is used as a | |
| 236 type annotation or in a generic type `G` used as a type annotation, no | |
| 237 checked mode checks will ever fail for initialization or assignment to a | |
| 238 local variable or parameter whose type annotation is `X`, and if the type | |
| 239 annotation is a generic type `G` that contains `X`, checked mode checks will | |
| 240 succeed or fail as if `X` had been the type `dynamic`. | |
|
Lasse Reichstein Nielsen
2017/04/26 09:15:31
This is different from a malformed type, which wou
eernst
2017/05/19 18:49:36
Removed the phrase `which .. annotation`, in line
| |
| 241 | |
| 242 ## Changes | |
| 243 | |
| 244 2017-Jan-04: Changed 'static error' to 'compile-time error', which is the | |
| 245 phrase that the language specification uses. | |
| 246 | |
| 247 ## Notes | |
| 248 | |
| 249 [^1]: These expressions violate the common style in Dart with respect to | |
| 250 spacing and capitalization. That is because the ambiguity implies | |
| 251 conflicting requirements, and we do not want to bias the appearance in | |
| 252 one of the two directions. | |
| OLD | NEW |