Version 2.13.0-188.0.dev

Merge commit '2a14259fd0ec065f8e09e5e910d3d6ce28ef4280' 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 927f9e8..ccf323b 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -1301,11 +1301,13 @@
   void _unaliasTypeAliasedConstructorInvocations() {
     for (TypeAliasedConstructorInvocationJudgment invocation
         in typeAliasedConstructorInvocations) {
-      DartType unaliasedType = new TypedefType(
-              invocation.typeAliasBuilder.typedef,
-              Nullability.nonNullable,
-              invocation.arguments.types)
-          .unalias;
+      DartType aliasedType = new TypedefType(
+          invocation.typeAliasBuilder.typedef,
+          Nullability.nonNullable,
+          invocation.arguments.types);
+      libraryBuilder.checkBoundsInType(
+          aliasedType, typeEnvironment, uri, invocation.fileOffset);
+      DartType unaliasedType = aliasedType.unalias;
       List<DartType> invocationTypeArguments = null;
       if (unaliasedType is InterfaceType) {
         invocationTypeArguments = unaliasedType.typeArguments;
@@ -1323,11 +1325,13 @@
   void _unaliasTypeAliasedFactoryInvocations() {
     for (TypeAliasedFactoryInvocationJudgment invocation
         in typeAliasedFactoryInvocations) {
-      DartType unaliasedType = new TypedefType(
-              invocation.typeAliasBuilder.typedef,
-              Nullability.nonNullable,
-              invocation.arguments.types)
-          .unalias;
+      DartType aliasedType = new TypedefType(
+          invocation.typeAliasBuilder.typedef,
+          Nullability.nonNullable,
+          invocation.arguments.types);
+      libraryBuilder.checkBoundsInType(
+          aliasedType, typeEnvironment, uri, invocation.fileOffset);
+      DartType unaliasedType = aliasedType.unalias;
       List<DartType> invocationTypeArguments = null;
       if (unaliasedType is InterfaceType) {
         invocationTypeArguments = unaliasedType.typeArguments;
@@ -4500,7 +4504,8 @@
       List<UnresolvedType> typeArguments,
       int charOffset,
       Constness constness,
-      {bool isTypeArgumentsInForest = false}) {
+      {bool isTypeArgumentsInForest = false,
+      TypeDeclarationBuilder typeAliasBuilder}) {
     if (arguments == null) {
       return buildProblem(fasta.messageMissingArgumentList,
           nameToken.charOffset, nameToken.length);
@@ -4727,7 +4732,8 @@
         invocation = buildStaticInvocation(target, arguments,
             constness: constness,
             charOffset: nameToken.charOffset,
-            charLength: nameToken.length);
+            charLength: nameToken.length,
+            typeAliasBuilder: typeAliasBuilder);
 
         if (invocation is StaticInvocation &&
             isRedirectingFactory(target, helper: this)) {
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 a6e54b2..19f3191 100644
--- a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
@@ -3058,8 +3058,9 @@
     Arguments arguments = send.arguments;
 
     TypeDeclarationBuilder declarationBuilder = declaration;
+    TypeAliasBuilder aliasBuilder;
     if (declarationBuilder is TypeAliasBuilder) {
-      TypeAliasBuilder aliasBuilder = declarationBuilder;
+      aliasBuilder = declarationBuilder;
       declarationBuilder = aliasBuilder.unaliasDeclaration(null,
           isUsedAsClass: true,
           usedAsClassCharOffset: this.fileOffset,
@@ -3085,7 +3086,8 @@
               send.typeArguments,
               token.charOffset,
               Constness.implicit,
-              isTypeArgumentsInForest: send.isTypeArgumentsInForest);
+              isTypeArgumentsInForest: send.isTypeArgumentsInForest,
+              typeAliasBuilder: aliasBuilder);
         }
       } else if (member is AmbiguousBuilder) {
         return _helper.buildProblem(
diff --git a/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart b/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart
index 83bd4ab..2faf2ed 100644
--- a/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart
@@ -126,7 +126,8 @@
       List<UnresolvedType> typeArguments,
       int charOffset,
       Constness constness,
-      {bool isTypeArgumentsInForest = false});
+      {bool isTypeArgumentsInForest = false,
+      TypeDeclarationBuilder typeAliasBuilder});
 
   UnresolvedType validateTypeUse(UnresolvedType unresolved,
       {bool nonInstanceAccessIsError, bool allowPotentiallyConstantType});
diff --git a/pkg/front_end/test/explicit_creation_git_test.dart b/pkg/front_end/test/explicit_creation_git_test.dart
index a0378d2..9229e49 100644
--- a/pkg/front_end/test/explicit_creation_git_test.dart
+++ b/pkg/front_end/test/explicit_creation_git_test.dart
@@ -237,7 +237,8 @@
       List<UnresolvedType> typeArguments,
       int charOffset,
       Constness constness,
-      {bool isTypeArgumentsInForest = false}) {
+      {bool isTypeArgumentsInForest = false,
+      TypeDeclarationBuilder typeAliasBuilder}) {
     Token maybeNewOrConst = nameToken.previous;
     bool doReport = true;
     if (maybeNewOrConst is KeywordToken) {
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart
new file mode 100644
index 0000000..9a5768c
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart
@@ -0,0 +1,13 @@
+// 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.
+
+class C<X> {}
+typedef G<X> = X Function(X);
+typedef A<X extends G<C<X>>> = C<X>;
+
+test() {
+  A a = throw 42; // Error.
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart.strong.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart.strong.expect
new file mode 100644
index 0000000..20cf2a0a
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart.strong.expect
@@ -0,0 +1,31 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart:10:3: Error: Inferred type argument 'C<dynamic> Function(C<dynamic>)' doesn't conform to the bound 'C<X> Function(C<X>)' of the type variable 'X' on 'A'.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+//   A a = throw 42; // Error.
+//   ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart:7:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef A<X extends G<C<X>>> = C<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart:10:3: Context: If you want 'A<C<dynamic> Function(C<dynamic>)>' to be a super-bounded type, note that the inverted type 'A<G<C<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart'.
+//   A a = throw 42; // Error.
+//   ^
+//
+import self as self;
+import "dart:core" as core;
+
+typedef G<invariant X extends core::Object? = dynamic> = (X%) → X%;
+typedef A<X extends (self::C<X>) → self::C<X> = (self::C<dynamic>) → self::C<dynamic>> = self::C<X>;
+class C<X extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::C<self::C::X%>
+    : super core::Object::•()
+    ;
+}
+static method test() → dynamic {
+  self::C<(self::C<dynamic>) → self::C<dynamic>> a = throw 42;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart.strong.transformed.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart.strong.transformed.expect
new file mode 100644
index 0000000..20cf2a0a
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart.strong.transformed.expect
@@ -0,0 +1,31 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart:10:3: Error: Inferred type argument 'C<dynamic> Function(C<dynamic>)' doesn't conform to the bound 'C<X> Function(C<X>)' of the type variable 'X' on 'A'.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+//   A a = throw 42; // Error.
+//   ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart:7:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef A<X extends G<C<X>>> = C<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart:10:3: Context: If you want 'A<C<dynamic> Function(C<dynamic>)>' to be a super-bounded type, note that the inverted type 'A<G<C<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart'.
+//   A a = throw 42; // Error.
+//   ^
+//
+import self as self;
+import "dart:core" as core;
+
+typedef G<invariant X extends core::Object? = dynamic> = (X%) → X%;
+typedef A<X extends (self::C<X>) → self::C<X> = (self::C<dynamic>) → self::C<dynamic>> = self::C<X>;
+class C<X extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::C<self::C::X%>
+    : super core::Object::•()
+    ;
+}
+static method test() → dynamic {
+  self::C<(self::C<dynamic>) → self::C<dynamic>> a = throw 42;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart.textual_outline.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart.textual_outline.expect
new file mode 100644
index 0000000..0d8eec5
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart.textual_outline.expect
@@ -0,0 +1,5 @@
+class C<X> {}
+typedef G<X> = X Function(X);
+typedef A<X extends G<C<X>>> = C<X>;
+test() {}
+main() {}
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart.weak.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart.weak.expect
new file mode 100644
index 0000000..20cf2a0a
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart.weak.expect
@@ -0,0 +1,31 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart:10:3: Error: Inferred type argument 'C<dynamic> Function(C<dynamic>)' doesn't conform to the bound 'C<X> Function(C<X>)' of the type variable 'X' on 'A'.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+//   A a = throw 42; // Error.
+//   ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart:7:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef A<X extends G<C<X>>> = C<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart:10:3: Context: If you want 'A<C<dynamic> Function(C<dynamic>)>' to be a super-bounded type, note that the inverted type 'A<G<C<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart'.
+//   A a = throw 42; // Error.
+//   ^
+//
+import self as self;
+import "dart:core" as core;
+
+typedef G<invariant X extends core::Object? = dynamic> = (X%) → X%;
+typedef A<X extends (self::C<X>) → self::C<X> = (self::C<dynamic>) → self::C<dynamic>> = self::C<X>;
+class C<X extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::C<self::C::X%>
+    : super core::Object::•()
+    ;
+}
+static method test() → dynamic {
+  self::C<(self::C<dynamic>) → self::C<dynamic>> a = throw 42;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart.weak.outline.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart.weak.outline.expect
new file mode 100644
index 0000000..2c7e28a
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart.weak.outline.expect
@@ -0,0 +1,14 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+typedef G<invariant X extends core::Object? = dynamic> = (X%) → X%;
+typedef A<X extends (self::C<X>) → self::C<X> = (self::C<dynamic>) → self::C<dynamic>> = self::C<X>;
+class C<X extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::C<self::C::X%>
+    ;
+}
+static method test() → dynamic
+  ;
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart.weak.transformed.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart.weak.transformed.expect
new file mode 100644
index 0000000..20cf2a0a
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart.weak.transformed.expect
@@ -0,0 +1,31 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart:10:3: Error: Inferred type argument 'C<dynamic> Function(C<dynamic>)' doesn't conform to the bound 'C<X> Function(C<X>)' of the type variable 'X' on 'A'.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+//   A a = throw 42; // Error.
+//   ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart:7:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef A<X extends G<C<X>>> = C<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart:10:3: Context: If you want 'A<C<dynamic> Function(C<dynamic>)>' to be a super-bounded type, note that the inverted type 'A<G<C<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519.dart'.
+//   A a = throw 42; // Error.
+//   ^
+//
+import self as self;
+import "dart:core" as core;
+
+typedef G<invariant X extends core::Object? = dynamic> = (X%) → X%;
+typedef A<X extends (self::C<X>) → self::C<X> = (self::C<dynamic>) → self::C<dynamic>> = self::C<X>;
+class C<X extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::C<self::C::X%>
+    : super core::Object::•()
+    ;
+}
+static method test() → dynamic {
+  self::C<(self::C<dynamic>) → self::C<dynamic>> a = throw 42;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart
new file mode 100644
index 0000000..de364ca
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart
@@ -0,0 +1,28 @@
+// 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.
+
+class C<X> {
+  factory C() => new C.foo();
+  C.foo() {}
+  factory C.bar() = C;
+}
+class D<X> {
+  D();
+  factory D.foo() => new D();
+  factory D.bar() = D;
+}
+typedef G<X> = X Function(X);
+typedef A<X extends G<C<X>>> = C<X>;
+typedef B<X extends G<D<X>>> = D<X>;
+
+test() {
+  A(); // Error.
+  A.foo(); // Error.
+  A.bar(); // Error.
+  B(); // Error.
+  B.foo(); // Error.
+  B.bar(); // Error.
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart.strong.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart.strong.expect
new file mode 100644
index 0000000..2e82e80
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart.strong.expect
@@ -0,0 +1,128 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:21:5: Error: Type argument 'C<Object?> Function(C<Never>)' doesn't conform to the bound 'C<X> Function(C<X>)' of the type variable 'X' on 'A'.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+// Try changing type arguments so that they conform to the bounds.
+//   A.foo(); // Error.
+//     ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:16:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef A<X extends G<C<X>>> = C<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:21:5: Context: If you want 'A<C<Object?> Function(C<Never>)>' to be a super-bounded type, note that the inverted type 'A<G<C<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+//   A.foo(); // Error.
+//     ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:23:3: Error: Type argument 'D<Object?> Function(D<Never>)' doesn't conform to the bound 'D<X> Function(D<X>)' of the type variable 'X' on 'B'.
+//  - 'D' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+// Try changing type arguments so that they conform to the bounds.
+//   B(); // Error.
+//   ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:17:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef B<X extends G<D<X>>> = D<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:23:3: Context: If you want 'B<D<Object?> Function(D<Never>)>' to be a super-bounded type, note that the inverted type 'B<G<D<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'D' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+//   B(); // Error.
+//   ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:20:3: Error: Type argument 'C<Object?> Function(C<Never>)' doesn't conform to the bound 'C<X> Function(C<X>)' of the type variable 'X' on 'A'.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+// Try changing type arguments so that they conform to the bounds.
+//   A(); // Error.
+//   ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:16:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef A<X extends G<C<X>>> = C<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:20:3: Context: If you want 'A<C<Object?> Function(C<Never>)>' to be a super-bounded type, note that the inverted type 'A<G<C<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+//   A(); // Error.
+//   ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:22:5: Error: Type argument 'C<Object?> Function(C<Never>)' doesn't conform to the bound 'C<X> Function(C<X>)' of the type variable 'X' on 'A'.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+// Try changing type arguments so that they conform to the bounds.
+//   A.bar(); // Error.
+//     ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:16:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef A<X extends G<C<X>>> = C<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:22:5: Context: If you want 'A<C<Object?> Function(C<Never>)>' to be a super-bounded type, note that the inverted type 'A<G<C<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+//   A.bar(); // Error.
+//     ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:24:5: Error: Type argument 'D<Object?> Function(D<Never>)' doesn't conform to the bound 'D<X> Function(D<X>)' of the type variable 'X' on 'B'.
+//  - 'D' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+// Try changing type arguments so that they conform to the bounds.
+//   B.foo(); // Error.
+//     ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:17:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef B<X extends G<D<X>>> = D<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:24:5: Context: If you want 'B<D<Object?> Function(D<Never>)>' to be a super-bounded type, note that the inverted type 'B<G<D<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'D' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+//   B.foo(); // Error.
+//     ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:25:5: Error: Type argument 'D<Object?> Function(D<Never>)' doesn't conform to the bound 'D<X> Function(D<X>)' of the type variable 'X' on 'B'.
+//  - 'D' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+// Try changing type arguments so that they conform to the bounds.
+//   B.bar(); // Error.
+//     ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:17:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef B<X extends G<D<X>>> = D<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:25:5: Context: If you want 'B<D<Object?> Function(D<Never>)>' to be a super-bounded type, note that the inverted type 'B<G<D<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'D' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+//   B.bar(); // Error.
+//     ^
+//
+import self as self;
+import "dart:core" as core;
+
+typedef G<invariant X extends core::Object? = dynamic> = (X%) → X%;
+typedef A<X extends (self::C<X>) → self::C<X> = (self::C<dynamic>) → self::C<dynamic>> = self::C<X>;
+typedef B<X extends (self::D<X>) → self::D<X> = (self::D<dynamic>) → self::D<dynamic>> = self::D<X>;
+class C<X extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::C::bar]/*isLegacy*/;
+  constructor foo() → self::C<self::C::X%>
+    : super core::Object::•() {}
+  static factory •<X extends core::Object? = dynamic>() → self::C<self::C::•::X%>
+    return new self::C::foo<self::C::•::X%>();
+  static factory bar<X extends core::Object? = dynamic>() → self::C<self::C::bar::X%>
+    let dynamic #redirecting_factory = self::C::• in let self::C::bar::X% #typeArg0 = null in invalid-expression;
+}
+class D<X extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::D::bar]/*isLegacy*/;
+  constructor •() → self::D<self::D::X%>
+    : super core::Object::•()
+    ;
+  static factory foo<X extends core::Object? = dynamic>() → self::D<self::D::foo::X%>
+    return new self::D::•<self::D::foo::X%>();
+  static factory bar<X extends core::Object? = dynamic>() → self::D<self::D::bar::X%>
+    let dynamic #redirecting_factory = self::D::• in let self::D::bar::X% #typeArg0 = null in invalid-expression;
+}
+static method test() → dynamic {
+  self::C::•<(self::C<Never>) → self::C<core::Object?>>();
+  new self::C::foo<(self::C<Never>) → self::C<core::Object?>>();
+  self::C::•<(self::C<Never>) → self::C<core::Object?>>();
+  new self::D::•<(self::D<Never>) → self::D<core::Object?>>();
+  self::D::foo<(self::D<Never>) → self::D<core::Object?>>();
+  new self::D::•<(self::D<Never>) → self::D<core::Object?>>();
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart.strong.transformed.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart.strong.transformed.expect
new file mode 100644
index 0000000..e2f8dde
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart.strong.transformed.expect
@@ -0,0 +1,128 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:21:5: Error: Type argument 'C<Object?> Function(C<Never>)' doesn't conform to the bound 'C<X> Function(C<X>)' of the type variable 'X' on 'A'.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+// Try changing type arguments so that they conform to the bounds.
+//   A.foo(); // Error.
+//     ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:16:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef A<X extends G<C<X>>> = C<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:21:5: Context: If you want 'A<C<Object?> Function(C<Never>)>' to be a super-bounded type, note that the inverted type 'A<G<C<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+//   A.foo(); // Error.
+//     ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:23:3: Error: Type argument 'D<Object?> Function(D<Never>)' doesn't conform to the bound 'D<X> Function(D<X>)' of the type variable 'X' on 'B'.
+//  - 'D' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+// Try changing type arguments so that they conform to the bounds.
+//   B(); // Error.
+//   ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:17:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef B<X extends G<D<X>>> = D<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:23:3: Context: If you want 'B<D<Object?> Function(D<Never>)>' to be a super-bounded type, note that the inverted type 'B<G<D<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'D' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+//   B(); // Error.
+//   ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:20:3: Error: Type argument 'C<Object?> Function(C<Never>)' doesn't conform to the bound 'C<X> Function(C<X>)' of the type variable 'X' on 'A'.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+// Try changing type arguments so that they conform to the bounds.
+//   A(); // Error.
+//   ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:16:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef A<X extends G<C<X>>> = C<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:20:3: Context: If you want 'A<C<Object?> Function(C<Never>)>' to be a super-bounded type, note that the inverted type 'A<G<C<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+//   A(); // Error.
+//   ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:22:5: Error: Type argument 'C<Object?> Function(C<Never>)' doesn't conform to the bound 'C<X> Function(C<X>)' of the type variable 'X' on 'A'.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+// Try changing type arguments so that they conform to the bounds.
+//   A.bar(); // Error.
+//     ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:16:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef A<X extends G<C<X>>> = C<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:22:5: Context: If you want 'A<C<Object?> Function(C<Never>)>' to be a super-bounded type, note that the inverted type 'A<G<C<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+//   A.bar(); // Error.
+//     ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:24:5: Error: Type argument 'D<Object?> Function(D<Never>)' doesn't conform to the bound 'D<X> Function(D<X>)' of the type variable 'X' on 'B'.
+//  - 'D' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+// Try changing type arguments so that they conform to the bounds.
+//   B.foo(); // Error.
+//     ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:17:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef B<X extends G<D<X>>> = D<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:24:5: Context: If you want 'B<D<Object?> Function(D<Never>)>' to be a super-bounded type, note that the inverted type 'B<G<D<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'D' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+//   B.foo(); // Error.
+//     ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:25:5: Error: Type argument 'D<Object?> Function(D<Never>)' doesn't conform to the bound 'D<X> Function(D<X>)' of the type variable 'X' on 'B'.
+//  - 'D' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+// Try changing type arguments so that they conform to the bounds.
+//   B.bar(); // Error.
+//     ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:17:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef B<X extends G<D<X>>> = D<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:25:5: Context: If you want 'B<D<Object?> Function(D<Never>)>' to be a super-bounded type, note that the inverted type 'B<G<D<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'D' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+//   B.bar(); // Error.
+//     ^
+//
+import self as self;
+import "dart:core" as core;
+
+typedef G<invariant X extends core::Object? = dynamic> = (X%) → X%;
+typedef A<X extends (self::C<X>) → self::C<X> = (self::C<dynamic>) → self::C<dynamic>> = self::C<X>;
+typedef B<X extends (self::D<X>) → self::D<X> = (self::D<dynamic>) → self::D<dynamic>> = self::D<X>;
+class C<X extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::C::bar]/*isLegacy*/;
+  constructor foo() → self::C<self::C::X%>
+    : super core::Object::•() {}
+  static factory •<X extends core::Object? = dynamic>() → self::C<self::C::•::X%>
+    return new self::C::foo<self::C::•::X%>();
+  static factory bar<X extends core::Object? = dynamic>() → self::C<self::C::bar::X%>
+    let <X extends core::Object? = dynamic>() → self::C<X%> #redirecting_factory = self::C::• in let self::C::bar::X% #typeArg0 = null in invalid-expression;
+}
+class D<X extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::D::bar]/*isLegacy*/;
+  constructor •() → self::D<self::D::X%>
+    : super core::Object::•()
+    ;
+  static factory foo<X extends core::Object? = dynamic>() → self::D<self::D::foo::X%>
+    return new self::D::•<self::D::foo::X%>();
+  static factory bar<X extends core::Object? = dynamic>() → self::D<self::D::bar::X%>
+    let Never #redirecting_factory = self::D::• in let self::D::bar::X% #typeArg0 = null in invalid-expression;
+}
+static method test() → dynamic {
+  self::C::•<(self::C<Never>) → self::C<core::Object?>>();
+  new self::C::foo<(self::C<Never>) → self::C<core::Object?>>();
+  self::C::•<(self::C<Never>) → self::C<core::Object?>>();
+  new self::D::•<(self::D<Never>) → self::D<core::Object?>>();
+  self::D::foo<(self::D<Never>) → self::D<core::Object?>>();
+  new self::D::•<(self::D<Never>) → self::D<core::Object?>>();
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart.textual_outline.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart.textual_outline.expect
new file mode 100644
index 0000000..329c512
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart.textual_outline.expect
@@ -0,0 +1,15 @@
+class C<X> {
+  factory C() => new C.foo();
+  C.foo() {}
+  factory C.bar() = C;
+}
+class D<X> {
+  D();
+  factory D.foo() => new D();
+  factory D.bar() = D;
+}
+typedef G<X> = X Function(X);
+typedef A<X extends G<C<X>>> = C<X>;
+typedef B<X extends G<D<X>>> = D<X>;
+test() {}
+main() {}
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart.weak.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart.weak.expect
new file mode 100644
index 0000000..2e82e80
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart.weak.expect
@@ -0,0 +1,128 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:21:5: Error: Type argument 'C<Object?> Function(C<Never>)' doesn't conform to the bound 'C<X> Function(C<X>)' of the type variable 'X' on 'A'.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+// Try changing type arguments so that they conform to the bounds.
+//   A.foo(); // Error.
+//     ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:16:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef A<X extends G<C<X>>> = C<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:21:5: Context: If you want 'A<C<Object?> Function(C<Never>)>' to be a super-bounded type, note that the inverted type 'A<G<C<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+//   A.foo(); // Error.
+//     ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:23:3: Error: Type argument 'D<Object?> Function(D<Never>)' doesn't conform to the bound 'D<X> Function(D<X>)' of the type variable 'X' on 'B'.
+//  - 'D' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+// Try changing type arguments so that they conform to the bounds.
+//   B(); // Error.
+//   ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:17:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef B<X extends G<D<X>>> = D<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:23:3: Context: If you want 'B<D<Object?> Function(D<Never>)>' to be a super-bounded type, note that the inverted type 'B<G<D<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'D' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+//   B(); // Error.
+//   ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:20:3: Error: Type argument 'C<Object?> Function(C<Never>)' doesn't conform to the bound 'C<X> Function(C<X>)' of the type variable 'X' on 'A'.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+// Try changing type arguments so that they conform to the bounds.
+//   A(); // Error.
+//   ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:16:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef A<X extends G<C<X>>> = C<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:20:3: Context: If you want 'A<C<Object?> Function(C<Never>)>' to be a super-bounded type, note that the inverted type 'A<G<C<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+//   A(); // Error.
+//   ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:22:5: Error: Type argument 'C<Object?> Function(C<Never>)' doesn't conform to the bound 'C<X> Function(C<X>)' of the type variable 'X' on 'A'.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+// Try changing type arguments so that they conform to the bounds.
+//   A.bar(); // Error.
+//     ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:16:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef A<X extends G<C<X>>> = C<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:22:5: Context: If you want 'A<C<Object?> Function(C<Never>)>' to be a super-bounded type, note that the inverted type 'A<G<C<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+//   A.bar(); // Error.
+//     ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:24:5: Error: Type argument 'D<Object?> Function(D<Never>)' doesn't conform to the bound 'D<X> Function(D<X>)' of the type variable 'X' on 'B'.
+//  - 'D' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+// Try changing type arguments so that they conform to the bounds.
+//   B.foo(); // Error.
+//     ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:17:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef B<X extends G<D<X>>> = D<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:24:5: Context: If you want 'B<D<Object?> Function(D<Never>)>' to be a super-bounded type, note that the inverted type 'B<G<D<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'D' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+//   B.foo(); // Error.
+//     ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:25:5: Error: Type argument 'D<Object?> Function(D<Never>)' doesn't conform to the bound 'D<X> Function(D<X>)' of the type variable 'X' on 'B'.
+//  - 'D' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+// Try changing type arguments so that they conform to the bounds.
+//   B.bar(); // Error.
+//     ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:17:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef B<X extends G<D<X>>> = D<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:25:5: Context: If you want 'B<D<Object?> Function(D<Never>)>' to be a super-bounded type, note that the inverted type 'B<G<D<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'D' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+//   B.bar(); // Error.
+//     ^
+//
+import self as self;
+import "dart:core" as core;
+
+typedef G<invariant X extends core::Object? = dynamic> = (X%) → X%;
+typedef A<X extends (self::C<X>) → self::C<X> = (self::C<dynamic>) → self::C<dynamic>> = self::C<X>;
+typedef B<X extends (self::D<X>) → self::D<X> = (self::D<dynamic>) → self::D<dynamic>> = self::D<X>;
+class C<X extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::C::bar]/*isLegacy*/;
+  constructor foo() → self::C<self::C::X%>
+    : super core::Object::•() {}
+  static factory •<X extends core::Object? = dynamic>() → self::C<self::C::•::X%>
+    return new self::C::foo<self::C::•::X%>();
+  static factory bar<X extends core::Object? = dynamic>() → self::C<self::C::bar::X%>
+    let dynamic #redirecting_factory = self::C::• in let self::C::bar::X% #typeArg0 = null in invalid-expression;
+}
+class D<X extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::D::bar]/*isLegacy*/;
+  constructor •() → self::D<self::D::X%>
+    : super core::Object::•()
+    ;
+  static factory foo<X extends core::Object? = dynamic>() → self::D<self::D::foo::X%>
+    return new self::D::•<self::D::foo::X%>();
+  static factory bar<X extends core::Object? = dynamic>() → self::D<self::D::bar::X%>
+    let dynamic #redirecting_factory = self::D::• in let self::D::bar::X% #typeArg0 = null in invalid-expression;
+}
+static method test() → dynamic {
+  self::C::•<(self::C<Never>) → self::C<core::Object?>>();
+  new self::C::foo<(self::C<Never>) → self::C<core::Object?>>();
+  self::C::•<(self::C<Never>) → self::C<core::Object?>>();
+  new self::D::•<(self::D<Never>) → self::D<core::Object?>>();
+  self::D::foo<(self::D<Never>) → self::D<core::Object?>>();
+  new self::D::•<(self::D<Never>) → self::D<core::Object?>>();
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart.weak.outline.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart.weak.outline.expect
new file mode 100644
index 0000000..5bcbed2
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart.weak.outline.expect
@@ -0,0 +1,29 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+typedef G<invariant X extends core::Object? = dynamic> = (X%) → X%;
+typedef A<X extends (self::C<X>) → self::C<X> = (self::C<dynamic>) → self::C<dynamic>> = self::C<X>;
+typedef B<X extends (self::D<X>) → self::D<X> = (self::D<dynamic>) → self::D<dynamic>> = self::D<X>;
+class C<X extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::C::bar]/*isLegacy*/;
+  constructor foo() → self::C<self::C::X%>
+    ;
+  static factory •<X extends core::Object? = dynamic>() → self::C<self::C::•::X%>
+    ;
+  static factory bar<X extends core::Object? = dynamic>() → self::C<self::C::bar::X%>
+    let dynamic #redirecting_factory = self::C::• in let self::C::bar::X% #typeArg0 = null in invalid-expression;
+}
+class D<X extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::D::bar]/*isLegacy*/;
+  constructor •() → self::D<self::D::X%>
+    ;
+  static factory foo<X extends core::Object? = dynamic>() → self::D<self::D::foo::X%>
+    ;
+  static factory bar<X extends core::Object? = dynamic>() → self::D<self::D::bar::X%>
+    let dynamic #redirecting_factory = self::D::• in let self::D::bar::X% #typeArg0 = null in invalid-expression;
+}
+static method test() → dynamic
+  ;
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart.weak.transformed.expect b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart.weak.transformed.expect
new file mode 100644
index 0000000..e2f8dde
--- /dev/null
+++ b/pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart.weak.transformed.expect
@@ -0,0 +1,128 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:21:5: Error: Type argument 'C<Object?> Function(C<Never>)' doesn't conform to the bound 'C<X> Function(C<X>)' of the type variable 'X' on 'A'.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+// Try changing type arguments so that they conform to the bounds.
+//   A.foo(); // Error.
+//     ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:16:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef A<X extends G<C<X>>> = C<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:21:5: Context: If you want 'A<C<Object?> Function(C<Never>)>' to be a super-bounded type, note that the inverted type 'A<G<C<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+//   A.foo(); // Error.
+//     ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:23:3: Error: Type argument 'D<Object?> Function(D<Never>)' doesn't conform to the bound 'D<X> Function(D<X>)' of the type variable 'X' on 'B'.
+//  - 'D' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+// Try changing type arguments so that they conform to the bounds.
+//   B(); // Error.
+//   ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:17:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef B<X extends G<D<X>>> = D<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:23:3: Context: If you want 'B<D<Object?> Function(D<Never>)>' to be a super-bounded type, note that the inverted type 'B<G<D<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'D' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+//   B(); // Error.
+//   ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:20:3: Error: Type argument 'C<Object?> Function(C<Never>)' doesn't conform to the bound 'C<X> Function(C<X>)' of the type variable 'X' on 'A'.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+// Try changing type arguments so that they conform to the bounds.
+//   A(); // Error.
+//   ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:16:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef A<X extends G<C<X>>> = C<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:20:3: Context: If you want 'A<C<Object?> Function(C<Never>)>' to be a super-bounded type, note that the inverted type 'A<G<C<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+//   A(); // Error.
+//   ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:22:5: Error: Type argument 'C<Object?> Function(C<Never>)' doesn't conform to the bound 'C<X> Function(C<X>)' of the type variable 'X' on 'A'.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+// Try changing type arguments so that they conform to the bounds.
+//   A.bar(); // Error.
+//     ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:16:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef A<X extends G<C<X>>> = C<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:22:5: Context: If you want 'A<C<Object?> Function(C<Never>)>' to be a super-bounded type, note that the inverted type 'A<G<C<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'C' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+//   A.bar(); // Error.
+//     ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:24:5: Error: Type argument 'D<Object?> Function(D<Never>)' doesn't conform to the bound 'D<X> Function(D<X>)' of the type variable 'X' on 'B'.
+//  - 'D' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+// Try changing type arguments so that they conform to the bounds.
+//   B.foo(); // Error.
+//     ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:17:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef B<X extends G<D<X>>> = D<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:24:5: Context: If you want 'B<D<Object?> Function(D<Never>)>' to be a super-bounded type, note that the inverted type 'B<G<D<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'D' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+//   B.foo(); // Error.
+//     ^
+//
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:25:5: Error: Type argument 'D<Object?> Function(D<Never>)' doesn't conform to the bound 'D<X> Function(D<X>)' of the type variable 'X' on 'B'.
+//  - 'D' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+// Try changing type arguments so that they conform to the bounds.
+//   B.bar(); // Error.
+//     ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:17:11: Context: This is the type variable whose bound isn't conformed to.
+// typedef B<X extends G<D<X>>> = D<X>;
+//           ^
+// pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart:25:5: Context: If you want 'B<D<Object?> Function(D<Never>)>' to be a super-bounded type, note that the inverted type 'B<G<D<Never>>>' must then satisfy its bounds, which it does not.
+//  - 'D' is from 'pkg/front_end/testcases/nonfunction_type_aliases/issue45519_2.dart'.
+//  - 'Object' is from 'dart:core'.
+//   B.bar(); // Error.
+//     ^
+//
+import self as self;
+import "dart:core" as core;
+
+typedef G<invariant X extends core::Object? = dynamic> = (X%) → X%;
+typedef A<X extends (self::C<X>) → self::C<X> = (self::C<dynamic>) → self::C<dynamic>> = self::C<X>;
+typedef B<X extends (self::D<X>) → self::D<X> = (self::D<dynamic>) → self::D<dynamic>> = self::D<X>;
+class C<X extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::C::bar]/*isLegacy*/;
+  constructor foo() → self::C<self::C::X%>
+    : super core::Object::•() {}
+  static factory •<X extends core::Object? = dynamic>() → self::C<self::C::•::X%>
+    return new self::C::foo<self::C::•::X%>();
+  static factory bar<X extends core::Object? = dynamic>() → self::C<self::C::bar::X%>
+    let <X extends core::Object? = dynamic>() → self::C<X%> #redirecting_factory = self::C::• in let self::C::bar::X% #typeArg0 = null in invalid-expression;
+}
+class D<X extends core::Object? = dynamic> extends core::Object {
+  static final field dynamic _redirecting# = <dynamic>[self::D::bar]/*isLegacy*/;
+  constructor •() → self::D<self::D::X%>
+    : super core::Object::•()
+    ;
+  static factory foo<X extends core::Object? = dynamic>() → self::D<self::D::foo::X%>
+    return new self::D::•<self::D::foo::X%>();
+  static factory bar<X extends core::Object? = dynamic>() → self::D<self::D::bar::X%>
+    let Never #redirecting_factory = self::D::• in let self::D::bar::X% #typeArg0 = null in invalid-expression;
+}
+static method test() → dynamic {
+  self::C::•<(self::C<Never>) → self::C<core::Object?>>();
+  new self::C::foo<(self::C<Never>) → self::C<core::Object?>>();
+  self::C::•<(self::C<Never>) → self::C<core::Object?>>();
+  new self::D::•<(self::D<Never>) → self::D<core::Object?>>();
+  self::D::foo<(self::D<Never>) → self::D<core::Object?>>();
+  new self::D::•<(self::D<Never>) → self::D<core::Object?>>();
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/textual_outline.status b/pkg/front_end/testcases/textual_outline.status
index 13ce720..4486b80 100644
--- a/pkg/front_end/testcases/textual_outline.status
+++ b/pkg/front_end/testcases/textual_outline.status
@@ -135,6 +135,8 @@
 nonfunction_type_aliases/issue42446: FormatterCrash
 nonfunction_type_aliases/issue45051: FormatterCrash
 nonfunction_type_aliases/issue45464: FormatterCrash
+nonfunction_type_aliases/issue45519: FormatterCrash
+nonfunction_type_aliases/issue45519_2: FormatterCrash
 nonfunction_type_aliases/nullable_supertypes: FormatterCrash
 nonfunction_type_aliases/old_version: FormatterCrash
 nonfunction_type_aliases/unaliased_bounds_checks_in_constructor_calls: FormatterCrash
diff --git a/pkg/kernel/lib/src/bounds_checks.dart b/pkg/kernel/lib/src/bounds_checks.dart
index 13b791c..a0d9fb8 100644
--- a/pkg/kernel/lib/src/bounds_checks.dart
+++ b/pkg/kernel/lib/src/bounds_checks.dart
@@ -685,6 +685,17 @@
     }
     return createTypedef(node, newNullability, newTypeArguments);
   }
