[dart2wasm] Compute correct type for generic function instantiation

Change-Id: I3811e37002ed6ae458600513bff3ac29482a6af3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/271104
Reviewed-by: Joshua Litt <joshualitt@google.com>
This commit is contained in:
Aske Simon Christensen 2022-12-08 13:54:57 +00:00
parent 8c717be409
commit a22e0ac5ea
3 changed files with 113 additions and 21 deletions

View file

@ -150,6 +150,11 @@ class ClosureLayouter extends RecursiveVisitor {
late final w.StructType closureBaseStruct = _makeClosureStruct("#ClosureBase",
vtableBaseStruct, translator.classInfo[translator.functionClass]!.struct);
late final w.RefType typeType =
translator.classInfo[translator.typeClass]!.nonNullableType;
late final w.RefType functionTypeType =
translator.classInfo[translator.functionTypeClass]!.nonNullableType;
w.StructType _makeClosureStruct(
String name, w.StructType vtableStruct, w.StructType superType) {
// A closure contains:
@ -165,15 +170,13 @@ class ClosureLayouter extends RecursiveVisitor {
w.FieldType(w.RefType.data(nullable: false)),
w.FieldType(w.RefType.def(vtableStruct, nullable: false),
mutable: false),
w.FieldType(typeType, mutable: false)
w.FieldType(functionTypeType, mutable: false)
],
superType: superType);
}
w.Module get m => translator.m;
w.ValueType get topType => translator.topInfo.nullableType;
w.ValueType get typeType =>
translator.classInfo[translator.typeClass]!.nonNullableType;
ClosureLayouter(this.translator)
: procedureAttributeMetadata =
@ -477,25 +480,55 @@ class ClosureLayouter extends RecursiveVisitor {
w.Local preciseClosure = instantiationFunction.addLocal(genericClosureType);
w.Instructions b = instantiationFunction.body;
// Parameters to the instantiation function
final w.Local closureParam = instantiationFunction.locals[0];
w.Local typeParam(int i) => instantiationFunction.locals[1 + i];
// Header for the closure struct
b.i32_const(info.classId);
b.i32_const(initialIdentityHash);
// Context for the instantiated closure, containing the original closure and
// the type arguments
b.local_get(instantiationFunction.locals[0]);
b.local_get(closureParam);
b.ref_cast(genericClosureStruct);
b.local_tee(preciseClosure);
for (int i = 0; i < typeCount; i++) {
b.local_get(instantiationFunction.locals[1 + i]);
b.local_get(typeParam(i));
}
b.struct_new(contextStruct);
// The rest of the closure struct
b.global_get(vtable);
// TODO(askesc): Substitute type arguments into type
// Construct the type of the instantiated closure, which is the type of the
// original closure with the type arguments of the instantiation substituted
// for its type parameters.
// Type of the original closure
b.local_get(preciseClosure);
b.struct_get(genericClosureStruct, FieldIndex.closureRuntimeType);
// Put type arguments into a `List<_Type>`.
ClassInfo listInfo = translator.classInfo[translator.fixedLengthListClass]!;
translator.functions.allocateClass(listInfo.classId);
Constant typeConstant = TypeLiteralConstant(
InterfaceType(translator.typeClass, Nullability.nonNullable));
b.i32_const(listInfo.classId);
b.i32_const(initialIdentityHash);
translator.constants
.instantiateConstant(instantiationFunction, b, typeConstant, typeType);
b.i64_const(typeCount);
for (int i = 0; i < typeCount; i++) {
b.local_get(typeParam(i));
}
b.array_new_fixed(translator.listArrayType, typeCount);
b.struct_new(listInfo.struct);
// Call [_TypeUniverse.substituteFunctionTypeArgument].
b.call(translator.functions
.getFunction(translator.substituteFunctionTypeArgument.reference));
// Finally, allocate closure struct.
b.struct_new(instantiatedRepresentation.closureStruct);
b.end();

View file

@ -186,6 +186,8 @@ mixin KernelNodes {
index.getProcedure("dart:core", "_Type", "get:asNullable");
late final Procedure createNormalizedFutureOrType = index.getProcedure(
"dart:core", "_TypeUniverse", "createNormalizedFutureOrType");
late final Procedure substituteFunctionTypeArgument = index.getProcedure(
"dart:core", "_TypeUniverse", "substituteFunctionTypeArgument");
// dart:core dynamic invocation helper procedures
late final Procedure getNamedParameter =

View file

@ -521,7 +521,7 @@ class _TypeUniverse {
isSpecificInterfaceType(t, ClassID.cidFunction) ||
isSpecificInterfaceType(t, ClassID.cid_Function);
static _Type substituteTypeParameter(
static _Type substituteInterfaceTypeParameter(
_InterfaceTypeParameterType typeParameter, List<_Type> substitutions) {
// If the type parameter is non-nullable, or the substitution type is
// nullable, then just return the substitution type. Otherwise, we return
@ -533,44 +533,99 @@ class _TypeUniverse {
return substitution;
}
static _Type substituteTypeArgument(_Type type, List<_Type> substitutions) {
static _Type substituteFunctionTypeParameter(
_FunctionTypeParameterType typeParameter,
List<_Type> substitutions,
_FunctionType? rootFunction) {
if (rootFunction != null &&
typeParameter.index >= rootFunction.typeParameterOffset) {
_Type substitution =
substitutions[typeParameter.index - rootFunction.typeParameterOffset];
if (typeParameter.isDeclaredNullable) return substitution.asNullable;
return substitution;
} else {
return typeParameter;
}
}
@pragma("wasm:entry-point")
static _FunctionType substituteFunctionTypeArgument(
_FunctionType functionType, List<_Type> substitutions) {
return substituteTypeArgument(functionType, substitutions, functionType)
.as<_FunctionType>();
}
/// Substitute the type parameters of an interface type or function type.
///
/// For interface types, [rootFunction] is always `null`.
///
/// For function types, [rootFunction] is the function whose type parameters
/// are being substituted, or `null` when inside a nested function type that
/// is guaranteed not to contain any type parameter types that are to be
/// substituted.
static _Type substituteTypeArgument(
_Type type, List<_Type> substitutions, _FunctionType? rootFunction) {
if (type.isNever || type.isDynamic || type.isVoid || type.isNull) {
return type;
} else if (type.isFutureOr) {
return createNormalizedFutureOrType(
type.isDeclaredNullable,
substituteTypeArgument(
type.as<_FutureOrType>().typeArgument, substitutions));
substituteTypeArgument(type.as<_FutureOrType>().typeArgument,
substitutions, rootFunction));
} else if (type.isInterface) {
_InterfaceType interfaceType = type.as<_InterfaceType>();
return _InterfaceType(
interfaceType.classId,
interfaceType.isDeclaredNullable,
interfaceType.typeArguments
.map((type) => substituteTypeArgument(type, substitutions))
.map((type) =>
substituteTypeArgument(type, substitutions, rootFunction))
.toList());
} else if (type.isInterfaceTypeParameterType) {
return substituteTypeParameter(
assert(rootFunction == null);
return substituteInterfaceTypeParameter(
type.as<_InterfaceTypeParameterType>(), substitutions);
} else if (type.isFunction) {
_FunctionType functionType = type.as<_FunctionType>();
bool isRoot = identical(type, rootFunction);
if (!isRoot &&
rootFunction != null &&
functionType.typeParameterOffset +
functionType.typeParameterBounds.length >
rootFunction.typeParameterOffset) {
// The type parameter index range of this nested generic function type
// overlaps that of the root function, which means it does not contain
// any function type parameter types referring to the root function.
// Pass `null` as the `rootFunction` to avoid mis-interpreting enclosed
// type parameter types as referring to the root function.
rootFunction = null;
}
return _FunctionType(
functionType.typeParameterOffset,
functionType.typeParameterBounds
.map((type) => substituteTypeArgument(type, substitutions))
.toList(),
substituteTypeArgument(functionType.returnType, substitutions),
isRoot
? const []
: functionType.typeParameterBounds
.map((type) =>
substituteTypeArgument(type, substitutions, rootFunction))
.toList(),
substituteTypeArgument(
functionType.returnType, substitutions, rootFunction),
functionType.positionalParameters
.map((type) => substituteTypeArgument(type, substitutions))
.map((type) =>
substituteTypeArgument(type, substitutions, rootFunction))
.toList(),
functionType.requiredParameterCount,
functionType.namedParameters
.map((named) => _NamedParameter(
named.name,
substituteTypeArgument(named.type, substitutions),
substituteTypeArgument(
named.type, substitutions, rootFunction),
named.isRequired))
.toList(),
functionType.isDeclaredNullable);
} else if (type.isFunctionTypeParameterType) {
return substituteFunctionTypeParameter(
type.as<_FunctionTypeParameterType>(), substitutions, rootFunction);
} else {
throw 'Type argument substitution not supported for $type';
}
@ -578,8 +633,10 @@ class _TypeUniverse {
static List<_Type> substituteTypeArguments(
List<_Type> types, List<_Type> substitutions) =>
List<_Type>.generate(types.length,
(int index) => substituteTypeArgument(types[index], substitutions),
List<_Type>.generate(
types.length,
(int index) =>
substituteTypeArgument(types[index], substitutions, null),
growable: false);
static _Type createNormalizedFutureOrType(