Version 2.15.0-210.0.dev

Merge commit 'd83e78c7a37e86b8b70b34cee0777e9b516e314f' into 'dev'
diff --git a/DEPS b/DEPS
index 00f0e0c..c6cd91f 100644
--- a/DEPS
+++ b/DEPS
@@ -85,7 +85,7 @@
   "characters_rev": "6ec389c4dfa8fce14820dc5cbf6e693202e7e052",
   "charcode_rev": "84ea427711e24abf3b832923959caa7dd9a8514b",
   "chrome_rev" : "19997",
-  "cli_util_rev" : "71ba36e2554f7b7717f3f12b5ddd33751a4e3ddd",
+  "cli_util_rev" : "b0adbba89442b2ea6fef39c7a82fe79cb31e1168",
   "clock_rev" : "a494269254ba978e7ef8f192c5f7fec3fc05b9d3",
   "collection_rev": "a4c941ab94044d118b2086a3f261c30377604127",
   "convert_rev": "e063fdca4bebffecbb5e6aa5525995120982d9ce",
diff --git a/pkg/front_end/lib/src/fasta/source/source_class_builder.dart b/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
index e1dfd44..426a2e0 100644
--- a/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
@@ -10,7 +10,12 @@
 import 'package:kernel/src/bounds_checks.dart';
 import 'package:kernel/src/legacy_erasure.dart';
 import 'package:kernel/src/types.dart' show Types;
-import 'package:kernel/type_algebra.dart' show Substitution;
+import 'package:kernel/type_algebra.dart'
+    show
+        FreshTypeParameters,
+        Substitution,
+        getFreshTypeParameters,
+        updateBoundNullabilities;
 import 'package:kernel/type_environment.dart';
 
 import '../builder/builder.dart';