+
+  @override
+  DartType? visitFunctionType(FunctionType node, int variance) {
+    // The variance of the Typedef parameters should be taken into account only
+    // when for the NNBD code.
+    if (node.typedefType != null && isNonNullableByDefault) {
+      return node.typedefType!.accept1(this, variance);
+    } else {
+      return super.visitFunctionType(node, variance);
+    }
+  }
 }
 
 int computeVariance(TypeParameter typeParameter, DartType type,
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index af19ffa..4a63843 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -3249,13 +3249,6 @@
       // In --use-bare-instruction we reduce the extra indirection via the
       // [Function] object by storing the entry point directly into the hashmap.
       //
-      // Currently our AOT compiler will emit megamorphic calls in certain
-      // situations (namely in slow-path code of CheckedSmi* instructions).
-      //
-      // TODO(compiler-team): Change the CheckedSmi* slow path code to use
-      // normal switchable calls instead of megamorphic calls. (This is also a
-      // memory balance beause [MegamorphicCache]s are per-selector while
-      // [ICData] are per-callsite.)
       auto& cache = MegamorphicCache::Handle(d->zone());
       for (intptr_t i = start_index_; i < stop_index_; ++i) {
         cache ^= refs.At(i);
diff --git a/runtime/vm/compiler/aot/aot_call_specializer.cc b/runtime/vm/compiler/aot/aot_call_specializer.cc
index 49bbe05..215bca5 100644
--- a/runtime/vm/compiler/aot/aot_call_specializer.cc
+++ b/runtime/vm/compiler/aot/aot_call_specializer.cc
@@ -448,7 +448,6 @@
     CompileType* left_type = left_value->Type();
     CompileType* right_type = right_value->Type();
 
-    const bool is_equality_op = Token::IsEqualityOperator(op_kind);
     bool has_nullable_int_args =
         left_type->IsNullableInt() && right_type->IsNullableInt();
 
@@ -458,12 +457,6 @@
       }
     }
 
