Version 2.18.0-210.0.dev

Merge commit '0d702207f53ede3bc920bf8c0e33f494d1b6bd47' into 'dev'
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index 105a6f1..6bfba8e 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -4211,7 +4211,7 @@
       result = name.buildTypeWithResolvedArguments(
           libraryBuilder.nullableBuilderIfTrue(isMarkedAsNullable), arguments,
           allowPotentiallyConstantType: allowPotentiallyConstantType,
-          forTypeLiteral: false);
+          forTypeCanonicalization: constantContext != ConstantContext.none);
       // ignore: unnecessary_null_comparison
       if (result == null) {
         unhandled("null", "result", beginToken.charOffset, uri);
diff --git a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
index f295cdd..33ca2e8 100644
--- a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
@@ -256,7 +256,7 @@
   TypeBuilder buildTypeWithResolvedArguments(
       NullabilityBuilder nullabilityBuilder, List<TypeBuilder>? arguments,
       {required bool allowPotentiallyConstantType,
-      required bool forTypeLiteral}) {
+      required bool forTypeCanonicalization}) {
     Message message = templateNotAType.withArguments(token.lexeme);
     _helper.libraryBuilder
         .addProblem(message, fileOffset, lengthForToken(token), _uri);
@@ -2931,13 +2931,13 @@
   TypeBuilder buildTypeWithResolvedArguments(
       NullabilityBuilder nullabilityBuilder, List<TypeBuilder>? arguments,
       {required bool allowPotentiallyConstantType,
-      required bool forTypeLiteral}) {
+      required bool forTypeCanonicalization}) {
     String name = "${prefixGenerator._plainNameForRead}."
         "${suffixGenerator._plainNameForRead}";
     TypeBuilder type = suffixGenerator.buildTypeWithResolvedArguments(
         nullabilityBuilder, arguments,
         allowPotentiallyConstantType: allowPotentiallyConstantType,
-        forTypeLiteral: forTypeLiteral);
+        forTypeCanonicalization: forTypeCanonicalization);
     LocatedMessage message;
     if (type is NamedTypeBuilder &&
         type.declaration is InvalidTypeDeclarationBuilder) {
@@ -3058,7 +3058,7 @@
   TypeBuilder buildTypeWithResolvedArguments(
       NullabilityBuilder nullabilityBuilder, List<TypeBuilder>? arguments,
       {required bool allowPotentiallyConstantType,
-      required bool forTypeLiteral}) {
+      required bool forTypeCanonicalization}) {
     if (declaration is OmittedTypeDeclarationBuilder) {
       // TODO(johnniwinther): Report errors when this occurs in-body or with
       // type arguments.
@@ -3071,7 +3071,7 @@
         fileUri: _uri,
         charOffset: fileOffset,
         instanceTypeVariableAccess: _helper.instanceTypeVariableAccessState,
-        forTypeLiteral: forTypeLiteral)
+        forTypeLiteral: forTypeCanonicalization)
       ..bind(_helper.libraryBuilder, declaration);
   }
 
@@ -3118,7 +3118,8 @@
             _helper.buildDartType(
                 buildTypeWithResolvedArguments(
                     _helper.libraryBuilder.nonNullableBuilder, typeArguments,
-                    allowPotentiallyConstantType: true, forTypeLiteral: true),
+                    allowPotentiallyConstantType: true,
+                    forTypeCanonicalization: true),
                 TypeUse.typeLiteral,
                 allowPotentiallyConstantType:
                     _helper.libraryFeatures.constructorTearoffs.isEnabled));