@@ -1287,16 +1292,34 @@
     } else if (declaredFunction?.typeParameters != null) {
       Map<TypeParameter, DartType> substitutionMap =
           <TypeParameter, DartType>{};
+
+      // Since the bound of `interfaceFunction!.parameter[i]` may have changed
+      // during substitution, it can affect the nullabilities of the types in
+      // the substitution map. The first parameter to
+      // [TypeParameterType.forAlphaRenaming] should be updated to account for
+      // the change.
+      List<TypeParameter> interfaceTypeParameters;
+      if (interfaceSubstitution == null) {
+        interfaceTypeParameters = interfaceFunction!.typeParameters;
+      } else {
+        FreshTypeParameters freshTypeParameters =
+            getFreshTypeParameters(interfaceFunction!.typeParameters);
+        interfaceTypeParameters = freshTypeParameters.freshTypeParameters;
+        for (TypeParameter parameter in interfaceTypeParameters) {
+          parameter.bound =
+              interfaceSubstitution.substituteType(parameter.bound);
+        }
+        updateBoundNullabilities(interfaceTypeParameters);
+      }
       for (int i = 0; i < declaredFunction!.typeParameters.length; ++i) {
-        substitutionMap[interfaceFunction!.typeParameters[i]] =
+        substitutionMap[interfaceFunction.typeParameters[i]] =
             new TypeParameterType.forAlphaRenaming(
-                interfaceFunction.typeParameters[i],
-                declaredFunction.typeParameters[i]);
+                interfaceTypeParameters[i], declaredFunction.typeParameters[i]);
       }
       Substitution substitution = Substitution.fromMap(substitutionMap);
       for (int i = 0; i < declaredFunction.typeParameters.length; ++i) {
         TypeParameter declaredParameter = declaredFunction.typeParameters[i];
-        TypeParameter interfaceParameter = interfaceFunction!.typeParameters[i];
+        TypeParameter interfaceParameter = interfaceFunction.typeParameters[i];
         if (!interfaceParameter.isCovariantByClass) {
           DartType declaredBound = declaredParameter.bound;
           DartType interfaceBound = interfaceParameter.bound;
diff --git a/pkg/front_end/test/spell_checking_list_common.txt b/pkg/front_end/test/spell_checking_list_common.txt
index 93046af..cad9efd 100644
--- a/pkg/front_end/test/spell_checking_list_common.txt
+++ b/pkg/front_end/test/spell_checking_list_common.txt
@@ -2415,6 +2415,7 @@
 reasons
 reassign
 rebase
+recalculates
 receive
 receiver
 recent
diff --git a/pkg/front_end/testcases/nnbd/issue47311.dart b/pkg/front_end/testcases/nnbd/issue47311.dart
new file mode 100644
index 0000000..7fef532
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue47311.dart
@@ -0,0 +1,49 @@
+// Copyright (c) 2021, 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';
+
+typedef Baz<T> = T Function(T);
+
+class Foo1<T> {
+  void method<S extends T>(Baz<S> x) {}
+}
+
+class Bar1 implements Foo1<Object> {
+  void method<T extends Object>(Baz<T> x) {}
+}
+
+class Foo2<T> {
+  void method<V extends S, S extends T>(Baz<S> x, Baz<V> y) {}
+}
+
+class Bar2 implements Foo2<Object> {
+  void method<V extends T, T extends Object>(Baz<T> x, Baz<V> y) {}
+}
+
+class Foo3<T> {
+  void method<V extends S, S extends FutureOr<T>>(Baz<S> x, Baz<V> y) {}
+}
+
+class Bar3 implements Foo3<Object> {
+  void method<V extends T, T extends FutureOr<Object>>(Baz<T> x, Baz<V> y) {}
+}
+
+class Foo4<T> {
+  void method<V extends FutureOr<S>, S extends T>(Baz<S> x, Baz<V> y) {}
+}
+
+class Bar4 implements Foo4<Object> {
+  void method<V extends FutureOr<T>, T extends Object>(Baz<T> x, Baz<V> y) {}
+}
+
+class Foo5<T> {
+  void method<V extends FutureOr<S>, S extends FutureOr<T>>(Baz<S> x, Baz<V> y) {}
+}
+
+class Bar5 implements Foo5<Object> {
+  void method<V extends FutureOr<T>, T extends FutureOr<Object>>(Baz<T> x, Baz<V> y) {}
+}
+
+void main() {}
diff --git a/pkg/front_end/testcases/nnbd/issue47311.dart.strong.expect b/pkg/front_end/testcases/nnbd/issue47311.dart.strong.expect
new file mode 100644
index 0000000..baea864
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue47311.dart.strong.expect
@@ -0,0 +1,68 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "dart:async";
+
+typedef Baz<invariant T extends core::Object? = dynamic> = (T%) → T%;
+class Foo1<T extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo1<self::Foo1::T%>
+    : super core::Object::•()
+    ;
+  method method<covariant-by-class S extends self::Foo1::T%>((self::Foo1::method::S%) → self::Foo1::method::S% x) → void {}
+}
+class Bar1 extends core::Object implements self::Foo1<core::Object> {
+  synthetic constructor •() → self::Bar1
+    : super core::Object::•()
+    ;
+  method method<covariant-by-class T extends core::Object>((self::Bar1::method::T) → self::Bar1::method::T x) → void {}
+}
+class Foo2<T extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo2<self::Foo2::T%>
+    : super core::Object::•()
+    ;
+  method method<V extends self::Foo2::method::S% = self::Foo2::T%, covariant-by-class S extends self::Foo2::T%>((self::Foo2::method::S%) → self::Foo2::method::S% x, (self::Foo2::method::V%) → self::Foo2::method::V% y) → void {}
+}
+class Bar2 extends core::Object implements self::Foo2<core::Object> {
+  synthetic constructor •() → self::Bar2
+    : super core::Object::•()
+    ;
+  method method<V extends self::Bar2::method::T = core::Object, covariant-by-class T extends core::Object>((self::Bar2::method::T) → self::Bar2::method::T x, (self::Bar2::method::V) → self::Bar2::method::V y) → void {}
+}
+class Foo3<T extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo3<self::Foo3::T%>
+    : super core::Object::•()
+    ;
+  method method<V extends self::Foo3::method::S% = FutureOr<self::Foo3::T%>, covariant-by-class S extends FutureOr<self::Foo3::T%>>((self::Foo3::method::S%) → self::Foo3::method::S% x, (self::Foo3::method::V%) → self::Foo3::method::V% y) → void {}
+}
+class Bar3 extends core::Object implements self::Foo3<core::Object> {
+  synthetic constructor •() → self::Bar3
+    : super core::Object::•()
+    ;
+  method method<V extends self::Bar3::method::T = FutureOr<core::Object>, covariant-by-class T extends FutureOr<core::Object>>((self::Bar3::method::T) → self::Bar3::method::T x, (self::Bar3::method::V) → self::Bar3::method::V y) → void {}
+}
+class Foo4<T extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo4<self::Foo4::T%>
+    : super core::Object::•()
+    ;
+  method method<V extends FutureOr<self::Foo4::method::S%> = FutureOr<self::Foo4::T%>, covariant-by-class S extends self::Foo4::T%>((self::Foo4::method::S%) → self::Foo4::method::S% x, (self::Foo4::method::V%) → self::Foo4::method::V% y) → void {}
+}
+class Bar4 extends core::Object implements self::Foo4<core::Object> {
+  synthetic constructor •() → self::Bar4
+    : super core::Object::•()
+    ;
+  method method<V extends FutureOr<self::Bar4::method::T> = FutureOr<core::Object>, covariant-by-class T extends core::Object>((self::Bar4::method::T) → self::Bar4::method::T x, (self::Bar4::method::V) → self::Bar4::method::V y) → void {}
+}
+class Foo5<T extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo5<self::Foo5::T%>
+    : super core::Object::•()
+    ;
+  method method<V extends FutureOr<self::Foo5::method::S%> = FutureOr<FutureOr<self::Foo5::T%>>, covariant-by-class S extends FutureOr<self::Foo5::T%>>((self::Foo5::method::S%) → self::Foo5::method::S% x, (self::Foo5::method::V%) → self::Foo5::method::V% y) → void {}
+}
+class Bar5 extends core::Object implements self::Foo5<core::Object> {
+  synthetic constructor •() → self::Bar5
+    : super core::Object::•()
+    ;
+  method method<V extends FutureOr<self::Bar5::method::T> = FutureOr<FutureOr<core::Object>>, covariant-by-class T extends FutureOr<core::Object>>((self::Bar5::method::T) → self::Bar5::method::T x, (self::Bar5::method::V) → self::Bar5::method::V y) → void {}
+}
+static method main() → void {}
diff --git a/pkg/front_end/testcases/nnbd/issue47311.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/issue47311.dart.strong.transformed.expect
new file mode 100644
index 0000000..baea864
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue47311.dart.strong.transformed.expect
@@ -0,0 +1,68 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "dart:async";
+
+typedef Baz<invariant T extends core::Object? = dynamic> = (T%) → T%;
+class Foo1<T extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo1<self::Foo1::T%>
+    : super core::Object::•()
+    ;
+  method method<covariant-by-class S extends self::Foo1::T%>((self::Foo1::method::S%) → self::Foo1::method::S% x) → void {}
+}
+class Bar1 extends core::Object implements self::Foo1<core::Object> {
+  synthetic constructor •() → self::Bar1
+    : super core::Object::•()
+    ;
+  method method<covariant-by-class T extends core::Object>((self::Bar1::method::T) → self::Bar1::method::T x) → void {}
+}
+class Foo2<T extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo2<self::Foo2::T%>
+    : super core::Object::•()
+    ;
+  method method<V extends self::Foo2::method::S% = self::Foo2::T%, covariant-by-class S extends self::Foo2::T%>((self::Foo2::method::S%) → self::Foo2::method::S% x, (self::Foo2::method::V%) → self::Foo2::method::V% y) → void {}
+}
+class Bar2 extends core::Object implements self::Foo2<core::Object> {
+  synthetic constructor •() → self::Bar2
+    : super core::Object::•()
+    ;
+  method method<V extends self::Bar2::method::T = core::Object, covariant-by-class T extends core::Object>((self::Bar2::method::T) → self::Bar2::method::T x, (self::Bar2::method::V) → self::Bar2::method::V y) → void {}
+}
+class Foo3<T extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo3<self::Foo3::T%>
+    : super core::Object::•()
+    ;
+  method method<V extends self::Foo3::method::S% = FutureOr<self::Foo3::T%>, covariant-by-class S extends FutureOr<self::Foo3::T%>>((self::Foo3::method::S%) → self::Foo3::method::S% x, (self::Foo3::method::V%) → self::Foo3::method::V% y) → void {}
+}
+class Bar3 extends core::Object implements self::Foo3<core::Object> {
+  synthetic constructor •() → self::Bar3
+    : super core::Object::•()
+    ;
+  method method<V extends self::Bar3::method::T = FutureOr<core::Object>, covariant-by-class T extends FutureOr<core::Object>>((self::Bar3::method::T) → self::Bar3::method::T x, (self::Bar3::method::V) → self::Bar3::method::V y) → void {}
+}
+class Foo4<T extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo4<self::Foo4::T%>
+    : super core::Object::•()
+    ;
+  method method<V extends FutureOr<self::Foo4::method::S%> = FutureOr<self::Foo4::T%>, covariant-by-class S extends self::Foo4::T%>((self::Foo4::method::S%) → self::Foo4::method::S% x, (self::Foo4::method::V%) → self::Foo4::method::V% y) → void {}
+}
+class Bar4 extends core::Object implements self::Foo4<core::Object> {
+  synthetic constructor •() → self::Bar4
+    : super core::Object::•()
+    ;
+  method method<V extends FutureOr<self::Bar4::method::T> = FutureOr<core::Object>, covariant-by-class T extends core::Object>((self::Bar4::method::T) → self::Bar4::method::T x, (self::Bar4::method::V) → self::Bar4::method::V y) → void {}
+}
+class Foo5<T extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo5<self::Foo5::T%>
+    : super core::Object::•()
+    ;
+  method method<V extends FutureOr<self::Foo5::method::S%> = FutureOr<FutureOr<self::Foo5::T%>>, covariant-by-class S extends FutureOr<self::Foo5::T%>>((self::Foo5::method::S%) → self::Foo5::method::S% x, (self::Foo5::method::V%) → self::Foo5::method::V% y) → void {}
+}
+class Bar5 extends core::Object implements self::Foo5<core::Object> {
+  synthetic constructor •() → self::Bar5
+    : super core::Object::•()
+    ;
+  method method<V extends FutureOr<self::Bar5::method::T> = FutureOr<FutureOr<core::Object>>, covariant-by-class T extends FutureOr<core::Object>>((self::Bar5::method::T) → self::Bar5::method::T x, (self::Bar5::method::V) → self::Bar5::method::V y) → void {}
+}
+static method main() → void {}
diff --git a/pkg/front_end/testcases/nnbd/issue47311.dart.textual_outline.expect b/pkg/front_end/testcases/nnbd/issue47311.dart.textual_outline.expect
new file mode 100644
index 0000000..07a20ea
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue47311.dart.textual_outline.expect
@@ -0,0 +1,47 @@
+import 'dart:async';
+
+typedef Baz<T> = T Function(T);
+
+class Foo1<T> {
+  void method<S extends T>(Baz<S> x) {}
+}
+
+class Bar1 implements Foo1<Object> {
+  void method<T extends Object>(Baz<T> x) {}
+}
+
+class Foo2<T> {
+  void method<V extends S, S extends T>(Baz<S> x, Baz<V> y) {}
+}
+
+class Bar2 implements Foo2<Object> {
+  void method<V extends T, T extends Object>(Baz<T> x, Baz<V> y) {}
+}
+
+class Foo3<T> {
+  void method<V extends S, S extends FutureOr<T>>(Baz<S> x, Baz<V> y) {}
+}
+
+class Bar3 implements Foo3<Object> {
+  void method<V extends T, T extends FutureOr<Object>>(Baz<T> x, Baz<V> y) {}
+}
+
+class Foo4<T> {
+  void method<V extends FutureOr<S>, S extends T>(Baz<S> x, Baz<V> y) {}
+}
+
+class Bar4 implements Foo4<Object> {
+  void method<V extends FutureOr<T>, T extends Object>(Baz<T> x, Baz<V> y) {}
+}
+
+class Foo5<T> {
+  void method<V extends FutureOr<S>, S extends FutureOr<T>>(
+      Baz<S> x, Baz<V> y) {}
+}
+
+class Bar5 implements Foo5<Object> {
+  void method<V extends FutureOr<T>, T extends FutureOr<Object>>(
+      Baz<T> x, Baz<V> y) {}
+}
+
+void main() {}
diff --git a/pkg/front_end/testcases/nnbd/issue47311.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/nnbd/issue47311.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..fbbf978
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue47311.dart.textual_outline_modelled.expect
@@ -0,0 +1,46 @@
+import 'dart:async';
+
+class Bar1 implements Foo1<Object> {
+  void method<T extends Object>(Baz<T> x) {}
+}
+
+class Bar2 implements Foo2<Object> {
+  void method<V extends T, T extends Object>(Baz<T> x, Baz<V> y) {}
+}
+
+class Bar3 implements Foo3<Object> {
+  void method<V extends T, T extends FutureOr<Object>>(Baz<T> x, Baz<V> y) {}
+}
+
+class Bar4 implements Foo4<Object> {
+  void method<V extends FutureOr<T>, T extends Object>(Baz<T> x, Baz<V> y) {}
+}
+
+class Bar5 implements Foo5<Object> {
+  void method<V extends FutureOr<T>, T extends FutureOr<Object>>(
+      Baz<T> x, Baz<V> y) {}
+}
+
+class Foo1<T> {
+  void method<S extends T>(Baz<S> x) {}
+}
+
+class Foo2<T> {
+  void method<V extends S, S extends T>(Baz<S> x, Baz<V> y) {}
+}
+
+class Foo3<T> {
+  void method<V extends S, S extends FutureOr<T>>(Baz<S> x, Baz<V> y) {}
+}
+
+class Foo4<T> {
+  void method<V extends FutureOr<S>, S extends T>(Baz<S> x, Baz<V> y) {}
+}
+
+class Foo5<T> {
+  void method<V extends FutureOr<S>, S extends FutureOr<T>>(
+      Baz<S> x, Baz<V> y) {}
+}
+
+typedef Baz<T> = T Function(T);
+void main() {}
diff --git a/pkg/front_end/testcases/nnbd/issue47311.dart.weak.expect b/pkg/front_end/testcases/nnbd/issue47311.dart.weak.expect
new file mode 100644
index 0000000..baea864
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue47311.dart.weak.expect
@@ -0,0 +1,68 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "dart:async";
+
+typedef Baz<invariant T extends core::Object? = dynamic> = (T%) → T%;
+class Foo1<T extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo1<self::Foo1::T%>
+    : super core::Object::•()
+    ;
+  method method<covariant-by-class S extends self::Foo1::T%>((self::Foo1::method::S%) → self::Foo1::method::S% x) → void {}
+}
+class Bar1 extends core::Object implements self::Foo1<core::Object> {
+  synthetic constructor •() → self::Bar1
+    : super core::Object::•()
+    ;
+  method method<covariant-by-class T extends core::Object>((self::Bar1::method::T) → self::Bar1::method::T x) → void {}
+}
+class Foo2<T extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo2<self::Foo2::T%>
+    : super core::Object::•()
+    ;
+  method method<V extends self::Foo2::method::S% = self::Foo2::T%, covariant-by-class S extends self::Foo2::T%>((self::Foo2::method::S%) → self::Foo2::method::S% x, (self::Foo2::method::V%) → self::Foo2::method::V% y) → void {}
+}
+class Bar2 extends core::Object implements self::Foo2<core::Object> {
+  synthetic constructor •() → self::Bar2
+    : super core::Object::•()
+    ;
+  method method<V extends self::Bar2::method::T = core::Object, covariant-by-class T extends core::Object>((self::Bar2::method::T) → self::Bar2::method::T x, (self::Bar2::method::V) → self::Bar2::method::V y) → void {}
+}
+class Foo3<T extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo3<self::Foo3::T%>
+    : super core::Object::•()
+    ;
+  method method<V extends self::Foo3::method::S% = FutureOr<self::Foo3::T%>, covariant-by-class S extends FutureOr<self::Foo3::T%>>((self::Foo3::method::S%) → self::Foo3::method::S% x, (self::Foo3::method::V%) → self::Foo3::method::V% y) → void {}
+}
+class Bar3 extends core::Object implements self::Foo3<core::Object> {
+  synthetic constructor •() → self::Bar3
+    : super core::Object::•()
+    ;
+  method method<V extends self::Bar3::method::T = FutureOr<core::Object>, covariant-by-class T extends FutureOr<core::Object>>((self::Bar3::method::T) → self::Bar3::method::T x, (self::Bar3::method::V) → self::Bar3::method::V y) → void {}
+}
+class Foo4<T extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo4<self::Foo4::T%>
+    : super core::Object::•()
+    ;
+  method method<V extends FutureOr<self::Foo4::method::S%> = FutureOr<self::Foo4::T%>, covariant-by-class S extends self::Foo4::T%>((self::Foo4::method::S%) → self::Foo4::method::S% x, (self::Foo4::method::V%) → self::Foo4::method::V% y) → void {}
+}
+class Bar4 extends core::Object implements self::Foo4<core::Object> {
+  synthetic constructor •() → self::Bar4
+    : super core::Object::•()
+    ;
+  method method<V extends FutureOr<self::Bar4::method::T> = FutureOr<core::Object>, covariant-by-class T extends core::Object>((self::Bar4::method::T) → self::Bar4::method::T x, (self::Bar4::method::V) → self::Bar4::method::V y) → void {}
+}
+class Foo5<T extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo5<self::Foo5::T%>
+    : super core::Object::•()
+    ;
+  method method<V extends FutureOr<self::Foo5::method::S%> = FutureOr<FutureOr<self::Foo5::T%>>, covariant-by-class S extends FutureOr<self::Foo5::T%>>((self::Foo5::method::S%) → self::Foo5::method::S% x, (self::Foo5::method::V%) → self::Foo5::method::V% y) → void {}
+}
+class Bar5 extends core::Object implements self::Foo5<core::Object> {
+  synthetic constructor •() → self::Bar5
+    : super core::Object::•()
+    ;
+  method method<V extends FutureOr<self::Bar5::method::T> = FutureOr<FutureOr<core::Object>>, covariant-by-class T extends FutureOr<core::Object>>((self::Bar5::method::T) → self::Bar5::method::T x, (self::Bar5::method::V) → self::Bar5::method::V y) → void {}
+}
+static method main() → void {}
diff --git a/pkg/front_end/testcases/nnbd/issue47311.dart.weak.outline.expect b/pkg/front_end/testcases/nnbd/issue47311.dart.weak.outline.expect
new file mode 100644
index 0000000..7bf55ff
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue47311.dart.weak.outline.expect
@@ -0,0 +1,69 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "dart:async";
+
+typedef Baz<invariant T extends core::Object? = dynamic> = (T%) → T%;
+class Foo1<T extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo1<self::Foo1::T%>
+    ;
+  method method<covariant-by-class S extends self::Foo1::T%>((self::Foo1::method::S%) → self::Foo1::method::S% x) → void
+    ;
+}
+class Bar1 extends core::Object implements self::Foo1<core::Object> {
+  synthetic constructor •() → self::Bar1
+    ;
+  method method<covariant-by-class T extends core::Object>((self::Bar1::method::T) → self::Bar1::method::T x) → void
+    ;
+}
+class Foo2<T extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo2<self::Foo2::T%>
+    ;
+  method method<V extends self::Foo2::method::S% = self::Foo2::T%, covariant-by-class S extends self::Foo2::T%>((self::Foo2::method::S%) → self::Foo2::method::S% x, (self::Foo2::method::V%) → self::Foo2::method::V% y) → void
+    ;
+}
+class Bar2 extends core::Object implements self::Foo2<core::Object> {
+  synthetic constructor •() → self::Bar2
+    ;
+  method method<V extends self::Bar2::method::T = core::Object, covariant-by-class T extends core::Object>((self::Bar2::method::T) → self::Bar2::method::T x, (self::Bar2::method::V) → self::Bar2::method::V y) → void
+    ;
+}
+class Foo3<T extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo3<self::Foo3::T%>
+    ;
+  method method<V extends self::Foo3::method::S% = FutureOr<self::Foo3::T%>, covariant-by-class S extends FutureOr<self::Foo3::T%>>((self::Foo3::method::S%) → self::Foo3::method::S% x, (self::Foo3::method::V%) → self::Foo3::method::V% y) → void
+    ;
+}
+class Bar3 extends core::Object implements self::Foo3<core::Object> {
+  synthetic constructor •() → self::Bar3
+    ;
+  method method<V extends self::Bar3::method::T = FutureOr<core::Object>, covariant-by-class T extends FutureOr<core::Object>>((self::Bar3::method::T) → self::Bar3::method::T x, (self::Bar3::method::V) → self::Bar3::method::V y) → void
+    ;
+}
+class Foo4<T extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo4<self::Foo4::T%>
+    ;
+  method method<V extends FutureOr<self::Foo4::method::S%> = FutureOr<self::Foo4::T%>, covariant-by-class S extends self::Foo4::T%>((self::Foo4::method::S%) → self::Foo4::method::S% x, (self::Foo4::method::V%) → self::Foo4::method::V% y) → void
+    ;
+}
+class Bar4 extends core::Object implements self::Foo4<core::Object> {
+  synthetic constructor •() → self::Bar4
+    ;
+  method method<V extends FutureOr<self::Bar4::method::T> = FutureOr<core::Object>, covariant-by-class T extends core::Object>((self::Bar4::method::T) → self::Bar4::method::T x, (self::Bar4::method::V) → self::Bar4::method::V y) → void
+    ;
+}
+class Foo5<T extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo5<self::Foo5::T%>
+    ;
+  method method<V extends FutureOr<self::Foo5::method::S%> = FutureOr<FutureOr<self::Foo5::T%>>, covariant-by-class S extends FutureOr<self::Foo5::T%>>((self::Foo5::method::S%) → self::Foo5::method::S% x, (self::Foo5::method::V%) → self::Foo5::method::V% y) → void
+    ;
+}
+class Bar5 extends core::Object implements self::Foo5<core::Object> {
+  synthetic constructor •() → self::Bar5
+    ;
+  method method<V extends FutureOr<self::Bar5::method::T> = FutureOr<FutureOr<core::Object>>, covariant-by-class T extends FutureOr<core::Object>>((self::Bar5::method::T) → self::Bar5::method::T x, (self::Bar5::method::V) → self::Bar5::method::V y) → void
+    ;
+}
+static method main() → void
+  ;
diff --git a/pkg/front_end/testcases/nnbd/issue47311.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/issue47311.dart.weak.transformed.expect
new file mode 100644
index 0000000..baea864
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue47311.dart.weak.transformed.expect
@@ -0,0 +1,68 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "dart:async";
+
+typedef Baz<invariant T extends core::Object? = dynamic> = (T%) → T%;
+class Foo1<T extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo1<self::Foo1::T%>
+    : super core::Object::•()
+    ;
+  method method<covariant-by-class S extends self::Foo1::T%>((self::Foo1::method::S%) → self::Foo1::method::S% x) → void {}
+}
+class Bar1 extends core::Object implements self::Foo1<core::Object> {
+  synthetic constructor •() → self::Bar1
+    : super core::Object::•()
+    ;
+  method method<covariant-by-class T extends core::Object>((self::Bar1::method::T) → self::Bar1::method::T x) → void {}
+}
+class Foo2<T extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo2<self::Foo2::T%>
+    : super core::Object::•()
+    ;
+  method method<V extends self::Foo2::method::S% = self::Foo2::T%, covariant-by-class S extends self::Foo2::T%>((self::Foo2::method::S%) → self::Foo2::method::S% x, (self::Foo2::method::V%) → self::Foo2::method::V% y) → void {}
+}
+class Bar2 extends core::Object implements self::Foo2<core::Object> {
+  synthetic constructor •() → self::Bar2
+    : super core::Object::•()
+    ;
+  method method<V extends self::Bar2::method::T = core::Object, covariant-by-class T extends core::Object>((self::Bar2::method::T) → self::Bar2::method::T x, (self::Bar2::method::V) → self::Bar2::method::V y) → void {}
+}
+class Foo3<T extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo3<self::Foo3::T%>
+    : super core::Object::•()
+    ;
+  method method<V extends self::Foo3::method::S% = FutureOr<self::Foo3::T%>, covariant-by-class S extends FutureOr<self::Foo3::T%>>((self::Foo3::method::S%) → self::Foo3::method::S% x, (self::Foo3::method::V%) → self::Foo3::method::V% y) → void {}
+}
+class Bar3 extends core::Object implements self::Foo3<core::Object> {
+  synthetic constructor •() → self::Bar3
+    : super core::Object::•()
+    ;
+  method method<V extends self::Bar3::method::T = FutureOr<core::Object>, covariant-by-class T extends FutureOr<core::Object>>((self::Bar3::method::T) → self::Bar3::method::T x, (self::Bar3::method::V) → self::Bar3::method::V y) → void {}
+}
+class Foo4<T extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo4<self::Foo4::T%>
+    : super core::Object::•()
+    ;
+  method method<V extends FutureOr<self::Foo4::method::S%> = FutureOr<self::Foo4::T%>, covariant-by-class S extends self::Foo4::T%>((self::Foo4::method::S%) → self::Foo4::method::S% x, (self::Foo4::method::V%) → self::Foo4::method::V% y) → void {}
+}
+class Bar4 extends core::Object implements self::Foo4<core::Object> {
+  synthetic constructor •() → self::Bar4
+    : super core::Object::•()
+    ;
+  method method<V extends FutureOr<self::Bar4::method::T> = FutureOr<core::Object>, covariant-by-class T extends core::Object>((self::Bar4::method::T) → self::Bar4::method::T x, (self::Bar4::method::V) → self::Bar4::method::V y) → void {}
+}
+class Foo5<T extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo5<self::Foo5::T%>
+    : super core::Object::•()
+    ;
+  method method<V extends FutureOr<self::Foo5::method::S%> = FutureOr<FutureOr<self::Foo5::T%>>, covariant-by-class S extends FutureOr<self::Foo5::T%>>((self::Foo5::method::S%) → self::Foo5::method::S% x, (self::Foo5::method::V%) → self::Foo5::method::V% y) → void {}
+}
+class Bar5 extends core::Object implements self::Foo5<core::Object> {
+  synthetic constructor •() → self::Bar5
+    : super core::Object::•()
+    ;
+  method method<V extends FutureOr<self::Bar5::method::T> = FutureOr<FutureOr<core::Object>>, covariant-by-class T extends FutureOr<core::Object>>((self::Bar5::method::T) → self::Bar5::method::T x, (self::Bar5::method::V) → self::Bar5::method::V y) → void {}
+}
+static method main() → void {}
diff --git a/pkg/kernel/lib/type_algebra.dart b/pkg/kernel/lib/type_algebra.dart
index f836d1e..af4b1d0 100644
--- a/pkg/kernel/lib/type_algebra.dart
+++ b/pkg/kernel/lib/type_algebra.dart
@@ -1422,3 +1422,60 @@
       ? TypeParameterType.computeNullabilityFromBound(parameter)
       : Nullability.legacy;
 }
