Add extension types to the CHANGELOG.
Change-Id: Id3bf8bd8b0a366ce58eb9070f0e2fa15d55bf07d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/350002
Reviewed-by: Alexander Thomas <athom@google.com>
Reviewed-by: Leaf Petersen <leafp@google.com>
Commit-Queue: Alexander Thomas <athom@google.com>
Reviewed-by: Erik Ernst <eernst@google.com>
Auto-Submit: Bob Nystrom <rnystrom@google.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 81d22ef..3db516f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -66,6 +66,97 @@
### Language
+Dart 3.3 adds [extension types] to the language. To use them, set your
+package's [SDK constraint][language version] lower bound to 3.3 or greater
+(`sdk: '^3.3.0'`).
+
+#### Extension types
+
+[extension types]: https://github.com/dart-lang/language/issues/2727
+
+An _extension type_ wraps an existing type with a different, static-only
+interface. It works in a way which is in many ways similar to a class that
+contains a single final instance variable holding the wrapped object, but
+without the space and time overhead of an actual wrapper object.
+
+Extension types are introduced by _extension type declarations_. Each
+such declaration declares a new named type (not just a new name for the
+same type). It declares a _representation variable_ whose type is the
+_representation type_. The effect of using an extension type is that the
+_representation_ (that is, the value of the representation variable) has
+the members declared by the extension type rather than the members declared
+by its "own" type (the representation type). Example:
+
+```dart
+extension type Meters(int value) {
+ String get label => '${value}m';
+ Meters operator +(Meters other) => Meters(value + other.value);
+}
+
+void main() {
+ var m = Meters(42); // Has type `Meters`.
+ var m2 = m + m; // OK, type `Meters`.
+ // int i = m; // Compile-time error, wrong type.
+ // m.isEven; // Compile-time error, no such member.
+ assert(identical(m, m.value)); // Succeeds.
+}
+```
+
+The declaration `Meters` is an extension type that has representation type
+`int`. It introduces an implicit constructor `Meters(int value);` and a
+getter `int get value`. `m` and `m.value` is the very same object, but `m`
+has type `Meters` and `m.value` has type `int`. The point is that `m`
+has the members of `Meters` and `m.value` has the members of `int`.
+
+Extension types are entirely static, they do not exist at run time. If `o`
+is the value of an expression whose static type is an extension type `E`
+with representation type `R`, then `o` is just a normal object whose
+run-time type is a subtype of `R`, exactly like the value of an expression
+of type `R`. Also the run-time value of `E` is `R` (for example, `E == R`
+is true). In short: At run time, an extension type is erased to the
+corresponding representation type.
+
+A method call on an expression of an extension type is resolved at
+compile-time, based on the static type of the receiver, similar to how
+extension method calls work. There is no virtual or dynamic dispatch. This,
+combined with no memory overhead, means that extension types are zero-cost
+wrappers around their representation value.
+
+While there is thus no performance cost to using extension types, there is
+a safety cost. Since extension types are erased at compile time, run-time
+type tests on values that are statically typed as an extension type will
+check the type of the representation object instead, and if the type check
+looks like it tests for an extension type, like `is Meters`, it actually
+checks for the representation type, that is, it works exactly like `is int`
+at run time. Moreover, as mentioned above, if an extension type is used as
+a type argument to a generic class or function, the type variable will be
+bound to the representation type at run time. For example:
+
+```dart
+void main() {
+ var meters = Meters(3);
+
+ // At run time, `Meters` is just `int`.
+ print(meters is int); // Prints "true".
+ print(<Meters>[] is List<int>); // Prints "true".
+
+ // An explicit cast is allowed and succeeds as well:
+ List<Meters> meterList = <int>[1, 2, 3] as List<Meters>;
+ print(meterList[1].label); // Prints "2m".
+}
+```
+
+Extension types are useful when you are willing to sacrifice some run-time
+encapsulation in order to avoid the overhead of wrapping values in
+instances of wrapper classes, but still want to provide a different
+interface than the wrapped object. An example of that is interop, where you
+may have data that are not Dart objects to begin with (for example, raw
+JavaScript objects when using JavaScript interop), and you may have large
+collections of objects where it's not efficient to allocate an extra object
+for each element.
+
+#### Other changes
+
- **Breaking Change** [#54056][]: The rules for private field promotion have
been changed so that an abstract getter is considered promotable if there are
no conflicting declarations. There are no conflicting declarations if