From 119eed217b7e7682b77dade31567ebdbee5091d2 Mon Sep 17 00:00:00 2001 From: Marya Belanger Date: Mon, 10 Feb 2025 14:08:57 -0800 Subject: [PATCH 01/16] add f-bound and inference sections --- .../test/language_tour/generics_test.dart | 14 ++++++++ examples/type_system/lib/strong_analysis.dart | 15 ++++++++ src/content/language/generics.md | 25 ++++++++++++++ src/content/language/type-system.md | 34 +++++++++++++++++++ 4 files changed, 88 insertions(+) diff --git a/examples/misc/test/language_tour/generics_test.dart b/examples/misc/test/language_tour/generics_test.dart index f27d2d3f2e..78fe18d436 100644 --- a/examples/misc/test/language_tour/generics_test.dart +++ b/examples/misc/test/language_tour/generics_test.dart @@ -65,3 +65,17 @@ void main() { } class View {} + +// #docregion f-bound +abstract class Comparable { + int compareTo(T o); +} + +class MyClass> implements Comparable> { + @override + int compareTo(MyClass other) { + // implementation... + return 0; + } +} +// #enddocregion f-bound \ No newline at end of file diff --git a/examples/type_system/lib/strong_analysis.dart b/examples/type_system/lib/strong_analysis.dart index 93b955bf7a..c0a0d964f1 100644 --- a/examples/type_system/lib/strong_analysis.dart +++ b/examples/type_system/lib/strong_analysis.dart @@ -156,3 +156,18 @@ void _miscDeclAnalyzedButNotTested() { // #enddocregion generic-type-assignment-implied-cast } } + + +// #docregion inference-using-bounds +class A> {} +class B extends A {} +class C extends B {} + +void f>(X x) {} + +void main() { + f(B()); // OK. + f(C()); // Inference fails, compile-time error. + f(C()); // OK. +} +// #enddocregion inference-using-bounds \ No newline at end of file diff --git a/src/content/language/generics.md b/src/content/language/generics.md index dd9945ffd2..a379a7f4d7 100644 --- a/src/content/language/generics.md +++ b/src/content/language/generics.md @@ -144,6 +144,7 @@ an object is a List, but you can't test whether it's a `List`. When implementing a generic type, you might want to limit the types that can be provided as arguments, so that the argument must be a subtype of a particular type. +This restriction is called a bound. You can do this using `extends`. A common use case is ensuring that a type is non-nullable @@ -193,6 +194,30 @@ Specifying any non-`SomeBaseClass` type results in an error: var foo = [!Foo!](); ``` +### Self-referential type parameter restrictions + +When using bounds to restrict parameter types, you can refer the bound +back to the type parameter itself. This creates a self-referential constraint, +or an F-bound. For example: + + +```dart +abstract class Comparable { + int compareTo(T o); +} + +class MyClass> implements Comparable> { + @override + int compareTo(MyClass other) { + // implementation... + return 0; + } +} +``` + +The F-bound `T extends Comparable` means `T` must be comparable to itself. +So, `MyClass` can only be used with types that can be compared to +other instances of the same type. ## Using generic methods diff --git a/src/content/language/type-system.md b/src/content/language/type-system.md index 511499cab7..00d94ba066 100644 --- a/src/content/language/type-system.md +++ b/src/content/language/type-system.md @@ -358,6 +358,40 @@ The return type of the closure is inferred as `int` using upward information. Dart uses this return type as upward information when inferring the `map()` method's type argument: ``. +#### Inference using bounds + +:::version-note +Inference using bounds requires a [language version][] of at least 3.7.0. +::: + +Dart's type inference algorithm generates constraints by combining +exisiting lower-bound constraints with the actual type bounds, +not just best-effort approximations. +This is especially important for [F-bounded][] types, +where inference using bounds correctly infers that, in the example below, +`C` is a subtype of `B`, +without needing you to explicitly specify the type `f(C())`: + + +```dart +class A> {} +class B extends A {} +class C extends B {} + +void f>(X x) {} + +void main() { + f(B()); // OK. + f(C()); // Inference fails, compile-time error. + f(C()); // OK. +} +``` + +For more information on the inference using bounds algorithm, +read the [desgin document][]. + +[F-bounded]: /language/generics/#self-referential-type-parameter-restrictions +[design document]: {{site.repo.dart.lang}}/blob/main/accepted/future-releases/3009-inference-using-bounds/design-document.md#motivating-example ## Substituting types From b04ce26db32865282870b8a8af032bb82ba516e2 Mon Sep 17 00:00:00 2001 From: Marya Belanger Date: Mon, 10 Feb 2025 14:16:37 -0800 Subject: [PATCH 02/16] fix link --- src/content/language/type-system.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/language/type-system.md b/src/content/language/type-system.md index 00d94ba066..3b3250338d 100644 --- a/src/content/language/type-system.md +++ b/src/content/language/type-system.md @@ -388,7 +388,7 @@ void main() { ``` For more information on the inference using bounds algorithm, -read the [desgin document][]. +read the [design document][]. [F-bounded]: /language/generics/#self-referential-type-parameter-restrictions [design document]: {{site.repo.dart.lang}}/blob/main/accepted/future-releases/3009-inference-using-bounds/design-document.md#motivating-example From fe6bbb0a34dc0461a96e9b851dd8828647b3a807 Mon Sep 17 00:00:00 2001 From: Marya Belanger Date: Mon, 10 Feb 2025 14:23:57 -0800 Subject: [PATCH 03/16] format code files --- .../misc/test/language_tour/generics_test.dart | 14 +++++++------- examples/type_system/lib/strong_analysis.dart | 5 +++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/examples/misc/test/language_tour/generics_test.dart b/examples/misc/test/language_tour/generics_test.dart index 78fe18d436..662d18433e 100644 --- a/examples/misc/test/language_tour/generics_test.dart +++ b/examples/misc/test/language_tour/generics_test.dart @@ -68,14 +68,14 @@ class View {} // #docregion f-bound abstract class Comparable { - int compareTo(T o); + int compareTo(T o); } class MyClass> implements Comparable> { - @override - int compareTo(MyClass other) { - // implementation... - return 0; - } + @override + int compareTo(MyClass other) { + // implementation... + return 0; + } } -// #enddocregion f-bound \ No newline at end of file +// #enddocregion f-bound diff --git a/examples/type_system/lib/strong_analysis.dart b/examples/type_system/lib/strong_analysis.dart index c0a0d964f1..64adb62a64 100644 --- a/examples/type_system/lib/strong_analysis.dart +++ b/examples/type_system/lib/strong_analysis.dart @@ -157,10 +157,11 @@ void _miscDeclAnalyzedButNotTested() { } } - // #docregion inference-using-bounds class A> {} + class B extends A {} + class C extends B {} void f>(X x) {} @@ -170,4 +171,4 @@ void main() { f(C()); // Inference fails, compile-time error. f(C()); // OK. } -// #enddocregion inference-using-bounds \ No newline at end of file +// #enddocregion inference-using-bounds From 682485dcd4d1b72510940a644257d8aee69bcc7b Mon Sep 17 00:00:00 2001 From: Marya Belanger Date: Mon, 10 Feb 2025 14:39:17 -0800 Subject: [PATCH 04/16] refresh excerpts --- src/content/language/generics.md | 12 ++++++------ src/content/language/type-system.md | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/content/language/generics.md b/src/content/language/generics.md index a379a7f4d7..172339dc7f 100644 --- a/src/content/language/generics.md +++ b/src/content/language/generics.md @@ -203,15 +203,15 @@ or an F-bound. For example: ```dart abstract class Comparable { - int compareTo(T o); + int compareTo(T o); } class MyClass> implements Comparable> { - @override - int compareTo(MyClass other) { - // implementation... - return 0; - } + @override + int compareTo(MyClass other) { + // implementation... + return 0; + } } ``` diff --git a/src/content/language/type-system.md b/src/content/language/type-system.md index 3b3250338d..b667fa00fc 100644 --- a/src/content/language/type-system.md +++ b/src/content/language/type-system.md @@ -375,7 +375,9 @@ without needing you to explicitly specify the type `f(C())`: ```dart class A> {} + class B extends A {} + class C extends B {} void f>(X x) {} From 60b8925e8292067232388a535a13b952db2a06e0 Mon Sep 17 00:00:00 2001 From: Parker Lougheed Date: Wed, 12 Feb 2025 11:45:53 -0600 Subject: [PATCH 05/16] Update formatting with tall style --- examples/misc/test/language_tour/generics_test.dart | 1 + examples/type_system/lib/strong_analysis.dart | 1 + 2 files changed, 2 insertions(+) diff --git a/examples/misc/test/language_tour/generics_test.dart b/examples/misc/test/language_tour/generics_test.dart index 662d18433e..1500f7503e 100644 --- a/examples/misc/test/language_tour/generics_test.dart +++ b/examples/misc/test/language_tour/generics_test.dart @@ -78,4 +78,5 @@ class MyClass> implements Comparable> { return 0; } } + // #enddocregion f-bound diff --git a/examples/type_system/lib/strong_analysis.dart b/examples/type_system/lib/strong_analysis.dart index 64adb62a64..58a2e44d99 100644 --- a/examples/type_system/lib/strong_analysis.dart +++ b/examples/type_system/lib/strong_analysis.dart @@ -171,4 +171,5 @@ void main() { f(C()); // Inference fails, compile-time error. f(C()); // OK. } + // #enddocregion inference-using-bounds From 0608bef4980fdec132ccde0ba50cbfd90c88e1d0 Mon Sep 17 00:00:00 2001 From: Parker Lougheed Date: Wed, 12 Feb 2025 11:57:27 -0600 Subject: [PATCH 06/16] Add ignore for one_member_abstracts --- examples/misc/test/language_tour/generics_test.dart | 3 ++- src/content/language/generics.md | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/misc/test/language_tour/generics_test.dart b/examples/misc/test/language_tour/generics_test.dart index 1500f7503e..52f13e0b73 100644 --- a/examples/misc/test/language_tour/generics_test.dart +++ b/examples/misc/test/language_tour/generics_test.dart @@ -67,7 +67,8 @@ void main() { class View {} // #docregion f-bound -abstract class Comparable { +// ignore: one_member_abstracts +abstract interface class Comparable { int compareTo(T o); } diff --git a/src/content/language/generics.md b/src/content/language/generics.md index fd869ecfee..071def57cd 100644 --- a/src/content/language/generics.md +++ b/src/content/language/generics.md @@ -204,7 +204,7 @@ or an F-bound. For example: ```dart -abstract class Comparable { +abstract interface class Comparable { int compareTo(T o); } From 020b98ff0944a7368e648ff6ca0c8bc1f43d30f6 Mon Sep 17 00:00:00 2001 From: Marya <111139605+MaryaBelanger@users.noreply.github.com> Date: Thu, 13 Feb 2025 09:27:16 -0800 Subject: [PATCH 07/16] Update src/content/language/generics.md --- src/content/language/generics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/language/generics.md b/src/content/language/generics.md index 071def57cd..52f7e51e94 100644 --- a/src/content/language/generics.md +++ b/src/content/language/generics.md @@ -200,7 +200,7 @@ var foo = [!Foo!](); When using bounds to restrict parameter types, you can refer the bound back to the type parameter itself. This creates a self-referential constraint, -or an F-bound. For example: +or F-bound. For example: ```dart From 8458c79272b0c6603df8d8239f057c474bd48d97 Mon Sep 17 00:00:00 2001 From: Marya <111139605+MaryaBelanger@users.noreply.github.com> Date: Fri, 14 Feb 2025 10:25:59 -0800 Subject: [PATCH 08/16] Apply suggestions from code review Co-authored-by: Erik Ernst --- src/content/language/generics.md | 2 +- src/content/language/type-system.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/content/language/generics.md b/src/content/language/generics.md index 52f7e51e94..72fa0bb274 100644 --- a/src/content/language/generics.md +++ b/src/content/language/generics.md @@ -196,7 +196,7 @@ Specifying any non-`SomeBaseClass` type results in an error: var foo = [!Foo!](); ``` -### Self-referential type parameter restrictions +### Self-referential type parameter restrictions (F-bounds) When using bounds to restrict parameter types, you can refer the bound back to the type parameter itself. This creates a self-referential constraint, diff --git a/src/content/language/type-system.md b/src/content/language/type-system.md index 6c87767f13..dbcdad16f5 100644 --- a/src/content/language/type-system.md +++ b/src/content/language/type-system.md @@ -379,7 +379,7 @@ Inference using bounds requires a [language version][] of at least 3.7.0. ::: Dart's type inference algorithm generates constraints by combining -exisiting lower-bound constraints with the actual type bounds, +existing lower-bound constraints with the actual type bounds, not just best-effort approximations. This is especially important for [F-bounded][] types, where inference using bounds correctly infers that, in the example below, From 52effd6e20496a9b94ca0beda5805fe54785ef0a Mon Sep 17 00:00:00 2001 From: Marya <111139605+MaryaBelanger@users.noreply.github.com> Date: Fri, 14 Feb 2025 10:56:34 -0800 Subject: [PATCH 09/16] Apply suggestions from eernstg code review Co-authored-by: Erik Ernst --- src/content/language/generics.md | 14 ++++++++------ src/content/language/type-system.md | 6 +++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/content/language/generics.md b/src/content/language/generics.md index 72fa0bb274..e4094e56e3 100644 --- a/src/content/language/generics.md +++ b/src/content/language/generics.md @@ -208,17 +208,19 @@ abstract interface class Comparable { int compareTo(T o); } -class MyClass> implements Comparable> { +int compareAndOffset>(T t1, T t2) => + t1.compareTo(t2) + 1; + +class A implements Comparable { @override - int compareTo(MyClass other) { - // implementation... - return 0; - } + int compareTo(A other) => /*...implementation...*/ 0; } + +var useIt = compareAndOffset(A(), A()); ``` The F-bound `T extends Comparable` means `T` must be comparable to itself. -So, `MyClass` can only be used with types that can be compared to +So, `A` can only be compared to other instances of the same type. ## Using generic methods diff --git a/src/content/language/type-system.md b/src/content/language/type-system.md index dbcdad16f5..e32de022bd 100644 --- a/src/content/language/type-system.md +++ b/src/content/language/type-system.md @@ -378,13 +378,13 @@ method's type argument: ``. Inference using bounds requires a [language version][] of at least 3.7.0. ::: -Dart's type inference algorithm generates constraints by combining +With this feature, Dart's type inference algorithm generates constraints by combining existing lower-bound constraints with the actual type bounds, not just best-effort approximations. This is especially important for [F-bounded][] types, where inference using bounds correctly infers that, in the example below, -`C` is a subtype of `B`, -without needing you to explicitly specify the type `f(C())`: +`X` can be bound to `B`. +Without the feature, the type argument must be specified explicitly: `f(C())`: ```dart From efce6ff46c11c581dc164129ca55002cd42a23dd Mon Sep 17 00:00:00 2001 From: Marya Belanger Date: Fri, 14 Feb 2025 13:35:14 -0800 Subject: [PATCH 10/16] more eernstg review --- .../test/language_tour/generics_test.dart | 12 ++++++---- .../lib/bounded/instantiate_to_bound.dart | 11 +++++++++ examples/type_system/lib/strong_analysis.dart | 6 ++--- src/content/language/generics.md | 3 +-- src/content/language/type-system.md | 24 ++++++++++++++++--- 5 files changed, 43 insertions(+), 13 deletions(-) diff --git a/examples/misc/test/language_tour/generics_test.dart b/examples/misc/test/language_tour/generics_test.dart index 52f13e0b73..6515dd1217 100644 --- a/examples/misc/test/language_tour/generics_test.dart +++ b/examples/misc/test/language_tour/generics_test.dart @@ -72,12 +72,14 @@ abstract interface class Comparable { int compareTo(T o); } -class MyClass> implements Comparable> { +int compareAndOffset>(T t1, T t2) => + t1.compareTo(t2) + 1; + +class A implements Comparable { @override - int compareTo(MyClass other) { - // implementation... - return 0; - } + int compareTo(A other) => /*...implementation...*/ 0; } +var useIt = compareAndOffset(A(), A()); + // #enddocregion f-bound diff --git a/examples/type_system/lib/bounded/instantiate_to_bound.dart b/examples/type_system/lib/bounded/instantiate_to_bound.dart index 1269453d5f..be78a27639 100644 --- a/examples/type_system/lib/bounded/instantiate_to_bound.dart +++ b/examples/type_system/lib/bounded/instantiate_to_bound.dart @@ -7,3 +7,14 @@ void cannotRunThis() { c.add(2); // #enddocregion undefined-method } + +// #docregion inference-using-bounds-2 +X max>(X x1, X x2) => x1.compareTo(x2) > 0 ? x1 : x2; + +void main() { + max( + 3, + 7, + ); // Infers `num` with the feature, would have reported an error without it. +} +// #enddocregion inference-using-bounds-2 \ No newline at end of file diff --git a/examples/type_system/lib/strong_analysis.dart b/examples/type_system/lib/strong_analysis.dart index 58a2e44d99..5433554378 100644 --- a/examples/type_system/lib/strong_analysis.dart +++ b/examples/type_system/lib/strong_analysis.dart @@ -168,8 +168,8 @@ void f>(X x) {} void main() { f(B()); // OK. - f(C()); // Inference fails, compile-time error. + f(C()); // OK. Without using bounds, inference relying on best-effort + // approximations would fail after detecting that `C` is not a subtype of `A`. f(C()); // OK. } - -// #enddocregion inference-using-bounds +// #enddocregion inference-using-bounds \ No newline at end of file diff --git a/src/content/language/generics.md b/src/content/language/generics.md index e4094e56e3..62ce12ef80 100644 --- a/src/content/language/generics.md +++ b/src/content/language/generics.md @@ -220,8 +220,7 @@ var useIt = compareAndOffset(A(), A()); ``` The F-bound `T extends Comparable` means `T` must be comparable to itself. -So, `A` can only be compared to -other instances of the same type. +So, `A` can only be compared to other instances of the same type. ## Using generic methods diff --git a/src/content/language/type-system.md b/src/content/language/type-system.md index e32de022bd..e159af936e 100644 --- a/src/content/language/type-system.md +++ b/src/content/language/type-system.md @@ -378,9 +378,11 @@ method's type argument: ``. Inference using bounds requires a [language version][] of at least 3.7.0. ::: -With this feature, Dart's type inference algorithm generates constraints by combining -existing lower-bound constraints with the actual type bounds, +With the inference using bounds feature, +Dart's type inference algorithm generates constraints by +combining existing constraints with the declared type bounds, not just best-effort approximations. + This is especially important for [F-bounded][] types, where inference using bounds correctly infers that, in the example below, `X` can be bound to `B`. @@ -398,11 +400,27 @@ void f>(X x) {} void main() { f(B()); // OK. - f(C()); // Inference fails, compile-time error. + f(C()); // OK. Without using bounds, inference relying on best-effort + // approximations would fail after detecting that `C` is not a subtype of `A`. f(C()); // OK. } ``` +Here's an example using everyday types in Dart like `int` or `num`, +that typically have their own special properties unlike the simple example above: + + +```dart +X max>(X x1, X x2) => x1.compareTo(x2) > 0 ? x1 : x2; + +void main() { + max( + 3, + 7, + ); // Infers `num` with the feature, would have reported an error without it. +} +``` + For more information on the inference using bounds algorithm, read the [design document][]. From 254a538f6a44e3f5b43ae71cc19cafc770559aa0 Mon Sep 17 00:00:00 2001 From: Marya Belanger Date: Fri, 14 Feb 2025 14:29:34 -0800 Subject: [PATCH 11/16] fix link and format --- examples/type_system/lib/bounded/instantiate_to_bound.dart | 3 ++- examples/type_system/lib/strong_analysis.dart | 3 ++- src/content/language/type-system.md | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/type_system/lib/bounded/instantiate_to_bound.dart b/examples/type_system/lib/bounded/instantiate_to_bound.dart index be78a27639..aecb13954f 100644 --- a/examples/type_system/lib/bounded/instantiate_to_bound.dart +++ b/examples/type_system/lib/bounded/instantiate_to_bound.dart @@ -17,4 +17,5 @@ void main() { 7, ); // Infers `num` with the feature, would have reported an error without it. } -// #enddocregion inference-using-bounds-2 \ No newline at end of file + +// #enddocregion inference-using-bounds-2 diff --git a/examples/type_system/lib/strong_analysis.dart b/examples/type_system/lib/strong_analysis.dart index 5433554378..a6ca120e60 100644 --- a/examples/type_system/lib/strong_analysis.dart +++ b/examples/type_system/lib/strong_analysis.dart @@ -172,4 +172,5 @@ void main() { // approximations would fail after detecting that `C` is not a subtype of `A`. f(C()); // OK. } -// #enddocregion inference-using-bounds \ No newline at end of file + +// #enddocregion inference-using-bounds diff --git a/src/content/language/type-system.md b/src/content/language/type-system.md index e159af936e..db56503b52 100644 --- a/src/content/language/type-system.md +++ b/src/content/language/type-system.md @@ -424,7 +424,7 @@ void main() { For more information on the inference using bounds algorithm, read the [design document][]. -[F-bounded]: /language/generics/#self-referential-type-parameter-restrictions +[F-bounded]: /language/generics/#self-referential-type-parameter-restrictions-f-bounds [design document]: {{site.repo.dart.lang}}/blob/main/accepted/future-releases/3009-inference-using-bounds/design-document.md#motivating-example ## Substituting types From 3b4ed5d4d14d489afdf3ff88e8d4ae1ba4c3c542 Mon Sep 17 00:00:00 2001 From: Marya Belanger Date: Fri, 14 Feb 2025 15:16:09 -0800 Subject: [PATCH 12/16] add section on deconstructing type arguments --- src/content/language/type-system.md | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/content/language/type-system.md b/src/content/language/type-system.md index db56503b52..05f4185b4f 100644 --- a/src/content/language/type-system.md +++ b/src/content/language/type-system.md @@ -406,8 +406,8 @@ void main() { } ``` -Here's an example using everyday types in Dart like `int` or `num`, -that typically have their own special properties unlike the simple example above: +Here's a more realistic example using everyday types in Dart like `int` or `num`, +that typically have their own special properties: ```dart @@ -421,6 +421,25 @@ void main() { } ``` +With inference using bounds, Dart can *deconstruct* type arguments, +extracting type information from a generic type parameter's bound. +This allows functions like `f1` in the following example to preserve both the +specific iterable type (`List` or `Set`) *and* the element type. +Before inference using bounds, this wasn't possible +without losing type safety or specific type information. + +```dart +(X, Y) f1, Y>(X x) => (x, x.first); + +void main() { + var (myList, myInt) = f1(); + myInt.whatever; // Compile-time error, `myInt` has type `int`. + + var (mySet, myString) = f1({'Hello!'}); + mySet.union({}); // Works, `mySet` has type `Set`. +} +``` + For more information on the inference using bounds algorithm, read the [design document][]. From d13ec9b3abc4834a4925e4f87a4dfa78feda0494 Mon Sep 17 00:00:00 2001 From: Marya <111139605+MaryaBelanger@users.noreply.github.com> Date: Tue, 18 Feb 2025 09:00:45 -0800 Subject: [PATCH 13/16] Apply suggestions from eernstg review Co-authored-by: Erik Ernst --- src/content/language/type-system.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/content/language/type-system.md b/src/content/language/type-system.md index 05f4185b4f..14d6bfde41 100644 --- a/src/content/language/type-system.md +++ b/src/content/language/type-system.md @@ -406,18 +406,15 @@ void main() { } ``` -Here's a more realistic example using everyday types in Dart like `int` or `num`, -that typically have their own special properties: +Here's a more realistic example using everyday types in Dart like `int` or `num`: ```dart X max>(X x1, X x2) => x1.compareTo(x2) > 0 ? x1 : x2; void main() { - max( - 3, - 7, - ); // Infers `num` with the feature, would have reported an error without it. + // Inferred as `max(3, 7)` with the feature, fails without it. + max(3, 7); } ``` @@ -440,6 +437,7 @@ void main() { } ``` +Without the feature, `myInt` has type `dynamic`. This implies that an expression like `myInt.whatever` is not detected as being wrong at compile time, it will instead throw at run time. Conversely, `mySet.union({})` is a compile-time error when we don't have the feature, because we can't preserve the information that `mySet` is a `Set`. For more information on the inference using bounds algorithm, read the [design document][]. From a180b33ace4366016c06506ed1c29b505fd09d63 Mon Sep 17 00:00:00 2001 From: Marya Belanger Date: Tue, 18 Feb 2025 09:37:22 -0800 Subject: [PATCH 14/16] add deconstruction conclusion --- .../lib/bounded/instantiate_to_bound.dart | 6 ++---- src/content/language/type-system.md | 12 +++++++++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/examples/type_system/lib/bounded/instantiate_to_bound.dart b/examples/type_system/lib/bounded/instantiate_to_bound.dart index aecb13954f..56c8cffd37 100644 --- a/examples/type_system/lib/bounded/instantiate_to_bound.dart +++ b/examples/type_system/lib/bounded/instantiate_to_bound.dart @@ -12,10 +12,8 @@ void cannotRunThis() { X max>(X x1, X x2) => x1.compareTo(x2) > 0 ? x1 : x2; void main() { - max( - 3, - 7, - ); // Infers `num` with the feature, would have reported an error without it. + // Inferred as `max(3, 7)` with the feature, fails without it. + max(3, 7); } // #enddocregion inference-using-bounds-2 diff --git a/src/content/language/type-system.md b/src/content/language/type-system.md index 14d6bfde41..52aed0c9a5 100644 --- a/src/content/language/type-system.md +++ b/src/content/language/type-system.md @@ -420,13 +420,13 @@ void main() { With inference using bounds, Dart can *deconstruct* type arguments, extracting type information from a generic type parameter's bound. -This allows functions like `f1` in the following example to preserve both the +This allows functions like `f` in the following example to preserve both the specific iterable type (`List` or `Set`) *and* the element type. Before inference using bounds, this wasn't possible without losing type safety or specific type information. ```dart -(X, Y) f1, Y>(X x) => (x, x.first); +(X, Y) f, Y>(X x) => (x, x.first); void main() { var (myList, myInt) = f1(); @@ -437,7 +437,13 @@ void main() { } ``` -Without the feature, `myInt` has type `dynamic`. This implies that an expression like `myInt.whatever` is not detected as being wrong at compile time, it will instead throw at run time. Conversely, `mySet.union({})` is a compile-time error when we don't have the feature, because we can't preserve the information that `mySet` is a `Set`. +Without inference using bounds, `myInt` would have the type `dynamic`. +The previous inference algorithm wouldn't catch the incorrect expression +`myInt.whatever` at compile time, and would instead throw at run time. +Conversely, `mySet.union({})` would be a compile-time error +without inference using bounds, because the previous algorithm couldn't +preserve the information that `mySet` is a `Set`. + For more information on the inference using bounds algorithm, read the [design document][]. From 19389dbb42204c6a753bece997a9d810f1732526 Mon Sep 17 00:00:00 2001 From: Marya Belanger Date: Tue, 18 Feb 2025 10:48:31 -0800 Subject: [PATCH 15/16] format comment and heading --- examples/type_system/lib/strong_analysis.dart | 7 +++++-- src/content/language/generics.md | 2 +- src/content/language/type-system.md | 9 ++++++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/examples/type_system/lib/strong_analysis.dart b/examples/type_system/lib/strong_analysis.dart index a6ca120e60..432e0bb8c4 100644 --- a/examples/type_system/lib/strong_analysis.dart +++ b/examples/type_system/lib/strong_analysis.dart @@ -168,8 +168,11 @@ void f>(X x) {} void main() { f(B()); // OK. - f(C()); // OK. Without using bounds, inference relying on best-effort - // approximations would fail after detecting that `C` is not a subtype of `A`. + + // OK. Without using bounds, inference relying on best-effort approximations + // would fail after detecting that `C` is not a subtype of `A`. + f(C()); + f(C()); // OK. } diff --git a/src/content/language/generics.md b/src/content/language/generics.md index 62ce12ef80..e98c6b8c1f 100644 --- a/src/content/language/generics.md +++ b/src/content/language/generics.md @@ -196,7 +196,7 @@ Specifying any non-`SomeBaseClass` type results in an error: var foo = [!Foo!](); ``` -### Self-referential type parameter restrictions (F-bounds) +### Self-referential type parameter restrictions (F-bounds) {:#f-bounds} When using bounds to restrict parameter types, you can refer the bound back to the type parameter itself. This creates a self-referential constraint, diff --git a/src/content/language/type-system.md b/src/content/language/type-system.md index 52aed0c9a5..a706548ea5 100644 --- a/src/content/language/type-system.md +++ b/src/content/language/type-system.md @@ -400,8 +400,11 @@ void f>(X x) {} void main() { f(B()); // OK. - f(C()); // OK. Without using bounds, inference relying on best-effort - // approximations would fail after detecting that `C` is not a subtype of `A`. + + // OK. Without using bounds, inference relying on best-effort approximations + // would fail after detecting that `C` is not a subtype of `A`. + f(C()); + f(C()); // OK. } ``` @@ -447,7 +450,7 @@ preserve the information that `mySet` is a `Set`. For more information on the inference using bounds algorithm, read the [design document][]. -[F-bounded]: /language/generics/#self-referential-type-parameter-restrictions-f-bounds +[F-bounded]: /language/generics/#f-bounds [design document]: {{site.repo.dart.lang}}/blob/main/accepted/future-releases/3009-inference-using-bounds/design-document.md#motivating-example ## Substituting types From 242bc13f79960021f934806a00c87798c3ff5ee9 Mon Sep 17 00:00:00 2001 From: Marya Belanger Date: Tue, 18 Feb 2025 11:02:59 -0800 Subject: [PATCH 16/16] format and refresh excerpts --- examples/type_system/lib/strong_analysis.dart | 5 ----- 1 file changed, 5 deletions(-) diff --git a/examples/type_system/lib/strong_analysis.dart b/examples/type_system/lib/strong_analysis.dart index 9c8513712d..432e0bb8c4 100644 --- a/examples/type_system/lib/strong_analysis.dart +++ b/examples/type_system/lib/strong_analysis.dart @@ -169,15 +169,10 @@ void f>(X x) {} void main() { f(B()); // OK. - // OK. Without using bounds, inference relying on best-effort approximations // would fail after detecting that `C` is not a subtype of `A`. f(C()); - - f(C()); // OK. Without using bounds, inference relying on best-effort - // approximations would fail after detecting that `C` is not a subtype of `A`. - f(C()); // OK. }