From 4352f10c0567f92f4824ffb1a19c1806424553d5 Mon Sep 17 00:00:00 2001 From: Chloe Stefantsova Date: Wed, 16 Aug 2023 09:49:34 +0000 Subject: [PATCH] [cfe] Implement spec update on generator element type The update can be found here: https://github.com/dart-lang/language/pull/3218/files Closes https://github.com/dart-lang/sdk/issues/53052 Change-Id: I08146a6ca09667cc5e0ebcd564e60f454d03a468 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/320763 Reviewed-by: Johnni Winther Commit-Queue: Chloe Stefantsova --- .../fasta/type_inference/closure_context.dart | 8 ++- .../testcases/general/issue53052.dart | 45 +++++++++++++++ .../general/issue53052.dart.strong.expect | 51 +++++++++++++++++ .../issue53052.dart.strong.transformed.expect | 57 +++++++++++++++++++ .../issue53052.dart.textual_outline.expect | 7 +++ ...53052.dart.textual_outline_modelled.expect | 7 +++ .../general/issue53052.dart.weak.expect | 51 +++++++++++++++++ .../issue53052.dart.weak.modular.expect | 51 +++++++++++++++++ .../issue53052.dart.weak.outline.expect | 17 ++++++ .../issue53052.dart.weak.transformed.expect | 57 +++++++++++++++++++ pkg/kernel/lib/type_environment.dart | 32 +++++++++++ 11 files changed, 381 insertions(+), 2 deletions(-) create mode 100644 pkg/front_end/testcases/general/issue53052.dart create mode 100644 pkg/front_end/testcases/general/issue53052.dart.strong.expect create mode 100644 pkg/front_end/testcases/general/issue53052.dart.strong.transformed.expect create mode 100644 pkg/front_end/testcases/general/issue53052.dart.textual_outline.expect create mode 100644 pkg/front_end/testcases/general/issue53052.dart.textual_outline_modelled.expect create mode 100644 pkg/front_end/testcases/general/issue53052.dart.weak.expect create mode 100644 pkg/front_end/testcases/general/issue53052.dart.weak.modular.expect create mode 100644 pkg/front_end/testcases/general/issue53052.dart.weak.outline.expect create mode 100644 pkg/front_end/testcases/general/issue53052.dart.weak.transformed.expect diff --git a/pkg/front_end/lib/src/fasta/type_inference/closure_context.dart b/pkg/front_end/lib/src/fasta/type_inference/closure_context.dart index 6c4ea11f3dde..e6d68a0ca5fe 100644 --- a/pkg/front_end/lib/src/fasta/type_inference/closure_context.dart +++ b/pkg/front_end/lib/src/fasta/type_inference/closure_context.dart @@ -53,12 +53,16 @@ abstract class ClosureContext { if (isGenerator) { if (isAsync) { DartType yieldContext = inferrer.getTypeArgumentOf( - returnContext, inferrer.coreTypes.streamClass); + inferrer.typeSchemaEnvironment.getUnionFreeType(returnContext, + isNonNullableByDefault: inferrer.isNonNullableByDefault), + inferrer.coreTypes.streamClass); return new _AsyncStarClosureContext( inferrer, yieldContext, declaredReturnType, needToInferReturnType); } else { DartType yieldContext = inferrer.getTypeArgumentOf( - returnContext, inferrer.coreTypes.iterableClass); + inferrer.typeSchemaEnvironment.getUnionFreeType(returnContext, + isNonNullableByDefault: inferrer.isNonNullableByDefault), + inferrer.coreTypes.iterableClass); return new _SyncStarClosureContext( inferrer, yieldContext, declaredReturnType, needToInferReturnType); } diff --git a/pkg/front_end/testcases/general/issue53052.dart b/pkg/front_end/testcases/general/issue53052.dart new file mode 100644 index 000000000000..ba431f5901c4 --- /dev/null +++ b/pkg/front_end/testcases/general/issue53052.dart @@ -0,0 +1,45 @@ +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:async'; + +FutureOr> f() sync* { + yield 'Hello!' as dynamic; +} + +FutureOr> g() async* { + yield* 'Hello!' as dynamic; +} + +main() async { + var iterable = f(); + if (iterable is Future) return; + expectThrows(() { int i = iterable.first; }); + + var stream = g(); + if (stream is Future) return; + await expectAsyncThrows(() async { int i = await stream.first; }); +} + +expectThrows(f) { + bool hasThrown = true; + try { + f(); + hasThrown = false; + } catch(e) {} + if (!hasThrown) { + throw "Expected the function to throw."; + } +} + +expectAsyncThrows(f) async { + bool hasThrown = true; + try { + await f(); + hasThrown = false; + } catch(e) {} + if (!hasThrown) { + throw "Expected the function to throw."; + } +} diff --git a/pkg/front_end/testcases/general/issue53052.dart.strong.expect b/pkg/front_end/testcases/general/issue53052.dart.strong.expect new file mode 100644 index 000000000000..50334072e79f --- /dev/null +++ b/pkg/front_end/testcases/general/issue53052.dart.strong.expect @@ -0,0 +1,51 @@ +library; +import self as self; +import "dart:core" as core; +import "dart:async" as asy; + +import "dart:async"; + +static method f() → FutureOr> sync* { + yield("Hello!" as dynamic) as{TypeError,ForDynamic} core::int; +} +static method g() → FutureOr> async* { + yield*("Hello!" as dynamic) as{TypeError,ForDynamic} asy::Stream; +} +static method main() → dynamic async /* futureValueType= dynamic */ { + FutureOr>iterable = self::f(); + if(iterable is asy::Future) + return; + self::expectThrows(() → Null { + core::int i = iterable{core::Iterable}.{core::Iterable::first}{core::int}; + }); + FutureOr>stream = self::g(); + if(stream is asy::Future) + return; + await self::expectAsyncThrows(() → asy::Future async /* futureValueType= Null */ { + core::int i = await stream{asy::Stream}.{asy::Stream::first}{asy::Future}; + }) /* runtimeCheckType= asy::Future */ ; +} +static method expectThrows(dynamic f) → dynamic { + core::bool hasThrown = true; + try { + f{dynamic}.call(); + hasThrown = false; + } + on core::Object catch(final core::Object e) { + } + if(!hasThrown) { + throw "Expected the function to throw."; + } +} +static method expectAsyncThrows(dynamic f) → dynamic async /* futureValueType= dynamic */ { + core::bool hasThrown = true; + try { + await f{dynamic}.call() /* runtimeCheckType= asy::Future */ ; + hasThrown = false; + } + on core::Object catch(final core::Object e) { + } + if(!hasThrown) { + throw "Expected the function to throw."; + } +} diff --git a/pkg/front_end/testcases/general/issue53052.dart.strong.transformed.expect b/pkg/front_end/testcases/general/issue53052.dart.strong.transformed.expect new file mode 100644 index 000000000000..1eec43e2e0e9 --- /dev/null +++ b/pkg/front_end/testcases/general/issue53052.dart.strong.transformed.expect @@ -0,0 +1,57 @@ +library; +import self as self; +import "dart:core" as core; +import "dart:async" as asy; + +import "dart:async"; + +static method f() → FutureOr> sync* { + yield("Hello!" as dynamic) as{TypeError,ForDynamic} core::int; +} +static method g() → FutureOr> async* { + yield*("Hello!" as dynamic) as{TypeError,ForDynamic} asy::Stream; +} +static method main() → dynamic async /* futureValueType= dynamic */ { + FutureOr>iterable = self::f(); + if(iterable is asy::Future) + return; + self::expectThrows(() → Null { + core::int i = iterable{core::Iterable}.{core::Iterable::first}{core::int}; + }); + FutureOr>stream = self::g(); + if(stream is asy::Future) + return; + await self::expectAsyncThrows(() → asy::Future async /* futureValueType= Null */ { + core::int i = await stream{asy::Stream}.{asy::Stream::first}{asy::Future}; + }) /* runtimeCheckType= asy::Future */ ; +} +static method expectThrows(dynamic f) → dynamic { + core::bool hasThrown = true; + try { + f{dynamic}.call(); + hasThrown = false; + } + on core::Object catch(final core::Object e) { + } + if(!hasThrown) { + throw "Expected the function to throw."; + } +} +static method expectAsyncThrows(dynamic f) → dynamic async /* futureValueType= dynamic */ { + core::bool hasThrown = true; + try { + await f{dynamic}.call() /* runtimeCheckType= asy::Future */ ; + hasThrown = false; + } + on core::Object catch(final core::Object e) { + } + if(!hasThrown) { + throw "Expected the function to throw."; + } +} + + +Extra constant evaluation status: +Evaluated: AsExpression @ org-dartlang-testcase:///issue53052.dart:8:18 -> StringConstant("Hello!") +Evaluated: AsExpression @ org-dartlang-testcase:///issue53052.dart:12:19 -> StringConstant("Hello!") +Extra constant evaluation: evaluated: 33, effectively constant: 2 diff --git a/pkg/front_end/testcases/general/issue53052.dart.textual_outline.expect b/pkg/front_end/testcases/general/issue53052.dart.textual_outline.expect new file mode 100644 index 000000000000..a717b88ac967 --- /dev/null +++ b/pkg/front_end/testcases/general/issue53052.dart.textual_outline.expect @@ -0,0 +1,7 @@ +import 'dart:async'; + +FutureOr> f() sync* {} +FutureOr> g() async* {} +main() async {} +expectThrows(f) {} +expectAsyncThrows(f) async {} diff --git a/pkg/front_end/testcases/general/issue53052.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/issue53052.dart.textual_outline_modelled.expect new file mode 100644 index 000000000000..3e68bada9039 --- /dev/null +++ b/pkg/front_end/testcases/general/issue53052.dart.textual_outline_modelled.expect @@ -0,0 +1,7 @@ +import 'dart:async'; + +FutureOr> f() sync* {} +FutureOr> g() async* {} +expectAsyncThrows(f) async {} +expectThrows(f) {} +main() async {} diff --git a/pkg/front_end/testcases/general/issue53052.dart.weak.expect b/pkg/front_end/testcases/general/issue53052.dart.weak.expect new file mode 100644 index 000000000000..50334072e79f --- /dev/null +++ b/pkg/front_end/testcases/general/issue53052.dart.weak.expect @@ -0,0 +1,51 @@ +library; +import self as self; +import "dart:core" as core; +import "dart:async" as asy; + +import "dart:async"; + +static method f() → FutureOr> sync* { + yield("Hello!" as dynamic) as{TypeError,ForDynamic} core::int; +} +static method g() → FutureOr> async* { + yield*("Hello!" as dynamic) as{TypeError,ForDynamic} asy::Stream; +} +static method main() → dynamic async /* futureValueType= dynamic */ { + FutureOr>iterable = self::f(); + if(iterable is asy::Future) + return; + self::expectThrows(() → Null { + core::int i = iterable{core::Iterable}.{core::Iterable::first}{core::int}; + }); + FutureOr>stream = self::g(); + if(stream is asy::Future) + return; + await self::expectAsyncThrows(() → asy::Future async /* futureValueType= Null */ { + core::int i = await stream{asy::Stream}.{asy::Stream::first}{asy::Future}; + }) /* runtimeCheckType= asy::Future */ ; +} +static method expectThrows(dynamic f) → dynamic { + core::bool hasThrown = true; + try { + f{dynamic}.call(); + hasThrown = false; + } + on core::Object catch(final core::Object e) { + } + if(!hasThrown) { + throw "Expected the function to throw."; + } +} +static method expectAsyncThrows(dynamic f) → dynamic async /* futureValueType= dynamic */ { + core::bool hasThrown = true; + try { + await f{dynamic}.call() /* runtimeCheckType= asy::Future */ ; + hasThrown = false; + } + on core::Object catch(final core::Object e) { + } + if(!hasThrown) { + throw "Expected the function to throw."; + } +} diff --git a/pkg/front_end/testcases/general/issue53052.dart.weak.modular.expect b/pkg/front_end/testcases/general/issue53052.dart.weak.modular.expect new file mode 100644 index 000000000000..50334072e79f --- /dev/null +++ b/pkg/front_end/testcases/general/issue53052.dart.weak.modular.expect @@ -0,0 +1,51 @@ +library; +import self as self; +import "dart:core" as core; +import "dart:async" as asy; + +import "dart:async"; + +static method f() → FutureOr> sync* { + yield("Hello!" as dynamic) as{TypeError,ForDynamic} core::int; +} +static method g() → FutureOr> async* { + yield*("Hello!" as dynamic) as{TypeError,ForDynamic} asy::Stream; +} +static method main() → dynamic async /* futureValueType= dynamic */ { + FutureOr>iterable = self::f(); + if(iterable is asy::Future) + return; + self::expectThrows(() → Null { + core::int i = iterable{core::Iterable}.{core::Iterable::first}{core::int}; + }); + FutureOr>stream = self::g(); + if(stream is asy::Future) + return; + await self::expectAsyncThrows(() → asy::Future async /* futureValueType= Null */ { + core::int i = await stream{asy::Stream}.{asy::Stream::first}{asy::Future}; + }) /* runtimeCheckType= asy::Future */ ; +} +static method expectThrows(dynamic f) → dynamic { + core::bool hasThrown = true; + try { + f{dynamic}.call(); + hasThrown = false; + } + on core::Object catch(final core::Object e) { + } + if(!hasThrown) { + throw "Expected the function to throw."; + } +} +static method expectAsyncThrows(dynamic f) → dynamic async /* futureValueType= dynamic */ { + core::bool hasThrown = true; + try { + await f{dynamic}.call() /* runtimeCheckType= asy::Future */ ; + hasThrown = false; + } + on core::Object catch(final core::Object e) { + } + if(!hasThrown) { + throw "Expected the function to throw."; + } +} diff --git a/pkg/front_end/testcases/general/issue53052.dart.weak.outline.expect b/pkg/front_end/testcases/general/issue53052.dart.weak.outline.expect new file mode 100644 index 000000000000..4f912c935a93 --- /dev/null +++ b/pkg/front_end/testcases/general/issue53052.dart.weak.outline.expect @@ -0,0 +1,17 @@ +library; +import self as self; +import "dart:core" as core; +import "dart:async" as asy; + +import "dart:async"; + +static method f() → FutureOr> sync* + ; +static method g() → FutureOr> async* + ; +static method main() → dynamic async + ; +static method expectThrows(dynamic f) → dynamic + ; +static method expectAsyncThrows(dynamic f) → dynamic async + ; diff --git a/pkg/front_end/testcases/general/issue53052.dart.weak.transformed.expect b/pkg/front_end/testcases/general/issue53052.dart.weak.transformed.expect new file mode 100644 index 000000000000..1eec43e2e0e9 --- /dev/null +++ b/pkg/front_end/testcases/general/issue53052.dart.weak.transformed.expect @@ -0,0 +1,57 @@ +library; +import self as self; +import "dart:core" as core; +import "dart:async" as asy; + +import "dart:async"; + +static method f() → FutureOr> sync* { + yield("Hello!" as dynamic) as{TypeError,ForDynamic} core::int; +} +static method g() → FutureOr> async* { + yield*("Hello!" as dynamic) as{TypeError,ForDynamic} asy::Stream; +} +static method main() → dynamic async /* futureValueType= dynamic */ { + FutureOr>iterable = self::f(); + if(iterable is asy::Future) + return; + self::expectThrows(() → Null { + core::int i = iterable{core::Iterable}.{core::Iterable::first}{core::int}; + }); + FutureOr>stream = self::g(); + if(stream is asy::Future) + return; + await self::expectAsyncThrows(() → asy::Future async /* futureValueType= Null */ { + core::int i = await stream{asy::Stream}.{asy::Stream::first}{asy::Future}; + }) /* runtimeCheckType= asy::Future */ ; +} +static method expectThrows(dynamic f) → dynamic { + core::bool hasThrown = true; + try { + f{dynamic}.call(); + hasThrown = false; + } + on core::Object catch(final core::Object e) { + } + if(!hasThrown) { + throw "Expected the function to throw."; + } +} +static method expectAsyncThrows(dynamic f) → dynamic async /* futureValueType= dynamic */ { + core::bool hasThrown = true; + try { + await f{dynamic}.call() /* runtimeCheckType= asy::Future */ ; + hasThrown = false; + } + on core::Object catch(final core::Object e) { + } + if(!hasThrown) { + throw "Expected the function to throw."; + } +} + + +Extra constant evaluation status: +Evaluated: AsExpression @ org-dartlang-testcase:///issue53052.dart:8:18 -> StringConstant("Hello!") +Evaluated: AsExpression @ org-dartlang-testcase:///issue53052.dart:12:19 -> StringConstant("Hello!") +Extra constant evaluation: evaluated: 33, effectively constant: 2 diff --git a/pkg/kernel/lib/type_environment.dart b/pkg/kernel/lib/type_environment.dart index 4b1585feeea7..77e22534c969 100644 --- a/pkg/kernel/lib/type_environment.dart +++ b/pkg/kernel/lib/type_environment.dart @@ -162,6 +162,38 @@ abstract class TypeEnvironment extends Types { } } + /// Computes the underlying type of a union type + /// + /// Dart doesn't have generalized union types, but two specific ones: the + /// FutureOr type, which can be seen as a union of T and Future, and the + /// nullable type T?, which can be seen as the union of T and Null. In both + /// cases the union type can be seen as application of the corresponding type + /// constructor, FutureOr or ?, to the underlying type T. [getUnionFreeType] + /// computes the underlying type of the given union type, accounting for + /// potential nesting of the union types. + /// + /// The following are examples of the union-free types computed on for the + /// given types. + /// + /// getUnionFreeType(int) = int + /// getUnionFreeType(int?) = int + /// getUnionFreeType(FutureOr) = int + /// getUnionFreeType(FutureOr?) = int + DartType getUnionFreeType(DartType type, + {required bool isNonNullableByDefault}) { + if (isNullableTypeConstructorApplication(type)) { + return getUnionFreeType( + computeTypeWithoutNullabilityMarker(type, + isNonNullableByDefault: isNonNullableByDefault), + isNonNullableByDefault: isNonNullableByDefault); + } else if (type is FutureOrType) { + return getUnionFreeType(type.typeArgument, + isNonNullableByDefault: isNonNullableByDefault); + } else { + return type; + } + } + /// True if [member] is a binary operator whose return type is defined by /// the both operand types. bool isSpecialCasedBinaryOperator(Procedure member,