diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d4be953515..2680c5519fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -134,8 +134,7 @@ greater (if using a beta preview release, an sdk constraint of `>=2.15.0-0` must be used). -- **[Explicit generic method instantiations][Explicit instantiation]**: **BETA - PREVIEW** +- **[Explicit generic method instantiations][Explicit instantiation]**: **BETA PREVIEW** Previous Dart versions allowed generic methods to be implicitly specialized (or "instantiated") to non-generic versions when assigned to a location with a @@ -197,6 +196,65 @@ [Explicit instantiation]: https://github.com/dart-lang/language/blob/master/accepted/future-releases/constructor-tearoffs/feature-specification.md#explicitly-instantiated-classes-and-functions +- **[Generic instantiation of function objects][Object instantiation]**: **BETA PREVIEW** + + Generic function instantiation was previously restricted to function + declarations. For example, as soon as a function had already been torn + off, it could not be instantiated: + + ```dart + X id(X x) => x; + + void main() { + var fo = id; // Tear off `id`, creating a function object. + var c1 = fo; // Compile-time error: can't instantiate `fo`. + int Function(int) c2 = fo; // Same compile-time error. + // Constants are treated the same. + } + ``` + + New in Dart 2.15, this restriction has been lifted, and it is now possible + to obtain a generic instantiation of an existing function object, both + explicitly and implicitly (again, this works the same for non-constants): + + ```dart + X id(X x) => x; + X other(X x) => throw x; + + void main() { + const fo = id; // Tear off `id`, creating a function object. + + // Generic function instantiation on `fo` is no longer an error. + const c1 = fo; // OK. + const int Function(int) c2 = fo; // OK. + + // This also generalizes function instantiation because we can, + // e.g., use non-trivial expressions and go via a constructor. + const c3 = A(true); // OK. + } + + class A { + final int Function(int) x; + // `(...)` is now allowed, also in a `const` constructor. + const A(bool b): x = (b ? id : other); + } + ``` + + The new generic instantation of objects feature is currently in **BETA + PREVIEW**. The feature is enabled in beta releases only, and is still subject + to breaking changes. It is not fully supported by all tools and there may be + known issues with the tool support. Feedback on the feature is welcome, but it + is not recommended that production code use the feature until it has been + released in a stable version. + + Generic instantiation of objects is only available as part of the 2.15 + [language version](https://dart.dev/guides/language/evolution). To use this + feature, you must set the lower bound on the sdk constraint for your package + to 2.15 or greater (if using a beta preview release, an sdk constraint of + `>=2.15.0-0` must be used). + +[Object instantiation]: https://github.com/dart-lang/language/pull/1812 + - Annotations on type parameters of classes can no longer refer to class members without a prefix. For example, this used to be permitted: @@ -226,7 +284,7 @@ if (!iIsNull) { print(i + 1); // OK, because `i` is known to be non-null } - }<> + } ``` Previously, the above program had a compile-time error because type promotion @@ -266,6 +324,73 @@ instance variable, and it brings the implementation behavior in line with the specification in all other cases. +- **Function object canonicalization and equality** + + Several corner cases in the area of function object canonicalization and + function object equality have been updated, such that all tools behave in + the same way, and the behavior matches the specification. + + In particular, function objects are now equal when they are obtained by + generic instantiation from the same function with the same actual type + arguments, even when that type argument is not known at compile time. + When the expressions are constant then said function objects are identical. + Constant expressions are treated as such even when they do not occur in a + constant context (e.g., `var f = top;`). Here are the main cases where the + behavior has been adjusted: + + ```dart + void top() {} + X? genericTop() => null; + + class A { + static void stat() {} + static X? genericStat() => null; + void inst() {} + X? genericInst() => null; + } + + const int? Function() cIntTop1 = genericTop; + const int? Function() cIntStat1 = A.genericStat; + + int? Function() vIntTop1 = genericTop; + int? Function() vIntStat1 = A.genericStat; + int? Function() vIntTop2 = genericTop; + int? Function() vIntStat2 = A.genericStat; + + String? Function() vStringTop = genericTop; + String? Function() vStringStat = A.genericStat; + + void main() { + var a = A(); + int? Function() vIntInst1 = a.genericInst; + int? Function() vIntInst2 = a.genericInst; + + // All true (used to be false). + identical(cIntTop1, vIntTop2); + identical(cIntStat1, vIntStat2); + identical(vIntTop1, vIntTop2); + identical(vIntStat1, vIntStat2); + vIntInst1 == vIntInst2; + + () { + X? Function() vXTop1 = genericTop; + X? Function() vXStat1 = A.genericStat; + X? Function() vXTop2 = genericTop; + X? Function() vXStat2 = A.genericStat; + X? Function() vXInst1 = a.genericInst; + X? Function() vXInst2 = a.genericInst; + + // All true (used to be false). + vXTop1 == vXTop2; + vXStat1 == vXStat2; + vXInst1 == vXInst2; + vXTop1 == vIntTop1; + vXStat1 == vIntStat1; + vXInst1 == vIntInst2; + }(); + } + ``` + ### Core libraries #### `dart:async`