OLD | NEW |
---|---|
(Empty) | |
1 # Feature: Generic Method Syntax | |
2 | |
3 ## Feature: Generic Method Syntax | |
4 | |
5 **This document** is an informal specification of the support for generic method s and functions which has been implemented in `dart2js` with option `--generic-m ethod-syntax`, starting with commit [acc5f59](https://github.com/dart-lang/sdk/c ommit/acc5f59a99d5d8747459c935e6360ac325606cc6). In SDK 1.21 this feature is ava ilable by default (i.e., also without the option) in the virtual machine and the analyzer, as well as in `dart2js`. | |
Lasse Reichstein Nielsen
2017/03/06 09:45:14
Consider breaking paragraphs into shorter lines.
| |
6 | |
7 The **motivation for** having this **feature** is that it enables partial suppor t for generic methods and functions, thus providing a bridge between not having generic methods and having full support for generic methods. In particular, code declaring and using generic methods may be type checked and compiled in strong mode, and the same code will now be acceptable in standard (non-strong) mode as well. The semantics is different in certain cases, but standard mode analysis wi ll emit diagnostic messages (e.g., errors) for that. | |
8 | |
9 In this document, the word **routine** will be used when referring to something which can be a method, a top level function, a local function, or a function lit eral expression. | |
Lasse Reichstein Nielsen
2017/03/06 09:45:15
The "routine" name is a little odd, and I'm not su
| |
10 | |
11 With **this feature** it is possible to compile code where generic methods and f unctions are declared, implemented, and invoked. The runtime semantics does not include reification of type arguments. Evaluations of the runtime value of a rou tine type parameter is a runtime error or yields `dynamic`, depending on the con text. No type checking takes place at usages of a method or function type parame ter in the body, and no type checking regarding explicitly specified or omitted type arguments takes place at call sites. | |
Lasse Reichstein Nielsen
2017/03/06 09:45:14
"in the body" of what?
Lasse Reichstein Nielsen
2017/03/06 09:45:14
"The runtime semantics does not include reificatio
Lasse Reichstein Nielsen
2017/03/06 09:45:14
All in all, sentence is very hard to read, and I'm
Lasse Reichstein Nielsen
2017/03/06 09:45:14
"Evaluations of the runtime value of ..."
I wouldn
| |
12 | |
13 In short, generic methods and functions are supported syntactically, and the run time semantics prevents dynamic usages of the type argument values, but it allow s all usages where that dynamic value is not required. For instance, a generic r outine type parameter, `T`, cannot be used in an expression like `x is T`, but i t can be used as a type annotation. In a context where other tools may perform t ype checking, this allows for a similar level of expressive power as do language designs where type arguments are erased at compile time. | |
Lasse Reichstein Nielsen
2017/03/06 09:45:14
Please break into separate lines, it's impossible
Lasse Reichstein Nielsen
2017/03/06 09:45:14
What does "is not required mean"? Probably not syn
| |
14 | |
15 The **motivation for** this **document** is that it serves as an informal specif ication for the implementation of support for the generic method syntax feature in all Dart tools. | |
Lasse Reichstein Nielsen
2017/03/06 09:45:14
That's a long sentence. Far too many null-words to
| |
16 | |
17 ## Syntax | |
18 | |
19 The syntactic elements which are added or modified in order to support this feat ure are as follows, based on grammar rules given in the Dart Language Specificat ion (Aug 19, 2015). | |
Lasse Reichstein Nielsen
2017/03/06 09:45:14
Maybe: which -> that (not sure if the "which" her
| |
20 | |
21 ``` | |
22 formalParameterPart: | |
23 typeParameters? formalParameterList | |
24 functionSignature: | |
25 metadata returnType? identifier formalParameterPart | |
26 typeParameter: | |
27 metadata identifier (('extends'|'super') type)? | |
28 functionExpression: | |
29 formalParameterPart functionBody | |
30 fieldFormalParameter: | |
31 metadata finalConstVarOrType? 'this' '.' identifier | |
32 formalParameterPart? | |
33 argumentPart: | |
34 typeArguments? arguments | |
35 selector: | |
36 assignableSelector | argumentPart | |
37 assignableExpression: | |
38 primary (argumentPart* assignableSelector)+ | | |
39 'super' unconditionalAssignableSelector | | |
40 identifier | |
41 cascadeSection: | |
42 '..' (cascadeSelector argumentPart*) | |
43 (assignableSelector argumentPart*)* | |
44 (assignmentOperator expressionWithoutCascade)? | |
45 ``` | |
46 | |
47 In a [draft specification](https://codereview.chromium.org/1177073002) of generi c methods from June 2015, the number of grammar changes is significantly higher, but that form can be obtained via renaming. | |
Lasse Reichstein Nielsen
2017/03/06 09:45:14
Renaming of what? Is this sentence important, or j
| |
48 | |
49 This extension to the grammar gives rise to an **ambiguity** where the same toke ns may be angle brackets of a type argument list as well as relational operators . For instance, `foo(a<b,c>(d))`[^1] may be parsed as a `postfixExpression` on t he form `primary arguments` where the arguments are two relational expressions ( `a<b` and `c>(d)`), and it may also be parsed such that there is a single argum ent which is an invocation of a generic function (`a<b,c>(d)`). The ambiguity i s resolved in **favor** of the **generic function invocation** whenever the `pri mary` is followed by a balanced pair of angle brackets where the next token afte r the final `>` is a left parenthesis (in short, we are "looking at `< .. >(`"). | |
Lasse Reichstein Nielsen
2017/03/06 09:45:14
looking at `e< ... >(`
That is: add `e` before th
| |
50 | |
51 This implies that an expression like `foo(a<b,2>(d))` will be rejected because i t is parsed such that `foo` gets one argument which must be a generic function i nvocation, but `2` cannot parse correctly as a `type`. This is a breaking change , because the same expression used to parse correctly as an invocation of `foo` with two arguments. | |
52 | |
53 The **reason** why the generic function invocation is favored over the relationa l expressions is that it is considered to be a rare exception that this ambiguit y arises: It requires a balanced set of angle brackets followed by a left parent hesis, which is already an unusual form. On top of that, the style guide recomme ndation to use named parameters for boolean arguments helps making this situatio n even less common. | |
54 | |
55 If it does occur then there is an easy **workaround**: an extra set of parenthes es (as in `foo(a<b,(2>(d)))`) will resolve the ambiguity in the direction of rel ational expressions; or we might simply be able to remove the parentheses around the last expression (as in `foo(a<b,2>d)`), which will also eliminate the ambig uity. | |
56 | |
57 It should be noted that parsing techniques like recursive descent seem to confli ct with this approach to disambiguation: Determining whether the remaining input starts with a balanced expression on the form `<` .. `>` seems to imply a need for an unbounded lookahead. However, if some type of "diet" parsing is used and various kinds of bracket tokens are matched up during the lexical analysis then it takes only a simple O(1) check in the parser to perform the required check. | |
Lasse Reichstein Nielsen
2017/03/06 09:45:14
"diet parsing" isn't a concept that the reader can
| |
58 | |
59 ## Scope of the Mechanism | |
60 | |
61 With the syntax in place, it is obvious that certain potential extensions have * *not** been **included**. | |
62 | |
63 For instance, constructors, setters, getters, and operators cannot be declared a s generic. Actual type arguments cannot be passed at invocation sites for setter s, getters, and operators, and for constructors there is a need to find a way to distinguish between type arguments for the new instance and type arguments for the constructor itself. It is possible that some of these restrictions will be l ifted in a full-fledged version of this extension. | |
Lasse Reichstein Nielsen
2017/03/06 09:45:15
I wouldn't say that getters, setters or operators
| |
64 | |
65 Conversely, the inclusion of lower bounds for type parameters (using the keyword `super`) serves to demonstrate that lower bounds fit well into the syntax. Ther e is no guarantee that a final version of generic methods will support lower bou nds, and it is not required that an implementation must support them. | |
Lasse Reichstein Nielsen
2017/03/06 09:45:14
Then don't include it. Implementations should impl
| |
66 | |
67 In general, the support for generic methods offered by this feature is not inten ded to be complete, it is **intended** to allow **for** **experiments** such tha t a final version of the generic method support can be designed well, **and** it is intended to allow for the **subset of usages** where reification is not requ ired. | |
Lasse Reichstein Nielsen
2017/03/06 09:45:14
This is confusing. That's not what you said above
| |
68 | |
69 ## Resolution and Type Checking | |
70 | |
71 In order to be useful, the support for generic methods and functions must be suf ficiently complete and consistent to **avoid spurious** diagnostic **messages**. In particular, even though no regular type checks take place at usages of routi ne type parameters in the body where they are in scope, those type parameters sh ould be resolved. If they had been ignored then any usage of a routine type para meter `X` would give rise to a `Cannot resolve type X` error message, or the usa ge might resolve to other declarations of `X` in enclosing scopes such as a clas s type parameter, both of which is unacceptable. | |
Lasse Reichstein Nielsen
2017/03/06 09:45:14
"In particular, even though no regular type checks
| |
72 | |
73 In `dart2js` resolution, the desired behavior has been achieved by adding a new type parameter **scope** and putting the type parameters into that scope, giving each of them the bound `dynamic`. The type parameter scope is the current scope during resolution of the routine signature and the type parameter bounds, it en closes the formal parameter scope of the routine, and the formal parameter scope in turn encloses the body scope. | |
74 | |
75 This implies that every usage of a routine type parameter is treated during **ty pe checking** as if it had been an alias for the type `dynamic`. | |
76 | |
77 Static checks for **invocations** of methods or functions where type arguments a re passed are omitted entirely: The type arguments are parsed, but no checks are applied to certify that the given routine accepts type arguments, and no checks are applied for bound violations. Similarly, no checks are performed for invoca tions where no type arguments are passed, whether or not the given routine is st atically known to accept type arguments. | |
78 | |
79 Certain usages of a routine type parameter `X` give rise to **errors**: It is a compile-time error if `X` is used as a type literal (e.g., `foo(X)`), or in an e xpression on the form `e is X` or `e is! X`. | |
Lasse Reichstein Nielsen
2017/03/06 09:45:14
I think we should also disallow `on T { ... }` in
| |
80 | |
81 It could be argued that it should be a warning or an error if a routine type par ameter `X` is used in an expression on the form `e as X`. The blind success of t his test at runtime may introduce bugs into correct programs in situations where the type constraint is violated; in particular, this could cause "wrong" object s to propagate through local variables and parameters and even into data structu res (say, when a `List<T>` is actually a `List<dynamic>`, because `T` is not pre sent at runtime when the list is created). However, considering that these type constraint violations are expected to be rare, and considering that it is common to require that programs compile without warnings, we have chosen to omit this warning. A tool is still free to emit a hint, or in some other way indicate that there is an issue. | |
82 | |
83 ## Dynamic semantics | |
84 | |
85 If a routine invocation specifies actual type arguments, e.g., `int` in the **in vocation** `f<int>(42)`, those type arguments will not be evaluated at runtime, and they will not be passed to the routine in the invocation. Similarly, no type arguments are ever passed to a generic routine due to call-site inference. This corresponds to the fact that the type arguments have no runtime representation. | |
Lasse Reichstein Nielsen
2017/03/06 09:45:14
This is Dart 1 only, so don't mention inference.
| |
86 | |
87 When the body of a generic **routine** is **executed**, usages of the formal typ e parameters will either result in a run-time error, or they will yield the valu e yielded by an evaluation of `dynamic`, following the treatment of malformed ty pes in Dart. There are the following cases: | |
Lasse Reichstein Nielsen
2017/03/06 09:45:14
Again, I prefer not to use "evaluate" and "value"
| |
88 | |
89 When `X` is a routine type parameter, the evaluation of `e is X`, `e is! X`, and `X` used as a type literal proceeds as if `X` had been a malformed type, produc ing a dynamic error if the type test itself is reached; the evaluation of `e as X` has the same outcome as the evaluation of `e`. | |
90 | |
91 Note that the forms containing `is` are compile-time errors, which means that im plementations are free to reject the program, or to compile the program with a d ifferent runtime semantics for these expressions. The rationale for `dart2js` to allow the construct and compile it to a run time error is that (1) this allows more programs using generic methods to be compiled, and (2) an `is` expression t hat blindly returns `true` every time (or `false` every time) may silently intro duce a bug into an otherwise correct program, so the expression must fail if it is ever evaluated. | |
Lasse Reichstein Nielsen
2017/03/06 09:45:14
You didn't say above that it's a compile-time erro
| |
92 | |
93 When `X` is a routine type parameter which is passed as a type argument to a gen eric class instantiation `G` which is itself used in `e is G`, `e is! G`, `e as G`, and `G` used as a type literal, evaluation again proceeds as if `X` were a m alformed type, which in this case means that it is treated like `dynamic`. | |
Lasse Reichstein Nielsen
2017/03/06 09:45:14
Be more explicit (meaning, actually write "G<X>"):
| |
94 | |
95 This may be surprising, so let us consider a couple of examples: When `X` is a r outine type parameter, `42 is X` raises a dynamic error, `<int>[42] is List<X>` yields the value `true`, and `42 as X` yields `42`, no matter whether the syntax for the invocation of the routine included an actual type argument, and, if so, no matter which value the actual type argument would have had at the invocation . | |
Lasse Reichstein Nielsen
2017/03/06 09:45:14
Drop the "no matter ..." part. We already establis
| |
96 | |
97 Object construction is similar: When `X` is a routine type parameter which is a passed as a type argument to a constructor invocation, the value passed to the c onstructor will be the value yielded by an evaluation of `dynamic`. | |
Lasse Reichstein Nielsen
2017/03/06 09:45:14
Explicit example: , e.g., `new Map<int, X>()` or `
| |
98 | |
99 In **checked mode**, when `X` is a routine type parameter which is used as a typ e annotation or in a generic type `G` used as a type annotation, no checked mode checks will ever fail for initialization or assignment to a local variable or p arameter whose type annotation is `X`, and if the type annotation is a generic t ype `G` that contains `X`, checked mode checks will succeed or fail as if `X` ha d been the type `dynamic`. | |
Lasse Reichstein Nielsen
2017/03/06 09:45:14
Feels like it's repeating itself here. G<X> is G<d
| |
100 | |
101 ## Changes | |
102 | |
103 2017-Jan-04: Changed 'static error' to 'compile-time error', which is the phrase that the language specification uses. | |
104 | |
105 ## Notes | |
106 | |
107 [^1]: These expressions violate the common style in Dart with respect to spacing and capitalization. That is because the ambiguity implies conflicting requireme nts, and we do not want to bias the appearance in one of the two directions. | |
OLD | NEW |