mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 11:58:13 +00:00
[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:
parent
8c717be409
commit
a22e0ac5ea
|
@ -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();
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in a new issue