Remove effectively empty type holders
+ correctly join with path with less information.
Change-Id: I44921f5b1d1613934d5321d87840db689b3b4b0b
Reviewed-on: https://dart-review.googlesource.com/c/88573
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/ir/static_type.dart b/pkg/compiler/lib/src/ir/static_type.dart
index 4003b5b..47129b6 100644
--- a/pkg/compiler/lib/src/ir/static_type.dart
+++ b/pkg/compiler/lib/src/ir/static_type.dart
@@ -34,6 +34,7 @@
/// the readily compute static types of subexpressions.
abstract class StaticTypeVisitor extends StaticTypeBase {
Map<ir.Expression, ir.DartType> _cache = {};
+ Map<ir.Expression, TypeMap> typeMapsForTesting;
StaticTypeVisitor(ir.TypeEnvironment typeEnvironment)
: super(typeEnvironment);
@@ -51,6 +52,7 @@
VariableScopeModel get variableScopeModel;
bool completes(ir.DartType type) => type != const ir.BottomType();
+
Set<ir.VariableDeclaration> _currentVariables;
Set<ir.VariableDeclaration> _invalidatedVariables =
new Set<ir.VariableDeclaration>();
@@ -511,6 +513,9 @@
@override
ir.DartType visitVariableGet(ir.VariableGet node) {
+ if (typeMapsForTesting != null) {
+ typeMapsForTesting[node] = typeMap;
+ }
ir.DartType promotedType = typeMap.typeOf(node, typeEnvironment);
assert(
node.promotedType == null ||
@@ -1246,6 +1251,23 @@
equalSets(falseTypes, other.falseTypes);
}
+ void _getText(
+ StringBuffer sb, String Function(Iterable<ir.DartType>) typesToText) {
+ sb.write('{');
+ String comma = '';
+ if (trueTypes != null) {
+ sb.write('true:');
+ sb.write(typesToText(trueTypes));
+ comma = ',';
+ }
+ if (falseTypes != null) {
+ sb.write(comma);
+ sb.write('false:');
+ sb.write(typesToText(falseTypes));
+ }
+ sb.write('}');
+ }
+
String toString() {
StringBuffer sb = new StringBuffer();
sb.write('TypeHolder(');
@@ -1318,51 +1340,6 @@
newTypeHolders.add(TypeHolder(declaredType, trueTypes, falseTypes));
return changed;
}
- /*bool addTypeHolder(TypeHolder typeHolder) {
- bool changed = true;
- TypeHolder newTypeHolder;
- if (typeHolder != null) {
- if (isTrue) {
- if (typeHolder.trueTypes == null) {
- newTypeHolder = new TypeHolder(declaredType,
- new Set<ir.DartType>()..add(type), typeHolder.falseTypes);
- } else if (typeHolder.trueTypes.contains(type)) {
- changed = false;
- newTypeHolder = typeHolder;
- } else {
- newTypeHolder = new TypeHolder(
- declaredType,
- new Set<ir.DartType>.from(typeHolder.trueTypes)..add(type),
- typeHolder.falseTypes);
- }
- } else {
- if (typeHolder.falseTypes == null) {
- newTypeHolder = new TypeHolder(declaredType, typeHolder.trueTypes,
- new Set<ir.DartType>()..add(type));
- } else if (typeHolder.falseTypes.contains(type)) {
- changed = false;
- newTypeHolder = typeHolder;
- } else {
- newTypeHolder = new TypeHolder(declaredType, typeHolder.trueTypes,
- new Set<ir.DartType>.from(typeHolder.falseTypes)..add(type));
- }
- }
- } else {
- if (isTrue) {
- newTypeHolder = new TypeHolder(
- declaredType, new Set<ir.DartType>()..add(type), null);
- } else {
- newTypeHolder = new TypeHolder(
- declaredType, null, new Set<ir.DartType>()..add(type));
- }
- }
- // TODO(johnniwinther): Check validity; if the true types are
- // contradicting, for instance if the local is known to be and instance
- // of types `int` and `String` simultaneously, then we could flag code
- // as dead code.
- newTypeHolders.add(newTypeHolder);
- return changed;
- }*/
bool changed = false;
if (typeHolders.isEmpty) {
@@ -1387,16 +1364,101 @@
/// Returns the [TargetInfo] that describes that the local is either of [this]
/// or the [other] type.
+ ///
+ /// Returns `null` if the join is empty.
TargetInfo join(TargetInfo other) {
+ if (other == null) return null;
if (identical(this, other)) return this;
- Set<TypeHolder> newTypeHolders = new Set<TypeHolder>.from(typeHolders);
- newTypeHolders.addAll(other.typeHolders);
- Set<ir.DartType> newDeclarationsOfInterest =
- new Set<ir.DartType>.from(typesOfInterest);
- newDeclarationsOfInterest.addAll(other.typesOfInterest);
- return new TargetInfo(
- declaredType, newTypeHolders, newDeclarationsOfInterest);
+ Set<TypeHolder> newTypeHolders = new Set<TypeHolder>();
+ Set<ir.DartType> newTypesOfInterest = new Set<ir.DartType>();
+
+ /// Adds the [typeHolders] to [newTypeHolders] for types in
+ /// [otherTypesOfInterest] while removing the information
+ /// invalidated by [otherTrueTypes] and [otherFalseTypes].
+ void addTypeHolders(
+ Iterable<TypeHolder> typeHolders,
+ Set<ir.DartType> otherTrueTypes,
+ Set<ir.DartType> otherFalseTypes,
+ Iterable<ir.DartType> otherTypesOfInterest) {
+ for (TypeHolder typeHolder in typeHolders) {
+ Set<ir.DartType> newTrueTypes;
+ if (typeHolder.trueTypes != null) {
+ newTrueTypes = new Set<ir.DartType>.from(typeHolder.trueTypes);
+
+ /// Only types in [otherTypesOfInterest] has information from all
+ /// paths.
+ newTrueTypes.retainAll(otherTypesOfInterest);
+
+ /// Remove types that are known to be false on other paths; these
+ /// would amount to knowing that a variable is or is not of some
+ /// type.
+ newTrueTypes.removeAll(otherFalseTypes);
+ if (newTrueTypes.isEmpty) {
+ newTrueTypes = null;
+ } else {
+ newTypesOfInterest.addAll(newTrueTypes);
+ }
+ }
+ Set<ir.DartType> newFalseTypes;
+ if (typeHolder.falseTypes != null) {
+ newFalseTypes = new Set<ir.DartType>.from(typeHolder.falseTypes);
+
+ /// Only types in [otherTypesOfInterest] has information from all
+ /// paths.
+ newFalseTypes.retainAll(otherTypesOfInterest);
+
+ /// Remove types that are known to be true on other paths; these
+ /// would amount to knowing that a variable is or is not of some
+ /// type.
+ newFalseTypes.removeAll(otherTrueTypes);
+ if (newFalseTypes.isEmpty) {
+ newFalseTypes = null;
+ } else {
+ newTypesOfInterest.addAll(newFalseTypes);
+ }
+ }
+ if (newTrueTypes != null || newFalseTypes != null) {
+ // Only include type holders with information.
+ newTypeHolders
+ .add(new TypeHolder(declaredType, newTrueTypes, newFalseTypes));
+ }
+ }
+ }
+
+ Set<ir.DartType> thisTrueTypes = new Set<ir.DartType>();
+ Set<ir.DartType> thisFalseTypes = new Set<ir.DartType>();
+ for (TypeHolder typeHolder in typeHolders) {
+ if (typeHolder.trueTypes != null) {
+ thisTrueTypes.addAll(typeHolder.trueTypes);
+ }
+ if (typeHolder.falseTypes != null) {
+ thisFalseTypes.addAll(typeHolder.falseTypes);
+ }
+ }
+
+ Set<ir.DartType> otherTrueTypes = new Set<ir.DartType>();
+ Set<ir.DartType> otherFalseTypes = new Set<ir.DartType>();
+ for (TypeHolder typeHolder in other.typeHolders) {
+ if (typeHolder.trueTypes != null) {
+ otherTrueTypes.addAll(typeHolder.trueTypes);
+ }
+ if (typeHolder.falseTypes != null) {
+ otherFalseTypes.addAll(typeHolder.falseTypes);
+ }
+ }
+
+ addTypeHolders(this.typeHolders, otherTrueTypes, otherFalseTypes,
+ other.typesOfInterest);
+ addTypeHolders(
+ other.typeHolders, thisTrueTypes, thisFalseTypes, this.typesOfInterest);
+
+ if (newTypeHolders.isEmpty) {
+ assert(newTypesOfInterest.isEmpty);
+ return null;
+ }
+
+ return new TargetInfo(declaredType, newTypeHolders, newTypesOfInterest);
}
/// Computes a single type that soundly represents the promoted type of the
@@ -1429,6 +1491,20 @@
return candidate;
}
+ void _getText(
+ StringBuffer sb, String Function(Iterable<ir.DartType>) typesToText) {
+ sb.write('[');
+ String comma = '';
+ for (TypeHolder typeHolder in typeHolders) {
+ sb.write(comma);
+ typeHolder._getText(sb, typesToText);
+ comma = ',';
+ }
+ sb.write('|');
+ sb.write(typesToText(typesOfInterest));
+ sb.write(']');
+ }
+
String toString() {
StringBuffer sb = new StringBuffer();
sb.write('TargetInfo(');
@@ -1489,22 +1565,11 @@
Map<ir.VariableDeclaration, TargetInfo> newInfoMap = {};
bool changed = false;
_targetInfoMap.forEach((ir.VariableDeclaration variable, TargetInfo info) {
- TargetInfo otherInfo = other._targetInfoMap[variable];
- if (otherInfo != null) {
- TargetInfo result = info.join(otherInfo);
- changed |= !identical(info, result);
+ TargetInfo result = info.join(other._targetInfoMap[variable]);
+ changed |= !identical(info, result);
+ if (result != null) {
+ // Add only non-empty information.
newInfoMap[variable] = result;
- } else {
- changed = true;
- newInfoMap[variable] = info;
- }
- });
- other._targetInfoMap
- .forEach((ir.VariableDeclaration variable, TargetInfo otherInfo) {
- TargetInfo info = _targetInfoMap[variable];
- if (info == null) {
- changed = true;
- newInfoMap[variable] = otherInfo;
}
});
return changed ? new TypeMap(newInfoMap) : this;
@@ -1571,6 +1636,19 @@
return type ?? node.promotedType ?? node.variable.type;
}
+ String getText(String Function(Iterable<ir.DartType>) typesToText) {
+ StringBuffer sb = new StringBuffer();
+ sb.write('{');
+ String comma = '';
+ _targetInfoMap.forEach((ir.VariableDeclaration variable, TargetInfo info) {
+ sb.write('${comma}${variable.name}:');
+ info._getText(sb, typesToText);
+ comma = ',';
+ });
+ sb.write('}');
+ return sb.toString();
+ }
+
String toString() {
StringBuffer sb = new StringBuffer();
sb.write('TypeMap(');
diff --git a/pkg/compiler/lib/src/kernel/element_map_impl.dart b/pkg/compiler/lib/src/kernel/element_map_impl.dart
index 695412d..6c0a91c 100644
--- a/pkg/compiler/lib/src/kernel/element_map_impl.dart
+++ b/pkg/compiler/lib/src/kernel/element_map_impl.dart
@@ -30,6 +30,7 @@
import '../frontend_strategy.dart';
import '../ir/debug.dart';
import '../ir/element_map.dart';
+import '../ir/static_type.dart';
import '../ir/scope.dart';
import '../ir/types.dart';
import '../ir/visitors.dart';
@@ -109,6 +110,8 @@
BehaviorBuilder _nativeBehaviorBuilder;
FrontendStrategy _frontendStrategy;
+ Map<KMember, Map<ir.Expression, TypeMap>> typeMapsForTesting;
+
KernelToElementMapImpl(this.reporter, Environment environment,
this._frontendStrategy, this.options) {
_elementEnvironment = new KernelElementEnvironment(this);
@@ -1344,6 +1347,10 @@
ir.Member node = memberData.node;
KernelImpactBuilder builder = new KernelImpactBuilder(
this, member, reporter, options, variableScopeModel, annotations);
+ if (retainDataForTesting) {
+ typeMapsForTesting ??= {};
+ typeMapsForTesting[member] = builder.typeMapsForTesting = {};
+ }
node.accept(builder);
memberData.staticTypes = builder.cachedStaticTypes;
return builder.impactBuilder;
@@ -1361,6 +1368,10 @@
return staticTypes;
}
+ Map<ir.Expression, TypeMap> getTypeMapsForTesting(KMember member) {
+ return typeMapsForTesting[member];
+ }
+
/// Returns the kernel [ir.Procedure] node for the [method].
ir.Procedure _lookupProcedure(KFunction method) {
return members.getData(method).node;
diff --git a/tests/compiler/dart2js/helpers/ir_types.dart b/tests/compiler/dart2js/helpers/ir_types.dart
new file mode 100644
index 0000000..280ae12
--- /dev/null
+++ b/tests/compiler/dart2js/helpers/ir_types.dart
@@ -0,0 +1,137 @@
+// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:kernel/ast.dart' as ir;
+import 'package:kernel/class_hierarchy.dart' as ir;
+import 'package:kernel/core_types.dart' as ir;
+import 'package:kernel/type_algebra.dart' as ir;
+import 'package:kernel/type_environment.dart' as ir;
+
+class TypeTextVisitor implements ir.DartTypeVisitor1<void, StringBuffer> {
+ const TypeTextVisitor();
+
+ @override
+ void defaultDartType(ir.DartType node, StringBuffer sb) {
+ throw new UnsupportedError("Unhandled type $node (${node.runtimeType}).");
+ }
+
+ void writeType(ir.DartType type, StringBuffer sb) {
+ type.accept1(this, sb);
+ }
+
+ void _writeTypes(List<ir.DartType> types, StringBuffer sb) {
+ String comma = '';
+ for (ir.DartType type in types) {
+ sb.write(comma);
+ writeType(type, sb);
+ comma = ',';
+ }
+ }
+
+ void _writeTypeArguments(List<ir.DartType> typeArguments, StringBuffer sb) {
+ if (typeArguments.isNotEmpty) {
+ sb.write('<');
+ _writeTypes(typeArguments, sb);
+ sb.write('>');
+ }
+ }
+
+ @override
+ void visitTypedefType(ir.TypedefType node, StringBuffer sb) {
+ sb.write(node.typedefNode.name);
+ _writeTypeArguments(node.typeArguments, sb);
+ }
+
+ @override
+ void visitTypeParameterType(ir.TypeParameterType node, StringBuffer sb) {
+ sb.write(node.parameter.name);
+ }
+
+ @override
+ void visitFunctionType(ir.FunctionType node, StringBuffer sb) {
+ writeType(node.returnType, sb);
+ sb.write(' Function');
+ if (node.typeParameters.isNotEmpty) {
+ sb.write('<');
+ String comma = '';
+ for (ir.TypeParameter typeParameter in node.typeParameters) {
+ sb.write(comma);
+ sb.write(typeParameter.name);
+ if (typeParameter is! ir.DynamicType) {
+ sb.write(' extends ');
+ writeType(typeParameter.bound, sb);
+ }
+ comma = ',';
+ }
+ sb.write('>');
+ }
+ sb.write('(');
+ _writeTypes(
+ node.positionalParameters.take(node.requiredParameterCount), sb);
+ if (node.requiredParameterCount < node.positionalParameters.length) {
+ if (node.requiredParameterCount > 0) {
+ sb.write(',');
+ }
+ _writeTypes(
+ node.positionalParameters.skip(node.requiredParameterCount), sb);
+ }
+ if (node.namedParameters.isNotEmpty) {
+ if (node.positionalParameters.isNotEmpty) {
+ sb.write(',');
+ }
+ String comma = '';
+ for (ir.NamedType namedType in node.namedParameters) {
+ sb.write(comma);
+ sb.write(namedType.name);
+ sb.write(': ');
+ writeType(namedType.type, sb);
+ comma = ',';
+ }
+ }
+ sb.write(')');
+ }
+
+ @override
+ void visitInterfaceType(ir.InterfaceType node, StringBuffer sb) {
+ sb.write(node.classNode.name);
+ _writeTypeArguments(node.typeArguments, sb);
+ }
+
+ @override
+ void visitBottomType(ir.BottomType node, StringBuffer sb) {
+ sb.write('<bottom>');
+ }
+
+ @override
+ void visitVoidType(ir.VoidType node, StringBuffer sb) {
+ sb.write('void');
+ }
+
+ @override
+ void visitDynamicType(ir.DynamicType node, StringBuffer sb) {
+ sb.write('dynamic');
+ }
+
+ @override
+ void visitInvalidType(ir.InvalidType node, StringBuffer sb) {
+ sb.write('<invalid>');
+ }
+}
+
+String typeToText(ir.DartType type) {
+ StringBuffer sb = new StringBuffer();
+ const TypeTextVisitor().writeType(type, sb);
+ return sb.toString();
+}
+
+String typesToText(Iterable<ir.DartType> types) {
+ StringBuffer sb = new StringBuffer();
+ String comma = '';
+ for (ir.DartType type in types) {
+ sb.write(comma);
+ const TypeTextVisitor().writeType(type, sb);
+ comma = ',';
+ }
+ return sb.toString();
+}
diff --git a/tests/compiler/dart2js/static_type/static_type_test.dart b/tests/compiler/dart2js/static_type/static_type_test.dart
index 0216e66..070d0f6 100644
--- a/tests/compiler/dart2js/static_type/static_type_test.dart
+++ b/tests/compiler/dart2js/static_type/static_type_test.dart
@@ -17,6 +17,7 @@
import 'package:kernel/type_environment.dart' as ir;
import '../equivalence/id_equivalence.dart';
import '../equivalence/id_equivalence_helper.dart';
+import '../helpers/ir_types.dart';
main(List<String> args) {
asyncTest(() async {
@@ -26,14 +27,6 @@
});
}
-class Tags {
- static const String typeUse = 'type';
- static const String staticUse = 'static';
- static const String dynamicUse = 'dynamic';
- static const String constantUse = 'constant';
- static const String runtimeTypeUse = 'runtimeType';
-}
-
class StaticTypeDataComputer extends DataComputer<String> {
ir.TypeEnvironment _typeEnvironment;
@@ -70,117 +63,6 @@
DataInterpreter<String> get dataValidator => const StringDataInterpreter();
}
-class TypeTextVisitor implements ir.DartTypeVisitor1<void, StringBuffer> {
- const TypeTextVisitor();
-
- @override
- void defaultDartType(ir.DartType node, StringBuffer sb) {
- throw new UnsupportedError("Unhandled type $node (${node.runtimeType}).");
- }
-
- void writeType(ir.DartType type, StringBuffer sb) {
- type.accept1(this, sb);
- }
-
- void _writeTypes(List<ir.DartType> types, StringBuffer sb) {
- String comma = '';
- for (ir.DartType type in types) {
- sb.write(comma);
- writeType(type, sb);
- comma = ',';
- }
- }
-
- void _writeTypeArguments(List<ir.DartType> typeArguments, StringBuffer sb) {
- if (typeArguments.isNotEmpty) {
- sb.write('<');
- _writeTypes(typeArguments, sb);
- sb.write('>');
- }
- }
-
- @override
- void visitTypedefType(ir.TypedefType node, StringBuffer sb) {
- sb.write(node.typedefNode.name);
- _writeTypeArguments(node.typeArguments, sb);
- }
-
- @override
- void visitTypeParameterType(ir.TypeParameterType node, StringBuffer sb) {
- sb.write(node.parameter.name);
- }
-
- @override
- void visitFunctionType(ir.FunctionType node, StringBuffer sb) {
- writeType(node.returnType, sb);
- sb.write(' Function');
- if (node.typeParameters.isNotEmpty) {
- sb.write('<');
- String comma = '';
- for (ir.TypeParameter typeParameter in node.typeParameters) {
- sb.write(comma);
- sb.write(typeParameter.name);
- if (typeParameter is! ir.DynamicType) {
- sb.write(' extends ');
- writeType(typeParameter.bound, sb);
- }
- comma = ',';
- }
- sb.write('>');
- }
- sb.write('(');
- _writeTypes(
- node.positionalParameters.take(node.requiredParameterCount), sb);
- if (node.requiredParameterCount < node.positionalParameters.length) {
- if (node.requiredParameterCount > 0) {
- sb.write(',');
- }
- _writeTypes(
- node.positionalParameters.skip(node.requiredParameterCount), sb);
- }
- if (node.namedParameters.isNotEmpty) {
- if (node.positionalParameters.isNotEmpty) {
- sb.write(',');
- }
- String comma = '';
- for (ir.NamedType namedType in node.namedParameters) {
- sb.write(comma);
- sb.write(namedType.name);
- sb.write(': ');
- writeType(namedType.type, sb);
- comma = ',';
- }
- }
- sb.write(')');
- }
-
- @override
- void visitInterfaceType(ir.InterfaceType node, StringBuffer sb) {
- sb.write(node.classNode.name);
- _writeTypeArguments(node.typeArguments, sb);
- }
-
- @override
- void visitBottomType(ir.BottomType node, StringBuffer sb) {
- sb.write('<bottom>');
- }
-
- @override
- void visitVoidType(ir.VoidType node, StringBuffer sb) {
- sb.write('void');
- }
-
- @override
- void visitDynamicType(ir.DynamicType node, StringBuffer sb) {
- sb.write('dynamic');
- }
-
- @override
- void visitInvalidType(ir.InvalidType node, StringBuffer sb) {
- sb.write('<invalid>');
- }
-}
-
/// IR visitor for computing inference data for a member.
class StaticTypeIrComputer extends IrDataExtractor<String> {
final CachedStaticType staticTypeCache;
@@ -189,12 +71,6 @@
Map<Id, ActualData<String>> actualMap, this.staticTypeCache)
: super(reporter, actualMap);
- String getStaticTypeValue(ir.DartType type) {
- StringBuffer sb = new StringBuffer();
- const TypeTextVisitor().writeType(type, sb);
- return sb.toString();
- }
-
@override
String computeMemberValue(Id id, ir.Member node) {
return null;
@@ -203,7 +79,7 @@
@override
String computeNodeValue(Id id, ir.TreeNode node) {
if (node is ir.VariableGet || node is ir.MethodInvocation) {
- return getStaticTypeValue(node.accept(staticTypeCache));
+ return typeToText(node.accept(staticTypeCache));
}
return null;
}
diff --git a/tests/compiler/dart2js/static_type/type_promotion_data/equals.dart b/tests/compiler/dart2js/static_type/type_promotion_data/equals.dart
new file mode 100644
index 0000000..17df94a
--- /dev/null
+++ b/tests/compiler/dart2js/static_type/type_promotion_data/equals.dart
@@ -0,0 +1,76 @@
+// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+bool equals(e1, e2, bool unordered) {
+ if (/*{}*/ e1 is Set) {
+ return /*{e1:[{true:Set<dynamic>}|Set<dynamic>]}*/
+ e2 is Set &&
+ /*{
+ e1:[{true:Set<dynamic>}|Set<dynamic>],
+ e2:[{true:Set<dynamic>}|Set<dynamic>]}
+ */
+ e1 == null;
+ }
+ if (/*{e1:[{false:Set<dynamic>}|Set<dynamic>]}*/ e1 is Map) {
+ return
+ /*{e1:[{true:Map<dynamic,dynamic>,false:Set<dynamic>}|Set<dynamic>,Map<dynamic,dynamic>]}*/
+ e2 is Map &&
+ /*{
+ e1:[{true:Map<dynamic,dynamic>,false:Set<dynamic>}|Set<dynamic>,Map<dynamic,dynamic>],
+ e2:[{true:Map<dynamic,dynamic>}|Map<dynamic,dynamic>]}
+ */
+ e1 == null;
+ }
+ if (! /*{e1:[{false:Set<dynamic>,Map<dynamic,dynamic>}|Set<dynamic>,Map<dynamic,dynamic>]}*/
+ unordered) {
+ if (/*{e1:[{false:Set<dynamic>,Map<dynamic,dynamic>}|Set<dynamic>,Map<dynamic,dynamic>]}*/
+ e1 is List) {
+ return
+ /*{e1:[{true:List<dynamic>,false:Set<dynamic>,Map<dynamic,dynamic>}|Set<dynamic>,Map<dynamic,dynamic>,List<dynamic>]}*/
+ e2 is List &&
+ /*{
+ e1:[{true:List<dynamic>,false:Set<dynamic>,Map<dynamic,dynamic>}|Set<dynamic>,Map<dynamic,dynamic>,List<dynamic>],
+ e2:[{true:List<dynamic>}|List<dynamic>]}
+ */
+ e1 == null;
+ }
+ if (/*{e1:[{false:Set<dynamic>,Map<dynamic,dynamic>,List<dynamic>}|Set<dynamic>,Map<dynamic,dynamic>,List<dynamic>]}*/
+ e1 is Iterable) {
+ return
+ /*{e1:[{true:Iterable<dynamic>,false:Set<dynamic>,Map<dynamic,dynamic>,List<dynamic>}|Set<dynamic>,Map<dynamic,dynamic>,List<dynamic>,Iterable<dynamic>]}*/
+ e2 is Iterable &&
+ /*{
+ e1:[{true:Iterable<dynamic>,false:Set<dynamic>,Map<dynamic,dynamic>,List<dynamic>}|Set<dynamic>,Map<dynamic,dynamic>,List<dynamic>,Iterable<dynamic>],
+ e2:[{true:Iterable<dynamic>}|Iterable<dynamic>]}
+ */
+ e1 == null;
+ }
+ } else if (/*{e1:[{false:Set<dynamic>,Map<dynamic,dynamic>}|Set<dynamic>,Map<dynamic,dynamic>]}*/
+ e1 is Iterable) {
+ if (
+ /*{e1:[{true:Iterable<dynamic>,false:Set<dynamic>,Map<dynamic,dynamic>}|Set<dynamic>,Map<dynamic,dynamic>,Iterable<dynamic>]}*/
+ e1 is List !=
+ /*{e1:[{true:Iterable<dynamic>,false:Set<dynamic>,Map<dynamic,dynamic>}|Set<dynamic>,Map<dynamic,dynamic>,Iterable<dynamic>]}*/
+ e2 is List) {
+ return
+ /*{e1:[{true:Iterable<dynamic>,false:Set<dynamic>,Map<dynamic,dynamic>}|Iterable<dynamic>,Set<dynamic>,Map<dynamic,dynamic>]}*/
+ e1 == null;
+ }
+ return /*{e1:[{true:Iterable<dynamic>,false:Set<dynamic>,Map<dynamic,dynamic>}|Iterable<dynamic>,Set<dynamic>,Map<dynamic,dynamic>]}*/
+ e2 is Iterable &&
+
+ /*{
+ e1:[{true:Iterable<dynamic>,false:Set<dynamic>,Map<dynamic,dynamic>}|Iterable<dynamic>,Set<dynamic>,Map<dynamic,dynamic>],
+ e2:[{true:Iterable<dynamic>}|Iterable<dynamic>]}
+ */
+ e1 == null;
+ }
+ return
+ /*{e1:[{false:Set<dynamic>,Map<dynamic,dynamic>,Iterable<dynamic>}|Set<dynamic>,Map<dynamic,dynamic>,Iterable<dynamic>]}*/
+ e1 == null;
+}
+
+main() {
+ equals(null, null, true);
+}
diff --git a/tests/compiler/dart2js/static_type/type_promotion_data/if.dart b/tests/compiler/dart2js/static_type/type_promotion_data/if.dart
new file mode 100644
index 0000000..5c54faa
--- /dev/null
+++ b/tests/compiler/dart2js/static_type/type_promotion_data/if.dart
@@ -0,0 +1,102 @@
+// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+class A {}
+
+class B {}
+
+main() {
+ ifThen(null);
+ ifThenSequence(null);
+ ifThenElse(null);
+ ifThenElseSequence(null);
+ ifNotReturn(null);
+ nestedIf(null);
+ nestedIf2(null);
+ nestedIfNotReturn(null);
+}
+
+ifThen(o) {
+ /*{}*/ o;
+ if (/*{}*/ o is A) {
+ /*{o:[{true:A}|A]}*/ o;
+ }
+ /*{}*/ o;
+}
+
+ifThenSequence(o) {
+ /*{}*/ o;
+ if (/*{}*/ o is A) {
+ /*{o:[{true:A}|A]}*/ o;
+ }
+ /*{}*/ o;
+ if (/*{}*/ o is B) {
+ /*{o:[{true:B}|B]}*/ o;
+ }
+ /*{}*/ o;
+}
+
+ifThenElse(o) {
+ /*{}*/ o;
+ if (/*{}*/ o is A) {
+ /*{o:[{true:A}|A]}*/ o;
+ } else {
+ /*{o:[{false:A}|A]}*/ o;
+ }
+ /*{}*/ o;
+}
+
+ifThenElseSequence(o) {
+ /*{}*/ o;
+ if (/*{}*/ o is A) {
+ /*{o:[{true:A}|A]}*/ o;
+ } else {
+ /*{o:[{false:A}|A]}*/ o;
+ }
+ /*{}*/ o;
+ if (/*{}*/ o is B) {
+ /*{o:[{true:B}|B]}*/ o;
+ } else {
+ /*{o:[{false:B}|B]}*/ o;
+ }
+ /*{}*/ o;
+}
+
+ifNotReturn(o) {
+ /*{}*/ o;
+ if (/*{}*/ o is! A) {
+ return /*{o:[{false:A}|A]}*/ o;
+ }
+ /*{o:[{true:A}|A]}*/ o;
+}
+
+nestedIf(o) {
+ if (/*{}*/ o is A) {
+ if (/*{o:[{true:A}|A]}*/ o is B) {
+ return /*{o:[{true:A,B}|A,B]}*/ o;
+ }
+ }
+ /*{}*/ o;
+}
+
+nestedIf2(o) {
+ if (/*{}*/ o is A) {
+ if (/*{o:[{true:A}|A]}*/ o is B) {
+ return /*{o:[{true:A,B}|A,B]}*/ o;
+ }
+ } else if (/*{o:[{false:A}|A]}*/ o is B) {
+ /*{o:[{true:B,false:A}|A,B]}*/ o;
+ }
+ /*{}*/ o;
+}
+
+nestedIfNotReturn(o) {
+ if (/*{}*/ o is A) {
+ if (/*{o:[{true:A}|A]}*/ o is! B) {
+ return /*{o:[{true:A,false:B}|A,B]}*/ o;
+ }
+ /*{o:[{true:A,B}|A,B]}*/ o;
+ }
+ /*{}*/ o;
+}
diff --git a/tests/compiler/dart2js/static_type/type_promotion_data/null.dart b/tests/compiler/dart2js/static_type/type_promotion_data/null.dart
new file mode 100644
index 0000000..02a3697
--- /dev/null
+++ b/tests/compiler/dart2js/static_type/type_promotion_data/null.dart
@@ -0,0 +1,46 @@
+// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+main() {
+ ifNull(null);
+ ifNullElse(null);
+ ifNotNull(null);
+ ifNotNullElse(null);
+}
+
+ifNull(o) {
+ /*{}*/ o;
+ if (/*{}*/ o == null) {
+ /*{o:[{false:dynamic}|dynamic]}*/ o;
+ }
+ /*{}*/ o;
+}
+
+ifNullElse(o) {
+ /*{}*/ o;
+ if (/*{}*/ o == null) {
+ /*{o:[{false:dynamic}|dynamic]}*/ o;
+ } else {
+ /*{o:[{true:dynamic}|dynamic]}*/ o;
+ }
+ /*{}*/ o;
+}
+
+ifNotNull(o) {
+ /*{}*/ o;
+ if (/*{}*/ o != null) {
+ /*{o:[{true:dynamic}|dynamic]}*/ o;
+ }
+ /*{}*/ o;
+}
+
+ifNotNullElse(o) {
+ /*{}*/ o;
+ if (/*{}*/ o != null) {
+ /*{o:[{true:dynamic}|dynamic]}*/ o;
+ } else {
+ /*{o:[{false:dynamic}|dynamic]}*/ o;
+ }
+ /*{}*/ o;
+}
diff --git a/tests/compiler/dart2js/static_type/type_promotion_test.dart b/tests/compiler/dart2js/static_type/type_promotion_test.dart
new file mode 100644
index 0000000..76f448f
--- /dev/null
+++ b/tests/compiler/dart2js/static_type/type_promotion_test.dart
@@ -0,0 +1,84 @@
+// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:io';
+import 'package:async_helper/async_helper.dart';
+import 'package:compiler/src/compiler.dart';
+import 'package:compiler/src/diagnostics/diagnostic_listener.dart';
+import 'package:compiler/src/elements/entities.dart';
+import 'package:compiler/src/ir/static_type.dart';
+import 'package:compiler/src/kernel/element_map_impl.dart';
+import 'package:compiler/src/kernel/kernel_strategy.dart';
+import 'package:kernel/ast.dart' as ir;
+import 'package:kernel/class_hierarchy.dart' as ir;
+import 'package:kernel/core_types.dart' as ir;
+import 'package:kernel/type_algebra.dart' as ir;
+import 'package:kernel/type_environment.dart' as ir;
+import '../equivalence/id_equivalence.dart';
+import '../equivalence/id_equivalence_helper.dart';
+import '../helpers/ir_types.dart';
+
+main(List<String> args) {
+ asyncTest(() async {
+ Directory dataDir =
+ new Directory.fromUri(Platform.script.resolve('type_promotion_data'));
+ await checkTests(dataDir, new TypePromotionDataComputer(),
+ args: args, testFrontend: true);
+ });
+}
+
+class TypePromotionDataComputer extends DataComputer<String> {
+ ir.TypeEnvironment _typeEnvironment;
+
+ ir.TypeEnvironment getTypeEnvironment(KernelToElementMapImpl elementMap) {
+ if (_typeEnvironment == null) {
+ ir.Component component = elementMap.env.mainComponent;
+ _typeEnvironment = new ir.TypeEnvironment(
+ new ir.CoreTypes(component), new ir.ClassHierarchy(component));
+ }
+ return _typeEnvironment;
+ }
+
+ /// Compute type inference data for [member] from kernel based inference.
+ ///
+ /// Fills [actualMap] with the data.
+ @override
+ void computeMemberData(Compiler compiler, MemberEntity member,
+ Map<Id, ActualData<String>> actualMap,
+ {bool verbose: false}) {
+ KernelFrontEndStrategy frontendStrategy = compiler.frontendStrategy;
+ KernelToElementMapImpl elementMap = frontendStrategy.elementMap;
+ Map<ir.Expression, TypeMap> typeMaps =
+ elementMap.getTypeMapsForTesting(member);
+ ir.Member node = elementMap.getMemberNode(member);
+ new TypePromotionIrComputer(compiler.reporter, actualMap, typeMaps)
+ .run(node);
+ }
+
+ @override
+ DataInterpreter<String> get dataValidator => const StringDataInterpreter();
+}
+
+/// IR visitor for computing inference data for a member.
+class TypePromotionIrComputer extends IrDataExtractor<String> {
+ final Map<ir.Expression, TypeMap> typeMaps;
+
+ TypePromotionIrComputer(DiagnosticReporter reporter,
+ Map<Id, ActualData<String>> actualMap, this.typeMaps)
+ : super(reporter, actualMap);
+
+ @override
+ String computeMemberValue(Id id, ir.Member node) {
+ return null;
+ }
+
+ @override
+ String computeNodeValue(Id id, ir.TreeNode node) {
+ if (node is ir.VariableGet) {
+ TypeMap type = typeMaps[node];
+ return type.getText(typesToText);
+ }
+ return null;
+ }
+}