mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:09:49 +00:00
[dart2wasm] Build type rules for interface type arguments.
Change-Id: I4017d14ff9b32b2a556592f5074cdd3401c7d961 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/243960 Reviewed-by: Aske Simon Christensen <askesc@google.com> Commit-Queue: Joshua Litt <joshualitt@google.com>
This commit is contained in:
parent
d35f5c1ac1
commit
05dfe9047d
|
@ -422,8 +422,8 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
StringLiteral(expected),
|
||||
translator
|
||||
.translateType(translator.coreTypes.stringNonNullableRawType));
|
||||
_call(translator.stackTraceCurrent.reference);
|
||||
_call(translator.throwWasmRefError.reference);
|
||||
call(translator.stackTraceCurrent.reference);
|
||||
call(translator.throwWasmRefError.reference);
|
||||
b.unreachable();
|
||||
}
|
||||
|
||||
|
@ -436,7 +436,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
return expectedType;
|
||||
}
|
||||
|
||||
w.ValueType _call(Reference target) {
|
||||
w.ValueType call(Reference target) {
|
||||
w.BaseFunction targetFunction = translator.functions.getFunction(target);
|
||||
if (translator.shouldInline(target)) {
|
||||
List<w.Local> inlinedLocals =
|
||||
|
@ -492,7 +492,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
this, TypeParameterType(typeParam, Nullability.nonNullable));
|
||||
}
|
||||
_visitArguments(node.arguments, node.targetReference, 1);
|
||||
_call(node.targetReference);
|
||||
call(node.targetReference);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -511,7 +511,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
}
|
||||
_visitArguments(node.arguments, node.targetReference,
|
||||
1 + supertype.typeArguments.length);
|
||||
_call(node.targetReference);
|
||||
call(node.targetReference);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -910,7 +910,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
nonNullableType =
|
||||
translator.classInfo[translator.stringBaseClass]!.nonNullableType;
|
||||
nullableType = nonNullableType.withNullability(true);
|
||||
compare = () => _call(translator.stringEquals.reference);
|
||||
compare = () => call(translator.stringEquals.reference);
|
||||
} else {
|
||||
// Object switch
|
||||
assert(check<InvalidExpression, InstanceConstant>());
|
||||
|
@ -1108,7 +1108,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
b.ref_as_non_null();
|
||||
}
|
||||
_visitArguments(node.arguments, node.targetReference, 1);
|
||||
_call(node.targetReference);
|
||||
call(node.targetReference);
|
||||
if (expectedType != voidMarker) {
|
||||
b.local_get(temp);
|
||||
return temp.type;
|
||||
|
@ -1124,7 +1124,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
if (intrinsicResult != null) return intrinsicResult;
|
||||
|
||||
_visitArguments(node.arguments, node.targetReference, 0);
|
||||
return _call(node.targetReference);
|
||||
return call(node.targetReference);
|
||||
}
|
||||
|
||||
Member _lookupSuperTarget(Member interfaceTarget, {required bool setter}) {
|
||||
|
@ -1143,7 +1143,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
w.ValueType thisType = visitThis(receiverType);
|
||||
translator.convertType(function, thisType, receiverType);
|
||||
_visitArguments(node.arguments, target, 1);
|
||||
return _call(target);
|
||||
return call(target);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -1182,7 +1182,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
translator.functions.getFunction(singleTarget.reference);
|
||||
wrap(node.receiver, targetFunction.type.inputs.first);
|
||||
_visitArguments(node.arguments, node.interfaceTargetReference, 1);
|
||||
return _call(singleTarget.reference);
|
||||
return call(singleTarget.reference);
|
||||
}
|
||||
return _virtualCall(node, target,
|
||||
(signature) => wrap(node.receiver, signature.inputs.first), (_) {
|
||||
|
@ -1258,7 +1258,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
if (singleTarget != null) {
|
||||
left();
|
||||
right();
|
||||
_call(singleTarget.reference);
|
||||
call(singleTarget.reference);
|
||||
} else {
|
||||
_virtualCall(node, node.interfaceTarget, left, right,
|
||||
getter: false, setter: false);
|
||||
|
@ -1307,7 +1307,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
assert(selector.targetCount <= 1);
|
||||
if (selector.targetCount == 1) {
|
||||
pushArguments(selector.signature);
|
||||
return _call(selector.singularTarget!);
|
||||
return call(selector.singularTarget!);
|
||||
} else {
|
||||
b.comment("Virtual call of ${selector.name} with no targets"
|
||||
" at ${node.location}");
|
||||
|
@ -1367,7 +1367,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
b.i32_const(id);
|
||||
b.i32_eq();
|
||||
b.if_(selector.signature.inputs, selector.signature.inputs);
|
||||
_call(target);
|
||||
call(target);
|
||||
b.br(block);
|
||||
b.end();
|
||||
implementations.remove(id);
|
||||
|
@ -1387,7 +1387,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
b.i32_const(pivotId);
|
||||
b.i32_lt_u();
|
||||
b.if_(selector.signature.inputs, selector.signature.inputs);
|
||||
_call(target);
|
||||
call(target);
|
||||
b.br(block);
|
||||
b.end();
|
||||
for (int id in sorted) {
|
||||
|
@ -1398,7 +1398,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
}
|
||||
// Call remaining implementation.
|
||||
Reference target = implementations.values.first;
|
||||
_call(target);
|
||||
call(target);
|
||||
b.end();
|
||||
}
|
||||
|
||||
|
@ -1468,7 +1468,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
if (target is Field) {
|
||||
return translator.globals.readGlobal(b, target);
|
||||
} else {
|
||||
return _call(target.reference);
|
||||
return call(target.reference);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1502,7 +1502,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
temp = addLocal(translateType(dartTypeOf(node.value)));
|
||||
b.local_tee(temp);
|
||||
}
|
||||
_call(target.reference);
|
||||
call(target.reference);
|
||||
if (preserved) {
|
||||
b.local_get(temp!);
|
||||
return temp.type;
|
||||
|
@ -1622,7 +1622,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
w.BaseFunction targetFunction =
|
||||
translator.functions.getFunction(target.reference);
|
||||
wrap(receiver, targetFunction.type.inputs.single);
|
||||
return _call(target.reference);
|
||||
return call(target.reference);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1688,7 +1688,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
b.local_tee(temp);
|
||||
translator.convertType(function, temp.type, paramType);
|
||||
}
|
||||
_call(target.reference);
|
||||
call(target.reference);
|
||||
}
|
||||
if (preserved) {
|
||||
b.local_get(temp!);
|
||||
|
@ -1853,8 +1853,8 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
// We lower a null check to a br_on_non_null, throwing a [TypeError] in the
|
||||
// null case.
|
||||
b.br_on_non_null(nullCheckBlock);
|
||||
_call(translator.stackTraceCurrent.reference);
|
||||
_call(translator.throwNullCheckError.reference);
|
||||
call(translator.stackTraceCurrent.reference);
|
||||
call(translator.throwNullCheckError.reference);
|
||||
b.unreachable();
|
||||
b.end();
|
||||
return nonNullOperandType;
|
||||
|
@ -1905,13 +1905,13 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
StringConcatenation node, w.ValueType expectedType) {
|
||||
makeList(node.expressions, translator.fixedLengthListClass,
|
||||
InterfaceType(translator.stringBaseClass, Nullability.nonNullable));
|
||||
return _call(translator.stringInterpolate.reference);
|
||||
return call(translator.stringInterpolate.reference);
|
||||
}
|
||||
|
||||
@override
|
||||
w.ValueType visitThrow(Throw node, w.ValueType expectedType) {
|
||||
wrap(node.expression, translator.topInfo.nonNullableType);
|
||||
_call(translator.stackTraceCurrent.reference);
|
||||
call(translator.stackTraceCurrent.reference);
|
||||
|
||||
// At this point, we have the exception and the current stack trace on the
|
||||
// stack, so just throw them using the exception tag.
|
||||
|
@ -2101,8 +2101,8 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
b.br_if(asCheckBlock);
|
||||
b.local_get(operand);
|
||||
types.makeType(this, node.type);
|
||||
_call(translator.stackTraceCurrent.reference);
|
||||
_call(translator.throwAsCheckError.reference);
|
||||
call(translator.stackTraceCurrent.reference);
|
||||
call(translator.throwAsCheckError.reference);
|
||||
b.unreachable();
|
||||
b.end();
|
||||
b.local_get(operand);
|
||||
|
|
|
@ -775,7 +775,6 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?> {
|
|||
@override
|
||||
ConstantInfo? visitTypeLiteralConstant(TypeLiteralConstant constant) {
|
||||
DartType type = constant.type;
|
||||
assert(type is! TypeParameterType);
|
||||
|
||||
ClassInfo info = translator.classInfo[types.classForType(type)]!;
|
||||
translator.functions.allocateClass(info.classId);
|
||||
|
@ -796,6 +795,18 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?> {
|
|||
} else {
|
||||
return _makeFunctionType(constant, type, info);
|
||||
}
|
||||
} else if (type is TypeParameterType) {
|
||||
// TODO(joshualitt): Handle generic function types.
|
||||
assert(!types.isGenericFunctionTypeParameter(type));
|
||||
int environmentIndex =
|
||||
types.interfaceTypeEnvironment.lookup(type.parameter);
|
||||
return createConstant(constant, info.nonNullableType, (function, b) {
|
||||
b.i32_const(info.classId);
|
||||
b.i32_const(initialIdentityHash);
|
||||
types.encodeNullability(b, type);
|
||||
b.i32_const(environmentIndex);
|
||||
translator.struct_new(b, info);
|
||||
});
|
||||
} else {
|
||||
assert(type is VoidType ||
|
||||
type is NeverType ||
|
||||
|
|
|
@ -639,8 +639,10 @@ class Intrinsifier {
|
|||
codeGen.wrap(stackTrace, stackTraceType);
|
||||
b.throw_(translator.exceptionTag);
|
||||
return codeGen.voidMarker;
|
||||
case "_getSubtypeMap":
|
||||
return translator.types.makeSubtypeMap(b);
|
||||
case "_getTypeRulesSupers":
|
||||
return translator.types.makeTypeRulesSupers(b);
|
||||
case "_getTypeRulesSubstitutions":
|
||||
return translator.types.makeTypeRulesSubstitutions(b);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -97,6 +97,8 @@ class Translator {
|
|||
late final Class interfaceTypeClass;
|
||||
late final Class functionTypeClass;
|
||||
late final Class genericFunctionTypeClass;
|
||||
late final Class interfaceTypeParameterTypeClass;
|
||||
late final Class genericFunctionTypeParameterTypeClass;
|
||||
late final Class namedParameterClass;
|
||||
late final Class stackTraceClass;
|
||||
late final Class ffiCompoundClass;
|
||||
|
@ -119,7 +121,6 @@ class Translator {
|
|||
late final Procedure setFactory;
|
||||
late final Procedure setAdd;
|
||||
late final Procedure hashImmutableIndexNullable;
|
||||
// TODO(joshualitt): Wire up runtime type checks.
|
||||
late final Procedure isSubtype;
|
||||
late final Map<Class, w.StorageType> builtinTypes;
|
||||
late final Map<w.ValueType, Class> boxedClasses;
|
||||
|
@ -213,6 +214,9 @@ class Translator {
|
|||
interfaceTypeClass = lookupCore("_InterfaceType");
|
||||
functionTypeClass = lookupCore("_FunctionType");
|
||||
genericFunctionTypeClass = lookupCore("_GenericFunctionType");
|
||||
interfaceTypeParameterTypeClass = lookupCore("_InterfaceTypeParameterType");
|
||||
genericFunctionTypeParameterTypeClass =
|
||||
lookupCore("_GenericFunctionTypeParameterType");
|
||||
namedParameterClass = lookupCore("_NamedParameter");
|
||||
stackTraceClass = lookupCore("StackTrace");
|
||||
typeUniverseClass = lookupCore("_TypeUniverse");
|
||||
|
|
|
@ -7,9 +7,27 @@ import 'package:dart2wasm/code_generator.dart';
|
|||
import 'package:dart2wasm/translator.dart';
|
||||
|
||||
import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/core_types.dart';
|
||||
|
||||
import 'package:wasm_builder/wasm_builder.dart' as w;
|
||||
|
||||
class InterfaceTypeEnvironment {
|
||||
final Map<TypeParameter, int> typeOffsets = {};
|
||||
|
||||
void _add(InterfaceType type) {
|
||||
Class cls = type.classNode;
|
||||
if (typeOffsets.containsKey(cls)) {
|
||||
return;
|
||||
}
|
||||
int i = 0;
|
||||
for (TypeParameter typeParameter in cls.typeParameters) {
|
||||
typeOffsets[typeParameter] = i++;
|
||||
}
|
||||
}
|
||||
|
||||
int lookup(TypeParameter typeParameter) => typeOffsets[typeParameter]!;
|
||||
}
|
||||
|
||||
/// Helper class for building runtime types.
|
||||
class Types {
|
||||
final Translator translator;
|
||||
|
@ -19,6 +37,32 @@ class Types {
|
|||
late final w.ValueType namedParametersExpectedType = classAndFieldToType(
|
||||
translator.functionTypeClass, FieldIndex.functionTypeNamedParameters);
|
||||
|
||||
/// A mapping from concrete subclass `classID` to [Map]s of superclass
|
||||
/// `classID` and the necessary substitutions which must be performed to test
|
||||
/// for a valid subtyping relationship.
|
||||
late final Map<int, Map<int, List<DartType>>> typeRules = _buildTypeRules();
|
||||
|
||||
/// We will build the [interfaceTypeEnvironment] when building the
|
||||
/// [typeRules].
|
||||
final InterfaceTypeEnvironment interfaceTypeEnvironment =
|
||||
InterfaceTypeEnvironment();
|
||||
|
||||
/// Because we can't currently support [Map]s in our `TypeUniverse`, we have
|
||||
/// to decompose [typeRules] into two [Map]s based on [List]s.
|
||||
///
|
||||
/// [typeRulesSupers] is a [List] where the index in the list is a subclasses'
|
||||
/// `classID` and the value at that index is a [List] of superclass
|
||||
/// `classID`s.
|
||||
late final List<List<int>> typeRulesSupers = _buildTypeRulesSupers();
|
||||
|
||||
/// [typeRulesSubstitutions] is a [List] where the index in the list is a
|
||||
/// subclasses' `classID` and the value at that index is a [List] indexed by
|
||||
/// the index of the superclasses' `classID` in [typeRulesSuper] and the value
|
||||
/// at that index is a [List] of [DartType]s which must be substituted for the
|
||||
/// subtyping relationship to be valid.
|
||||
late final List<List<List<DartType>>> typeRulesSubstitutions =
|
||||
_buildTypeRulesSubstitutions();
|
||||
|
||||
Types(this.translator);
|
||||
|
||||
w.ValueType classAndFieldToType(Class cls, int fieldIndex) =>
|
||||
|
@ -34,46 +78,135 @@ class Types {
|
|||
InterfaceType get namedParameterType =>
|
||||
InterfaceType(translator.namedParameterClass, Nullability.nonNullable);
|
||||
|
||||
/// Build a [Map<int, List<int>>] to store subtype information.
|
||||
Map<int, List<int>> _buildSubtypeMap() {
|
||||
List<ClassInfo> classes = translator.classes;
|
||||
Map<int, List<int>> subtypeMap = {};
|
||||
for (ClassInfo classInfo in classes) {
|
||||
if (classInfo.cls == null) continue;
|
||||
List<int> classIds = _getConcreteSubtypes(classInfo.cls!)
|
||||
.map((cls) => translator.classInfo[cls]!.classId)
|
||||
.where((classId) => classId != classInfo.classId)
|
||||
.toList();
|
||||
CoreTypes get coreTypes => translator.coreTypes;
|
||||
|
||||
if (classIds.isEmpty) continue;
|
||||
subtypeMap[classInfo.classId] = classIds;
|
||||
/// Builds a [Map<int, Map<int, List<DartType>>>] to store subtype
|
||||
/// information. The first key is the class id of a subtype. This returns a
|
||||
/// map where each key is the class id of a transitively implemented super
|
||||
/// type and each value is a list of the necessary type substitutions required
|
||||
/// for the subtyping relationship to be valid.
|
||||
Map<int, Map<int, List<DartType>>> _buildTypeRules() {
|
||||
List<ClassInfo> classes = translator.classes;
|
||||
Map<int, Map<int, List<DartType>>> subtypeMap = {};
|
||||
for (ClassInfo classInfo in classes) {
|
||||
ClassInfo superclassInfo = classInfo;
|
||||
|
||||
// We don't need type rules for any class without a superclass, or for
|
||||
// classes whose supertype is [Object]. The latter case will be handled
|
||||
// directly in the subtype checking algorithm.
|
||||
if (superclassInfo.cls == null ||
|
||||
superclassInfo.cls == coreTypes.objectClass) continue;
|
||||
Class superclass = superclassInfo.cls!;
|
||||
Iterable<Class> subclasses =
|
||||
_getConcreteSubtypes(superclass).where((cls) => cls != superclass);
|
||||
Iterable<InterfaceType> subtypes = subclasses.map(
|
||||
(Class cls) => cls.getThisType(coreTypes, Nullability.nonNullable));
|
||||
for (InterfaceType subtype in subtypes) {
|
||||
interfaceTypeEnvironment._add(subtype);
|
||||
List<DartType>? typeArguments = translator.hierarchy
|
||||
.getTypeArgumentsAsInstanceOf(subtype, superclass);
|
||||
ClassInfo subclassInfo = translator.classInfo[subtype.classNode]!;
|
||||
Map<int, List<DartType>> substitutionMap =
|
||||
subtypeMap[subclassInfo.classId] ??= {};
|
||||
substitutionMap[superclassInfo.classId] = typeArguments ?? const [];
|
||||
}
|
||||
}
|
||||
return subtypeMap;
|
||||
}
|
||||
|
||||
/// Builds the subtype map and pushes it onto the stack.
|
||||
w.ValueType makeSubtypeMap(w.Instructions b) {
|
||||
// Instantiate subtype map constant.
|
||||
Map<int, List<int>> subtypeMap = _buildSubtypeMap();
|
||||
ClassInfo immutableMapInfo =
|
||||
translator.classInfo[translator.immutableMapClass]!;
|
||||
w.ValueType expectedType = immutableMapInfo.nonNullableType;
|
||||
DartType mapAndSetKeyType = translator.coreTypes.intNonNullableRawType;
|
||||
DartType mapValueType = InterfaceType(translator.immutableListClass,
|
||||
Nullability.nonNullable, [mapAndSetKeyType]);
|
||||
List<ConstantMapEntry> entries = subtypeMap.entries.map((mapEntry) {
|
||||
return ConstantMapEntry(
|
||||
IntConstant(mapEntry.key),
|
||||
ListConstant(mapAndSetKeyType,
|
||||
mapEntry.value.map((i) => IntConstant(i)).toList()));
|
||||
}).toList();
|
||||
translator.constants.instantiateConstant(null, b,
|
||||
MapConstant(mapAndSetKeyType, mapValueType, entries), expectedType);
|
||||
List<List<int>> _buildTypeRulesSupers() {
|
||||
List<List<int>> typeRulesSupers = [];
|
||||
for (int i = 0; i < translator.classInfoCollector.nextClassId; i++) {
|
||||
List<int>? superclassIds = typeRules[i]?.keys.toList();
|
||||
if (superclassIds == null) {
|
||||
typeRulesSupers.add(const []);
|
||||
} else {
|
||||
superclassIds.sort();
|
||||
typeRulesSupers.add(superclassIds);
|
||||
}
|
||||
}
|
||||
return typeRulesSupers;
|
||||
}
|
||||
|
||||
List<List<List<DartType>>> _buildTypeRulesSubstitutions() {
|
||||
List<List<List<DartType>>> typeRulesSubstitutions = [];
|
||||
for (int i = 0; i < translator.classInfoCollector.nextClassId; i++) {
|
||||
List<int> supers = typeRulesSupers[i];
|
||||
typeRulesSubstitutions.add(supers.isEmpty ? const [] : []);
|
||||
for (int j = 0; j < supers.length; j++) {
|
||||
int superId = supers[j];
|
||||
typeRulesSubstitutions.last.add(typeRules[i]![superId]!);
|
||||
}
|
||||
}
|
||||
return typeRulesSubstitutions;
|
||||
}
|
||||
|
||||
/// Builds a map of subclasses to the transitive set of superclasses they
|
||||
/// implement.
|
||||
/// TODO(joshualitt): This implementation is just temporary. Eventually we
|
||||
/// should move to a data structure more closely resembling [typeRules].
|
||||
w.ValueType makeTypeRulesSupers(w.Instructions b) {
|
||||
w.ValueType expectedType =
|
||||
translator.classInfo[translator.immutableListClass]!.nonNullableType;
|
||||
DartType listIntType = InterfaceType(translator.immutableListClass,
|
||||
Nullability.nonNullable, [translator.coreTypes.intNonNullableRawType]);
|
||||
List<ListConstant> listIntConstant = [];
|
||||
for (List<int> supers in typeRulesSupers) {
|
||||
listIntConstant.add(ListConstant(
|
||||
listIntType, supers.map((i) => IntConstant(i)).toList()));
|
||||
}
|
||||
DartType listListIntType = InterfaceType(
|
||||
translator.immutableListClass, Nullability.nonNullable, [listIntType]);
|
||||
translator.constants.instantiateConstant(
|
||||
null, b, ListConstant(listListIntType, listIntConstant), expectedType);
|
||||
return expectedType;
|
||||
}
|
||||
|
||||
/// Similar to the above, but provides the substitutions required for each
|
||||
/// supertype.
|
||||
/// TODO(joshualitt): Like [makeTypeRulesSupers], this is just temporary.
|
||||
w.ValueType makeTypeRulesSubstitutions(w.Instructions b) {
|
||||
w.ValueType expectedType =
|
||||
translator.classInfo[translator.immutableListClass]!.nonNullableType;
|
||||
DartType listTypeType = InterfaceType(
|
||||
translator.immutableListClass,
|
||||
Nullability.nonNullable,
|
||||
[translator.typeClass.getThisType(coreTypes, Nullability.nonNullable)]);
|
||||
DartType listListTypeType = InterfaceType(
|
||||
translator.immutableListClass, Nullability.nonNullable, [listTypeType]);
|
||||
DartType listListListTypeType = InterfaceType(translator.immutableListClass,
|
||||
Nullability.nonNullable, [listListTypeType]);
|
||||
List<ListConstant> substitutionsConstantL0 = [];
|
||||
for (List<List<DartType>> substitutionsL1 in typeRulesSubstitutions) {
|
||||
List<ListConstant> substitutionsConstantL1 = [];
|
||||
for (List<DartType> substitutionsL2 in substitutionsL1) {
|
||||
substitutionsConstantL1.add(ListConstant(
|
||||
listTypeType,
|
||||
substitutionsL2.map((t) {
|
||||
// TODO(joshualitt): implement generic functions
|
||||
if (t is FunctionType && isGenericFunction(t)) {
|
||||
return TypeLiteralConstant(DynamicType());
|
||||
} else {
|
||||
return TypeLiteralConstant(t);
|
||||
}
|
||||
}).toList()));
|
||||
}
|
||||
substitutionsConstantL0
|
||||
.add(ListConstant(listListTypeType, substitutionsConstantL1));
|
||||
}
|
||||
translator.constants.instantiateConstant(
|
||||
null,
|
||||
b,
|
||||
ListConstant(listListListTypeType, substitutionsConstantL0),
|
||||
expectedType);
|
||||
return expectedType;
|
||||
}
|
||||
|
||||
bool isGenericFunction(FunctionType type) => type.typeParameters.isNotEmpty;
|
||||
|
||||
bool isGenericFunctionTypeParameter(TypeParameterType type) =>
|
||||
type.parameter.parent == null;
|
||||
|
||||
bool _isTypeConstant(DartType type) {
|
||||
return type is DynamicType ||
|
||||
type is VoidType ||
|
||||
|
@ -113,6 +246,12 @@ class Types {
|
|||
} else {
|
||||
return translator.functionTypeClass;
|
||||
}
|
||||
} else if (type is TypeParameterType) {
|
||||
if (isGenericFunctionTypeParameter(type)) {
|
||||
return translator.genericFunctionTypeParameterTypeClass;
|
||||
} else {
|
||||
return translator.interfaceTypeParameterTypeClass;
|
||||
}
|
||||
}
|
||||
throw "Unexpected DartType: $type";
|
||||
}
|
||||
|
@ -251,7 +390,9 @@ class Types {
|
|||
TreeNode node) {
|
||||
w.Instructions b = codeGen.b;
|
||||
if (type is! InterfaceType) {
|
||||
// TODO(askesc): Implement type test for remaining types
|
||||
// TODO(joshualitt): We can enable this after fixing `.runtimeType`.
|
||||
// makeType(codeGen, type);
|
||||
// codeGen.call(translator.isSubtype.reference);
|
||||
print("Not implemented: Type test with non-interface type $type"
|
||||
" at ${node.location}");
|
||||
b.drop();
|
||||
|
@ -284,7 +425,7 @@ class Types {
|
|||
}
|
||||
}
|
||||
List<Class> concrete = _getConcreteSubtypes(type.classNode).toList();
|
||||
if (type.classNode == translator.coreTypes.functionClass) {
|
||||
if (type.classNode == coreTypes.functionClass) {
|
||||
ClassInfo functionInfo = translator.classInfo[translator.functionClass]!;
|
||||
translator.ref_test(b, functionInfo);
|
||||
} else if (concrete.isEmpty) {
|
||||
|
|
|
@ -38,6 +38,10 @@ class ClassID {
|
|||
external static int get cidFunctionType;
|
||||
@pragma("wasm:class-id", "dart.core#_GenericFunctionType")
|
||||
external static int get cidGenericFunctionType;
|
||||
@pragma("wasm:class-id", "dart.core#_GenericFunctionTypeParameterType")
|
||||
external static int get cidGenericFunctionTypeParameterType;
|
||||
@pragma("wasm:class-id", "dart.core#_InterfaceTypeParameterType")
|
||||
external static int get cidInterfaceTypeParameterType;
|
||||
|
||||
// Dummy, only used by VM-specific hash table code.
|
||||
static final int numPredefinedCids = 1;
|
||||
|
|
|
@ -26,14 +26,18 @@ abstract class _Type implements Type {
|
|||
bool get isNull => _testID(ClassID.cidNullType);
|
||||
bool get isFutureOr => _testID(ClassID.cidFutureOrType);
|
||||
bool get isInterface => _testID(ClassID.cidInterfaceType);
|
||||
bool get isInterfaceTypeParameterType =>
|
||||
_testID(ClassID.cidInterfaceTypeParameterType);
|
||||
bool get isFunction => _testID(ClassID.cidFunctionType);
|
||||
bool get isGenericFunction => _testID(ClassID.cidGenericFunctionType);
|
||||
|
||||
T as<T>() => unsafeCast<T>(this);
|
||||
|
||||
_Type get asNonNullable => isNullable ? _asNonNullable : this;
|
||||
_Type get asNullable => isNullable ? this : _asNullable;
|
||||
|
||||
_Type get _asNonNullable;
|
||||
_Type get _asNullable;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => ClassID.getID(this) == ClassID.getID(other);
|
||||
|
@ -49,6 +53,10 @@ class _NeverType extends _Type {
|
|||
@override
|
||||
_Type get _asNonNullable => this;
|
||||
|
||||
/// Never? normalizes to Null.
|
||||
@override
|
||||
_Type get _asNullable => const _NullType();
|
||||
|
||||
@override
|
||||
String toString() => 'Never';
|
||||
}
|
||||
|
@ -60,6 +68,9 @@ class _DynamicType extends _Type {
|
|||
@override
|
||||
_Type get _asNonNullable => throw '`dynamic` type is always nullable.';
|
||||
|
||||
@override
|
||||
_Type get _asNullable => this;
|
||||
|
||||
@override
|
||||
String toString() => 'dynamic';
|
||||
}
|
||||
|
@ -71,6 +82,9 @@ class _VoidType extends _Type {
|
|||
@override
|
||||
_Type get _asNonNullable => throw '`void` type is always nullable.';
|
||||
|
||||
@override
|
||||
_Type get _asNullable => this;
|
||||
|
||||
@override
|
||||
String toString() => 'void';
|
||||
}
|
||||
|
@ -82,10 +96,53 @@ class _NullType extends _Type {
|
|||
@override
|
||||
_Type get _asNonNullable => const _NeverType();
|
||||
|
||||
@override
|
||||
_Type get _asNullable => this;
|
||||
|
||||
@override
|
||||
String toString() => 'Null';
|
||||
}
|
||||
|
||||
/// Because Interface type parameters are fundamentally different from Generic
|
||||
/// function type parameters, we are keeping these classes separate for the time
|
||||
/// being.
|
||||
@pragma("wasm:entry-point")
|
||||
class _InterfaceTypeParameterType extends _Type {
|
||||
final int environmentIndex;
|
||||
|
||||
const _InterfaceTypeParameterType(super.isNullable, this.environmentIndex);
|
||||
|
||||
@override
|
||||
_Type get _asNonNullable =>
|
||||
throw 'Type parameter should have been substituted already.';
|
||||
|
||||
@override
|
||||
_Type get _asNullable =>
|
||||
throw 'Type parameter should have been substituted already.';
|
||||
|
||||
@override
|
||||
String toString() => '$environmentIndex';
|
||||
}
|
||||
|
||||
@pragma("wasm:entry-point")
|
||||
class _GenericFunctionTypeParameterType extends _Type {
|
||||
final int environmentIndex;
|
||||
|
||||
const _GenericFunctionTypeParameterType(
|
||||
super.isNullable, this.environmentIndex);
|
||||
|
||||
@override
|
||||
_Type get _asNonNullable =>
|
||||
throw 'Type parameter should have been substituted already..';
|
||||
|
||||
@override
|
||||
_Type get _asNullable =>
|
||||
throw 'Type parameter should have been substituted already.';
|
||||
|
||||
@override
|
||||
String toString() => '$environmentIndex';
|
||||
}
|
||||
|
||||
@pragma("wasm:entry-point")
|
||||
class _FutureOrType extends _Type {
|
||||
final _Type typeArgument;
|
||||
|
@ -102,6 +159,9 @@ class _FutureOrType extends _Type {
|
|||
throw '`$this` cannot be non nullable.';
|
||||
}
|
||||
|
||||
@override
|
||||
_Type get _asNullable => _FutureOrType(true, typeArgument);
|
||||
|
||||
@override
|
||||
bool operator ==(Object o) {
|
||||
if (!(super == o)) return false;
|
||||
|
@ -141,6 +201,9 @@ class _InterfaceType extends _Type {
|
|||
@override
|
||||
_Type get _asNonNullable => _InterfaceType(classId, false, typeArguments);
|
||||
|
||||
@override
|
||||
_Type get _asNullable => _InterfaceType(classId, true, typeArguments);
|
||||
|
||||
@override
|
||||
bool operator ==(Object o) {
|
||||
if (!(super == o)) return false;
|
||||
|
@ -234,6 +297,10 @@ class _FunctionType extends _Type {
|
|||
_Type get _asNonNullable => _FunctionType(returnType, positionalParameters,
|
||||
requiredParameterCount, namedParameters, false);
|
||||
|
||||
@override
|
||||
_Type get _asNullable => _FunctionType(returnType, positionalParameters,
|
||||
requiredParameterCount, namedParameters, true);
|
||||
|
||||
bool operator ==(Object o) {
|
||||
if (!(super == o)) return false;
|
||||
_FunctionType other = unsafeCast<_FunctionType>(o);
|
||||
|
@ -304,20 +371,64 @@ class _GenericFunctionType extends _Type {
|
|||
@override
|
||||
_Type get _asNonNullable => throw 'unimplemented';
|
||||
|
||||
@override
|
||||
_Type get _asNullable => throw 'unimplemented';
|
||||
|
||||
@override
|
||||
String toString() => 'GenericFunctionType';
|
||||
}
|
||||
|
||||
external Map<int, List<int>> _getSubtypeMap();
|
||||
external List<List<int>> _getTypeRulesSupers();
|
||||
external List<List<List<_Type>>> _getTypeRulesSubstitutions();
|
||||
|
||||
class _Environment {
|
||||
List<List<_Type>> scopes = [];
|
||||
|
||||
_Environment();
|
||||
|
||||
factory _Environment.from(List<_Type> initialScope) {
|
||||
final env = _Environment();
|
||||
env.push(initialScope);
|
||||
return env;
|
||||
}
|
||||
|
||||
void push(List<_Type> scope) => scopes.add(scope);
|
||||
|
||||
void pop() => scopes.removeLast();
|
||||
|
||||
_Type _substituteTypeParameter(bool declaredNullable, _Type type) {
|
||||
// If the type parameter is non-nullable, or the substitution type is
|
||||
// nullable, then just return the substitution type. Otherwise, we return
|
||||
// [type] as nullable.
|
||||
// Note: This will throw if the required nullability is impossible to
|
||||
// generate.
|
||||
if (!declaredNullable || type.isNullable) {
|
||||
return type;
|
||||
}
|
||||
return type.asNullable;
|
||||
}
|
||||
|
||||
_Type lookup(_InterfaceTypeParameterType typeParameter) {
|
||||
// Lookup `InterfaceType` parameters in the top environment.
|
||||
// TODO(joshualitt): When we implement generic functions be sure to keep the
|
||||
// environments distinct.
|
||||
return _substituteTypeParameter(
|
||||
typeParameter.isNullable, scopes.last[typeParameter.environmentIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
class _TypeUniverse {
|
||||
/// 'Map' of classId to range of subclasses.
|
||||
final Map<int, List<int>> _subtypeMap;
|
||||
/// 'Map' of classId to the transitive set of super classes it implements.
|
||||
final List<List<int>> typeRulesSupers;
|
||||
|
||||
const _TypeUniverse._(this._subtypeMap);
|
||||
/// 'Map' of classId, and super offset(from [typeRulesSupers]) to a list of
|
||||
/// type substitutions.
|
||||
final List<List<List<_Type>>> typeRulesSubstitutions;
|
||||
|
||||
const _TypeUniverse._(this.typeRulesSupers, this.typeRulesSubstitutions);
|
||||
|
||||
factory _TypeUniverse.create() {
|
||||
return _TypeUniverse._(_getSubtypeMap());
|
||||
return _TypeUniverse._(_getTypeRulesSupers(), _getTypeRulesSubstitutions());
|
||||
}
|
||||
|
||||
bool isSpecificInterfaceType(_Type t, int classId) {
|
||||
|
@ -341,27 +452,55 @@ class _TypeUniverse {
|
|||
bool isFunctionType(_Type t) =>
|
||||
isSpecificInterfaceType(t, ClassID.cidFunction);
|
||||
|
||||
bool isInterfaceSubtype(_InterfaceType s, _InterfaceType t) {
|
||||
int sId = s.classId;
|
||||
int tId = t.classId;
|
||||
if (sId == tId) {
|
||||
assert(s.typeArguments.length == t.typeArguments.length);
|
||||
for (int i = 0; i < s.typeArguments.length; i++) {
|
||||
if (!isSubtype(s.typeArguments[i], t.typeArguments[i])) {
|
||||
return false;
|
||||
}
|
||||
bool areTypeArgumentsSubtypes(List<_Type> sArgs, _Environment? sEnv,
|
||||
List<_Type> tArgs, _Environment? tEnv) {
|
||||
assert(sArgs.length == tArgs.length);
|
||||
for (int i = 0; i < sArgs.length; i++) {
|
||||
if (!isSubtype(sArgs[i], sEnv, tArgs[i], tEnv)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
List<int>? subtypes = _subtypeMap[tId];
|
||||
if (subtypes == null) return false;
|
||||
if (!subtypes.contains(sId)) return false;
|
||||
// TODO(joshualitt): Compare type arguments.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isFunctionSubtype(_FunctionType s, _FunctionType t) {
|
||||
if (!isSubtype(s.returnType, t.returnType)) return false;
|
||||
bool isInterfaceSubtype(_InterfaceType s, _Environment? sEnv,
|
||||
_InterfaceType t, _Environment? tEnv) {
|
||||
int sId = s.classId;
|
||||
int tId = t.classId;
|
||||
|
||||
// If we have the same class, simply compare type arguments.
|
||||
if (sId == tId) {
|
||||
return areTypeArgumentsSubtypes(
|
||||
s.typeArguments, sEnv, t.typeArguments, tEnv);
|
||||
}
|
||||
|
||||
// Otherwise, check if [s] is a subtype of [t], and if it is then compare
|
||||
// [s]'s type substitutions with [t]'s type arguments.
|
||||
List<int> sSupers = typeRulesSupers[sId];
|
||||
if (sSupers.isEmpty) return false;
|
||||
int sSuperIndexOfT = sSupers.indexOf(tId);
|
||||
if (sSuperIndexOfT == -1) return false;
|
||||
assert(sSuperIndexOfT < typeRulesSubstitutions[sId].length);
|
||||
|
||||
List<_Type> substitutions = typeRulesSubstitutions[sId][sSuperIndexOfT];
|
||||
|
||||
// If [sEnv] is null, then create a new environment. Otherwise, we are doing
|
||||
// a recursive type check, so extend the existing environment with [s]'s
|
||||
// type arguments.
|
||||
if (sEnv == null) {
|
||||
sEnv = _Environment.from(s.typeArguments);
|
||||
} else {
|
||||
sEnv.push(s.typeArguments);
|
||||
}
|
||||
bool result =
|
||||
areTypeArgumentsSubtypes(substitutions, sEnv, t.typeArguments, tEnv);
|
||||
sEnv.pop();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool isFunctionSubtype(_FunctionType s, _Environment? sEnv, _FunctionType t,
|
||||
_Environment? tEnv) {
|
||||
if (!isSubtype(s.returnType, sEnv, t.returnType, tEnv)) return false;
|
||||
|
||||
// Check [s] does not have more required positional arguments than [t].
|
||||
int sRequiredCount = s.requiredParameterCount;
|
||||
|
@ -385,7 +524,7 @@ class _TypeUniverse {
|
|||
for (int i = 0; i < tPositionalLength; i++) {
|
||||
_Type sParameter = sPositional[i];
|
||||
_Type tParameter = tPositional[i];
|
||||
if (!isSubtype(tParameter, sParameter)) {
|
||||
if (!isSubtype(tParameter, tEnv, sParameter, sEnv)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -414,7 +553,8 @@ class _TypeUniverse {
|
|||
}
|
||||
bool tIsRequired = tNamedParameter.isRequired;
|
||||
if (sIsRequired && !tIsRequired) return false;
|
||||
if (!isSubtype(tNamedParameter.type, sNamedParameter.type)) {
|
||||
if (!isSubtype(
|
||||
tNamedParameter.type, tEnv, sNamedParameter.type, sEnv)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
@ -428,7 +568,7 @@ class _TypeUniverse {
|
|||
|
||||
// Subtype check based off of sdk/lib/_internal/js_runtime/lib/rti.dart.
|
||||
// Returns true if [s] is a subtype of [t], false otherwise.
|
||||
bool isSubtype(_Type s, _Type t) {
|
||||
bool isSubtype(_Type s, _Environment? sEnv, _Type t, _Environment? tEnv) {
|
||||
// Reflexivity:
|
||||
if (identical(s, t)) return true;
|
||||
|
||||
|
@ -442,7 +582,11 @@ class _TypeUniverse {
|
|||
if (isBottomType(s)) return true;
|
||||
|
||||
// Left Type Variable Bound 1:
|
||||
// TODO(joshualitt): Implement.
|
||||
// TODO(joshualitt): Implement for generic function type parameters.
|
||||
if (s.isInterfaceTypeParameterType) {
|
||||
return isSubtype(
|
||||
sEnv!.lookup(s.as<_InterfaceTypeParameterType>()), sEnv, t, tEnv);
|
||||
}
|
||||
|
||||
// Left Null:
|
||||
// TODO(joshualitt): Combine with 'Right Null', and this can just be:
|
||||
|
@ -459,7 +603,7 @@ class _TypeUniverse {
|
|||
// Left FutureOr:
|
||||
if (s.isFutureOr) {
|
||||
_FutureOrType sFutureOr = s.as<_FutureOrType>();
|
||||
if (!isSubtype(sFutureOr.typeArgument, t)) {
|
||||
if (!isSubtype(sFutureOr.typeArgument, sEnv, t, tEnv)) {
|
||||
return false;
|
||||
}
|
||||
return _isSubtype(sFutureOr.asFuture, t);
|
||||
|
@ -467,7 +611,7 @@ class _TypeUniverse {
|
|||
|
||||
// Left Nullable:
|
||||
if (s.isNullable) {
|
||||
return t.isNullable && isSubtype(s.asNonNullable, t);
|
||||
return t.isNullable && isSubtype(s.asNonNullable, sEnv, t, tEnv);
|
||||
}
|
||||
|
||||
// Type Variable Reflexivity 1 is subsumed by Reflexivity and therefore
|
||||
|
@ -478,15 +622,15 @@ class _TypeUniverse {
|
|||
// Right FutureOr:
|
||||
if (t.isFutureOr) {
|
||||
_FutureOrType tFutureOr = t.as<_FutureOrType>();
|
||||
if (isSubtype(s, tFutureOr.typeArgument)) {
|
||||
if (isSubtype(s, sEnv, tFutureOr.typeArgument, tEnv)) {
|
||||
return true;
|
||||
}
|
||||
return isSubtype(s, tFutureOr.asFuture);
|
||||
return isSubtype(s, sEnv, tFutureOr.asFuture, tEnv);
|
||||
}
|
||||
|
||||
// Right Nullable:
|
||||
if (t.isNullable) {
|
||||
return isSubtype(s, t.asNonNullable);
|
||||
return isSubtype(s, sEnv, t.asNonNullable, tEnv);
|
||||
}
|
||||
|
||||
// Left Promoted Variable does not apply at runtime.
|
||||
|
@ -506,13 +650,15 @@ class _TypeUniverse {
|
|||
}
|
||||
|
||||
if (s.isFunction && t.isFunction) {
|
||||
return isFunctionSubtype(s.as<_FunctionType>(), t.as<_FunctionType>());
|
||||
return isFunctionSubtype(
|
||||
s.as<_FunctionType>(), sEnv, t.as<_FunctionType>(), tEnv);
|
||||
}
|
||||
|
||||
// Interface Compositionality + Super-Interface:
|
||||
if (s.isInterface &&
|
||||
t.isInterface &&
|
||||
isInterfaceSubtype(s.as<_InterfaceType>(), t.as<_InterfaceType>())) {
|
||||
isInterfaceSubtype(
|
||||
s.as<_InterfaceType>(), sEnv, t.as<_InterfaceType>(), tEnv)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -523,5 +669,6 @@ _TypeUniverse _typeUniverse = _TypeUniverse.create();
|
|||
|
||||
@pragma("wasm:entry-point")
|
||||
bool _isSubtype(Object? s, _Type t) {
|
||||
return _typeUniverse.isSubtype(unsafeCast<_Type>(s.runtimeType), t);
|
||||
return _typeUniverse.isSubtype(
|
||||
unsafeCast<_Type>(s.runtimeType), null, t, null);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue