Use custom thisType to signal static type precision.
This also removes the dependency on the state (!) in ir.TypeEnvironment.
Change-Id: I0180d111ecf45b685c4abce12c6a9bd52c1f308e
Reviewed-on: https://dart-review.googlesource.com/c/88968
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/ir/cached_static_type.dart b/pkg/compiler/lib/src/ir/cached_static_type.dart
index 19ff07f..5daea39 100644
--- a/pkg/compiler/lib/src/ir/cached_static_type.dart
+++ b/pkg/compiler/lib/src/ir/cached_static_type.dart
@@ -14,8 +14,10 @@
/// and a precomputed cache for complex expression type.
class CachedStaticType extends StaticTypeBase implements StaticTypeProvider {
final Map<ir.Expression, ir.DartType> _cache;
+ final ThisInterfaceType thisType;
- CachedStaticType(ir.TypeEnvironment typeEnvironment, this._cache)
+ CachedStaticType(
+ ir.TypeEnvironment typeEnvironment, this._cache, this.thisType)
: super(typeEnvironment);
@override
diff --git a/pkg/compiler/lib/src/ir/impact.dart b/pkg/compiler/lib/src/ir/impact.dart
index edd7503..d9be817 100644
--- a/pkg/compiler/lib/src/ir/impact.dart
+++ b/pkg/compiler/lib/src/ir/impact.dart
@@ -12,6 +12,7 @@
import '../common.dart';
import 'scope.dart';
import 'static_type.dart';
+import 'static_type_base.dart';
import 'util.dart';
abstract class ImpactBuilder extends StaticTypeVisitor {
@@ -21,6 +22,16 @@
ir.ClassHierarchy classHierarchy, this.variableScopeModel)
: super(typeEnvironment, classHierarchy);
+ ClassRelation _computeClassRelationFromType(ir.DartType type) {
+ if (type is ThisInterfaceType) {
+ return ClassRelation.thisExpression;
+ } else if (type is ExactInterfaceType) {
+ return ClassRelation.exact;
+ } else {
+ return ClassRelation.subtype;
+ }
+ }
+
void registerIntLiteral(int value);
@override
@@ -402,9 +413,7 @@
receiver.variable.parent is ir.FunctionDeclaration) {
registerLocalFunctionInvocation(receiver.variable.parent, node.arguments);
} else {
- ClassRelation relation = receiver is ir.ThisExpression
- ? ClassRelation.thisExpression
- : ClassRelation.subtype;
+ ClassRelation relation = _computeClassRelationFromType(receiverType);
ir.Member interfaceTarget = node.interfaceTarget;
if (interfaceTarget == null) {
@@ -452,9 +461,7 @@
@override
void handlePropertyGet(
ir.PropertyGet node, ir.DartType receiverType, ir.DartType resultType) {
- ClassRelation relation = node.receiver is ir.ThisExpression
- ? ClassRelation.thisExpression
- : ClassRelation.subtype;
+ ClassRelation relation = _computeClassRelationFromType(receiverType);
if (node.interfaceTarget != null) {
registerInstanceGet(receiverType, relation, node.interfaceTarget);
} else {
@@ -477,9 +484,7 @@
@override
void handlePropertySet(
ir.PropertySet node, ir.DartType receiverType, ir.DartType valueType) {
- ClassRelation relation = node.receiver is ir.ThisExpression
- ? ClassRelation.thisExpression
- : ClassRelation.subtype;
+ ClassRelation relation = _computeClassRelationFromType(receiverType);
if (node.interfaceTarget != null) {
registerInstanceSet(receiverType, relation, node.interfaceTarget);
} else {
diff --git a/pkg/compiler/lib/src/ir/static_type.dart b/pkg/compiler/lib/src/ir/static_type.dart
index dae8bd2..8cafb6f 100644
--- a/pkg/compiler/lib/src/ir/static_type.dart
+++ b/pkg/compiler/lib/src/ir/static_type.dart
@@ -41,6 +41,8 @@
final ir.ClassHierarchy hierarchy;
+ ThisInterfaceType _thisType;
+
StaticTypeVisitor(ir.TypeEnvironment typeEnvironment, this.hierarchy)
: super(typeEnvironment);
@@ -56,6 +58,16 @@
VariableScopeModel get variableScopeModel;
+ ThisInterfaceType get thisType {
+ assert(_thisType != null);
+ return _thisType;
+ }
+
+ void set thisType(ThisInterfaceType value) {
+ assert(value == null || _thisType == null);
+ _thisType = value;
+ }
+
bool completes(ir.DartType type) => type != const DoesNotCompleteType();
Set<ir.VariableDeclaration> _currentVariables;
@@ -615,8 +627,8 @@
ir.DartType visitConstructorInvocation(ir.ConstructorInvocation node) {
ArgumentTypes argumentTypes = _visitArguments(node.arguments);
ir.DartType resultType = node.arguments.types.isEmpty
- ? node.target.enclosingClass.rawType
- : new ir.InterfaceType(
+ ? new ExactInterfaceType.from(node.target.enclosingClass.rawType)
+ : new ExactInterfaceType(
node.target.enclosingClass, node.arguments.types);
_cache[node] = resultType;
handleConstructorInvocation(node, argumentTypes, resultType);
@@ -637,8 +649,8 @@
if (declaringClass.typeParameters.isEmpty) {
resultType = node.interfaceTarget.getterType;
} else {
- ir.DartType receiver = typeEnvironment.getTypeAsInstanceOf(
- typeEnvironment.thisType, declaringClass);
+ ir.DartType receiver =
+ typeEnvironment.getTypeAsInstanceOf(thisType, declaringClass);
resultType = ir.Substitution.fromInterfaceType(receiver)
.substituteType(node.interfaceTarget.getterType);
}
@@ -670,8 +682,8 @@
returnType = const ir.DynamicType();
} else {
ir.Class superclass = node.interfaceTarget.enclosingClass;
- ir.InterfaceType receiverType = typeEnvironment.getTypeAsInstanceOf(
- typeEnvironment.thisType, superclass);
+ ir.InterfaceType receiverType =
+ typeEnvironment.getTypeAsInstanceOf(thisType, superclass);
returnType = ir.Substitution.fromInterfaceType(receiverType)
.substituteType(node.interfaceTarget.function.returnType);
returnType = ir.Substitution.fromPairs(
@@ -1158,22 +1170,21 @@
@override
Null visitProcedure(ir.Procedure node) {
- typeEnvironment.thisType =
- node.enclosingClass != null ? node.enclosingClass.thisType : null;
+ thisType = new ThisInterfaceType.from(node.enclosingClass?.thisType);
_currentVariables = new Set<ir.VariableDeclaration>();
visitSignature(node.function);
visitNode(node.function.body);
handleProcedure(node);
_invalidatedVariables.removeAll(_currentVariables);
_currentVariables = null;
- typeEnvironment.thisType = null;
+ thisType = null;
}
void handleConstructor(ir.Constructor node) {}
@override
Null visitConstructor(ir.Constructor node) {
- typeEnvironment.thisType = node.enclosingClass.thisType;
+ thisType = new ThisInterfaceType.from(node.enclosingClass.thisType);
_currentVariables = new Set<ir.VariableDeclaration>();
visitSignature(node.function);
visitNodes(node.initializers);
@@ -1181,18 +1192,17 @@
handleConstructor(node);
_invalidatedVariables.removeAll(_currentVariables);
_currentVariables = null;
- typeEnvironment.thisType = null;
+ thisType = null;
}
void handleField(ir.Field node) {}
@override
Null visitField(ir.Field node) {
- typeEnvironment.thisType =
- node.enclosingClass != null ? node.enclosingClass.thisType : null;
+ thisType = new ThisInterfaceType.from(node.enclosingClass?.thisType);
visitNode(node.initializer);
handleField(node);
- typeEnvironment.thisType = null;
+ thisType = null;
}
void handleVariableDeclaration(ir.VariableDeclaration node) {}
diff --git a/pkg/compiler/lib/src/ir/static_type_base.dart b/pkg/compiler/lib/src/ir/static_type_base.dart
index 6ab9972..20cf00c 100644
--- a/pkg/compiler/lib/src/ir/static_type_base.dart
+++ b/pkg/compiler/lib/src/ir/static_type_base.dart
@@ -13,6 +13,34 @@
/// and return statements.
class DoesNotCompleteType extends ir.BottomType {
const DoesNotCompleteType();
+
+ String toString() => 'DoesNotCompleteType()';
+}
+
+/// Special interface type used to signal that the static type of an expression
+/// has precision of a this-expression.
+class ThisInterfaceType extends ir.InterfaceType {
+ ThisInterfaceType(ir.Class classNode, [List<ir.DartType> typeArguments])
+ : super(classNode, typeArguments);
+
+ factory ThisInterfaceType.from(ir.InterfaceType type) => type != null
+ ? new ThisInterfaceType(type.classNode, type.typeArguments)
+ : null;
+
+ String toString() => 'this:${super.toString()}';
+}
+
+/// Special interface type used to signal that the static type of an expression
+/// is exact, i.e. the runtime type is not a subtype or subclass of the type.
+class ExactInterfaceType extends ir.InterfaceType {
+ ExactInterfaceType(ir.Class classNode, [List<ir.DartType> typeArguments])
+ : super(classNode, typeArguments);
+
+ factory ExactInterfaceType.from(ir.InterfaceType type) => type != null
+ ? new ExactInterfaceType(type.classNode, type.typeArguments)
+ : null;
+
+ String toString() => 'exact:${super.toString()}';
}
/// Base class for computing static types.
@@ -25,7 +53,7 @@
/// expression kind. For instance method invocations whose static type depend
/// on the static types of the receiver and type arguments and the signature
/// of the targeted procedure.
-class StaticTypeBase extends ir.Visitor<ir.DartType> {
+abstract class StaticTypeBase extends ir.Visitor<ir.DartType> {
final ir.TypeEnvironment _typeEnvironment;
StaticTypeBase(this._typeEnvironment);
@@ -34,6 +62,8 @@
ir.TypeEnvironment get typeEnvironment => _typeEnvironment;
+ ThisInterfaceType get thisType;
+
@override
ir.DartType defaultNode(ir.Node node) {
return null;
@@ -119,8 +149,7 @@
}
@override
- ir.DartType visitThisExpression(ir.ThisExpression node) =>
- typeEnvironment.thisType;
+ ThisInterfaceType visitThisExpression(ir.ThisExpression node) => thisType;
@override
ir.DartType visitStaticGet(ir.StaticGet node) => node.target.getterType;
diff --git a/pkg/compiler/lib/src/js_model/element_map_impl.dart b/pkg/compiler/lib/src/js_model/element_map_impl.dart
index ba6d29a..47433bc 100644
--- a/pkg/compiler/lib/src/js_model/element_map_impl.dart
+++ b/pkg/compiler/lib/src/js_model/element_map_impl.dart
@@ -33,6 +33,7 @@
import '../ir/element_map.dart';
import '../ir/types.dart';
import '../ir/visitors.dart';
+import '../ir/static_type_base.dart';
import '../ir/static_type_provider.dart';
import '../ir/util.dart';
import '../js/js.dart' as js;
@@ -1102,12 +1103,8 @@
}
assert(cachedStaticTypes != null, "No static types cached for $member.");
- return new CachedStaticType(
- // We need a copy of the type environment since the `thisType` field
- // is holds state, making the environment contextually bound.
- new ir.TypeEnvironment(typeEnvironment.coreTypes, classHierarchy)
- ..thisType = thisType,
- cachedStaticTypes);
+ return new CachedStaticType(typeEnvironment, cachedStaticTypes,
+ new ThisInterfaceType.from(thisType));
}
Name getName(ir.Name name) {
diff --git a/pkg/compiler/lib/src/serialization/abstract_source.dart b/pkg/compiler/lib/src/serialization/abstract_source.dart
index 28da0ca..28fdf8a 100644
--- a/pkg/compiler/lib/src/serialization/abstract_source.dart
+++ b/pkg/compiler/lib/src/serialization/abstract_source.dart
@@ -219,6 +219,8 @@
return const ir.InvalidType();
case DartTypeNodeKind.bottomType:
return const ir.BottomType();
+ case DartTypeNodeKind.doesNotComplete:
+ return const DoesNotCompleteType();
case DartTypeNodeKind.typeParameterType:
ir.TypeParameter typeParameter = readTypeParameterNode();
ir.DartType promotedBound = _readDartTypeNode(functionTypeVariables);
@@ -270,6 +272,16 @@
List<ir.DartType> typeArguments =
_readDartTypeNodes(functionTypeVariables);
return new ir.InterfaceType(cls, typeArguments);
+ case DartTypeNodeKind.thisInterfaceType:
+ ir.Class cls = readClassNode();
+ List<ir.DartType> typeArguments =
+ _readDartTypeNodes(functionTypeVariables);
+ return new ThisInterfaceType(cls, typeArguments);
+ case DartTypeNodeKind.exactInterfaceType:
+ ir.Class cls = readClassNode();
+ List<ir.DartType> typeArguments =
+ _readDartTypeNodes(functionTypeVariables);
+ return new ExactInterfaceType(cls, typeArguments);
case DartTypeNodeKind.typedef:
ir.Typedef typedef = readTypedefNode();
List<ir.DartType> typeArguments =
diff --git a/pkg/compiler/lib/src/serialization/helpers.dart b/pkg/compiler/lib/src/serialization/helpers.dart
index 589c6eef..a75b7e3 100644
--- a/pkg/compiler/lib/src/serialization/helpers.dart
+++ b/pkg/compiler/lib/src/serialization/helpers.dart
@@ -90,7 +90,7 @@
interfaceType,
typedef,
dynamicType,
- futureOr
+ futureOr,
}
/// Visitor that serializes [DartType] object together with [AbstractDataSink].
@@ -194,6 +194,9 @@
dynamicType,
bottomType,
invalidType,
+ thisInterfaceType,
+ exactInterfaceType,
+ doesNotComplete,
}
const String functionTypeNodeTag = 'function-type-node';
@@ -235,12 +238,22 @@
void visitBottomType(
ir.BottomType node, List<ir.TypeParameter> functionTypeVariables) {
- _sink.writeEnum(DartTypeNodeKind.bottomType);
+ if (node == const DoesNotCompleteType()) {
+ _sink.writeEnum(DartTypeNodeKind.doesNotComplete);
+ } else {
+ _sink.writeEnum(DartTypeNodeKind.bottomType);
+ }
}
void visitInterfaceType(
ir.InterfaceType node, List<ir.TypeParameter> functionTypeVariables) {
- _sink.writeEnum(DartTypeNodeKind.interfaceType);
+ if (node is ThisInterfaceType) {
+ _sink.writeEnum(DartTypeNodeKind.thisInterfaceType);
+ } else if (node is ExactInterfaceType) {
+ _sink.writeEnum(DartTypeNodeKind.exactInterfaceType);
+ } else {
+ _sink.writeEnum(DartTypeNodeKind.interfaceType);
+ }
_sink.writeClassNode(node.classNode);
visitTypes(node.typeArguments, functionTypeVariables);
}
diff --git a/pkg/compiler/lib/src/serialization/serialization.dart b/pkg/compiler/lib/src/serialization/serialization.dart
index 2d2fd9b..3348edd 100644
--- a/pkg/compiler/lib/src/serialization/serialization.dart
+++ b/pkg/compiler/lib/src/serialization/serialization.dart
@@ -14,6 +14,7 @@
import '../elements/entities.dart';
import '../elements/indexed.dart';
import '../elements/types.dart';
+import '../ir/static_type_base.dart';
import '../js_model/closure.dart';
import '../js_model/locals.dart';
diff --git a/tests/compiler/dart2js/impact/data/classes.dart b/tests/compiler/dart2js/impact/data/classes.dart
index a31b32f..bacab72 100644
--- a/tests/compiler/dart2js/impact/data/classes.dart
+++ b/tests/compiler/dart2js/impact/data/classes.dart
@@ -240,7 +240,7 @@
}
/*strong.element: testInstanceGenericMethod:
- dynamic=[GenericClass.genericMethod<bool>(1)],
+ dynamic=[exact:GenericClass.genericMethod<bool>(1)],
static=[
GenericClass.generative(0),
assertIsSubtype,
diff --git a/tests/compiler/dart2js/impact/data/exact.dart b/tests/compiler/dart2js/impact/data/exact.dart
new file mode 100644
index 0000000..11a6d2d
--- /dev/null
+++ b/tests/compiler/dart2js/impact/data/exact.dart
@@ -0,0 +1,48 @@
+// 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.
+
+/*element: A.:static=[Object.(0)]*/
+class A {
+ method1() {}
+ method2() {}
+ method3() {}
+}
+
+/*element: B.:static=[A.(0)]*/
+class B extends A {
+ method1() {}
+ method2() {}
+ method3() {}
+}
+
+/*element: C.:static=[B.(0)]*/
+class C extends B {
+ method1() {}
+ method2() {}
+ method3() {}
+}
+
+/*element: main:static=[callOnEffectivelyFinalB(0),callOnNewB(0),callOnNewC(0)]*/
+main() {
+ callOnNewB();
+ callOnNewC();
+ callOnEffectivelyFinalB();
+ callOnEffectivelyFinalB();
+}
+
+/*element: callOnNewB:dynamic=[exact:B.method1(0)],static=[B.(0)]*/
+callOnNewB() {
+ new B().method1();
+}
+
+/*element: callOnNewC:dynamic=[exact:C.method2(0)],static=[C.(0)]*/
+callOnNewC() {
+ new C().method2();
+}
+
+/*element: callOnEffectivelyFinalB:dynamic=[exact:B.method3(0)],static=[B.(0)]*/
+callOnEffectivelyFinalB() {
+ A a = new B();
+ a.method3();
+}
diff --git a/tests/compiler/dart2js/impact/data/runtime_type.dart b/tests/compiler/dart2js/impact/data/runtime_type.dart
index 93e027c..96fac37 100644
--- a/tests/compiler/dart2js/impact/data/runtime_type.dart
+++ b/tests/compiler/dart2js/impact/data/runtime_type.dart
@@ -379,7 +379,7 @@
notEquals4(Class3 a, Class4 b) => a?.runtimeType != b?.runtimeType;
/*element: main:
- dynamic=[Class1a.==],
+ dynamic=[exact:Class1a.==],
static=[
Class1a.(0),
Class1b.(0),
diff --git a/tests/compiler/dart2js/model/strong_mode_closed_world_test.dart b/tests/compiler/dart2js/model/strong_mode_closed_world_test.dart
index d721dc7..aeb3108 100644
--- a/tests/compiler/dart2js/model/strong_mode_closed_world_test.dart
+++ b/tests/compiler/dart2js/model/strong_mode_closed_world_test.dart
@@ -17,11 +17,18 @@
}
runTest() async {
- CompilationResult result = await runCompiler(memorySourceFiles: {
- 'main.dart': '''
+ // Pretend this is a dart2js_native test to allow use of 'native' keyword
+ // and import of private libraries.
+ String main = 'sdk/tests/compiler/dart2js_native/main.dart';
+ Uri entryPoint = Uri.parse('memory:$main');
+
+ CompilationResult result =
+ await runCompiler(entryPoint: entryPoint, memorySourceFiles: {
+ main: '''
class A {
method1() {}
method2() {}
+ method4() {}
get getter => 42;
set setter(_) {}
}
@@ -29,6 +36,7 @@
class B {
method1() {}
method2() {}
+ method5() {}
get getter => 42;
set setter(_) {}
}
@@ -36,6 +44,7 @@
class C extends A {
method1() {}
method2() {}
+ method4() {}
get getter => 42;
set setter(_) {}
}
@@ -43,6 +52,7 @@
class D implements B {
method1() {}
method2() {}
+ method5() {}
get getter => 42;
set setter(_) {}
}
@@ -50,6 +60,7 @@
class E implements A {
method1() {}
method2() {}
+ method4() {}
get getter => 42;
set setter(_) {}
}
@@ -57,6 +68,7 @@
class F extends B {
method1() {}
method2() {}
+ method5() {}
get getter => 42;
set setter(_) {}
}
@@ -64,6 +76,7 @@
class G {
method1() {}
method2() {}
+ method4() {}
get getter => 42;
set setter(_) {}
}
@@ -73,6 +86,7 @@
class I {
method1() {}
method2() {}
+ method4() {}
get getter => 42;
set setter(_) {}
}
@@ -124,6 +138,12 @@
}
main() {
+ method1();
+ method2();
+}
+
+@pragma('dart2js:disableFinal')
+method1() {
A a = new A();
B b = new B();
a.method1();
@@ -148,14 +168,21 @@
new Class1b();
new Class2().c(0, 1, 2);
}
+
+method2() {
+ A a = new A();
+ B b = new B();
+ a.method4();
+ b.method5();
+}
'''
});
Expect.isTrue(result.isSuccess);
Compiler compiler = result.compiler;
Map<String, List<String>> expectedLiveMembersMap = <String, List<String>>{
- 'A': ['method1', 'getter'],
- 'B': ['method2', 'setter'],
+ 'A': ['method1', 'getter', 'method4'],
+ 'B': ['method2', 'setter', 'method5'],
'C': ['method1', 'getter'],
'D': ['method2', 'setter'],
'G': ['method1', 'getter'],
diff --git a/tests/compiler/dart2js/serialization/data/custom_types.dart b/tests/compiler/dart2js/serialization/data/custom_types.dart
new file mode 100644
index 0000000..a301473
--- /dev/null
+++ b/tests/compiler/dart2js/serialization/data/custom_types.dart
@@ -0,0 +1,31 @@
+// 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.
+
+class A {
+ method() {
+ Object c = this;
+ return c;
+ }
+}
+
+main() {
+ exactInterfaceType();
+ thisInterfaceType();
+ doesNotCompleteType();
+}
+
+exactInterfaceType() {
+ Object c = new A();
+ return c;
+}
+
+thisInterfaceType() {
+ new A().method();
+}
+
+doesNotCompleteType() {
+ Object c = throw '';
+ // ignore: dead_code
+ return c;
+}
diff --git a/tests/compiler/dart2js/static_type/static_type_test.dart b/tests/compiler/dart2js/static_type/static_type_test.dart
index 070d0f6..1f935d8 100644
--- a/tests/compiler/dart2js/static_type/static_type_test.dart
+++ b/tests/compiler/dart2js/static_type/static_type_test.dart
@@ -8,6 +8,7 @@
import 'package:compiler/src/diagnostics/diagnostic_listener.dart';
import 'package:compiler/src/elements/entities.dart';
import 'package:compiler/src/ir/cached_static_type.dart';
+import 'package:compiler/src/ir/static_type_base.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;
@@ -55,7 +56,9 @@
compiler.reporter,
actualMap,
new CachedStaticType(
- getTypeEnvironment(elementMap), staticTypeCache))
+ getTypeEnvironment(elementMap),
+ staticTypeCache,
+ new ThisInterfaceType.from(node.enclosingClass?.thisType)))
.run(node);
}