[vm] Use TFA to remove explicit type-checks (including keyword-covariant parameters).

- Also allow using the unchecked entry-point for invocations of generic functions where
  there are no bounds or the bounds don't require dynamic checks.

Change-Id: I6ca1ebec777ecf2989c4fb77425d65d542d5adf2
Cq-Include-Trybots: luci.dart.try:vm-kernel-optcounter-threshold-linux-release-x64-try, vm-kernel-precomp-linux-debug-x64-try, vm-kernel-precomp-linux-release-simarm-try, vm-kernel-precomp-linux-release-simarm64-try, vm-kernel-precomp-linux-release-x64-try, vm-kernel-precomp-mac-release-simarm64-try, vm-kernel-precomp-win-release-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/87181
Commit-Queue: Samir Jindel <sjindel@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Samir Jindel 2019-01-08 08:36:58 +00:00 committed by commit-bot@chromium.org
parent 0213c9f9d2
commit b58e8d7307
20 changed files with 192 additions and 90 deletions

View file

@ -154,7 +154,8 @@ class _DirectInvocation extends _Invocation {
//
// TODO(sjindel): Use [TypeCheck] to avoid bounds checks.
if (selector.member.function != null) {
typeChecksNeeded = !selector.member.function.typeParameters.isEmpty;
typeChecksNeeded = selector.member.function.typeParameters
.any((t) => t.isGenericCovariantImpl);
}
}
@ -1206,6 +1207,7 @@ class _ClassHierarchyCache implements TypeHierarchy {
Class get futureOrClass => environment.coreTypes.futureOrClass;
Class get futureClass => environment.coreTypes.futureClass;
Class get functionClass => environment.coreTypes.functionClass;
}
class _WorkList {
@ -1395,10 +1397,16 @@ class TypeFlowAnalysis implements EntryPointsListener, CallHandler {
Call callSite(TreeNode node) => summaryCollector.callSites[node];
TypeCheck explicitCast(AsExpression cast) =>
summaryCollector.explicitCasts[cast];
Type fieldType(Field field) => _fieldValues[field]?.value;
Args<Type> argumentTypes(Member member) => _summaries[member]?.argumentTypes;
List<VariableDeclaration> uncheckedParameters(Member member) =>
_summaries[member]?.uncheckedParameters;
bool isTearOffTaken(Member member) => _tearOffTaken.contains(member);
/// Returns true if this member is called dynamically.

View file

@ -413,9 +413,30 @@ class TypeCheck extends Statement {
TypeExpr arg;
TypeExpr type;
final VariableDeclaration parameter;
// The Kernel which this TypeCheck corresponds to. Can be a
// VariableDeclaration, AsExpression or Field.
//
// VariableDeclaration is used for parameter type-checks.
// Field is used for type-checks of parameters to implicit setters.
final TreeNode node;
TypeCheck(this.arg, this.type, this.parameter);
final Type staticType;
// 'isTestedOnlyOnCheckedEntryPoint' is whether or not this parameter's type-check will
// occur on the "checked" entrypoint in the VM but will be skipped on
// "unchecked" entrypoint.
bool isTestedOnlyOnCheckedEntryPoint;
VariableDeclaration get parameter =>
node is VariableDeclaration ? node : null;
bool canAlwaysSkip = true;
TypeCheck(this.arg, this.type, this.node, this.staticType) {
assertx(node != null);
isTestedOnlyOnCheckedEntryPoint =
parameter != null && !parameter.isCovariant;
}
@override
void accept(StatementVisitor visitor) => visitor.visitTypeCheck(this);
@ -423,9 +444,7 @@ class TypeCheck extends Statement {
@override
String dump() {
String result = "$label = _TypeCheck ($arg against $type)";
if (parameter != null) {
result += " (for parameter ${parameter.name})";
}
result += " (for ${node})";
return result;
}
@ -437,33 +456,35 @@ class TypeCheck extends Statement {
// TODO(sjindel/tfa): Narrow the result if possible.
assertx(checkType is AnyType || checkType is RuntimeType);
bool canSkip = true; // Can this check be skipped on this invocation.
if (checkType is AnyType) {
// If we don't know what the RHS of the check is going to be, we can't
// guarantee that it will pass.
callHandler.typeCheckTriggered();
if (kPrintTrace) {
tracePrint("TypeCheck failed, type is unknown");
}
canSkip = false;
} else if (checkType is RuntimeType) {
final bool canSkip =
argType.isSubtypeOfRuntimeType(typeHierarchy, checkType);
if (!canSkip) {
callHandler.typeCheckTriggered();
if (kPrintTrace) {
tracePrint("TypeCheck of $argType against $checkType failed.");
}
}
canSkip = argType.isSubtypeOfRuntimeType(typeHierarchy, checkType);
argType = argType.intersection(
Type.fromStatic(checkType.representedTypeRaw), typeHierarchy);
} else {
assertx(false, details: "Cannot see $checkType on RHS of TypeCheck.");
}
if (parameter != null) {
argType =
argType.intersection(Type.fromStatic(parameter.type), typeHierarchy);
// If this check might be skipped on an
// unchecked entry-point, we need to signal that the call-site must be
// checked.
if (!canSkip) {
canAlwaysSkip = false;
if (isTestedOnlyOnCheckedEntryPoint) {
callHandler.typeCheckTriggered();
}
if (kPrintTrace) {
tracePrint("TypeCheck of $argType against $checkType failed.");
}
}
argType = argType.intersection(staticType, typeHierarchy);
return argType;
}
}
@ -602,4 +623,16 @@ class Summary {
}
return new Args<Type>(argTypes, names: argNames);
}
List<VariableDeclaration> get uncheckedParameters {
final params = List<VariableDeclaration>();
for (Statement statement in _statements) {
if (statement is TypeCheck &&
statement.canAlwaysSkip &&
statement.parameter != null) {
params.add(statement.parameter);
}
}
return params;
}
}

View file

@ -61,7 +61,7 @@ class _SummaryNormalizer extends StatementVisitor {
}
for (Statement st in statements) {
if (st is Call) {
if (st is Call || st is TypeCheck) {
_normalizeExpr(st, false);
} else if (st is Use) {
_normalizeExpr(st.arg, true);
@ -283,6 +283,8 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
final GenericInterfacesInfo _genericInterfacesInfo;
final Map<TreeNode, Call> callSites = <TreeNode, Call>{};
final Map<AsExpression, TypeCheck> explicitCasts =
<AsExpression, TypeCheck>{};
final _FallthroughDetector _fallthroughDetector = new _FallthroughDetector();
Summary _summary;
@ -338,7 +340,8 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
} else {
Parameter valueParam = _declareParameter("value", member.type, null);
TypeExpr runtimeType = _translator.translate(member.type);
final check = new TypeCheck(valueParam, runtimeType, null);
final check = new TypeCheck(
valueParam, runtimeType, member, Type.fromStatic(member.type));
_summary.add(check);
_summary.result = check;
}
@ -393,17 +396,19 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
}
for (int i = 0; i < function.positionalParameters.length; ++i) {
final decl = function.positionalParameters[i];
_declareParameter(
function.positionalParameters[i].name,
function.positionalParameters[i].isGenericCovariantImpl
decl.name,
_useTypeCheckForParameter(decl)
? null
: useTypesFrom.positionalParameters[i].type,
function.positionalParameters[i].initializer);
}
for (int i = 0; i < function.namedParameters.length; ++i) {
final decl = function.namedParameters[i];
_declareParameter(
function.namedParameters[i].name,
function.namedParameters[i].isGenericCovariantImpl
decl.name,
_useTypeCheckForParameter(decl)
? null
: useTypesFrom.namedParameters[i].type,
function.namedParameters[i].initializer);
@ -411,15 +416,16 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
int count = firstParamIndex;
for (int i = 0; i < function.positionalParameters.length; ++i) {
Join v = _declareVariable(function.positionalParameters[i],
useTypeCheck:
function.positionalParameters[i].isGenericCovariantImpl,
final decl = function.positionalParameters[i];
Join v = _declareVariable(decl,
useTypeCheck: _useTypeCheckForParameter(decl),
checkType: useTypesFrom.positionalParameters[i].type);
v.values.add(_summary.statements[count++]);
}
for (int i = 0; i < function.namedParameters.length; ++i) {
Join v = _declareVariable(function.namedParameters[i],
useTypeCheck: function.namedParameters[i].isGenericCovariantImpl,
final decl = function.namedParameters[i];
Join v = _declareVariable(decl,
useTypeCheck: _useTypeCheckForParameter(decl),
checkType: useTypesFrom.namedParameters[i].type);
v.values.add(_summary.statements[count++]);
}
@ -470,6 +476,10 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
return _summary;
}
bool _useTypeCheckForParameter(VariableDeclaration decl) {
return decl.isCovariant || decl.isGenericCovariantImpl;
}
Args<Type> rawArguments(Selector selector) {
final member = selector.member;
assertx(member != null);
@ -599,9 +609,9 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
TypeExpr variable = join;
if (useTypeCheck) {
TypeExpr runtimeType = _translator.translate(type);
variable = new TypeCheck(variable, runtimeType, decl);
variable = new TypeCheck(
variable, runtimeType, decl, Type.fromStatic(decl.type));
_summary.add(variable);
_summary.add(new Use(variable));
}
_variables[decl] = variable;
@ -729,15 +739,10 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
TypeExpr visitAsExpression(AsExpression node) {
TypeExpr operand = _visit(node.operand);
Type type = new Type.fromStatic(node.type);
TypeExpr result = _makeNarrow(operand, type);
TypeExpr runtimeType = _translator.translate(node.type);
if (runtimeType is Statement) {
result = new TypeCheck(operand, runtimeType, /*parameter=*/ null);
_summary.add(result);
}
TypeExpr result = new TypeCheck(operand, runtimeType, node, type);
explicitCasts[node] = result;
_summary.add(result);
return result;
}

View file

@ -16,6 +16,7 @@ import 'package:kernel/type_environment.dart';
import 'analysis.dart';
import 'calls.dart';
import 'summary.dart';
import 'summary_collector.dart';
import 'types.dart';
import 'utils.dart';
@ -208,6 +209,8 @@ class AnnotateKernel extends RecursiveVisitor<Null> {
_setInferredType(member, _typeFlowAnalysis.fieldType(member));
} else {
Args<Type> argTypes = _typeFlowAnalysis.argumentTypes(member);
final uncheckedParameters =
_typeFlowAnalysis.uncheckedParameters(member);
assertx(argTypes != null);
final int firstParamIndex =
@ -219,7 +222,8 @@ class AnnotateKernel extends RecursiveVisitor<Null> {
for (int i = 0; i < positionalParams.length; i++) {
_setInferredType(
positionalParams[i], argTypes.values[firstParamIndex + i]);
positionalParams[i], argTypes.values[firstParamIndex + i],
skipCheck: uncheckedParameters.contains(positionalParams[i]));
}
// TODO(dartbug.com/32292): make sure parameters are sorted in kernel
@ -229,7 +233,8 @@ class AnnotateKernel extends RecursiveVisitor<Null> {
final param = findNamedParameter(member.function, names[i]);
assertx(param != null);
_setInferredType(param,
argTypes.values[firstParamIndex + positionalParams.length + i]);
argTypes.values[firstParamIndex + positionalParams.length + i],
skipCheck: uncheckedParameters.contains(param));
}
// TODO(alexmarkov): figure out how to pass receiver type.
@ -484,6 +489,7 @@ class _TreeShakerTypeVisitor extends RecursiveVisitor<Null> {
/// transforms unreachable calls into 'throw' expressions.
class _TreeShakerPass1 extends Transformer {
final TreeShaker shaker;
Procedure _unsafeCast;
_TreeShakerPass1(this.shaker);
@ -813,6 +819,24 @@ class _TreeShakerPass1 extends Transformer {
TreeNode visitAssertInitializer(AssertInitializer node) {
return _visitAssertNode(node);
}
@override
TreeNode visitAsExpression(AsExpression node) {
node.transformChildren(this);
TypeCheck check = shaker.typeFlowAnalysis.explicitCast(node);
if (check != null && check.canAlwaysSkip) {
return StaticInvocation(
unsafeCast, Arguments([node.operand], types: [node.type]));
}
return node;
}
Procedure get unsafeCast {
_unsafeCast ??= shaker.typeFlowAnalysis.environment.coreTypes.index
.getTopLevelMember('dart:_internal', 'unsafeCast');
assertx(_unsafeCast != null);
return _unsafeCast;
}
}
/// The second pass of [TreeShaker]. It is called after set of used

View file

@ -45,6 +45,7 @@ abstract class TypeHierarchy implements GenericInterfacesInfo {
Class get futureOrClass;
Class get futureClass;
Class get functionClass;
}
/// Basic normalization of Dart types.
@ -615,6 +616,15 @@ class ConcreteType extends Type implements Comparable<ConcreteType> {
bool isSubtypeOfRuntimeType(
TypeHierarchy typeHierarchy, RuntimeType runtimeType) {
if (runtimeType._type is InterfaceType &&
(runtimeType._type as InterfaceType).classNode ==
typeHierarchy.functionClass) {
// TODO(35573): "implements/extends Function" is not handled correctly by
// the CFE. By returning "false" we force an approximation -- that a type
// check against "Function" might fail, whatever the LHS is.
return false;
}
if (!typeHierarchy.isSubtype(this.classNode.rawType, runtimeType._type)) {
return false;
}

View file

@ -39,6 +39,8 @@ class TestTypeHierarchy implements TypeHierarchy {
Class get futureOrClass =>
throw "futureOrClass not supported in the types test.";
Class get futureClass => throw "futureClass not supported in the types test.";
Class get functionClass =>
throw "functionClass not supported in the types test.";
}
main() {

View file

@ -6,21 +6,24 @@ RESULT: _T {}?
RESULT: _T {}?
------------ #lib::bool_expressions ------------
t0* = _Call direct [#lib::foo] ()
t1* = _Call direct [#lib::bar] ()
t2* = _Call [dart.core::num::+] (_T (dart.core::int)+?, _T (dart.core::_Smi))
i = _Join [dart.core::int] (_T (dart.core::_Smi), t2)
t4* = _Call [dart.core::num::<] (i, _T (dart.core::_Smi))
t5* = _Call direct [#lib::bar] ()
x = _Join [dart.core::bool] (t5, _T (dart.core::bool)+)
t7* = _Call direct [#lib::foo] ()
t8* = _Call direct [#lib::bar] ()
t9* = _Call direct [#lib::bar] ()
t10* = _Call direct [#lib::foo] ()
t11* = _Call direct [#lib::foo] ()
t12 = _Join [dynamic] (_T (dart.core::bool)+, t7)
t13 = _Narrow (t12 to _T ANY?)
t14 = _Narrow (t13 to _T (dart.core::bool)+?)
y = _Join [dart.core::bool] (_T (dart.core::bool)+, t14, _T (dart.core::bool)+, _T (dart.core::bool)+)
t1 = _TypeCheck (t0 against dart.core::bool) (for #lib::foo() as{TypeError} dart.core::bool)
t2* = _Call direct [#lib::bar] ()
t3* = _Call [dart.core::num::+] (_T (dart.core::int)+?, _T (dart.core::_Smi))
i = _Join [dart.core::int] (_T (dart.core::_Smi), t3)
t5* = _Call [dart.core::num::<] (i, _T (dart.core::_Smi))
t6* = _Call direct [#lib::bar] ()
x = _Join [dart.core::bool] (t6, _T (dart.core::bool)+)
t8* = _Call direct [#lib::foo] ()
t9 = _Join [dynamic] (_T (dart.core::bool)+, t8)
t10 = _Narrow (t9 to _T ANY?)
t11 = _TypeCheck (t10 against dart.core::bool) (for (x{dart.core::bool} ?{dynamic} true : #lib::foo()) as{TypeError} dart.core::bool)
t12* = _Call direct [#lib::bar] ()
t13* = _Call direct [#lib::bar] ()
t14* = _Call direct [#lib::foo] ()
t15 = _TypeCheck (t14 against dart.core::bool) (for #lib::foo() as{TypeError} dart.core::bool)
t16* = _Call direct [#lib::foo] ()
t17 = _TypeCheck (t16 against dart.core::bool) (for #lib::foo() as{TypeError} dart.core::bool)
y = _Join [dart.core::bool] (_T (dart.core::bool)+, t11, _T (dart.core::bool)+, _T (dart.core::bool)+)
RESULT: _T {}?
------------ #lib::main ------------

View file

@ -37,7 +37,7 @@ RESULT: _T {}?
t5 = _Call direct [#lib::B::] (_T (#lib::B))
t6 = _Call [#lib::A::foo1] (%aa, _T (#lib::B))
t7* = _Call get [#lib::A::foo2] (%aa)
t8 = _Narrow (t7 to _T (dart.core::int)+?)
t8 = _TypeCheck (t7 against dart.core::int) (for aa.{#lib::A::foo2} as{TypeError} dart.core::int)
t9 = _Call set [#lib::A::foo3] (%aa, t8)
t10* = _Call get [#lib::A::foo1] (%aa)
t11* = _Call get [#lib::A::foo2] (%aa)

View file

@ -12,13 +12,13 @@ RESULT: t2
%this = _Parameter #0 [_T (#lib::C<dynamic>)+]
%x = _Parameter #1
t2 = _Extract (%this[#lib::C/0])
t3 = _TypeCheck (%x against t2) (for parameter x)
t3 = _TypeCheck (%x against t2) (for x)
RESULT: t3
------------ #lib::C::id2 ------------
%this = _Parameter #0 [_T (#lib::C<dynamic>)+]
%x = _Parameter #1
t2 = _Extract (%this[#lib::C/0])
t3 = _TypeCheck (%x against t2) (for parameter x)
t3 = _TypeCheck (%x against t2) (for x)
RESULT: t3
------------ #lib::D:: ------------
%this = _Parameter #0 [_T (#lib::D<dynamic>)+]
@ -77,7 +77,7 @@ RESULT: _T {}?
%x = _Parameter #1
t2 = _Extract (%this[#lib::C2/0])
t3 = _CreateRuntimeType (dart.core::Comparable @ (t2))
t4 = _TypeCheck (%x against t3) (for parameter x)
t4 = _TypeCheck (%x against t3) (for x)
RESULT: t4
------------ #lib::C2::id4 ------------
%this = _Parameter #0 [_T (#lib::C2<dynamic>)+]
@ -85,7 +85,7 @@ RESULT: t4
t2 = _Extract (%this[#lib::C2/0])
t3 = _CreateRuntimeType (#lib::I @ (t2))
t4 = _CreateRuntimeType (#lib::K @ (t3))
t5 = _TypeCheck (%x against t4) (for parameter x)
t5 = _TypeCheck (%x against t4) (for x)
RESULT: t5
------------ #lib::main ------------
t0 = _Call direct [#lib::C::] (_T (#lib::C<dart.core::int>))

View file

@ -17,9 +17,9 @@ RESULT: _T {}?
%key = _Parameter #1
%value = _Parameter #2
t3 = _Extract (%this[#lib::_NotRealHashMap/0])
t4 = _TypeCheck (%key against t3) (for parameter key)
t4 = _TypeCheck (%key against t3) (for key)
t5 = _Extract (%this[#lib::_NotRealHashMap/1])
t6 = _TypeCheck (%value against t5) (for parameter value)
t6 = _TypeCheck (%value against t5) (for value)
RESULT: _T {}?
------------ #lib::InheritedElement:: ------------
%this = _Parameter #0 [_T (#lib::InheritedElement)+]

View file

@ -1,11 +1,12 @@
library #lib;
import self as self;
import "dart:core" as core;
import "dart:_internal" as _in;
static method isPrime([@vm.inferred-type.metadata=int?] dynamic n) → core::bool {
if([@vm.direct-call.metadata=dart.core::_IntegerImplementation::<??] [@vm.inferred-type.metadata=dart.core::bool] n.<(2) as{TypeError} core::bool)
if(_in::unsafeCast<core::bool>([@vm.direct-call.metadata=dart.core::_IntegerImplementation::<??] [@vm.inferred-type.metadata=dart.core::bool] n.<(2)))
return false;
for (core::int i = 2; [@vm.direct-call.metadata=dart.core::_IntegerImplementation::<=??] [@vm.inferred-type.metadata=dart.core::bool (skip check)] [@vm.direct-call.metadata=dart.core::_IntegerImplementation::*??] [@vm.inferred-type.metadata=int? (skip check)] i.{core::num::*}(i).{core::num::<=}(n as{TypeError} core::num); i = [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+??] [@vm.inferred-type.metadata=int? (skip check)] i.{core::num::+}(1)) {
for (core::int i = 2; [@vm.direct-call.metadata=dart.core::_IntegerImplementation::<=??] [@vm.inferred-type.metadata=dart.core::bool (skip check)] [@vm.direct-call.metadata=dart.core::_IntegerImplementation::*??] [@vm.inferred-type.metadata=int? (skip check)] i.{core::num::*}(i).{core::num::<=}(_in::unsafeCast<core::num>(n)); i = [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+??] [@vm.inferred-type.metadata=int? (skip check)] i.{core::num::+}(1)) {
if([@vm.inferred-type.metadata=dart.core::bool] [@vm.direct-call.metadata=dart.core::_IntegerImplementation::%??] [@vm.inferred-type.metadata=int?] n.%(i).{core::Object::==}(0))
return false;
}

View file

@ -8,7 +8,7 @@ class C<T extends core::Object = dynamic> extends core::Object {
;
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasTearOffUses:false] method foo() → dynamic
return new self::D::•<self::C::T>();
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false] method id1([@vm.inferred-type.metadata=#lib::Y] generic-covariant-impl self::C::T x) → dynamic
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false] method id1([@vm.inferred-type.metadata=#lib::Y (skip check)] generic-covariant-impl self::C::T x) → dynamic
return x;
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false] method id2([@vm.inferred-type.metadata=#lib::Z] generic-covariant-impl self::C::T x) → dynamic
return x;
@ -57,9 +57,9 @@ class C2<T extends core::Object = dynamic> extends core::Object {
synthetic constructor •() → self::C2<self::C2::T>
: super core::Object::•()
;
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false] method id3([@vm.inferred-type.metadata=dart.core::_Double] generic-covariant-impl core::Comparable<self::C2::T> x) → dynamic
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false] method id3([@vm.inferred-type.metadata=dart.core::_Double (skip check)] generic-covariant-impl core::Comparable<self::C2::T> x) → dynamic
return x;
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false] method id4([@vm.inferred-type.metadata=#lib::K<#lib::J>] generic-covariant-impl self::K<self::I<self::C2::T>> x) → dynamic
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false] method id4([@vm.inferred-type.metadata=#lib::K<#lib::J> (skip check)] generic-covariant-impl self::K<self::I<self::C2::T>> x) → dynamic
return x;
}
static method main() → dynamic {

View file

@ -7,14 +7,14 @@ class C<T extends core::Object = dynamic> extends core::Object {
synthetic constructor •() → self::C<self::C::T>
: super core::Object::•()
;
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test2c([@vm.inferred-type.metadata=dart.core::_Smi] generic-covariant-impl asy::FutureOr<self::C::T> x) → void {}
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test3c([@vm.inferred-type.metadata=dart.async::_Future<dart.core::int>] generic-covariant-impl asy::Future<self::C::T> x) → void {}
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test4c([@vm.inferred-type.metadata=dart.async::_Future<dart.core::int>] generic-covariant-impl asy::FutureOr<self::C::T> x) → void {}
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test2r([@vm.inferred-type.metadata=#lib::C<dart.core::int>] generic-covariant-impl self::C<asy::FutureOr<self::C::T>> x) → void {}
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test3r([@vm.inferred-type.metadata=#lib::C<dart.async::Future<dart.core::int>>] generic-covariant-impl self::C<asy::Future<self::C::T>> x) → void {}
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test4r([@vm.inferred-type.metadata=#lib::C<dart.async::Future<dart.core::int>>] generic-covariant-impl self::C<asy::FutureOr<self::C::T>> x) → void {}
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test2c([@vm.inferred-type.metadata=dart.core::_Smi (skip check)] generic-covariant-impl asy::FutureOr<self::C::T> x) → void {}
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test3c([@vm.inferred-type.metadata=dart.async::_Future<dart.core::int> (skip check)] generic-covariant-impl asy::Future<self::C::T> x) → void {}
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test4c([@vm.inferred-type.metadata=dart.async::_Future<dart.core::int> (skip check)] generic-covariant-impl asy::FutureOr<self::C::T> x) → void {}
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test2r([@vm.inferred-type.metadata=#lib::C<dart.core::int> (skip check)] generic-covariant-impl self::C<asy::FutureOr<self::C::T>> x) → void {}
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test3r([@vm.inferred-type.metadata=#lib::C<dart.async::Future<dart.core::int>> (skip check)] generic-covariant-impl self::C<asy::Future<self::C::T>> x) → void {}
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test4r([@vm.inferred-type.metadata=#lib::C<dart.async::Future<dart.core::int>> (skip check)] generic-covariant-impl self::C<asy::FutureOr<self::C::T>> x) → void {}
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test5r([@vm.inferred-type.metadata=#lib::C<dart.async::FutureOr<dart.core::int>>] generic-covariant-impl self::C<asy::Future<self::C::T>> x) → void {}
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test6r([@vm.inferred-type.metadata=#lib::C<dart.async::FutureOr<dart.core::int>>] generic-covariant-impl self::C<asy::FutureOr<self::C::T>> x) → void {}
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test6r([@vm.inferred-type.metadata=#lib::C<dart.async::FutureOr<dart.core::int>> (skip check)] generic-covariant-impl self::C<asy::FutureOr<self::C::T>> x) → void {}
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test7r([@vm.inferred-type.metadata=#lib::C<dart.async::FutureOr<dart.core::int>>] generic-covariant-impl self::C<self::C::T> x) → void {}
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test8r([@vm.inferred-type.metadata=#lib::C<dart.async::Future<dart.core::int>>] generic-covariant-impl self::C<self::C::T> x) → void {}
}

View file

@ -29,7 +29,7 @@ class Q<T extends core::Object = dynamic> extends core::Object {
;
}
static method foo1([@vm.inferred-type.metadata=dart.core::_GrowableList<#lib::T1>] core::List<self::T1> list) → dynamic {
[@vm.direct-call.metadata=#lib::T3::run] [@vm.inferred-type.metadata=!? (skip check)] [@vm.direct-call.metadata=#lib::T1::go??] [@vm.inferred-type.metadata=#lib::T3 (skip check)] [@vm.direct-call.metadata=#lib::Q::result??] [@vm.direct-call.metadata=dart._internal::ListIterable::first] [@vm.direct-call.metadata=dart.collection::_ListBase&Object&ListMixin::map] [@vm.inferred-type.metadata=dart._internal::MappedListIterable<#lib::T1, ?>] list.{core::Iterable::map}<self::Q<self::T1>>((self::T1 t1) → self::Q<self::T1> => new self::Q::•<self::T1>(t1)).{core::Iterable::first}.{self::Q::result}.{self::T1::go}().{self::T3::run}();
[@vm.direct-call.metadata=#lib::T3::run] [@vm.inferred-type.metadata=!? (skip check)] [@vm.direct-call.metadata=#lib::T1::go??] [@vm.inferred-type.metadata=#lib::T3 (skip check)] [@vm.direct-call.metadata=#lib::Q::result??] [@vm.direct-call.metadata=dart._internal::ListIterable::first] [@vm.direct-call.metadata=dart.collection::_ListBase&Object&ListMixin::map] [@vm.inferred-type.metadata=dart._internal::MappedListIterable<#lib::T1, ?> (skip check)] list.{core::Iterable::map}<self::Q<self::T1>>((self::T1 t1) → self::Q<self::T1> => new self::Q::•<self::T1>(t1)).{core::Iterable::first}.{self::Q::result}.{self::T1::go}().{self::T3::run}();
}
static method foo2NewValue() → self::Q<dynamic>
return new self::Q::•<self::T2>(new self::T2::•());

View file

@ -1,6 +1,7 @@
library #lib;
import self as self;
import "dart:core" as core;
import "dart:_internal" as _in;
class T1 extends core::Object {
synthetic constructor •() → self::T1
@ -48,11 +49,11 @@ class B extends self::A {
return new self::T1::•();
}
no-such-method-forwarder get bar() → dynamic
return [@vm.direct-call.metadata=#lib::B::noSuchMethod] [@vm.inferred-type.metadata=#lib::T1 (skip check)] this.{self::B::noSuchMethod}(new core::_InvocationMirror::_withType(#bar, 1, const <core::Type>[], const <dynamic>[], [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol, dynamic>] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}))) as{TypeError} dynamic;
return _in::unsafeCast<dynamic>([@vm.direct-call.metadata=#lib::B::noSuchMethod] [@vm.inferred-type.metadata=#lib::T1 (skip check)] this.{self::B::noSuchMethod}(new core::_InvocationMirror::_withType(#bar, 1, const <core::Type>[], const <dynamic>[], [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol, dynamic>] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}))));
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false] no-such-method-forwarder method foo() → dynamic
return [@vm.direct-call.metadata=#lib::B::noSuchMethod] [@vm.inferred-type.metadata=#lib::T1 (skip check)] this.{self::B::noSuchMethod}(new core::_InvocationMirror::_withType(#foo, 0, const <core::Type>[], const <dynamic>[], [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol, dynamic>] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}))) as{TypeError} dynamic;
return _in::unsafeCast<dynamic>([@vm.direct-call.metadata=#lib::B::noSuchMethod] [@vm.inferred-type.metadata=#lib::T1 (skip check)] this.{self::B::noSuchMethod}(new core::_InvocationMirror::_withType(#foo, 0, const <core::Type>[], const <dynamic>[], [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol, dynamic>] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}))));
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false] no-such-method-forwarder method bazz([@vm.inferred-type.metadata=dart.core::_Smi] dynamic a1, [@vm.inferred-type.metadata=dart.core::_Smi] dynamic a2, [@vm.inferred-type.metadata=dart.core::_Smi] dynamic a3, [[@vm.inferred-type.metadata=dart.core::_Smi] dynamic a4 = null, [@vm.inferred-type.metadata=dart.core::Null?] dynamic a5 = null]) → dynamic
return [@vm.direct-call.metadata=#lib::B::noSuchMethod] [@vm.inferred-type.metadata=#lib::T1 (skip check)] this.{self::B::noSuchMethod}(new core::_InvocationMirror::_withType(#bazz, 0, const <core::Type>[], core::List::unmodifiable<dynamic>(<dynamic>[a1, a2, a3, a4, a5]), [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol, dynamic>] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}))) as{TypeError} dynamic;
return _in::unsafeCast<dynamic>([@vm.direct-call.metadata=#lib::B::noSuchMethod] [@vm.inferred-type.metadata=#lib::T1 (skip check)] this.{self::B::noSuchMethod}(new core::_InvocationMirror::_withType(#bazz, 0, const <core::Type>[], core::List::unmodifiable<dynamic>(<dynamic>[a1, a2, a3, a4, a5]), [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol, dynamic>] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}))));
}
abstract class C extends core::Object {
synthetic constructor •() → self::C
@ -67,11 +68,11 @@ class D extends self::C implements self::A {
: super self::C::•()
;
no-such-method-forwarder get bar() → dynamic
return [@vm.direct-call.metadata=#lib::C::noSuchMethod] [@vm.inferred-type.metadata=#lib::T2 (skip check)] this.{self::C::noSuchMethod}(new core::_InvocationMirror::_withType(#bar, 1, const <core::Type>[], const <dynamic>[], [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol, dynamic>] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}))) as{TypeError} dynamic;
return _in::unsafeCast<dynamic>([@vm.direct-call.metadata=#lib::C::noSuchMethod] [@vm.inferred-type.metadata=#lib::T2 (skip check)] this.{self::C::noSuchMethod}(new core::_InvocationMirror::_withType(#bar, 1, const <core::Type>[], const <dynamic>[], [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol, dynamic>] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}))));
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false] no-such-method-forwarder method foo() → dynamic
return [@vm.direct-call.metadata=#lib::C::noSuchMethod] [@vm.inferred-type.metadata=#lib::T2 (skip check)] this.{self::C::noSuchMethod}(new core::_InvocationMirror::_withType(#foo, 0, const <core::Type>[], const <dynamic>[], [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol, dynamic>] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}))) as{TypeError} dynamic;
return _in::unsafeCast<dynamic>([@vm.direct-call.metadata=#lib::C::noSuchMethod] [@vm.inferred-type.metadata=#lib::T2 (skip check)] this.{self::C::noSuchMethod}(new core::_InvocationMirror::_withType(#foo, 0, const <core::Type>[], const <dynamic>[], [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol, dynamic>] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}))));
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false] no-such-method-forwarder method bazz([@vm.inferred-type.metadata=dart.core::_Smi] dynamic a1, [@vm.inferred-type.metadata=dart.core::_Smi] dynamic a2, [@vm.inferred-type.metadata=dart.core::_Smi] dynamic a3, [[@vm.inferred-type.metadata=dart.core::_Smi] dynamic a4 = null, [@vm.inferred-type.metadata=dart.core::Null?] dynamic a5 = null]) → dynamic
return [@vm.direct-call.metadata=#lib::C::noSuchMethod] [@vm.inferred-type.metadata=#lib::T2 (skip check)] this.{self::C::noSuchMethod}(new core::_InvocationMirror::_withType(#bazz, 0, const <core::Type>[], core::List::unmodifiable<dynamic>(<dynamic>[a1, a2, a3, a4, a5]), [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol, dynamic>] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}))) as{TypeError} dynamic;
return _in::unsafeCast<dynamic>([@vm.direct-call.metadata=#lib::C::noSuchMethod] [@vm.inferred-type.metadata=#lib::T2 (skip check)] this.{self::C::noSuchMethod}(new core::_InvocationMirror::_withType(#bazz, 0, const <core::Type>[], core::List::unmodifiable<dynamic>(<dynamic>[a1, a2, a3, a4, a5]), [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol, dynamic>] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}))));
}
class E extends core::Object implements self::A {
synthetic constructor •() → self::E
@ -81,7 +82,7 @@ class E extends core::Object implements self::A {
return new self::T4::•();
}
no-such-method-forwarder get bar() → dynamic
return [@vm.direct-call.metadata=#lib::E::noSuchMethod] [@vm.inferred-type.metadata=#lib::T4 (skip check)] this.{self::E::noSuchMethod}(new core::_InvocationMirror::_withType(#bar, 1, const <core::Type>[], const <dynamic>[], [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol, dynamic>] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}))) as{TypeError} dynamic;
return _in::unsafeCast<dynamic>([@vm.direct-call.metadata=#lib::E::noSuchMethod] [@vm.inferred-type.metadata=#lib::T4 (skip check)] this.{self::E::noSuchMethod}(new core::_InvocationMirror::_withType(#bar, 1, const <core::Type>[], const <dynamic>[], [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol, dynamic>] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}))));
}
class F extends core::Object {
synthetic constructor •() → self::F

View file

@ -1,6 +1,7 @@
library #lib;
import self as self;
import "dart:core" as core;
import "dart:_internal" as _in;
import "package:expect/expect.dart" as exp;
class T1 extends core::Object {
@ -17,7 +18,7 @@ class A1 extends core::Object {
: super core::Object::•()
;
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method call([dynamic a1 = null, dynamic a2 = null, dynamic a3 = null, [@vm.inferred-type.metadata=dart.core::_Smi?] dynamic a4 = null, [@vm.inferred-type.metadata=#lib::T1?] dynamic a5 = null]) → void {
[@vm.direct-call.metadata=#lib::A1::foo] this.{self::A1::foo} = a5 as{TypeError} self::T1;
[@vm.direct-call.metadata=#lib::A1::foo] this.{self::A1::foo} = _in::unsafeCast<self::T1>(a5);
}
}
class B1 extends core::Object {

View file

@ -1,6 +1,7 @@
library #lib;
import self as self;
import "dart:core" as core;
import "dart:_internal" as _in;
abstract class A extends core::Object {
synthetic constructor •() → self::A
@ -13,7 +14,7 @@ class B extends self::A {
: super self::A::•()
;
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasNonThisUses:false] method foo() → core::int
return [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+] [@vm.inferred-type.metadata=int? (skip check)] 1.{core::num::+}([@vm.direct-call.metadata=#lib::B::bar] [@vm.inferred-type.metadata=dart.core::_Smi] [@vm.inferred-type.metadata=#lib::B] self::knownResult().bar() as{TypeError} core::num) as{TypeError} core::int;
return _in::unsafeCast<core::int>([@vm.direct-call.metadata=dart.core::_IntegerImplementation::+] [@vm.inferred-type.metadata=int? (skip check)] 1.{core::num::+}(_in::unsafeCast<core::num>([@vm.direct-call.metadata=#lib::B::bar] [@vm.inferred-type.metadata=dart.core::_Smi] [@vm.inferred-type.metadata=#lib::B] self::knownResult().bar())));
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method bar() → core::int
return 3;
}

View file

@ -1,6 +1,7 @@
library #lib;
import self as self;
import "dart:core" as core;
import "dart:_internal" as _in;
abstract class A extends core::Object {
synthetic constructor •() → self::A
@ -13,14 +14,14 @@ class B extends self::A {
: super self::A::•()
;
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method foo() → core::int
return [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+] [@vm.inferred-type.metadata=int? (skip check)] 1.{core::num::+}([@vm.direct-call.metadata=#lib::B::foo] [@vm.inferred-type.metadata=int?] [@vm.inferred-type.metadata=#lib::B] self::knownResult().foo() as{TypeError} core::num) as{TypeError} core::int;
return _in::unsafeCast<core::int>([@vm.direct-call.metadata=dart.core::_IntegerImplementation::+] [@vm.inferred-type.metadata=int? (skip check)] 1.{core::num::+}(_in::unsafeCast<core::num>([@vm.direct-call.metadata=#lib::B::foo] [@vm.inferred-type.metadata=int?] [@vm.inferred-type.metadata=#lib::B] self::knownResult().foo())));
}
abstract class Base extends core::Object {
synthetic constructor •() → self::Base
: super core::Object::•()
;
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasNonThisUses:false] method foo() → core::int
return [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+] [@vm.inferred-type.metadata=int? (skip check)] 3.{core::num::+}([@vm.direct-call.metadata=#lib::B::foo] [@vm.inferred-type.metadata=int?] [@vm.inferred-type.metadata=#lib::B] self::knownResult().foo() as{TypeError} core::num) as{TypeError} core::int;
return _in::unsafeCast<core::int>([@vm.direct-call.metadata=dart.core::_IntegerImplementation::+] [@vm.inferred-type.metadata=int? (skip check)] 3.{core::num::+}(_in::unsafeCast<core::num>([@vm.direct-call.metadata=#lib::B::foo] [@vm.inferred-type.metadata=int?] [@vm.inferred-type.metadata=#lib::B] self::knownResult().foo())));
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasNonThisUses:false,hasTearOffUses:false] method doCall(dynamic x) → core::int
return [@vm.call-site-attributes.metadata=receiverType:dynamic] x.call() as{TypeError} core::int;
}

View file

@ -49,6 +49,11 @@ C getC() {
void testOneC(C x, int i) => x.target1(i);
test(List<String> args) {
// Make sure the check on target1.x is not completely eliminated.
if (args.length > 0) {
(C<int>() as C<num>).target1(1.0);
}
expectedEntryPoint = -1;
for (int i = 0; i < 100; ++i) {
testOneC(getC(), i);

View file

@ -1473,6 +1473,13 @@ void ScopeBuilder::AddVariableDeclarationParameter(
variable->set_type_check_mode(LocalVariable::kTypeCheckedByCaller);
break;
}
// TODO(sjindel): We can also skip these checks on dynamic invocations as
// well.
if (parameter_type.IsSkipCheck()) {
variable->set_type_check_mode(LocalVariable::kTypeCheckedByCaller);
}
scope_->InsertParameterAt(pos, variable);
result_->locals.Insert(helper_.data_program_offset_ + kernel_offset,
variable);