From 29a4f66f71df464dd6be11f65ba22b8b5e81d113 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 17 Sep 2021 17:29:55 +0200 Subject: [PATCH 1/7] Add support for a type variable as a potentially constant expression and type expression --- .../feature-specification.md | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/accepted/future-releases/constructor-tearoffs/feature-specification.md b/accepted/future-releases/constructor-tearoffs/feature-specification.md index 789bd3d559..9ba2f936ec 100644 --- a/accepted/future-releases/constructor-tearoffs/feature-specification.md +++ b/accepted/future-releases/constructor-tearoffs/feature-specification.md @@ -55,9 +55,9 @@ If *C* denotes a class declaration (it's an identifier or qualified identifier w just as you can currently invoke the constructor as *C*.*name*(*args*), or *C*\<*typeArgs*>.*name*(*args*). -Expressions of the form *C*\<*typeArgs*>.*name* are potentially compile-time constant expressions and are compile-time constants if the type arguments are constant types (and *C*.*name* actually denotes a constructor). +_These expressions can be constant, as specified in the section about constant expressions._ -_The former syntax, without type arguments, is currently allowed by the language grammar, but is rejected by the static semantics as not being a valid expression when denoting a constructor. The latter syntax is not currently grammatically an_ expression_. Both can occur as part of a constructor invocation, but cannot be expressions by themselves because they have no values. We introduce a static and dynamic expression semantics for such a *named constructor tear-off expression*, which makes them valid expressions._ +_The syntax without type arguments, is currently allowed by the language grammar, but is rejected by the static semantics as not being a valid expression when denoting a constructor. The syntax with type arguments is not currently grammatically an expression. Both can occur as part of a constructor invocation, but cannot be expressions by themselves because they have no values. We introduce a static and dynamic expression semantics for such a *named constructor tear-off expression*, which makes them valid expressions._ A named constructor tear-off expression of one of the forms above evaluates to a function value which could be created by tearing off a *corresponding constructor function*, which would be a static function defined on the class denoted by *C*, with a fresh name here represented by adding `$tearoff`: @@ -519,20 +519,23 @@ The grammar changes necessary for these changes are provided separately (as [cha We add the following to the set of expressions that are potentially constant and constant: -If `e` is a potentially constant expression, `T1..Tk` is derived from ``, and `e` is derived from ` *`, then `e` is a potentially constant expression. -If moreover `e` is a constant expression whose static type is a function type `F`, or `e` is a type literal, and `T1..Tk` is a list of constant type expressions, then `e` is a constant expression. +If `e` is a potentially constant expression, T1..Tk is derived from \, and e\1..Tk> is derived from \ \*, then e\1..Tk> is a potentially constant expression. +If moreover `e` is a constant expression whose static type is a function type `F`, or `e` is a type literal, and T1..Tk is a list of constant type expressions, then e\1..Tk> is a constant expression. -*It follows that `F` is a generic function type taking `k` type arguments, and `T1..Tk` satisfy the bounds of `F`, and similarly for the type literal, because otherwise the program would have a compile-time error.* +*It follows that `F` is a generic function type taking `k` type arguments, and T1..Tk satisfy the bounds of `F`, and similarly for the type literal, because otherwise the program would have a compile-time error.* -The following cases are specified elsewhere in this document: +Assume that *C*.*name* denotes a constructor, and T1..Tk is derived from \, for some k >= 0. +*C*\1..Tk>.*name* is then a potentially constant expression if Tj is a potentially constant type expression for each j. +It is further a constant expression if Tj is a constant type expression for each j. -*Section 'Named constructor tearoffs': -This section says that expressions of the form *C*\<*typeArgs*>.*name* can be potentially constant and constant expressions. -Also, the constantness of named constructor tearoffs follows the constantness of the tearoffs of the corresponding constructor functions.* +_In particular, *C*.*name*, which is the case where k == 0, is always constant when *C* is a non-generic class, and it may or may not be constant if it is generic and the type arguments are inferred._ -*Section 'Tearing off constructors from type aliases': -This section contains several rules about constant expressions: About non-generic type aliases; about generic type aliases that are applied to some actual type arguments; about generic type aliases that are 'proper renames' and that do not receive any actual type arguments; and about generic type aliases that are not 'proper renames' and do not receive any actual type arguments. -In general, their constantness follows the constantness of specific corresponding constructor functions.* +If `T` denotes a type variable then `T` is a potentially constant type expression, and a potentially constant expression. + +_This is just a simpler way to write something which is already supported: If we wish to use a type variable as an expression in a constant constructor initializer list, we can define typedef F\ = X; and express the same thing as F\._ + +*Section 'Tearing off constructors from type aliases' specifies several additional cases, all saying that a tearoff from a type alias is constant if and only if the corresponding constructor function tearoff is constant. +This is specified about non-generic type aliases; about generic type aliases that are applied to some actual type arguments; about generic type aliases that are 'proper renames' and that do not receive any actual type arguments; and about generic type aliases that are not 'proper renames' and do not receive any actual type arguments.* ## Summary @@ -730,4 +733,5 @@ In this case, most of the parameters are *unnecessary*, and a tear-off expressio * 2.12: Mention abstract classes. * 2.13: Add `is` and `as` disambiguation tokens. * 2.14: Remove many disambiguation tokens. Allow instantiating function *objects* and *callable objects*. Mention forwarding constructors from mixin applications. -* 2.15: Add section about constants and specify new rules about potentially constant and constant expressions of the form `e`. +* 2.15: Add section about constants and specify new rules about potentially constant and constant expressions of the form e\1..Tk>. +* 2.16: Add one more kind of potential constant, type parameters, From 7e2d6d4d7b7a7010ca7e43042fccb7079ea00a0f Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 17 Sep 2021 17:51:10 +0200 Subject: [PATCH 2/7] Typo --- .../constructor-tearoffs/feature-specification.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accepted/future-releases/constructor-tearoffs/feature-specification.md b/accepted/future-releases/constructor-tearoffs/feature-specification.md index 9ba2f936ec..c362987c22 100644 --- a/accepted/future-releases/constructor-tearoffs/feature-specification.md +++ b/accepted/future-releases/constructor-tearoffs/feature-specification.md @@ -519,7 +519,7 @@ The grammar changes necessary for these changes are provided separately (as [cha We add the following to the set of expressions that are potentially constant and constant: -If `e` is a potentially constant expression, T1..Tk is derived from \, and e\1..Tk> is derived from \ \*, then e\1..Tk> is a potentially constant expression. +If `e` is a potentially constant expression derived from \ \*, T1..Tk is derived from \, then e\1..Tk> is a potentially constant expression. If moreover `e` is a constant expression whose static type is a function type `F`, or `e` is a type literal, and T1..Tk is a list of constant type expressions, then e\1..Tk> is a constant expression. *It follows that `F` is a generic function type taking `k` type arguments, and T1..Tk satisfy the bounds of `F`, and similarly for the type literal, because otherwise the program would have a compile-time error.* From ff71e5c6124091c37279caf9acb190a7f3007a2d Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 17 Sep 2021 17:52:06 +0200 Subject: [PATCH 3/7] Typo --- .../constructor-tearoffs/feature-specification.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accepted/future-releases/constructor-tearoffs/feature-specification.md b/accepted/future-releases/constructor-tearoffs/feature-specification.md index c362987c22..8989c15d75 100644 --- a/accepted/future-releases/constructor-tearoffs/feature-specification.md +++ b/accepted/future-releases/constructor-tearoffs/feature-specification.md @@ -519,7 +519,7 @@ The grammar changes necessary for these changes are provided separately (as [cha We add the following to the set of expressions that are potentially constant and constant: -If `e` is a potentially constant expression derived from \ \*, T1..Tk is derived from \, then e\1..Tk> is a potentially constant expression. +If `e` is a potentially constant expression derived from \ \* and T1..Tk is derived from \, then e\1..Tk> is a potentially constant expression. If moreover `e` is a constant expression whose static type is a function type `F`, or `e` is a type literal, and T1..Tk is a list of constant type expressions, then e\1..Tk> is a constant expression. *It follows that `F` is a generic function type taking `k` type arguments, and T1..Tk satisfy the bounds of `F`, and similarly for the type literal, because otherwise the program would have a compile-time error.* From 369e35f0050b3c3b32465ec364d7e7ddb53042e3 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 17 Sep 2021 17:55:45 +0200 Subject: [PATCH 4/7] More small fixes --- .../constructor-tearoffs/feature-specification.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/accepted/future-releases/constructor-tearoffs/feature-specification.md b/accepted/future-releases/constructor-tearoffs/feature-specification.md index 8989c15d75..dca5bfcdd4 100644 --- a/accepted/future-releases/constructor-tearoffs/feature-specification.md +++ b/accepted/future-releases/constructor-tearoffs/feature-specification.md @@ -519,8 +519,8 @@ The grammar changes necessary for these changes are provided separately (as [cha We add the following to the set of expressions that are potentially constant and constant: -If `e` is a potentially constant expression derived from \ \* and T1..Tk is derived from \, then e\1..Tk> is a potentially constant expression. -If moreover `e` is a constant expression whose static type is a function type `F`, or `e` is a type literal, and T1..Tk is a list of constant type expressions, then e\1..Tk> is a constant expression. +If `e` is a potentially constant expression derived from \ \* and T1..Tk derived from \ is a list of potentially constant type expressions, then e\1..Tk> is a potentially constant expression. +If moreover `e` is a constant expression whose static type is a function type `F` or `e` is a type literal, and T1..Tk is a list of constant type expressions, then e\1..Tk> is a constant expression. *It follows that `F` is a generic function type taking `k` type arguments, and T1..Tk satisfy the bounds of `F`, and similarly for the type literal, because otherwise the program would have a compile-time error.* From 8610bc1df7f2588414a6b596c63a12b3135b61ca Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Fri, 17 Sep 2021 17:56:46 +0200 Subject: [PATCH 5/7] Typo --- .../constructor-tearoffs/feature-specification.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accepted/future-releases/constructor-tearoffs/feature-specification.md b/accepted/future-releases/constructor-tearoffs/feature-specification.md index dca5bfcdd4..025b756fbf 100644 --- a/accepted/future-releases/constructor-tearoffs/feature-specification.md +++ b/accepted/future-releases/constructor-tearoffs/feature-specification.md @@ -517,7 +517,7 @@ The grammar changes necessary for these changes are provided separately (as [cha ### Constant expressions -We add the following to the set of expressions that are potentially constant and constant: +We add the following to the set of expressions that are potentially constant or constant: If `e` is a potentially constant expression derived from \ \* and T1..Tk derived from \ is a list of potentially constant type expressions, then e\1..Tk> is a potentially constant expression. If moreover `e` is a constant expression whose static type is a function type `F` or `e` is a type literal, and T1..Tk is a list of constant type expressions, then e\1..Tk> is a constant expression. From cf165d034834bce84e797e3bd0303a4ba53f59e3 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Thu, 23 Sep 2021 15:28:53 +0200 Subject: [PATCH 6/7] Review response --- .../feature-specification.md | 96 +++++++++---------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/accepted/future-releases/constructor-tearoffs/feature-specification.md b/accepted/future-releases/constructor-tearoffs/feature-specification.md index 025b756fbf..22640f7b5e 100644 --- a/accepted/future-releases/constructor-tearoffs/feature-specification.md +++ b/accepted/future-releases/constructor-tearoffs/feature-specification.md @@ -16,7 +16,7 @@ The goal is that you can always tear off a constructor, then invoke the torn off var v1 = C.name(args); var v2 = (C.name)(args); -// and +// and var v3 = C.name(args); var v4 = (C.name)(args); @@ -25,7 +25,7 @@ var v5 = (C.name)(args); should always give equivalent values for `v1` and `v2`, and for `v3`, `v4` and `v5`. -We also want a consistent and useful *identity* and *equality* of the torn off functions, with the tear-off expression being a constant expression where possible. It should match what we already do for static function tear-off where that makes sense. +We also want a consistent and useful *identity* and *equality* of the torn off functions, with the tear-off expression being a constant expression where possible. It should match what we already do for static function tear-off where that makes sense. ## Proposal @@ -57,7 +57,7 @@ just as you can currently invoke the constructor as *C*.*name*(*args*)\<*typeParams*\> and \<*typeArgs*\> are omitted. Otherwise \<*typeParams*\> are exactly the same type parameters as those of the class declaration of *C* (including bounds), and \<*typeArgs*> applies those type parameter variables directly as type arguments to *C*. -Similarly, *params* is *almost* exactly the same parameter list as the constructor *C*.*name*, with the one exception that *initializing formals* are represented by normal parameters with the same name and type. All remaining properties of the parameters are the same as for the corresponding constructor parameter, including any default values, and *args* is an argument list passing those parameters to `C.name` directly as they are received. +Similarly, *params* is *almost* exactly the same parameter list as the constructor *C*.*name*, with the one exception that *initializing formals* are represented by normal parameters with the same name and type. All remaining properties of the parameters are the same as for the corresponding constructor parameter, including any default values, and *args* is an argument list passing those parameters to `C.name` directly as they are received. For example, `Uri.http` evaluates to an expression which could have been created by tearing off a corresponding static function declaration: ```dart -static Uri http$tearoff(String authority, String unencodedPath, [Map? queryParameters]) => +static Uri http$tearoff(String authority, String unencodedPath, [Map? queryParameters]) => Uri.http(authority, unencodedPath, queryParameters); ``` @@ -118,11 +118,11 @@ where *args* passes the parameters *params* directly to as arguments to *C.name* For the example aliases above, the constructor functions corresponding to `List.filled` would be: ```dart -List IntList$filled$tearoff(int length, int value) => +List IntList$filled$tearoff(int length, int value) => List.filled(length, value); -List NumList$filled$tearoff(int length, T value) => +List NumList$filled$tearoff(int length, T value) => List.filled(length, value); -List MyList$filled$tearoff(int length, T value) => +List MyList$filled$tearoff(int length, T value) => List.filled(length, value); ``` @@ -157,7 +157,7 @@ Example: ```dart // Equivalent to `List.filled` or `List.filled$tearoff` -var makeIntList = NumList.filled; +var makeIntList = NumList.filled; // Same as `List.filled` after inference. List Function(int, double) makeDoubleList = NumList.filled; ``` @@ -177,7 +177,7 @@ In this example, `Ignore2.filled` is treated exactly like `List.fille **If the generic alias is not a proper rename for the class it aliases, then tearing off a constructor from the uninstantiated alias is equivalent to tearing off the corresponding constructor function of the alias, which is a generic function. The result always a generic function, and is always a compile-time constant.** -If the generic alias is *not* instantiated before the constructor is torn off, then the tear-off abstracts over the type parameters *of the alias*, and tearing off a constructor works equivalently to tearing off the corresponding constructor function *of the alias* (where the generics match the type alias, not the underlying class). This is where we use the corresponding constructor function of the alias—except when the alias is a *proper rename*, as defined below. +If the generic alias is *not* instantiated before the constructor is torn off, then the tear-off abstracts over the type parameters *of the alias*, and tearing off a constructor works equivalently to tearing off the corresponding constructor function *of the alias* (where the generics match the type alias, not the underlying class). This is where we use the corresponding constructor function of the alias—except when the alias is a *proper rename*, as defined below. Example: @@ -192,7 +192,7 @@ Example: ```dart typedef ListList = List>; // Corresponding factory function -List> ListList$filled$tearoff(int length, List value) => +List> ListList$filled$tearoff(int length, List value) => List>.filled(length, value); var f = ListList.filled; // Equivalent to `= ListList$filled$tearoff;` ``` @@ -230,17 +230,17 @@ Example : ```dart var f = MyList.filled; // Equivalent to `List.filled` or `List.filled$tearoff` -// Instantiated type aliases use the aliased type, +// Instantiated type aliases use the aliased type, // and are constant and canonicalized when the type is constant. print(identical(MyList.filled, NumList.filled)); // true print(identical(MyList.filled, List.filled)); // true -print(identical(NumList.filled, List.filled)); // true +print(identical(NumList.filled, List.filled)); // true // Non-instantiated type aliases have their own generic function. print(identical(MyList.filled, MyList.filled)); // true print(identical(NumList.filled, NumList.filled)); // true print(identical(MyList.filled, NumList.filled)); // false -print(identical(MyList.filled, List.filled)); // true (proper rename!) +print(identical(MyList.filled, List.filled)); // true (proper rename!) // Implicitly instantiated tear-off. List Function(int, int) myList = MyList.filled; @@ -262,14 +262,14 @@ class C { // Proper rename, different, but equivalent, bound. typedef A = C; void main() { - // Static type : C Function() + // Static type : C Function() // Runtime type: C Function() var cf = C.name; // Static type : C Function() // Runtime type: C Function() var af = A.name; var co = (cf as dynamic)(); - var ao = (af as dynamic)(); + var ao = (af as dynamic)(); // Dynamic instantiate to bounds uses actual bounds. print(co.runtimeType); // C print(ao.runtimeType); // C @@ -321,7 +321,7 @@ It's still not allowed to have two constructor declarations with the same name, ### Explicitly instantiated classes and functions -The above named constructor tear-off feature allows you to explicitly instantiate a constructor tear-off as `List.filled`. We do not have a similar ability to explicitly instantiate function tear-offs. Currently you have to provide a context type and rely on *implicit instantiation* if you want to tear off an instantiated version of a generic function. +The above named constructor tear-off feature allows you to explicitly instantiate a constructor tear-off as `List.filled`. We do not have a similar ability to explicitly instantiate function tear-offs. Currently you have to provide a context type and rely on *implicit instantiation* if you want to tear off an instantiated version of a generic function. We can also use type aliases to define instantiated interface types, but we cannot do the same thing in-line. @@ -357,14 +357,14 @@ For an expression of the form *e*\<*typeArgs*>, which is not follow * If *e* denotes a generic instance method (*e* has the form *r*.*name* and *r* has a static type for which *name* is a generic interface method), then *e*\<*typeArgs*> performs an explicitly instantiated method tear-off, which works just like the current implicitly instantiated method tear-off except that the types are provided instead of inferred. * If *e* has a static type which is a generic callable object type (a non-function type with a generic method named `call`), then *e*\<*typeArgs*> is equivalent to the instantiated method-tear off *e*\.call<*typeArgs*>. * Otherwise, if *e* has a static type which is a generic function type, then *e*\<*typeArgs*> is equivalent to the instantiated method-tear off *e*\.call<*typeArgs*>. -* Otherwise the expression is a compile-time error. +* Otherwise the expression is a compile-time error. * This includes *e* having the static type `dynamic` or `Function`. We do not support implicit or explicit instantiation of functions where we do not know the number and bounds of the type parameters at compile-time. * It also includes *e* denoting a constructor. _(We reserve this syntax for denoting instantiation of generic constructors, should the language add [generic constructors](https://github.com/dart-lang/language/issues/647) in the future. Instead just write (*C*.*name*)\<*typeArgs*\> or *C*\.*name*.)_ Cascades can contain explicitly instantiated tearoffs, because they can contain any selector and instantiation is now a selector, e.g., `receiver..foo()..instanceMethod..bar`. _Note that this example is allowed for consistency, but it will compute a value and discard it. Instantiation without immediate invocation is expected to be primarily used in places where the value of that instantiation will be stored for later use, and using it in a cascade is outside of that usage pattern. One example where it could be useful would be as a receiver for an extension method on function types, like `receiver..foo()..bar.apply(argList)`. The first selector of a cascade section must still be one of `..identifier` or `..[index]`, it cannot be `..` any more than it can be `..(argumentList)`._ ```dart -class A { +class A { List m(X x) => [x]; } @@ -373,7 +373,7 @@ extension FunctionApplier on Function { print(Function.apply(this, positionalArguments, const {})); } -void main() { +void main() { A() ..m.applyAndPrint([2]) ..m.applyAndPrint(['three']); @@ -382,7 +382,7 @@ void main() { The static type of the explicitly instantiated tear-offs are the same as if the type parameter had been inferred, but no longer depends on the context type. Missing type arguments in implicit instantiation expressions can now be considered "filled in" by type inference, as if they had been written explicitly, just as for other inferred type arguments. -The static type of the instantiated type literal is `Type`. This feature also satisfies issue [#123](https://github.com/dart-lang/language/issues/123). +The static type of the instantiated type literal is `Type`. This feature also satisfies issue [#123](https://github.com/dart-lang/language/issues/123). As mentioned above, we **do not allow** *dynamic* explicit instantiation. If an expression `e` has type `dynamic` (or `Never` or `Function` ), then e\ is a **compile-time error**. It's not possible to do implicit instantiation without knowing the member signature to some extent, and we also don't allow explicit instantiation. _(Possible alternative: Allow it, and handle it all at run-time, including any errors from having the wrong number or types of arguments, or just not existing at all. We won't do this for now.)_ @@ -391,7 +391,7 @@ We **now allow** both implicit and explicit instantiation of *callable objects* Previously, the following code was invalid: ```dart -class Id { +class Id { T call(T value) => value; } int Function(int) intId = Id(); @@ -413,7 +413,7 @@ Also, we allow explicitly instantiating a callable object: var intId = Id(); ``` -is also type-inferred to the same initialization. +is also type-inferred to the same initialization. **That is**, given an expression of the form *e*\<*typeArgs*>, if *e* has a static type which is a callable object, the expression is equivalent to *e*\.call<*typeArgs*>. Since no object with an interface type can otherwise support type-instantiation, this coercion turns an error into useful code, and allows a typed callable object to be consistently treated like a function object equivalent to its `call` method. @@ -429,7 +429,7 @@ f(x.a-d); // f((x.a)-d) or f((x.a < b), (c > -d])) The `x.a` can be an explicitly instantiated generic function tear-off or an explicitly instantiated type literal named using a prefix, which is new. While neither type objects nor functions declare `operator-` or `operator[]`, such could be added using extension methods. -We will disambiguate such situations *heuristically* based on the token following the `>` that matches the `<` we are ambiguous about. In the existing ambiguity we treat `(` as a sign that the `<` starts a generic invocation. We extend the number of tokens which, when following a potential type argument list, makes us choose to parse the previous tokens as that type argument list. +We will disambiguate such situations *heuristically* based on the token following the `>` that matches the `<` we are ambiguous about. In the existing ambiguity we treat `(` as a sign that the `<` starts a generic invocation. We extend the number of tokens which, when following a potential type argument list, makes us choose to parse the previous tokens as that type argument list. There is a number of tokens which very consistently *end* an expression, and we include all those: @@ -437,7 +437,7 @@ There is a number of tokens which very consistently *end* an expression, and we Then we include tokens which we *predict* will continue a generic instantiation: -> `(` `.` `==` `!=` +> `(` `.` `==` `!=` The first six are tokens which cannot possibly start an expression, and therefore cannot occur after a greater-than infix operator. The last four tokens can continue an expression, and of those only `(` can also start an expression, and we already decided how to disambiguate that). @@ -452,18 +452,18 @@ Grammatically, we restrict the productions for the less-than operator and the ty > | `>' > | `<=' > | `<' NEGATIVE_LOOKAHEAD( `>' ( | )) -> +> > ::= > '!' > | assignableSelector > | argumentPart > | typeArguments LOOKAHEAD( | ) -> +> > ::= `(' | `.' | `==' | `!=' > ::= `)' | `]' | `}' | `;' | `:' | `,' > ``` -That is, if a `<` occurs where it could potentially be either a type arguments list or a less than operator, absent any knowledge of the rest of the program, the parser can first try to parse it as a type argument list, then look at the following token, and if that token is one of the ones listed above, it *must* be a type argument list, because the relational less-than operator cannot possibly match due to its negative lookahead on exactly the thing that was just matched. +That is, if a `<` occurs where it could potentially be either a type arguments list or a less than operator, absent any knowledge of the rest of the program, the parser can first try to parse it as a type argument list, then look at the following token, and if that token is one of the ones listed above, it *must* be a type argument list, because the relational less-than operator cannot possibly match due to its negative lookahead on exactly the thing that was just matched. If the next token is not one of those characters, then the compiler can backtrack and try parsing as a relational less-than operator because the type arguments selector cannot possibly match. (If the compiler can somehow peek at the token following the matching `>` of a type arguments list before parsing the list, then it can potentially skip parsing as a type argument list entirely if the following token is not one of the chosen ones. It will have to try parsing as a type argument list for the cases where *that* part could fail to match, and thereby satisfy the negative lookahead of the relational operator, like `f(2 < 3, 4 > (5))` where the `2` and `3` are not valid type productions. @@ -498,9 +498,9 @@ A mixin application introduces *forwarding constructors* for accessible supercla Example: ```dart -class A { - A.named(); - A(); +class A { + A.named(); + A(); } mixin M {} class B = A with M; @@ -519,18 +519,18 @@ The grammar changes necessary for these changes are provided separately (as [cha We add the following to the set of expressions that are potentially constant or constant: -If `e` is a potentially constant expression derived from \ \* and T1..Tk derived from \ is a list of potentially constant type expressions, then e\1..Tk> is a potentially constant expression. -If moreover `e` is a constant expression whose static type is a function type `F` or `e` is a type literal, and T1..Tk is a list of constant type expressions, then e\1..Tk> is a constant expression. +If *e* is a potentially constant expression derived from \ \* and T1..Tk derived from \ is a list of potentially constant type expressions, then *e*\<*T**1*..*T**k*> is a potentially constant expression. +If moreover *e* is a constant expression whose static type is a function type *F* or *e* is a type literal, and *T**1*..*T**k* is a list of constant type expressions, then *e*\<*T**1*..*T**k*> is a constant expression. -*It follows that `F` is a generic function type taking `k` type arguments, and T1..Tk satisfy the bounds of `F`, and similarly for the type literal, because otherwise the program would have a compile-time error.* +*It follows that *F* is a generic function type taking *k* type arguments, and *T**1*..*T**k* satisfy the bounds of *F*, and similarly for the type literal, because otherwise the program would have a compile-time error.* -Assume that *C*.*name* denotes a constructor, and T1..Tk is derived from \, for some k >= 0. -*C*\1..Tk>.*name* is then a potentially constant expression if Tj is a potentially constant type expression for each j. -It is further a constant expression if Tj is a constant type expression for each j. +Assume that *C*.*name* denotes a constructor, and *T**1*..*T**k* is derived from \, for some *k* >= 0. +*C*\<*T**1*..*T**k*>.*name* is then a potentially constant expression if *T**j* is a potentially constant type expression for each *j*. +It is further a constant expression if *T**j* is a constant type expression for each *j*. -_In particular, *C*.*name*, which is the case where k == 0, is always constant when *C* is a non-generic class, and it may or may not be constant if it is generic and the type arguments are inferred._ +_In particular, *C*.*name*, which is the case where *k* == 0, is always constant when *C* is a non-generic class, and it may or may not be constant if it is generic and the type arguments are inferred._ -If `T` denotes a type variable then `T` is a potentially constant type expression, and a potentially constant expression. +If *T* denotes a type variable then *T* is a potentially constant type expression, and a potentially constant expression. _This is just a simpler way to write something which is already supported: If we wish to use a type variable as an expression in a constant constructor initializer list, we can define typedef F\ = X; and express the same thing as F\._ @@ -555,7 +555,7 @@ We allow `TypeName.new` and `TypeName.new` everywhere we allow a refer class C { final T x; const C.new(this.x); // Same as: `const C(this.x);` - C.other(T x) : this.new(x); // Same as: `: this(x)` + C.other(T x) : this.new(x); // Same as: `: this(x)` factory C.d(T x) = D.new; // same as: `= D;` } @@ -618,8 +618,8 @@ extension Ext on C { } class D extends C with M { void method() { - var f4 = super.inst; // works like (int $) => super.inst($) - var f4TypeName = super.inst.runtimeType.toString(); + var f4 = super.inst; // works like (int $) => super.inst($) + var f4TypeName = super.inst.runtimeType.toString(); } } void main() { @@ -627,7 +627,7 @@ void main() { var t1 = List; // Type object for `List`. var t2 = ListList; // Type object for `List>`. - // Instantiated function tear-offs. + // Instantiated function tear-offs. T local(T value) => value; const f1 = top; // int Function(int), works like (int $) => top($); const f2 = C.stat; // int Function(int), works like (int $) => C.stat($); @@ -635,7 +635,7 @@ void main() { var d = D(); var f4 = d.inst; // int Function(int), works like (int $) => c.inst($); var f5 = d.minst; // int Function(int), works like (int $) => c.minst($); - var f6 = d.einst; // int Function(int), works like (int $) => Ext(c).einst($); + var f6 = d.einst; // int Function(int), works like (int $) => Ext(c).einst($); var typeName = List.toString(); var functionTypeName = local.runtimeType.toString(); } @@ -663,7 +663,7 @@ That makes a type instantiation expression of the form *e*\<*typeArgs*> C(a: a, b: b, c: c, d: d, e: e, f: f, g: g, h: h, j: j, l: l, m: m); } -... +... void Function() f = C.new; // closure of new$tearoff ``` @@ -734,4 +734,4 @@ In this case, most of the parameters are *unnecessary*, and a tear-off expressio * 2.13: Add `is` and `as` disambiguation tokens. * 2.14: Remove many disambiguation tokens. Allow instantiating function *objects* and *callable objects*. Mention forwarding constructors from mixin applications. * 2.15: Add section about constants and specify new rules about potentially constant and constant expressions of the form e\1..Tk>. -* 2.16: Add one more kind of potential constant, type parameters, +* 2.16: Add one more kind of potential constant, type parameters, From b4ef1e7a769ab10bfbc77d0c52a61615d447e4e7 Mon Sep 17 00:00:00 2001 From: Erik Ernst Date: Thu, 23 Sep 2021 15:32:03 +0200 Subject: [PATCH 7/7] Typo --- .../constructor-tearoffs/feature-specification.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/accepted/future-releases/constructor-tearoffs/feature-specification.md b/accepted/future-releases/constructor-tearoffs/feature-specification.md index 22640f7b5e..8c5768c86e 100644 --- a/accepted/future-releases/constructor-tearoffs/feature-specification.md +++ b/accepted/future-releases/constructor-tearoffs/feature-specification.md @@ -519,7 +519,7 @@ The grammar changes necessary for these changes are provided separately (as [cha We add the following to the set of expressions that are potentially constant or constant: -If *e* is a potentially constant expression derived from \ \* and T1..Tk derived from \ is a list of potentially constant type expressions, then *e*\<*T**1*..*T**k*> is a potentially constant expression. +If *e* is a potentially constant expression derived from \ \* and *T**1*..*T**k* derived from \ is a list of potentially constant type expressions, then *e*\<*T**1*..*T**k*> is a potentially constant expression. If moreover *e* is a constant expression whose static type is a function type *F* or *e* is a type literal, and *T**1*..*T**k* is a list of constant type expressions, then *e*\<*T**1*..*T**k*> is a constant expression. *It follows that *F* is a generic function type taking *k* type arguments, and *T**1*..*T**k* satisfy the bounds of *F*, and similarly for the type literal, because otherwise the program would have a compile-time error.* @@ -734,4 +734,4 @@ In this case, most of the parameters are *unnecessary*, and a tear-off expressio * 2.13: Add `is` and `as` disambiguation tokens. * 2.14: Remove many disambiguation tokens. Allow instantiating function *objects* and *callable objects*. Mention forwarding constructors from mixin applications. * 2.15: Add section about constants and specify new rules about potentially constant and constant expressions of the form e\1..Tk>. -* 2.16: Add one more kind of potential constant, type parameters, +* 2.16: Add one more kind of potential constant, type parameters.