[dart2js] global type analysis for unreachable closure calls
The return value of a closure call on `null` or bottom should be
bottom.
Change-Id: I4520a8d6b13575172cf5407b0fbcbaa0b6be5e63
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/162384
Commit-Queue: Stephen Adams <sra@google.com>
Reviewed-by: Mayank Patke <fishythefish@google.com>
diff --git a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
index 4aa804c..365500e 100644
--- a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
+++ b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
@@ -1502,7 +1502,20 @@
}
@override
- AbstractValue computeType(InferrerEngine inferrer) => safeType(inferrer);
+ AbstractValue computeType(InferrerEngine inferrer) {
+ AbstractValueDomain abstractValueDomain = inferrer.abstractValueDomain;
+ AbstractValue closureType = closure.type;
+ // We are not tracking closure calls, but if the receiver is not callable,
+ // the call will fail. The abstract value domain does not have a convenient
+ // method for detecting callable types, but we know `null` and unreachable
+ // code have no result type. This is helpful for propagating
+ // unreachability, i.e. tree-shaking.
+ if (abstractValueDomain.isEmpty(closureType).isDefinitelyTrue ||
+ abstractValueDomain.isNull(closureType).isDefinitelyTrue) {
+ return abstractValueDomain.emptyType;
+ }
+ return safeType(inferrer);
+ }
@override
Iterable<MemberEntity> get callees {
diff --git a/pkg/compiler/lib/src/inferrer/types.dart b/pkg/compiler/lib/src/inferrer/types.dart
index 25305e4..bafd5cc3 100644
--- a/pkg/compiler/lib/src/inferrer/types.dart
+++ b/pkg/compiler/lib/src/inferrer/types.dart
@@ -310,22 +310,29 @@
@override
AbstractValue resultTypeOfSelector(
Selector selector, AbstractValue receiver) {
- // Bailout for closure calls. We're not tracking types of
- // closures.
- if (selector.isClosureCall)
- return closedWorld.abstractValueDomain.dynamicType;
+ AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain;
+
+ // Bailout for closure calls. We're not tracking types of closures.
+ if (selector.isClosureCall) {
+ // But if the receiver is not callable, the call will fail.
+ if (abstractValueDomain.isEmpty(receiver).isDefinitelyTrue ||
+ abstractValueDomain.isNull(receiver).isDefinitelyTrue) {
+ return abstractValueDomain.emptyType;
+ }
+ return abstractValueDomain.dynamicType;
+ }
if (selector.isSetter || selector.isIndexSet) {
- return closedWorld.abstractValueDomain.dynamicType;
+ return abstractValueDomain.dynamicType;
}
if (returnsListElementType(selector, receiver)) {
- return closedWorld.abstractValueDomain.getContainerElementType(receiver);
+ return abstractValueDomain.getContainerElementType(receiver);
}
if (returnsMapValueType(selector, receiver)) {
- return closedWorld.abstractValueDomain.getMapValueType(receiver);
+ return abstractValueDomain.getMapValueType(receiver);
}
if (closedWorld.includesClosureCall(selector, receiver)) {
- return closedWorld.abstractValueDomain.dynamicType;
+ return abstractValueDomain.dynamicType;
} else {
Iterable<MemberEntity> elements =
closedWorld.locateMembers(selector, receiver);
@@ -334,7 +341,7 @@
AbstractValue type = typeOfMemberWithSelector(element, selector);
types.add(type);
}
- return closedWorld.abstractValueDomain.unionOfMany(types);
+ return abstractValueDomain.unionOfMany(types);
}
}
diff --git a/pkg/compiler/test/inference/data/static.dart b/pkg/compiler/test/inference/data/static.dart
index f641d1a..d34d4fc 100644
--- a/pkg/compiler/test/inference/data/static.dart
+++ b/pkg/compiler/test/inference/data/static.dart
@@ -181,7 +181,7 @@
/*member: _field1:[null]*/
dynamic _field1;
-/*member: invokeStaticFieldUninitialized:[null|subclass=Object]*/
+/*member: invokeStaticFieldUninitialized:[empty]*/
invokeStaticFieldUninitialized() => _field1();
////////////////////////////////////////////////////////////////////////////////