+
+/// Recalculates and updates nullabilities of the bounds in [typeParameters].
+///
+/// The procedure is intended to be used on type parameters that are in the
+/// scope of another declaration with type parameters. After a substitution
+/// involving the outer type parameters is performed, some potentially nullable
+/// bounds of the inner parameters can change to non-nullable. Since type
+/// parameters can depend on each other, the occurrences of those with changed
+/// nullabilities need to be updated in the bounds of the entire type parameter
+/// set.
+void updateBoundNullabilities(List<TypeParameter> typeParameters) {
+  if (typeParameters.isEmpty) return;
+  List<bool> visited =
+      new List<bool>.filled(typeParameters.length, false, growable: false);
+  for (int parameterIndex = 0;
+      parameterIndex < typeParameters.length;
+      parameterIndex++) {
+    _updateBoundNullabilities(typeParameters, visited, parameterIndex);
+  }
+}
+
+void _updateBoundNullabilities(
+    List<TypeParameter> typeParameters, List<bool> visited, int startIndex) {
+  if (visited[startIndex]) return;
+  visited[startIndex] = true;
+
+  TypeParameter parameter = typeParameters[startIndex];
+  DartType bound = parameter.bound;
+  while (bound is FutureOrType) {
+    bound = bound.typeArgument;
+  }
+  if (bound is TypeParameterType) {
+    int nextIndex = typeParameters.indexOf(bound.parameter);
+    if (nextIndex != -1) {
+      _updateBoundNullabilities(typeParameters, visited, nextIndex);
+      Nullability updatedNullability =
+          TypeParameterType.computeNullabilityFromBound(
+              typeParameters[nextIndex]);
+      if (bound.declaredNullability != updatedNullability) {
+        parameter.bound = _updateNestedFutureOrNullability(
+            parameter.bound, updatedNullability);
+      }
+    }
+  }
+}
+
+DartType _updateNestedFutureOrNullability(
+    DartType typeToUpdate, Nullability updatedNullability) {
+  if (typeToUpdate is FutureOrType) {
+    return new FutureOrType(
+        _updateNestedFutureOrNullability(
+            typeToUpdate.typeArgument, updatedNullability),
+        typeToUpdate.declaredNullability);
+  } else {
+    return typeToUpdate.withDeclaredNullability(updatedNullability);
+  }
+}
diff --git a/runtime/tests/vm/dart/rematerialize_unboxed_double_field_test.dart b/runtime/tests/vm/dart/rematerialize_unboxed_double_field_test.dart
index cf764a2..b025120 100644
--- a/runtime/tests/vm/dart/rematerialize_unboxed_double_field_test.dart
+++ b/runtime/tests/vm/dart/rematerialize_unboxed_double_field_test.dart
@@ -2,7 +2,7 @@
 // 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.
 //
