mirror of
https://github.com/dart-lang/sdk
synced 2024-09-18 21:11:19 +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),
|
StringLiteral(expected),
|
||||||
translator
|
translator
|
||||||
.translateType(translator.coreTypes.stringNonNullableRawType));
|
.translateType(translator.coreTypes.stringNonNullableRawType));
|
||||||
_call(translator.stackTraceCurrent.reference);
|
call(translator.stackTraceCurrent.reference);
|
||||||
_call(translator.throwWasmRefError.reference);
|
call(translator.throwWasmRefError.reference);
|
||||||
b.unreachable();
|
b.unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -436,7 +436,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
||||||
return expectedType;
|
return expectedType;
|
||||||
}
|
}
|
||||||
|
|
||||||
w.ValueType _call(Reference target) {
|
w.ValueType call(Reference target) {
|
||||||
w.BaseFunction targetFunction = translator.functions.getFunction(target);
|
w.BaseFunction targetFunction = translator.functions.getFunction(target);
|
||||||
if (translator.shouldInline(target)) {
|
if (translator.shouldInline(target)) {
|
||||||
List<w.Local> inlinedLocals =
|
List<w.Local> inlinedLocals =
|
||||||
|
@ -492,7 +492,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
||||||
this, TypeParameterType(typeParam, Nullability.nonNullable));
|
this, TypeParameterType(typeParam, Nullability.nonNullable));
|
||||||
}
|
}
|
||||||
_visitArguments(node.arguments, node.targetReference, 1);
|
_visitArguments(node.arguments, node.targetReference, 1);
|
||||||
_call(node.targetReference);
|
call(node.targetReference);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -511,7 +511,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
||||||
}
|
}
|
||||||
_visitArguments(node.arguments, node.targetReference,
|
_visitArguments(node.arguments, node.targetReference,
|
||||||
1 + supertype.typeArguments.length);
|
1 + supertype.typeArguments.length);
|
||||||
_call(node.targetReference);
|
call(node.targetReference);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -910,7 +910,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
||||||
nonNullableType =
|
nonNullableType =
|
||||||
translator.classInfo[translator.stringBaseClass]!.nonNullableType;
|
translator.classInfo[translator.stringBaseClass]!.nonNullableType;
|
||||||
nullableType = nonNullableType.withNullability(true);
|
nullableType = nonNullableType.withNullability(true);
|
||||||
compare = () => _call(translator.stringEquals.reference);
|
compare = () => call(translator.stringEquals.reference);
|
||||||
} else {
|
} else {
|
||||||
// Object switch
|
// Object switch
|
||||||
assert(check<InvalidExpression, InstanceConstant>());
|
assert(check<InvalidExpression, InstanceConstant>());
|
||||||
|
@ -1108,7 +1108,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
||||||
b.ref_as_non_null();
|
b.ref_as_non_null();
|
||||||
}
|
}
|
||||||
_visitArguments(node.arguments, node.targetReference, 1);
|
_visitArguments(node.arguments, node.targetReference, 1);
|
||||||
_call(node.targetReference);
|
call(node.targetReference);
|
||||||
if (expectedType != voidMarker) {
|
if (expectedType != voidMarker) {
|
||||||
b.local_get(temp);
|
b.local_get(temp);
|
||||||
return temp.type;
|
return temp.type;
|
||||||
|
@ -1124,7 +1124,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
||||||
if (intrinsicResult != null) return intrinsicResult;
|
if (intrinsicResult != null) return intrinsicResult;
|
||||||
|
|
||||||
_visitArguments(node.arguments, node.targetReference, 0);
|
_visitArguments(node.arguments, node.targetReference, 0);
|
||||||
return _call(node.targetReference);
|
return call(node.targetReference);
|
||||||
}
|
}
|
||||||
|
|
||||||
Member _lookupSuperTarget(Member interfaceTarget, {required bool setter}) {
|
Member _lookupSuperTarget(Member interfaceTarget, {required bool setter}) {
|
||||||
|
@ -1143,7 +1143,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
||||||
w.ValueType thisType = visitThis(receiverType);
|
w.ValueType thisType = visitThis(receiverType);
|
||||||
translator.convertType(function, thisType, receiverType);
|
translator.convertType(function, thisType, receiverType);
|
||||||
_visitArguments(node.arguments, target, 1);
|
_visitArguments(node.arguments, target, 1);
|
||||||
return _call(target);
|
return call(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -1182,7 +1182,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
||||||
translator.functions.getFunction(singleTarget.reference);
|
translator.functions.getFunction(singleTarget.reference);
|
||||||
wrap(node.receiver, targetFunction.type.inputs.first);
|
wrap(node.receiver, targetFunction.type.inputs.first);
|
||||||
_visitArguments(node.arguments, node.interfaceTargetReference, 1);
|
_visitArguments(node.arguments, node.interfaceTargetReference, 1);
|
||||||
return _call(singleTarget.reference);
|
return call(singleTarget.reference);
|
||||||
}
|
}
|
||||||
return _virtualCall(node, target,
|
return _virtualCall(node, target,
|
||||||
(signature) => wrap(node.receiver, signature.inputs.first), (_) {
|
(signature) => wrap(node.receiver, signature.inputs.first), (_) {
|
||||||
|
@ -1258,7 +1258,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
||||||
if (singleTarget != null) {
|
if (singleTarget != null) {
|
||||||
left();
|
left();
|
||||||
right();
|
right();
|
||||||
_call(singleTarget.reference);
|
call(singleTarget.reference);
|
||||||
} else {
|
} else {
|
||||||
_virtualCall(node, node.interfaceTarget, left, right,
|
_virtualCall(node, node.interfaceTarget, left, right,
|
||||||
getter: false, setter: false);
|
getter: false, setter: false);
|
||||||
|
@ -1307,7 +1307,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
||||||
assert(selector.targetCount <= 1);
|
assert(selector.targetCount <= 1);
|
||||||
if (selector.targetCount == 1) {
|
if (selector.targetCount == 1) {
|
||||||
pushArguments(selector.signature);
|
pushArguments(selector.signature);
|
||||||
return _call(selector.singularTarget!);
|
return call(selector.singularTarget!);
|
||||||
} else {
|
} else {
|
||||||
b.comment("Virtual call of ${selector.name} with no targets"
|
b.comment("Virtual call of ${selector.name} with no targets"
|
||||||
" at ${node.location}");
|
" at ${node.location}");
|
||||||
|
@ -1367,7 +1367,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
||||||
b.i32_const(id);
|
b.i32_const(id);
|
||||||
b.i32_eq();
|
b.i32_eq();
|
||||||
b.if_(selector.signature.inputs, selector.signature.inputs);
|
b.if_(selector.signature.inputs, selector.signature.inputs);
|
||||||
_call(target);
|
call(target);
|
||||||
b.br(block);
|
b.br(block);
|
||||||
b.end();
|
b.end();
|
||||||
implementations.remove(id);
|
implementations.remove(id);
|
||||||
|
@ -1387,7 +1387,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
||||||
b.i32_const(pivotId);
|
b.i32_const(pivotId);
|
||||||
b.i32_lt_u();
|
b.i32_lt_u();
|
||||||
b.if_(selector.signature.inputs, selector.signature.inputs);
|
b.if_(selector.signature.inputs, selector.signature.inputs);
|
||||||
_call(target);
|
call(target);
|
||||||
b.br(block);
|
b.br(block);
|
||||||
b.end();
|
b.end();
|
||||||
for (int id in sorted) {
|
for (int id in sorted) {
|
||||||
|
@ -1398,7 +1398,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
||||||
}
|
}
|
||||||
// Call remaining implementation.
|
// Call remaining implementation.
|
||||||
Reference target = implementations.values.first;
|
Reference target = implementations.values.first;
|
||||||
_call(target);
|
call(target);
|
||||||
b.end();
|
b.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1468,7 +1468,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
||||||
if (target is Field) {
|
if (target is Field) {
|
||||||
return translator.globals.readGlobal(b, target);
|
return translator.globals.readGlobal(b, target);
|
||||||
} else {
|
} 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)));
|
temp = addLocal(translateType(dartTypeOf(node.value)));
|
||||||
b.local_tee(temp);
|
b.local_tee(temp);
|
||||||
}
|
}
|
||||||
_call(target.reference);
|
call(target.reference);
|
||||||
if (preserved) {
|
if (preserved) {
|
||||||
b.local_get(temp!);
|
b.local_get(temp!);
|
||||||
return temp.type;
|
return temp.type;
|
||||||
|
@ -1622,7 +1622,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
||||||
w.BaseFunction targetFunction =
|
w.BaseFunction targetFunction =
|
||||||
translator.functions.getFunction(target.reference);
|
translator.functions.getFunction(target.reference);
|
||||||
wrap(receiver, targetFunction.type.inputs.single);
|
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);
|
b.local_tee(temp);
|
||||||
translator.convertType(function, temp.type, paramType);
|
translator.convertType(function, temp.type, paramType);
|
||||||
}
|
}
|
||||||
_call(target.reference);
|
call(target.reference);
|
||||||
}
|
}
|
||||||
if (preserved) {
|
if (preserved) {
|
||||||
b.local_get(temp!);
|
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
|
// We lower a null check to a br_on_non_null, throwing a [TypeError] in the
|
||||||
// null case.
|
// null case.
|
||||||
b.br_on_non_null(nullCheckBlock);
|
b.br_on_non_null(nullCheckBlock);
|
||||||
_call(translator.stackTraceCurrent.reference);
|
call(translator.stackTraceCurrent.reference);
|
||||||
_call(translator.throwNullCheckError.reference);
|
call(translator.throwNullCheckError.reference);
|
||||||
b.unreachable();
|
b.unreachable();
|
||||||
b.end();
|
b.end();
|
||||||
return nonNullOperandType;
|
return nonNullOperandType;
|
||||||
|
@ -1905,13 +1905,13 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
||||||
StringConcatenation node, w.ValueType expectedType) {
|
StringConcatenation node, w.ValueType expectedType) {
|
||||||
makeList(node.expressions, translator.fixedLengthListClass,
|
makeList(node.expressions, translator.fixedLengthListClass,
|
||||||
InterfaceType(translator.stringBaseClass, Nullability.nonNullable));
|
InterfaceType(translator.stringBaseClass, Nullability.nonNullable));
|
||||||
return _call(translator.stringInterpolate.reference);
|
return call(translator.stringInterpolate.reference);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
w.ValueType visitThrow(Throw node, w.ValueType expectedType) {
|
w.ValueType visitThrow(Throw node, w.ValueType expectedType) {
|
||||||
wrap(node.expression, translator.topInfo.nonNullableType);
|
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
|
// At this point, we have the exception and the current stack trace on the
|
||||||
// stack, so just throw them using the exception tag.
|
// 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.br_if(asCheckBlock);
|
||||||
b.local_get(operand);
|
b.local_get(operand);
|
||||||
types.makeType(this, node.type);
|
types.makeType(this, node.type);
|
||||||
_call(translator.stackTraceCurrent.reference);
|
call(translator.stackTraceCurrent.reference);
|
||||||
_call(translator.throwAsCheckError.reference);
|
call(translator.throwAsCheckError.reference);
|
||||||
b.unreachable();
|
b.unreachable();
|
||||||
b.end();
|
b.end();
|
||||||
b.local_get(operand);
|
b.local_get(operand);
|
||||||
|
|
|
@ -775,7 +775,6 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?> {
|
||||||
@override
|
@override
|
||||||
ConstantInfo? visitTypeLiteralConstant(TypeLiteralConstant constant) {
|
ConstantInfo? visitTypeLiteralConstant(TypeLiteralConstant constant) {
|
||||||
DartType type = constant.type;
|
DartType type = constant.type;
|
||||||
assert(type is! TypeParameterType);
|
|
||||||
|
|
||||||
ClassInfo info = translator.classInfo[types.classForType(type)]!;
|
ClassInfo info = translator.classInfo[types.classForType(type)]!;
|
||||||
translator.functions.allocateClass(info.classId);
|
translator.functions.allocateClass(info.classId);
|
||||||
|
@ -796,6 +795,18 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?> {
|
||||||
} else {
|
} else {
|
||||||
return _makeFunctionType(constant, type, info);
|
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 {
|
} else {
|
||||||
assert(type is VoidType ||
|
assert(type is VoidType ||
|
||||||
type is NeverType ||
|
type is NeverType ||
|
||||||
|
|
|
@ -639,8 +639,10 @@ class Intrinsifier {
|
||||||
codeGen.wrap(stackTrace, stackTraceType);
|
codeGen.wrap(stackTrace, stackTraceType);
|
||||||
b.throw_(translator.exceptionTag);
|
b.throw_(translator.exceptionTag);
|
||||||
return codeGen.voidMarker;
|
return codeGen.voidMarker;
|
||||||
case "_getSubtypeMap":
|
case "_getTypeRulesSupers":
|
||||||
return translator.types.makeSubtypeMap(b);
|
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 interfaceTypeClass;
|
||||||
late final Class functionTypeClass;
|
late final Class functionTypeClass;
|
||||||
late final Class genericFunctionTypeClass;
|
late final Class genericFunctionTypeClass;
|
||||||
|
late final Class interfaceTypeParameterTypeClass;
|
||||||
|
late final Class genericFunctionTypeParameterTypeClass;
|
||||||
late final Class namedParameterClass;
|
late final Class namedParameterClass;
|
||||||
late final Class stackTraceClass;
|
late final Class stackTraceClass;
|
||||||
late final Class ffiCompoundClass;
|
late final Class ffiCompoundClass;
|
||||||
|
@ -119,7 +121,6 @@ class Translator {
|
||||||
late final Procedure setFactory;
|
late final Procedure setFactory;
|
||||||
late final Procedure setAdd;
|
late final Procedure setAdd;
|
||||||
late final Procedure hashImmutableIndexNullable;
|
late final Procedure hashImmutableIndexNullable;
|
||||||
// TODO(joshualitt): Wire up runtime type checks.
|
|
||||||
late final Procedure isSubtype;
|
late final Procedure isSubtype;
|
||||||
late final Map<Class, w.StorageType> builtinTypes;
|
late final Map<Class, w.StorageType> builtinTypes;
|
||||||
late final Map<w.ValueType, Class> boxedClasses;
|
late final Map<w.ValueType, Class> boxedClasses;
|
||||||
|
@ -213,6 +214,9 @@ class Translator {
|
||||||
interfaceTypeClass = lookupCore("_InterfaceType");
|
interfaceTypeClass = lookupCore("_InterfaceType");
|
||||||
functionTypeClass = lookupCore("_FunctionType");
|
functionTypeClass = lookupCore("_FunctionType");
|
||||||
genericFunctionTypeClass = lookupCore("_GenericFunctionType");
|
genericFunctionTypeClass = lookupCore("_GenericFunctionType");
|
||||||
|
interfaceTypeParameterTypeClass = lookupCore("_InterfaceTypeParameterType");
|
||||||
|
genericFunctionTypeParameterTypeClass =
|
||||||
|
lookupCore("_GenericFunctionTypeParameterType");
|
||||||
namedParameterClass = lookupCore("_NamedParameter");
|
namedParameterClass = lookupCore("_NamedParameter");
|
||||||
stackTraceClass = lookupCore("StackTrace");
|
stackTraceClass = lookupCore("StackTrace");
|
||||||
typeUniverseClass = lookupCore("_TypeUniverse");
|
typeUniverseClass = lookupCore("_TypeUniverse");
|
||||||
|
|
|
@ -7,9 +7,27 @@ import 'package:dart2wasm/code_generator.dart';
|
||||||
import 'package:dart2wasm/translator.dart';
|
import 'package:dart2wasm/translator.dart';
|
||||||
|
|
||||||
import 'package:kernel/ast.dart';
|
import 'package:kernel/ast.dart';
|
||||||
|
import 'package:kernel/core_types.dart';
|
||||||
|
|
||||||
import 'package:wasm_builder/wasm_builder.dart' as w;
|
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.
|
/// Helper class for building runtime types.
|
||||||
class Types {
|
class Types {
|
||||||
final Translator translator;
|
final Translator translator;
|
||||||
|
@ -19,6 +37,32 @@ class Types {
|
||||||
late final w.ValueType namedParametersExpectedType = classAndFieldToType(
|
late final w.ValueType namedParametersExpectedType = classAndFieldToType(
|
||||||
translator.functionTypeClass, FieldIndex.functionTypeNamedParameters);
|
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);
|
Types(this.translator);
|
||||||
|
|
||||||
w.ValueType classAndFieldToType(Class cls, int fieldIndex) =>
|
w.ValueType classAndFieldToType(Class cls, int fieldIndex) =>
|
||||||
|
@ -34,46 +78,135 @@ class Types {
|
||||||
InterfaceType get namedParameterType =>
|
InterfaceType get namedParameterType =>
|
||||||
InterfaceType(translator.namedParameterClass, Nullability.nonNullable);
|
InterfaceType(translator.namedParameterClass, Nullability.nonNullable);
|
||||||
|
|
||||||
/// Build a [Map<int, List<int>>] to store subtype information.
|
CoreTypes get coreTypes => translator.coreTypes;
|
||||||
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();
|
|
||||||
|
|
||||||
if (classIds.isEmpty) continue;
|
/// Builds a [Map<int, Map<int, List<DartType>>>] to store subtype
|
||||||
subtypeMap[classInfo.classId] = classIds;
|
/// 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;
|
return subtypeMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds the subtype map and pushes it onto the stack.
|
List<List<int>> _buildTypeRulesSupers() {
|
||||||
w.ValueType makeSubtypeMap(w.Instructions b) {
|
List<List<int>> typeRulesSupers = [];
|
||||||
// Instantiate subtype map constant.
|
for (int i = 0; i < translator.classInfoCollector.nextClassId; i++) {
|
||||||
Map<int, List<int>> subtypeMap = _buildSubtypeMap();
|
List<int>? superclassIds = typeRules[i]?.keys.toList();
|
||||||
ClassInfo immutableMapInfo =
|
if (superclassIds == null) {
|
||||||
translator.classInfo[translator.immutableMapClass]!;
|
typeRulesSupers.add(const []);
|
||||||
w.ValueType expectedType = immutableMapInfo.nonNullableType;
|
} else {
|
||||||
DartType mapAndSetKeyType = translator.coreTypes.intNonNullableRawType;
|
superclassIds.sort();
|
||||||
DartType mapValueType = InterfaceType(translator.immutableListClass,
|
typeRulesSupers.add(superclassIds);
|
||||||
Nullability.nonNullable, [mapAndSetKeyType]);
|
}
|
||||||
List<ConstantMapEntry> entries = subtypeMap.entries.map((mapEntry) {
|
}
|
||||||
return ConstantMapEntry(
|
return typeRulesSupers;
|
||||||
IntConstant(mapEntry.key),
|
}
|
||||||
ListConstant(mapAndSetKeyType,
|
|
||||||
mapEntry.value.map((i) => IntConstant(i)).toList()));
|
List<List<List<DartType>>> _buildTypeRulesSubstitutions() {
|
||||||
}).toList();
|
List<List<List<DartType>>> typeRulesSubstitutions = [];
|
||||||
translator.constants.instantiateConstant(null, b,
|
for (int i = 0; i < translator.classInfoCollector.nextClassId; i++) {
|
||||||
MapConstant(mapAndSetKeyType, mapValueType, entries), expectedType);
|
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;
|
return expectedType;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isGenericFunction(FunctionType type) => type.typeParameters.isNotEmpty;
|
bool isGenericFunction(FunctionType type) => type.typeParameters.isNotEmpty;
|
||||||
|
|
||||||
|
bool isGenericFunctionTypeParameter(TypeParameterType type) =>
|
||||||
|
type.parameter.parent == null;
|
||||||
|
|
||||||
bool _isTypeConstant(DartType type) {
|
bool _isTypeConstant(DartType type) {
|
||||||
return type is DynamicType ||
|
return type is DynamicType ||
|
||||||
type is VoidType ||
|
type is VoidType ||
|
||||||
|
@ -113,6 +246,12 @@ class Types {
|
||||||
} else {
|
} else {
|
||||||
return translator.functionTypeClass;
|
return translator.functionTypeClass;
|
||||||
}
|
}
|
||||||
|
} else if (type is TypeParameterType) {
|
||||||
|
if (isGenericFunctionTypeParameter(type)) {
|
||||||
|
return translator.genericFunctionTypeParameterTypeClass;
|
||||||
|
} else {
|
||||||
|
return translator.interfaceTypeParameterTypeClass;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
throw "Unexpected DartType: $type";
|
throw "Unexpected DartType: $type";
|
||||||
}
|
}
|
||||||
|
@ -251,7 +390,9 @@ class Types {
|
||||||
TreeNode node) {
|
TreeNode node) {
|
||||||
w.Instructions b = codeGen.b;
|
w.Instructions b = codeGen.b;
|
||||||
if (type is! InterfaceType) {
|
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"
|
print("Not implemented: Type test with non-interface type $type"
|
||||||
" at ${node.location}");
|
" at ${node.location}");
|
||||||
b.drop();
|
b.drop();
|
||||||
|
@ -284,7 +425,7 @@ class Types {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
List<Class> concrete = _getConcreteSubtypes(type.classNode).toList();
|
List<Class> concrete = _getConcreteSubtypes(type.classNode).toList();
|
||||||
if (type.classNode == translator.coreTypes.functionClass) {
|
if (type.classNode == coreTypes.functionClass) {
|
||||||
ClassInfo functionInfo = translator.classInfo[translator.functionClass]!;
|
ClassInfo functionInfo = translator.classInfo[translator.functionClass]!;
|
||||||
translator.ref_test(b, functionInfo);
|
translator.ref_test(b, functionInfo);
|
||||||
} else if (concrete.isEmpty) {
|
} else if (concrete.isEmpty) {
|
||||||
|
|
|
@ -38,6 +38,10 @@ class ClassID {
|
||||||
external static int get cidFunctionType;
|
external static int get cidFunctionType;
|
||||||
@pragma("wasm:class-id", "dart.core#_GenericFunctionType")
|
@pragma("wasm:class-id", "dart.core#_GenericFunctionType")
|
||||||
external static int get cidGenericFunctionType;
|
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.
|
// Dummy, only used by VM-specific hash table code.
|
||||||
static final int numPredefinedCids = 1;
|
static final int numPredefinedCids = 1;
|
||||||
|
|
|
@ -26,14 +26,18 @@ abstract class _Type implements Type {
|
||||||
bool get isNull => _testID(ClassID.cidNullType);
|
bool get isNull => _testID(ClassID.cidNullType);
|
||||||
bool get isFutureOr => _testID(ClassID.cidFutureOrType);
|
bool get isFutureOr => _testID(ClassID.cidFutureOrType);
|
||||||
bool get isInterface => _testID(ClassID.cidInterfaceType);
|
bool get isInterface => _testID(ClassID.cidInterfaceType);
|
||||||
|
bool get isInterfaceTypeParameterType =>
|
||||||
|
_testID(ClassID.cidInterfaceTypeParameterType);
|
||||||
bool get isFunction => _testID(ClassID.cidFunctionType);
|
bool get isFunction => _testID(ClassID.cidFunctionType);
|
||||||
bool get isGenericFunction => _testID(ClassID.cidGenericFunctionType);
|
bool get isGenericFunction => _testID(ClassID.cidGenericFunctionType);
|
||||||
|
|
||||||
T as<T>() => unsafeCast<T>(this);
|
T as<T>() => unsafeCast<T>(this);
|
||||||
|
|
||||||
_Type get asNonNullable => isNullable ? _asNonNullable : this;
|
_Type get asNonNullable => isNullable ? _asNonNullable : this;
|
||||||
|
_Type get asNullable => isNullable ? this : _asNullable;
|
||||||
|
|
||||||
_Type get _asNonNullable;
|
_Type get _asNonNullable;
|
||||||
|
_Type get _asNullable;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) => ClassID.getID(this) == ClassID.getID(other);
|
bool operator ==(Object other) => ClassID.getID(this) == ClassID.getID(other);
|
||||||
|
@ -49,6 +53,10 @@ class _NeverType extends _Type {
|
||||||
@override
|
@override
|
||||||
_Type get _asNonNullable => this;
|
_Type get _asNonNullable => this;
|
||||||
|
|
||||||
|
/// Never? normalizes to Null.
|
||||||
|
@override
|
||||||
|
_Type get _asNullable => const _NullType();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'Never';
|
String toString() => 'Never';
|
||||||
}
|
}
|
||||||
|
@ -60,6 +68,9 @@ class _DynamicType extends _Type {
|
||||||
@override
|
@override
|
||||||
_Type get _asNonNullable => throw '`dynamic` type is always nullable.';
|
_Type get _asNonNullable => throw '`dynamic` type is always nullable.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
_Type get _asNullable => this;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'dynamic';
|
String toString() => 'dynamic';
|
||||||
}
|
}
|
||||||
|
@ -71,6 +82,9 @@ class _VoidType extends _Type {
|
||||||
@override
|
@override
|
||||||
_Type get _asNonNullable => throw '`void` type is always nullable.';
|
_Type get _asNonNullable => throw '`void` type is always nullable.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
_Type get _asNullable => this;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'void';
|
String toString() => 'void';
|
||||||
}
|
}
|
||||||
|
@ -82,10 +96,53 @@ class _NullType extends _Type {
|
||||||
@override
|
@override
|
||||||
_Type get _asNonNullable => const _NeverType();
|
_Type get _asNonNullable => const _NeverType();
|
||||||
|
|
||||||
|
@override
|
||||||
|
_Type get _asNullable => this;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'Null';
|
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")
|
@pragma("wasm:entry-point")
|
||||||
class _FutureOrType extends _Type {
|
class _FutureOrType extends _Type {
|
||||||
final _Type typeArgument;
|
final _Type typeArgument;
|
||||||
|
@ -102,6 +159,9 @@ class _FutureOrType extends _Type {
|
||||||
throw '`$this` cannot be non nullable.';
|
throw '`$this` cannot be non nullable.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
_Type get _asNullable => _FutureOrType(true, typeArgument);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object o) {
|
bool operator ==(Object o) {
|
||||||
if (!(super == o)) return false;
|
if (!(super == o)) return false;
|
||||||
|
@ -141,6 +201,9 @@ class _InterfaceType extends _Type {
|
||||||
@override
|
@override
|
||||||
_Type get _asNonNullable => _InterfaceType(classId, false, typeArguments);
|
_Type get _asNonNullable => _InterfaceType(classId, false, typeArguments);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_Type get _asNullable => _InterfaceType(classId, true, typeArguments);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object o) {
|
bool operator ==(Object o) {
|
||||||
if (!(super == o)) return false;
|
if (!(super == o)) return false;
|
||||||
|
@ -234,6 +297,10 @@ class _FunctionType extends _Type {
|
||||||
_Type get _asNonNullable => _FunctionType(returnType, positionalParameters,
|
_Type get _asNonNullable => _FunctionType(returnType, positionalParameters,
|
||||||
requiredParameterCount, namedParameters, false);
|
requiredParameterCount, namedParameters, false);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_Type get _asNullable => _FunctionType(returnType, positionalParameters,
|
||||||
|
requiredParameterCount, namedParameters, true);
|
||||||
|
|
||||||
bool operator ==(Object o) {
|
bool operator ==(Object o) {
|
||||||
if (!(super == o)) return false;
|
if (!(super == o)) return false;
|
||||||
_FunctionType other = unsafeCast<_FunctionType>(o);
|
_FunctionType other = unsafeCast<_FunctionType>(o);
|
||||||
|
@ -304,20 +371,64 @@ class _GenericFunctionType extends _Type {
|
||||||
@override
|
@override
|
||||||
_Type get _asNonNullable => throw 'unimplemented';
|
_Type get _asNonNullable => throw 'unimplemented';
|
||||||
|
|
||||||
|
@override
|
||||||
|
_Type get _asNullable => throw 'unimplemented';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'GenericFunctionType';
|
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 {
|
class _TypeUniverse {
|
||||||
/// 'Map' of classId to range of subclasses.
|
/// 'Map' of classId to the transitive set of super classes it implements.
|
||||||
final Map<int, List<int>> _subtypeMap;
|
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() {
|
factory _TypeUniverse.create() {
|
||||||
return _TypeUniverse._(_getSubtypeMap());
|
return _TypeUniverse._(_getTypeRulesSupers(), _getTypeRulesSubstitutions());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isSpecificInterfaceType(_Type t, int classId) {
|
bool isSpecificInterfaceType(_Type t, int classId) {
|
||||||
|
@ -341,27 +452,55 @@ class _TypeUniverse {
|
||||||
bool isFunctionType(_Type t) =>
|
bool isFunctionType(_Type t) =>
|
||||||
isSpecificInterfaceType(t, ClassID.cidFunction);
|
isSpecificInterfaceType(t, ClassID.cidFunction);
|
||||||
|
|
||||||
bool isInterfaceSubtype(_InterfaceType s, _InterfaceType t) {
|
bool areTypeArgumentsSubtypes(List<_Type> sArgs, _Environment? sEnv,
|
||||||
int sId = s.classId;
|
List<_Type> tArgs, _Environment? tEnv) {
|
||||||
int tId = t.classId;
|
assert(sArgs.length == tArgs.length);
|
||||||
if (sId == tId) {
|
for (int i = 0; i < sArgs.length; i++) {
|
||||||
assert(s.typeArguments.length == t.typeArguments.length);
|
if (!isSubtype(sArgs[i], sEnv, tArgs[i], tEnv)) {
|
||||||
for (int i = 0; i < s.typeArguments.length; i++) {
|
return false;
|
||||||
if (!isSubtype(s.typeArguments[i], t.typeArguments[i])) {
|
|
||||||
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isFunctionSubtype(_FunctionType s, _FunctionType t) {
|
bool isInterfaceSubtype(_InterfaceType s, _Environment? sEnv,
|
||||||
if (!isSubtype(s.returnType, t.returnType)) return false;
|
_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].
|
// Check [s] does not have more required positional arguments than [t].
|
||||||
int sRequiredCount = s.requiredParameterCount;
|
int sRequiredCount = s.requiredParameterCount;
|
||||||
|
@ -385,7 +524,7 @@ class _TypeUniverse {
|
||||||
for (int i = 0; i < tPositionalLength; i++) {
|
for (int i = 0; i < tPositionalLength; i++) {
|
||||||
_Type sParameter = sPositional[i];
|
_Type sParameter = sPositional[i];
|
||||||
_Type tParameter = tPositional[i];
|
_Type tParameter = tPositional[i];
|
||||||
if (!isSubtype(tParameter, sParameter)) {
|
if (!isSubtype(tParameter, tEnv, sParameter, sEnv)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -414,7 +553,8 @@ class _TypeUniverse {
|
||||||
}
|
}
|
||||||
bool tIsRequired = tNamedParameter.isRequired;
|
bool tIsRequired = tNamedParameter.isRequired;
|
||||||
if (sIsRequired && !tIsRequired) return false;
|
if (sIsRequired && !tIsRequired) return false;
|
||||||
if (!isSubtype(tNamedParameter.type, sNamedParameter.type)) {
|
if (!isSubtype(
|
||||||
|
tNamedParameter.type, tEnv, sNamedParameter.type, sEnv)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -428,7 +568,7 @@ class _TypeUniverse {
|
||||||
|
|
||||||
// Subtype check based off of sdk/lib/_internal/js_runtime/lib/rti.dart.
|
// Subtype check based off of sdk/lib/_internal/js_runtime/lib/rti.dart.
|
||||||
// Returns true if [s] is a subtype of [t], false otherwise.
|
// 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:
|
// Reflexivity:
|
||||||
if (identical(s, t)) return true;
|
if (identical(s, t)) return true;
|
||||||
|
|
||||||
|
@ -442,7 +582,11 @@ class _TypeUniverse {
|
||||||
if (isBottomType(s)) return true;
|
if (isBottomType(s)) return true;
|
||||||
|
|
||||||
// Left Type Variable Bound 1:
|
// 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:
|
// Left Null:
|
||||||
// TODO(joshualitt): Combine with 'Right Null', and this can just be:
|
// TODO(joshualitt): Combine with 'Right Null', and this can just be:
|
||||||
|
@ -459,7 +603,7 @@ class _TypeUniverse {
|
||||||
// Left FutureOr:
|
// Left FutureOr:
|
||||||
if (s.isFutureOr) {
|
if (s.isFutureOr) {
|
||||||
_FutureOrType sFutureOr = s.as<_FutureOrType>();
|
_FutureOrType sFutureOr = s.as<_FutureOrType>();
|
||||||
if (!isSubtype(sFutureOr.typeArgument, t)) {
|
if (!isSubtype(sFutureOr.typeArgument, sEnv, t, tEnv)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return _isSubtype(sFutureOr.asFuture, t);
|
return _isSubtype(sFutureOr.asFuture, t);
|
||||||
|
@ -467,7 +611,7 @@ class _TypeUniverse {
|
||||||
|
|
||||||
// Left Nullable:
|
// Left Nullable:
|
||||||
if (s.isNullable) {
|
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
|
// Type Variable Reflexivity 1 is subsumed by Reflexivity and therefore
|
||||||
|
@ -478,15 +622,15 @@ class _TypeUniverse {
|
||||||
// Right FutureOr:
|
// Right FutureOr:
|
||||||
if (t.isFutureOr) {
|
if (t.isFutureOr) {
|
||||||
_FutureOrType tFutureOr = t.as<_FutureOrType>();
|
_FutureOrType tFutureOr = t.as<_FutureOrType>();
|
||||||
if (isSubtype(s, tFutureOr.typeArgument)) {
|
if (isSubtype(s, sEnv, tFutureOr.typeArgument, tEnv)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return isSubtype(s, tFutureOr.asFuture);
|
return isSubtype(s, sEnv, tFutureOr.asFuture, tEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right Nullable:
|
// Right Nullable:
|
||||||
if (t.isNullable) {
|
if (t.isNullable) {
|
||||||
return isSubtype(s, t.asNonNullable);
|
return isSubtype(s, sEnv, t.asNonNullable, tEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Left Promoted Variable does not apply at runtime.
|
// Left Promoted Variable does not apply at runtime.
|
||||||
|
@ -506,13 +650,15 @@ class _TypeUniverse {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s.isFunction && t.isFunction) {
|
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:
|
// Interface Compositionality + Super-Interface:
|
||||||
if (s.isInterface &&
|
if (s.isInterface &&
|
||||||
t.isInterface &&
|
t.isInterface &&
|
||||||
isInterfaceSubtype(s.as<_InterfaceType>(), t.as<_InterfaceType>())) {
|
isInterfaceSubtype(
|
||||||
|
s.as<_InterfaceType>(), sEnv, t.as<_InterfaceType>(), tEnv)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -523,5 +669,6 @@ _TypeUniverse _typeUniverse = _TypeUniverse.create();
|
||||||
|
|
||||||
@pragma("wasm:entry-point")
|
@pragma("wasm:entry-point")
|
||||||
bool _isSubtype(Object? s, _Type t) {
|
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