[dart2js] Ensure live/checked type visitors consider instantiations of
type variables.
Change-Id: I2fb404e5d544382fd4387268fd8c5765475abe65
Fixes: #48277
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/241148
Reviewed-by: Stephen Adams <sra@google.com>
Commit-Queue: Mayank Patke <fishythefish@google.com>
diff --git a/pkg/compiler/lib/src/js_backend/runtime_types.dart b/pkg/compiler/lib/src/js_backend/runtime_types.dart
index 8f63902..8b351dc 100644
--- a/pkg/compiler/lib/src/js_backend/runtime_types.dart
+++ b/pkg/compiler/lib/src/js_backend/runtime_types.dart
@@ -546,6 +546,20 @@
Set<ClassEntity> typeLiterals = {};
Set<ClassEntity> typeArguments = {};
+ Iterable<DartType> instantiateTypeVariable(TypeVariableEntity variable) {
+ Entity declaration = variable.typeDeclaration;
+ int index = variable.index;
+ if (declaration is ClassEntity) {
+ return typeVariableTests
+ .classInstantiationsOf(declaration)
+ .map((InterfaceType interface) => interface.typeArguments[index]);
+ } else {
+ return typeVariableTests.instantiationsOf(declaration).map(
+ (GenericInstantiation instantiation) =>
+ instantiation.typeArguments[index]);
+ }
+ }
+
// The [liveTypeVisitor] is used to register class use in the type of
// instantiated objects like `new T` and the function types of
// tear offs and closures.
@@ -559,27 +573,28 @@
// new A<B Function(C)>();
//
// makes A and B live but C tested.
- TypeVisitor liveTypeVisitor =
- TypeVisitor(onClass: (ClassEntity cls, {TypeVisitorState state}) {
- ClassUse classUse = classUseMap.putIfAbsent(cls, () => ClassUse());
- switch (state) {
- case TypeVisitorState.covariantTypeArgument:
- classUse.typeArgument = true;
- typeArguments.add(cls);
- break;
- case TypeVisitorState.contravariantTypeArgument:
- classUse.typeArgument = true;
- classUse.checkedTypeArgument = true;
- typeArguments.add(cls);
- break;
- case TypeVisitorState.typeLiteral:
- classUse.typeLiteral = true;
- typeLiterals.add(cls);
- break;
- case TypeVisitorState.direct:
- break;
- }
- });
+ TypeVisitor liveTypeVisitor = TypeVisitor(
+ onClass: (ClassEntity cls, {TypeVisitorState state}) {
+ ClassUse classUse = classUseMap.putIfAbsent(cls, () => ClassUse());
+ switch (state) {
+ case TypeVisitorState.covariantTypeArgument:
+ classUse.typeArgument = true;
+ typeArguments.add(cls);
+ break;
+ case TypeVisitorState.contravariantTypeArgument:
+ classUse.typeArgument = true;
+ classUse.checkedTypeArgument = true;
+ typeArguments.add(cls);
+ break;
+ case TypeVisitorState.typeLiteral:
+ classUse.typeLiteral = true;
+ typeLiterals.add(cls);
+ break;
+ case TypeVisitorState.direct:
+ break;
+ }
+ },
+ instantiateTypeVariable: instantiateTypeVariable);
// The [testedTypeVisitor] is used to register class use in type tests like
// `o is T` and `o as T` (both implicit and explicit).
@@ -593,26 +608,27 @@
// o is A<B Function(C)>;
//
// makes A and B tested but C live.
- TypeVisitor testedTypeVisitor =
- TypeVisitor(onClass: (ClassEntity cls, {TypeVisitorState state}) {
- ClassUse classUse = classUseMap.putIfAbsent(cls, () => ClassUse());
- switch (state) {
- case TypeVisitorState.covariantTypeArgument:
- classUse.typeArgument = true;
- classUse.checkedTypeArgument = true;
- typeArguments.add(cls);
- break;
- case TypeVisitorState.contravariantTypeArgument:
- classUse.typeArgument = true;
- typeArguments.add(cls);
- break;
- case TypeVisitorState.typeLiteral:
- break;
- case TypeVisitorState.direct:
- classUse.checkedInstance = true;
- break;
- }
- });
+ TypeVisitor testedTypeVisitor = TypeVisitor(
+ onClass: (ClassEntity cls, {TypeVisitorState state}) {
+ ClassUse classUse = classUseMap.putIfAbsent(cls, () => ClassUse());
+ switch (state) {
+ case TypeVisitorState.covariantTypeArgument:
+ classUse.typeArgument = true;
+ classUse.checkedTypeArgument = true;
+ typeArguments.add(cls);
+ break;
+ case TypeVisitorState.contravariantTypeArgument:
+ classUse.typeArgument = true;
+ typeArguments.add(cls);
+ break;
+ case TypeVisitorState.typeLiteral:
+ break;
+ case TypeVisitorState.direct:
+ classUse.checkedInstance = true;
+ break;
+ }
+ },
+ instantiateTypeVariable: instantiateTypeVariable);
codegenWorld.instantiatedClasses.forEach((ClassEntity cls) {
ClassUse classUse = classUseMap.putIfAbsent(cls, () => ClassUse());
@@ -875,15 +891,14 @@
}
class TypeVisitor extends DartTypeVisitor<void, TypeVisitorState> {
+ final Set<TypeVariableType> _visitedTypeVariables = {};
final Set<FunctionTypeVariable> _visitedFunctionTypeVariables = {};
final void Function(ClassEntity entity, {TypeVisitorState state}) onClass;
- final void Function(TypeVariableEntity entity, {TypeVisitorState state})
- onTypeVariable;
- final void Function(FunctionType type, {TypeVisitorState state})
- onFunctionType;
+ final Iterable<DartType> Function(TypeVariableEntity entity)
+ instantiateTypeVariable;
- TypeVisitor({this.onClass, this.onTypeVariable, this.onFunctionType});
+ TypeVisitor({this.onClass, this.instantiateTypeVariable});
void visitType(DartType type, TypeVisitorState state) =>
type.accept(this, state);
@@ -938,8 +953,10 @@
@override
void visitTypeVariableType(TypeVariableType type, TypeVisitorState state) {
- if (onTypeVariable != null) {
- onTypeVariable(type.element, state: state);
+ if (_visitedTypeVariables.add(type) && instantiateTypeVariable != null) {
+ for (DartType instantiation in instantiateTypeVariable(type.element)) {
+ visitType(instantiation, state);
+ }
}
}
@@ -952,9 +969,6 @@
@override
void visitFunctionType(FunctionType type, TypeVisitorState state) {
- if (onFunctionType != null) {
- onFunctionType(type, state: state);
- }
// Visit all nested types as type arguments; these types are not runtime
// instances but runtime type representations.
visitType(type.returnType, covariantArgument(state));
diff --git a/pkg/compiler/lib/src/js_backend/runtime_types_resolution.dart b/pkg/compiler/lib/src/js_backend/runtime_types_resolution.dart
index 365a3f8..ce85047 100644
--- a/pkg/compiler/lib/src/js_backend/runtime_types_resolution.dart
+++ b/pkg/compiler/lib/src/js_backend/runtime_types_resolution.dart
@@ -258,6 +258,12 @@
_instantiationMap.forEach(f);
}
+ Set<GenericInstantiation> instantiationsOf(Entity target) =>
+ _instantiationMap[target] ?? const {};
+
+ Set<InterfaceType> classInstantiationsOf(ClassEntity cls) =>
+ _classInstantiationMap[cls] ?? const {};
+
ClassNode _getClassNode(ClassEntity cls) {
return _classes.putIfAbsent(cls, () => ClassNode(cls));
}
@@ -554,12 +560,12 @@
TypeVariableEntity entity = variable.element;
Entity declaration = entity.typeDeclaration;
if (declaration is ClassEntity) {
- _classInstantiationMap[declaration]?.forEach((InterfaceType type) {
+ classInstantiationsOf(declaration).forEach((InterfaceType type) {
_addImplicitCheck(type.typeArguments[entity.index]);
});
} else {
- _instantiationMap[declaration]
- ?.forEach((GenericInstantiation instantiation) {
+ instantiationsOf(declaration)
+ .forEach((GenericInstantiation instantiation) {
_addImplicitCheck(instantiation.typeArguments[entity.index]);
});
_world.forEachStaticTypeArgument(
@@ -598,7 +604,7 @@
// one of its type arguments in an is-check and add the arguments to the
// set of is-checks.
for (ClassEntity base in _classHierarchy.allSubtypesOf(cls)) {
- _classInstantiationMap[base]?.forEach((InterfaceType subtype) {
+ classInstantiationsOf(base).forEach((InterfaceType subtype) {
InterfaceType instance = _dartTypes.asInstanceOf(subtype, cls);
assert(instance != null);
_addImplicitChecks(instance.typeArguments);
diff --git a/pkg/compiler/test/rti/emission/future_or_generic2.dart b/pkg/compiler/test/rti/emission/future_or_generic2.dart
index 1bb791c..dae38e57 100644
--- a/pkg/compiler/test/rti/emission/future_or_generic2.dart
+++ b/pkg/compiler/test/rti/emission/future_or_generic2.dart
@@ -18,10 +18,10 @@
/*class: B:checkedInstance,checkedTypeArgument,checks=[],instance,typeArgument*/
class B<T> {}
-/*class: C:checkedInstance,typeArgument*/
+/*class: C:checkedInstance,checkedTypeArgument,typeArgument*/
class C {}
-/*class: D:checkedInstance,typeArgument*/
+/*class: D:checkedInstance,checkedTypeArgument,typeArgument*/
class D {}
main() {