[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(