Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(529)

Side by Side Diff: docs/language/informal/generic-method-syntax.md

Issue 2841483003: Added informal generic method syntax and generic function type specs. (Closed)
Patch Set: Review response Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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: Whenever the `primary` is followed by a balanced pair of angle
88 brackets where the next token after the final `>` is a left parenthesis (in
89 short, we are "looking at `< .. >(`"), it is parsed as a
90 **generic function invocation**.
Lasse Reichstein Nielsen 2017/06/01 07:57:00 Still disagree that this is what we want to specif
eernst 2017/07/06 10:02:13 Agreed, it's not pretty. I thought we'd need it in
91
92 This implies that an expression like `foo(a<b,2>(d))` will be rejected
93 because it is parsed such that `foo` gets one argument which must be a
94 generic function invocation, but `2` cannot parse correctly as a
95 `type`. This is a breaking change, because the same expression used to parse
96 correctly as an invocation of `foo` with two arguments.
97
98 The **reason** why the generic function invocation is favored over the
99 relational expressions is that it is considered to be a rare exception that
100 this ambiguity arises: It requires a balanced set of angle brackets followed
101 by a left parenthesis, which is already an unusual form. On top of that, the
102 style guide recommendation to use named parameters for boolean arguments
103 helps making this situation even less common.
104
105 If it does occur then there is an easy **workaround**: an extra set of
106 parentheses (as in `foo(a<b,(2>(d)))`) will resolve the ambiguity in the
107 direction of relational expressions; or we might simply be able to remove
108 the parentheses around the last expression (as in `foo(a<b,2>d)`), which
109 will also eliminate the ambiguity.
110
111 _It should be noted that parsing techniques like recursive descent seem to
112 conflict with this approach to disambiguation: Determining whether the
113 remaining input starts with a balanced expression on the form `<` .. `>`
114 seems to imply a need for an unbounded lookahead. However, if some type of
115 "diet" parsing is used and various kinds of bracket tokens are matched up
116 during the lexical analysis then it takes only a simple O(1) check in the
117 parser to perform the required check._
118
119 ## Scope of the Mechanism
120
121 With the syntax in place, it is obvious that certain potential extensions
122 have **not** been **included**.
123
124 For instance, constructors, setters, getters, and operators cannot be
125 declared as generic: Actual type arguments cannot be passed at invocation
126 sites for setters, getters, and operators, and for constructors there is a
127 need to find a way to distinguish between type arguments for the new
128 instance and type arguments for the constructor itself. It is possible that
129 some of these restrictions will be lifted in a future version of this
130 extension.
Lasse Reichstein Nielsen 2017/06/01 07:57:00 This is correct. It is only the constructor restri
eernst 2017/07/06 10:02:13 For setters and operators it isn't impossible. Who
131
132 This informal specification specifies a dynamic semantics where the values
133 of **actual type arguments are not reified** at run time. A future
134 extension of this mechanism may add this reification, such that dynamic
135 type tests and type casts involving routine type variables will be
136 supported.
137
138 ## Resolution and Type Checking
139
140 In order to be useful, the support for generic methods and functions must be
141 sufficiently complete and consistent to **avoid spurious** diagnostic
142 **messages**. In particular, even though no regular type checks take place
143 at usages of routine type parameters in the body where they are in scope,
144 those type parameters should be resolved. If they had been ignored then any
145 usage of a routine type parameter `X` would give rise to a `Cannot resolve
146 type X` error message, or the usage might resolve to other declarations of
147 `X` in enclosing scopes such as a class type parameter, both of which is
148 unacceptable.
149
150 In `dart2js` resolution, the desired behavior has been achieved by adding a
151 new type parameter **scope** and putting the type parameters into that
152 scope, giving each of them the bound `dynamic`. The type parameter scope is
153 the current scope during resolution of the routine signature and the type
154 parameter bounds, it encloses the formal parameter scope of the routine, and
155 the formal parameter scope in turn encloses the body scope.
156
157 This implies that every usage of a routine type parameter is treated during
158 **type checking** as if it had been an alias for the type dynamic.
159
160 Static checks for **invocations** of methods or functions where type
161 arguments are passed are omitted entirely: The type arguments are parsed,
162 but no checks are applied to certify that the given routine accepts type
163 arguments, and no checks are applied for bound violations. Similarly, no
164 checks are performed for invocations where no type arguments are passed,
165 whether or not the given routine is statically known to accept type
166 arguments.
167
168 Certain usages of a routine type parameter `X` give rise to **errors**: It
169 is a compile-time error if `X` is used as a type literal (e.g., `foo(X)`),
170 or in an expression on the form `e is X` or `e is! X`.
171
172 It could be argued that it should be a warning or an error if a routine type
173 parameter `X` is used in an expression on the form `e as X`. The blind
174 success of this test at runtime may introduce bugs into correct programs in
175 situations where the type constraint is violated; in particular, this could
176 cause "wrong" objects to propagate through local variables and parameters
177 and even into data structures (say, when a `List<T>` is actually a
178 `List<dynamic>`, because `T` is not present at runtime when the list is
179 created). However, considering that these type constraint violations are
180 expected to be rare, and considering that it is common to require that
181 programs compile without warnings, we have chosen to omit this warning. A
182 tool is still free to emit a hint, or in some other way indicate that there
183 is an issue.
184
185 ## Dynamic semantics
186
187 If a routine invocation specifies actual type arguments, e.g., `int` in the
188 **invocation** `f<int>(42)`, those type arguments will not be evaluated at
189 runtime, and they will not be passed to the routine in the
190 invocation. Similarly, no type arguments are ever passed to a generic
191 routine due to call-site inference. This corresponds to the fact that the
192 type arguments have no runtime representation.
193
194 When the body of a generic **routine** is **executed**, usages of the formal
195 type parameters will either result in a run-time error, or they will yield
196 the type dynamic, following the treatment of malformed types in
197 Dart. There are the following cases:
198
199 When `X` is a routine type parameter, the evaluation of `e is X`, `e is! X`,
200 and `X` used as an expression proceeds as if `X` had been a malformed type,
201 producing a dynamic error; the evaluation of `e as X` has the same outcome
202 as the evaluation of `e`.
203
204 Note that the forms containing `is` are compile-time errors, which means
205 that compilers may reject the program or offer ways to compile the program
206 with a different runtime semantics for these expressions. The rationale for
207 `dart2js` allowing the construct and compiling it to a run time error is
208 that (1) this allows more programs using generic methods to be compiled,
Lasse Reichstein Nielsen 2017/06/01 07:57:00 Why don't we do that everywhere then, if that is a
eernst 2017/07/06 10:02:13 Because the vm folks thought it would be too much
209 and (2) an `is` expression that blindly returns `true` every time (or
210 `false` every time) may silently introduce a bug into an otherwise correct
211 program, so the expression must fail if it is ever evaluated.
Lasse Reichstein Nielsen 2017/06/01 07:57:00 I still don't like this. Either it's a compile-tim
eernst 2017/07/06 10:02:13 But it is a compile-time error, and it is specifie
212
213 When `X` is a routine type parameter which is passed as a type argument to a
214 generic class instantiation `G`, it is again treated like a malformed type,
215 i.e., it is considered to denote the type dynamic.
216
217 This may be surprising, so let us consider a couple of examples: When `X` is
218 a routine type parameter, `42 is X` raises a dynamic error, `<int>[42] is
219 List<X>` yields the value `true`, and `42 as X` yields `42`, no matter
220 whether the syntax for the invocation of the routine included an actual type
221 argument, and, if so, no matter which value the actual type argument would
222 have had at the invocation.
223
224 Object construction is similar: When `X` is a routine type parameter which
225 is a passed as a type argument in a constructor invocation, the actual
226 value of the type type argument will be the type dynamic, as it would have
227 been with a malformed type.
228
229 In **checked mode**, when `X` is a routine type parameter, no checked mode
230 checks will ever fail for initialization or assignment to a local variable
231 or parameter whose type annotation is `X`, and if the type annotation is a
232 generic type `G` that contains `X`, checked mode checks will succeed or
233 fail as if `X` had been the type dynamic. Note that this differs from the
234 treatment of malformed types.
235
236 ## Changes
237
238 2017-Jan-04: Changed 'static error' to 'compile-time error', which is the
239 phrase that the language specification uses.
240
241 ## Notes
242
243 [^1]: These expressions violate the common style in Dart with respect to
244 spacing and capitalization. That is because the ambiguity implies
245 conflicting requirements, and we do not want to bias the appearance in
246 one of the two directions.
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698