-    // NOTE: We cannot use strict comparisons if the receiver has an overridden
-    // == operator or if either side can be a double, since 1.0 == 1.
-    const bool can_use_strict_compare =
-        is_equality_op && has_nullable_int_args &&
-        (left_type->IsNullableSmi() || right_type->IsNullableSmi());
-
     // We only support binary operations if both operands are nullable integers
     // or when we can use a cheap strict comparison operation.
     if (!has_nullable_int_args) {
@@ -473,43 +466,32 @@
     switch (op_kind) {
       case Token::kEQ:
       case Token::kNE:
-      case Token::kLT:
-      case Token::kLTE:
-      case Token::kGT:
-      case Token::kGTE: {
-        const bool can_use_equality_compare =
-            is_equality_op && left_type->IsInt() && right_type->IsInt();
-
-        // We prefer equality compare, since it doesn't require boxing.
-        if (!can_use_equality_compare && can_use_strict_compare) {
+        if (left_type->IsNull() || left_type->IsNullableSmi() ||
+            right_type->IsNull() || right_type->IsNullableSmi()) {
           replacement = new (Z) StrictCompareInstr(
               instr->source(),
               (op_kind == Token::kEQ) ? Token::kEQ_STRICT : Token::kNE_STRICT,
               left_value->CopyWithType(Z), right_value->CopyWithType(Z),
               /*needs_number_check=*/false, DeoptId::kNone);
-          break;
-        }
-
-        if (can_use_equality_compare) {
+        } else {
+          const bool null_aware =
+              left_type->is_nullable() || right_type->is_nullable();
           replacement = new (Z) EqualityCompareInstr(
               instr->source(), op_kind, left_value->CopyWithType(Z),
               right_value->CopyWithType(Z), kMintCid, DeoptId::kNone,
-              Instruction::kNotSpeculative);
-        } else if (Token::IsRelationalOperator(op_kind)) {
-          left_value = PrepareStaticOpInput(left_value, kMintCid, instr);
-          right_value = PrepareStaticOpInput(right_value, kMintCid, instr);
-          replacement = new (Z) RelationalOpInstr(
-              instr->source(), op_kind, left_value, right_value, kMintCid,
-              DeoptId::kNone, Instruction::kNotSpeculative);
-        } else {
-          // TODO(dartbug.com/30480): Figure out how to handle null in
-          // equality comparisons.
-          replacement = new (Z)
-              CheckedSmiComparisonInstr(op_kind, left_value->CopyWithType(Z),
-                                        right_value->CopyWithType(Z), instr);
+              null_aware, Instruction::kNotSpeculative);
         }
         break;
-      }
+      case Token::kLT:
+      case Token::kLTE:
+      case Token::kGT:
+      case Token::kGTE:
+        left_value = PrepareStaticOpInput(left_value, kMintCid, instr);
+        right_value = PrepareStaticOpInput(right_value, kMintCid, instr);
+        replacement = new (Z) RelationalOpInstr(
+            instr->source(), op_kind, left_value, right_value, kMintCid,
+            DeoptId::kNone, Instruction::kNotSpeculative);
+        break;
       case Token::kMOD:
         replacement = TryOptimizeMod(instr, op_kind, left_value, right_value);
         if (replacement != nullptr) break;
@@ -625,7 +607,8 @@
           right_value = PrepareStaticOpInput(right_value, kDoubleCid, instr);
           replacement = new (Z) EqualityCompareInstr(
               instr->source(), op_kind, left_value, right_value, kDoubleCid,
-              DeoptId::kNone, Instruction::kNotSpeculative);
+              DeoptId::kNone, /*null_aware=*/false,
+              Instruction::kNotSpeculative);
           break;
         }
         break;
diff --git a/runtime/vm/compiler/aot/aot_call_specializer.h b/runtime/vm/compiler/aot/aot_call_specializer.h
index fd23ebf..b537ae8 100644
--- a/runtime/vm/compiler/aot/aot_call_specializer.h
+++ b/runtime/vm/compiler/aot/aot_call_specializer.h
@@ -59,12 +59,12 @@
   const Function& InterfaceTargetForTableDispatch(InstanceCallBaseInstr* call);
 
   // Try to replace a call with a more specialized instruction working on
-  // integers (e.g. BinaryInt64OpInstr, CheckedSmiComparisonInstr,
+  // integers (e.g. BinaryInt64OpInstr, EqualityCompareInstr,
   // RelationalOpInstr)
   bool TryOptimizeIntegerOperation(TemplateDartCall<0>* call, Token::Kind kind);
 
   // Try to replace a call with a more specialized instruction working on
-  // doubles (e.g. BinaryDoubleOpInstr, CheckedSmiComparisonInstr,
+  // doubles (e.g. BinaryDoubleOpInstr, EqualityCompareInstr,
   // RelationalOpInstr)
   bool TryOptimizeDoubleOperation(TemplateDartCall<0>* call, Token::Kind kind);
 
diff --git a/runtime/vm/compiler/backend/constant_propagator.cc b/runtime/vm/compiler/backend/constant_propagator.cc
index df2b9df..1ba1c36 100644
--- a/runtime/vm/compiler/backend/constant_propagator.cc
+++ b/runtime/vm/compiler/backend/constant_propagator.cc
@@ -1155,15 +1155,6 @@
   SetValue(binary_op, non_constant_);
 }
 
-void ConstantPropagator::VisitCheckedSmiOp(CheckedSmiOpInstr* instr) {
-  SetValue(instr, non_constant_);
-}
-
-void ConstantPropagator::VisitCheckedSmiComparison(
-    CheckedSmiComparisonInstr* instr) {
-  SetValue(instr, non_constant_);
-}
-
 void ConstantPropagator::VisitBinarySmiOp(BinarySmiOpInstr* instr) {
   VisitBinaryIntegerOp(instr);
 }
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.h b/runtime/vm/compiler/backend/flow_graph_compiler.h
index e05c568..6973f62 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.h
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.h
@@ -983,8 +983,6 @@
   friend class StoreIndexedInstr;        // For AddPcRelativeCallStubTarget().
   friend class StoreInstanceFieldInstr;  // For AddPcRelativeCallStubTarget().
   friend class CheckStackOverflowSlowPath;  // For pending_deoptimization_env_.
-  friend class CheckedSmiSlowPath;          // Same.
-  friend class CheckedSmiComparisonSlowPath;  // Same.
   friend class GraphInstrinsicCodeGenScope;   // For optimizing_.
 
   // Architecture specific implementation of simple native moves.
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 5c5db00..0bdc1df 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -2437,79 +2437,6 @@
   return op;
 }
 
-Definition* CheckedSmiOpInstr::Canonicalize(FlowGraph* flow_graph) {
-  if ((left()->Type()->ToCid() == kSmiCid) &&
-      (right()->Type()->ToCid() == kSmiCid)) {
-    Definition* replacement = NULL;
-    // Operations that can't deoptimize are specialized here: These include
-    // bit-wise operators and comparisons. Other arithmetic operations can
-    // overflow or divide by 0 and can't be specialized unless we have extra
-    // range information.
-    switch (op_kind()) {
-      case Token::kBIT_AND:
-        FALL_THROUGH;
-      case Token::kBIT_OR:
-        FALL_THROUGH;
-      case Token::kBIT_XOR:
-        replacement = new BinarySmiOpInstr(
-            op_kind(), new Value(left()->definition()),
-            new Value(right()->definition()), DeoptId::kNone);
-        FALL_THROUGH;
-      default:
-        break;
-    }
-    if (replacement != NULL) {
-      flow_graph->InsertBefore(this, replacement, env(), FlowGraph::kValue);
-      return replacement;
-    }
-  }
-  return this;
-}
-
-ComparisonInstr* CheckedSmiComparisonInstr::CopyWithNewOperands(Value* left,
-                                                                Value* right) {
-  UNREACHABLE();
-  return NULL;
-}
-
-Definition* CheckedSmiComparisonInstr::Canonicalize(FlowGraph* flow_graph) {
-  CompileType* left_type = left()->Type();
-  CompileType* right_type = right()->Type();
-  intptr_t op_cid = kIllegalCid;
-  SpeculativeMode speculative_mode = kGuardInputs;
-
-  if ((left_type->ToCid() == kSmiCid) && (right_type->ToCid() == kSmiCid)) {
-    op_cid = kSmiCid;
-  } else if (  // TODO(dartbug.com/30480): handle nullable types here
-      left_type->IsNullableInt() && !left_type->is_nullable() &&
-      right_type->IsNullableInt() && !right_type->is_nullable()) {
-    op_cid = kMintCid;
-    speculative_mode = kNotSpeculative;
-  }
-
-  if (op_cid != kIllegalCid) {
-    Definition* replacement = NULL;
-    if (Token::IsRelationalOperator(kind())) {
-      replacement = new RelationalOpInstr(
-          source(), kind(), left()->CopyWithType(), right()->CopyWithType(),
-          op_cid, DeoptId::kNone, speculative_mode);
-    } else if (Token::IsEqualityOperator(kind())) {
-      replacement = new EqualityCompareInstr(
-          source(), kind(), left()->CopyWithType(), right()->CopyWithType(),
-          op_cid, DeoptId::kNone, speculative_mode);
-    }
-    if (replacement != NULL) {
-      if (FLAG_trace_strong_mode_types && (op_cid == kMintCid)) {
-        THR_Print("[Strong mode] Optimization: replacing %s with %s\n",
-                  ToCString(), replacement->ToCString());
-      }
-      flow_graph->InsertBefore(this, replacement, env(), FlowGraph::kValue);
-      return replacement;
-    }
-  }
-  return this;
-}
-
 Definition* BinaryIntegerOpInstr::Canonicalize(FlowGraph* flow_graph) {
   // If both operands are constants evaluate this expression. Might
   // occur due to load forwarding after constant propagation pass
@@ -3669,6 +3596,30 @@
   return replacement;
 }
 