@@ -4194,7 +4195,7 @@
   TypeBuilder buildTypeWithResolvedArguments(
       NullabilityBuilder nullabilityBuilder, List<TypeBuilder>? arguments,
       {required bool allowPotentiallyConstantType,
-      required bool forTypeLiteral}) {
+      required bool forTypeCanonicalization}) {
     Template<Message Function(String, String)> template = isUnresolved
         ? templateUnresolvedPrefixInTypeAnnotation
         : templateNotAPrefixInTypeAnnotation;
@@ -4314,7 +4315,7 @@
   TypeBuilder buildTypeWithResolvedArguments(
       NullabilityBuilder nullabilityBuilder, List<TypeBuilder>? arguments,
       {required bool allowPotentiallyConstantType,
-      required bool forTypeLiteral}) {
+      required bool forTypeCanonicalization}) {
     _helper.libraryBuilder.addProblem(message, fileOffset, noLength, _uri);
     return new NamedTypeBuilder.forInvalidType(token.lexeme, nullabilityBuilder,
         message.withLocation(_uri, fileOffset, noLength));
diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt
index e5db1cd..78c6f01 100644
--- a/pkg/front_end/test/spell_checking_list_tests.txt
+++ b/pkg/front_end/test/spell_checking_list_tests.txt
@@ -463,6 +463,7 @@
 ops
 optimal
 oracle
+ot
 outbound
 outliers
 overhead
diff --git a/pkg/front_end/testcases/general/issue46518.dart b/pkg/front_end/testcases/general/issue46518.dart
new file mode 100644
index 0000000..788c568
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue46518.dart
@@ -0,0 +1,22 @@
+// 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.
+
+// @dart=2.9
+import "./issue46518_lib.dart";
+
+const optedOutToken = OT<NullableIntF>();
+
+class CheckIdentical {
+  const CheckIdentical(x, y) : assert(identical(x, y));
+}
+
+testOptedOut() {
+  const localToken = OT<NullableIntF>();
+  const CheckIdentical(optedInToken, localToken);
+  const CheckIdentical(optedOutToken, localToken);
+}
+
+const testCrossLibraries = const CheckIdentical(optedInToken, optedOutToken);
+
+main() {}
diff --git a/pkg/front_end/testcases/general/issue46518.dart.textual_outline.expect b/pkg/front_end/testcases/general/issue46518.dart.textual_outline.expect
new file mode 100644
index 0000000..f3cfe6d
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue46518.dart.textual_outline.expect
@@ -0,0 +1,12 @@
+// @dart = 2.9
+import "./issue46518_lib.dart";
+
+const optedOutToken = OT<NullableIntF>();
+
+class CheckIdentical {
+  const CheckIdentical(x, y) : assert(identical(x, y));
+}
+
+testOptedOut() {}
+const testCrossLibraries = const CheckIdentical(optedInToken, optedOutToken);
+main() {}
diff --git a/pkg/front_end/testcases/general/issue46518.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/issue46518.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..6505ed0
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue46518.dart.textual_outline_modelled.expect
@@ -0,0 +1,11 @@
+// @dart = 2.9
+import "./issue46518_lib.dart";
+
+class CheckIdentical {
+  const CheckIdentical(x, y) : assert(identical(x, y));
+}
+
+const optedOutToken = OT<NullableIntF>();
+const testCrossLibraries = const CheckIdentical(optedInToken, optedOutToken);
+main() {}
+testOptedOut() {}
diff --git a/pkg/front_end/testcases/general/issue46518.dart.weak.expect b/pkg/front_end/testcases/general/issue46518.dart.weak.expect
new file mode 100644
index 0000000..2c9870a
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue46518.dart.weak.expect
@@ -0,0 +1,72 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "issue46518_lib.dart" as iss;
+import "dart:async" as asy;
+
+import "org-dartlang-testcase:///issue46518_lib.dart";
+
+class CheckIdentical extends core::Object /*hasConstConstructor*/  {
+  const constructor •(dynamic x, dynamic y) → self::CheckIdentical*
+    : assert(core::identical(x, y)), super core::Object::•()
+    ;
+  abstract member-signature get _identityHashCode() → core::int*; -> core::Object::_identityHashCode
+  abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*; -> core::Object::_instanceOf
+  abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOf
+  abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOfTrue
+  abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOfFalse
+  abstract member-signature operator ==(dynamic other) → core::bool*; -> core::Object::==
+  abstract member-signature get hashCode() → core::int*; -> core::Object::hashCode
+  abstract member-signature method toString() → core::String*; -> core::Object::toString
+  abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic; -> core::Object::noSuchMethod
+  abstract member-signature get runtimeType() → core::Type*; -> core::Object::runtimeType
+}
+static const field iss::OT<() →* asy::Future<core::int*>*>* optedOutToken = #C1;
+static const field self::CheckIdentical* testCrossLibraries = #C2;
+static method testOptedOut() → dynamic {
+  #C2;
+  #C2;
+}
+static method main() → dynamic {}
+
+library /*isNonNullableByDefault*/;
+import self as iss;
+import "dart:async" as asy;
+import "dart:core" as core;
+import "issue46518.dart" as self;
+
+import "org-dartlang-testcase:///issue46518.dart";
+
+typedef NullableIntF = () → asy::Future<core::int?>;
+class OT<T extends core::Object> extends core::Object /*hasConstConstructor*/  {
+  const constructor •() → iss::OT<iss::OT::T>
+    : super core::Object::•()
+    ;
+  @#C3
+  method toString() → core::String {
+    return "${this.{core::Object::runtimeType}{core::Type}}";
+  }
+}
+static const field iss::OT<() → asy::Future<core::int?>> optedInToken = #C1;
+static method testOptedIn() → dynamic {
+  #C2;
+  #C2;
+}
+
+constants  {
+  #C1 = iss::OT<() →* asy::Future<core::int?>*> {}
+  #C2 = self::CheckIdentical {}
+  #C3 = core::_Override {}
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///issue46518.dart:
+- OT. (from org-dartlang-testcase:///issue46518_lib.dart:10:9)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
+- CheckIdentical. (from org-dartlang-testcase:///issue46518.dart:11:9)
+
+org-dartlang-testcase:///issue46518_lib.dart:
+- OT. (from org-dartlang-testcase:///issue46518_lib.dart:10:9)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
+- CheckIdentical. (from org-dartlang-testcase:///issue46518.dart:11:9)
diff --git a/pkg/front_end/testcases/general/issue46518.dart.weak.modular.expect b/pkg/front_end/testcases/general/issue46518.dart.weak.modular.expect
new file mode 100644
index 0000000..2c9870a
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue46518.dart.weak.modular.expect
@@ -0,0 +1,72 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "issue46518_lib.dart" as iss;
+import "dart:async" as asy;
+
+import "org-dartlang-testcase:///issue46518_lib.dart";
+
+class CheckIdentical extends core::Object /*hasConstConstructor*/  {
+  const constructor •(dynamic x, dynamic y) → self::CheckIdentical*
+    : assert(core::identical(x, y)), super core::Object::•()
+    ;
+  abstract member-signature get _identityHashCode() → core::int*; -> core::Object::_identityHashCode
+  abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*; -> core::Object::_instanceOf
+  abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOf
+  abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOfTrue
+  abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOfFalse
+  abstract member-signature operator ==(dynamic other) → core::bool*; -> core::Object::==
+  abstract member-signature get hashCode() → core::int*; -> core::Object::hashCode
+  abstract member-signature method toString() → core::String*; -> core::Object::toString
+  abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic; -> core::Object::noSuchMethod
+  abstract member-signature get runtimeType() → core::Type*; -> core::Object::runtimeType
+}
+static const field iss::OT<() →* asy::Future<core::int*>*>* optedOutToken = #C1;
+static const field self::CheckIdentical* testCrossLibraries = #C2;
+static method testOptedOut() → dynamic {
+  #C2;
+  #C2;
+}
+static method main() → dynamic {}
+
+library /*isNonNullableByDefault*/;
+import self as iss;
+import "dart:async" as asy;
+import "dart:core" as core;
+import "issue46518.dart" as self;
+
+import "org-dartlang-testcase:///issue46518.dart";
+
+typedef NullableIntF = () → asy::Future<core::int?>;
+class OT<T extends core::Object> extends core::Object /*hasConstConstructor*/  {
+  const constructor •() → iss::OT<iss::OT::T>
+    : super core::Object::•()
+    ;
+  @#C3
+  method toString() → core::String {
+    return "${this.{core::Object::runtimeType}{core::Type}}";
+  }
+}
+static const field iss::OT<() → asy::Future<core::int?>> optedInToken = #C1;
+static method testOptedIn() → dynamic {
+  #C2;
+  #C2;
+}
+
+constants  {
+  #C1 = iss::OT<() →* asy::Future<core::int?>*> {}
+  #C2 = self::CheckIdentical {}
+  #C3 = core::_Override {}
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///issue46518.dart:
+- OT. (from org-dartlang-testcase:///issue46518_lib.dart:10:9)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
+- CheckIdentical. (from org-dartlang-testcase:///issue46518.dart:11:9)
+
+org-dartlang-testcase:///issue46518_lib.dart:
+- OT. (from org-dartlang-testcase:///issue46518_lib.dart:10:9)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
+- CheckIdentical. (from org-dartlang-testcase:///issue46518.dart:11:9)
diff --git a/pkg/front_end/testcases/general/issue46518.dart.weak.outline.expect b/pkg/front_end/testcases/general/issue46518.dart.weak.outline.expect
new file mode 100644
index 0000000..18423e5
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue46518.dart.weak.outline.expect
@@ -0,0 +1,57 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "issue46518_lib.dart" as iss;
+import "dart:async" as asy;
+
+import "org-dartlang-testcase:///issue46518_lib.dart";
+
+class CheckIdentical extends core::Object /*hasConstConstructor*/  {
+  const constructor •(dynamic x, dynamic y) → self::CheckIdentical*
+    : assert(core::identical(x, y)), super core::Object::•()
+    ;
+  abstract member-signature get _identityHashCode() → core::int*; -> core::Object::_identityHashCode
+  abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*; -> core::Object::_instanceOf
+  abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOf
+  abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOfTrue
+  abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOfFalse
+  abstract member-signature operator ==(dynamic other) → core::bool*; -> core::Object::==
+  abstract member-signature get hashCode() → core::int*; -> core::Object::hashCode
+  abstract member-signature method toString() → core::String*; -> core::Object::toString
+  abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic; -> core::Object::noSuchMethod
+  abstract member-signature get runtimeType() → core::Type*; -> core::Object::runtimeType
+}
+static const field iss::OT<() →* asy::Future<core::int*>*>* optedOutToken = const iss::OT::•<() →* asy::Future<core::int?>>();
+static const field self::CheckIdentical* testCrossLibraries = const self::CheckIdentical::•(iss::optedInToken, self::optedOutToken);
+static method testOptedOut() → dynamic
+  ;
+static method main() → dynamic
+  ;
+
+library /*isNonNullableByDefault*/;
+import self as iss;
+import "dart:async" as asy;
+import "dart:core" as core;
+
+import "org-dartlang-testcase:///issue46518.dart";
+
+typedef NullableIntF = () → asy::Future<core::int?>;
+class OT<T extends core::Object> extends core::Object /*hasConstConstructor*/  {
+  const constructor •() → iss::OT<iss::OT::T>
+    : super core::Object::•()
+    ;
+  @core::override
+  method toString() → core::String
+    ;
+}
+static const field iss::OT<() → asy::Future<core::int?>> optedInToken = const iss::OT::•<() → asy::Future<core::int?>>();
+static method testOptedIn() → dynamic
+  ;
+
+
+Extra constant evaluation status:
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///issue46518.dart:8:23 -> InstanceConstant(const OT<Future<int?>* Function()*>{})
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///issue46518.dart:20:34 -> InstanceConstant(const CheckIdentical{})
+Evaluated: StaticGet @ org-dartlang-testcase:///issue46518_lib.dart:12:4 -> InstanceConstant(const _Override{})
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///issue46518_lib.dart:24:22 -> InstanceConstant(const OT<Future<int?>* Function()*>{})
+Extra constant evaluation: evaluated: 7, effectively constant: 4
diff --git a/pkg/front_end/testcases/general/issue46518.dart.weak.transformed.expect b/pkg/front_end/testcases/general/issue46518.dart.weak.transformed.expect
new file mode 100644
index 0000000..2c9870a
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue46518.dart.weak.transformed.expect
@@ -0,0 +1,72 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "issue46518_lib.dart" as iss;
+import "dart:async" as asy;
+
+import "org-dartlang-testcase:///issue46518_lib.dart";
+
+class CheckIdentical extends core::Object /*hasConstConstructor*/  {
+  const constructor •(dynamic x, dynamic y) → self::CheckIdentical*
+    : assert(core::identical(x, y)), super core::Object::•()
+    ;
+  abstract member-signature get _identityHashCode() → core::int*; -> core::Object::_identityHashCode
+  abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*; -> core::Object::_instanceOf
+  abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOf
+  abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOfTrue
+  abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOfFalse
+  abstract member-signature operator ==(dynamic other) → core::bool*; -> core::Object::==
+  abstract member-signature get hashCode() → core::int*; -> core::Object::hashCode
+  abstract member-signature method toString() → core::String*; -> core::Object::toString
+  abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic; -> core::Object::noSuchMethod
+  abstract member-signature get runtimeType() → core::Type*; -> core::Object::runtimeType
+}
+static const field iss::OT<() →* asy::Future<core::int*>*>* optedOutToken = #C1;
+static const field self::CheckIdentical* testCrossLibraries = #C2;
+static method testOptedOut() → dynamic {
+  #C2;
+  #C2;
+}
+static method main() → dynamic {}
+
+library /*isNonNullableByDefault*/;
+import self as iss;
+import "dart:async" as asy;
+import "dart:core" as core;
+import "issue46518.dart" as self;
+
+import "org-dartlang-testcase:///issue46518.dart";
+
+typedef NullableIntF = () → asy::Future<core::int?>;
+class OT<T extends core::Object> extends core::Object /*hasConstConstructor*/  {
+  const constructor •() → iss::OT<iss::OT::T>
+    : super core::Object::•()
+    ;
+  @#C3
+  method toString() → core::String {
+    return "${this.{core::Object::runtimeType}{core::Type}}";
+  }
+}
+static const field iss::OT<() → asy::Future<core::int?>> optedInToken = #C1;
+static method testOptedIn() → dynamic {
+  #C2;
+  #C2;
+}
+
+constants  {
+  #C1 = iss::OT<() →* asy::Future<core::int?>*> {}
+  #C2 = self::CheckIdentical {}
+  #C3 = core::_Override {}
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///issue46518.dart:
+- OT. (from org-dartlang-testcase:///issue46518_lib.dart:10:9)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
+- CheckIdentical. (from org-dartlang-testcase:///issue46518.dart:11:9)
+
+org-dartlang-testcase:///issue46518_lib.dart:
+- OT. (from org-dartlang-testcase:///issue46518_lib.dart:10:9)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
+- CheckIdentical. (from org-dartlang-testcase:///issue46518.dart:11:9)
diff --git a/pkg/front_end/testcases/general/issue46518_lib.dart b/pkg/front_end/testcases/general/issue46518_lib.dart
new file mode 100644
index 0000000..35602fd
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue46518_lib.dart
@@ -0,0 +1,24 @@
+// 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 './issue46518.dart';
+
+typedef NullableIntF = Future<int?> Function();
+
+class OT<T extends Object> {
+  const OT();
+
+  @override
+  String toString() {
+    return "$runtimeType";
+  }
+}
+
+testOptedIn() {
+  const localToken = OT<NullableIntF>();
+  const CheckIdentical(optedInToken, localToken);
+  const CheckIdentical(optedOutToken, localToken);
+}
+
+const optedInToken = OT<NullableIntF>();
diff --git a/pkg/front_end/testcases/nnbd/issue42546.dart.weak.outline.expect b/pkg/front_end/testcases/nnbd/issue42546.dart.weak.outline.expect
index e218902..ec6a7f6 100644
--- a/pkg/front_end/testcases/nnbd/issue42546.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/nnbd/issue42546.dart.weak.outline.expect
@@ -28,19 +28,19 @@
 
 
 Extra constant evaluation status:
-Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:821:13 -> SymbolConstant(#catchError)
-Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:821:13 -> ListConstant(const <Type*>[])
-Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:821:13 -> SymbolConstant(#test)
-Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:872:13 -> SymbolConstant(#whenComplete)
-Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:872:13 -> ListConstant(const <Type*>[])
-Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:872:13 -> MapConstant(const <Symbol*, dynamic>{})
-Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:916:13 -> SymbolConstant(#timeout)
-Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:916:13 -> ListConstant(const <Type*>[])
-Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:916:13 -> SymbolConstant(#onTimeout)
-Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:770:13 -> SymbolConstant(#then)
-Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:770:13 -> SymbolConstant(#onError)
-Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:881:13 -> SymbolConstant(#asStream)
-Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:881:13 -> ListConstant(const <Type*>[])
-Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:881:13 -> ListConstant(const <dynamic>[])
-Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:881:13 -> MapConstant(const <Symbol*, dynamic>{})
+Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:814:13 -> SymbolConstant(#catchError)
+Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:814:13 -> ListConstant(const <Type*>[])
+Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:814:13 -> SymbolConstant(#test)
+Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:865:13 -> SymbolConstant(#whenComplete)
+Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:865:13 -> ListConstant(const <Type*>[])
+Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:865:13 -> MapConstant(const <Symbol*, dynamic>{})
+Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:909:13 -> SymbolConstant(#timeout)
+Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:909:13 -> ListConstant(const <Type*>[])
+Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:909:13 -> SymbolConstant(#onTimeout)
+Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:763:13 -> SymbolConstant(#then)
+Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:763:13 -> SymbolConstant(#onError)
+Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:874:13 -> SymbolConstant(#asStream)
+Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:874:13 -> ListConstant(const <Type*>[])
+Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:874:13 -> ListConstant(const <dynamic>[])
+Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:874:13 -> MapConstant(const <Symbol*, dynamic>{})
 Extra constant evaluation: evaluated: 61, effectively constant: 15
diff --git a/sdk/lib/async/async.dart b/sdk/lib/async/async.dart
index ca05fc6..f4da9ca 100644
--- a/sdk/lib/async/async.dart
+++ b/sdk/lib/async/async.dart
@@ -109,6 +109,7 @@
         CastStreamTransformer,
         checkNotNullable,
         EmptyIterator,
+        isNullFuture,
         IterableElementError,
         nullFuture,
         printToZone,
diff --git a/sdk/lib/async/future.dart b/sdk/lib/async/future.dart
index f83386d..908e5e1 100644
--- a/sdk/lib/async/future.dart
+++ b/sdk/lib/async/future.dart
@@ -223,13 +223,6 @@
 /// it's very clearly documented.
 @pragma("wasm:entry-point")
 abstract class Future<T> {
-  /// A `Future<Null>` completed with `null`.
-  ///
-  /// Currently shared with `dart:internal`.
-  /// If that future can be removed, then change this back to
-  /// `_Future<Null>.zoneValue(null, _rootZone);`
-  static final _Future<Null> _nullFuture = nullFuture as _Future<Null>;
-
   /// A `Future<bool>` completed with `false`.
   static final _Future<bool> _falseFuture =
       new _Future<bool>.zoneValue(false, _rootZone);
diff --git a/sdk/lib/async/stream.dart b/sdk/lib/async/stream.dart
index 038c3bb..bb942d9 100644
--- a/sdk/lib/async/stream.dart
+++ b/sdk/lib/async/stream.dart
@@ -515,7 +515,7 @@
       controller
         ..onCancel = () {
           timer.cancel();
-          return Future._nullFuture;
+          return nullFuture;
         }
         ..onPause = () {
           watch.stop();
diff --git a/sdk/lib/async/stream_controller.dart b/sdk/lib/async/stream_controller.dart
index 2baf008..2d381c5 100644
--- a/sdk/lib/async/stream_controller.dart
+++ b/sdk/lib/async/stream_controller.dart
@@ -588,7 +588,10 @@
   Future<void> get done => _ensureDoneFuture();
 
   Future<void> _ensureDoneFuture() =>
-      _doneFuture ??= _isCanceled ? Future._nullFuture : _Future<void>();
+      _doneFuture ??
+      (_isCanceled
+          ? nullFuture as Future<void>
+          : _doneFuture = _Future<void>());
 
   /// Send or enqueue a data event.
   void add(T value) {
@@ -919,7 +922,7 @@
     var cancel = addSubscription.cancel();
     if (cancel == null) {
       addStreamFuture._asyncComplete(null);
-      return Future._nullFuture;
+      return nullFuture;
     }
     return cancel.whenComplete(() {
       addStreamFuture._asyncComplete(null);
diff --git a/sdk/lib/async/stream_impl.dart b/sdk/lib/async/stream_impl.dart
index b4fb5d8..9065c46 100644
--- a/sdk/lib/async/stream_impl.dart
+++ b/sdk/lib/async/stream_impl.dart
@@ -197,7 +197,7 @@
     if (!_isCanceled) {
       _cancel();
     }
-    return _cancelFuture ?? Future._nullFuture;
+    return _cancelFuture ?? nullFuture;
   }
 
   Future<E> asFuture<E>([E? futureValue]) {
@@ -217,7 +217,7 @@
     };
     _onError = (Object error, StackTrace stackTrace) {
       Future cancelFuture = cancel();
-      if (!identical(cancelFuture, Future._nullFuture)) {
+      if (!isNullFuture(Zone._current, cancelFuture)) {
         cancelFuture.whenComplete(() {
           result._completeError(error, stackTrace);
         });
@@ -297,7 +297,7 @@
   // Hooks called when the input is paused, unpaused or canceled.
   // These must not throw. If overwritten to call user code, include suitable
   // try/catch wrapping and send any errors to
-  // [_Zone.current.handleUncaughtError].
+  // [Zone._current.handleUncaughtError].
   void _onPause() {
     assert(_isInputPaused);
   }
@@ -352,7 +352,6 @@
       // future to finish we must not report the error.
       if (_isCanceled && !_waitsForCancel) return;
       _state |= _STATE_IN_CALLBACK;
-      // TODO(floitsch): this dynamic should be 'void'.
       var onError = _onError;
       if (onError is void Function(Object, StackTrace)) {
         _zone.runBinaryGuarded<Object, StackTrace>(onError, error, stackTrace);
@@ -366,8 +365,7 @@
       _state |= _STATE_WAIT_FOR_CANCEL;
       _cancel();
       var cancelFuture = _cancelFuture;
-      if (cancelFuture != null &&
-          !identical(cancelFuture, Future._nullFuture)) {
+      if (cancelFuture != null && !isNullFuture(Zone._current, cancelFuture)) {
         cancelFuture.whenComplete(sendError);
       } else {
         sendError();
@@ -396,7 +394,7 @@
     _cancel();
     _state |= _STATE_WAIT_FOR_CANCEL;
     var cancelFuture = _cancelFuture;
-    if (cancelFuture != null && !identical(cancelFuture, Future._nullFuture)) {
+    if (cancelFuture != null && !isNullFuture(Zone._current, cancelFuture)) {
       cancelFuture.whenComplete(sendDone);
     } else {
       sendDone();
@@ -672,7 +670,7 @@
     }
   }
 
-  Future cancel() => Future._nullFuture;
+  Future cancel() => nullFuture;
 
   Future<E> asFuture<E>([E? futureValue]) {
     E resultValue;
@@ -819,7 +817,7 @@
 
   Future cancel() {
     _stream._cancelSubscription();
-    return Future._nullFuture;
+    return nullFuture;
   }
 
   bool get isPaused {
@@ -963,7 +961,7 @@
       }
       return subscription.cancel();
     }
-    return Future._nullFuture;
+    return nullFuture;
   }
 
   void _onData(T data) {
diff --git a/sdk/lib/async/stream_pipe.dart b/sdk/lib/async/stream_pipe.dart
index cd707be..78ad675 100644
--- a/sdk/lib/async/stream_pipe.dart
+++ b/sdk/lib/async/stream_pipe.dart
@@ -26,7 +26,7 @@
 void _cancelAndError(StreamSubscription subscription, _Future future,
     Object error, StackTrace stackTrace) {
   var cancelFuture = subscription.cancel();
-  if (cancelFuture != null && !identical(cancelFuture, Future._nullFuture)) {
+  if (cancelFuture != null && !isNullFuture(Zone._current, cancelFuture)) {
     cancelFuture.whenComplete(() => future._completeError(error, stackTrace));
   } else {
     future._completeError(error, stackTrace);
@@ -55,7 +55,7 @@
   before completing with a value. */
 void _cancelAndValue(StreamSubscription subscription, _Future future, value) {
   var cancelFuture = subscription.cancel();
-  if (cancelFuture != null && !identical(cancelFuture, Future._nullFuture)) {
+  if (cancelFuture != null && !isNullFuture(Zone._current, cancelFuture)) {
     cancelFuture.whenComplete(() => future._complete(value));
   } else {
     future._complete(value);
diff --git a/sdk/lib/html/dart2js/html_dart2js.dart b/sdk/lib/html/dart2js/html_dart2js.dart
index 671f68a..4bc1905 100644
--- a/sdk/lib/html/dart2js/html_dart2js.dart
+++ b/sdk/lib/html/dart2js/html_dart2js.dart
@@ -37302,7 +37302,7 @@
   }
 
   Future cancel() {
-    if (_canceled) return nullFuture;
+    if (_canceled) return Future<void>.value(null);
 
     _unlisten();
     // Clear out the target to indicate this is complete.
diff --git a/sdk/lib/internal/internal.dart b/sdk/lib/internal/internal.dart
index ff3d890..a763b12 100644
--- a/sdk/lib/internal/internal.dart
+++ b/sdk/lib/internal/internal.dart
@@ -136,7 +136,7 @@
   return digit1 * 16 + digit2 - (digit2 & 256);
 }
 
-/// A reusable `null`-valued future used by `dart:async`.
+/// A reusable `null`-valued future per zone used by `dart:async`.
 ///
 /// **DO NOT USE.**
 ///
@@ -156,7 +156,14 @@
 /// This future will be removed again if we can ever do so.
 /// Do not use it for anything other than preserving timing
 /// during the null safety migration.
-final Future<Null> nullFuture = Zone.root.run(() => Future<Null>.value(null));
+Future<Null> get nullFuture => _nullFutures[Zone.current] ??= 
+    Future<Null>.value(null);
+
+/// Whether [future] is the null future of the current zone.
+bool isNullFuture(Zone zone, Future future) =>
+    identical(_nullFutures[zone], future);
+
+final Expando<Future<Null>> _nullFutures = Expando<Future<Null>>();
 
 /// A default hash function used by the platform in various places.
 ///
diff --git a/tests/lib/async/null_future_zone_test.dart b/tests/lib/async/null_future_zone_test.dart
index 9a266be..34201e7 100644
--- a/tests/lib/async/null_future_zone_test.dart
+++ b/tests/lib/async/null_future_zone_test.dart
@@ -13,21 +13,32 @@
     Expect.isFalse(await it.moveNext());
 
     late Future nullFuture;
+
+    bool nullFutureZoneUsed = false;
+    runZoned(() {
+      nullFuture = (new StreamController()..stream.listen(null).cancel()).done;
+    }, zoneSpecification: new ZoneSpecification(scheduleMicrotask:
+        (Zone self, ZoneDelegate parent, Zone zone, void f()) {
+      Expect.identical(zone, self);
+      nullFutureZoneUsed = true;
+      parent.scheduleMicrotask(zone, f);
+    }));
+
+    nullFuture.then((value) {
+      Expect.isNull(value);
+      Expect.isTrue(nullFutureZoneUsed);
+      asyncEnd();
+    });
+
     late Future falseFuture;
 
     runZoned(() {
-      nullFuture = (new StreamController()..stream.listen(null).cancel()).done;
       falseFuture = it.moveNext();
     }, zoneSpecification: new ZoneSpecification(scheduleMicrotask:
         (Zone self, ZoneDelegate parent, Zone zone, void f()) {
       Expect.fail("Should not be called");
     }));
 
-    nullFuture.then((value) {
-      Expect.isNull(value);
-      asyncEnd();
-    });
-
     falseFuture.then((value) {
       Expect.isFalse(value);
       asyncEnd();
diff --git a/tests/lib_2/async/null_future_zone_test.dart b/tests/lib_2/async/null_future_zone_test.dart
index 3322916..77cb1b7 100644
--- a/tests/lib_2/async/null_future_zone_test.dart
+++ b/tests/lib_2/async/null_future_zone_test.dart
@@ -15,21 +15,32 @@
     Expect.isFalse(await it.moveNext());
 
     Future nullFuture;
+
+    bool nullFutureZoneUsed = false;
+    runZoned(() {
+      nullFuture = (new StreamController()..stream.listen(null).cancel()).done;
+    }, zoneSpecification: new ZoneSpecification(scheduleMicrotask:
+        (Zone self, ZoneDelegate parent, Zone zone, void f()) {
+      Expect.identical(zone, self);
+      nullFutureZoneUsed = true;
+      parent.scheduleMicrotask(zone, f);
+    }));
+
+    nullFuture.then((value) {
+      Expect.isNull(value);
+      Expect.isTrue(nullFutureZoneUsed);
+      asyncEnd();
+    });
+
     Future falseFuture;
 
     runZoned(() {
-      nullFuture = (new StreamController()..stream.listen(null).cancel()).done;
       falseFuture = it.moveNext();
     }, zoneSpecification: new ZoneSpecification(scheduleMicrotask:
         (Zone self, ZoneDelegate parent, Zone zone, void f()) {
       Expect.fail("Should not be called");
     }));
 
-    nullFuture.then((value) {
-      Expect.isNull(value);
-      asyncEnd();
-    });
-
     falseFuture.then((value) {
       Expect.isFalse(value);
       asyncEnd();
diff --git a/tools/VERSION b/tools/VERSION
index 90241e9..db6faae 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 18
 PATCH 0
-PRERELEASE 209
+PRERELEASE 210
 PRERELEASE_PATCH 0
\ No newline at end of file