[cfe] Remove unnecesary uses of SubtypeCheckMode.ignoreNullabilities

Change-Id: I8b64ca39e7a153141885d428fc5bd95e446fcb49
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/135640
Commit-Queue: Dmitry Stefantsov <dmitryas@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/front_end/lib/src/fasta/builder/class_builder.dart b/pkg/front_end/lib/src/fasta/builder/class_builder.dart
index 3668b87..9a160af 100644
--- a/pkg/front_end/lib/src/fasta/builder/class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/class_builder.dart
@@ -67,6 +67,8 @@
 
 import '../kernel/types.dart' show Types;
 
+import '../loader.dart';
+
 import '../modifier.dart';
 
 import '../names.dart' show noSuchMethodName;
@@ -77,6 +79,8 @@
 
 import '../source/source_library_builder.dart' show SourceLibraryBuilder;
 
+import '../source/source_loader.dart';
+
 import '../type_inference/type_schema.dart' show UnknownType;
 
 import 'builder.dart';
@@ -688,50 +692,84 @@
       Supertype supertype, TypeEnvironment typeEnvironment) {
     SourceLibraryBuilder library = this.library;
 
-    List<TypeArgumentIssue> issues = findTypeArgumentIssues(
-        new InterfaceType(
-            supertype.classNode, library.nonNullable, supertype.typeArguments),
-        typeEnvironment,
-        allowSuperBounded: false);
-    if (issues != null) {
-      for (TypeArgumentIssue issue in issues) {
-        Message message;
+    Set<TypeArgumentIssue> legacyIssues = findTypeArgumentIssues(
+            new InterfaceType(supertype.classNode, library.nonNullable,
+                supertype.typeArguments),
+            typeEnvironment,
+            SubtypeCheckMode.ignoringNullabilities,
+            allowSuperBounded: false)
+        ?.toSet();
+    Set<TypeArgumentIssue> nnbdIssues =
+        library.isNonNullableByDefault && library.loader.performNnbdChecks
+            ? findTypeArgumentIssues(
+                    new InterfaceType(supertype.classNode, library.nonNullable,
+                        supertype.typeArguments),
+                    typeEnvironment,
+                    SubtypeCheckMode.withNullabilities,
+                    allowSuperBounded: false)
+                ?.toSet()
+            : null;
+    if (legacyIssues != null || nnbdIssues != null) {
+      Set<TypeArgumentIssue> mergedIssues = legacyIssues ?? {};
+      if (nnbdIssues != null) {
+        mergedIssues.addAll(nnbdIssues.where(
+            (issue) => legacyIssues == null || !legacyIssues.contains(issue)));
+      }
+      for (TypeArgumentIssue issue in mergedIssues) {
         DartType argument = issue.argument;
         TypeParameter typeParameter = issue.typeParameter;
         bool inferred = library.inferredTypes.contains(argument);
         if (argument is FunctionType && argument.typeParameters.length > 0) {
           if (inferred) {
-            message = templateGenericFunctionTypeInferredAsActualTypeArgument
-                .withArguments(argument, library.isNonNullableByDefault);
+            library.reportTypeArgumentIssue(
+                templateGenericFunctionTypeInferredAsActualTypeArgument
+                    .withArguments(argument, library.isNonNullableByDefault),
+                fileUri,
+                charOffset,
+                null);
           } else {
-            message = messageGenericFunctionTypeUsedAsActualTypeArgument;
+            library.reportTypeArgumentIssue(
+                messageGenericFunctionTypeUsedAsActualTypeArgument,
+                fileUri,
+                charOffset,
+                null);
           }
-          typeParameter = null;
         } else {
-          if (inferred) {
-            message =
-                templateIncorrectTypeArgumentInSupertypeInferred.withArguments(
+          void reportProblem(
+              Template<
+                      Message Function(DartType, DartType, String, String,
+                          String, String, bool)>
+                  template) {
+            library.reportTypeArgumentIssue(
+                template.withArguments(
                     argument,
                     typeParameter.bound,
                     typeParameter.name,
                     getGenericTypeName(issue.enclosingType),
                     supertype.classNode.name,
                     name,
-                    library.isNonNullableByDefault);
+                    library.isNonNullableByDefault),
+                fileUri,
+                charOffset,
+                typeParameter);
+          }
+
+          nnbdIssues ??= const {};
+          if (inferred) {
+            if (nnbdIssues.contains(issue) && !library.loader.nnbdStrongMode) {
+              reportProblem(
+                  templateIncorrectTypeArgumentInSupertypeInferredWarning);
+            } else {
+              reportProblem(templateIncorrectTypeArgumentInSupertypeInferred);
+            }
           } else {
-            message = templateIncorrectTypeArgumentInSupertype.withArguments(
-                argument,
-                typeParameter.bound,
-                typeParameter.name,
-                getGenericTypeName(issue.enclosingType),
-                supertype.classNode.name,
-                name,
-                library.isNonNullableByDefault);
+            if (nnbdIssues.contains(issue) && !library.loader.nnbdStrongMode) {
+              reportProblem(templateIncorrectTypeArgumentInSupertypeWarning);
+            } else {
+              reportProblem(templateIncorrectTypeArgumentInSupertype);
+            }
           }
         }
-
-        library.reportTypeArgumentIssue(
-            message, fileUri, charOffset, typeParameter);
       }
     }
   }
@@ -742,11 +780,26 @@
 
     // Check in bounds of own type variables.
     for (TypeParameter parameter in cls.typeParameters) {
-      List<TypeArgumentIssue> issues = findTypeArgumentIssues(
-          parameter.bound, typeEnvironment,
-          allowSuperBounded: true);
-      if (issues != null) {
-        for (TypeArgumentIssue issue in issues) {
+      Set<TypeArgumentIssue> legacyIssues = findTypeArgumentIssues(
+              parameter.bound,
+              typeEnvironment,
+              SubtypeCheckMode.ignoringNullabilities,
+              allowSuperBounded: true)
+          ?.toSet();
+      Set<TypeArgumentIssue> nnbdIssues =
+          library.isNonNullableByDefault && library.loader.performNnbdChecks
+              ? findTypeArgumentIssues(parameter.bound, typeEnvironment,
+                      SubtypeCheckMode.withNullabilities,
+                      allowSuperBounded: true)
+                  ?.toSet()
+              : null;
+      if (legacyIssues != null || nnbdIssues != null) {
+        Set<TypeArgumentIssue> mergedIssues = legacyIssues ?? {};
+        if (nnbdIssues != null) {
+          mergedIssues.addAll(nnbdIssues.where((issue) =>
+              legacyIssues == null || !legacyIssues.contains(issue)));
+        }
+        for (TypeArgumentIssue issue in mergedIssues) {
           DartType argument = issue.argument;
           TypeParameter typeParameter = issue.typeParameter;
           if (library.inferredTypes.contains(argument)) {
@@ -758,21 +811,37 @@
             continue;
           }
 
-          Message message;
           if (argument is FunctionType && argument.typeParameters.length > 0) {
-            message = messageGenericFunctionTypeUsedAsActualTypeArgument;
-            typeParameter = null;
+            library.reportTypeArgumentIssue(
+                messageGenericFunctionTypeUsedAsActualTypeArgument,
+                fileUri,
+                parameter.fileOffset,
+                null);
           } else {
-            message = templateIncorrectTypeArgument.withArguments(
-                argument,
-                typeParameter.bound,
-                typeParameter.name,
-                getGenericTypeName(issue.enclosingType),
-                library.isNonNullableByDefault);
-          }
+            void reportProblem(
+                Template<
+                        Message Function(
+                            DartType, DartType, String, String, bool)>
+                    template) {
+              library.reportTypeArgumentIssue(
+                  template.withArguments(
+                      argument,
+                      typeParameter.bound,
+                      typeParameter.name,
+                      getGenericTypeName(issue.enclosingType),
+                      library.isNonNullableByDefault),
+                  fileUri,
+                  parameter.fileOffset,
+                  typeParameter);
+            }
 
-          library.reportTypeArgumentIssue(
-              message, fileUri, parameter.fileOffset, typeParameter);
+            nnbdIssues ??= const {};
+            if (nnbdIssues.contains(issue) && !library.loader.nnbdStrongMode) {
+              reportProblem(templateIncorrectTypeArgumentWarning);
+            } else {
+              reportProblem(templateIncorrectTypeArgument);
+            }
+          }
         }
       }
     }
@@ -1178,11 +1247,11 @@
     DartType supertype = inParameter ? declaredType : interfaceType;
 
     if (types.isSubtypeOfKernel(
-        subtype, supertype, SubtypeCheckMode.ignoringNullabilities)) {
+        subtype, supertype, SubtypeCheckMode.withNullabilities)) {
       // No problem--the proper subtyping relation is satisfied.
     } else if (isCovariant &&
         types.isSubtypeOfKernel(
-            supertype, subtype, SubtypeCheckMode.ignoringNullabilities)) {
+            supertype, subtype, SubtypeCheckMode.withNullabilities)) {
       // No problem--the overriding parameter is marked "covariant" and has
       // a type which is a subtype of the parameter it overrides.
     } else if (subtype is InvalidType || supertype is InvalidType) {
@@ -1190,48 +1259,82 @@
       // been reported.
     } else {
       // Report an error.
-      String declaredMemberName =
-          '${declaredMember.enclosingClass.name}.${declaredMember.name.name}';
-      String interfaceMemberName =
-          '${interfaceMember.enclosingClass.name}.${interfaceMember.name.name}';
-      Message message;
-      int fileOffset;
-      if (declaredParameter == null) {
-        if (asIfDeclaredParameter) {
-          // Setter overridden by field
-          message = templateOverrideTypeMismatchSetter.withArguments(
-              declaredMemberName,
-              declaredType,
-              interfaceType,
-              interfaceMemberName,
-              library.isNonNullableByDefault);
+      bool isErrorInNnbdOptedOutMode = !types.isSubtypeOfKernel(
+              subtype, supertype, SubtypeCheckMode.ignoringNullabilities) &&
+          (!isCovariant ||
+              !types.isSubtypeOfKernel(
+                  supertype, subtype, SubtypeCheckMode.ignoringNullabilities));
+      Loader loader = library.loader;
+      bool performNnbdChecks = loader is SourceLoader &&
+          library.isNonNullableByDefault &&
+          loader.performNnbdChecks;
+      bool nnbdStrongMode = loader is SourceLoader && loader.nnbdStrongMode;
+      bool isError =
+          isErrorInNnbdOptedOutMode || performNnbdChecks && nnbdStrongMode;
+      bool isWarning =
+          !isErrorInNnbdOptedOutMode && performNnbdChecks && !nnbdStrongMode;
+      assert(
+          !isError || !isWarning,
+          "A compile-time problem can't be an error and a warning "
+          "at the same time.");
+      if (isError || isWarning) {
+        String declaredMemberName = '${declaredMember.enclosingClass.name}'
+            '.${declaredMember.name.name}';
+        String interfaceMemberName = '${interfaceMember.enclosingClass.name}'
+            '.${interfaceMember.name.name}';
+        Message message;
+        int fileOffset;
+        if (declaredParameter == null) {
+          if (asIfDeclaredParameter) {
+            // Setter overridden by field
+            Template<Message Function(String, DartType, DartType, String, bool)>
+                template = isError
+                    ? templateOverrideTypeMismatchSetter
+                    : templateOverrideTypeMismatchSetterWarning;
+            message = template.withArguments(
+                declaredMemberName,
+                declaredType,
+                interfaceType,
+                interfaceMemberName,
+                library.isNonNullableByDefault);
+          } else {
+            Template<Message Function(String, DartType, DartType, String, bool)>
+                template = isError
+                    ? templateOverrideTypeMismatchReturnType
+                    : templateOverrideTypeMismatchReturnTypeWarning;
+            message = template.withArguments(
+                declaredMemberName,
+                declaredType,
+                interfaceType,
+                interfaceMemberName,
+                library.isNonNullableByDefault);
+          }
+          fileOffset = declaredMember.fileOffset;
         } else {
-          message = templateOverrideTypeMismatchReturnType.withArguments(
+          Template<
+                  Message Function(
+                      String, String, DartType, DartType, String, bool)>
+              template = isError
+                  ? templateOverrideTypeMismatchParameter
+                  : templateOverrideTypeMismatchParameterWarning;
+          message = template.withArguments(
+              declaredParameter.name,
               declaredMemberName,
               declaredType,
               interfaceType,
               interfaceMemberName,
               library.isNonNullableByDefault);
+          fileOffset = declaredParameter.fileOffset;
         }
-        fileOffset = declaredMember.fileOffset;
-      } else {
-        message = templateOverrideTypeMismatchParameter.withArguments(
-            declaredParameter.name,
-            declaredMemberName,
-            declaredType,
-            interfaceType,
-            interfaceMemberName,
-            library.isNonNullableByDefault);
-        fileOffset = declaredParameter.fileOffset;
+        reportInvalidOverride(
+            isInterfaceCheck, declaredMember, message, fileOffset, noLength,
+            context: [
+              templateOverriddenMethodCause
+                  .withArguments(interfaceMember.name.name)
+                  .withLocation(_getMemberUri(interfaceMember),
+                      interfaceMember.fileOffset, noLength)
+            ]);
       }
-      reportInvalidOverride(
-          isInterfaceCheck, declaredMember, message, fileOffset, noLength,
-          context: [
-            templateOverriddenMethodCause
-                .withArguments(interfaceMember.name.name)
-                .withLocation(_getMemberUri(interfaceMember),
-                    interfaceMember.fileOffset, noLength)
-          ]);
     }
   }
 
@@ -1652,6 +1755,7 @@
         DartType typeArgument = typeArguments[i];
         // Check whether the [typeArgument] respects the bounds of
         // [typeParameter].
+        Loader loader = library.loader;
         if (!typeEnvironment.isSubtypeOf(typeArgument, typeParameterBound,
             SubtypeCheckMode.ignoringNullabilities)) {
           addProblem(
@@ -1662,6 +1766,29 @@
               redirectionTarget.charOffset,
               noLength);
           hasProblem = true;
+        } else if (library.isNonNullableByDefault &&
+            loader is SourceLoader &&
+            loader.performNnbdChecks) {
+          if (!typeEnvironment.isSubtypeOf(typeArgument, typeParameterBound,
+              SubtypeCheckMode.withNullabilities)) {
+            if (loader.nnbdStrongMode) {
+              addProblem(
+                  templateRedirectingFactoryIncompatibleTypeArgument
+                      .withArguments(typeArgument, typeParameterBound,
+                          library.isNonNullableByDefault),
+                  redirectionTarget.charOffset,
+                  noLength);
+              hasProblem = true;
+            } else {
+              addProblem(
+                  templateRedirectingFactoryIncompatibleTypeArgumentWarning
+                      .withArguments(typeArgument, typeParameterBound,
+                          library.isNonNullableByDefault),
+                  redirectionTarget.charOffset,
+                  noLength);
+              hasProblem = false;
+            }
+          }
         }
       }
     } else if (typeArguments == null &&
@@ -1716,6 +1843,7 @@
     if (redirecteeType == null) return;
 
     // Check whether [redirecteeType] <: [factoryType].
+    Loader loader = library.loader;
     if (!typeEnvironment.isSubtypeOf(
         redirecteeType, factoryType, SubtypeCheckMode.ignoringNullabilities)) {
       addProblem(
@@ -1723,6 +1851,25 @@
               redirecteeType, factoryType, library.isNonNullableByDefault),
           factory.redirectionTarget.charOffset,
           noLength);
+    } else if (library.isNonNullableByDefault &&
+        loader is SourceLoader &&
+        loader.performNnbdChecks) {
+      if (!typeEnvironment.isSubtypeOf(
+          redirecteeType, factoryType, SubtypeCheckMode.withNullabilities)) {
+        if (loader.nnbdStrongMode) {
+          addProblem(
+              templateIncompatibleRedirecteeFunctionType.withArguments(
+                  redirecteeType, factoryType, library.isNonNullableByDefault),
+              factory.redirectionTarget.charOffset,
+              noLength);
+        } else {
+          addProblem(
+              templateIncompatibleRedirecteeFunctionTypeWarning.withArguments(
+                  redirecteeType, factoryType, library.isNonNullableByDefault),
+              factory.redirectionTarget.charOffset,
+              noLength);
+        }
+      }
     }
   }
 
diff --git a/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart b/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart
index 8085b94..c28169c 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart
@@ -1053,6 +1053,44 @@
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<
+        Message Function(
+            DartType _type, DartType _type2, bool isNonNullableByDefault)>
+    templateIncompatibleRedirecteeFunctionTypeWarning = const Template<
+            Message Function(
+                DartType _type, DartType _type2, bool isNonNullableByDefault)>(
+        messageTemplate:
+            r"""The constructor function type '#type' isn't a subtype of '#type2'.""",
+        withArguments: _withArgumentsIncompatibleRedirecteeFunctionTypeWarning);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<
+        Message Function(
+            DartType _type, DartType _type2, bool isNonNullableByDefault)>
+    codeIncompatibleRedirecteeFunctionTypeWarning = const Code<
+            Message Function(
+                DartType _type, DartType _type2, bool isNonNullableByDefault)>(
+        "IncompatibleRedirecteeFunctionTypeWarning",
+        templateIncompatibleRedirecteeFunctionTypeWarning,
+        analyzerCodes: <String>["REDIRECT_TO_INVALID_TYPE"],
+        severity: Severity.warning);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsIncompatibleRedirecteeFunctionTypeWarning(
+    DartType _type, DartType _type2, bool isNonNullableByDefault) {
+  TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
+  List<Object> typeParts = labeler.labelType(_type);
+  List<Object> type2Parts = labeler.labelType(_type2);
+  String type = typeParts.join();
+  String type2 = type2Parts.join();
+  return new Message(codeIncompatibleRedirecteeFunctionTypeWarning,
+      message:
+          """The constructor function type '${type}' isn't a subtype of '${type2}'.""" +
+              labeler.originMessages,
+      arguments: {'type': _type, 'type2': _type2});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
         Message Function(DartType _type, DartType _type2, String name,
             String name2, bool isNonNullableByDefault)>
     templateIncorrectTypeArgument = const Template<
@@ -1150,6 +1188,56 @@
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<
+        Message Function(DartType _type, DartType _type2, String name,
+            String name2, bool isNonNullableByDefault)>
+    templateIncorrectTypeArgumentInReturnTypeWarning = const Template<
+            Message Function(DartType _type, DartType _type2, String name,
+                String name2, bool isNonNullableByDefault)>(
+        messageTemplate:
+            r"""Type argument '#type' doesn't conform to the bound '#type2' of the type variable '#name' on '#name2' in the return type.""",
+        tipTemplate:
+            r"""Try changing type arguments so that they conform to the bounds.""",
+        withArguments: _withArgumentsIncorrectTypeArgumentInReturnTypeWarning);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<
+        Message Function(DartType _type, DartType _type2, String name,
+            String name2, bool isNonNullableByDefault)>
+    codeIncorrectTypeArgumentInReturnTypeWarning = const Code<
+            Message Function(DartType _type, DartType _type2, String name,
+                String name2, bool isNonNullableByDefault)>(
+        "IncorrectTypeArgumentInReturnTypeWarning",
+        templateIncorrectTypeArgumentInReturnTypeWarning,
+        analyzerCodes: <String>["TYPE_ARGUMENT_NOT_MATCHING_BOUNDS"],
+        severity: Severity.warning);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsIncorrectTypeArgumentInReturnTypeWarning(DartType _type,
+    DartType _type2, String name, String name2, bool isNonNullableByDefault) {
+  TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
+  List<Object> typeParts = labeler.labelType(_type);
+  List<Object> type2Parts = labeler.labelType(_type2);
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  if (name2.isEmpty) throw 'No name provided';
+  name2 = demangleMixinApplicationName(name2);
+  String type = typeParts.join();
+  String type2 = type2Parts.join();
+  return new Message(codeIncorrectTypeArgumentInReturnTypeWarning,
+      message:
+          """Type argument '${type}' doesn't conform to the bound '${type2}' of the type variable '${name}' on '${name2}' in the return type.""" +
+              labeler.originMessages,
+      tip: """Try changing type arguments so that they conform to the bounds.""",
+      arguments: {
+        'type': _type,
+        'type2': _type2,
+        'name': name,
+        'name2': name2
+      });
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
         Message Function(
             DartType _type,
             DartType _type2,
@@ -1320,6 +1408,181 @@
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<
+        Message Function(
+            DartType _type,
+            DartType _type2,
+            String name,
+            String name2,
+            String name3,
+            String name4,
+            bool isNonNullableByDefault)>
+    templateIncorrectTypeArgumentInSupertypeInferredWarning = const Template<
+            Message Function(
+                DartType _type,
+                DartType _type2,
+                String name,
+                String name2,
+                String name3,
+                String name4,
+                bool isNonNullableByDefault)>(
+        messageTemplate:
+            r"""Inferred type argument '#type' doesn't conform to the bound '#type2' of the type variable '#name' on '#name2' in the supertype '#name3' of class '#name4'.""",
+        tipTemplate:
+            r"""Try specifying type arguments explicitly so that they conform to the bounds.""",
+        withArguments:
+            _withArgumentsIncorrectTypeArgumentInSupertypeInferredWarning);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<
+        Message Function(
+            DartType _type,
+            DartType _type2,
+            String name,
+            String name2,
+            String name3,
+            String name4,
+            bool isNonNullableByDefault)>
+    codeIncorrectTypeArgumentInSupertypeInferredWarning = const Code<
+            Message Function(
+                DartType _type,
+                DartType _type2,
+                String name,
+                String name2,
+                String name3,
+                String name4,
+                bool isNonNullableByDefault)>(
+        "IncorrectTypeArgumentInSupertypeInferredWarning",
+        templateIncorrectTypeArgumentInSupertypeInferredWarning,
+        analyzerCodes: <String>["TYPE_ARGUMENT_NOT_MATCHING_BOUNDS"],
+        severity: Severity.warning);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsIncorrectTypeArgumentInSupertypeInferredWarning(
+    DartType _type,
+    DartType _type2,
+    String name,
+    String name2,
+    String name3,
+    String name4,
+    bool isNonNullableByDefault) {
+  TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
+  List<Object> typeParts = labeler.labelType(_type);
+  List<Object> type2Parts = labeler.labelType(_type2);
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  if (name2.isEmpty) throw 'No name provided';
+  name2 = demangleMixinApplicationName(name2);
+  if (name3.isEmpty) throw 'No name provided';
+  name3 = demangleMixinApplicationName(name3);
+  if (name4.isEmpty) throw 'No name provided';
+  name4 = demangleMixinApplicationName(name4);
+  String type = typeParts.join();
+  String type2 = type2Parts.join();
+  return new Message(codeIncorrectTypeArgumentInSupertypeInferredWarning,
+      message:
+          """Inferred type argument '${type}' doesn't conform to the bound '${type2}' of the type variable '${name}' on '${name2}' in the supertype '${name3}' of class '${name4}'.""" +
+              labeler.originMessages,
+      tip:
+          """Try specifying type arguments explicitly so that they conform to the bounds.""",
+      arguments: {
+        'type': _type,
+        'type2': _type2,
+        'name': name,
+        'name2': name2,
+        'name3': name3,
+        'name4': name4
+      });
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+        Message Function(
+            DartType _type,
+            DartType _type2,
+            String name,
+            String name2,
+            String name3,
+            String name4,
+            bool isNonNullableByDefault)>
+    templateIncorrectTypeArgumentInSupertypeWarning = const Template<
+            Message Function(
+                DartType _type,
+                DartType _type2,
+                String name,
+                String name2,
+                String name3,
+                String name4,
+                bool isNonNullableByDefault)>(
+        messageTemplate:
+            r"""Type argument '#type' doesn't conform to the bound '#type2' of the type variable '#name' on '#name2' in the supertype '#name3' of class '#name4'.""",
+        tipTemplate:
+            r"""Try changing type arguments so that they conform to the bounds.""",
+        withArguments: _withArgumentsIncorrectTypeArgumentInSupertypeWarning);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<
+        Message Function(
+            DartType _type,
+            DartType _type2,
+            String name,
+            String name2,
+            String name3,
+            String name4,
+            bool isNonNullableByDefault)>
+    codeIncorrectTypeArgumentInSupertypeWarning = const Code<
+            Message Function(
+                DartType _type,
+                DartType _type2,
+                String name,
+                String name2,
+                String name3,
+                String name4,
+                bool isNonNullableByDefault)>(
+        "IncorrectTypeArgumentInSupertypeWarning",
+        templateIncorrectTypeArgumentInSupertypeWarning,
+        analyzerCodes: <String>["TYPE_ARGUMENT_NOT_MATCHING_BOUNDS"],
+        severity: Severity.warning);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsIncorrectTypeArgumentInSupertypeWarning(
+    DartType _type,
+    DartType _type2,
+    String name,
+    String name2,
+    String name3,
+    String name4,
+    bool isNonNullableByDefault) {
+  TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
+  List<Object> typeParts = labeler.labelType(_type);
+  List<Object> type2Parts = labeler.labelType(_type2);
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  if (name2.isEmpty) throw 'No name provided';
+  name2 = demangleMixinApplicationName(name2);
+  if (name3.isEmpty) throw 'No name provided';
+  name3 = demangleMixinApplicationName(name3);
+  if (name4.isEmpty) throw 'No name provided';
+  name4 = demangleMixinApplicationName(name4);
+  String type = typeParts.join();
+  String type2 = type2Parts.join();
+  return new Message(codeIncorrectTypeArgumentInSupertypeWarning,
+      message:
+          """Type argument '${type}' doesn't conform to the bound '${type2}' of the type variable '${name}' on '${name2}' in the supertype '${name3}' of class '${name4}'.""" +
+              labeler.originMessages,
+      tip:
+          """Try changing type arguments so that they conform to the bounds.""",
+      arguments: {
+        'type': _type,
+        'type2': _type2,
+        'name': name,
+        'name2': name2,
+        'name3': name3,
+        'name4': name4
+      });
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
         Message Function(DartType _type, DartType _type2, String name,
             String name2, bool isNonNullableByDefault)>
     templateIncorrectTypeArgumentInferred = const Template<
@@ -1369,6 +1632,56 @@
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<
         Message Function(DartType _type, DartType _type2, String name,
+            String name2, bool isNonNullableByDefault)>
+    templateIncorrectTypeArgumentInferredWarning = const Template<
+            Message Function(DartType _type, DartType _type2, String name,
+                String name2, bool isNonNullableByDefault)>(
+        messageTemplate:
+            r"""Inferred type argument '#type' doesn't conform to the bound '#type2' of the type variable '#name' on '#name2'.""",
+        tipTemplate:
+            r"""Try specifying type arguments explicitly so that they conform to the bounds.""",
+        withArguments: _withArgumentsIncorrectTypeArgumentInferredWarning);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<
+        Message Function(DartType _type, DartType _type2, String name,
+            String name2, bool isNonNullableByDefault)>
+    codeIncorrectTypeArgumentInferredWarning = const Code<
+            Message Function(DartType _type, DartType _type2, String name,
+                String name2, bool isNonNullableByDefault)>(
+        "IncorrectTypeArgumentInferredWarning",
+        templateIncorrectTypeArgumentInferredWarning,
+        analyzerCodes: <String>["TYPE_ARGUMENT_NOT_MATCHING_BOUNDS"],
+        severity: Severity.warning);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsIncorrectTypeArgumentInferredWarning(DartType _type,
+    DartType _type2, String name, String name2, bool isNonNullableByDefault) {
+  TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
+  List<Object> typeParts = labeler.labelType(_type);
+  List<Object> type2Parts = labeler.labelType(_type2);
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  if (name2.isEmpty) throw 'No name provided';
+  name2 = demangleMixinApplicationName(name2);
+  String type = typeParts.join();
+  String type2 = type2Parts.join();
+  return new Message(codeIncorrectTypeArgumentInferredWarning,
+      message:
+          """Inferred type argument '${type}' doesn't conform to the bound '${type2}' of the type variable '${name}' on '${name2}'.""" +
+              labeler.originMessages,
+      tip: """Try specifying type arguments explicitly so that they conform to the bounds.""",
+      arguments: {
+        'type': _type,
+        'type2': _type2,
+        'name': name,
+        'name2': name2
+      });
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+        Message Function(DartType _type, DartType _type2, String name,
             DartType _type3, String name2, bool isNonNullableByDefault)>
     templateIncorrectTypeArgumentQualified = const Template<
             Message Function(DartType _type, DartType _type2, String name,
@@ -1481,6 +1794,172 @@
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<
+        Message Function(DartType _type, DartType _type2, String name,
+            DartType _type3, String name2, bool isNonNullableByDefault)>
+    templateIncorrectTypeArgumentQualifiedInferredWarning = const Template<
+            Message Function(DartType _type, DartType _type2, String name,
+                DartType _type3, String name2, bool isNonNullableByDefault)>(
+        messageTemplate:
+            r"""Inferred type argument '#type' doesn't conform to the bound '#type2' of the type variable '#name' on '#type3.#name2'.""",
+        tipTemplate:
+            r"""Try specifying type arguments explicitly so that they conform to the bounds.""",
+        withArguments:
+            _withArgumentsIncorrectTypeArgumentQualifiedInferredWarning);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<
+        Message Function(DartType _type, DartType _type2, String name,
+            DartType _type3, String name2, bool isNonNullableByDefault)>
+    codeIncorrectTypeArgumentQualifiedInferredWarning = const Code<
+            Message Function(DartType _type, DartType _type2, String name,
+                DartType _type3, String name2, bool isNonNullableByDefault)>(
+        "IncorrectTypeArgumentQualifiedInferredWarning",
+        templateIncorrectTypeArgumentQualifiedInferredWarning,
+        analyzerCodes: <String>["TYPE_ARGUMENT_NOT_MATCHING_BOUNDS"],
+        severity: Severity.warning);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsIncorrectTypeArgumentQualifiedInferredWarning(
+    DartType _type,
+    DartType _type2,
+    String name,
+    DartType _type3,
+    String name2,
+    bool isNonNullableByDefault) {
+  TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
+  List<Object> typeParts = labeler.labelType(_type);
+  List<Object> type2Parts = labeler.labelType(_type2);
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  List<Object> type3Parts = labeler.labelType(_type3);
+  if (name2.isEmpty) throw 'No name provided';
+  name2 = demangleMixinApplicationName(name2);
+  String type = typeParts.join();
+  String type2 = type2Parts.join();
+  String type3 = type3Parts.join();
+  return new Message(codeIncorrectTypeArgumentQualifiedInferredWarning,
+      message:
+          """Inferred type argument '${type}' doesn't conform to the bound '${type2}' of the type variable '${name}' on '${type3}.${name2}'.""" +
+              labeler.originMessages,
+      tip: """Try specifying type arguments explicitly so that they conform to the bounds.""",
+      arguments: {
+        'type': _type,
+        'type2': _type2,
+        'name': name,
+        'type3': _type3,
+        'name2': name2
+      });
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+        Message Function(DartType _type, DartType _type2, String name,
+            DartType _type3, String name2, bool isNonNullableByDefault)>
+    templateIncorrectTypeArgumentQualifiedWarning = const Template<
+            Message Function(DartType _type, DartType _type2, String name,
+                DartType _type3, String name2, bool isNonNullableByDefault)>(
+        messageTemplate:
+            r"""Type argument '#type' doesn't conform to the bound '#type2' of the type variable '#name' on '#type3.#name2'.""",
+        tipTemplate:
+            r"""Try changing type arguments so that they conform to the bounds.""",
+        withArguments: _withArgumentsIncorrectTypeArgumentQualifiedWarning);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<
+        Message Function(DartType _type, DartType _type2, String name,
+            DartType _type3, String name2, bool isNonNullableByDefault)>
+    codeIncorrectTypeArgumentQualifiedWarning = const Code<
+            Message Function(DartType _type, DartType _type2, String name,
+                DartType _type3, String name2, bool isNonNullableByDefault)>(
+        "IncorrectTypeArgumentQualifiedWarning",
+        templateIncorrectTypeArgumentQualifiedWarning,
+        analyzerCodes: <String>["TYPE_ARGUMENT_NOT_MATCHING_BOUNDS"],
+        severity: Severity.warning);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsIncorrectTypeArgumentQualifiedWarning(
+    DartType _type,
+    DartType _type2,
+    String name,
+    DartType _type3,
+    String name2,
+    bool isNonNullableByDefault) {
+  TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
+  List<Object> typeParts = labeler.labelType(_type);
+  List<Object> type2Parts = labeler.labelType(_type2);
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  List<Object> type3Parts = labeler.labelType(_type3);
+  if (name2.isEmpty) throw 'No name provided';
+  name2 = demangleMixinApplicationName(name2);
+  String type = typeParts.join();
+  String type2 = type2Parts.join();
+  String type3 = type3Parts.join();
+  return new Message(codeIncorrectTypeArgumentQualifiedWarning,
+      message:
+          """Type argument '${type}' doesn't conform to the bound '${type2}' of the type variable '${name}' on '${type3}.${name2}'.""" +
+              labeler.originMessages,
+      tip: """Try changing type arguments so that they conform to the bounds.""",
+      arguments: {
+        'type': _type,
+        'type2': _type2,
+        'name': name,
+        'type3': _type3,
+        'name2': name2
+      });
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+        Message Function(DartType _type, DartType _type2, String name,
+            String name2, bool isNonNullableByDefault)>
+    templateIncorrectTypeArgumentWarning = const Template<
+            Message Function(DartType _type, DartType _type2, String name,
+                String name2, bool isNonNullableByDefault)>(
+        messageTemplate:
+            r"""Type argument '#type' doesn't conform to the bound '#type2' of the type variable '#name' on '#name2'.""",
+        tipTemplate:
+            r"""Try changing type arguments so that they conform to the bounds.""",
+        withArguments: _withArgumentsIncorrectTypeArgumentWarning);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<
+        Message Function(DartType _type, DartType _type2, String name,
+            String name2, bool isNonNullableByDefault)>
+    codeIncorrectTypeArgumentWarning = const Code<
+            Message Function(DartType _type, DartType _type2, String name,
+                String name2, bool isNonNullableByDefault)>(
+        "IncorrectTypeArgumentWarning", templateIncorrectTypeArgumentWarning,
+        analyzerCodes: <String>["TYPE_ARGUMENT_NOT_MATCHING_BOUNDS"],
+        severity: Severity.warning);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsIncorrectTypeArgumentWarning(DartType _type,
+    DartType _type2, String name, String name2, bool isNonNullableByDefault) {
+  TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
+  List<Object> typeParts = labeler.labelType(_type);
+  List<Object> type2Parts = labeler.labelType(_type2);
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  if (name2.isEmpty) throw 'No name provided';
+  name2 = demangleMixinApplicationName(name2);
+  String type = typeParts.join();
+  String type2 = type2Parts.join();
+  return new Message(codeIncorrectTypeArgumentWarning,
+      message:
+          """Type argument '${type}' doesn't conform to the bound '${type2}' of the type variable '${name}' on '${name2}'.""" +
+              labeler.originMessages,
+      tip: """Try changing type arguments so that they conform to the bounds.""",
+      arguments: {
+        'type': _type,
+        'type2': _type2,
+        'name': name,
+        'name2': name2
+      });
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
         Message Function(String name, DartType _type, DartType _type2,
             bool isNonNullableByDefault)>
     templateInitializingFormalTypeMismatch = const Template<
@@ -2503,6 +2982,64 @@
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<
+        Message Function(String name, String name2, DartType _type,
+            DartType _type2, String name3, bool isNonNullableByDefault)>
+    templateOverrideTypeMismatchParameterWarning = const Template<
+            Message Function(String name, String name2, DartType _type,
+                DartType _type2, String name3, bool isNonNullableByDefault)>(
+        messageTemplate:
+            r"""The parameter '#name' of the method '#name2' has type '#type', which does not match the corresponding type, '#type2', in the overridden method, '#name3'.""",
+        tipTemplate:
+            r"""Change to a supertype of '#type2', or, for a covariant parameter, a subtype.""",
+        withArguments: _withArgumentsOverrideTypeMismatchParameterWarning);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<
+        Message Function(String name, String name2, DartType _type,
+            DartType _type2, String name3, bool isNonNullableByDefault)>
+    codeOverrideTypeMismatchParameterWarning = const Code<
+            Message Function(String name, String name2, DartType _type,
+                DartType _type2, String name3, bool isNonNullableByDefault)>(
+        "OverrideTypeMismatchParameterWarning",
+        templateOverrideTypeMismatchParameterWarning,
+        analyzerCodes: <String>["INVALID_METHOD_OVERRIDE"],
+        severity: Severity.warning);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsOverrideTypeMismatchParameterWarning(
+    String name,
+    String name2,
+    DartType _type,
+    DartType _type2,
+    String name3,
+    bool isNonNullableByDefault) {
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  if (name2.isEmpty) throw 'No name provided';
+  name2 = demangleMixinApplicationName(name2);
+  TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
+  List<Object> typeParts = labeler.labelType(_type);
+  List<Object> type2Parts = labeler.labelType(_type2);
+  if (name3.isEmpty) throw 'No name provided';
+  name3 = demangleMixinApplicationName(name3);
+  String type = typeParts.join();
+  String type2 = type2Parts.join();
+  return new Message(codeOverrideTypeMismatchParameterWarning,
+      message:
+          """The parameter '${name}' of the method '${name2}' has type '${type}', which does not match the corresponding type, '${type2}', in the overridden method, '${name3}'.""" +
+              labeler.originMessages,
+      tip: """Change to a supertype of '${type2}', or, for a covariant parameter, a subtype.""",
+      arguments: {
+        'name': name,
+        'name2': name2,
+        'type': _type,
+        'type2': _type2,
+        'name3': name3
+      });
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
         Message Function(String name, DartType _type, DartType _type2,
             String name2, bool isNonNullableByDefault)>
     templateOverrideTypeMismatchReturnType = const Template<
@@ -2557,6 +3094,59 @@
 const Template<
         Message Function(String name, DartType _type, DartType _type2,
             String name2, bool isNonNullableByDefault)>
+    templateOverrideTypeMismatchReturnTypeWarning = const Template<
+            Message Function(String name, DartType _type, DartType _type2,
+                String name2, bool isNonNullableByDefault)>(
+        messageTemplate:
+            r"""The return type of the method '#name' is '#type', which does not match the return type, '#type2', of the overridden method, '#name2'.""",
+        tipTemplate: r"""Change to a subtype of '#type2'.""",
+        withArguments: _withArgumentsOverrideTypeMismatchReturnTypeWarning);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<
+        Message Function(String name, DartType _type, DartType _type2,
+            String name2, bool isNonNullableByDefault)>
+    codeOverrideTypeMismatchReturnTypeWarning = const Code<
+            Message Function(String name, DartType _type, DartType _type2,
+                String name2, bool isNonNullableByDefault)>(
+        "OverrideTypeMismatchReturnTypeWarning",
+        templateOverrideTypeMismatchReturnTypeWarning,
+        analyzerCodes: <String>["INVALID_METHOD_OVERRIDE"],
+        severity: Severity.warning);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsOverrideTypeMismatchReturnTypeWarning(
+    String name,
+    DartType _type,
+    DartType _type2,
+    String name2,
+    bool isNonNullableByDefault) {
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
+  List<Object> typeParts = labeler.labelType(_type);
+  List<Object> type2Parts = labeler.labelType(_type2);
+  if (name2.isEmpty) throw 'No name provided';
+  name2 = demangleMixinApplicationName(name2);
+  String type = typeParts.join();
+  String type2 = type2Parts.join();
+  return new Message(codeOverrideTypeMismatchReturnTypeWarning,
+      message:
+          """The return type of the method '${name}' is '${type}', which does not match the return type, '${type2}', of the overridden method, '${name2}'.""" +
+              labeler.originMessages,
+      tip: """Change to a subtype of '${type2}'.""",
+      arguments: {
+        'name': name,
+        'type': _type,
+        'type2': _type2,
+        'name2': name2
+      });
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+        Message Function(String name, DartType _type, DartType _type2,
+            String name2, bool isNonNullableByDefault)>
     templateOverrideTypeMismatchSetter = const Template<
             Message Function(String name, DartType _type, DartType _type2,
                 String name2, bool isNonNullableByDefault)>(
@@ -2600,6 +3190,57 @@
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<
+        Message Function(String name, DartType _type, DartType _type2,
+            String name2, bool isNonNullableByDefault)>
+    templateOverrideTypeMismatchSetterWarning = const Template<
+            Message Function(String name, DartType _type, DartType _type2,
+                String name2, bool isNonNullableByDefault)>(
+        messageTemplate:
+            r"""The field '#name' has type '#type', which does not match the corresponding type, '#type2', in the overridden setter, '#name2'.""",
+        withArguments: _withArgumentsOverrideTypeMismatchSetterWarning);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<
+        Message Function(String name, DartType _type, DartType _type2,
+            String name2, bool isNonNullableByDefault)>
+    codeOverrideTypeMismatchSetterWarning = const Code<
+            Message Function(String name, DartType _type, DartType _type2,
+                String name2, bool isNonNullableByDefault)>(
+        "OverrideTypeMismatchSetterWarning",
+        templateOverrideTypeMismatchSetterWarning,
+        analyzerCodes: <String>["INVALID_METHOD_OVERRIDE"],
+        severity: Severity.warning);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsOverrideTypeMismatchSetterWarning(
+    String name,
+    DartType _type,
+    DartType _type2,
+    String name2,
+    bool isNonNullableByDefault) {
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
+  List<Object> typeParts = labeler.labelType(_type);
+  List<Object> type2Parts = labeler.labelType(_type2);
+  if (name2.isEmpty) throw 'No name provided';
+  name2 = demangleMixinApplicationName(name2);
+  String type = typeParts.join();
+  String type2 = type2Parts.join();
+  return new Message(codeOverrideTypeMismatchSetterWarning,
+      message:
+          """The field '${name}' has type '${type}', which does not match the corresponding type, '${type2}', in the overridden setter, '${name2}'.""" +
+              labeler.originMessages,
+      arguments: {
+        'name': name,
+        'type': _type,
+        'type2': _type2,
+        'name2': name2
+      });
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
         Message Function(DartType _type, String name, String name2,
             DartType _type2, String name3, bool isNonNullableByDefault)>
     templateOverrideTypeVariablesBoundMismatch = const Template<
@@ -2694,6 +3335,45 @@
 const Template<
         Message Function(
             DartType _type, DartType _type2, bool isNonNullableByDefault)>
+    templateRedirectingFactoryIncompatibleTypeArgumentWarning = const Template<
+            Message Function(
+                DartType _type, DartType _type2, bool isNonNullableByDefault)>(
+        messageTemplate: r"""The type '#type' doesn't extend '#type2'.""",
+        tipTemplate: r"""Try using a different type as argument.""",
+        withArguments:
+            _withArgumentsRedirectingFactoryIncompatibleTypeArgumentWarning);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<
+        Message Function(
+            DartType _type, DartType _type2, bool isNonNullableByDefault)>
+    codeRedirectingFactoryIncompatibleTypeArgumentWarning = const Code<
+            Message Function(
+                DartType _type, DartType _type2, bool isNonNullableByDefault)>(
+        "RedirectingFactoryIncompatibleTypeArgumentWarning",
+        templateRedirectingFactoryIncompatibleTypeArgumentWarning,
+        analyzerCodes: <String>["TYPE_ARGUMENT_NOT_MATCHING_BOUNDS"],
+        severity: Severity.warning);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsRedirectingFactoryIncompatibleTypeArgumentWarning(
+    DartType _type, DartType _type2, bool isNonNullableByDefault) {
+  TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
+  List<Object> typeParts = labeler.labelType(_type);
+  List<Object> type2Parts = labeler.labelType(_type2);
+  String type = typeParts.join();
+  String type2 = type2Parts.join();
+  return new Message(codeRedirectingFactoryIncompatibleTypeArgumentWarning,
+      message: """The type '${type}' doesn't extend '${type2}'.""" +
+          labeler.originMessages,
+      tip: """Try using a different type as argument.""",
+      arguments: {'type': _type, 'type2': _type2});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+        Message Function(
+            DartType _type, DartType _type2, bool isNonNullableByDefault)>
     templateSpreadElementTypeMismatch = const Template<
             Message Function(
                 DartType _type, DartType _type2, bool isNonNullableByDefault)>(
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 526fdce..ace9ea1 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -1030,8 +1030,8 @@
     switch (asyncModifier) {
       case AsyncMarker.Async:
         DartType futureBottomType = libraryBuilder.loader.futureOfBottom;
-        if (!typeEnvironment.isSubtypeOf(futureBottomType, returnType,
-            SubtypeCheckMode.ignoringNullabilities)) {
+        if (!typeEnvironment.isSubtypeOf(
+            futureBottomType, returnType, SubtypeCheckMode.withNullabilities)) {
           problem = fasta.messageIllegalAsyncReturnType;
         }
         break;
@@ -1040,8 +1040,8 @@
         DartType streamBottomType = libraryBuilder.loader.streamOfBottom;
         if (returnType is VoidType) {
           problem = fasta.messageIllegalAsyncGeneratorVoidReturnType;
-        } else if (!typeEnvironment.isSubtypeOf(streamBottomType, returnType,
-            SubtypeCheckMode.ignoringNullabilities)) {
+        } else if (!typeEnvironment.isSubtypeOf(
+            streamBottomType, returnType, SubtypeCheckMode.withNullabilities)) {
           problem = fasta.messageIllegalAsyncGeneratorReturnType;
         }
         break;
@@ -1051,7 +1051,7 @@
         if (returnType is VoidType) {
           problem = fasta.messageIllegalSyncGeneratorVoidReturnType;
         } else if (!typeEnvironment.isSubtypeOf(iterableBottomType, returnType,
-            SubtypeCheckMode.ignoringNullabilities)) {
+            SubtypeCheckMode.withNullabilities)) {
           problem = fasta.messageIllegalSyncGeneratorReturnType;
         }
         break;
@@ -5560,7 +5560,7 @@
         if (formal != null && formal.type != null) {
           DartType formalType = formal.variable.type;
           if (!typeEnvironment.isSubtypeOf(formalType, builder.fieldType,
-              SubtypeCheckMode.ignoringNullabilities)) {
+              SubtypeCheckMode.withNullabilities)) {
             libraryBuilder.addProblem(
                 fasta.templateInitializingFormalTypeMismatch.withArguments(
                     name,
diff --git a/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart b/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart
index 874406a..fb3b432 100644
--- a/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart
@@ -2348,8 +2348,12 @@
             if (a.hadTypesInferred) {
               if (b.isSetter &&
                   (!a.isAssignable ||
-                      hierarchy.types.isSubtypeOfKernel(type, a.fieldType,
-                          SubtypeCheckMode.ignoringNullabilities))) {
+                      hierarchy.types.isSubtypeOfKernel(
+                          type,
+                          a.fieldType,
+                          classBuilder.library.isNonNullableByDefault
+                              ? SubtypeCheckMode.withNullabilities
+                              : SubtypeCheckMode.ignoringNullabilities))) {
                 type = a.fieldType;
               } else {
                 reportCantInferFieldType(classBuilder, a);
@@ -2636,10 +2640,10 @@
   bool isMoreSpecific(ClassHierarchyBuilder hierarchy, DartType a, DartType b) {
     if (isSetter) {
       return hierarchy.types
-          .isSubtypeOfKernel(b, a, SubtypeCheckMode.ignoringNullabilities);
+          .isSubtypeOfKernel(b, a, SubtypeCheckMode.withNullabilities);
     } else {
       return hierarchy.types
-          .isSubtypeOfKernel(a, b, SubtypeCheckMode.ignoringNullabilities);
+          .isSubtypeOfKernel(a, b, SubtypeCheckMode.withNullabilities);
     }
   }
 
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
index 645e2b95..430c3df 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -1983,7 +1983,7 @@
       }
     }
     return typeEnvironment.isSubtypeOf(
-        constantType, type, SubtypeCheckMode.ignoringNullabilities);
+        constantType, type, SubtypeCheckMode.withNullabilities);
   }
 
   Constant ensureIsSubtype(Constant constant, DartType type, TreeNode node) {
diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
index e2b7695..64cb9c8 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -1791,11 +1791,11 @@
       bool isMap = inferrer.typeSchemaEnvironment.isSubtypeOf(
           spreadType,
           inferrer.coreTypes.mapRawType(inferrer.library.nullable),
-          SubtypeCheckMode.ignoringNullabilities);
+          SubtypeCheckMode.withNullabilities);
       bool isIterable = inferrer.typeSchemaEnvironment.isSubtypeOf(
           spreadType,
           inferrer.coreTypes.iterableRawType(inferrer.library.nullable),
-          SubtypeCheckMode.ignoringNullabilities);
+          SubtypeCheckMode.withNullabilities);
       if (isMap && !isIterable) {
         mapSpreadOffset = entry.fileOffset;
       }
diff --git a/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart b/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart
index c35373e..d5a37ef 100644
--- a/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart
@@ -257,7 +257,7 @@
     Statement loopBody;
     if (element.elementType == null ||
         !typeEnvironment.isSubtypeOf(element.elementType, elementType,
-            SubtypeCheckMode.ignoringNullabilities)) {
+            SubtypeCheckMode.withNullabilities)) {
       variable = _createForInVariable(element.fileOffset, const DynamicType());
       VariableDeclaration castedVar = _createVariable(
           _createImplicitAs(element.expression.fileOffset,
@@ -441,8 +441,8 @@
     VariableDeclaration variable;
     Statement loopBody;
     if (entry.entryType == null ||
-        !typeEnvironment.isSubtypeOf(entry.entryType, entryType,
-            SubtypeCheckMode.ignoringNullabilities)) {
+        !typeEnvironment.isSubtypeOf(
+            entry.entryType, entryType, SubtypeCheckMode.withNullabilities)) {
       variable = _createForInVariable(
           entry.fileOffset,
           new InterfaceType(mapEntryClass, _currentLibrary.nonNullable,
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index 8cd6381..ace7e55 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -67,7 +67,8 @@
 
 import 'package:kernel/type_algebra.dart' show substitute;
 
-import 'package:kernel/type_environment.dart' show TypeEnvironment;
+import 'package:kernel/type_environment.dart'
+    show SubtypeCheckMode, TypeEnvironment;
 
 import '../builder/builder.dart';
 import '../builder/builtin_type_builder.dart';
@@ -3015,11 +3016,12 @@
   }
 
   void reportTypeArgumentIssues(
-      List<TypeArgumentIssue> issues, Uri fileUri, int offset,
+      Iterable<TypeArgumentIssue> issues, Uri fileUri, int offset,
       {bool inferred,
       TypeArgumentsInfo typeArgumentsInfo,
       DartType targetReceiver,
-      String targetName}) {
+      String targetName,
+      bool areWarnings = false}) {
     for (TypeArgumentIssue issue in issues) {
       DartType argument = issue.argument;
       TypeParameter typeParameter = issue.typeParameter;
@@ -3041,16 +3043,27 @@
       } else {
         if (issue.enclosingType == null && targetReceiver != null) {
           if (issueInferred) {
-            message =
-                templateIncorrectTypeArgumentQualifiedInferred.withArguments(
-                    argument,
-                    typeParameter.bound,
-                    typeParameter.name,
-                    targetReceiver,
-                    targetName,
-                    isNonNullableByDefault);
+            Template<
+                    Message Function(
+                        DartType, DartType, String, DartType, String, bool)>
+                template = areWarnings
+                    ? templateIncorrectTypeArgumentQualifiedInferredWarning
+                    : templateIncorrectTypeArgumentQualifiedInferred;
+            message = template.withArguments(
+                argument,
+                typeParameter.bound,
+                typeParameter.name,
+                targetReceiver,
+                targetName,
+                isNonNullableByDefault);
           } else {
-            message = templateIncorrectTypeArgumentQualified.withArguments(
+            Template<
+                    Message Function(
+                        DartType, DartType, String, DartType, String, bool)>
+                template = areWarnings
+                    ? templateIncorrectTypeArgumentQualifiedWarning
+                    : templateIncorrectTypeArgumentQualified;
+            message = template.withArguments(
                 argument,
                 typeParameter.bound,
                 typeParameter.name,
@@ -3064,19 +3077,19 @@
               : getGenericTypeName(issue.enclosingType);
           assert(enclosingName != null);
           if (issueInferred) {
-            message = templateIncorrectTypeArgumentInferred.withArguments(
-                argument,
-                typeParameter.bound,
-                typeParameter.name,
-                enclosingName,
-                isNonNullableByDefault);
+            Template<Message Function(DartType, DartType, String, String, bool)>
+                template = areWarnings
+                    ? templateIncorrectTypeArgumentInferredWarning
+                    : templateIncorrectTypeArgumentInferred;
+            message = template.withArguments(argument, typeParameter.bound,
+                typeParameter.name, enclosingName, isNonNullableByDefault);
           } else {
-            message = templateIncorrectTypeArgument.withArguments(
-                argument,
-                typeParameter.bound,
-                typeParameter.name,
-                enclosingName,
-                isNonNullableByDefault);
+            Template<Message Function(DartType, DartType, String, String, bool)>
+                template = areWarnings
+                    ? templateIncorrectTypeArgumentWarning
+                    : templateIncorrectTypeArgument;
+            message = template.withArguments(argument, typeParameter.bound,
+                typeParameter.name, enclosingName, isNonNullableByDefault);
           }
         }
       }
@@ -3203,32 +3216,61 @@
       }
     }
     if (returnType != null) {
-      List<TypeArgumentIssue> issues = findTypeArgumentIssues(
-          returnType, typeEnvironment,
-          allowSuperBounded: true);
-      if (issues != null) {
+      Set<TypeArgumentIssue> legacyIssues = findTypeArgumentIssues(returnType,
+              typeEnvironment, SubtypeCheckMode.ignoringNullabilities,
+              allowSuperBounded: true)
+          ?.toSet();
+      Set<TypeArgumentIssue> nnbdIssues =
+          isNonNullableByDefault && loader.performNnbdChecks
+              ? findTypeArgumentIssues(returnType, typeEnvironment,
+                      SubtypeCheckMode.withNullabilities)
+                  ?.toSet()
+              : null;
+      if (legacyIssues != null || nnbdIssues != null) {
+        Set<TypeArgumentIssue> mergedIssues = legacyIssues ?? {};
+        if (nnbdIssues != null) {
+          mergedIssues.addAll(nnbdIssues.where((issue) =>
+              legacyIssues == null || !legacyIssues.contains(issue)));
+        }
         int offset = fileOffset;
-        for (TypeArgumentIssue issue in issues) {
+        for (TypeArgumentIssue issue in mergedIssues) {
           DartType argument = issue.argument;
           TypeParameter typeParameter = issue.typeParameter;
 
           // We don't need to check if [argument] was inferred or specified
           // here, because inference in return types boils down to instantiate-
           // -to-bound, and it can't provide a type that violates the bound.
-          Message message;
           if (argument is FunctionType && argument.typeParameters.length > 0) {
-            message = messageGenericFunctionTypeUsedAsActualTypeArgument;
-            typeParameter = null;
+            reportTypeArgumentIssue(
+                messageGenericFunctionTypeUsedAsActualTypeArgument,
+                fileUri,
+                offset,
+                null);
           } else {
-            message = templateIncorrectTypeArgumentInReturnType.withArguments(
-                argument,
-                typeParameter.bound,
-                typeParameter.name,
-                getGenericTypeName(issue.enclosingType),
-                isNonNullableByDefault);
-          }
+            void reportProblem(
+                Template<
+                        Message Function(
+                            DartType, DartType, String, String, bool)>
+                    template) {
+              reportTypeArgumentIssue(
+                  template.withArguments(
+                      argument,
+                      typeParameter.bound,
+                      typeParameter.name,
+                      getGenericTypeName(issue.enclosingType),
+                      isNonNullableByDefault),
+                  fileUri,
+                  offset,
+                  typeParameter);
+            }
 
-          reportTypeArgumentIssue(message, fileUri, offset, typeParameter);
+            nnbdIssues ??= const {};
+            if (nnbdIssues.contains(issue) && !loader.nnbdStrongMode) {
+              reportProblem(templateIncorrectTypeArgumentInReturnTypeWarning);
+            } else {
+              reportProblem(templateIncorrectTypeArgumentInReturnType);
+            }
+          }
         }
       }
     }
@@ -3273,11 +3315,27 @@
   void checkBoundsInType(
       DartType type, TypeEnvironment typeEnvironment, Uri fileUri, int offset,
       {bool inferred, bool allowSuperBounded = true}) {
-    List<TypeArgumentIssue> issues = findTypeArgumentIssues(
-        type, typeEnvironment,
-        allowSuperBounded: allowSuperBounded);
-    if (issues != null) {
-      reportTypeArgumentIssues(issues, fileUri, offset, inferred: inferred);
+    Set<TypeArgumentIssue> legacyIssues = findTypeArgumentIssues(
+            type, typeEnvironment, SubtypeCheckMode.ignoringNullabilities,
+            allowSuperBounded: allowSuperBounded)
+        ?.toSet();
+    Set<TypeArgumentIssue> nnbdIssues =
+        isNonNullableByDefault && loader.performNnbdChecks
+            ? findTypeArgumentIssues(
+                    type, typeEnvironment, SubtypeCheckMode.withNullabilities,
+                    allowSuperBounded: allowSuperBounded)
+                ?.toSet()
+            : null;
+    if (legacyIssues != null) {
+      reportTypeArgumentIssues(legacyIssues, fileUri, offset,
+          inferred: inferred);
+    }
+    if (nnbdIssues != null) {
+      if (legacyIssues != null) {
+        nnbdIssues = nnbdIssues.where((issue) => !legacyIssues.contains(issue));
+      }
+      reportTypeArgumentIssues(nnbdIssues, fileUri, offset,
+          inferred: inferred, areWarnings: !loader.nnbdStrongMode);
     }
   }
 
@@ -3330,20 +3388,46 @@
     List<DartType> arguments = node.arguments.types;
     // The following error is to be reported elsewhere.
     if (parameters.length != arguments.length) return;
-    List<TypeArgumentIssue> issues = findTypeArgumentIssuesForInvocation(
-        parameters, arguments, typeEnvironment);
-    if (issues != null) {
+    Set<TypeArgumentIssue> legacyIssues = findTypeArgumentIssuesForInvocation(
+            parameters,
+            arguments,
+            typeEnvironment,
+            SubtypeCheckMode.ignoringNullabilities)
+        ?.toSet();
+    Set<TypeArgumentIssue> nnbdIssues =
+        isNonNullableByDefault && loader.performNnbdChecks
+            ? findTypeArgumentIssuesForInvocation(parameters, arguments,
+                    typeEnvironment, SubtypeCheckMode.withNullabilities)
+                ?.toSet()
+            : null;
+    if (legacyIssues != null) {
       DartType targetReceiver;
       if (klass != null) {
         targetReceiver =
             new InterfaceType(klass, klass.enclosingLibrary.nonNullable);
       }
       String targetName = node.target.name.name;
-      reportTypeArgumentIssues(issues, fileUri, node.fileOffset,
+      reportTypeArgumentIssues(legacyIssues, fileUri, node.fileOffset,
           typeArgumentsInfo: typeArgumentsInfo,
           targetReceiver: targetReceiver,
           targetName: targetName);
     }
+    if (nnbdIssues != null) {
+      DartType targetReceiver;
+      if (klass != null) {
+        targetReceiver =
+            new InterfaceType(klass, klass.enclosingLibrary.nonNullable);
+      }
+      String targetName = node.target.name.name;
+      if (legacyIssues != null) {
+        nnbdIssues = nnbdIssues.where((issue) => !legacyIssues.contains(issue));
+      }
+      reportTypeArgumentIssues(nnbdIssues, fileUri, node.fileOffset,
+          typeArgumentsInfo: typeArgumentsInfo,
+          targetReceiver: targetReceiver,
+          targetName: targetName,
+          areWarnings: !loader.nnbdStrongMode);
+    }
   }
 
   void checkBoundsInMethodInvocation(
@@ -3396,14 +3480,37 @@
       instantiatedMethodParameters[i].bound =
           substitute(methodParameters[i].bound, substitutionMap);
     }
-    List<TypeArgumentIssue> issues = findTypeArgumentIssuesForInvocation(
-        instantiatedMethodParameters, arguments.types, typeEnvironment);
-    if (issues != null) {
-      reportTypeArgumentIssues(issues, fileUri, offset,
+    Set<TypeArgumentIssue> legacyIssues = findTypeArgumentIssuesForInvocation(
+            instantiatedMethodParameters,
+            arguments.types,
+            typeEnvironment,
+            SubtypeCheckMode.ignoringNullabilities)
+        ?.toSet();
+    Set<TypeArgumentIssue> nnbdIssues =
+        isNonNullableByDefault && loader.performNnbdChecks
+            ? findTypeArgumentIssuesForInvocation(
+                    instantiatedMethodParameters,
+                    arguments.types,
+                    typeEnvironment,
+                    SubtypeCheckMode.withNullabilities)
+                ?.toSet()
+            : null;
+    if (legacyIssues != null) {
+      reportTypeArgumentIssues(legacyIssues, fileUri, offset,
           typeArgumentsInfo: getTypeArgumentsInfo(arguments),
           targetReceiver: receiverType,
           targetName: name.name);
     }
+    if (nnbdIssues != null) {
+      if (legacyIssues != null) {
+        nnbdIssues = nnbdIssues.where((issue) => !legacyIssues.contains(issue));
+      }
+      reportTypeArgumentIssues(nnbdIssues, fileUri, offset,
+          typeArgumentsInfo: getTypeArgumentsInfo(arguments),
+          targetReceiver: receiverType,
+          targetName: name.name,
+          areWarnings: !loader.nnbdStrongMode);
+    }
   }
 
   void checkTypesInOutline(TypeEnvironment typeEnvironment) {
diff --git a/pkg/front_end/lib/src/fasta/type_inference/standard_bounds.dart b/pkg/front_end/lib/src/fasta/type_inference/standard_bounds.dart
index 6257581..5dc83a6 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/standard_bounds.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/standard_bounds.dart
@@ -419,6 +419,15 @@
 
   DartType getNullabilityObliviousStandardLowerBound(
       DartType type1, DartType type2, Library clientLibrary) {
+    // Do legacy erasure on the argument, so that the result types that are
+    // computed from arguments are legacy.
+    type1 = type1 == coreTypes.nullType
+        ? type1
+        : type1.withNullability(Nullability.legacy);
+    type2 = type2 == coreTypes.nullType
+        ? type2
+        : type2.withNullability(Nullability.legacy);
+
     // For all types T, SLB(T,T) = T.  Note that we don't test for equality
     // because we don't want to make the algorithm quadratic.  This is ok
     // because the check is not needed for correctness; it's just a speed
@@ -1390,10 +1399,10 @@
     // 3. Otherwise return the spec-defined standard upper bound.  This will
     //    be an upper bound, might (or might not) be least, and might
     //    (or might not) be a well-formed type.
-    if (isSubtypeOf(type1, type2, SubtypeCheckMode.ignoringNullabilities)) {
+    if (isSubtypeOf(type1, type2, SubtypeCheckMode.withNullabilities)) {
       return type2;
     }
-    if (isSubtypeOf(type2, type1, SubtypeCheckMode.ignoringNullabilities)) {
+    if (isSubtypeOf(type2, type1, SubtypeCheckMode.withNullabilities)) {
       return type1;
     }
     if (type1 is InterfaceType &&
@@ -1411,7 +1420,7 @@
           tArgs[i] = getStandardLowerBound(tArgs1[i], tArgs2[i], clientLibrary);
         } else if (tParams[i].variance == Variance.invariant) {
           if (!areMutualSubtypes(
-              tArgs1[i], tArgs2[i], SubtypeCheckMode.ignoringNullabilities)) {
+              tArgs1[i], tArgs2[i], SubtypeCheckMode.withNullabilities)) {
             // No bound will be valid, find bound at the interface level.
             return getLegacyLeastUpperBound(type1, type2, clientLibrary);
           }
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index e90f6b9..eff3fb7 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -343,7 +343,7 @@
     DartType inferredType =
         inferrer.inferReturnType(_inferredUnwrappedReturnOrYieldType);
     if (!inferrer.typeSchemaEnvironment.isSubtypeOf(inferredType,
-        returnOrYieldContext, SubtypeCheckMode.ignoringNullabilities)) {
+        returnOrYieldContext, SubtypeCheckMode.withNullabilities)) {
       // If the inferred return type isn't a subtype of the context, we use the
       // context.
       inferredType = greatestClosure(inferrer.coreTypes, returnOrYieldContext);
@@ -1140,8 +1140,8 @@
               DartType typeArgument = inferredTypeArguments[index];
               DartType bound =
                   inferredSubstitution.substituteType(typeParameter.bound);
-              if (!typeSchemaEnvironment.isSubtypeOf(typeArgument, bound,
-                  SubtypeCheckMode.ignoringNullabilities)) {
+              if (!typeSchemaEnvironment.isSubtypeOf(
+                  typeArgument, bound, SubtypeCheckMode.withNullabilities)) {
                 return;
               }
             }
@@ -4257,9 +4257,9 @@
     if (this.isPlatform == other.isPlatform) {
       // Both are platform or not platform.
       bool thisIsSubtype = typeSchemaEnvironment.isSubtypeOf(
-          this.onType, other.onType, SubtypeCheckMode.ignoringNullabilities);
+          this.onType, other.onType, SubtypeCheckMode.withNullabilities);
       bool thisIsSupertype = typeSchemaEnvironment.isSubtypeOf(
-          other.onType, this.onType, SubtypeCheckMode.ignoringNullabilities);
+          other.onType, this.onType, SubtypeCheckMode.withNullabilities);
       if (thisIsSubtype && !thisIsSupertype) {
         // This is subtype of other and not vice-versa.
         return true;
@@ -4270,11 +4270,11 @@
         thisIsSubtype = typeSchemaEnvironment.isSubtypeOf(
             this.onTypeInstantiateToBounds,
             other.onTypeInstantiateToBounds,
-            SubtypeCheckMode.ignoringNullabilities);
+            SubtypeCheckMode.withNullabilities);
         thisIsSupertype = typeSchemaEnvironment.isSubtypeOf(
             other.onTypeInstantiateToBounds,
             this.onTypeInstantiateToBounds,
-            SubtypeCheckMode.ignoringNullabilities);
+            SubtypeCheckMode.withNullabilities);
         if (thisIsSubtype && !thisIsSupertype) {
           // This is subtype of other and not vice-versa.
           return true;
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_promotion.dart b/pkg/front_end/lib/src/fasta/type_inference/type_promotion.dart
index 11c5323..4a8dca0 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_promotion.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_promotion.dart
@@ -686,13 +686,13 @@
     // the variable and the type we are checking against.
     DartType previousType = previousPromotedType ?? variable.type;
     if (promoter.typeSchemaEnvironment.isSubtypeOf(
-        checkedType, previousType, SubtypeCheckMode.ignoringNullabilities)) {
+        checkedType, previousType, SubtypeCheckMode.withNullabilities)) {
       // The type we are checking against is a subtype of the previous type of
       // the variable, so this is a refinement; we can promote.
       return checkedType;
     } else if (previousType is TypeParameterType &&
         promoter.typeSchemaEnvironment.isSubtypeOf(checkedType,
-            previousType.bound, SubtypeCheckMode.ignoringNullabilities)) {
+            previousType.bound, SubtypeCheckMode.withNullabilities)) {
       // The type we are checking against is a subtype of the bound of the
       // previous type of the variable; we can promote the bound.
       return new TypeParameterType.intersection(
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart b/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart
index 789301a..ada372b 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart
@@ -163,10 +163,10 @@
     // override.
     if (type1 is InterfaceType && type1.classNode == coreTypes.intClass) {
       if (type2 is InterfaceType && type2.classNode == coreTypes.intClass) {
-        return type2;
+        return type2.withNullability(type1.nullability);
       }
       if (type2 is InterfaceType && type2.classNode == coreTypes.doubleClass) {
-        return type2;
+        return type2.withNullability(type1.nullability);
       }
     }
     return coreTypes.numRawType(type1.nullability);
@@ -326,8 +326,8 @@
       if (success && !hasOmittedBound(typeParam)) {
         // If everything else succeeded, check the `extends` constraint.
         DartType extendsConstraint = typeParamBound;
-        success = isSubtypeOf(inferred, extendsConstraint,
-            SubtypeCheckMode.ignoringNullabilities);
+        success = isSubtypeOf(
+            inferred, extendsConstraint, SubtypeCheckMode.withNullabilities);
       }
 
       if (!success) {
@@ -351,7 +351,15 @@
   bool isSubtypeOf(
       DartType subtype, DartType supertype, SubtypeCheckMode mode) {
     if (subtype is UnknownType) return true;
-    if (subtype == Null && supertype is UnknownType) return true;
+    DartType unwrappedSupertype = supertype;
+    while (unwrappedSupertype is InterfaceType &&
+        unwrappedSupertype.classNode == futureOrClass) {
+      unwrappedSupertype =
+          (unwrappedSupertype as InterfaceType).typeArguments.single;
+    }
+    if (subtype == coreTypes.nullType && unwrappedSupertype is UnknownType) {
+      return true;
+    }
     return super.isSubtypeOf(subtype, supertype, mode);
   }
 
@@ -444,9 +452,8 @@
   /// Determine if the given [type] satisfies the given type [constraint].
   bool typeSatisfiesConstraint(DartType type, TypeConstraint constraint) {
     return isSubtypeOf(
-            constraint.lower, type, SubtypeCheckMode.ignoringNullabilities) &&
-        isSubtypeOf(
-            type, constraint.upper, SubtypeCheckMode.ignoringNullabilities);
+            constraint.lower, type, SubtypeCheckMode.withNullabilities) &&
+        isSubtypeOf(type, constraint.upper, SubtypeCheckMode.withNullabilities);
   }
 
   DartType _inferTypeParameterFromAll(
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index dd4f20c..869d90b 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -360,6 +360,14 @@
 ImportAfterPart/script1: Fail
 IncompatibleRedirecteeFunctionType/part_wrapped_script6: Fail
 IncompatibleRedirecteeFunctionType/script6: Fail # Triggers multiple errors.
+IncompatibleRedirecteeFunctionTypeWarning/example: Fail
+IncorrectTypeArgumentInReturnTypeWarning/example: Fail
+IncorrectTypeArgumentInSupertypeInferredWarning/example: Fail
+IncorrectTypeArgumentInSupertypeWarning/example: Fail
+IncorrectTypeArgumentInferredWarning/example: Fail
+IncorrectTypeArgumentQualifiedInferredWarning/example: Fail
+IncorrectTypeArgumentQualifiedWarning/example: Fail
+IncorrectTypeArgumentWarning/example: Fail
 InitializerForStaticField/example: Fail
 InitializerOutsideConstructor/example: Fail
 InputFileNotFound/analyzerCode: Fail
@@ -528,8 +536,11 @@
 OverrideMismatchNamedParameter/example: Fail
 OverrideMoreRequiredArguments/example: Fail
 OverrideTypeMismatchParameter/example: Fail
+OverrideTypeMismatchParameterWarning/example: Fail
 OverrideTypeMismatchReturnType/example: Fail
+OverrideTypeMismatchReturnTypeWarning/example: Fail
 OverrideTypeMismatchSetter/example: Fail
+OverrideTypeMismatchSetterWarning/example: Fail
 OverrideTypeVariablesBoundMismatch/analyzerCode: Fail
 OverrideTypeVariablesBoundMismatch/example: Fail
 OverrideTypeVariablesMismatch/example: Fail
@@ -566,6 +577,7 @@
 PrivateNamedParameter/example: Fail
 RedirectingConstructorWithBody/part_wrapped_script1: Fail
 RedirectingConstructorWithBody/script1: Fail
+RedirectingFactoryIncompatibleTypeArgumentWarning/example: Fail
 RedirectionInNonFactory/part_wrapped_script1: Fail
 RedirectionInNonFactory/script1: Fail
 RedirectionTargetNotFound/example: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 79fc4f4..a5b9d37 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -2059,15 +2059,32 @@
   tip: "Change to a supertype of '#type2', or, for a covariant parameter, a subtype."
   analyzerCode: INVALID_METHOD_OVERRIDE
 
+OverrideTypeMismatchParameterWarning:
+  template: "The parameter '#name' of the method '#name2' has type '#type', which does not match the corresponding type, '#type2', in the overridden method, '#name3'."
+  tip: "Change to a supertype of '#type2', or, for a covariant parameter, a subtype."
+  severity: WARNING
+  analyzerCode: INVALID_METHOD_OVERRIDE
+
 OverrideTypeMismatchReturnType:
   template: "The return type of the method '#name' is '#type', which does not match the return type, '#type2', of the overridden method, '#name2'."
   tip: "Change to a subtype of '#type2'."
   analyzerCode: INVALID_METHOD_OVERRIDE
 
+OverrideTypeMismatchReturnTypeWarning:
+  template: "The return type of the method '#name' is '#type', which does not match the return type, '#type2', of the overridden method, '#name2'."
+  tip: "Change to a subtype of '#type2'."
+  severity: WARNING
+  analyzerCode: INVALID_METHOD_OVERRIDE
+
 OverrideTypeMismatchSetter:
   template: "The field '#name' has type '#type', which does not match the corresponding type, '#type2', in the overridden setter, '#name2'."
   analyzerCode: INVALID_METHOD_OVERRIDE
 
+OverrideTypeMismatchSetterWarning:
+  template: "The field '#name' has type '#type', which does not match the corresponding type, '#type2', in the overridden setter, '#name2'."
+  severity: WARNING
+  analyzerCode: INVALID_METHOD_OVERRIDE
+
 PartOfSelf:
   template: "A file can't be a part of itself."
   analyzerCode: PART_OF_NON_PART
@@ -3354,6 +3371,11 @@
       }
       class B<T extends int, S extends String> implements A<T> {}
 
+IncompatibleRedirecteeFunctionTypeWarning:
+  template: "The constructor function type '#type' isn't a subtype of '#type2'."
+  severity: WARNING
+  analyzerCode: REDIRECT_TO_INVALID_TYPE
+
 RedirectingFactoryIncompatibleTypeArgument:
   template: "The type '#type' doesn't extend '#type2'."
   tip: "Try using a different type as argument."
@@ -3365,6 +3387,12 @@
       }
       class B<T extends int, S extends String> implements A<T> {}
 
+RedirectingFactoryIncompatibleTypeArgumentWarning:
+  template: "The type '#type' doesn't extend '#type2'."
+  tip: "Try using a different type as argument."
+  severity: WARNING
+  analyzerCode: TYPE_ARGUMENT_NOT_MATCHING_BOUNDS
+
 SyntheticToken:
   template: "This couldn't be parsed."
   frontendInternal: true
@@ -3377,6 +3405,12 @@
     class C<T extends num> {}
     main() { new C<String>(); }
 
+IncorrectTypeArgumentWarning:
+  template: "Type argument '#type' doesn't conform to the bound '#type2' of the type variable '#name' on '#name2'."
+  tip: "Try changing type arguments so that they conform to the bounds."
+  severity: WARNING
+  analyzerCode: TYPE_ARGUMENT_NOT_MATCHING_BOUNDS
+
 IncorrectTypeArgumentQualified:
   template: "Type argument '#type' doesn't conform to the bound '#type2' of the type variable '#name' on '#type3.#name2'."
   tip: "Try changing type arguments so that they conform to the bounds."
@@ -3385,6 +3419,12 @@
     class C<T> { foo<U extends num>() {} }
     main() { new C<String>().foo<String>(); }
 
+IncorrectTypeArgumentQualifiedWarning:
+  template: "Type argument '#type' doesn't conform to the bound '#type2' of the type variable '#name' on '#type3.#name2'."
+  tip: "Try changing type arguments so that they conform to the bounds."
+  severity: WARNING
+  analyzerCode: TYPE_ARGUMENT_NOT_MATCHING_BOUNDS
+
 IncorrectTypeArgumentInSupertype:
   template: "Type argument '#type' doesn't conform to the bound '#type2' of the type variable '#name' on '#name2' in the supertype '#name3' of class '#name4'."
   tip: "Try changing type arguments so that they conform to the bounds."
@@ -3393,6 +3433,12 @@
     class A<T extends num> {}
     class B extends A<String> {}
 
+IncorrectTypeArgumentInSupertypeWarning:
+  template: "Type argument '#type' doesn't conform to the bound '#type2' of the type variable '#name' on '#name2' in the supertype '#name3' of class '#name4'."
+  tip: "Try changing type arguments so that they conform to the bounds."
+  severity: WARNING
+  analyzerCode: TYPE_ARGUMENT_NOT_MATCHING_BOUNDS
+
 IncorrectTypeArgumentInReturnType:
   template: "Type argument '#type' doesn't conform to the bound '#type2' of the type variable '#name' on '#name2' in the return type."
   tip: "Try changing type arguments so that they conform to the bounds."
@@ -3401,6 +3447,12 @@
     class A<T extends num> {}
     A<String> foo() => null;
 
+IncorrectTypeArgumentInReturnTypeWarning:
+  template: "Type argument '#type' doesn't conform to the bound '#type2' of the type variable '#name' on '#name2' in the return type."
+  tip: "Try changing type arguments so that they conform to the bounds."
+  severity: WARNING
+  analyzerCode: TYPE_ARGUMENT_NOT_MATCHING_BOUNDS
+
 IncorrectTypeArgumentInferred:
   template: "Inferred type argument '#type' doesn't conform to the bound '#type2' of the type variable '#name' on '#name2'."
   tip: "Try specifying type arguments explicitly so that they conform to the bounds."
@@ -3409,6 +3461,12 @@
     void foo<T extends num>(T t) {}
     main() { foo("bar"); }
 
+IncorrectTypeArgumentInferredWarning:
+  template: "Inferred type argument '#type' doesn't conform to the bound '#type2' of the type variable '#name' on '#name2'."
+  tip: "Try specifying type arguments explicitly so that they conform to the bounds."
+  severity: WARNING
+  analyzerCode: TYPE_ARGUMENT_NOT_MATCHING_BOUNDS
+
 IncorrectTypeArgumentQualifiedInferred:
   template: "Inferred type argument '#type' doesn't conform to the bound '#type2' of the type variable '#name' on '#type3.#name2'."
   tip: "Try specifying type arguments explicitly so that they conform to the bounds."
@@ -3417,6 +3475,12 @@
     class C<T> { foo<U extends num>(U u) {} }
     main() { new C<String>().foo(""); }
 
+IncorrectTypeArgumentQualifiedInferredWarning:
+  template: "Inferred type argument '#type' doesn't conform to the bound '#type2' of the type variable '#name' on '#type3.#name2'."
+  tip: "Try specifying type arguments explicitly so that they conform to the bounds."
+  severity: WARNING
+  analyzerCode: TYPE_ARGUMENT_NOT_MATCHING_BOUNDS
+
 IncorrectTypeArgumentInSupertypeInferred:
   template: "Inferred type argument '#type' doesn't conform to the bound '#type2' of the type variable '#name' on '#name2' in the supertype '#name3' of class '#name4'."
   tip: "Try specifying type arguments explicitly so that they conform to the bounds."
@@ -3425,6 +3489,12 @@
     class A<T extends A<T>> {}
     class B extends A {}
 
+IncorrectTypeArgumentInSupertypeInferredWarning:
+  template: "Inferred type argument '#type' doesn't conform to the bound '#type2' of the type variable '#name' on '#name2' in the supertype '#name3' of class '#name4'."
+  tip: "Try specifying type arguments explicitly so that they conform to the bounds."
+  severity: WARNING
+  analyzerCode: TYPE_ARGUMENT_NOT_MATCHING_BOUNDS
+
 IncorrectTypeArgumentVariable:
   template: "This is the type variable whose bound isn't conformed to."
   severity: CONTEXT
diff --git a/pkg/front_end/testcases/nnbd/bounds_checks.dart b/pkg/front_end/testcases/nnbd/bounds_checks.dart
new file mode 100644
index 0000000..3250d57
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/bounds_checks.dart
@@ -0,0 +1,28 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+class A<X extends num> {}
+
+foo(A<num?> a) {} // Error in strong mode and Warning in weak mode.
+
+A<num?> bar() {} // Error in strong mode and Warning in weak mode.
+
+baz<T extends A<num?>>() {} // Error in strong mode and Warning in weak mode.
+
+class B extends A<num?> {} // Error in strong mode and Warning in weak mode.
+
+class C<T extends A<num?>> {} // Error in strong mode and Warning in weak mode.
+
+void hest<T extends num>() {}
+
+class Hest {
+  void hest<T extends num>() {}
+}
+
+fisk(Hest h) {
+  hest<num?>();
+  h.hest<num?>();
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/bounds_checks.dart.outline.expect b/pkg/front_end/testcases/nnbd/bounds_checks.dart.outline.expect
new file mode 100644
index 0000000..a66b2fe
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/bounds_checks.dart.outline.expect
@@ -0,0 +1,77 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:7:13: Error: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'X' on 'A'.
+// Try changing type arguments so that they conform to the bounds.
+// foo(A<num?> a) {} // Error in strong mode and Warning in weak mode.
+//             ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends num> {}
+//         ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:9:12: Error: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'X' on 'A' in the return type.
+// Try changing type arguments so that they conform to the bounds.
+// A<num?> bar() {} // Error in strong mode and Warning in weak mode.
+//            ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends num> {}
+//         ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:11:5: Error: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'X' on 'A'.
+// Try changing type arguments so that they conform to the bounds.
+// baz<T extends A<num?>>() {} // Error in strong mode and Warning in weak mode.
+//     ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends num> {}
+//         ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:13:7: Error: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'X' on 'A' in the supertype 'A' of class 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// class B extends A<num?> {} // Error in strong mode and Warning in weak mode.
+//       ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends num> {}
+//         ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:15:9: Error: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'X' on 'A'.
+// Try changing type arguments so that they conform to the bounds.
+// class C<T extends A<num?>> {} // Error in strong mode and Warning in weak mode.
+//         ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends num> {}
+//         ^
+//
+import self as self;
+import "dart:core" as core;
+
+class A<X extends core::num = core::num> extends core::Object {
+  synthetic constructor •() → self::A<self::A::X>
+    ;
+}
+class B extends self::A<core::num?> {
+  synthetic constructor •() → self::B
+    ;
+}
+class C<T extends self::A<core::num?> = self::A<core::num?>> extends core::Object {
+  synthetic constructor •() → self::C<self::C::T>
+    ;
+}
+class Hest extends core::Object {
+  synthetic constructor •() → self::Hest
+    ;
+  method hest<T extends core::num = core::num>() → void
+    ;
+}
+static method foo(self::A<core::num?> a) → dynamic
+  ;
+static method bar() → self::A<core::num?>
+  ;
+static method baz<T extends self::A<core::num?> = self::A<core::num?>>() → dynamic
+  ;
+static method hest<T extends core::num = core::num>() → void
+  ;
+static method fisk(self::Hest h) → dynamic
+  ;
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/nnbd/bounds_checks.dart.strong.expect b/pkg/front_end/testcases/nnbd/bounds_checks.dart.strong.expect
new file mode 100644
index 0000000..686cc68
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/bounds_checks.dart.strong.expect
@@ -0,0 +1,91 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:7:13: Error: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'X' on 'A'.
+// Try changing type arguments so that they conform to the bounds.
+// foo(A<num?> a) {} // Error in strong mode and Warning in weak mode.
+//             ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends num> {}
+//         ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:9:12: Error: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'X' on 'A' in the return type.
+// Try changing type arguments so that they conform to the bounds.
+// A<num?> bar() {} // Error in strong mode and Warning in weak mode.
+//            ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends num> {}
+//         ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:11:5: Error: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'X' on 'A'.
+// Try changing type arguments so that they conform to the bounds.
+// baz<T extends A<num?>>() {} // Error in strong mode and Warning in weak mode.
+//     ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends num> {}
+//         ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:13:7: Error: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'X' on 'A' in the supertype 'A' of class 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// class B extends A<num?> {} // Error in strong mode and Warning in weak mode.
+//       ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends num> {}
+//         ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:15:9: Error: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'X' on 'A'.
+// Try changing type arguments so that they conform to the bounds.
+// class C<T extends A<num?>> {} // Error in strong mode and Warning in weak mode.
+//         ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends num> {}
+//         ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:24:3: Error: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'T' on 'hest'.
+// Try changing type arguments so that they conform to the bounds.
+//   hest<num?>();
+//   ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:17:11: Context: This is the type variable whose bound isn't conformed to.
+// void hest<T extends num>() {}
+//           ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:25:5: Error: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'T' on 'Hest.hest'.
+//  - 'Hest' is from 'pkg/front_end/testcases/nnbd/bounds_checks.dart'.
+// Try changing type arguments so that they conform to the bounds.
+//   h.hest<num?>();
+//     ^
+//
+import self as self;
+import "dart:core" as core;
+
+class A<X extends core::num = core::num> extends core::Object {
+  synthetic constructor •() → self::A<self::A::X>
+    : super core::Object::•()
+    ;
+}
+class B extends self::A<core::num?> {
+  synthetic constructor •() → self::B
+    : super self::A::•()
+    ;
+}
+class C<T extends self::A<core::num?> = self::A<core::num?>> extends core::Object {
+  synthetic constructor •() → self::C<self::C::T>
+    : super core::Object::•()
+    ;
+}
+class Hest extends core::Object {
+  synthetic constructor •() → self::Hest
+    : super core::Object::•()
+    ;
+  method hest<T extends core::num = core::num>() → void {}
+}
+static method foo(self::A<core::num?> a) → dynamic {}
+static method bar() → self::A<core::num?> {}
+static method baz<T extends self::A<core::num?> = self::A<core::num?>>() → dynamic {}
+static method hest<T extends core::num = core::num>() → void {}
+static method fisk(self::Hest h) → dynamic {
+  self::hest<core::num?>();
+  h.{self::Hest::hest}<core::num?>();
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/bounds_checks.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/bounds_checks.dart.strong.transformed.expect
new file mode 100644
index 0000000..686cc68
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/bounds_checks.dart.strong.transformed.expect
@@ -0,0 +1,91 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:7:13: Error: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'X' on 'A'.
+// Try changing type arguments so that they conform to the bounds.
+// foo(A<num?> a) {} // Error in strong mode and Warning in weak mode.
+//             ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends num> {}
+//         ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:9:12: Error: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'X' on 'A' in the return type.
+// Try changing type arguments so that they conform to the bounds.
+// A<num?> bar() {} // Error in strong mode and Warning in weak mode.
+//            ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends num> {}
+//         ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:11:5: Error: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'X' on 'A'.
+// Try changing type arguments so that they conform to the bounds.
+// baz<T extends A<num?>>() {} // Error in strong mode and Warning in weak mode.
+//     ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends num> {}
+//         ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:13:7: Error: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'X' on 'A' in the supertype 'A' of class 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// class B extends A<num?> {} // Error in strong mode and Warning in weak mode.
+//       ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends num> {}
+//         ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:15:9: Error: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'X' on 'A'.
+// Try changing type arguments so that they conform to the bounds.
+// class C<T extends A<num?>> {} // Error in strong mode and Warning in weak mode.
+//         ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends num> {}
+//         ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:24:3: Error: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'T' on 'hest'.
+// Try changing type arguments so that they conform to the bounds.
+//   hest<num?>();
+//   ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:17:11: Context: This is the type variable whose bound isn't conformed to.
+// void hest<T extends num>() {}
+//           ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:25:5: Error: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'T' on 'Hest.hest'.
+//  - 'Hest' is from 'pkg/front_end/testcases/nnbd/bounds_checks.dart'.
+// Try changing type arguments so that they conform to the bounds.
+//   h.hest<num?>();
+//     ^
+//
+import self as self;
+import "dart:core" as core;
+
+class A<X extends core::num = core::num> extends core::Object {
+  synthetic constructor •() → self::A<self::A::X>
+    : super core::Object::•()
+    ;
+}
+class B extends self::A<core::num?> {
+  synthetic constructor •() → self::B
+    : super self::A::•()
+    ;
+}
+class C<T extends self::A<core::num?> = self::A<core::num?>> extends core::Object {
+  synthetic constructor •() → self::C<self::C::T>
+    : super core::Object::•()
+    ;
+}
+class Hest extends core::Object {
+  synthetic constructor •() → self::Hest
+    : super core::Object::•()
+    ;
+  method hest<T extends core::num = core::num>() → void {}
+}
+static method foo(self::A<core::num?> a) → dynamic {}
+static method bar() → self::A<core::num?> {}
+static method baz<T extends self::A<core::num?> = self::A<core::num?>>() → dynamic {}
+static method hest<T extends core::num = core::num>() → void {}
+static method fisk(self::Hest h) → dynamic {
+  self::hest<core::num?>();
+  h.{self::Hest::hest}<core::num?>();
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/bounds_checks.dart.weak.expect b/pkg/front_end/testcases/nnbd/bounds_checks.dart.weak.expect
new file mode 100644
index 0000000..d78654a
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/bounds_checks.dart.weak.expect
@@ -0,0 +1,91 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:7:13: Warning: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'X' on 'A'.
+// Try changing type arguments so that they conform to the bounds.
+// foo(A<num?> a) {} // Error in strong mode and Warning in weak mode.
+//             ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends num> {}
+//         ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:9:12: Warning: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'X' on 'A' in the return type.
+// Try changing type arguments so that they conform to the bounds.
+// A<num?> bar() {} // Error in strong mode and Warning in weak mode.
+//            ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends num> {}
+//         ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:11:5: Warning: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'X' on 'A'.
+// Try changing type arguments so that they conform to the bounds.
+// baz<T extends A<num?>>() {} // Error in strong mode and Warning in weak mode.
+//     ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends num> {}
+//         ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:13:7: Warning: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'X' on 'A' in the supertype 'A' of class 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// class B extends A<num?> {} // Error in strong mode and Warning in weak mode.
+//       ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends num> {}
+//         ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:15:9: Warning: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'X' on 'A'.
+// Try changing type arguments so that they conform to the bounds.
+// class C<T extends A<num?>> {} // Error in strong mode and Warning in weak mode.
+//         ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends num> {}
+//         ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:24:3: Warning: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'T' on 'hest'.
+// Try changing type arguments so that they conform to the bounds.
+//   hest<num?>();
+//   ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:17:11: Context: This is the type variable whose bound isn't conformed to.
+// void hest<T extends num>() {}
+//           ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:25:5: Warning: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'T' on 'Hest.hest'.
+//  - 'Hest' is from 'pkg/front_end/testcases/nnbd/bounds_checks.dart'.
+// Try changing type arguments so that they conform to the bounds.
+//   h.hest<num?>();
+//     ^
+//
+import self as self;
+import "dart:core" as core;
+
+class A<X extends core::num = core::num> extends core::Object {
+  synthetic constructor •() → self::A<self::A::X>
+    : super core::Object::•()
+    ;
+}
+class B extends self::A<core::num?> {
+  synthetic constructor •() → self::B
+    : super self::A::•()
+    ;
+}
+class C<T extends self::A<core::num?> = self::A<core::num?>> extends core::Object {
+  synthetic constructor •() → self::C<self::C::T>
+    : super core::Object::•()
+    ;
+}
+class Hest extends core::Object {
+  synthetic constructor •() → self::Hest
+    : super core::Object::•()
+    ;
+  method hest<T extends core::num = core::num>() → void {}
+}
+static method foo(self::A<core::num?> a) → dynamic {}
+static method bar() → self::A<core::num?> {}
+static method baz<T extends self::A<core::num?> = self::A<core::num?>>() → dynamic {}
+static method hest<T extends core::num = core::num>() → void {}
+static method fisk(self::Hest h) → dynamic {
+  self::hest<core::num?>();
+  h.{self::Hest::hest}<core::num?>();
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/bounds_checks.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/bounds_checks.dart.weak.transformed.expect
new file mode 100644
index 0000000..d78654a
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/bounds_checks.dart.weak.transformed.expect
@@ -0,0 +1,91 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:7:13: Warning: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'X' on 'A'.
+// Try changing type arguments so that they conform to the bounds.
+// foo(A<num?> a) {} // Error in strong mode and Warning in weak mode.
+//             ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends num> {}
+//         ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:9:12: Warning: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'X' on 'A' in the return type.
+// Try changing type arguments so that they conform to the bounds.
+// A<num?> bar() {} // Error in strong mode and Warning in weak mode.
+//            ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends num> {}
+//         ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:11:5: Warning: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'X' on 'A'.
+// Try changing type arguments so that they conform to the bounds.
+// baz<T extends A<num?>>() {} // Error in strong mode and Warning in weak mode.
+//     ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends num> {}
+//         ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:13:7: Warning: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'X' on 'A' in the supertype 'A' of class 'B'.
+// Try changing type arguments so that they conform to the bounds.
+// class B extends A<num?> {} // Error in strong mode and Warning in weak mode.
+//       ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends num> {}
+//         ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:15:9: Warning: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'X' on 'A'.
+// Try changing type arguments so that they conform to the bounds.
+// class C<T extends A<num?>> {} // Error in strong mode and Warning in weak mode.
+//         ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:5:9: Context: This is the type variable whose bound isn't conformed to.
+// class A<X extends num> {}
+//         ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:24:3: Warning: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'T' on 'hest'.
+// Try changing type arguments so that they conform to the bounds.
+//   hest<num?>();
+//   ^
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:17:11: Context: This is the type variable whose bound isn't conformed to.
+// void hest<T extends num>() {}
+//           ^
+//
+// pkg/front_end/testcases/nnbd/bounds_checks.dart:25:5: Warning: Type argument 'num?' doesn't conform to the bound 'num' of the type variable 'T' on 'Hest.hest'.
+//  - 'Hest' is from 'pkg/front_end/testcases/nnbd/bounds_checks.dart'.
+// Try changing type arguments so that they conform to the bounds.
+//   h.hest<num?>();
+//     ^
+//
+import self as self;
+import "dart:core" as core;
+
+class A<X extends core::num = core::num> extends core::Object {
+  synthetic constructor •() → self::A<self::A::X>
+    : super core::Object::•()
+    ;
+}
+class B extends self::A<core::num?> {
+  synthetic constructor •() → self::B
+    : super self::A::•()
+    ;
+}
+class C<T extends self::A<core::num?> = self::A<core::num?>> extends core::Object {
+  synthetic constructor •() → self::C<self::C::T>
+    : super core::Object::•()
+    ;
+}
+class Hest extends core::Object {
+  synthetic constructor •() → self::Hest
+    : super core::Object::•()
+    ;
+  method hest<T extends core::num = core::num>() → void {}
+}
+static method foo(self::A<core::num?> a) → dynamic {}
+static method bar() → self::A<core::num?> {}
+static method baz<T extends self::A<core::num?> = self::A<core::num?>>() → dynamic {}
+static method hest<T extends core::num = core::num>() → void {}
+static method fisk(self::Hest h) → dynamic {
+  self::hest<core::num?>();
+  h.{self::Hest::hest}<core::num?>();
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/infer_in_legacy_from_opted_in.dart b/pkg/front_end/testcases/nnbd/infer_in_legacy_from_opted_in.dart
new file mode 100644
index 0000000..739c609
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/infer_in_legacy_from_opted_in.dart
@@ -0,0 +1,16 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart=2.6
+
+// The test checks that a legacy type is inferred even if some of the input
+// types are from opted-in libraries.
+
+import './infer_in_legacy_from_opted_in_lib.dart';
+
+bar(int x) {
+  baz(foo(x, y));
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/infer_in_legacy_from_opted_in.dart.outline.expect b/pkg/front_end/testcases/nnbd/infer_in_legacy_from_opted_in.dart.outline.expect
new file mode 100644
index 0000000..f226132
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/infer_in_legacy_from_opted_in.dart.outline.expect
@@ -0,0 +1,20 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+import "org-dartlang-testcase:///infer_in_legacy_from_opted_in_lib.dart";
+
+static method bar(core::int* x) → dynamic
+  ;
+static method main() → dynamic
+  ;
+
+library /*isNonNullableByDefault*/;
+import self as self2;
+import "dart:core" as core;
+
+static field core::int y;
+static method foo<T extends core::num = core::num>(self2::foo::T t1, self2::foo::T t2) → self2::foo::T
+  ;
+static method baz(core::int? v) → dynamic
+  ;
diff --git a/pkg/front_end/testcases/nnbd/infer_in_legacy_from_opted_in.dart.strong.expect b/pkg/front_end/testcases/nnbd/infer_in_legacy_from_opted_in.dart.strong.expect
new file mode 100644
index 0000000..52dc9a8
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/infer_in_legacy_from_opted_in.dart.strong.expect
@@ -0,0 +1,20 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "infer_in_legacy_from_opted_in_lib.dart" as inf;
+
+import "org-dartlang-testcase:///infer_in_legacy_from_opted_in_lib.dart";
+
+static method bar(core::int* x) → dynamic {
+  inf::baz(inf::foo<core::int*>(x, inf::y));
+}
+static method main() → dynamic {}
+
+library /*isNonNullableByDefault*/;
+import self as inf;
+import "dart:core" as core;
+
+static field core::int y = 42;
+static method foo<T extends core::num = core::num>(inf::foo::T t1, inf::foo::T t2) → inf::foo::T
+  return t1;
+static method baz(core::int? v) → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/infer_in_legacy_from_opted_in.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/infer_in_legacy_from_opted_in.dart.strong.transformed.expect
new file mode 100644
index 0000000..52dc9a8
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/infer_in_legacy_from_opted_in.dart.strong.transformed.expect
@@ -0,0 +1,20 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "infer_in_legacy_from_opted_in_lib.dart" as inf;
+
+import "org-dartlang-testcase:///infer_in_legacy_from_opted_in_lib.dart";
+
+static method bar(core::int* x) → dynamic {
+  inf::baz(inf::foo<core::int*>(x, inf::y));
+}
+static method main() → dynamic {}
+
+library /*isNonNullableByDefault*/;
+import self as inf;
+import "dart:core" as core;
+
+static field core::int y = 42;
+static method foo<T extends core::num = core::num>(inf::foo::T t1, inf::foo::T t2) → inf::foo::T
+  return t1;
+static method baz(core::int? v) → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/infer_in_legacy_from_opted_in.dart.weak.expect b/pkg/front_end/testcases/nnbd/infer_in_legacy_from_opted_in.dart.weak.expect
new file mode 100644
index 0000000..52dc9a8
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/infer_in_legacy_from_opted_in.dart.weak.expect
@@ -0,0 +1,20 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "infer_in_legacy_from_opted_in_lib.dart" as inf;
+
+import "org-dartlang-testcase:///infer_in_legacy_from_opted_in_lib.dart";
+
+static method bar(core::int* x) → dynamic {
+  inf::baz(inf::foo<core::int*>(x, inf::y));
+}
+static method main() → dynamic {}
+
+library /*isNonNullableByDefault*/;
+import self as inf;
+import "dart:core" as core;
+
+static field core::int y = 42;
+static method foo<T extends core::num = core::num>(inf::foo::T t1, inf::foo::T t2) → inf::foo::T
+  return t1;
+static method baz(core::int? v) → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/infer_in_legacy_from_opted_in.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/infer_in_legacy_from_opted_in.dart.weak.transformed.expect
new file mode 100644
index 0000000..52dc9a8
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/infer_in_legacy_from_opted_in.dart.weak.transformed.expect
@@ -0,0 +1,20 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "infer_in_legacy_from_opted_in_lib.dart" as inf;
+
+import "org-dartlang-testcase:///infer_in_legacy_from_opted_in_lib.dart";
+
+static method bar(core::int* x) → dynamic {
+  inf::baz(inf::foo<core::int*>(x, inf::y));
+}
+static method main() → dynamic {}
+
+library /*isNonNullableByDefault*/;
+import self as inf;
+import "dart:core" as core;
+
+static field core::int y = 42;
+static method foo<T extends core::num = core::num>(inf::foo::T t1, inf::foo::T t2) → inf::foo::T
+  return t1;
+static method baz(core::int? v) → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/infer_in_legacy_from_opted_in_lib.dart b/pkg/front_end/testcases/nnbd/infer_in_legacy_from_opted_in_lib.dart
new file mode 100644
index 0000000..75230d2
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/infer_in_legacy_from_opted_in_lib.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2020, 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.
+
+T foo<T extends num>(T t1, T t2) => t1;
+
+int y = 42;
+
+baz(int? v) {}
diff --git a/pkg/front_end/testcases/nnbd/member_inheritance_from_opt_in.dart.outline.expect b/pkg/front_end/testcases/nnbd/member_inheritance_from_opt_in.dart.outline.expect
index 331438b..23d3652 100644
--- a/pkg/front_end/testcases/nnbd/member_inheritance_from_opt_in.dart.outline.expect
+++ b/pkg/front_end/testcases/nnbd/member_inheritance_from_opt_in.dart.outline.expect
@@ -46,30 +46,30 @@
     ;
   set property4(core::int* value) → void
     ;
-  abstract member-signature get property6() → core::int*;
+  abstract forwarding-stub member-signature get property6() → core::int*;
   abstract member-signature get getter1() → core::int*;
   abstract member-signature get field1() → core::int*;
-  abstract member-signature get field2() → core::int*;
-  abstract member-signature get getter2() → core::int*;
+  abstract forwarding-stub member-signature get field2() → core::int*;
+  abstract forwarding-stub member-signature get getter2() → core::int*;
   abstract member-signature method method5a(core::int* a, core::int* b) → core::int*;
   abstract member-signature method method9a(core::int* a, {core::int* b}) → core::int*;
   abstract member-signature method method5c([core::int* a, core::int* b]) → core::int*;
   abstract member-signature get property5() → core::int*;
-  abstract member-signature method method2() → core::int*;
+  abstract forwarding-stub member-signature method method2() → core::int*;
   abstract member-signature method method7a(core::int* a, {core::int* b}) → core::int*;
   abstract member-signature method method5b(core::int* a, [core::int* b]) → core::int*;
   abstract member-signature method method9b({core::int* a, core::int* b}) → core::int*;
   abstract member-signature method method1() → core::int*;
   abstract member-signature get property1() → core::int*;
-  abstract member-signature get property2() → core::int*;
+  abstract forwarding-stub member-signature get property2() → core::int*;
   abstract member-signature method method7b({core::int* a, core::int* b}) → core::int*;
-  abstract member-signature set setter1(core::int* value) → void;
+  abstract forwarding-stub member-signature set setter1(core::int* value) → void;
   abstract member-signature set property6(core::int* _) → void;
-  abstract member-signature set field1(core::int* _) → void;
+  abstract forwarding-stub member-signature set field1(core::int* _) → void;
   abstract member-signature set field2(core::int* _) → void;
-  abstract member-signature set property5(core::int* _) → void;
+  abstract forwarding-stub member-signature set property5(core::int* value) → void;
   abstract member-signature set setter2(core::int* value) → void;
-  abstract member-signature set property1(core::int* value) → void;
+  abstract forwarding-stub member-signature set property1(core::int* value) → void;
   abstract member-signature set property2(core::int* value) → void;
 }
 static method main() → dynamic
diff --git a/pkg/front_end/testcases/nnbd/member_inheritance_from_opt_in.dart.strong.expect b/pkg/front_end/testcases/nnbd/member_inheritance_from_opt_in.dart.strong.expect
index a926f8b..6208ce9 100644
--- a/pkg/front_end/testcases/nnbd/member_inheritance_from_opt_in.dart.strong.expect
+++ b/pkg/front_end/testcases/nnbd/member_inheritance_from_opt_in.dart.strong.expect
@@ -43,30 +43,30 @@
   get property4() → core::int*
     return 0;
   set property4(core::int* value) → void {}
-  abstract member-signature get property6() → core::int*;
+  abstract forwarding-stub member-signature get property6() → core::int*;
   abstract member-signature get getter1() → core::int*;
   abstract member-signature get field1() → core::int*;
-  abstract member-signature get field2() → core::int*;
-  abstract member-signature get getter2() → core::int*;
+  abstract forwarding-stub member-signature get field2() → core::int*;
+  abstract forwarding-stub member-signature get getter2() → core::int*;
   abstract member-signature method method5a(core::int* a, core::int* b) → core::int*;
   abstract member-signature method method9a(core::int* a, {core::int* b = #C1}) → core::int*;
   abstract member-signature method method5c([core::int* a = #C2, core::int* b = #C1]) → core::int*;
   abstract member-signature get property5() → core::int*;
-  abstract member-signature method method2() → core::int*;
+  abstract forwarding-stub member-signature method method2() → core::int*;
   abstract member-signature method method7a(core::int* a, {core::int* b = #C1}) → core::int*;
   abstract member-signature method method5b(core::int* a, [core::int* b = #C1]) → core::int*;
   abstract member-signature method method9b({core::int* a = #C1, core::int* b = #C1}) → core::int*;
   abstract member-signature method method1() → core::int*;
   abstract member-signature get property1() → core::int*;
-  abstract member-signature get property2() → core::int*;
+  abstract forwarding-stub member-signature get property2() → core::int*;
   abstract member-signature method method7b({core::int* a = #C2, core::int* b = #C1}) → core::int*;
-  abstract member-signature set setter1(core::int* value) → void;
+  abstract forwarding-stub member-signature set setter1(core::int* value) → void;
   abstract member-signature set property6(core::int* _) → void;
-  abstract member-signature set field1(core::int* _) → void;
+  abstract forwarding-stub member-signature set field1(core::int* _) → void;
   abstract member-signature set field2(core::int* _) → void;
-  abstract member-signature set property5(core::int* _) → void;
+  abstract forwarding-stub member-signature set property5(core::int* value) → void;
   abstract member-signature set setter2(core::int* value) → void;
-  abstract member-signature set property1(core::int* value) → void;
+  abstract forwarding-stub member-signature set property1(core::int* value) → void;
   abstract member-signature set property2(core::int* value) → void;
 }
 static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/member_inheritance_from_opt_in.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/member_inheritance_from_opt_in.dart.strong.transformed.expect
index a926f8b..6208ce9 100644
--- a/pkg/front_end/testcases/nnbd/member_inheritance_from_opt_in.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/member_inheritance_from_opt_in.dart.strong.transformed.expect
@@ -43,30 +43,30 @@
   get property4() → core::int*
     return 0;
   set property4(core::int* value) → void {}
-  abstract member-signature get property6() → core::int*;
+  abstract forwarding-stub member-signature get property6() → core::int*;
   abstract member-signature get getter1() → core::int*;
   abstract member-signature get field1() → core::int*;
-  abstract member-signature get field2() → core::int*;
-  abstract member-signature get getter2() → core::int*;
+  abstract forwarding-stub member-signature get field2() → core::int*;
+  abstract forwarding-stub member-signature get getter2() → core::int*;
   abstract member-signature method method5a(core::int* a, core::int* b) → core::int*;
   abstract member-signature method method9a(core::int* a, {core::int* b = #C1}) → core::int*;
   abstract member-signature method method5c([core::int* a = #C2, core::int* b = #C1]) → core::int*;
   abstract member-signature get property5() → core::int*;
-  abstract member-signature method method2() → core::int*;
+  abstract forwarding-stub member-signature method method2() → core::int*;
   abstract member-signature method method7a(core::int* a, {core::int* b = #C1}) → core::int*;
   abstract member-signature method method5b(core::int* a, [core::int* b = #C1]) → core::int*;
   abstract member-signature method method9b({core::int* a = #C1, core::int* b = #C1}) → core::int*;
   abstract member-signature method method1() → core::int*;
   abstract member-signature get property1() → core::int*;
-  abstract member-signature get property2() → core::int*;
+  abstract forwarding-stub member-signature get property2() → core::int*;
   abstract member-signature method method7b({core::int* a = #C2, core::int* b = #C1}) → core::int*;
-  abstract member-signature set setter1(core::int* value) → void;
+  abstract forwarding-stub member-signature set setter1(core::int* value) → void;
   abstract member-signature set property6(core::int* _) → void;
-  abstract member-signature set field1(core::int* _) → void;
+  abstract forwarding-stub member-signature set field1(core::int* _) → void;
   abstract member-signature set field2(core::int* _) → void;
-  abstract member-signature set property5(core::int* _) → void;
+  abstract forwarding-stub member-signature set property5(core::int* value) → void;
   abstract member-signature set setter2(core::int* value) → void;
-  abstract member-signature set property1(core::int* value) → void;
+  abstract forwarding-stub member-signature set property1(core::int* value) → void;
   abstract member-signature set property2(core::int* value) → void;
 }
 static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/member_inheritance_from_opt_in.dart.weak.expect b/pkg/front_end/testcases/nnbd/member_inheritance_from_opt_in.dart.weak.expect
index a926f8b..6208ce9 100644
--- a/pkg/front_end/testcases/nnbd/member_inheritance_from_opt_in.dart.weak.expect
+++ b/pkg/front_end/testcases/nnbd/member_inheritance_from_opt_in.dart.weak.expect
@@ -43,30 +43,30 @@
   get property4() → core::int*
     return 0;
   set property4(core::int* value) → void {}
-  abstract member-signature get property6() → core::int*;
+  abstract forwarding-stub member-signature get property6() → core::int*;
   abstract member-signature get getter1() → core::int*;
   abstract member-signature get field1() → core::int*;
-  abstract member-signature get field2() → core::int*;
-  abstract member-signature get getter2() → core::int*;
+  abstract forwarding-stub member-signature get field2() → core::int*;
+  abstract forwarding-stub member-signature get getter2() → core::int*;
   abstract member-signature method method5a(core::int* a, core::int* b) → core::int*;
   abstract member-signature method method9a(core::int* a, {core::int* b = #C1}) → core::int*;
   abstract member-signature method method5c([core::int* a = #C2, core::int* b = #C1]) → core::int*;
   abstract member-signature get property5() → core::int*;
-  abstract member-signature method method2() → core::int*;
+  abstract forwarding-stub member-signature method method2() → core::int*;
   abstract member-signature method method7a(core::int* a, {core::int* b = #C1}) → core::int*;
   abstract member-signature method method5b(core::int* a, [core::int* b = #C1]) → core::int*;
   abstract member-signature method method9b({core::int* a = #C1, core::int* b = #C1}) → core::int*;
   abstract member-signature method method1() → core::int*;
   abstract member-signature get property1() → core::int*;
-  abstract member-signature get property2() → core::int*;
+  abstract forwarding-stub member-signature get property2() → core::int*;
   abstract member-signature method method7b({core::int* a = #C2, core::int* b = #C1}) → core::int*;
-  abstract member-signature set setter1(core::int* value) → void;
+  abstract forwarding-stub member-signature set setter1(core::int* value) → void;
   abstract member-signature set property6(core::int* _) → void;
-  abstract member-signature set field1(core::int* _) → void;
+  abstract forwarding-stub member-signature set field1(core::int* _) → void;
   abstract member-signature set field2(core::int* _) → void;
-  abstract member-signature set property5(core::int* _) → void;
+  abstract forwarding-stub member-signature set property5(core::int* value) → void;
   abstract member-signature set setter2(core::int* value) → void;
-  abstract member-signature set property1(core::int* value) → void;
+  abstract forwarding-stub member-signature set property1(core::int* value) → void;
   abstract member-signature set property2(core::int* value) → void;
 }
 static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/member_inheritance_from_opt_in.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/member_inheritance_from_opt_in.dart.weak.transformed.expect
index a926f8b..6208ce9 100644
--- a/pkg/front_end/testcases/nnbd/member_inheritance_from_opt_in.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/member_inheritance_from_opt_in.dart.weak.transformed.expect
@@ -43,30 +43,30 @@
   get property4() → core::int*
     return 0;
   set property4(core::int* value) → void {}
-  abstract member-signature get property6() → core::int*;
+  abstract forwarding-stub member-signature get property6() → core::int*;
   abstract member-signature get getter1() → core::int*;
   abstract member-signature get field1() → core::int*;
-  abstract member-signature get field2() → core::int*;
-  abstract member-signature get getter2() → core::int*;
+  abstract forwarding-stub member-signature get field2() → core::int*;
+  abstract forwarding-stub member-signature get getter2() → core::int*;
   abstract member-signature method method5a(core::int* a, core::int* b) → core::int*;
   abstract member-signature method method9a(core::int* a, {core::int* b = #C1}) → core::int*;
   abstract member-signature method method5c([core::int* a = #C2, core::int* b = #C1]) → core::int*;
   abstract member-signature get property5() → core::int*;
-  abstract member-signature method method2() → core::int*;
+  abstract forwarding-stub member-signature method method2() → core::int*;
   abstract member-signature method method7a(core::int* a, {core::int* b = #C1}) → core::int*;
   abstract member-signature method method5b(core::int* a, [core::int* b = #C1]) → core::int*;
   abstract member-signature method method9b({core::int* a = #C1, core::int* b = #C1}) → core::int*;
   abstract member-signature method method1() → core::int*;
   abstract member-signature get property1() → core::int*;
-  abstract member-signature get property2() → core::int*;
+  abstract forwarding-stub member-signature get property2() → core::int*;
   abstract member-signature method method7b({core::int* a = #C2, core::int* b = #C1}) → core::int*;
-  abstract member-signature set setter1(core::int* value) → void;
+  abstract forwarding-stub member-signature set setter1(core::int* value) → void;
   abstract member-signature set property6(core::int* _) → void;
-  abstract member-signature set field1(core::int* _) → void;
+  abstract forwarding-stub member-signature set field1(core::int* _) → void;
   abstract member-signature set field2(core::int* _) → void;
-  abstract member-signature set property5(core::int* _) → void;
+  abstract forwarding-stub member-signature set property5(core::int* value) → void;
   abstract member-signature set setter2(core::int* value) → void;
-  abstract member-signature set property1(core::int* value) → void;
+  abstract forwarding-stub member-signature set property1(core::int* value) → void;
   abstract member-signature set property2(core::int* value) → void;
 }
 static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/opt_out.dart.strong.expect b/pkg/front_end/testcases/nnbd/opt_out.dart.strong.expect
index cd4124b..2fce215 100644
--- a/pkg/front_end/testcases/nnbd/opt_out.dart.strong.expect
+++ b/pkg/front_end/testcases/nnbd/opt_out.dart.strong.expect
@@ -133,7 +133,7 @@
     : super self2::A::•()
     ;
 }
-static field core::List<core::String?>* l = <core::String?>[];
+static field core::List<core::String?>* l = <core::String*>[];
 static field core::String? s = null;
 static field core::String* t = self2::s!;
 static field core::int* field = 42;
diff --git a/pkg/front_end/testcases/nnbd/opt_out.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/opt_out.dart.strong.transformed.expect
index cd4124b..2fce215 100644
--- a/pkg/front_end/testcases/nnbd/opt_out.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/opt_out.dart.strong.transformed.expect
@@ -133,7 +133,7 @@
     : super self2::A::•()
     ;
 }
-static field core::List<core::String?>* l = <core::String?>[];
+static field core::List<core::String?>* l = <core::String*>[];
 static field core::String? s = null;
 static field core::String* t = self2::s!;
 static field core::int* field = 42;
diff --git a/pkg/front_end/testcases/nnbd/opt_out.dart.weak.expect b/pkg/front_end/testcases/nnbd/opt_out.dart.weak.expect
index cd4124b..2fce215 100644
--- a/pkg/front_end/testcases/nnbd/opt_out.dart.weak.expect
+++ b/pkg/front_end/testcases/nnbd/opt_out.dart.weak.expect
@@ -133,7 +133,7 @@
     : super self2::A::•()
     ;
 }
-static field core::List<core::String?>* l = <core::String?>[];
+static field core::List<core::String?>* l = <core::String*>[];
 static field core::String? s = null;
 static field core::String* t = self2::s!;
 static field core::int* field = 42;
diff --git a/pkg/front_end/testcases/nnbd/opt_out.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/opt_out.dart.weak.transformed.expect
index cd4124b..2fce215 100644
--- a/pkg/front_end/testcases/nnbd/opt_out.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/opt_out.dart.weak.transformed.expect
@@ -133,7 +133,7 @@
     : super self2::A::•()
     ;
 }
-static field core::List<core::String?>* l = <core::String?>[];
+static field core::List<core::String?>* l = <core::String*>[];
 static field core::String? s = null;
 static field core::String* t = self2::s!;
 static field core::int* field = 42;
diff --git a/pkg/front_end/testcases/nnbd/override_checks.dart b/pkg/front_end/testcases/nnbd/override_checks.dart
new file mode 100644
index 0000000..10e4918
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/override_checks.dart
@@ -0,0 +1,30 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+class A<X extends num> {}
+
+class B1 {
+  void set bar(num? value) {}
+  num get baz => 42;
+  void hest(num? value) {}
+}
+
+class B2 extends B1 {
+  num bar = 3.14; // Error in strong mode and Warning in weak mode.
+  num? get baz => null; // Error in strong mode and Warning in weak mode.
+  void hest(num value) {} // Error in strong mode and Warning in weak mode.
+}
+
+class C1 {
+  factory C1 = C2<int?>; // Error in strong mode and Warning in weak mode.
+}
+
+class C2<X extends int> implements C1 {}
+
+class D {
+  D.foo(num x);
+  factory D.bar(num? x) = D.foo; // Error in strong mode and Warning in weak mode.
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/override_checks.dart.outline.expect b/pkg/front_end/testcases/nnbd/override_checks.dart.outline.expect
new file mode 100644
index 0000000..6ca32e4
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/override_checks.dart.outline.expect
@@ -0,0 +1,86 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:20:14: Error: A function declaration needs an explicit list of parameters.
+// Try adding a parameter list to the function declaration.
+//   factory C1 = C2<int?>; // Error in strong mode and Warning in weak mode.
+//              ^
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:16:17: Error: The parameter 'value' of the method 'B2.hest' has type 'num', which does not match the corresponding type, 'num?', in the overridden method, 'B1.hest'.
+// Change to a supertype of 'num?', or, for a covariant parameter, a subtype.
+//   void hest(num value) {} // Error in strong mode and Warning in weak mode.
+//                 ^
+// pkg/front_end/testcases/nnbd/override_checks.dart:10:8: Context: This is the overridden method ('hest').
+//   void hest(num? value) {}
+//        ^
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:15:12: Error: The return type of the method 'B2.baz' is 'num?', which does not match the return type, 'num', of the overridden method, 'B1.baz'.
+// Change to a subtype of 'num'.
+//   num? get baz => null; // Error in strong mode and Warning in weak mode.
+//            ^
+// pkg/front_end/testcases/nnbd/override_checks.dart:9:11: Context: This is the overridden method ('baz').
+//   num get baz => 42;
+//           ^
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:14:7: Error: The field 'B2.bar' has type 'num', which does not match the corresponding type, 'num?', in the overridden setter, 'B1.bar'.
+//   num bar = 3.14; // Error in strong mode and Warning in weak mode.
+//       ^
+// pkg/front_end/testcases/nnbd/override_checks.dart:8:12: Context: This is the overridden method ('bar').
+//   void set bar(num? value) {}
+//            ^
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:20:16: Error: The type 'int?' doesn't extend 'int'.
+// Try using a different type as argument.
+//   factory C1 = C2<int?>; // Error in strong mode and Warning in weak mode.
+//                ^
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:27:27: Error: The constructor function type 'D Function(num)' isn't a subtype of 'D Function(num?)'.
+//  - 'D' is from 'pkg/front_end/testcases/nnbd/override_checks.dart'.
+//   factory D.bar(num? x) = D.foo; // Error in strong mode and Warning in weak mode.
+//                           ^
+//
+import self as self;
+import "dart:core" as core;
+
+class A<X extends core::num = core::num> extends core::Object {
+  synthetic constructor •() → self::A<self::A::X>
+    ;
+}
+class B1 extends core::Object {
+  synthetic constructor •() → self::B1
+    ;
+  set bar(core::num? value) → void
+    ;
+  get baz() → core::num
+    ;
+  method hest(core::num? value) → void
+    ;
+}
+class B2 extends self::B1 {
+  field core::num bar;
+  synthetic constructor •() → self::B2
+    ;
+  get baz() → core::num?
+    ;
+  method hest(core::num value) → void
+    ;
+}
+class C1 extends core::Object {
+  static field dynamic _redirecting# = <dynamic>[self::C1::•]/*isNullableByDefault*/;
+  static factory •() → self::C1
+    let dynamic #redirecting_factory = self::C2::• in let core::int? #typeArg0 = null in invalid-expression;
+}
+class C2<X extends core::int = core::int> extends core::Object implements self::C1 {
+  synthetic constructor •() → self::C2<self::C2::X>
+    ;
+}
+class D extends core::Object {
+  static field dynamic _redirecting# = <dynamic>[self::D::bar]/*isNullableByDefault*/;
+  constructor foo(core::num x) → self::D
+    ;
+  static factory bar(core::num? x) → self::D
+    let dynamic #redirecting_factory = self::D::foo in invalid-expression;
+}
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/nnbd/override_checks.dart.strong.expect b/pkg/front_end/testcases/nnbd/override_checks.dart.strong.expect
new file mode 100644
index 0000000..a2a15d0
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/override_checks.dart.strong.expect
@@ -0,0 +1,87 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:20:14: Error: A function declaration needs an explicit list of parameters.
+// Try adding a parameter list to the function declaration.
+//   factory C1 = C2<int?>; // Error in strong mode and Warning in weak mode.
+//              ^
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:16:17: Error: The parameter 'value' of the method 'B2.hest' has type 'num', which does not match the corresponding type, 'num?', in the overridden method, 'B1.hest'.
+// Change to a supertype of 'num?', or, for a covariant parameter, a subtype.
+//   void hest(num value) {} // Error in strong mode and Warning in weak mode.
+//                 ^
+// pkg/front_end/testcases/nnbd/override_checks.dart:10:8: Context: This is the overridden method ('hest').
+//   void hest(num? value) {}
+//        ^
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:15:12: Error: The return type of the method 'B2.baz' is 'num?', which does not match the return type, 'num', of the overridden method, 'B1.baz'.
+// Change to a subtype of 'num'.
+//   num? get baz => null; // Error in strong mode and Warning in weak mode.
+//            ^
+// pkg/front_end/testcases/nnbd/override_checks.dart:9:11: Context: This is the overridden method ('baz').
+//   num get baz => 42;
+//           ^
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:14:7: Error: The field 'B2.bar' has type 'num', which does not match the corresponding type, 'num?', in the overridden setter, 'B1.bar'.
+//   num bar = 3.14; // Error in strong mode and Warning in weak mode.
+//       ^
+// pkg/front_end/testcases/nnbd/override_checks.dart:8:12: Context: This is the overridden method ('bar').
+//   void set bar(num? value) {}
+//            ^
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:20:16: Error: The type 'int?' doesn't extend 'int'.
+// Try using a different type as argument.
+//   factory C1 = C2<int?>; // Error in strong mode and Warning in weak mode.
+//                ^
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:27:27: Error: The constructor function type 'D Function(num)' isn't a subtype of 'D Function(num?)'.
+//  - 'D' is from 'pkg/front_end/testcases/nnbd/override_checks.dart'.
+//   factory D.bar(num? x) = D.foo; // Error in strong mode and Warning in weak mode.
+//                           ^
+//
+import self as self;
+import "dart:core" as core;
+
+class A<X extends core::num = core::num> extends core::Object {
+  synthetic constructor •() → self::A<self::A::X>
+    : super core::Object::•()
+    ;
+}
+class B1 extends core::Object {
+  synthetic constructor •() → self::B1
+    : super core::Object::•()
+    ;
+  set bar(core::num? value) → void {}
+  get baz() → core::num
+    return 42;
+  method hest(core::num? value) → void {}
+}
+class B2 extends self::B1 {
+  field core::num bar = 3.14;
+  synthetic constructor •() → self::B2
+    : super self::B1::•()
+    ;
+  get baz() → core::num?
+    return null;
+  method hest(core::num value) → void {}
+}
+class C1 extends core::Object {
+  static field dynamic _redirecting# = <dynamic>[self::C1::•]/*isNullableByDefault*/;
+  static factory •() → self::C1
+    let dynamic #redirecting_factory = self::C2::• in let core::int? #typeArg0 = null in invalid-expression;
+}
+class C2<X extends core::int = core::int> extends core::Object implements self::C1 {
+  synthetic constructor •() → self::C2<self::C2::X>
+    : super core::Object::•()
+    ;
+}
+class D extends core::Object {
+  static field dynamic _redirecting# = <dynamic>[self::D::bar]/*isNullableByDefault*/;
+  constructor foo(core::num x) → self::D
+    : super core::Object::•()
+    ;
+  static factory bar(core::num? x) → self::D
+    let dynamic #redirecting_factory = self::D::foo in invalid-expression;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/override_checks.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/override_checks.dart.strong.transformed.expect
new file mode 100644
index 0000000..3ddcd06
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/override_checks.dart.strong.transformed.expect
@@ -0,0 +1,87 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:20:14: Error: A function declaration needs an explicit list of parameters.
+// Try adding a parameter list to the function declaration.
+//   factory C1 = C2<int?>; // Error in strong mode and Warning in weak mode.
+//              ^
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:16:17: Error: The parameter 'value' of the method 'B2.hest' has type 'num', which does not match the corresponding type, 'num?', in the overridden method, 'B1.hest'.
+// Change to a supertype of 'num?', or, for a covariant parameter, a subtype.
+//   void hest(num value) {} // Error in strong mode and Warning in weak mode.
+//                 ^
+// pkg/front_end/testcases/nnbd/override_checks.dart:10:8: Context: This is the overridden method ('hest').
+//   void hest(num? value) {}
+//        ^
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:15:12: Error: The return type of the method 'B2.baz' is 'num?', which does not match the return type, 'num', of the overridden method, 'B1.baz'.
+// Change to a subtype of 'num'.
+//   num? get baz => null; // Error in strong mode and Warning in weak mode.
+//            ^
+// pkg/front_end/testcases/nnbd/override_checks.dart:9:11: Context: This is the overridden method ('baz').
+//   num get baz => 42;
+//           ^
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:14:7: Error: The field 'B2.bar' has type 'num', which does not match the corresponding type, 'num?', in the overridden setter, 'B1.bar'.
+//   num bar = 3.14; // Error in strong mode and Warning in weak mode.
+//       ^
+// pkg/front_end/testcases/nnbd/override_checks.dart:8:12: Context: This is the overridden method ('bar').
+//   void set bar(num? value) {}
+//            ^
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:20:16: Error: The type 'int?' doesn't extend 'int'.
+// Try using a different type as argument.
+//   factory C1 = C2<int?>; // Error in strong mode and Warning in weak mode.
+//                ^
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:27:27: Error: The constructor function type 'D Function(num)' isn't a subtype of 'D Function(num?)'.
+//  - 'D' is from 'pkg/front_end/testcases/nnbd/override_checks.dart'.
+//   factory D.bar(num? x) = D.foo; // Error in strong mode and Warning in weak mode.
+//                           ^
+//
+import self as self;
+import "dart:core" as core;
+
+class A<X extends core::num = core::num> extends core::Object {
+  synthetic constructor •() → self::A<self::A::X>
+    : super core::Object::•()
+    ;
+}
+class B1 extends core::Object {
+  synthetic constructor •() → self::B1
+    : super core::Object::•()
+    ;
+  set bar(core::num? value) → void {}
+  get baz() → core::num
+    return 42;
+  method hest(core::num? value) → void {}
+}
+class B2 extends self::B1 {
+  field core::num bar = 3.14;
+  synthetic constructor •() → self::B2
+    : super self::B1::•()
+    ;
+  get baz() → core::num?
+    return null;
+  method hest(core::num value) → void {}
+}
+class C1 extends core::Object {
+  static field dynamic _redirecting# = <dynamic>[self::C1::•]/*isNullableByDefault*/;
+  static factory •() → self::C1
+    let<BottomType> #redirecting_factory = self::C2::• in let core::int? #typeArg0 = null in invalid-expression;
+}
+class C2<X extends core::int = core::int> extends core::Object implements self::C1 {
+  synthetic constructor •() → self::C2<self::C2::X>
+    : super core::Object::•()
+    ;
+}
+class D extends core::Object {
+  static field dynamic _redirecting# = <dynamic>[self::D::bar]/*isNullableByDefault*/;
+  constructor foo(core::num x) → self::D
+    : super core::Object::•()
+    ;
+  static factory bar(core::num? x) → self::D
+    let<BottomType> #redirecting_factory = self::D::foo in invalid-expression;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/override_checks.dart.weak.expect b/pkg/front_end/testcases/nnbd/override_checks.dart.weak.expect
new file mode 100644
index 0000000..ffb08e6
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/override_checks.dart.weak.expect
@@ -0,0 +1,87 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:20:14: Error: A function declaration needs an explicit list of parameters.
+// Try adding a parameter list to the function declaration.
+//   factory C1 = C2<int?>; // Error in strong mode and Warning in weak mode.
+//              ^
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:16:17: Warning: The parameter 'value' of the method 'B2.hest' has type 'num', which does not match the corresponding type, 'num?', in the overridden method, 'B1.hest'.
+// Change to a supertype of 'num?', or, for a covariant parameter, a subtype.
+//   void hest(num value) {} // Error in strong mode and Warning in weak mode.
+//                 ^
+// pkg/front_end/testcases/nnbd/override_checks.dart:10:8: Context: This is the overridden method ('hest').
+//   void hest(num? value) {}
+//        ^
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:15:12: Warning: The return type of the method 'B2.baz' is 'num?', which does not match the return type, 'num', of the overridden method, 'B1.baz'.
+// Change to a subtype of 'num'.
+//   num? get baz => null; // Error in strong mode and Warning in weak mode.
+//            ^
+// pkg/front_end/testcases/nnbd/override_checks.dart:9:11: Context: This is the overridden method ('baz').
+//   num get baz => 42;
+//           ^
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:14:7: Warning: The field 'B2.bar' has type 'num', which does not match the corresponding type, 'num?', in the overridden setter, 'B1.bar'.
+//   num bar = 3.14; // Error in strong mode and Warning in weak mode.
+//       ^
+// pkg/front_end/testcases/nnbd/override_checks.dart:8:12: Context: This is the overridden method ('bar').
+//   void set bar(num? value) {}
+//            ^
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:20:16: Warning: The type 'int?' doesn't extend 'int'.
+// Try using a different type as argument.
+//   factory C1 = C2<int?>; // Error in strong mode and Warning in weak mode.
+//                ^
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:27:27: Warning: The constructor function type 'D Function(num)' isn't a subtype of 'D Function(num?)'.
+//  - 'D' is from 'pkg/front_end/testcases/nnbd/override_checks.dart'.
+//   factory D.bar(num? x) = D.foo; // Error in strong mode and Warning in weak mode.
+//                           ^
+//
+import self as self;
+import "dart:core" as core;
+
+class A<X extends core::num = core::num> extends core::Object {
+  synthetic constructor •() → self::A<self::A::X>
+    : super core::Object::•()
+    ;
+}
+class B1 extends core::Object {
+  synthetic constructor •() → self::B1
+    : super core::Object::•()
+    ;
+  set bar(core::num? value) → void {}
+  get baz() → core::num
+    return 42;
+  method hest(core::num? value) → void {}
+}
+class B2 extends self::B1 {
+  field core::num bar = 3.14;
+  synthetic constructor •() → self::B2
+    : super self::B1::•()
+    ;
+  get baz() → core::num?
+    return null;
+  method hest(core::num value) → void {}
+}
+class C1 extends core::Object {
+  static field dynamic _redirecting# = <dynamic>[self::C1::•]/*isNullableByDefault*/;
+  static factory •() → self::C1
+    let dynamic #redirecting_factory = self::C2::• in let core::int? #typeArg0 = null in invalid-expression;
+}
+class C2<X extends core::int = core::int> extends core::Object implements self::C1 {
+  synthetic constructor •() → self::C2<self::C2::X>
+    : super core::Object::•()
+    ;
+}
+class D extends core::Object {
+  static field dynamic _redirecting# = <dynamic>[self::D::bar]/*isNullableByDefault*/;
+  constructor foo(core::num x) → self::D
+    : super core::Object::•()
+    ;
+  static factory bar(core::num? x) → self::D
+    let dynamic #redirecting_factory = self::D::foo in invalid-expression;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/override_checks.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/override_checks.dart.weak.transformed.expect
new file mode 100644
index 0000000..7042115
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/override_checks.dart.weak.transformed.expect
@@ -0,0 +1,87 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:20:14: Error: A function declaration needs an explicit list of parameters.
+// Try adding a parameter list to the function declaration.
+//   factory C1 = C2<int?>; // Error in strong mode and Warning in weak mode.
+//              ^
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:16:17: Warning: The parameter 'value' of the method 'B2.hest' has type 'num', which does not match the corresponding type, 'num?', in the overridden method, 'B1.hest'.
+// Change to a supertype of 'num?', or, for a covariant parameter, a subtype.
+//   void hest(num value) {} // Error in strong mode and Warning in weak mode.
+//                 ^
+// pkg/front_end/testcases/nnbd/override_checks.dart:10:8: Context: This is the overridden method ('hest').
+//   void hest(num? value) {}
+//        ^
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:15:12: Warning: The return type of the method 'B2.baz' is 'num?', which does not match the return type, 'num', of the overridden method, 'B1.baz'.
+// Change to a subtype of 'num'.
+//   num? get baz => null; // Error in strong mode and Warning in weak mode.
+//            ^
+// pkg/front_end/testcases/nnbd/override_checks.dart:9:11: Context: This is the overridden method ('baz').
+//   num get baz => 42;
+//           ^
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:14:7: Warning: The field 'B2.bar' has type 'num', which does not match the corresponding type, 'num?', in the overridden setter, 'B1.bar'.
+//   num bar = 3.14; // Error in strong mode and Warning in weak mode.
+//       ^
+// pkg/front_end/testcases/nnbd/override_checks.dart:8:12: Context: This is the overridden method ('bar').
+//   void set bar(num? value) {}
+//            ^
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:20:16: Warning: The type 'int?' doesn't extend 'int'.
+// Try using a different type as argument.
+//   factory C1 = C2<int?>; // Error in strong mode and Warning in weak mode.
+//                ^
+//
+// pkg/front_end/testcases/nnbd/override_checks.dart:27:27: Warning: The constructor function type 'D Function(num)' isn't a subtype of 'D Function(num?)'.
+//  - 'D' is from 'pkg/front_end/testcases/nnbd/override_checks.dart'.
+//   factory D.bar(num? x) = D.foo; // Error in strong mode and Warning in weak mode.
+//                           ^
+//
+import self as self;
+import "dart:core" as core;
+
+class A<X extends core::num = core::num> extends core::Object {
+  synthetic constructor •() → self::A<self::A::X>
+    : super core::Object::•()
+    ;
+}
+class B1 extends core::Object {
+  synthetic constructor •() → self::B1
+    : super core::Object::•()
+    ;
+  set bar(core::num? value) → void {}
+  get baz() → core::num
+    return 42;
+  method hest(core::num? value) → void {}
+}
+class B2 extends self::B1 {
+  field core::num bar = 3.14;
+  synthetic constructor •() → self::B2
+    : super self::B1::•()
+    ;
+  get baz() → core::num?
+    return null;
+  method hest(core::num value) → void {}
+}
+class C1 extends core::Object {
+  static field dynamic _redirecting# = <dynamic>[self::C1::•]/*isNullableByDefault*/;
+  static factory •() → self::C1
+    let<BottomType> #redirecting_factory = self::C2::• in let core::int? #typeArg0 = null in invalid-expression;
+}
+class C2<X extends core::int = core::int> extends core::Object implements self::C1 {
+  synthetic constructor •() → self::C2<self::C2::X>
+    : super core::Object::•()
+    ;
+}
+class D extends core::Object {
+  static field dynamic _redirecting# = <dynamic>[self::D::bar]/*isNullableByDefault*/;
+  constructor foo(core::num x) → self::D
+    : super core::Object::•()
+    ;
+  static factory bar(core::num? x) → self::D
+    let<BottomType> #redirecting_factory = self::D::foo in invalid-expression;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/shorting_stop.dart.strong.expect b/pkg/front_end/testcases/nnbd/shorting_stop.dart.strong.expect
index f35cdbd..9ddd160 100644
--- a/pkg/front_end/testcases/nnbd/shorting_stop.dart.strong.expect
+++ b/pkg/front_end/testcases/nnbd/shorting_stop.dart.strong.expect
@@ -54,7 +54,7 @@
 }
 static method test(self::Class? c) → dynamic {
   let final self::Class? #t1 = c in #t1.{core::Object::==}(null) ?{core::int?} null : #t1{self::Class}.{self::Class::next}.{self::Class::field};
-  self::throwsInStrong(() → core::int => let final<BottomType> #t2 = invalid-expression "pkg/front_end/testcases/nnbd/shorting_stop.dart:18:33: Error: Operator '+' cannot be called on 'int?' because it is potentially null.
+  self::throwsInStrong(() → core::int? => let final<BottomType> #t2 = invalid-expression "pkg/front_end/testcases/nnbd/shorting_stop.dart:18:33: Error: Operator '+' cannot be called on 'int?' because it is potentially null.
   throwsInStrong(() => c?.field + 2); // error
                                 ^" in (let final self::Class? #t3 = c in #t3.{core::Object::==}(null) ?{core::int?} null : #t3{self::Class}.{self::Class::field}).{core::num::+}(2));
   let final self::Class? #t4 = c in #t4.{core::Object::==}(null) ?{core::int?} null : let final core::int #t5 = #t4.{self::Class::field}.{core::num::+}(1) in let final void #t6 = #t4.{self::Class::field} = #t5 in #t5;
@@ -68,7 +68,7 @@
   throwsInStrong(() => -c?.field); // error
                        ^" in (let final self::Class? #t11 = c in #t11.{core::Object::==}(null) ?{core::int?} null : #t11{self::Class}.{self::Class::field}).{core::int::unary-}());
   let final self::Class? #t12 = c in #t12.{core::Object::==}(null) ?{core::bool?} null : #t12{self::Class}.{self::Class::next}.{self::Class::[]}(0).{core::int::isEven};
-  self::throwsInStrong(() → core::int => let final<BottomType> #t13 = invalid-expression "pkg/front_end/testcases/nnbd/shorting_stop.dart:25:35: Error: Operator '+' cannot be called on 'int?' because it is potentially null.
+  self::throwsInStrong(() → core::int? => let final<BottomType> #t13 = invalid-expression "pkg/front_end/testcases/nnbd/shorting_stop.dart:25:35: Error: Operator '+' cannot be called on 'int?' because it is potentially null.
   throwsInStrong(() => c?.next[0] + 2); // error
                                   ^" in (let final self::Class? #t14 = c in #t14.{core::Object::==}(null) ?{core::int?} null : #t14{self::Class}.{self::Class::next}.{self::Class::[]}(0)).{core::num::+}(2));
   let final self::Class? #t15 = c in #t15.{core::Object::==}(null) ?{core::int?} null : let final self::Class #t16 = #t15{self::Class}.{self::Class::next} in let final core::int #t17 = 0 in let final core::int #t18 = #t16.{self::Class::[]}(#t17).{core::num::+}(1) in let final void #t19 = #t16.{self::Class::[]=}(#t17, #t18) in #t18;
diff --git a/pkg/front_end/testcases/nnbd/shorting_stop.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/shorting_stop.dart.strong.transformed.expect
index f35cdbd..9ddd160 100644
--- a/pkg/front_end/testcases/nnbd/shorting_stop.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/shorting_stop.dart.strong.transformed.expect
@@ -54,7 +54,7 @@
 }
 static method test(self::Class? c) → dynamic {
   let final self::Class? #t1 = c in #t1.{core::Object::==}(null) ?{core::int?} null : #t1{self::Class}.{self::Class::next}.{self::Class::field};
-  self::throwsInStrong(() → core::int => let final<BottomType> #t2 = invalid-expression "pkg/front_end/testcases/nnbd/shorting_stop.dart:18:33: Error: Operator '+' cannot be called on 'int?' because it is potentially null.
+  self::throwsInStrong(() → core::int? => let final<BottomType> #t2 = invalid-expression "pkg/front_end/testcases/nnbd/shorting_stop.dart:18:33: Error: Operator '+' cannot be called on 'int?' because it is potentially null.
   throwsInStrong(() => c?.field + 2); // error
                                 ^" in (let final self::Class? #t3 = c in #t3.{core::Object::==}(null) ?{core::int?} null : #t3{self::Class}.{self::Class::field}).{core::num::+}(2));
   let final self::Class? #t4 = c in #t4.{core::Object::==}(null) ?{core::int?} null : let final core::int #t5 = #t4.{self::Class::field}.{core::num::+}(1) in let final void #t6 = #t4.{self::Class::field} = #t5 in #t5;
@@ -68,7 +68,7 @@
   throwsInStrong(() => -c?.field); // error
                        ^" in (let final self::Class? #t11 = c in #t11.{core::Object::==}(null) ?{core::int?} null : #t11{self::Class}.{self::Class::field}).{core::int::unary-}());
   let final self::Class? #t12 = c in #t12.{core::Object::==}(null) ?{core::bool?} null : #t12{self::Class}.{self::Class::next}.{self::Class::[]}(0).{core::int::isEven};
-  self::throwsInStrong(() → core::int => let final<BottomType> #t13 = invalid-expression "pkg/front_end/testcases/nnbd/shorting_stop.dart:25:35: Error: Operator '+' cannot be called on 'int?' because it is potentially null.
+  self::throwsInStrong(() → core::int? => let final<BottomType> #t13 = invalid-expression "pkg/front_end/testcases/nnbd/shorting_stop.dart:25:35: Error: Operator '+' cannot be called on 'int?' because it is potentially null.
   throwsInStrong(() => c?.next[0] + 2); // error
                                   ^" in (let final self::Class? #t14 = c in #t14.{core::Object::==}(null) ?{core::int?} null : #t14{self::Class}.{self::Class::next}.{self::Class::[]}(0)).{core::num::+}(2));
   let final self::Class? #t15 = c in #t15.{core::Object::==}(null) ?{core::int?} null : let final self::Class #t16 = #t15{self::Class}.{self::Class::next} in let final core::int #t17 = 0 in let final core::int #t18 = #t16.{self::Class::[]}(#t17).{core::num::+}(1) in let final void #t19 = #t16.{self::Class::[]=}(#t17, #t18) in #t18;
diff --git a/pkg/front_end/testcases/nnbd/shorting_stop.dart.weak.expect b/pkg/front_end/testcases/nnbd/shorting_stop.dart.weak.expect
index e32de91..0c83c79 100644
--- a/pkg/front_end/testcases/nnbd/shorting_stop.dart.weak.expect
+++ b/pkg/front_end/testcases/nnbd/shorting_stop.dart.weak.expect
@@ -54,13 +54,13 @@
 }
 static method test(self::Class? c) → dynamic {
   let final self::Class? #t1 = c in #t1.{core::Object::==}(null) ?{core::int?} null : #t1{self::Class}.{self::Class::next}.{self::Class::field};
-  self::throwsInStrong(() → core::int => (let final self::Class? #t2 = c in #t2.{core::Object::==}(null) ?{core::int?} null : #t2{self::Class}.{self::Class::field}).{core::num::+}(2));
+  self::throwsInStrong(() → core::int? => (let final self::Class? #t2 = c in #t2.{core::Object::==}(null) ?{core::int?} null : #t2{self::Class}.{self::Class::field}).{core::num::+}(2));
   let final self::Class? #t3 = c in #t3.{core::Object::==}(null) ?{core::int?} null : let final core::int #t4 = #t3.{self::Class::field}.{core::num::+}(1) in let final void #t5 = #t3.{self::Class::field} = #t4 in #t4;
   let final self::Class? #t6 = c in #t6.{core::Object::==}(null) ?{core::int?} null : #t6.{self::Class::field} = #t6.{self::Class::field}.{core::num::+}(1);
   self::throwsInStrong(() → core::int => (let final self::Class? #t7 = c in #t7.{core::Object::==}(null) ?{self::Class?} null : #t7{self::Class}.{self::Class::next}).{self::Class::field});
   self::throwsInStrong(() → core::int => (let final self::Class? #t8 = c in #t8.{core::Object::==}(null) ?{core::int?} null : #t8{self::Class}.{self::Class::field}).{core::int::unary-}());
   let final self::Class? #t9 = c in #t9.{core::Object::==}(null) ?{core::bool?} null : #t9{self::Class}.{self::Class::next}.{self::Class::[]}(0).{core::int::isEven};
-  self::throwsInStrong(() → core::int => (let final self::Class? #t10 = c in #t10.{core::Object::==}(null) ?{core::int?} null : #t10{self::Class}.{self::Class::next}.{self::Class::[]}(0)).{core::num::+}(2));
+  self::throwsInStrong(() → core::int? => (let final self::Class? #t10 = c in #t10.{core::Object::==}(null) ?{core::int?} null : #t10{self::Class}.{self::Class::next}.{self::Class::[]}(0)).{core::num::+}(2));
   let final self::Class? #t11 = c in #t11.{core::Object::==}(null) ?{core::int?} null : let final self::Class #t12 = #t11{self::Class}.{self::Class::next} in let final core::int #t13 = 0 in let final core::int #t14 = #t12.{self::Class::[]}(#t13).{core::num::+}(1) in let final void #t15 = #t12.{self::Class::[]=}(#t13, #t14) in #t14;
   let final self::Class? #t16 = c in #t16.{core::Object::==}(null) ?{core::int?} null : let final self::Class #t17 = #t16{self::Class}.{self::Class::next} in let final core::int #t18 = 0 in #t17.{self::Class::[]=}(#t18, #t17.{self::Class::[]}(#t18).{core::num::+}(1));
   self::throwsInStrong(() → core::bool => (let final self::Class? #t19 = c in #t19.{core::Object::==}(null) ?{core::int?} null : #t19{self::Class}.{self::Class::next}.{self::Class::[]}(0)).{core::int::isEven});
diff --git a/pkg/front_end/testcases/nnbd/shorting_stop.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/shorting_stop.dart.weak.transformed.expect
index e32de91..0c83c79 100644
--- a/pkg/front_end/testcases/nnbd/shorting_stop.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/shorting_stop.dart.weak.transformed.expect
@@ -54,13 +54,13 @@
 }
 static method test(self::Class? c) → dynamic {
   let final self::Class? #t1 = c in #t1.{core::Object::==}(null) ?{core::int?} null : #t1{self::Class}.{self::Class::next}.{self::Class::field};
-  self::throwsInStrong(() → core::int => (let final self::Class? #t2 = c in #t2.{core::Object::==}(null) ?{core::int?} null : #t2{self::Class}.{self::Class::field}).{core::num::+}(2));
+  self::throwsInStrong(() → core::int? => (let final self::Class? #t2 = c in #t2.{core::Object::==}(null) ?{core::int?} null : #t2{self::Class}.{self::Class::field}).{core::num::+}(2));
   let final self::Class? #t3 = c in #t3.{core::Object::==}(null) ?{core::int?} null : let final core::int #t4 = #t3.{self::Class::field}.{core::num::+}(1) in let final void #t5 = #t3.{self::Class::field} = #t4 in #t4;
   let final self::Class? #t6 = c in #t6.{core::Object::==}(null) ?{core::int?} null : #t6.{self::Class::field} = #t6.{self::Class::field}.{core::num::+}(1);
   self::throwsInStrong(() → core::int => (let final self::Class? #t7 = c in #t7.{core::Object::==}(null) ?{self::Class?} null : #t7{self::Class}.{self::Class::next}).{self::Class::field});
   self::throwsInStrong(() → core::int => (let final self::Class? #t8 = c in #t8.{core::Object::==}(null) ?{core::int?} null : #t8{self::Class}.{self::Class::field}).{core::int::unary-}());
   let final self::Class? #t9 = c in #t9.{core::Object::==}(null) ?{core::bool?} null : #t9{self::Class}.{self::Class::next}.{self::Class::[]}(0).{core::int::isEven};
-  self::throwsInStrong(() → core::int => (let final self::Class? #t10 = c in #t10.{core::Object::==}(null) ?{core::int?} null : #t10{self::Class}.{self::Class::next}.{self::Class::[]}(0)).{core::num::+}(2));
+  self::throwsInStrong(() → core::int? => (let final self::Class? #t10 = c in #t10.{core::Object::==}(null) ?{core::int?} null : #t10{self::Class}.{self::Class::next}.{self::Class::[]}(0)).{core::num::+}(2));
   let final self::Class? #t11 = c in #t11.{core::Object::==}(null) ?{core::int?} null : let final self::Class #t12 = #t11{self::Class}.{self::Class::next} in let final core::int #t13 = 0 in let final core::int #t14 = #t12.{self::Class::[]}(#t13).{core::num::+}(1) in let final void #t15 = #t12.{self::Class::[]=}(#t13, #t14) in #t14;
   let final self::Class? #t16 = c in #t16.{core::Object::==}(null) ?{core::int?} null : let final self::Class #t17 = #t16{self::Class}.{self::Class::next} in let final core::int #t18 = 0 in #t17.{self::Class::[]=}(#t18, #t17.{self::Class::[]}(#t18).{core::num::+}(1));
   self::throwsInStrong(() → core::bool => (let final self::Class? #t19 = c in #t19.{core::Object::==}(null) ?{core::int?} null : #t19{self::Class}.{self::Class::next}.{self::Class::[]}(0)).{core::int::isEven});
diff --git a/pkg/front_end/testcases/text_serialization.status b/pkg/front_end/testcases/text_serialization.status
index 9d55251..2b9be20 100644
--- a/pkg/front_end/testcases/text_serialization.status
+++ b/pkg/front_end/testcases/text_serialization.status
@@ -1221,6 +1221,7 @@
 new_const_insertion/simple: TextSerializationFailure # Was: Pass
 nnbd/assign_type_variable: TextSerializationFailure
 nnbd/assignability: TextSerializationFailure
+nnbd/bounds_checks: TextSerializationFailure
 nnbd/call: TextSerializationFailure
 nnbd/constants: TextSerializationFailure
 nnbd/definite_assignment_and_completion: TextSerializationFailure
@@ -1233,6 +1234,7 @@
 nnbd/infer_from_opt_in: TextSerializationFailure
 nnbd/infer_from_opt_out: TextSerializationFailure
 nnbd/infer_if_null: TextSerializationFailure
+nnbd/infer_in_legacy_from_opted_in: TextSerializationFailure
 nnbd/inheritance_from_opt_in: TypeCheckError
 nnbd/inheritance_from_opt_out: TextSerializationFailure
 nnbd/intersection_types: TextSerializationFailure
@@ -1279,6 +1281,7 @@
 nnbd/nullable_param: TextSerializationFailure
 nnbd/nullable_receiver: TextSerializationFailure
 nnbd/opt_out: TextSerializationFailure
+nnbd/override_checks: TextSerializationFailure
 nnbd/platform_definite_assignment/main: TextSerializationFailure
 nnbd/platform_optional_parameters/main: TextSerializationFailure
 nnbd/potentially_non_nullable_field: TextSerializationFailure
diff --git a/pkg/kernel/lib/naive_type_checker.dart b/pkg/kernel/lib/naive_type_checker.dart
index c7d012a..20ee3b5 100644
--- a/pkg/kernel/lib/naive_type_checker.dart
+++ b/pkg/kernel/lib/naive_type_checker.dart
@@ -112,6 +112,8 @@
     if (subtype is InvalidType || supertype is InvalidType) {
       return true;
     }
+    // TODO(dmitryas): Find a way to tell the weak mode from strong mode to use
+    // [SubtypeCheckMode.withNullabilities] where necessary.
     return environment.isSubtypeOf(
         subtype, supertype, SubtypeCheckMode.ignoringNullabilities);
   }
diff --git a/pkg/kernel/lib/src/bounds_checks.dart b/pkg/kernel/lib/src/bounds_checks.dart
index a92c8d3..03e68b4 100644
--- a/pkg/kernel/lib/src/bounds_checks.dart
+++ b/pkg/kernel/lib/src/bounds_checks.dart
@@ -210,12 +210,29 @@
 
   TypeArgumentIssue(
       this.index, this.argument, this.typeParameter, this.enclosingType);
+
+  int get hashCode {
+    int hash = 0x3fffffff & index;
+    hash = 0x3fffffff & (hash * 31 + (hash ^ argument.hashCode));
+    hash = 0x3fffffff & (hash * 31 + (hash ^ typeParameter.hashCode));
+    hash = 0x3fffffff & (hash * 31 + (hash ^ enclosingType.hashCode));
+    return hash;
+  }
+
+  bool operator ==(dynamic other) {
+    assert(other is TypeArgumentIssue);
+    if (other is! TypeArgumentIssue) return false;
+    return index == other.index &&
+        argument == other.argument &&
+        typeParameter == other.typeParameter &&
+        enclosingType == other.enclosingType;
+  }
 }
 
 // TODO(dmitryas):  Remove [typedefInstantiations] when type arguments passed to
 // typedefs are preserved in the Kernel output.
-List<TypeArgumentIssue> findTypeArgumentIssues(
-    DartType type, TypeEnvironment typeEnvironment,
+List<TypeArgumentIssue> findTypeArgumentIssues(DartType type,
+    TypeEnvironment typeEnvironment, SubtypeCheckMode subtypeCheckMode,
     {bool allowSuperBounded = false}) {
   List<TypeParameter> variables;
   List<DartType> arguments;
@@ -233,7 +250,8 @@
         typeParameters: functionType.typeParameters,
         requiredParameterCount: functionType.requiredParameterCount,
         typedefType: null);
-    typedefRhsResult = findTypeArgumentIssues(cloned, typeEnvironment,
+    typedefRhsResult = findTypeArgumentIssues(
+        cloned, typeEnvironment, subtypeCheckMode,
         allowSuperBounded: true);
     type = functionType.typedefType;
   }
@@ -247,21 +265,25 @@
   } else if (type is FunctionType) {
     List<TypeArgumentIssue> result = <TypeArgumentIssue>[];
     for (TypeParameter parameter in type.typeParameters) {
-      result.addAll(findTypeArgumentIssues(parameter.bound, typeEnvironment,
+      result.addAll(findTypeArgumentIssues(
+              parameter.bound, typeEnvironment, subtypeCheckMode,
               allowSuperBounded: true) ??
           const <TypeArgumentIssue>[]);
     }
     for (DartType formal in type.positionalParameters) {
-      result.addAll(findTypeArgumentIssues(formal, typeEnvironment,
+      result.addAll(findTypeArgumentIssues(
+              formal, typeEnvironment, subtypeCheckMode,
               allowSuperBounded: true) ??
           const <TypeArgumentIssue>[]);
     }
     for (NamedType named in type.namedParameters) {
-      result.addAll(findTypeArgumentIssues(named.type, typeEnvironment,
+      result.addAll(findTypeArgumentIssues(
+              named.type, typeEnvironment, subtypeCheckMode,
               allowSuperBounded: true) ??
           const <TypeArgumentIssue>[]);
     }
-    result.addAll(findTypeArgumentIssues(type.returnType, typeEnvironment,
+    result.addAll(findTypeArgumentIssues(
+            type.returnType, typeEnvironment, subtypeCheckMode,
             allowSuperBounded: true) ??
         const <TypeArgumentIssue>[]);
     return result.isEmpty ? null : result;
@@ -282,16 +304,19 @@
       // Generic function types aren't allowed as type arguments either.
       result ??= <TypeArgumentIssue>[];
       result.add(new TypeArgumentIssue(i, argument, variables[i], type));
-    } else if (!typeEnvironment.isSubtypeOf(
-        argument,
-        substitute(variables[i].bound, substitutionMap),
-        SubtypeCheckMode.ignoringNullabilities)) {
+    } else if (variables[i].bound is! InvalidType &&
+        !typeEnvironment.isSubtypeOf(
+            argument,
+            substitute(variables[i].bound, substitutionMap),
+            subtypeCheckMode)) {
+      // If the bound is InvalidType it's not checked, because an error was
+      // reported already at the time of the creation of InvalidType.
       result ??= <TypeArgumentIssue>[];
       result.add(new TypeArgumentIssue(i, argument, variables[i], type));
     }
 
     List<TypeArgumentIssue> issues = findTypeArgumentIssues(
-        argument, typeEnvironment,
+        argument, typeEnvironment, subtypeCheckMode,
         allowSuperBounded: true);
     if (issues != null) {
       argumentsResult ??= <TypeArgumentIssue>[];
@@ -330,10 +355,8 @@
       result ??= <TypeArgumentIssue>[];
       result.add(
           new TypeArgumentIssue(i, argumentsToReport[i], variables[i], type));
-    } else if (!typeEnvironment.isSubtypeOf(
-        argument,
-        substitute(variables[i].bound, substitutionMap),
-        SubtypeCheckMode.ignoringNullabilities)) {
+    } else if (!typeEnvironment.isSubtypeOf(argument,
+        substitute(variables[i].bound, substitutionMap), subtypeCheckMode)) {
       result ??= <TypeArgumentIssue>[];
       result.add(
           new TypeArgumentIssue(i, argumentsToReport[i], variables[i], type));
@@ -356,6 +379,7 @@
     List<TypeParameter> parameters,
     List<DartType> arguments,
     TypeEnvironment typeEnvironment,
+    SubtypeCheckMode subtypeCheckMode,
     {Map<FunctionType, List<DartType>> typedefInstantiations}) {
   assert(arguments.length == parameters.length);
   List<TypeArgumentIssue> result;
@@ -372,16 +396,14 @@
       // Generic function types aren't allowed as type arguments either.
       result ??= <TypeArgumentIssue>[];
       result.add(new TypeArgumentIssue(i, argument, parameters[i], null));
-    } else if (!typeEnvironment.isSubtypeOf(
-        argument,
-        substitute(parameters[i].bound, substitutionMap),
-        SubtypeCheckMode.ignoringNullabilities)) {
+    } else if (!typeEnvironment.isSubtypeOf(argument,
+        substitute(parameters[i].bound, substitutionMap), subtypeCheckMode)) {
       result ??= <TypeArgumentIssue>[];
       result.add(new TypeArgumentIssue(i, argument, parameters[i], null));
     }
 
     List<TypeArgumentIssue> issues = findTypeArgumentIssues(
-        argument, typeEnvironment,
+        argument, typeEnvironment, subtypeCheckMode,
         allowSuperBounded: true);
     if (issues != null) {
       result ??= <TypeArgumentIssue>[];