|
|
Created:
3 years, 10 months ago by eernst Modified:
3 years, 9 months ago Reviewers:
Lasse Reichstein Nielsen CC:
reviews_dartlang.org, floitsch Target Ref:
refs/heads/master Visibility:
Public. |
DescriptionAdded generic method syntax to language specification.
Patch Set 1 #
Total comments: 32
Patch Set 2 : Added informal spec #Patch Set 3 : Review response #
Total comments: 37
Messages
Total messages: 6 (1 generated)
eernst@google.com changed reviewers: + lrn@google.com
Added generic method syntax specification to dartLangSpec.tex. The basic idea is to introduce a notion of 'generic function erasure'; the rest of the language only gets to see the erased function, which takes care of the typing properties and the dynamic semantics.
https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.tex File docs/language/dartLangSpec.tex (right): https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:620: Functions include function declarations (\ref{functionDeclarations}), methods (\ref{instanceMethods}, \ref{staticMethods}), getters (\ref{getters}), setters (\ref{setters}), constructors (\ref{constructors}) and function literals (\ref{functionExpressions}). Does "function" refer to the syntactic declaration or its semantic meaning? If it's the former, consider changing "Functions" to "Function declarations". https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:623: All functions have a signature and a body. ... except abstract and external methods have no body, and neither does some constructors. (No rule without an exception :) https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:624: The signature describes the formal parameter part of the function, its name, and possibly its return type. ... except that function expressions do not have a name, so: "its name (if any),"? Is "signature" a lexical, static or dynamic concept? (which goes back to: is "function" a lexical, static or dynamic concept?) https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:625: The formal parameter part optionally specifies the formal type parameter list of the function, and it specifies its formal parameter list. formal parameter list, if any. (Getters have no formal parameter list, they are included in the list above). https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:712: However, the scope of a function's signature is the function's enclosing scope, not the formal parameter scope. What *is* a "signature"? https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:2448: Many function declarations may include a {\em formal type parameter list} (\ref{functions}), in which case we say that it is a {\em generic function}. "Many" -> "Some". https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:2452: The exceptions are getter, setter, operator, and constructor declarations. "... , which cannot be generic." (Didn't say what it was an exception of). https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:2461: The formal type parameter scope of a function $f$ is the current scope for the return type of $f$, if any, and for the formal type parameter list itself. The type parameter list is in its own scope. What does that *mean*? I understand that the bounds are in that scope, but what does it mean that the type parameter identifier itself is in a scope where it's declared? https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:2464: This enables the use of F-bounded type parameters (if you don't know what that is, see~\ref{generics}). Drop this enitre sentence. It doesn't say anything important. https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:2469: {\em Generic function erasure} takes as input a generic function declaration $D$ and either yields a non-generic function declaration $D^{\prime}$ or terminates with a compile-time error. Do you need to super-script the \prime? Consider using $D_E$ for the erased declaration. Do we use "prime" anywhere else? https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:2472: \item The formalParameterPart of $D^{\prime}$ is identical to the formalParameterList of $D$. Should "formalParameterPart" be styled in some way? https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:2473: \commentary{That is, the type parameter list is erased.} erased -> omitted "erase" is a destructive operation, but we don't change the original, we create a new one without some of the original parts. https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:2477: \item In all other cases, the location corresponding to $id$ in $D^{\prime}$ contains \DYNAMIC{}. ... all other cases, the occurrence of $id$ in $D$ is replaced by \DYNAMIC{} in $D_E$. https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:3879: In this situation, the parse as a generic function invocation is chosen. -> In this situation, the expression is always parsed as a generic function invocation. https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:3888: An example is \code{f(a<B, C>($d$))}, which may be an invocation of \code{f} passing two actual arguments of type \code{bool}, or an invocation of \code{f} passing the result returned by an invocation of the generic function \code{a}. Why is $d$ in code, but not a, B or C? I can see that B and C must be type names, but at least 'a' can also be an arbitrary expression. https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:3889: Note that the ambiguity may be eliminated by omitting the parentheses around the expression $d$, or adding parentheses around one of the relational expressions. Or around \code{B} or \code{C}.
Review response https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.tex File docs/language/dartLangSpec.tex (right): https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:620: Functions include function declarations (\ref{functionDeclarations}), methods (\ref{instanceMethods}, \ref{staticMethods}), getters (\ref{getters}), setters (\ref{setters}), constructors (\ref{constructors}) and function literals (\ref{functionExpressions}). On 2017/02/13 10:26:31, Lasse Reichstein Nielsen wrote: > Does "function" refer to the syntactic declaration or its semantic meaning? > > If it's the former, consider changing "Functions" to "Function declarations". This is one of several cases where I've removed spurious spaces from a line which was modified during the writing process but at the end came back to the original wording. I thought it wouldn't hurt to keep the space-normalization, now that I had already done it. So now I'll reconsider that original wording. ;-) I definitely think that the whole sentence fits in with the interpretation that it is a list of syntactic constructs. Adjusted the sentence such that the first word `Functions` refers to the semantic entity, and says that it is `introduced by` all those syntactic forms. https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:623: All functions have a signature and a body. On 2017/02/13 10:26:32, Lasse Reichstein Nielsen wrote: > ... except abstract and external methods have no body, and neither does some > constructors. > (No rule without an exception :) Maintaining the focus on syntax, and allowing for abstract and external declarations via the word 'some'. https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:624: The signature describes the formal parameter part of the function, its name, and possibly its return type. On 2017/02/13 10:26:31, Lasse Reichstein Nielsen wrote: > ... except that function expressions do not have a name, so: "its name (if > any),"? > > Is "signature" a lexical, static or dynamic concept? (which goes back to: is > "function" a lexical, static or dynamic concept?) Adjusted to mention the exceptions (getter, literal). I consider 'signature' to be a syntactic concept here. The coercion to a semantic entity may happen implicitly here and there, but since it is never a first class entity I believe that we can maintain "it's syntax" and tolerate the excursions into semantics as needed. Do you think we need to introduce explicit terminology for a semantic notion of signatures? We do need to talk about the function type, but that's a separate term already, and the spec explicitly specifies how to compute it. https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:625: The formal parameter part optionally specifies the formal type parameter list of the function, and it specifies its formal parameter list. On 2017/02/13 10:26:32, Lasse Reichstein Nielsen wrote: > formal parameter list, if any. > > (Getters have no formal parameter list, they are included in the list above). I've mentioned the exception for getters above, so they should be out of the picture at this point. https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:712: However, the scope of a function's signature is the function's enclosing scope, not the formal parameter scope. On 2017/02/13 10:26:32, Lasse Reichstein Nielsen wrote: > What *is* a "signature"? Line 712 is again something that I did not touch (but I split the long line into one line per sentence, as usual). I believe that a signature is syntax, and I'm not sure there is a semantic entity for it. Semantics is relevant for complete functions and for function types, but the function types do not include the name so that isn't exactly a signature. Scopes can apply to syntax because they are used during lookups, and lookups refers to names (and even though you can claim that they are 'semantic names', they are very nearly syntax). I usually say 'scope S is the current scope for A' in order to say that the specific scope S is the first one to be searched during lookups when a name is encountered in the syntactic construct A, except for parts of A where further nested scopes are active. I believe Gilad is saying that the parameter names aren't in scope for terms in the signature (so we could have `C foo(C C)` where the last `C` is the parameter name, and the first two `C`s are a class `C` from the enclosing scope). Changed line 712 to use the 'current' terminology. Added a commentary, too. https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:2448: Many function declarations may include a {\em formal type parameter list} (\ref{functions}), in which case we say that it is a {\em generic function}. On 2017/02/13 10:26:31, Lasse Reichstein Nielsen wrote: > "Many" -> "Some". Done. https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:2452: The exceptions are getter, setter, operator, and constructor declarations. On 2017/02/13 10:26:31, Lasse Reichstein Nielsen wrote: > "... , which cannot be generic." > (Didn't say what it was an exception of). Done. https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:2461: The formal type parameter scope of a function $f$ is the current scope for the return type of $f$, if any, and for the formal type parameter list itself. On 2017/02/13 10:26:31, Lasse Reichstein Nielsen wrote: > The type parameter list is in its own scope. What does that *mean*? > I understand that the bounds are in that scope, but what does it mean that the > type parameter identifier itself is in a scope where it's declared? The declaring occurrence of a type parameter identifier is syntactically known to be declaring, and applied occurrences in bounds are similarly known to be applied. So the latter will be subject to lookups, but the former are not. Hence it makes no difference which scope is the current scope for the declaring occurrences. Hence it is a non-problem that their current scope is the formal type parameter scope. No changes performed in this case. https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:2464: This enables the use of F-bounded type parameters (if you don't know what that is, see~\ref{generics}). On 2017/02/13 10:26:32, Lasse Reichstein Nielsen wrote: > Drop this enitre sentence. It doesn't say anything important. It was a stupid joke (referring to \ref{generics}: "Because type parameters are in scope in their bounds, we support F-bounded quantification (if you don't know what that is, don't ask)."). Deleted the joke, but kept the phrase mentioning F-bounded type parameters, because this is part of a natural progression: First we talk about direct access to the names of type parameters in this scope (F-bounds will find these names in the current scope) and then we talk about indirect access (type annotations and type arguments in the formal parameter list and in the body will find these names in an enclosing scope). https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:2469: {\em Generic function erasure} takes as input a generic function declaration $D$ and either yields a non-generic function declaration $D^{\prime}$ or terminates with a compile-time error. On 2017/02/13 10:26:32, Lasse Reichstein Nielsen wrote: > Do you need to super-script the \prime? As far as I can see, we have 54 occurrences of `\prime` and they are all in superscript. It may be unnecessary, but I guess this makes them a bit smaller, which might look better. Haven't made any experiments. > Consider using $D_E$ for the erased declaration. Do we use "prime" anywhere > else? Just checking for reasons: I believe `\prime` is used to describe auxiliar looked-up or computed entities. Line 1581: redirecting factory constructor, \prime is used to denote the target constructor Line 1886: for transitivity of subclassing, `S^\prime` is the intermediate class. Line 1915: `m^\prime` is a member which is overridden by a given member `m`, used in a discussion about overriding rules. Line 2100: more overriding stuff. Line 5957: `c^\prime` is computed from `c`, which is the condition of a 'for' statement. Line 5964: `v^\prime` is the fresh variable which is created for each iteration of a 'for' statement. Especially the last ones fit quite well, because they are about program transformations used to specify the semantics of a given construct. But after thinking about it, I guess it would be more readable to use `D_{erased}`, so I'll do that. https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:2472: \item The formalParameterPart of $D^{\prime}$ is identical to the formalParameterList of $D$. On 2017/02/13 10:26:32, Lasse Reichstein Nielsen wrote: > Should "formalParameterPart" be styled in some way? The grammar rules in text actually do not have any special font features, they're just boldface when on the left hand side of a grammar rule. Using boldface here, because that at least gives a hint of "grammar stuff". We have many occurrences of words like 'identifier' that happen to be grammar non-terminals as well as "normal words". I'm not sure whether there are any such words which should be considered carefully in order to decide whether to `\GrammarSymbol` them. Fixing only these two words at this point. https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:2473: \commentary{That is, the type parameter list is erased.} On 2017/02/13 10:26:32, Lasse Reichstein Nielsen wrote: > erased -> omitted > > "erase" is a destructive operation, but we don't change the original, we create > a new one without some of the original parts. True. I actually wrote `omitted` first because that's the direct way to say what is going on; but later I changed it to `erased` because it is so similar to the erasure which has been used in Java for many years. In that sense, it might push the right button in the heads of readers who know about Java. Still prefer `omitted`? https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:2477: \item In all other cases, the location corresponding to $id$ in $D^{\prime}$ contains \DYNAMIC{}. On 2017/02/13 10:26:31, Lasse Reichstein Nielsen wrote: > ... all other cases, the occurrence of $id$ in $D$ is replaced by \DYNAMIC{} in > $D_E$. Frem og tilbage er lige langt. ;-D In this case, 'the corresponding location contains' is completely declarative (..that's just how it is, nobody is doing anything..), whereas 'replace' implies mutation. In a sense, it contradicts the notion of having a $D$ and a \Derased{}, and specifying the latter in terms of the similarities/differences to the former, and hints at an editing process which operates on $D$ for a while, at which point we rename it to \Derased{}. Still `replace`? ;) https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:3879: In this situation, the parse as a generic function invocation is chosen. On 2017/02/13 10:26:32, Lasse Reichstein Nielsen wrote: > -> In this situation, the expression is always parsed as a generic function > invocation. Done. https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:3888: An example is \code{f(a<B, C>($d$))}, which may be an invocation of \code{f} passing two actual arguments of type \code{bool}, or an invocation of \code{f} passing the result returned by an invocation of the generic function \code{a}. On 2017/02/13 10:26:32, Lasse Reichstein Nielsen wrote: > Why is $d$ in code, but not a, B or C? > I can see that B and C must be type names, but at least 'a' can also be an > arbitrary expression. Right, but this is commentary and we just need a single example, so I was thinking about `a`, `B`, and `C` as concrete identifiers, i.e., literal syntax, and `d` as a more arbitrary expression, which could concretely create a need for the parentheses. Maybe we should just make `d` concrete as well, and use `\code{a<B, C>(d..foo()..bar())}`, which would create that need? https://codereview.chromium.org/2690553002/diff/1/docs/language/dartLangSpec.... docs/language/dartLangSpec.tex:3889: Note that the ambiguity may be eliminated by omitting the parentheses around the expression $d$, or adding parentheses around one of the relational expressions. On 2017/02/13 10:26:31, Lasse Reichstein Nielsen wrote: > Or around \code{B} or \code{C}. That wouldn't actually work, because we did not require for a commitment to "this is a generic call" that the type arguments be syntactically correct, we just required the matching angle brackets. So `f(a<B, (C)>(d))` would be a syntax error because `(C)` is not a proper type argument, because `a<.., ..>(` is enough to decide that it isn't two bool args.
After reading it, I have the nagging feeling that some things are said more than once. Or maybe it's just similar things that are not discussed together. https://codereview.chromium.org/2690553002/diff/40001/docs/language/informal/... File docs/language/informal/generic-method-syntax.md (right): https://codereview.chromium.org/2690553002/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:5: **This document** is an informal specification of the support for generic methods and functions which has been implemented in `dart2js` with option `--generic-method-syntax`, starting with commit [acc5f59](https://github.com/dart-lang/sdk/commit/acc5f59a99d5d8747459c935e6360ac325606cc6). In SDK 1.21 this feature is available by default (i.e., also without the option) in the virtual machine and the analyzer, as well as in `dart2js`. Consider breaking paragraphs into shorter lines. If nothing else, it makes it easier to comment on in the code-review tool! https://codereview.chromium.org/2690553002/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md: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 literal expression. The "routine" name is a little odd, and I'm not sure this definition makes me any wiser. I'm guessing the point is that it *omits* getters, setters and constructors. Since I don't know what "method" covers anyway, I wouldn't know if they are included in that, so be explicit. (For example, the spec says "the implicit getter method" at some point). Maybe something like: "That is, any callable declaration except getters, setters and constructors." Also, as defined here, it's not clear whether "routine" refers to syntax or to semantic values - method, top-level function or local function can be either, and literal expressions is only syntax (which evaluate to function values). https://codereview.chromium.org/2690553002/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:11: With **this feature** it is possible to compile code where generic methods and functions are declared, implemented, and invoked. The runtime semantics does not include reification of type arguments. Evaluations of the runtime value of a routine type parameter is a runtime error or yields `dynamic`, depending on the context. No type checking takes place at usages of a method or function type parameter in the body, and no type checking regarding explicitly specified or omitted type arguments takes place at call sites. "Evaluations of the runtime value of ..." I wouldn't say that you evaluate a value. You evaluate syntax, not values, so maybe "Evaluation of a routine type parameter as an expression...". I also wouldn't use "evaluate" about "type expression", only actual expressions. That leaves us without a word for what you do to the 'type' "T" to find the type it's bound to. Maybe: "The semantics of a routine type parameter is always either the dynamic type or an error."? https://codereview.chromium.org/2690553002/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:11: With **this feature** it is possible to compile code where generic methods and functions are declared, implemented, and invoked. The runtime semantics does not include reification of type arguments. Evaluations of the runtime value of a routine type parameter is a runtime error or yields `dynamic`, depending on the context. No type checking takes place at usages of a method or function type parameter in the body, and no type checking regarding explicitly specified or omitted type arguments takes place at call sites. "in the body" of what? https://codereview.chromium.org/2690553002/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:11: With **this feature** it is possible to compile code where generic methods and functions are declared, implemented, and invoked. The runtime semantics does not include reification of type arguments. Evaluations of the runtime value of a routine type parameter is a runtime error or yields `dynamic`, depending on the context. No type checking takes place at usages of a method or function type parameter in the body, and no type checking regarding explicitly specified or omitted type arguments takes place at call sites. All in all, sentence is very hard to read, and I'm not sure what it's saying. https://codereview.chromium.org/2690553002/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:11: With **this feature** it is possible to compile code where generic methods and functions are declared, implemented, and invoked. The runtime semantics does not include reification of type arguments. Evaluations of the runtime value of a routine type parameter is a runtime error or yields `dynamic`, depending on the context. No type checking takes place at usages of a method or function type parameter in the body, and no type checking regarding explicitly specified or omitted type arguments takes place at call sites. "The runtime semantics does not include reification of arguments" Very roundabout statement to say that something doesn't happen. How about just "Type arguments are not available at runtime." (Less is more!) https://codereview.chromium.org/2690553002/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:13: In short, generic methods and functions are supported syntactically, and the runtime semantics prevents dynamic usages of the type argument values, but it allows all usages where that dynamic value is not required. For instance, a generic routine type parameter, `T`, cannot be used in an expression like `x is T`, but it can be used as a type annotation. In a context where other tools may perform type checking, this allows for a similar level of expressive power as do language designs where type arguments are erased at compile time. Please break into separate lines, it's impossible to comment on these blocks in the code review tool :) (That's a bug, NOT a feature!) https://codereview.chromium.org/2690553002/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:13: In short, generic methods and functions are supported syntactically, and the runtime semantics prevents dynamic usages of the type argument values, but it allows all usages where that dynamic value is not required. For instance, a generic routine type parameter, `T`, cannot be used in an expression like `x is T`, but it can be used as a type annotation. In a context where other tools may perform type checking, this allows for a similar level of expressive power as do language designs where type arguments are erased at compile time. What does "is not required mean"? Probably not syntactically, so you have an idea that you are trying to express, but this sentence doesn't do that. It then gives examples that are not helping - you are not saying why `x is T` is different form `T x`. (The reasoning, as I understand it, is that *validation* is allowed but *tests* are not - we can continue running by assuming that a validation succeeded (even if it didn't) but we cannot pick a branch after a test. That rules out `is` tests (which create a boolean) and `on` tests, but type annotations and `as` casts. It's unclear what use as a class type-parameter means: `new List<T>`, `is List<T>`, but I think we treat that as a type annotation use. So, write that (if that is what you intend). https://codereview.chromium.org/2690553002/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:15: The **motivation for** this **document** is that it serves as an informal specification for the implementation of support for the generic method syntax feature in all Dart tools. That's a long sentence. Far too many null-words to say something that can be said shorter. Maybe: Motivation: This document is an informal specification for generic method syntax in Dart tools. Allow me a reference: http://www.kommunikationsforum.dk/log/multimedia/billeder%20til%20artikler/me... https://codereview.chromium.org/2690553002/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:19: The syntactic elements which are added or modified in order to support this feature are as follows, based on grammar rules given in the Dart Language Specification (Aug 19, 2015). Maybe: which -> that (not sure if the "which" here is restrictive or descriptive, but if it's descriptive, there should be commas). Consider avoiding the inserted phrase completely: The following syntactic elements are added or modified to support this feature, based on ... https://codereview.chromium.org/2690553002/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:47: In a [draft specification](https://codereview.chromium.org/1177073002) of generic methods from June 2015, the number of grammar changes is significantly higher, but that form can be obtained via renaming. Renaming of what? Is this sentence important, or just a historical curiosity? That is, consider removing it - an implementer won't care. https://codereview.chromium.org/2690553002/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:49: This extension to the grammar gives rise to an **ambiguity** where the same tokens 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 the 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 argument which is an invocation of a generic function (`a<b,c>(d)`). The ambiguity is resolved in **favor** of the **generic function invocation** whenever the `primary` is followed by a balanced pair of angle brackets where the next token after the final `>` is a left parenthesis (in short, we are "looking at `< .. >(`"). looking at `e< ... >(` That is: add `e` before the `<`. I think it's clearer that way. (Break into lines!) https://codereview.chromium.org/2690553002/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:57: It should be noted that parsing techniques like recursive descent seem to conflict 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. "diet parsing" isn't a concept that the reader can be assumed to be familiar with (if it's a known concept at all, and not just a name someone invented locally). Maybe "if using a multi-pass parsing strategy that matches brace-pairs before parsing expressions - perhaps during tokenization." https://codereview.chromium.org/2690553002/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:63: For instance, constructors, setters, getters, and operators cannot be declared as generic. Actual type arguments cannot be passed at invocation sites for setters, 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 lifted in a full-fledged version of this extension. I wouldn't say that getters, setters or operators are potential extensions. Getters and setters are actually syntactically viable (x.foo<int> = 42), but they only makes sense if you can have generic fields too, which are not in the cards. Operators could meaningfully be generic, but the syntax is against it. So, only constructors is really a *potential* extension, not just a speculative one. Drop the "it's possible" sentence. Maybe it will, maybe it won't, we'll know when we do it, but until then it's idle speculation that someone might mistake for an actual plan. https://codereview.chromium.org/2690553002/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md: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. There is no guarantee that a final version of generic methods will support lower bounds, and it is not required that an implementation must support them. Then don't include it. Implementations should implement this informal specification. Don't include anything in it that they don't need to implement anyway, that's just a recipe for confusion. https://codereview.chromium.org/2690553002/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:67: In general, the support for generic methods offered by this feature is not intended to be complete, it is **intended** to allow **for** **experiments** such that 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 required. This is confusing. That's not what you said above as the motivation for this document. Is it an experiment or a specification of the actual generic method syntax? Should the VM implement this or something else? Where is the specification for what it should implement? (and why is this not it?) So, just the facts. https://codereview.chromium.org/2690553002/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:71: In order to be useful, the support for generic methods and functions must be sufficiently complete and consistent to **avoid spurious** diagnostic **messages**. In particular, even though no regular type checks take place at usages of routine type parameters in the body where they are in scope, those type parameters should be resolved. If they had been ignored then any usage of a routine type parameter `X` would give rise to a `Cannot resolve type X` error message, or the usage might resolve to other declarations of `X` in enclosing scopes such as a class type parameter, both of which is unacceptable. "In particular, even though no regular type checks take place at usages of routine type parameters in the body where they are in scope" Can't parse this, or guess what it's trying to say. Try rewriting. (What is a "Regular type check"? Why would the usage of a type parameter introduce a type check? You can't use a variable where it's not in scope - it's not that variable then, just similar syntax). Are you trying to say that: - Formal type parameters do introduce a type variable in scope, - but those type variable won't be bound to the actual type arguments. ? (and as commentary: if they didn't introduce a variable in the lexical scope, it wouldn't be possible to use the variable without errors, and we want that to be possible too). https://codereview.chromium.org/2690553002/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md: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 expression on the form `e is X` or `e is! X`. I think we should also disallow `on T { ... }` in catch statements. It's basically the same as an is-test, and it throws for a malformed type. https://codereview.chromium.org/2690553002/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:85: If a routine invocation specifies actual type arguments, e.g., `int` in the **invocation** `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. This is Dart 1 only, so don't mention inference. If you have inference, then it's strong mode, and then the parameters *are* passed and usable. https://codereview.chromium.org/2690553002/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:87: When the body of a generic **routine** is **executed**, usages of the formal type parameters will either result in a run-time error, or they will yield the value yielded by an evaluation of `dynamic`, following the treatment of malformed types in Dart. There are the following cases: Again, I prefer not to use "evaluate" and "value" about types (except as type literals, which are expressions). Just say that the type parameter *refers* to the dynamic type, or that it is (as if it is) replaced by the type `dynamic`. https://codereview.chromium.org/2690553002/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:91: Note that the forms containing `is` are compile-time errors, which means that implementations are free to reject the program, or to compile the program with a different 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 that blindly returns `true` every time (or `false` every time) may silently introduce a bug into an otherwise correct program, so the expression must fail if it is ever evaluated. You didn't say above that it's a compile-time error, so why "note" it here? And it probably isn't a compile-time error. Dart2js is not free to compile a compile-time error into a runtime error. Compile-time errors can happen at runtime, if you interleave parsing and execution like the VM does, but even then, the entire function containing the compile-time error should be rejected. Only failing if the *is* is executed is not OK for a compile-time error. So, is it a compile-time error, or is it a static warning/runtime error? Pick one. https://codereview.chromium.org/2690553002/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:93: When `X` is a routine type parameter which is passed as a type argument to a generic 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 malformed type, which in this case means that it is treated like `dynamic`. Be more explicit (meaning, actually write "G<X>"): If `X` is a routine type parameter and `G<X>` is a generic class instantiation (and similarly if X is one of multiple type arguments), then `e is G<X>` and `e is! G<X>` and the type literal `G<X>` proceeds as if `X` is malformed type, which ... https://codereview.chromium.org/2690553002/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:95: This may be surprising, so let us consider a couple of examples: When `X` is a routine 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. Drop the "no matter ..." part. We already established that arguments are ignored. https://codereview.chromium.org/2690553002/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md: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 constructor will be the value yielded by an evaluation of `dynamic`. Explicit example: , e.g., `new Map<int, X>()` or `<X>[]`, the value yielded by an evaluation of `dynamic` -> the dynamic type. https://codereview.chromium.org/2690553002/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:99: In **checked mode**, when `X` is a routine type parameter which is used as a type 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 parameter whose type annotation is `X`, and if the type annotation is a generic type `G` that contains `X`, checked mode checks will succeed or fail as if `X` had been the type `dynamic`. Feels like it's repeating itself here. G<X> is G<dynamic> in every way. There is no advantage in splitting it into cases when all the cases are the same. A raw X acts like a malformed type in most cases: - static warning/runtime error when used directly in `is` checks, `on` checks, and as a type literal, dynamic everywhere else. When used as a type parameter again (<T>[], new Foo<T>(), x.bar<T>()), it's always treated as dynamic. This differs from a malformed type only in `as` checks (right?).
https://codereview.chromium.org/2690553002/diff/40001/docs/language/dartLangS... File docs/language/dartLangSpec.tex (right): https://codereview.chromium.org/2690553002/diff/40001/docs/language/dartLangS... docs/language/dartLangSpec.tex:620: Functions can be introduced by declarations of functions (\ref{functionDeclarations}), methods (\ref{instanceMethods}, \ref{staticMethods}), getters (\ref{getters}), setters (\ref{setters}), and constructors (\ref{constructors}); and they can be introduced by function literals (\ref{functionExpressions}). "Functions can be introduce by declarations of functions" sounds odd and circular. I think it would be better as: "Functions can be introduced by function declarations (...), method decalarations (...), ..." It makes it clear that a "function declaration" is not a function itself, it's a "declaration", whereas "declarations of functions" separates the two. To make it slightly shorter, maybe: "Functions can be introduced by function declarations (...), function expressions (...), and by declarations of setters (...), ... , and constructors (...)". https://codereview.chromium.org/2690553002/diff/40001/docs/language/dartLangS... docs/language/dartLangSpec.tex:623: All function declarations include a signature, and some include a body. Here "function declarations" looks like it only refers to \ref{functionDeclarations} - and the suggested rewrite above just makes it worse. Maybe "All declarations of functions ... " (because here it talks about the declarations above that introduced the functions). https://codereview.chromium.org/2690553002/diff/40001/docs/language/dartLangS... docs/language/dartLangSpec.tex:625: The formal parameter part optionally specifies the formal type parameter list of the function, and it specifies its formal parameter list. "formal type parameter list" - that wording rubs me the wrong way. Maybe because it emphasizes the "list", which is a grammatical concept. Maybe just "its formal type parameters". Similarly for its formal parameters, which are not a list (unless optional parameters are considered a single element?) https://codereview.chromium.org/2690553002/diff/40001/docs/language/dartLangS... docs/language/dartLangSpec.tex:712: The current scope for the function's signature is the scope that encloses the formal parameter scope. The type parameter list is part of the signature, so that sentence doesn't seem correct (or is it recursively the current scope for itself?) https://codereview.chromium.org/2690553002/diff/40001/docs/language/dartLangS... docs/language/dartLangSpec.tex:2482: \item If $id$ occurs as \code{$e$ \IS{} $id$} or \code{$e$ \IS{}! $id$} for some expression $e$, or if $id$ occurs as a type literal, a compile-time error occurs. Should we include `on` clauses? https://codereview.chromium.org/2690553002/diff/40001/docs/language/dartLangS... docs/language/dartLangSpec.tex:2482: \item If $id$ occurs as \code{$e$ \IS{} $id$} or \code{$e$ \IS{}! $id$} for some expression $e$, or if $id$ occurs as a type literal, a compile-time error occurs. I'm actually not sure what a "type literal" really is. I can't find a definition in the spec (only references to it), but I'm guessing that it only applies to class names, not type variables. (That's probably contrary to something I've said earlier, but the more you read, the less you know :) Or maybe it is a type literal, and the distinction is between "type literal" and "constant type literal". Not sure. So, just to be safe, "as a type literal" -> "as an identifier expression". https://codereview.chromium.org/2690553002/diff/40001/docs/language/dartLangS... docs/language/dartLangSpec.tex:2490: This is also the case when they are used as type arguments, both in type annotations and in instance creation expressions. and in things like `x is Foo<T>`, which is neither annotation nor instance creation - either just say that it's everywhere, expand the list to be exhaustive, or say that it's just examples. This is commentary anyway. https://codereview.chromium.org/2690553002/diff/40001/docs/language/dartLangS... docs/language/dartLangSpec.tex:2499: Finally, it could silently introduce bugs into correct programs if we had chosen to let type literals evaluate to a fixed value, e.g., \DYNAMIC{}. The "finally" here sounds like it's a separate thing, but isn't it just the conclusion to the previous sentence. That is, maybe change "Finally" to "So"? https://codereview.chromium.org/2690553002/diff/40001/docs/language/dartLangS... docs/language/dartLangSpec.tex:3776: The actual type arguments passed to $f$, if any, have no effect. That sounds like you actually pass something. As I read the previous text, you actually ignore the type arguments and don't pass anything. So maybe: "passed to $f$" -> "in the invocation of $f$" (it's the syntactic occurrence we refer to, it has no semantics). https://codereview.chromium.org/2690553002/diff/40001/docs/language/dartLangS... docs/language/dartLangSpec.tex:3886: In this situation, the expression is always parsed as a generic function invocation. The ambiguity is not just in argument lists, it's everywhere you have comma-separated expressions. That also includes list literals and for-loop increments. var x = [a<b,c>(d)] for (;; a<b,c(d)); // Admittedly useless, but we should be consistent. https://codereview.chromium.org/2690553002/diff/40001/docs/language/dartLangS... docs/language/dartLangSpec.tex:3892: % argumentPart in the grammar. We should specify which programs are valid, and what they mean. If we have a disambiguation rule that is different from the paragraph above (which is clear and precise - and doesn't match this disabmiguation rule), then we need to write it *instead* of the previous paragraph. We can take the easy way out (which is what we did above) and say that whenever there is a parsing ambiguity where one option is a generic method invocation, we resolve it to the generic function invocation. The problem is that it doesn't help parsing very much - it has to actually parse both versions and only reduce when you have successfully parsed a function invocation. Something like foo<bar,baz.qux>(42) is a function invocation but foo<bar,baz.qux.fip>(42) is not (baz.qux.fib can't be a "type"). On the other hand, specifying the rule that: e1<e2,e3>( where < and > are matching, always implies a method invocation ... requires us to *specify* what it means for < and > to be matching, in such a way that we don't miss foo<int,List<int>>(42) which ends in the token '>>' not '>'. I would prefer not to define this (what if e2 contains List<int>, what if e2 contains a << operator, what if e2 contains a + operator, what about f(x<<y,z>>(w)), what about f(x < a << 2, y >> 3 > (4))?) It's also annoying that foo((a)<(b), (c)>(d)) is taken as an (invalid) function invocation when it so obviously isn't. That expression only matches one production, and we want to somehow disallow it. The "if there is an ambiguity" rule can't do that since there is no ambiguity. What do the current implementations do? I think we should stay with the "if there is ambiguity, prefer invocation" and let parsers use the "matching < and > followed by (" as a hint to which parsing to try first, with an option to backtrack and try again. https://codereview.chromium.org/2690553002/diff/40001/docs/language/dartLangS... docs/language/dartLangSpec.tex:3895: An example is \code{f(a<B, C>($d$))}, which may be an invocation of \code{f} passing two actual arguments of type \code{bool}, or an invocation of \code{f} passing the result returned by an invocation of the generic function \code{a}. Nitpick: You don't know the type is `bool`, operator< is user definable. |