+Definition* EqualityCompareInstr::Canonicalize(FlowGraph* flow_graph) {
+  if (is_null_aware()) {
+    ASSERT(operation_cid() == kMintCid);
+    // Select more efficient instructions based on operand types.
+    CompileType* left_type = left()->Type();
+    CompileType* right_type = right()->Type();
+    if (left_type->IsNull() || left_type->IsNullableSmi() ||
+        right_type->IsNull() || right_type->IsNullableSmi()) {
+      auto replacement = new StrictCompareInstr(
+          source(),
+          (kind() == Token::kEQ) ? Token::kEQ_STRICT : Token::kNE_STRICT,
+          left()->CopyWithType(), right()->CopyWithType(),
+          /*needs_number_check=*/false, DeoptId::kNone);
+      flow_graph->InsertBefore(this, replacement, env(), FlowGraph::kValue);
+      return replacement;
+    } else {
+      if (!left_type->is_nullable() && !right_type->is_nullable()) {
+        set_null_aware(false);
+      }
+    }
+  }
+  return this;
+}
+
 Instruction* CheckClassInstr::Canonicalize(FlowGraph* flow_graph) {
   const intptr_t value_cid = value()->Type()->ToCid();
   if (value_cid == kDynamicCid) {
@@ -5816,7 +5767,8 @@
 ComparisonInstr* EqualityCompareInstr::CopyWithNewOperands(Value* new_left,
                                                            Value* new_right) {
   return new EqualityCompareInstr(source(), kind(), new_left, new_right,
-                                  operation_cid(), deopt_id());
+                                  operation_cid(), deopt_id(), is_null_aware(),
+                                  speculative_mode_);
 }
 
 ComparisonInstr* RelationalOpInstr::CopyWithNewOperands(Value* new_left,
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index 7befee0..fc05085 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -440,8 +440,6 @@
   M(AllocateUninitializedContext, _)                                           \
   M(CloneContext, _)                                                           \
   M(BinarySmiOp, kNoGC)                                                        \
-  M(CheckedSmiComparison, _)                                                   \
-  M(CheckedSmiOp, _)                                                           \
   M(BinaryInt32Op, kNoGC)                                                      \
   M(UnarySmiOp, kNoGC)                                                         \
   M(UnaryDoubleOp, kNoGC)                                                      \
@@ -4518,8 +4516,10 @@
                        Value* right,
                        intptr_t cid,
                        intptr_t deopt_id,
+                       bool null_aware = false,
                        SpeculativeMode speculative_mode = kGuardInputs)
       : TemplateComparison(source, kind, deopt_id),
+        null_aware_(null_aware),
         speculative_mode_(speculative_mode) {
     ASSERT(Token::IsEqualityOperator(kind));
     SetInputAt(0, left);
@@ -4535,8 +4535,12 @@
 
   virtual bool ComputeCanDeoptimize() const { return false; }
 
+  bool is_null_aware() const { return null_aware_; }
+  void set_null_aware(bool value) { null_aware_ = value; }
+
   virtual Representation RequiredInputRepresentation(intptr_t idx) const {
     ASSERT((idx == 0) || (idx == 1));
+    if (is_null_aware()) return kTagged;
     if (operation_cid() == kDoubleCid) return kUnboxedDouble;
     if (operation_cid() == kMintCid) return kUnboxedInt64;
     return kTagged;
@@ -4548,12 +4552,16 @@
 
   virtual bool AttributesEqual(Instruction* other) const {
     return ComparisonInstr::AttributesEqual(other) &&
+           (null_aware_ == other->AsEqualityCompare()->null_aware_) &&
            (speculative_mode_ == other->AsEqualityCompare()->speculative_mode_);
   }
 
+  virtual Definition* Canonicalize(FlowGraph* flow_graph);
+
   PRINT_OPERANDS_TO_SUPPORT
 
  private:
+  bool null_aware_;
   const SpeculativeMode speculative_mode_;
   DISALLOW_COPY_AND_ASSIGN(EqualityCompareInstr);
 };
@@ -7607,100 +7615,6 @@
   DISALLOW_COPY_AND_ASSIGN(UnaryInt64OpInstr);
 };
 
-class CheckedSmiOpInstr : public TemplateDefinition<2, Throws> {
- public:
-  CheckedSmiOpInstr(Token::Kind op_kind,
-                    Value* left,
-                    Value* right,
-                    TemplateDartCall<0>* call)
-      : TemplateDefinition(call->deopt_id()), call_(call), op_kind_(op_kind) {
-    ASSERT(call->type_args_len() == 0);
-    ASSERT(!call->IsInstanceCallBase() ||
-           call->AsInstanceCallBase()->CanReceiverBeSmiBasedOnInterfaceTarget(
-               Thread::Current()->zone()));
-
-    SetInputAt(0, left);
-    SetInputAt(1, right);
-  }
-
-  TemplateDartCall<0>* call() const { return call_; }
-  Token::Kind op_kind() const { return op_kind_; }
-  Value* left() const { return inputs_[0]; }
-  Value* right() const { return inputs_[1]; }
-
-  virtual bool ComputeCanDeoptimize() const { return false; }
-
-  virtual CompileType ComputeType() const;
-  virtual bool RecomputeType();
-
-  virtual bool HasUnknownSideEffects() const { return true; }
-  virtual bool CanCallDart() const { return true; }
-
-  virtual Definition* Canonicalize(FlowGraph* flow_graph);
-
-  PRINT_OPERANDS_TO_SUPPORT
-
-  DECLARE_INSTRUCTION(CheckedSmiOp)
-
- private:
-  TemplateDartCall<0>* call_;
-  const Token::Kind op_kind_;
-  DISALLOW_COPY_AND_ASSIGN(CheckedSmiOpInstr);
-};
-
-class CheckedSmiComparisonInstr : public TemplateComparison<2, Throws> {
- public:
-  CheckedSmiComparisonInstr(Token::Kind op_kind,
-                            Value* left,
-                            Value* right,
-                            TemplateDartCall<0>* call)
-      : TemplateComparison(call->source(), op_kind, call->deopt_id()),
-        call_(call),
-        is_negated_(false) {
-    ASSERT(call->type_args_len() == 0);
-    ASSERT(!call->IsInstanceCallBase() ||
-           call->AsInstanceCallBase()->CanReceiverBeSmiBasedOnInterfaceTarget(
-               Thread::Current()->zone()));
-
-    SetInputAt(0, left);
-    SetInputAt(1, right);
-  }
-
-  TemplateDartCall<0>* call() const { return call_; }
-
-  virtual bool ComputeCanDeoptimize() const { return false; }
-
-  virtual CompileType ComputeType() const;
-
-  virtual Definition* Canonicalize(FlowGraph* flow_graph);
-
-  virtual void NegateComparison() {
-    ComparisonInstr::NegateComparison();
-    is_negated_ = !is_negated_;
-  }
-
-  bool is_negated() const { return is_negated_; }
-
-  virtual bool HasUnknownSideEffects() const { return true; }
-  virtual bool CanCallDart() const { return true; }
-
-  PRINT_OPERANDS_TO_SUPPORT
-
-  DECLARE_INSTRUCTION(CheckedSmiComparison)
-
-  virtual void EmitBranchCode(FlowGraphCompiler* compiler, BranchInstr* branch);
-
-  virtual Condition EmitComparisonCode(FlowGraphCompiler* compiler,
-                                       BranchLabels labels);
-
-  virtual ComparisonInstr* CopyWithNewOperands(Value* left, Value* right);
-
- private:
-  TemplateDartCall<0>* call_;
-  bool is_negated_;
-  DISALLOW_COPY_AND_ASSIGN(CheckedSmiComparisonInstr);
-};
-
 class BinaryIntegerOpInstr : public TemplateDefinition<2, NoThrow, Pure> {
  public:
   BinaryIntegerOpInstr(Token::Kind op_kind,
diff --git a/runtime/vm/compiler/backend/il_arm.cc b/runtime/vm/compiler/backend/il_arm.cc
index 4f8e3aa..c8b681f 100644
--- a/runtime/vm/compiler/backend/il_arm.cc
+++ b/runtime/vm/compiler/backend/il_arm.cc
@@ -832,7 +832,7 @@
   __ Bind(&done);
 }
 
-static Condition TokenKindToSmiCondition(Token::Kind kind) {
+static Condition TokenKindToIntCondition(Token::Kind kind) {
   switch (kind) {
     case Token::kEQ:
       return EQ;
@@ -855,6 +855,16 @@
 LocationSummary* EqualityCompareInstr::MakeLocationSummary(Zone* zone,
                                                            bool opt) const {
   const intptr_t kNumInputs = 2;
+  if (is_null_aware()) {
+    const intptr_t kNumTemps = 1;
+    LocationSummary* locs = new (zone)
+        LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
+    locs->set_in(0, Location::RequiresRegister());
+    locs->set_in(1, Location::RequiresRegister());
+    locs->set_temp(0, Location::RequiresRegister());
+    locs->set_out(0, Location::RequiresRegister());
+    return locs;
+  }
   if (operation_cid() == kMintCid) {
     const intptr_t kNumTemps = 0;
     LocationSummary* locs = new (zone)
@@ -961,7 +971,7 @@
   Location right = locs->in(1);
   ASSERT(!left.IsConstant() || !right.IsConstant());
 
-  Condition true_condition = TokenKindToSmiCondition(kind);
+  Condition true_condition = TokenKindToIntCondition(kind);
 
   if (left.IsConstant()) {
     __ CompareObject(right.reg(), left.constant());
@@ -974,26 +984,6 @@
   return true_condition;
 }
 
-static Condition TokenKindToMintCondition(Token::Kind kind) {
-  switch (kind) {
-    case Token::kEQ:
-      return EQ;
-    case Token::kNE:
-      return NE;
-    case Token::kLT:
-      return LT;
-    case Token::kGT:
-      return GT;
-    case Token::kLTE:
-      return LE;
-    case Token::kGTE:
-      return GE;
-    default:
-      UNREACHABLE();
-      return VS;
-  }
-}
-
 static Condition EmitUnboxedMintEqualityOp(FlowGraphCompiler* compiler,
                                            LocationSummary* locs,
                                            Token::Kind kind) {
@@ -1009,7 +999,7 @@
   __ cmp(left_lo, compiler::Operand(right_lo));
   // Compare upper if lower is equal.
   __ cmp(left_hi, compiler::Operand(right_hi), EQ);
-  return TokenKindToMintCondition(kind);
+  return TokenKindToIntCondition(kind);
 }
 
 static Condition EmitUnboxedMintComparisonOp(FlowGraphCompiler* compiler,
@@ -1056,6 +1046,45 @@
   return lo_cond;
 }
 
+static Condition EmitNullAwareInt64ComparisonOp(FlowGraphCompiler* compiler,
+                                                LocationSummary* locs,
+                                                Token::Kind kind,
+                                                BranchLabels labels) {
+  ASSERT((kind == Token::kEQ) || (kind == Token::kNE));
+  const Register left = locs->in(0).reg();
+  const Register right = locs->in(1).reg();
+  const Register temp = locs->temp(0).reg();
+  const Condition true_condition = TokenKindToIntCondition(kind);
+  compiler::Label* equal_result =
+      (true_condition == EQ) ? labels.true_label : labels.false_label;
+  compiler::Label* not_equal_result =
+      (true_condition == EQ) ? labels.false_label : labels.true_label;
+
+  // Check if operands have the same value. If they don't, then they could
+  // be equal only if both of them are Mints with the same value.
+  __ cmp(left, compiler::Operand(right));
+  __ b(equal_result, EQ);
+  __ and_(temp, left, compiler::Operand(right));
+  __ BranchIfSmi(temp, not_equal_result);
+  __ CompareClassId(left, kMintCid, temp);
+  __ b(not_equal_result, NE);
+  __ CompareClassId(right, kMintCid, temp);
+  __ b(not_equal_result, NE);
+  __ LoadFieldFromOffset(temp, left, compiler::target::Mint::value_offset());
+  __ LoadFieldFromOffset(TMP, right, compiler::target::Mint::value_offset());
+  __ cmp(temp, compiler::Operand(TMP));
+  __ LoadFieldFromOffset(
+      temp, left,
+      compiler::target::Mint::value_offset() + compiler::target::kWordSize,
+      compiler::kFourBytes, EQ);
+  __ LoadFieldFromOffset(
+      TMP, right,
+      compiler::target::Mint::value_offset() + compiler::target::kWordSize,
+      compiler::kFourBytes, EQ);
+  __ cmp(temp, compiler::Operand(TMP), EQ);
+  return true_condition;
+}
+
 static Condition TokenKindToDoubleCondition(Token::Kind kind) {
   switch (kind) {
     case Token::kEQ:
@@ -1097,6 +1126,10 @@
 
 Condition EqualityCompareInstr::EmitComparisonCode(FlowGraphCompiler* compiler,
                                                    BranchLabels labels) {
+  if (is_null_aware()) {
+    ASSERT(operation_cid() == kMintCid);
+    return EmitNullAwareInt64ComparisonOp(compiler, locs(), kind(), labels);
+  }
   if (operation_cid() == kSmiCid) {
     return EmitSmiComparisonOp(compiler, locs(), kind());
   } else if (operation_cid() == kMintCid) {
@@ -3864,334 +3897,6 @@
   }
 }
 
-class CheckedSmiSlowPath : public TemplateSlowPathCode<CheckedSmiOpInstr> {
- public:
-  CheckedSmiSlowPath(CheckedSmiOpInstr* instruction, intptr_t try_index)
-      : TemplateSlowPathCode(instruction), try_index_(try_index) {}
-
-  static constexpr intptr_t kNumSlowPathArgs = 2;
-
-  virtual void EmitNativeCode(FlowGraphCompiler* compiler) {
-    if (compiler::Assembler::EmittingComments()) {
-      __ Comment("slow path smi operation");
-    }
-    __ Bind(entry_label());
-    LocationSummary* locs = instruction()->locs();
-    Register result = locs->out(0).reg();
-    locs->live_registers()->Remove(Location::RegisterLocation(result));
-
-    compiler->SaveLiveRegisters(locs);
-    if (instruction()->env() != NULL) {
-      Environment* env =
-          compiler->SlowPathEnvironmentFor(instruction(), kNumSlowPathArgs);
-      compiler->pending_deoptimization_env_ = env;
-    }
-    __ Push(locs->in(0).reg());
-    __ Push(locs->in(1).reg());
-    const auto& selector = String::Handle(instruction()->call()->Selector());
-    const auto& arguments_descriptor =
-        Array::Handle(ArgumentsDescriptor::NewBoxed(
-            /*type_args_len=*/0, /*num_arguments=*/2));
-    compiler->EmitMegamorphicInstanceCall(
-        selector, arguments_descriptor, instruction()->call()->deopt_id(),
-        instruction()->source(), locs, try_index_, kNumSlowPathArgs);
-    __ mov(result, compiler::Operand(R0));
-    compiler->RestoreLiveRegisters(locs);
-    __ b(exit_label());
-    compiler->pending_deoptimization_env_ = NULL;
-  }
-
- private:
-  intptr_t try_index_;
-};
-
-LocationSummary* CheckedSmiOpInstr::MakeLocationSummary(Zone* zone,
-                                                        bool opt) const {
-  const intptr_t kNumInputs = 2;
-  const intptr_t kNumTemps = 0;
-  LocationSummary* summary = new (zone) LocationSummary(
-      zone, kNumInputs, kNumTemps, LocationSummary::kCallOnSlowPath);
-  summary->set_in(0, Location::RequiresRegister());
-  summary->set_in(1, Location::RequiresRegister());
-  summary->set_out(0, Location::RequiresRegister());
-  return summary;
-}
-
-void CheckedSmiOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
-  CheckedSmiSlowPath* slow_path =
-      new CheckedSmiSlowPath(this, compiler->CurrentTryIndex());
-  compiler->AddSlowPathCode(slow_path);
-  // Test operands if necessary.
-  Register left = locs()->in(0).reg();
-  Register right = locs()->in(1).reg();
-  Register result = locs()->out(0).reg();
-  intptr_t left_cid = this->left()->Type()->ToCid();
-  intptr_t right_cid = this->right()->Type()->ToCid();
-  bool combined_smi_check = false;
-  if (FLAG_use_slow_path) {
-    __ b(slow_path->entry_label());
-  } else if (this->left()->definition() == this->right()->definition()) {
-    __ BranchIfNotSmi(left, slow_path->entry_label());
-  } else if (left_cid == kSmiCid) {
-    __ BranchIfNotSmi(right, slow_path->entry_label());
-  } else if (right_cid == kSmiCid) {
-    __ BranchIfNotSmi(left, slow_path->entry_label());
-  } else {
-    combined_smi_check = true;
-    __ orr(result, left, compiler::Operand(right));
-    __ BranchIfNotSmi(result, slow_path->entry_label());
-  }
-  switch (op_kind()) {
-    case Token::kADD:
-      __ adds(result, left, compiler::Operand(right));
-      __ b(slow_path->entry_label(), VS);
-      break;
-    case Token::kSUB:
-      __ subs(result, left, compiler::Operand(right));
-      __ b(slow_path->entry_label(), VS);
-      break;
-    case Token::kMUL:
-      __ SmiUntag(IP, left);
-      __ smull(result, IP, IP, right);
-      // IP: result bits 32..63.
-      __ cmp(IP, compiler::Operand(result, ASR, 31));
-      __ b(slow_path->entry_label(), NE);
-      break;
-    case Token::kBIT_OR:
-      // Operation may be part of combined smi check.
-      if (!combined_smi_check) {
-        __ orr(result, left, compiler::Operand(right));
-      }
-      break;
-    case Token::kBIT_AND:
-      __ and_(result, left, compiler::Operand(right));
-      break;
-    case Token::kBIT_XOR:
-      __ eor(result, left, compiler::Operand(right));
-      break;
-    case Token::kSHL:
-      ASSERT(result != left);
-      ASSERT(result != right);
-      __ CompareImmediate(
-          right, compiler::target::ToRawSmi(compiler::target::kSmiBits));
-      __ b(slow_path->entry_label(), HI);
-
-      __ SmiUntag(TMP, right);
-      // Check for overflow by shifting left and shifting back arithmetically.
-      // If the result is different from the original, there was overflow.
-      __ Lsl(result, left, TMP);
-      __ cmp(left, compiler::Operand(result, ASR, TMP));
-      __ b(slow_path->entry_label(), NE);
-      break;
-    case Token::kSHR:
-      ASSERT(result != left);
-      ASSERT(result != right);
-      __ CompareImmediate(
-          right, compiler::target::ToRawSmi(compiler::target::kSmiBits));
-      __ b(slow_path->entry_label(), HI);
-
-      __ SmiUntag(result, right);
-      __ SmiUntag(TMP, left);
-      __ Asr(result, TMP, result);
-      __ SmiTag(result);
-      break;
-    case Token::kUSHR: {
-      ASSERT(result != left);
-      ASSERT(result != right);
-      __ CompareImmediate(right, compiler::target::ToRawSmi(kBitsPerInt64));
-      __ b(slow_path->entry_label(), UNSIGNED_GREATER_EQUAL);
-
-      compiler::Label done;
-      __ SmiUntag(result, right);
-      // 64-bit representation of left operand value:
-      //
-      //       ss...sssss  s  s  xxxxxxxxxxxxx
-      //       |        |  |  |  |           |
-      //       63      32  31 30 kSmiBits-1  0
-      //
-      // Where 's' is a sign bit.
-      //
-      // If left operand is negative (sign bit is set), then
-      // result will fit into Smi range if and only if
-      // the shift amount >= 64 - kSmiBits.
-      //
-      // If left operand is non-negative, the result always
-      // fits into Smi range.
-      //
-      __ CompareImmediate(result, 64 - compiler::target::kSmiBits);
-      // Shift amount >= 64 - kSmiBits > 32, but < 64.
-      // Result is guaranteed to fit into Smi range.
-      // Low (Smi) part of the left operand is shifted out.
-      // High part is filled with sign bits.
-      __ sub(result, result, compiler::Operand(32), GE);
-      __ Asr(TMP, left, compiler::Operand(31), GE);
-      __ Lsr(result, TMP, result, GE);
-      __ SmiTag(result, GE);
-      __ b(&done, GE);
-      // Shift amount < 64 - kSmiBits.
-      // If left is negative, then result will not fit into Smi range.
-      // Also deopt in case of negative shift amount.
-      __ tst(left, compiler::Operand(left));
-      __ b(slow_path->entry_label(), MI);
-      // At this point left operand is non-negative, so unsigned shift
-      // can't overflow.
-      __ CompareImmediate(result, compiler::target::kSmiBits);
-      // Left operand >= 0, shift amount >= kSmiBits. Result is 0.
-      __ LoadImmediate(result, 0, GE);
-      // Left operand >= 0, shift amount < kSmiBits < 32.
-      __ SmiUntag(TMP, left, LT);
-      __ Lsr(result, TMP, result, LT);
-      __ SmiTag(result, LT);
-      __ Bind(&done);
-      break;
-    }
-    default:
-      UNREACHABLE();
-  }
-  __ Bind(slow_path->exit_label());
-}
-
-class CheckedSmiComparisonSlowPath
-    : public TemplateSlowPathCode<CheckedSmiComparisonInstr> {
- public:
-  static constexpr intptr_t kNumSlowPathArgs = 2;
-
-  CheckedSmiComparisonSlowPath(CheckedSmiComparisonInstr* instruction,
-                               Environment* env,
-                               intptr_t try_index,
-                               BranchLabels labels,
-                               bool merged)
-      : TemplateSlowPathCode(instruction),
-        try_index_(try_index),
-        labels_(labels),
-        merged_(merged),
-        env_(env) {
-    // The environment must either come from the comparison or the environment
-    // was cleared from the comparison (and moved to a branch).
-    ASSERT(env == instruction->env() ||
-           (merged && instruction->env() == nullptr));
-  }
-
-  virtual void EmitNativeCode(FlowGraphCompiler* compiler) {
-    if (compiler::Assembler::EmittingComments()) {
-      __ Comment("slow path smi operation");
-    }
-    __ Bind(entry_label());
-    LocationSummary* locs = instruction()->locs();
-    Register result = merged_ ? locs->temp(0).reg() : locs->out(0).reg();
-    locs->live_registers()->Remove(Location::RegisterLocation(result));
-
-    compiler->SaveLiveRegisters(locs);
-    if (env_ != nullptr) {
-      compiler->pending_deoptimization_env_ =
-          compiler->SlowPathEnvironmentFor(env_, locs, kNumSlowPathArgs);
-    }
-    __ Push(locs->in(0).reg());
-    __ Push(locs->in(1).reg());
-    const auto& selector = String::Handle(instruction()->call()->Selector());
-    const auto& arguments_descriptor =
-        Array::Handle(ArgumentsDescriptor::NewBoxed(
-            /*type_args_len=*/0, /*num_arguments=*/2));
-    compiler->EmitMegamorphicInstanceCall(
-        selector, arguments_descriptor, instruction()->call()->deopt_id(),
-        instruction()->source(), locs, try_index_, kNumSlowPathArgs);
-    __ mov(result, compiler::Operand(R0));
-    compiler->RestoreLiveRegisters(locs);
-    compiler->pending_deoptimization_env_ = nullptr;
-    if (merged_) {
-      __ CompareObject(result, Bool::True());
-      __ b(instruction()->is_negated() ? labels_.false_label
-                                       : labels_.true_label,
-           EQ);
-      __ b(instruction()->is_negated() ? labels_.true_label
-                                       : labels_.false_label);
-    } else {
-      if (instruction()->is_negated()) {
-        // Need to negate the result of slow path call.
-        __ CompareObject(result, Bool::True());
-        __ LoadObject(result, Bool::True(), NE);
-        __ LoadObject(result, Bool::False(), EQ);
-      }
-      __ b(exit_label());
-    }
-  }
-
- private:
-  intptr_t try_index_;
-  BranchLabels labels_;
-  bool merged_;
-  Environment* env_;
-};
-
-LocationSummary* CheckedSmiComparisonInstr::MakeLocationSummary(
-    Zone* zone,
-    bool opt) const {
-  const intptr_t kNumInputs = 2;
-  const intptr_t kNumTemps = 1;
-  LocationSummary* summary = new (zone) LocationSummary(
-      zone, kNumInputs, kNumTemps, LocationSummary::kCallOnSlowPath);
-  summary->set_in(0, Location::RequiresRegister());
-  summary->set_in(1, Location::RequiresRegister());
-  summary->set_temp(0, Location::RequiresRegister());
-  summary->set_out(0, Location::RequiresRegister());
-  return summary;
-}
-
-Condition CheckedSmiComparisonInstr::EmitComparisonCode(
-    FlowGraphCompiler* compiler,
-    BranchLabels labels) {
-  return EmitSmiComparisonOp(compiler, locs(), kind());
-}
-
-#define EMIT_SMI_CHECK                                                         \
-  Register left = locs()->in(0).reg();                                         \
-  Register right = locs()->in(1).reg();                                        \
-  Register temp = locs()->temp(0).reg();                                       \
-  intptr_t left_cid = this->left()->Type()->ToCid();                           \
-  intptr_t right_cid = this->right()->Type()->ToCid();                         \
-  if (FLAG_use_slow_path) {                                                    \
-    __ b(slow_path->entry_label());                                            \
-  } else if (this->left()->definition() == this->right()->definition()) {      \
-    __ BranchIfNotSmi(left, slow_path->entry_label());                         \
-  } else if (left_cid == kSmiCid) {                                            \
-    __ BranchIfNotSmi(right, slow_path->entry_label());                        \
-  } else if (right_cid == kSmiCid) {                                           \
-    __ BranchIfNotSmi(left, slow_path->entry_label());                         \
-  } else {                                                                     \
-    __ orr(temp, left, compiler::Operand(right));                              \
-    __ BranchIfNotSmi(temp, slow_path->entry_label());                         \
-  }
-
-void CheckedSmiComparisonInstr::EmitBranchCode(FlowGraphCompiler* compiler,
-                                               BranchInstr* branch) {
-  BranchLabels labels = compiler->CreateBranchLabels(branch);
-  CheckedSmiComparisonSlowPath* slow_path = new CheckedSmiComparisonSlowPath(
-      this, branch->env(), compiler->CurrentTryIndex(), labels,
-      /* merged = */ true);
-  compiler->AddSlowPathCode(slow_path);
-  EMIT_SMI_CHECK;
-  Condition true_condition = EmitComparisonCode(compiler, labels);
-  ASSERT(true_condition != kInvalidCondition);
-  EmitBranchOnCondition(compiler, true_condition, labels);
-  __ Bind(slow_path->exit_label());
-}
-
-void CheckedSmiComparisonInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
-  BranchLabels labels = {NULL, NULL, NULL};
-  CheckedSmiComparisonSlowPath* slow_path = new CheckedSmiComparisonSlowPath(
-      this, env(), compiler->CurrentTryIndex(), labels,
-      /* merged = */ false);
-  compiler->AddSlowPathCode(slow_path);
-  EMIT_SMI_CHECK;
-  Condition true_condition = EmitComparisonCode(compiler, labels);
-  ASSERT(true_condition != kInvalidCondition);
-  Register result = locs()->out(0).reg();
-  __ LoadObject(result, Bool::True(), true_condition);
-  __ LoadObject(result, Bool::False(), InvertCondition(true_condition));
-  __ Bind(slow_path->exit_label());
-}
-#undef EMIT_SMI_CHECK
-
 LocationSummary* BinarySmiOpInstr::MakeLocationSummary(Zone* zone,
                                                        bool opt) const {
   const intptr_t kNumInputs = 2;
diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc
index f8a5f78..84bcb80 100644
--- a/runtime/vm/compiler/backend/il_arm64.cc
+++ b/runtime/vm/compiler/backend/il_arm64.cc
@@ -740,7 +740,7 @@
   __ Bind(&done);
 }
 
-static Condition TokenKindToSmiCondition(Token::Kind kind) {
+static Condition TokenKindToIntCondition(Token::Kind kind) {
   switch (kind) {
     case Token::kEQ:
       return EQ;
@@ -847,7 +847,7 @@
   Location right = locs->in(1);
   ASSERT(!left.IsConstant() || !right.IsConstant());
 
-  Condition true_condition = TokenKindToSmiCondition(kind);
+  Condition true_condition = TokenKindToIntCondition(kind);
   if (left.IsConstant() || right.IsConstant()) {
     // Ensure constant is on the right.
     ConstantInstr* constant = nullptr;
@@ -891,7 +891,7 @@
   Location right = locs->in(1);
   ASSERT(!left.IsConstant() || !right.IsConstant());
 
-  Condition true_condition = TokenKindToSmiCondition(kind);
+  Condition true_condition = TokenKindToIntCondition(kind);
   if (left.IsConstant() || right.IsConstant()) {
     // Ensure constant is on the right.
     ConstantInstr* constant = nullptr;
@@ -925,6 +925,35 @@
   return true_condition;
 }
 
+static Condition EmitNullAwareInt64ComparisonOp(FlowGraphCompiler* compiler,
+                                                LocationSummary* locs,
+                                                Token::Kind kind,
+                                                BranchLabels labels) {
+  ASSERT((kind == Token::kEQ) || (kind == Token::kNE));
+  const Register left = locs->in(0).reg();
+  const Register right = locs->in(1).reg();
+  const Condition true_condition = TokenKindToIntCondition(kind);
+  compiler::Label* equal_result =
+      (true_condition == EQ) ? labels.true_label : labels.false_label;
+  compiler::Label* not_equal_result =
+      (true_condition == EQ) ? labels.false_label : labels.true_label;
+
+  // Check if operands have the same value. If they don't, then they could
+  // be equal only if both of them are Mints with the same value.
+  __ CompareObjectRegisters(left, right);
+  __ b(equal_result, EQ);
+  __ and_(TMP, left, compiler::Operand(right), compiler::kObjectBytes);
+  __ BranchIfSmi(TMP, not_equal_result);
+  __ CompareClassId(left, kMintCid);
+  __ b(not_equal_result, NE);
+  __ CompareClassId(right, kMintCid);
+  __ b(not_equal_result, NE);
+  __ LoadFieldFromOffset(TMP, left, Mint::value_offset());
+  __ LoadFieldFromOffset(TMP2, right, Mint::value_offset());
+  __ CompareRegisters(TMP, TMP2);
+  return true_condition;
+}
+
 LocationSummary* EqualityCompareInstr::MakeLocationSummary(Zone* zone,
                                                            bool opt) const {
   const intptr_t kNumInputs = 2;
@@ -941,13 +970,18 @@
     const intptr_t kNumTemps = 0;
     LocationSummary* locs = new (zone)
         LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
-    locs->set_in(0, LocationRegisterOrConstant(left()));
-    // Only one input can be a constant operand. The case of two constant
-    // operands should be handled by constant propagation.
-    // Only right can be a stack slot.
-    locs->set_in(1, locs->in(0).IsConstant()
-                        ? Location::RequiresRegister()
-                        : LocationRegisterOrConstant(right()));
+    if (is_null_aware()) {
+      locs->set_in(0, Location::RequiresRegister());
+      locs->set_in(1, Location::RequiresRegister());
+    } else {
+      locs->set_in(0, LocationRegisterOrConstant(left()));
+      // Only one input can be a constant operand. The case of two constant
+      // operands should be handled by constant propagation.
+      // Only right can be a stack slot.
+      locs->set_in(1, locs->in(0).IsConstant()
+                          ? Location::RequiresRegister()
+                          : LocationRegisterOrConstant(right()));
+    }
     locs->set_out(0, Location::RequiresRegister());
     return locs;
   }
@@ -993,6 +1027,10 @@
 
 Condition EqualityCompareInstr::EmitComparisonCode(FlowGraphCompiler* compiler,
                                                    BranchLabels labels) {
+  if (is_null_aware()) {
+    ASSERT(operation_cid() == kMintCid);
+    return EmitNullAwareInt64ComparisonOp(compiler, locs(), kind(), labels);
+  }
   if (operation_cid() == kSmiCid) {
     return EmitSmiComparisonOp(compiler, locs(), kind(), labels);
   } else if (operation_cid() == kMintCid) {
@@ -3429,311 +3467,6 @@
   }
 }
 
-class CheckedSmiSlowPath : public TemplateSlowPathCode<CheckedSmiOpInstr> {
- public:
-  static constexpr intptr_t kNumSlowPathArgs = 2;
-
-  CheckedSmiSlowPath(CheckedSmiOpInstr* instruction, intptr_t try_index)
-      : TemplateSlowPathCode(instruction), try_index_(try_index) {}
-
-  virtual void EmitNativeCode(FlowGraphCompiler* compiler) {
-    if (compiler::Assembler::EmittingComments()) {
-      __ Comment("slow path smi operation");
-    }
-    __ Bind(entry_label());
-    LocationSummary* locs = instruction()->locs();
-    Register result = locs->out(0).reg();
-    locs->live_registers()->Remove(Location::RegisterLocation(result));
-
-    compiler->SaveLiveRegisters(locs);
-    if (instruction()->env() != NULL) {
-      Environment* env =
-          compiler->SlowPathEnvironmentFor(instruction(), kNumSlowPathArgs);
-      compiler->pending_deoptimization_env_ = env;
-    }
-    __ PushPair(locs->in(1).reg(), locs->in(0).reg());
-    const auto& selector = String::Handle(instruction()->call()->Selector());
-    const auto& arguments_descriptor =
-        Array::Handle(ArgumentsDescriptor::NewBoxed(
-            /*type_args_len=*/0, /*num_arguments=*/2));
-    compiler->EmitMegamorphicInstanceCall(
-        selector, arguments_descriptor, instruction()->call()->deopt_id(),
-        instruction()->source(), locs, try_index_, kNumSlowPathArgs);
-    __ mov(result, R0);
-    compiler->RestoreLiveRegisters(locs);
-    __ b(exit_label());
-    compiler->pending_deoptimization_env_ = NULL;
-  }
-
- private:
-  intptr_t try_index_;
-};
-
-LocationSummary* CheckedSmiOpInstr::MakeLocationSummary(Zone* zone,
-                                                        bool opt) const {
-  const intptr_t kNumInputs = 2;
-  const intptr_t kNumTemps = 0;
-  LocationSummary* summary = new (zone) LocationSummary(
-      zone, kNumInputs, kNumTemps, LocationSummary::kCallOnSlowPath);
-  summary->set_in(0, Location::RequiresRegister());
-  summary->set_in(1, Location::RequiresRegister());
-  summary->set_out(0, Location::RequiresRegister());
-  return summary;
-}
-
-void CheckedSmiOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
-  CheckedSmiSlowPath* slow_path =
-      new CheckedSmiSlowPath(this, compiler->CurrentTryIndex());
-  compiler->AddSlowPathCode(slow_path);
-  // Test operands if necessary.
-  Register left = locs()->in(0).reg();
-  Register right = locs()->in(1).reg();
-  Register result = locs()->out(0).reg();
-  intptr_t left_cid = this->left()->Type()->ToCid();
-  intptr_t right_cid = this->right()->Type()->ToCid();
-  bool combined_smi_check = false;
-  if (FLAG_use_slow_path) {
-    __ b(slow_path->entry_label());
-  } else if (this->left()->definition() == this->right()->definition()) {
-    __ BranchIfNotSmi(left, slow_path->entry_label());
-  } else if (left_cid == kSmiCid) {
-    __ BranchIfNotSmi(right, slow_path->entry_label());
-  } else if (right_cid == kSmiCid) {
-    __ BranchIfNotSmi(left, slow_path->entry_label());
-  } else {
-    combined_smi_check = true;
-    __ orr(result, left, compiler::Operand(right));
-    __ BranchIfNotSmi(result, slow_path->entry_label());
-  }
-
-  switch (op_kind()) {
-    case Token::kADD:
-      __ adds(result, left, compiler::Operand(right), compiler::kObjectBytes);
-      __ b(slow_path->entry_label(), VS);
-      break;
-    case Token::kSUB:
-      __ subs(result, left, compiler::Operand(right), compiler::kObjectBytes);
-      __ b(slow_path->entry_label(), VS);
-      break;
-    case Token::kMUL:
-      __ SmiUntag(TMP, left);
-#if !defined(DART_COMPRESSED_POINTERS)
-      __ mul(result, TMP, right);
-      __ smulh(TMP, TMP, right);
-      // TMP: result bits 64..127.
-#else
-      __ smull(result, TMP, right);
-      __ AsrImmediate(TMP, result, 31);
-      // TMP: result bits 32..63.
-#endif
-      __ cmp(TMP, compiler::Operand(result, ASR, 63));
-      __ b(slow_path->entry_label(), NE);
-      break;
-    case Token::kBIT_OR:
-      // Operation may be part of combined smi check.
-      if (!combined_smi_check) {
-        __ orr(result, left, compiler::Operand(right));
-      }
-      break;
-    case Token::kBIT_AND:
-      __ and_(result, left, compiler::Operand(right));
-      break;
-    case Token::kBIT_XOR:
-      __ eor(result, left, compiler::Operand(right));
-      break;
-    case Token::kSHL:
-      ASSERT(result != left);
-      ASSERT(result != right);
-      __ CompareObject(right, Smi::ZoneHandle(Smi::New(Smi::kBits)));
-      __ b(slow_path->entry_label(), CS);
-
-      __ SmiUntag(TMP, right);
-      __ lslv(result, left, TMP, compiler::kObjectBytes);
-      __ asrv(TMP2, result, TMP, compiler::kObjectBytes);
-      __ cmp(left, compiler::Operand(TMP2), compiler::kObjectBytes);
-      __ b(slow_path->entry_label(), NE);  // Overflow.
-      break;
-    case Token::kSHR:
-      ASSERT(result != left);
-      ASSERT(result != right);
-      __ CompareObject(right, Smi::ZoneHandle(Smi::New(Smi::kBits)));
-      __ b(slow_path->entry_label(), CS);
-
-      __ SmiUntag(result, right);
-      __ SmiUntag(TMP, left);
-      __ asrv(result, TMP, result, compiler::kObjectBytes);
-      __ SmiTag(result);
-      break;
-    case Token::kUSHR:
-      ASSERT(result != left);
-      ASSERT(result != right);
-      __ CompareObject(right, Smi::ZoneHandle(Smi::New(kBitsPerInt64)));
-      __ b(slow_path->entry_label(), UNSIGNED_GREATER_EQUAL);
-
-      __ SmiUntag(result, right);
-      __ SmiUntag(TMP, left);
-      __ lsrv(result, TMP, result);
-      __ SmiTagAndBranchIfOverflow(result, slow_path->entry_label());
-      break;
-    default:
-      UNIMPLEMENTED();
-  }
-  __ Bind(slow_path->exit_label());
-}
-
-class CheckedSmiComparisonSlowPath
-    : public TemplateSlowPathCode<CheckedSmiComparisonInstr> {
- public:
-  static constexpr intptr_t kNumSlowPathArgs = 2;
-
-  CheckedSmiComparisonSlowPath(CheckedSmiComparisonInstr* instruction,
-                               Environment* env,
-                               intptr_t try_index,
-                               BranchLabels labels,
-                               bool merged)
-      : TemplateSlowPathCode(instruction),
-        try_index_(try_index),
-        labels_(labels),
-        merged_(merged),
-        env_(env) {
-    // The environment must either come from the comparison or the environment
-    // was cleared from the comparison (and moved to a branch).
-    ASSERT(env == instruction->env() ||
-           (merged && instruction->env() == nullptr));
-  }
-
-  virtual void EmitNativeCode(FlowGraphCompiler* compiler) {
-    if (compiler::Assembler::EmittingComments()) {
-      __ Comment("slow path smi operation");
-    }
-    __ Bind(entry_label());
-    LocationSummary* locs = instruction()->locs();
-    Register result = merged_ ? locs->temp(0).reg() : locs->out(0).reg();
-    locs->live_registers()->Remove(Location::RegisterLocation(result));
-
-    compiler->SaveLiveRegisters(locs);
-    if (env_ != nullptr) {
-      compiler->pending_deoptimization_env_ =
-          compiler->SlowPathEnvironmentFor(env_, locs, kNumSlowPathArgs);
-    }
-    __ PushPair(locs->in(1).reg(), locs->in(0).reg());
-    const auto& selector = String::Handle(instruction()->call()->Selector());
-    const auto& arguments_descriptor =
-        Array::Handle(ArgumentsDescriptor::NewBoxed(
-            /*type_args_len=*/0, /*num_arguments=*/2));
-    compiler->EmitMegamorphicInstanceCall(
-        selector, arguments_descriptor, instruction()->call()->deopt_id(),
-        instruction()->source(), locs, try_index_, kNumSlowPathArgs);
-    __ mov(result, R0);
-    compiler->RestoreLiveRegisters(locs);
-    compiler->pending_deoptimization_env_ = nullptr;
-    if (merged_) {
-      __ CompareObject(result, Bool::True());
-      __ b(instruction()->is_negated() ? labels_.false_label
-                                       : labels_.true_label,
-           EQ);
-      __ b(instruction()->is_negated() ? labels_.true_label
-                                       : labels_.false_label);
-      ASSERT(exit_label()->IsUnused());
-    } else {
-      ASSERT(!instruction()->is_negated());
-      __ b(exit_label());
-    }
-  }
-
- private:
-  intptr_t try_index_;
-  BranchLabels labels_;
-  bool merged_;
-  Environment* env_;
-};
-
-LocationSummary* CheckedSmiComparisonInstr::MakeLocationSummary(
-    Zone* zone,
-    bool opt) const {
-  const intptr_t kNumInputs = 2;
-  const intptr_t kNumTemps = 1;
-  LocationSummary* summary = new (zone) LocationSummary(
-      zone, kNumInputs, kNumTemps, LocationSummary::kCallOnSlowPath);
-  summary->set_in(0, Location::RequiresRegister());
-  summary->set_in(1, Location::RequiresRegister());
-  summary->set_temp(0, Location::RequiresRegister());
-  summary->set_out(0, Location::RequiresRegister());
-  return summary;
-}
-
-Condition CheckedSmiComparisonInstr::EmitComparisonCode(
-    FlowGraphCompiler* compiler,
-    BranchLabels labels) {
-  return EmitSmiComparisonOp(compiler, locs(), kind(), labels);
-}
-
-#define EMIT_SMI_CHECK                                                         \
-  Register left = locs()->in(0).reg();                                         \
-  Register right = locs()->in(1).reg();                                        \
-  Register temp = locs()->temp(0).reg();                                       \
-  intptr_t left_cid = this->left()->Type()->ToCid();                           \
-  intptr_t right_cid = this->right()->Type()->ToCid();                         \
-  if (FLAG_use_slow_path) {                                                    \
-    __ b(slow_path->entry_label());                                            \
-  } else if (this->left()->definition() == this->right()->definition()) {      \
-    __ BranchIfNotSmi(left, slow_path->entry_label());                         \
-  } else if (left_cid == kSmiCid) {                                            \
-    __ BranchIfNotSmi(right, slow_path->entry_label());                        \
-  } else if (right_cid == kSmiCid) {                                           \
-    __ BranchIfNotSmi(left, slow_path->entry_label());                         \
-  } else {                                                                     \
-    __ orr(temp, left, compiler::Operand(right));                              \
-    __ BranchIfNotSmi(temp, slow_path->entry_label());                         \
-  }
-
-void CheckedSmiComparisonInstr::EmitBranchCode(FlowGraphCompiler* compiler,
-                                               BranchInstr* branch) {
-  BranchLabels labels = compiler->CreateBranchLabels(branch);
-  CheckedSmiComparisonSlowPath* slow_path = new CheckedSmiComparisonSlowPath(
-      this, branch->env(), compiler->CurrentTryIndex(), labels,
-      /* merged = */ true);
-  compiler->AddSlowPathCode(slow_path);
-  EMIT_SMI_CHECK;
-  Condition true_condition = EmitComparisonCode(compiler, labels);
-  if (true_condition != kInvalidCondition) {
-    EmitBranchOnCondition(compiler, true_condition, labels);
-  }
-  // No need to bind slow_path->exit_label() as slow path exits through
-  // true/false branch labels.
-}
-
-void CheckedSmiComparisonInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
-  // Zone-allocate labels to pass them to slow-path which outlives local scope.
-  compiler::Label* true_label = new (Z) compiler::Label();
-  compiler::Label* false_label = new (Z) compiler::Label();
-  compiler::Label done;
-  BranchLabels labels = {true_label, false_label, false_label};
-  // In case of negated comparison result of a slow path call should be negated.
-  // For this purpose, 'merged' slow path is generated: it tests
-  // result of a call and jumps directly to true or false label.
-  CheckedSmiComparisonSlowPath* slow_path = new CheckedSmiComparisonSlowPath(
-      this, env(), compiler->CurrentTryIndex(), labels,
-      /* merged = */ is_negated());
-  compiler->AddSlowPathCode(slow_path);
-  EMIT_SMI_CHECK;
-  Condition true_condition = EmitComparisonCode(compiler, labels);
-  if (true_condition != kInvalidCondition) {
-    EmitBranchOnCondition(compiler, true_condition, labels);
-  }
-  Register result = locs()->out(0).reg();
-  __ Bind(false_label);
-  __ LoadObject(result, Bool::False());
-  __ b(&done);
-  __ Bind(true_label);
-  __ LoadObject(result, Bool::True());
-  __ Bind(&done);
-  // In case of negated comparison slow path exits through true/false labels.
-  if (!is_negated()) {
-    __ Bind(slow_path->exit_label());
-  }
-}
-
 LocationSummary* BinarySmiOpInstr::MakeLocationSummary(Zone* zone,
                                                        bool opt) const {
   const intptr_t kNumInputs = 2;
diff --git a/runtime/vm/compiler/backend/il_ia32.cc b/runtime/vm/compiler/backend/il_ia32.cc
index c7c46fc..8c6fe6d 100644
--- a/runtime/vm/compiler/backend/il_ia32.cc
+++ b/runtime/vm/compiler/backend/il_ia32.cc
@@ -529,7 +529,7 @@
   __ Bind(&done);
 }
 
-static Condition TokenKindToSmiCondition(Token::Kind kind) {
+static Condition TokenKindToIntCondition(Token::Kind kind) {
   switch (kind) {
     case Token::kEQ:
       return EQUAL;
@@ -665,7 +665,7 @@
   Location right = locs.in(1);
   ASSERT(!left.IsConstant() || !right.IsConstant());
 
-  Condition true_condition = TokenKindToSmiCondition(kind);
+  Condition true_condition = TokenKindToIntCondition(kind);
 
   if (left.IsConstant()) {
     __ CompareObject(right.reg(), left.constant());
@@ -680,26 +680,6 @@
   return true_condition;
 }
 
-static Condition TokenKindToMintCondition(Token::Kind kind) {
-  switch (kind) {
-    case Token::kEQ:
-      return EQUAL;
-    case Token::kNE:
-      return NOT_EQUAL;
-    case Token::kLT:
-      return LESS;
-    case Token::kGT:
-      return GREATER;
-    case Token::kLTE:
-      return LESS_EQUAL;
-    case Token::kGTE:
-      return GREATER_EQUAL;
-    default:
-      UNREACHABLE();
-      return OVERFLOW;
-  }
-}
-
 static Condition EmitUnboxedMintEqualityOp(FlowGraphCompiler* compiler,
                                            const LocationSummary& locs,
                                            Token::Kind kind,
@@ -718,7 +698,7 @@
   // Lower is equal, compare upper.
   __ cmpl(left2, right2);
   __ Bind(&done);
-  Condition true_condition = TokenKindToMintCondition(kind);
+  Condition true_condition = TokenKindToIntCondition(kind);
   return true_condition;
 }
 
@@ -803,6 +783,10 @@
 
 Condition EqualityCompareInstr::EmitComparisonCode(FlowGraphCompiler* compiler,
                                                    BranchLabels labels) {
+  if (is_null_aware()) {
+    // Null-aware EqualityCompare instruction is only used in AOT.
+    UNREACHABLE();
+  }
   if (operation_cid() == kSmiCid) {
     return EmitSmiComparisonOp(compiler, *locs(), kind(), labels);
   } else if (operation_cid() == kMintCid) {
@@ -3143,45 +3127,6 @@
   }
 }
 
-LocationSummary* CheckedSmiOpInstr::MakeLocationSummary(Zone* zone,
-                                                        bool opt) const {
-  // Only for precompiled code, not on ia32 currently.
-  UNIMPLEMENTED();
-  return NULL;
-}
-
-void CheckedSmiOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
-  // Only for precompiled code, not on ia32 currently.
-  UNIMPLEMENTED();
-}
-
-LocationSummary* CheckedSmiComparisonInstr::MakeLocationSummary(
-    Zone* zone,
-    bool opt) const {
-  // Only for precompiled code, not on ia32 currently.
-  UNIMPLEMENTED();
-  return NULL;
-}
-
-Condition CheckedSmiComparisonInstr::EmitComparisonCode(
-    FlowGraphCompiler* compiler,
-    BranchLabels labels) {
-  // Only for precompiled code, not on ia32 currently.
-  UNIMPLEMENTED();
-  return ZERO;
-}
-
-void CheckedSmiComparisonInstr::EmitBranchCode(FlowGraphCompiler* compiler,
-                                               BranchInstr* instr) {
-  // Only for precompiled code, not on ia32 currently.
-  UNIMPLEMENTED();
-}
-
-void CheckedSmiComparisonInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
-  // Only for precompiled code, not on ia32 currently.
-  UNIMPLEMENTED();
-}
-
 static bool IsSmiValue(const Object& constant, intptr_t value) {
   return constant.IsSmi() && (Smi::Cast(constant).Value() == value);
 }
diff --git a/runtime/vm/compiler/backend/il_printer.cc b/runtime/vm/compiler/backend/il_printer.cc
index b42d9de..5773401 100644
--- a/runtime/vm/compiler/backend/il_printer.cc
+++ b/runtime/vm/compiler/backend/il_printer.cc
@@ -730,22 +730,6 @@
   value()->PrintTo(f);
 }
 
-void CheckedSmiOpInstr::PrintOperandsTo(BaseTextBuffer* f) const {
-  f->Printf("%s", Token::Str(op_kind()));
-  f->AddString(", ");
-  left()->PrintTo(f);
-  f->AddString(", ");
-  right()->PrintTo(f);
-}
-
-void CheckedSmiComparisonInstr::PrintOperandsTo(BaseTextBuffer* f) const {
-  f->Printf("%s", Token::Str(kind()));
-  f->AddString(", ");
-  left()->PrintTo(f);
-  f->AddString(", ");
-  right()->PrintTo(f);
-}
-
 void BinaryIntegerOpInstr::PrintOperandsTo(BaseTextBuffer* f) const {
   f->Printf("%s", Token::Str(op_kind()));
   if (is_truncating()) {
diff --git a/runtime/vm/compiler/backend/il_test_helper.cc b/runtime/vm/compiler/backend/il_test_helper.cc
index b860562..a99374c 100644
--- a/runtime/vm/compiler/backend/il_test_helper.cc
+++ b/runtime/vm/compiler/backend/il_test_helper.cc
@@ -202,8 +202,7 @@
 
   // We expect there to be no deoptimizations.
   if (mode_ == CompilerPass::kAOT) {
-    // TODO(kustermann): Enable this once we get rid of [CheckedSmiSlowPath]s.
-    // EXPECT(deopt_info_array.IsNull() || deopt_info_array.Length() == 0);
+    EXPECT(deopt_info_array.IsNull() || deopt_info_array.Length() == 0);
   }
 }
 
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index 55eaf32..ed081c7 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -729,13 +729,18 @@
     const intptr_t kNumTemps = 0;
     LocationSummary* locs = new (zone)
         LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
-    locs->set_in(0, LocationRegisterOrConstant(left()));
-    // Only one input can be a constant operand. The case of two constant
-    // operands should be handled by constant propagation.
-    // Only right can be a stack slot.
-    locs->set_in(1, locs->in(0).IsConstant()
-                        ? Location::RequiresRegister()
-                        : LocationRegisterOrConstant(right()));
+    if (is_null_aware()) {
+      locs->set_in(0, Location::RequiresRegister());
+      locs->set_in(1, Location::RequiresRegister());
+    } else {
+      locs->set_in(0, LocationRegisterOrConstant(left()));
+      // Only one input can be a constant operand. The case of two constant
+      // operands should be handled by constant propagation.
+      // Only right can be a stack slot.
+      locs->set_in(1, locs->in(0).IsConstant()
+                          ? Location::RequiresRegister()
+                          : LocationRegisterOrConstant(right()));
+    }
     locs->set_out(0, Location::RequiresRegister());
     return locs;
   }
@@ -885,6 +890,35 @@
   return true_condition;
 }
 
+static Condition EmitNullAwareInt64ComparisonOp(FlowGraphCompiler* compiler,
+                                                const LocationSummary& locs,
+                                                Token::Kind kind,
+                                                BranchLabels labels) {
+  ASSERT((kind == Token::kEQ) || (kind == Token::kNE));
+  const Register left = locs.in(0).reg();
+  const Register right = locs.in(1).reg();
+  const Condition true_condition = TokenKindToIntCondition(kind);
+  compiler::Label* equal_result =
+      (true_condition == EQUAL) ? labels.true_label : labels.false_label;
+  compiler::Label* not_equal_result =
+      (true_condition == EQUAL) ? labels.false_label : labels.true_label;
+
+  // Check if operands have the same value. If they don't, then they could
+  // be equal only if both of them are Mints with the same value.
+  __ OBJ(cmp)(left, right);
+  __ j(EQUAL, equal_result);
+  __ OBJ(mov)(TMP, left);
+  __ OBJ(and)(TMP, right);
+  __ BranchIfSmi(TMP, not_equal_result);
+  __ CompareClassId(left, kMintCid);
+  __ j(NOT_EQUAL, not_equal_result);
+  __ CompareClassId(right, kMintCid);
+  __ j(NOT_EQUAL, not_equal_result);
+  __ movq(TMP, compiler::FieldAddress(left, Mint::value_offset()));
+  __ cmpq(TMP, compiler::FieldAddress(right, Mint::value_offset()));
+  return true_condition;
+}
+
 static Condition TokenKindToDoubleCondition(Token::Kind kind) {
   switch (kind) {
     case Token::kEQ:
@@ -923,6 +957,10 @@
 
 Condition EqualityCompareInstr::EmitComparisonCode(FlowGraphCompiler* compiler,
                                                    BranchLabels labels) {
+  if (is_null_aware()) {
+    ASSERT(operation_cid() == kMintCid);
+    return EmitNullAwareInt64ComparisonOp(compiler, *locs(), kind(), labels);
+  }
   if (operation_cid() == kSmiCid) {
     return EmitSmiComparisonOp(compiler, *locs(), kind());
   } else if (operation_cid() == kMintCid) {
@@ -3516,342 +3554,6 @@
   }
 }
 
-class CheckedSmiSlowPath : public TemplateSlowPathCode<CheckedSmiOpInstr> {
- public:
-  CheckedSmiSlowPath(CheckedSmiOpInstr* instruction, intptr_t try_index)
-      : TemplateSlowPathCode(instruction), try_index_(try_index) {}
-
-  static constexpr intptr_t kNumSlowPathArgs = 2;
-
-  virtual void EmitNativeCode(FlowGraphCompiler* compiler) {
-    if (compiler::Assembler::EmittingComments()) {
-      __ Comment("slow path smi operation");
-    }
-    __ Bind(entry_label());
-    LocationSummary* locs = instruction()->locs();
-    Register result = locs->out(0).reg();
-    locs->live_registers()->Remove(Location::RegisterLocation(result));
-
-    compiler->SaveLiveRegisters(locs);
-    if (instruction()->env() != NULL) {
-      Environment* env =
-          compiler->SlowPathEnvironmentFor(instruction(), kNumSlowPathArgs);
-      compiler->pending_deoptimization_env_ = env;
-    }
-    __ pushq(locs->in(0).reg());
-    __ pushq(locs->in(1).reg());
-    const auto& selector = String::Handle(instruction()->call()->Selector());
-    const auto& arguments_descriptor =
-        Array::Handle(ArgumentsDescriptor::NewBoxed(
-            /*type_args_len=*/0, /*num_arguments=*/2));
-    compiler->EmitMegamorphicInstanceCall(
-        selector, arguments_descriptor, instruction()->call()->deopt_id(),
-        instruction()->source(), locs, try_index_, kNumSlowPathArgs);
-    __ MoveRegister(result, RAX);
-    compiler->RestoreLiveRegisters(locs);
-    __ jmp(exit_label());
-    compiler->pending_deoptimization_env_ = NULL;
-  }
-
- private:
-  intptr_t try_index_;
-};
-
-LocationSummary* CheckedSmiOpInstr::MakeLocationSummary(Zone* zone,
-                                                        bool opt) const {
-  bool is_shift = (op_kind() == Token::kSHL) || (op_kind() == Token::kSHR) ||
-                  (op_kind() == Token::kUSHR);
-  const intptr_t kNumInputs = 2;
-  const intptr_t kNumTemps = is_shift ? 1 : 0;
-  LocationSummary* summary = new (zone) LocationSummary(
-      zone, kNumInputs, kNumTemps, LocationSummary::kCallOnSlowPath);
-  summary->set_in(0, Location::RequiresRegister());
-  summary->set_in(1, Location::RequiresRegister());
-  switch (op_kind()) {
-    case Token::kADD:
-    case Token::kSUB:
-    case Token::kMUL:
-    case Token::kSHL:
-    case Token::kSHR:
-    case Token::kUSHR:
-      summary->set_out(0, Location::RequiresRegister());
-      break;
-    case Token::kBIT_OR:
-    case Token::kBIT_AND:
-    case Token::kBIT_XOR:
-      summary->set_out(0, Location::SameAsFirstInput());
-      break;
-    default:
-      UNIMPLEMENTED();
-  }
-  if (is_shift) {
-    summary->set_temp(0, Location::RegisterLocation(RCX));
-  }
-  return summary;
-}
-
-void CheckedSmiOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
-  CheckedSmiSlowPath* slow_path =
-      new CheckedSmiSlowPath(this, compiler->CurrentTryIndex());
-  compiler->AddSlowPathCode(slow_path);
-  // Test operands if necessary.
-
-  intptr_t left_cid = left()->Type()->ToCid();
-  intptr_t right_cid = right()->Type()->ToCid();
-  Register left = locs()->in(0).reg();
-  Register right = locs()->in(1).reg();
-  if (FLAG_use_slow_path) {
-    __ jmp(slow_path->entry_label());
-  } else if (this->left()->definition() == this->right()->definition()) {
-    __ BranchIfNotSmi(left, slow_path->entry_label());
-  } else if (left_cid == kSmiCid) {
-    __ BranchIfNotSmi(right, slow_path->entry_label());
-  } else if (right_cid == kSmiCid) {
-    __ BranchIfNotSmi(left, slow_path->entry_label());
-  } else {
-    __ movq(TMP, left);
-    __ orq(TMP, right);
-    __ BranchIfNotSmi(TMP, slow_path->entry_label());
-  }
-  Register result = locs()->out(0).reg();
-  switch (op_kind()) {
-    case Token::kADD:
-      __ movq(result, left);
-      __ OBJ(add)(result, right);
-      __ j(OVERFLOW, slow_path->entry_label());
-      break;
-    case Token::kSUB:
-      __ movq(result, left);
-      __ OBJ(sub)(result, right);
-      __ j(OVERFLOW, slow_path->entry_label());
-      break;
-    case Token::kMUL:
-      __ movq(result, left);
-      __ SmiUntag(result);
-      __ OBJ(imul)(result, right);
-      __ j(OVERFLOW, slow_path->entry_label());
-      break;
-    case Token::kBIT_OR:
-      ASSERT(left == result);
-      __ orq(result, right);
-      break;
-    case Token::kBIT_AND:
-      ASSERT(left == result);
-      __ andq(result, right);
-      break;
-    case Token::kBIT_XOR:
-      ASSERT(left == result);
-      __ xorq(result, right);
-      break;
-    case Token::kSHL:
-      ASSERT(result != right);
-      ASSERT(locs()->temp(0).reg() == RCX);
-      __ CompareObject(right, Smi::ZoneHandle(Smi::New(Smi::kBits)));
-      __ j(ABOVE_EQUAL, slow_path->entry_label());
-
-      __ movq(RCX, right);
-      __ SmiUntag(RCX);
-      __ movq(result, left);
-      __ shlq(result, RCX);
-      __ movq(TMP, result);
-      __ OBJ(sar)(TMP, RCX);
-      __ OBJ(cmp)(TMP, left);
-      __ j(NOT_EQUAL, slow_path->entry_label());
-      break;
-    case Token::kSHR:
-      ASSERT(result != right);
-      ASSERT(locs()->temp(0).reg() == RCX);
-      __ CompareObject(right, Smi::ZoneHandle(Smi::New(Smi::kBits)));
-      __ j(ABOVE_EQUAL, slow_path->entry_label());
-
-      __ movq(RCX, right);
-      __ SmiUntag(RCX);
-      __ movq(result, left);
-      __ SmiUntag(result);
-      __ OBJ(sar)(result, RCX);
-      __ SmiTag(result);
-      break;
-    case Token::kUSHR: {
-      ASSERT(result != right);
-      ASSERT(locs()->temp(0).reg() == RCX);
-      __ CompareObject(right, Smi::ZoneHandle(Smi::New(kBitsPerInt64)));
-      __ j(ABOVE_EQUAL, slow_path->entry_label());
-
-      __ movq(RCX, right);
-      __ SmiUntag(RCX);
-      __ movq(result, left);
-      __ SmiUntagAndSignExtend(result);
-      __ shrq(result, RCX);
-      __ shlq(result, compiler::Immediate(1));  // SmiTag, keep hi bits.
-      __ j(OVERFLOW, slow_path->entry_label());
-#if defined(DART_COMPRESSED_POINTERS)
-      const Register temp = locs()->temp(0).reg();
-      __ movsxd(temp, result);
-      __ cmpq(temp, result);
-      __ j(NOT_EQUAL, slow_path->entry_label());
-#endif  // defined(DART_COMPRESSED_POINTERS)
-      break;
-    }
-    default:
-      UNIMPLEMENTED();
-  }
-  __ Bind(slow_path->exit_label());
-}
-
-class CheckedSmiComparisonSlowPath
-    : public TemplateSlowPathCode<CheckedSmiComparisonInstr> {
- public:
-  static constexpr intptr_t kNumSlowPathArgs = 2;
-
-  CheckedSmiComparisonSlowPath(CheckedSmiComparisonInstr* instruction,
-                               Environment* env,
-                               intptr_t try_index,
-                               BranchLabels labels,
-                               bool merged)
-      : TemplateSlowPathCode(instruction),
-        try_index_(try_index),
-        labels_(labels),
-        merged_(merged),
-        env_(env) {
-    // The environment must either come from the comparison or the environment
-    // was cleared from the comparison (and moved to a branch).
-    ASSERT(env == instruction->env() ||
-           (merged && instruction->env() == nullptr));
-  }
-
-  virtual void EmitNativeCode(FlowGraphCompiler* compiler) {
-    if (compiler::Assembler::EmittingComments()) {
-      __ Comment("slow path smi comparison");
-    }
-    __ Bind(entry_label());
-    LocationSummary* locs = instruction()->locs();
-    Register result = merged_ ? locs->temp(0).reg() : locs->out(0).reg();
-    locs->live_registers()->Remove(Location::RegisterLocation(result));
-
-    compiler->SaveLiveRegisters(locs);
-    if (env_ != nullptr) {
-      compiler->pending_deoptimization_env_ =
-          compiler->SlowPathEnvironmentFor(env_, locs, kNumSlowPathArgs);
-    }
-    __ pushq(locs->in(0).reg());
-    __ pushq(locs->in(1).reg());
-
-    const auto& selector = String::Handle(instruction()->call()->Selector());
-    const auto& arguments_descriptor =
-        Array::Handle(ArgumentsDescriptor::NewBoxed(
-            /*type_args_len=*/0, /*num_arguments=*/2));
-
-    compiler->EmitMegamorphicInstanceCall(
-        selector, arguments_descriptor, instruction()->call()->deopt_id(),
-        instruction()->source(), locs, try_index_, kNumSlowPathArgs);
-    __ MoveRegister(result, RAX);
-    compiler->RestoreLiveRegisters(locs);
-    compiler->pending_deoptimization_env_ = nullptr;
-    if (merged_) {
-      __ CompareObject(result, Bool::True());
-      __ j(EQUAL, instruction()->is_negated() ? labels_.false_label
-                                              : labels_.true_label);
-      __ jmp(instruction()->is_negated() ? labels_.true_label
-                                         : labels_.false_label);
-      ASSERT(exit_label()->IsUnused());
-    } else {
-      ASSERT(!instruction()->is_negated());
-      __ jmp(exit_label());
-    }
-  }
-
- private:
-  intptr_t try_index_;
-  BranchLabels labels_;
-  bool merged_;
-  Environment* env_;
-};
-
-LocationSummary* CheckedSmiComparisonInstr::MakeLocationSummary(
-    Zone* zone,
-    bool opt) const {
-  const intptr_t kNumInputs = 2;
-  const intptr_t kNumTemps = 1;
-  LocationSummary* summary = new (zone) LocationSummary(
-      zone, kNumInputs, kNumTemps, LocationSummary::kCallOnSlowPath);
-  summary->set_in(0, Location::RequiresRegister());
-  summary->set_in(1, Location::RequiresRegister());
-  summary->set_temp(0, Location::RequiresRegister());
-  summary->set_out(0, Location::RequiresRegister());
-  return summary;
-}
-
-Condition CheckedSmiComparisonInstr::EmitComparisonCode(
-    FlowGraphCompiler* compiler,
-    BranchLabels labels) {
-  return EmitSmiComparisonOp(compiler, *locs(), kind());
-}
-
-#define EMIT_SMI_CHECK                                                         \
-  intptr_t left_cid = left()->Type()->ToCid();                                 \
-  intptr_t right_cid = right()->Type()->ToCid();                               \
-  Register left = locs()->in(0).reg();                                         \
-  Register right = locs()->in(1).reg();                                        \
-  if (FLAG_use_slow_path) {                                                    \
-    __ jmp(slow_path->entry_label());                                          \
-  } else if (this->left()->definition() == this->right()->definition()) {      \
-    __ BranchIfNotSmi(left, slow_path->entry_label());                         \
-  } else if (left_cid == kSmiCid) {                                            \
-    __ BranchIfNotSmi(right, slow_path->entry_label());                        \
-  } else if (right_cid == kSmiCid) {                                           \
-    __ BranchIfNotSmi(left, slow_path->entry_label());                         \
-  } else {                                                                     \
-    __ movq(TMP, left);                                                        \
-    __ orq(TMP, right);                                                        \
-    __ BranchIfNotSmi(TMP, slow_path->entry_label());                          \
-  }
-
-void CheckedSmiComparisonInstr::EmitBranchCode(FlowGraphCompiler* compiler,
-                                               BranchInstr* branch) {
-  BranchLabels labels = compiler->CreateBranchLabels(branch);
-  CheckedSmiComparisonSlowPath* slow_path = new CheckedSmiComparisonSlowPath(
-      this, branch->env(), compiler->CurrentTryIndex(), labels,
-      /* merged = */ true);
-  compiler->AddSlowPathCode(slow_path);
-  EMIT_SMI_CHECK;
-  Condition true_condition = EmitComparisonCode(compiler, labels);
-  ASSERT(true_condition != kInvalidCondition);
-  EmitBranchOnCondition(compiler, true_condition, labels);
-  // No need to bind slow_path->exit_label() as slow path exits through
-  // true/false branch labels.
-}
-
-void CheckedSmiComparisonInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
-  // Zone-allocate labels to pass them to slow-path which outlives local scope.
-  compiler::Label* true_label = new (Z) compiler::Label();
-  compiler::Label* false_label = new (Z) compiler::Label();
-  compiler::Label done;
-  BranchLabels labels = {true_label, false_label, false_label};
-  // In case of negated comparison result of a slow path call should be negated.
-  // For this purpose, 'merged' slow path is generated: it tests
-  // result of a call and jumps directly to true or false label.
-  CheckedSmiComparisonSlowPath* slow_path = new CheckedSmiComparisonSlowPath(
-      this, env(), compiler->CurrentTryIndex(), labels,
-      /* merged = */ is_negated());
-  compiler->AddSlowPathCode(slow_path);
-  EMIT_SMI_CHECK;
-  Condition true_condition = EmitComparisonCode(compiler, labels);
-  ASSERT(true_condition != kInvalidCondition);
-  EmitBranchOnCondition(compiler, true_condition, labels,
-                        compiler::Assembler::kNearJump);
-  Register result = locs()->out(0).reg();
-  __ Bind(false_label);
-  __ LoadObject(result, Bool::False());
-  __ jmp(&done, compiler::Assembler::kNearJump);
-  __ Bind(true_label);
-  __ LoadObject(result, Bool::True());
-  __ Bind(&done);
-  // In case of negated comparison slow path exits through true/false labels.
-  if (!is_negated()) {
-    __ Bind(slow_path->exit_label());
-  }
-}
-
 static bool CanBeImmediate(const Object& constant) {
   return constant.IsSmi() &&
          compiler::Immediate(static_cast<int64_t>(constant.ptr())).is_int32();
diff --git a/runtime/vm/compiler/backend/type_propagator.cc b/runtime/vm/compiler/backend/type_propagator.cc
index c8e01c4..164e8bb 100644
--- a/runtime/vm/compiler/backend/type_propagator.cc
+++ b/runtime/vm/compiler/backend/type_propagator.cc
@@ -1595,30 +1595,6 @@
   return CompileType::Int();
 }
 
-CompileType CheckedSmiOpInstr::ComputeType() const {
-  if (left()->Type()->IsNullableInt() && right()->Type()->IsNullableInt()) {
-    const AbstractType& abstract_type =
-        AbstractType::ZoneHandle(Type::IntType());
-    TraceStrongModeType(this, abstract_type);
-    return CompileType::FromAbstractType(abstract_type,
-                                         CompileType::kNonNullable);
-  } else {
-    CompileType* type = call()->Type();
-    TraceStrongModeType(this, type);
-    return *type;
-  }
-}
-
-bool CheckedSmiOpInstr::RecomputeType() {
-  return UpdateType(ComputeType());
-}
-
-CompileType CheckedSmiComparisonInstr::ComputeType() const {
-  CompileType* type = call()->Type();
-  TraceStrongModeType(this, type);
-  return *type;
-}
-
 CompileType BoxIntegerInstr::ComputeType() const {
   return ValueFitsSmi() ? CompileType::FromCid(kSmiCid) : CompileType::Int();
 }
diff --git a/runtime/vm/os_thread_fuchsia.cc b/runtime/vm/os_thread_fuchsia.cc
index bf2469a..9108f5d 100644
--- a/runtime/vm/os_thread_fuchsia.cc
+++ b/runtime/vm/os_thread_fuchsia.cc
@@ -133,7 +133,7 @@
   return 0;
 }
 
-const ThreadId OSThread::kInvalidThreadId = ZX_KOID_INVALID;
+const ThreadId OSThread::kInvalidThreadId = ZX_HANDLE_INVALID;
 const ThreadJoinId OSThread::kInvalidThreadJoinId =
     static_cast<ThreadJoinId>(0);
 
@@ -163,15 +163,7 @@
 }
 
 ThreadId OSThread::GetCurrentThreadId() {
-  zx_info_handle_basic_t info;
-  zx_handle_t thread_handle = thrd_get_zx_handle(thrd_current());
-  zx_status_t status =
-      zx_object_get_info(thread_handle, ZX_INFO_HANDLE_BASIC, &info,
-                         sizeof(info), nullptr, nullptr);
-  if (status != ZX_OK) {
-    FATAL1("Failed to get thread koid: %s\n", zx_status_get_string(status));
-  }
-  return info.koid;
+  return thrd_get_zx_handle(thrd_current());
 }
 
 #ifdef SUPPORT_TIMELINE
diff --git a/runtime/vm/os_thread_fuchsia.h b/runtime/vm/os_thread_fuchsia.h
index da76e8e..e17b4a6 100644
--- a/runtime/vm/os_thread_fuchsia.h
+++ b/runtime/vm/os_thread_fuchsia.h
@@ -18,7 +18,7 @@
 namespace dart {
 
 typedef pthread_key_t ThreadLocalKey;
-typedef zx_koid_t ThreadId;
+typedef zx_handle_t ThreadId;
 typedef pthread_t ThreadJoinId;
 
 static const ThreadLocalKey kUnsetThreadLocalKey =
diff --git a/sdk/lib/_internal/vm/bin/socket_patch.dart b/sdk/lib/_internal/vm/bin/socket_patch.dart
index 339d9a7..62d56d5 100644
--- a/sdk/lib/_internal/vm/bin/socket_patch.dart
+++ b/sdk/lib/_internal/vm/bin/socket_patch.dart
@@ -491,7 +491,7 @@
   // a HttpServer, a WebSocket connection, a process pipe, etc.
   Object? owner;
 
-  static Future<List<_InternetAddress>> lookup(String host,
+  static Future<List<InternetAddress>> lookup(String host,
       {InternetAddressType type: InternetAddressType.any}) {
     return _IOService._dispatch(_IOService.socketLookup, [host, type._value])
         .then((response) {
@@ -506,9 +506,9 @@
     });
   }
 
-  static Stream<List<_InternetAddress>> lookupAsStream(String host,
+  static Stream<List<InternetAddress>> lookupAsStream(String host,
       {InternetAddressType type: InternetAddressType.any}) {
-    final controller = StreamController<List<_InternetAddress>>();
+    final controller = StreamController<List<InternetAddress>>();
     controller.onListen = () {
       lookup(host, type: type).then((list) {
         controller.add(list);
@@ -588,8 +588,8 @@
   /// This avoids making single OS lookup request that internally does both IPv4
   /// and IPv6 together, which on iOS sometimes seems to be taking unreasonably
   /// long because of slow IPv6 lookup even though IPv4 lookup is fast.
-  static Stream<List<_InternetAddress>> staggeredLookup(String host) {
-    final controller = StreamController<List<_InternetAddress>>(sync: true);
+  static Stream<List<InternetAddress>> staggeredLookup(String host) {
+    final controller = StreamController<List<InternetAddress>>(sync: true);
 
     controller.onListen = () {
       // Completed when there are no further addresses, or when the returned
@@ -684,7 +684,7 @@
       // so we run IPv4 and IPv6 name resolution in parallel(IPv6 slightly
       // delayed so if IPv4 is successfully looked up, we don't do IPv6 look up
       // at all) and grab first successfully resolved name we are able to connect to.
-      final Stream<List<_InternetAddress>> stream =
+      final Stream<List<InternetAddress>> stream =
           Platform.isIOS || staggeredLookupOverride
               ? staggeredLookup(hostname)
               : lookupAsStream(hostname);
@@ -697,7 +697,7 @@
       dynamic host,
       int port,
       _InternetAddress? source,
-      Stream<List<_InternetAddress>> addresses) {
+      Stream<List<InternetAddress>> addresses) {
     // Completer for result.
     final result = new Completer<_NativeSocket>();
     // Error, set if an error occurs.
@@ -713,14 +713,14 @@
     Timer? timer;
     // Addresses arrived from lookup stream, but haven't been tried to connect
     // to yet due to Timer-based throttling.
-    final pendingLookedUp = Queue<_InternetAddress>();
+    final pendingLookedUp = Queue<InternetAddress>();
 
     // When deciding how to handle errors we need to know whether more
     // addresses potentially are coming from the lookup stream.
     bool isLookedUpStreamClosed = false;
-    late StreamSubscription<List<_InternetAddress>> addressesSubscription;
+    late StreamSubscription<List<InternetAddress>> addressesSubscription;
 
-    Object? createConnection(_InternetAddress address, _InternetAddress? source,
+    Object? createConnection(InternetAddress address, _InternetAddress? source,
         _NativeSocket socket) {
       Object? connectionResult;
       if (address.type == InternetAddressType.unix) {
@@ -736,19 +736,20 @@
             connectionResult is Error ||
             connectionResult is OSError);
       } else {
+        final address_ = address as _InternetAddress;
         if (source == null) {
           connectionResult = socket.nativeCreateConnect(
-              address._in_addr, port, address._scope_id);
+              address_._in_addr, port, address_._scope_id);
         } else {
           connectionResult = socket.nativeCreateBindConnect(
-              address._in_addr, port, source._in_addr, address._scope_id);
+              address_._in_addr, port, source._in_addr, address_._scope_id);
         }
         assert(connectionResult == true || connectionResult is OSError);
       }
       return connectionResult;
     }
 
-    createConnectionError(Object? connectionResult, _InternetAddress address,
+    createConnectionError(Object? connectionResult, InternetAddress address,
         int port, _NativeSocket socket) {
       if (connectionResult is OSError) {
         final errorCode = connectionResult.errorCode;
diff --git a/tests/standalone/io/address_lookup_test.dart b/tests/standalone/io/address_lookup_test.dart
new file mode 100644
index 0000000..74b76c5
--- /dev/null
+++ b/tests/standalone/io/address_lookup_test.dart
@@ -0,0 +1,24 @@
+// 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.
+//
+// Verifies that one can provide timeout handler for InternetAddress.lookup,
+// which was reported broken https://github.com/dart-lang/sdk/issues/45542.
+
+import "dart:io";
+
+import "package:async_helper/async_helper.dart";
+import "package:expect/expect.dart";
+
+void main() async {
+  asyncStart();
+  final result = <InternetAddress>[];
+  try {
+    result.addAll(await InternetAddress.lookup("some.bad.host.name.7654321")
+        .timeout(const Duration(milliseconds: 1), onTimeout: () => []));
+  } catch (e) {
+    print('managed to fail with $e lookup before timeout');
+  }
+  Expect.isTrue(result.isEmpty);
+  asyncEnd();
+}
diff --git a/tests/standalone_2/io/address_lookup_test.dart b/tests/standalone_2/io/address_lookup_test.dart
new file mode 100644
index 0000000..74b76c5
--- /dev/null
+++ b/tests/standalone_2/io/address_lookup_test.dart
@@ -0,0 +1,24 @@
+// 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.
+//
+// Verifies that one can provide timeout handler for InternetAddress.lookup,
+// which was reported broken https://github.com/dart-lang/sdk/issues/45542.
+
+import "dart:io";
+
+import "package:async_helper/async_helper.dart";
+import "package:expect/expect.dart";
+
+void main() async {
+  asyncStart();
+  final result = <InternetAddress>[];
+  try {
+    result.addAll(await InternetAddress.lookup("some.bad.host.name.7654321")
+        .timeout(const Duration(milliseconds: 1), onTimeout: () => []));
+  } catch (e) {
+    print('managed to fail with $e lookup before timeout');
+  }
+  Expect.isTrue(result.isEmpty);
+  asyncEnd();
+}
diff --git a/tools/VERSION b/tools/VERSION
index 7096fe2..079095e 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 13
 PATCH 0
-PRERELEASE 187
+PRERELEASE 188
 PRERELEASE_PATCH 0
\ No newline at end of file