-// VMOptions=--deterministic --optimization-counter-threshold=10 --unbox-numeric-fields
+// VMOptions=--deterministic --optimization-counter-threshold=100 --unbox-numeric-fields
 
 import 'package:expect/expect.dart';
 
@@ -29,9 +29,9 @@
 
 void main(List<String> args) {
   var c = C();
-  for (var i = 0; i < 100; i++) c.d = 2.0;
+  for (var i = 0; i < 200; i++) c.d = 2.0;
 
-  for (var i = 0; i < 100; i++) {
+  for (var i = 0; i < 200; i++) {
     foo(NoopSink());
   }
 
diff --git a/runtime/tests/vm/dart_2/rematerialize_unboxed_double_field_test.dart b/runtime/tests/vm/dart_2/rematerialize_unboxed_double_field_test.dart
index d8e037f..b15d7ff 100644
--- a/runtime/tests/vm/dart_2/rematerialize_unboxed_double_field_test.dart
+++ b/runtime/tests/vm/dart_2/rematerialize_unboxed_double_field_test.dart
@@ -2,7 +2,7 @@
 // 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.
 //
-// VMOptions=--deterministic --optimization-counter-threshold=10 --unbox-numeric-fields
+// VMOptions=--deterministic --optimization-counter-threshold=100 --unbox-numeric-fields
 
 // @dart = 2.9
 
@@ -31,9 +31,9 @@
 
 void main(List<String> args) {
   var c = C();
-  for (var i = 0; i < 100; i++) c.d = 2.0;
+  for (var i = 0; i < 200; i++) c.d = 2.0;
 
-  for (var i = 0; i < 100; i++) {
+  for (var i = 0; i < 200; i++) {
     foo(NoopSink());
   }
 
diff --git a/tools/VERSION b/tools/VERSION
index 05bc800..2a7370b 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 209
+PRERELEASE 210
 PRERELEASE_PATCH 0
\ No newline at end of file