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`. | |
Lasse Reichstein Nielsen
2017/07/11 11:41:37
Then mentioning dart2js and the flag is irrelevant
eernst
2017/07/12 12:50:19
Done.
| |
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 | |
Lasse Reichstein Nielsen
2017/07/11 11:41:36
"Routine" is usually a synonym for "procedure", no
eernst
2017/07/12 12:50:19
The intention behind using `routine` was that this
| |
20 something which can be a method, a top level function, a local function, or | |
21 a function literal expression. | |
Lasse Reichstein Nielsen
2017/07/11 11:41:36
This matches values with syntax ("function" vs "ex
eernst
2017/07/12 12:50:19
Adjusted to make it obvious that these are all syn
| |
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 | |
Lasse Reichstein Nielsen
2017/07/11 11:41:36
Evaluation only applies to expressions, so evaluat
eernst
2017/07/12 12:50:19
Well, it's about expressions, like `foo<T>() => T;
| |
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 | |
Lasse Reichstein Nielsen
2017/07/11 11:41:36
Nope, not any more. Remove this paragraph.
eernst
2017/07/12 12:50:19
Oops, missed that. Done!
| |
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. | |
Lasse Reichstein Nielsen
2017/07/11 11:41:36
The breaking change is that:
foo(a<b,c>(d))
will
eernst
2017/07/12 12:50:19
Added a short paragraph saying that.
| |
94 | |
95 The **reason** why the generic function invocation is favored over the | |
Lasse Reichstein Nielsen
2017/07/11 11:41:36
Consider dropping "why". It feels redundant.
Or re
eernst
2017/07/12 12:50:19
Done.
| |
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 | |
Lasse Reichstein Nielsen
2017/07/11 11:41:36
drop "diet".
Or:
if a parsing strategy is used th
eernst
2017/07/12 12:50:19
Done.
| |
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 |