[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>
This commit is contained in:
Joshua Litt 2022-10-04 21:55:32 +00:00 committed by Commit Queue
parent a2e7decb7e
commit c3573379c4
2 changed files with 57 additions and 2 deletions

View file

@ -780,7 +780,7 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?> {
@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);

View file

@ -114,7 +114,9 @@ class Types {
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 @@ class Types {
_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 @@ class Types {
/// 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(