[dart2wasm] Normalize FutureOrType.

Attempts to incorporate some of the missing cases in:
https://github.com/dart-lang/language/blob/master/resources/type-system/normalization.md

Particularly NORM(FutureOr<T>).

I'm also trying to better handle one particular case in NORM(T?) for FutureOr and nullability. Particularly:
  if S is FutureOr<R> and R is nullable then S

Change-Id: I4671f1f4992b9114b055df127d8807d168cc0a66
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/261788
Reviewed-by: Aske Simon Christensen <askesc@google.com>
Commit-Queue: Joshua Litt <joshualitt@google.com>
diff --git a/pkg/dart2wasm/lib/constants.dart b/pkg/dart2wasm/lib/constants.dart
index a2f327e..4ba4963 100644
--- a/pkg/dart2wasm/lib/constants.dart
+++ b/pkg/dart2wasm/lib/constants.dart
@@ -780,7 +780,7 @@
 
   @override
   ConstantInfo? visitTypeLiteralConstant(TypeLiteralConstant constant) {
-    DartType type = constant.type;
+    DartType type = types.normalize(constant.type);
 
     ClassInfo info = translator.classInfo[types.classForType(type)]!;
     translator.functions.allocateClass(info.classId);
diff --git a/pkg/dart2wasm/lib/types.dart b/pkg/dart2wasm/lib/types.dart
index 20da36b..3eb2fed 100644
--- a/pkg/dart2wasm/lib/types.dart
+++ b/pkg/dart2wasm/lib/types.dart
@@ -114,7 +114,9 @@
       for (InterfaceType subtype in subtypes) {
         interfaceTypeEnvironment._add(subtype);
         List<DartType>? typeArguments = translator.hierarchy
-            .getTypeArgumentsAsInstanceOf(subtype, superclass);
+            .getTypeArgumentsAsInstanceOf(subtype, superclass)
+            ?.map(normalize)
+            .toList();
         ClassInfo subclassInfo = translator.classInfo[subtype.classNode]!;
         Map<int, List<DartType>> substitutionMap =
             subtypeMap[subclassInfo.classId] ??= {};
@@ -311,6 +313,57 @@
     _makeTypeList(codeGen, type.typeArguments);
   }
 
+  DartType normalizeFutureOrType(FutureOrType type) {
+    final s = normalize(type.typeArgument);
+
+    // `coreTypes.isTope` and `coreTypes.isObject` take into account the
+    // normalization rules of `futureOr`.
+    if (coreTypes.isTop(type) || coreTypes.isObject(type)) {
+      return s;
+    } else if (s is NeverType) {
+      return InterfaceType(coreTypes.futureClass, Nullability.nonNullable,
+          const [const NeverType.nonNullable()]);
+    } else if (s is NullType) {
+      return InterfaceType(coreTypes.futureClass, Nullability.nullable,
+          const [const NullType()]);
+    }
+
+    // The type is normalized, and remains a `FutureOr` so now we normalize its
+    // nullability.
+    final declaredNullability = s.nullability == Nullability.nullable
+        ? Nullability.nonNullable
+        : type.declaredNullability;
+    return FutureOrType(s, declaredNullability);
+  }
+
+  /// Normalizes a Dart type. Many rules are already applied for us, but some we
+  /// have to apply manually, particularly to [FutureOr].
+  DartType normalize(DartType type) {
+    if (type is InterfaceType) {
+      return InterfaceType(type.classNode, type.nullability,
+          type.typeArguments.map(normalize).toList());
+    } else if (type is FunctionType) {
+      return FunctionType(type.positionalParameters.map(normalize).toList(),
+          normalize(type.returnType), type.nullability,
+          namedParameters: type.namedParameters
+              .map((namedType) => NamedType(
+                  namedType.name, normalize(namedType.type),
+                  isRequired: namedType.isRequired))
+              .toList(),
+          typeParameters: type.typeParameters
+              .map((typeParameter) => TypeParameter(
+                  typeParameter.name,
+                  normalize(typeParameter.bound),
+                  normalize(typeParameter.defaultType)))
+              .toList(),
+          requiredParameterCount: type.requiredParameterCount);
+    } else if (type is FutureOrType) {
+      return normalizeFutureOrType(type);
+    } else {
+      return type;
+    }
+  }
+
   void _makeFutureOrType(CodeGenerator codeGen, FutureOrType type) {
     w.Instructions b = codeGen.b;
     w.DefinedFunction function = codeGen.function;
@@ -382,6 +435,8 @@
   /// TODO(joshualitt): Refactor this logic to remove the dependency on
   /// CodeGenerator.
   w.ValueType makeType(CodeGenerator codeGen, DartType type) {
+    // Always ensure type is normalized before making a type.
+    type = normalize(type);
     w.Instructions b = codeGen.b;
     if (_isTypeConstant(type)) {
       translator.constants.instantiateConstant(