[cfe] Adjust nullability of FutureOrType after substitution

Bug: https://github.com/dart-lang/sdk/issues/48631
Change-Id: I329a70386d59425cf3f157dc4b6316649f8aa389
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/240049
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Chloe Stefantsova <cstefantsova@google.com>
diff --git a/pkg/front_end/testcases/nnbd/issue48631_1.dart b/pkg/front_end/testcases/nnbd/issue48631_1.dart
new file mode 100644
index 0000000..f995c3e
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_1.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2022, 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<T?> foo<T>(T t) {}
+
+bar<S>(bool t, S s) {
+  var x = [foo(s), s];
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_1.dart.strong.expect b/pkg/front_end/testcases/nnbd/issue48631_1.dart.strong.expect
new file mode 100644
index 0000000..a26757b
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_1.dart.strong.expect
@@ -0,0 +1,11 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "dart:async";
+
+static method foo<T extends core::Object? = dynamic>(self::foo::T% t) → FutureOr<self::foo::T?> {}
+static method bar<S extends core::Object? = dynamic>(core::bool t, self::bar::S% s) → dynamic {
+  core::List<FutureOr<self::bar::S?>?> x = <FutureOr<self::bar::S?>?>[self::foo<self::bar::S%>(s), s];
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_1.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/issue48631_1.dart.strong.transformed.expect
new file mode 100644
index 0000000..b425585
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_1.dart.strong.transformed.expect
@@ -0,0 +1,11 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "dart:async";
+
+static method foo<T extends core::Object? = dynamic>(self::foo::T% t) → FutureOr<self::foo::T?> {}
+static method bar<S extends core::Object? = dynamic>(core::bool t, self::bar::S% s) → dynamic {
+  core::List<FutureOr<self::bar::S?>?> x = core::_GrowableList::_literal2<FutureOr<self::bar::S?>?>(self::foo<self::bar::S%>(s), s);
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_1.dart.textual_outline.expect b/pkg/front_end/testcases/nnbd/issue48631_1.dart.textual_outline.expect
new file mode 100644
index 0000000..65faadd
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_1.dart.textual_outline.expect
@@ -0,0 +1,5 @@
+import 'dart:async';
+
+FutureOr<T?> foo<T>(T t) {}
+bar<S>(bool t, S s) {}
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_1.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/nnbd/issue48631_1.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..65faadd
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_1.dart.textual_outline_modelled.expect
@@ -0,0 +1,5 @@
+import 'dart:async';
+
+FutureOr<T?> foo<T>(T t) {}
+bar<S>(bool t, S s) {}
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_1.dart.weak.expect b/pkg/front_end/testcases/nnbd/issue48631_1.dart.weak.expect
new file mode 100644
index 0000000..a26757b
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_1.dart.weak.expect
@@ -0,0 +1,11 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "dart:async";
+
+static method foo<T extends core::Object? = dynamic>(self::foo::T% t) → FutureOr<self::foo::T?> {}
+static method bar<S extends core::Object? = dynamic>(core::bool t, self::bar::S% s) → dynamic {
+  core::List<FutureOr<self::bar::S?>?> x = <FutureOr<self::bar::S?>?>[self::foo<self::bar::S%>(s), s];
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_1.dart.weak.modular.expect b/pkg/front_end/testcases/nnbd/issue48631_1.dart.weak.modular.expect
new file mode 100644
index 0000000..a26757b
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_1.dart.weak.modular.expect
@@ -0,0 +1,11 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "dart:async";
+
+static method foo<T extends core::Object? = dynamic>(self::foo::T% t) → FutureOr<self::foo::T?> {}
+static method bar<S extends core::Object? = dynamic>(core::bool t, self::bar::S% s) → dynamic {
+  core::List<FutureOr<self::bar::S?>?> x = <FutureOr<self::bar::S?>?>[self::foo<self::bar::S%>(s), s];
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_1.dart.weak.outline.expect b/pkg/front_end/testcases/nnbd/issue48631_1.dart.weak.outline.expect
new file mode 100644
index 0000000..f366279
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_1.dart.weak.outline.expect
@@ -0,0 +1,12 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "dart:async";
+
+static method foo<T extends core::Object? = dynamic>(self::foo::T% t) → FutureOr<self::foo::T?>
+  ;
+static method bar<S extends core::Object? = dynamic>(core::bool t, self::bar::S% s) → dynamic
+  ;
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/nnbd/issue48631_1.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/issue48631_1.dart.weak.transformed.expect
new file mode 100644
index 0000000..b425585
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_1.dart.weak.transformed.expect
@@ -0,0 +1,11 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "dart:async";
+
+static method foo<T extends core::Object? = dynamic>(self::foo::T% t) → FutureOr<self::foo::T?> {}
+static method bar<S extends core::Object? = dynamic>(core::bool t, self::bar::S% s) → dynamic {
+  core::List<FutureOr<self::bar::S?>?> x = core::_GrowableList::_literal2<FutureOr<self::bar::S?>?>(self::foo<self::bar::S%>(s), s);
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_2.dart b/pkg/front_end/testcases/nnbd/issue48631_2.dart
new file mode 100644
index 0000000..2cf30ba
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_2.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2022, 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';
+
+abstract class A {
+  T foo<T>(B<T> b);
+}
+
+class B<X> {
+  B(X x);
+}
+
+class C<Y> {
+  final Bar<FutureOr<Y>, D<Y>> bar;
+
+  C(this.bar);
+}
+
+abstract class D<W> implements A {}
+
+typedef Bar<V, U extends A> = V Function(U);
+
+final baz = C<int>((a) {
+    return a.foo(B(Future.value(0)));
+});
+
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_2.dart.strong.expect b/pkg/front_end/testcases/nnbd/issue48631_2.dart.strong.expect
new file mode 100644
index 0000000..e842794
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_2.dart.strong.expect
@@ -0,0 +1,34 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+import "dart:async";
+
+typedef Bar<V extends core::Object? = dynamic, contravariant U extends self::A> = (U) → V%;
+abstract class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+  abstract method foo<T extends core::Object? = dynamic>(self::B<self::A::foo::T%> b) → self::A::foo::T%;
+}
+class B<X extends core::Object? = dynamic> extends core::Object {
+  constructor •(self::B::X% x) → self::B<self::B::X%>
+    : super core::Object::•()
+    ;
+}
+class C<Y extends core::Object? = dynamic> extends core::Object {
+  final field (self::D<self::C::Y%>) → FutureOr<self::C::Y%>% bar;
+  constructor •((self::D<self::C::Y%>) → FutureOr<self::C::Y%>% bar) → self::C<self::C::Y%>
+    : self::C::bar = bar, super core::Object::•()
+    ;
+}
+abstract class D<W extends core::Object? = dynamic> extends core::Object implements self::A {
+  synthetic constructor •() → self::D<self::D::W%>
+    : super core::Object::•()
+    ;
+}
+static final field self::C<core::int> baz = new self::C::•<core::int>((self::D<core::int> a) → FutureOr<core::int> {
+  return a.{self::A::foo}<FutureOr<core::int>>(new self::B::•<FutureOr<core::int>>(asy::Future::value<core::int>(0))){(self::B<FutureOr<core::int>>) → FutureOr<core::int>};
+});
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_2.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/issue48631_2.dart.strong.transformed.expect
new file mode 100644
index 0000000..e842794
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_2.dart.strong.transformed.expect
@@ -0,0 +1,34 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+import "dart:async";
+
+typedef Bar<V extends core::Object? = dynamic, contravariant U extends self::A> = (U) → V%;
+abstract class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+  abstract method foo<T extends core::Object? = dynamic>(self::B<self::A::foo::T%> b) → self::A::foo::T%;
+}
+class B<X extends core::Object? = dynamic> extends core::Object {
+  constructor •(self::B::X% x) → self::B<self::B::X%>
+    : super core::Object::•()
+    ;
+}
+class C<Y extends core::Object? = dynamic> extends core::Object {
+  final field (self::D<self::C::Y%>) → FutureOr<self::C::Y%>% bar;
+  constructor •((self::D<self::C::Y%>) → FutureOr<self::C::Y%>% bar) → self::C<self::C::Y%>
+    : self::C::bar = bar, super core::Object::•()
+    ;
+}
+abstract class D<W extends core::Object? = dynamic> extends core::Object implements self::A {
+  synthetic constructor •() → self::D<self::D::W%>
+    : super core::Object::•()
+    ;
+}
+static final field self::C<core::int> baz = new self::C::•<core::int>((self::D<core::int> a) → FutureOr<core::int> {
+  return a.{self::A::foo}<FutureOr<core::int>>(new self::B::•<FutureOr<core::int>>(asy::Future::value<core::int>(0))){(self::B<FutureOr<core::int>>) → FutureOr<core::int>};
+});
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_2.dart.textual_outline.expect b/pkg/front_end/testcases/nnbd/issue48631_2.dart.textual_outline.expect
new file mode 100644
index 0000000..a409162
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_2.dart.textual_outline.expect
@@ -0,0 +1,22 @@
+import 'dart:async';
+
+abstract class A {
+  T foo<T>(B<T> b);
+}
+
+class B<X> {
+  B(X x);
+}
+
+class C<Y> {
+  final Bar<FutureOr<Y>, D<Y>> bar;
+  C(this.bar);
+}
+
+abstract class D<W> implements A {}
+
+typedef Bar<V, U extends A> = V Function(U);
+final baz = C<int>((a) {
+  return a.foo(B(Future.value(0)));
+});
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_2.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/nnbd/issue48631_2.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..bee82dc
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_2.dart.textual_outline_modelled.expect
@@ -0,0 +1,22 @@
+import 'dart:async';
+
+abstract class A {
+  T foo<T>(B<T> b);
+}
+
+abstract class D<W> implements A {}
+
+class B<X> {
+  B(X x);
+}
+
+class C<Y> {
+  C(this.bar);
+  final Bar<FutureOr<Y>, D<Y>> bar;
+}
+
+final baz = C<int>((a) {
+  return a.foo(B(Future.value(0)));
+});
+main() {}
+typedef Bar<V, U extends A> = V Function(U);
diff --git a/pkg/front_end/testcases/nnbd/issue48631_2.dart.weak.expect b/pkg/front_end/testcases/nnbd/issue48631_2.dart.weak.expect
new file mode 100644
index 0000000..e842794
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_2.dart.weak.expect
@@ -0,0 +1,34 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+import "dart:async";
+
+typedef Bar<V extends core::Object? = dynamic, contravariant U extends self::A> = (U) → V%;
+abstract class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+  abstract method foo<T extends core::Object? = dynamic>(self::B<self::A::foo::T%> b) → self::A::foo::T%;
+}
+class B<X extends core::Object? = dynamic> extends core::Object {
+  constructor •(self::B::X% x) → self::B<self::B::X%>
+    : super core::Object::•()
+    ;
+}
+class C<Y extends core::Object? = dynamic> extends core::Object {
+  final field (self::D<self::C::Y%>) → FutureOr<self::C::Y%>% bar;
+  constructor •((self::D<self::C::Y%>) → FutureOr<self::C::Y%>% bar) → self::C<self::C::Y%>
+    : self::C::bar = bar, super core::Object::•()
+    ;
+}
+abstract class D<W extends core::Object? = dynamic> extends core::Object implements self::A {
+  synthetic constructor •() → self::D<self::D::W%>
+    : super core::Object::•()
+    ;
+}
+static final field self::C<core::int> baz = new self::C::•<core::int>((self::D<core::int> a) → FutureOr<core::int> {
+  return a.{self::A::foo}<FutureOr<core::int>>(new self::B::•<FutureOr<core::int>>(asy::Future::value<core::int>(0))){(self::B<FutureOr<core::int>>) → FutureOr<core::int>};
+});
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_2.dart.weak.modular.expect b/pkg/front_end/testcases/nnbd/issue48631_2.dart.weak.modular.expect
new file mode 100644
index 0000000..e842794
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_2.dart.weak.modular.expect
@@ -0,0 +1,34 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+import "dart:async";
+
+typedef Bar<V extends core::Object? = dynamic, contravariant U extends self::A> = (U) → V%;
+abstract class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+  abstract method foo<T extends core::Object? = dynamic>(self::B<self::A::foo::T%> b) → self::A::foo::T%;
+}
+class B<X extends core::Object? = dynamic> extends core::Object {
+  constructor •(self::B::X% x) → self::B<self::B::X%>
+    : super core::Object::•()
+    ;
+}
+class C<Y extends core::Object? = dynamic> extends core::Object {
+  final field (self::D<self::C::Y%>) → FutureOr<self::C::Y%>% bar;
+  constructor •((self::D<self::C::Y%>) → FutureOr<self::C::Y%>% bar) → self::C<self::C::Y%>
+    : self::C::bar = bar, super core::Object::•()
+    ;
+}
+abstract class D<W extends core::Object? = dynamic> extends core::Object implements self::A {
+  synthetic constructor •() → self::D<self::D::W%>
+    : super core::Object::•()
+    ;
+}
+static final field self::C<core::int> baz = new self::C::•<core::int>((self::D<core::int> a) → FutureOr<core::int> {
+  return a.{self::A::foo}<FutureOr<core::int>>(new self::B::•<FutureOr<core::int>>(asy::Future::value<core::int>(0))){(self::B<FutureOr<core::int>>) → FutureOr<core::int>};
+});
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_2.dart.weak.outline.expect b/pkg/front_end/testcases/nnbd/issue48631_2.dart.weak.outline.expect
new file mode 100644
index 0000000..4eff2cf
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_2.dart.weak.outline.expect
@@ -0,0 +1,28 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "dart:async";
+
+typedef Bar<V extends core::Object? = dynamic, contravariant U extends self::A> = (U) → V%;
+abstract class A extends core::Object {
+  synthetic constructor •() → self::A
+    ;
+  abstract method foo<T extends core::Object? = dynamic>(self::B<self::A::foo::T%> b) → self::A::foo::T%;
+}
+class B<X extends core::Object? = dynamic> extends core::Object {
+  constructor •(self::B::X% x) → self::B<self::B::X%>
+    ;
+}
+class C<Y extends core::Object? = dynamic> extends core::Object {
+  final field (self::D<self::C::Y%>) → FutureOr<self::C::Y%>% bar;
+  constructor •((self::D<self::C::Y%>) → FutureOr<self::C::Y%>% bar) → self::C<self::C::Y%>
+    ;
+}
+abstract class D<W extends core::Object? = dynamic> extends core::Object implements self::A {
+  synthetic constructor •() → self::D<self::D::W%>
+    ;
+}
+static final field self::C<core::int> baz;
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/nnbd/issue48631_2.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/issue48631_2.dart.weak.transformed.expect
new file mode 100644
index 0000000..e842794
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_2.dart.weak.transformed.expect
@@ -0,0 +1,34 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+import "dart:async";
+
+typedef Bar<V extends core::Object? = dynamic, contravariant U extends self::A> = (U) → V%;
+abstract class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+  abstract method foo<T extends core::Object? = dynamic>(self::B<self::A::foo::T%> b) → self::A::foo::T%;
+}
+class B<X extends core::Object? = dynamic> extends core::Object {
+  constructor •(self::B::X% x) → self::B<self::B::X%>
+    : super core::Object::•()
+    ;
+}
+class C<Y extends core::Object? = dynamic> extends core::Object {
+  final field (self::D<self::C::Y%>) → FutureOr<self::C::Y%>% bar;
+  constructor •((self::D<self::C::Y%>) → FutureOr<self::C::Y%>% bar) → self::C<self::C::Y%>
+    : self::C::bar = bar, super core::Object::•()
+    ;
+}
+abstract class D<W extends core::Object? = dynamic> extends core::Object implements self::A {
+  synthetic constructor •() → self::D<self::D::W%>
+    : super core::Object::•()
+    ;
+}
+static final field self::C<core::int> baz = new self::C::•<core::int>((self::D<core::int> a) → FutureOr<core::int> {
+  return a.{self::A::foo}<FutureOr<core::int>>(new self::B::•<FutureOr<core::int>>(asy::Future::value<core::int>(0))){(self::B<FutureOr<core::int>>) → FutureOr<core::int>};
+});
+static method main() → dynamic {}
diff --git a/pkg/kernel/lib/type_algebra.dart b/pkg/kernel/lib/type_algebra.dart
index af4b1d0..4cfb5cd 100644
--- a/pkg/kernel/lib/type_algebra.dart
+++ b/pkg/kernel/lib/type_algebra.dart
@@ -614,7 +614,22 @@
     int before = useCounter;
     DartType typeArgument = node.typeArgument.accept(this);
     if (useCounter == before) return node;
-    return new FutureOrType(typeArgument, node.declaredNullability);
+
+    // The top-level nullability of a FutureOr should remain the same, with the
+    // exception of the case of [Nullability.undetermined].  In that case it
+    // remains undetermined if the nullability of [typeArgument] is
+    // undetermined, and otherwise it should become [Nullability.nonNullable].
+    Nullability nullability;
+    if (node.declaredNullability == Nullability.undetermined) {
+      if (typeArgument.nullability == Nullability.undetermined) {
+        nullability = Nullability.undetermined;
+      } else {
+        nullability = Nullability.nonNullable;
+      }
+    } else {
+      nullability = node.declaredNullability;
+    }
+    return new FutureOrType(typeArgument, nullability);
   }
 
   @override