diff --git a/pkg/front_end/testcases/general/issue52485.dart b/pkg/front_end/testcases/general/issue52485.dart new file mode 100644 index 00000000000..e231542e291 --- /dev/null +++ b/pkg/front_end/testcases/general/issue52485.dart @@ -0,0 +1,13 @@ +// 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. + +class A {} + +Future h1?>(X x) async { + var x2 = await x; // Remains null. + print([x2].runtimeType); // 'List`. + print(x2); // 'null': A soundness violation. +} + +void main() async => await h1(null); diff --git a/pkg/front_end/testcases/general/issue52485.dart.strong.expect b/pkg/front_end/testcases/general/issue52485.dart.strong.expect new file mode 100644 index 00000000000..a7bf23575fc --- /dev/null +++ b/pkg/front_end/testcases/general/issue52485.dart.strong.expect @@ -0,0 +1,17 @@ +library /*isNonNullableByDefault*/; +import self as self; +import "dart:core" as core; +import "dart:async" as asy; + +class A extends core::Object { + synthetic constructor •() → self::A + : super core::Object::•() + ; +} +static method h1?>(self::h1::X% x) → asy::Future async /* futureValueType= void */ { + self::A? x2 = await x /* runtimeCheckType= asy::Future */ ; + core::print([x2].{core::Object::runtimeType}{core::Type}); + core::print(x2); +} +static method main() → void async /* futureValueType= void */ + return await self::h1(null); diff --git a/pkg/front_end/testcases/general/issue52485.dart.strong.transformed.expect b/pkg/front_end/testcases/general/issue52485.dart.strong.transformed.expect new file mode 100644 index 00000000000..3471777bdbc --- /dev/null +++ b/pkg/front_end/testcases/general/issue52485.dart.strong.transformed.expect @@ -0,0 +1,17 @@ +library /*isNonNullableByDefault*/; +import self as self; +import "dart:core" as core; +import "dart:async" as asy; + +class A extends core::Object { + synthetic constructor •() → self::A + : super core::Object::•() + ; +} +static method h1?>(self::h1::X% x) → asy::Future async /* futureValueType= void */ { + self::A? x2 = await x /* runtimeCheckType= asy::Future */ ; + core::print(core::_GrowableList::_literal1(x2).{core::Object::runtimeType}{core::Type}); + core::print(x2); +} +static method main() → void async /* futureValueType= void */ + return await self::h1(null); diff --git a/pkg/front_end/testcases/general/issue52485.dart.textual_outline.expect b/pkg/front_end/testcases/general/issue52485.dart.textual_outline.expect new file mode 100644 index 00000000000..e7d14fd3383 --- /dev/null +++ b/pkg/front_end/testcases/general/issue52485.dart.textual_outline.expect @@ -0,0 +1,4 @@ +class A {} + +Future h1?>(X x) async {} +void main() async => await h1(null); diff --git a/pkg/front_end/testcases/general/issue52485.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/issue52485.dart.textual_outline_modelled.expect new file mode 100644 index 00000000000..9ea9b72839e --- /dev/null +++ b/pkg/front_end/testcases/general/issue52485.dart.textual_outline_modelled.expect @@ -0,0 +1,5 @@ +Future h1?>(X x) async {} + +class A {} + +void main() async => await h1(null); diff --git a/pkg/front_end/testcases/general/issue52485.dart.weak.expect b/pkg/front_end/testcases/general/issue52485.dart.weak.expect new file mode 100644 index 00000000000..a7bf23575fc --- /dev/null +++ b/pkg/front_end/testcases/general/issue52485.dart.weak.expect @@ -0,0 +1,17 @@ +library /*isNonNullableByDefault*/; +import self as self; +import "dart:core" as core; +import "dart:async" as asy; + +class A extends core::Object { + synthetic constructor •() → self::A + : super core::Object::•() + ; +} +static method h1?>(self::h1::X% x) → asy::Future async /* futureValueType= void */ { + self::A? x2 = await x /* runtimeCheckType= asy::Future */ ; + core::print([x2].{core::Object::runtimeType}{core::Type}); + core::print(x2); +} +static method main() → void async /* futureValueType= void */ + return await self::h1(null); diff --git a/pkg/front_end/testcases/general/issue52485.dart.weak.modular.expect b/pkg/front_end/testcases/general/issue52485.dart.weak.modular.expect new file mode 100644 index 00000000000..a7bf23575fc --- /dev/null +++ b/pkg/front_end/testcases/general/issue52485.dart.weak.modular.expect @@ -0,0 +1,17 @@ +library /*isNonNullableByDefault*/; +import self as self; +import "dart:core" as core; +import "dart:async" as asy; + +class A extends core::Object { + synthetic constructor •() → self::A + : super core::Object::•() + ; +} +static method h1?>(self::h1::X% x) → asy::Future async /* futureValueType= void */ { + self::A? x2 = await x /* runtimeCheckType= asy::Future */ ; + core::print([x2].{core::Object::runtimeType}{core::Type}); + core::print(x2); +} +static method main() → void async /* futureValueType= void */ + return await self::h1(null); diff --git a/pkg/front_end/testcases/general/issue52485.dart.weak.outline.expect b/pkg/front_end/testcases/general/issue52485.dart.weak.outline.expect new file mode 100644 index 00000000000..c912c4fb8c9 --- /dev/null +++ b/pkg/front_end/testcases/general/issue52485.dart.weak.outline.expect @@ -0,0 +1,13 @@ +library /*isNonNullableByDefault*/; +import self as self; +import "dart:core" as core; +import "dart:async" as asy; + +class A extends core::Object { + synthetic constructor •() → self::A + ; +} +static method h1?>(self::h1::X% x) → asy::Future async + ; +static method main() → void async + ; diff --git a/pkg/front_end/testcases/general/issue52485.dart.weak.transformed.expect b/pkg/front_end/testcases/general/issue52485.dart.weak.transformed.expect new file mode 100644 index 00000000000..3471777bdbc --- /dev/null +++ b/pkg/front_end/testcases/general/issue52485.dart.weak.transformed.expect @@ -0,0 +1,17 @@ +library /*isNonNullableByDefault*/; +import self as self; +import "dart:core" as core; +import "dart:async" as asy; + +class A extends core::Object { + synthetic constructor •() → self::A + : super core::Object::•() + ; +} +static method h1?>(self::h1::X% x) → asy::Future async /* futureValueType= void */ { + self::A? x2 = await x /* runtimeCheckType= asy::Future */ ; + core::print(core::_GrowableList::_literal1(x2).{core::Object::runtimeType}{core::Type}); + core::print(x2); +} +static method main() → void async /* futureValueType= void */ + return await self::h1(null); diff --git a/pkg/kernel/lib/type_environment.dart b/pkg/kernel/lib/type_environment.dart index 5fbd0efcb08..4b1585feeea 100644 --- a/pkg/kernel/lib/type_environment.dart +++ b/pkg/kernel/lib/type_environment.dart @@ -129,18 +129,33 @@ abstract class TypeEnvironment extends Types { DartType bound = t.right; DartType? futureType = _futureTypeOf(bound); if (futureType != null) { - return _withDeclaredNullability(flatten(futureType), t.nullability); + DartType flattenedFutureType = flatten(futureType); + return _withDeclaredNullability(flattenedFutureType, + uniteNullabilities(t.nullability, flattenedFutureType.nullability)); } else { - return _withDeclaredNullability(flatten(t.left), t.nullability); + DartType flattenedFutureType = flatten(t.left); + return _withDeclaredNullability(flattenedFutureType, + uniteNullabilities(t.nullability, flattenedFutureType.nullability)); } } else { DartType? futureType = _futureTypeOf(t); if (futureType is InterfaceType) { assert(futureType.classNode == coreTypes.futureClass); + DartType typeArgument = futureType.typeArguments.single; return _withDeclaredNullability( - futureType.typeArguments.single, t.nullability); + typeArgument, + uniteNullabilities( + t.nullability, + uniteNullabilities( + futureType.nullability, typeArgument.nullability))); } else if (futureType is FutureOrType) { - return _withDeclaredNullability(futureType.typeArgument, t.nullability); + DartType typeArgument = futureType.typeArgument; + return _withDeclaredNullability( + typeArgument, + uniteNullabilities( + t.nullability, + uniteNullabilities( + futureType.nullability, typeArgument.nullability))); } else { return t; }