[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>
diff --git a/pkg/vm/lib/transformations/type_flow/analysis.dart b/pkg/vm/lib/transformations/type_flow/analysis.dart
index 9db07f3..71d48cb 100644
--- a/pkg/vm/lib/transformations/type_flow/analysis.dart
+++ b/pkg/vm/lib/transformations/type_flow/analysis.dart
@@ -154,7 +154,8 @@
     //
     // 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 get futureOrClass => environment.coreTypes.futureOrClass;
   Class get futureClass => environment.coreTypes.futureClass;
+  Class get functionClass => environment.coreTypes.functionClass;
 }
 
 class _WorkList {
@@ -1395,10 +1397,16 @@
 
   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.
diff --git a/pkg/vm/lib/transformations/type_flow/summary.dart b/pkg/vm/lib/transformations/type_flow/summary.dart
index f914693..01529f7 100644
--- a/pkg/vm/lib/transformations/type_flow/summary.dart
+++ b/pkg/vm/lib/transformations/type_flow/summary.dart
@@ -413,9 +413,30 @@
   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 @@
   @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 @@
     // 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 @@
     }
     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;
+  }
 }
diff --git a/pkg/vm/lib/transformations/type_flow/summary_collector.dart b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
index 483ca7f..af0e734 100644
--- a/pkg/vm/lib/transformations/type_flow/summary_collector.dart
+++ b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
@@ -61,7 +61,7 @@
     }
 
     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 @@
   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 @@
       } 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 @@
       }
 
       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 @@
 
       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 @@
     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 @@
     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 @@
   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;
   }
 
diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart
index 684e96a..837baeb 100644
--- a/pkg/vm/lib/transformations/type_flow/transformer.dart
+++ b/pkg/vm/lib/transformations/type_flow/transformer.dart
@@ -16,6 +16,7 @@
 
 import 'analysis.dart';
 import 'calls.dart';
+import 'summary.dart';
 import 'summary_collector.dart';
 import 'types.dart';
 import 'utils.dart';
@@ -208,6 +209,8 @@
         _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 @@
 
         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 @@
           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 @@
 /// transforms unreachable calls into 'throw' expressions.
 class _TreeShakerPass1 extends Transformer {
   final TreeShaker shaker;
+  Procedure _unsafeCast;
 
   _TreeShakerPass1(this.shaker);
 
@@ -813,6 +819,24 @@
   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
diff --git a/pkg/vm/lib/transformations/type_flow/types.dart b/pkg/vm/lib/transformations/type_flow/types.dart
index cf23ea8..9904fa7 100644
--- a/pkg/vm/lib/transformations/type_flow/types.dart
+++ b/pkg/vm/lib/transformations/type_flow/types.dart
@@ -45,6 +45,7 @@
 
   Class get futureOrClass;
   Class get futureClass;
+  Class get functionClass;
 }
 
 /// Basic normalization of Dart types.
@@ -615,6 +616,15 @@
 
   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;
     }
diff --git a/pkg/vm/test/transformations/type_flow/types_test.dart b/pkg/vm/test/transformations/type_flow/types_test.dart
index 213b6d9..b317843 100644
--- a/pkg/vm/test/transformations/type_flow/types_test.dart
+++ b/pkg/vm/test/transformations/type_flow/types_test.dart
@@ -39,6 +39,8 @@
   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() {
diff --git a/pkg/vm/testcases/transformations/type_flow/summary_collector/bool_expressions.dart.expect b/pkg/vm/testcases/transformations/type_flow/summary_collector/bool_expressions.dart.expect
index d4deb68..bf7dfc6 100644
--- a/pkg/vm/testcases/transformations/type_flow/summary_collector/bool_expressions.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/summary_collector/bool_expressions.dart.expect
@@ -6,21 +6,24 @@
 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 ------------
 
diff --git a/pkg/vm/testcases/transformations/type_flow/summary_collector/calls.dart.expect b/pkg/vm/testcases/transformations/type_flow/summary_collector/calls.dart.expect
index e5c30ed..9843dc5 100644
--- a/pkg/vm/testcases/transformations/type_flow/summary_collector/calls.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/summary_collector/calls.dart.expect
@@ -37,7 +37,7 @@
 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)
diff --git a/pkg/vm/testcases/transformations/type_flow/summary_collector/class_generics_basic.dart.expect b/pkg/vm/testcases/transformations/type_flow/summary_collector/class_generics_basic.dart.expect
index dcdc1f2..0163697 100644
--- a/pkg/vm/testcases/transformations/type_flow/summary_collector/class_generics_basic.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/summary_collector/class_generics_basic.dart.expect
@@ -12,13 +12,13 @@
 %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 @@
 %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 @@
 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>))
diff --git a/pkg/vm/testcases/transformations/type_flow/summary_collector/class_generics_case1.dart.expect b/pkg/vm/testcases/transformations/type_flow/summary_collector/class_generics_case1.dart.expect
index f82c3ab..2095424 100644
--- a/pkg/vm/testcases/transformations/type_flow/summary_collector/class_generics_case1.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/summary_collector/class_generics_case1.dart.expect
@@ -17,9 +17,9 @@
 %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)+]
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/bench_is_prime.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/bench_is_prime.dart.expect
index e31b453..ffdd14f 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/bench_is_prime.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/bench_is_prime.dart.expect
@@ -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;
   }
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/class_generics_basic.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/class_generics_basic.dart.expect
index 3da6373..4acf3a3 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/class_generics_basic.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/class_generics_basic.dart.expect
@@ -8,7 +8,7 @@
     ;
 [@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 @@
   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 {
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/future.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/future.dart.expect
index 1a1fd6f..3998132 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/future.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/future.dart.expect
@@ -7,14 +7,14 @@
   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 {}
 }
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_set_field2.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_set_field2.dart.expect
index 761f166..925d3df 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_set_field2.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_set_field2.dart.expect
@@ -29,7 +29,7 @@
     ;
 }
 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::•());
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/no_such_method.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/no_such_method.dart.expect
index 54a1cd8..5c65020 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/no_such_method.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/no_such_method.dart.expect
@@ -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 @@
     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 @@
     : 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 @@
     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
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter16182.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter16182.dart.expect
index d0dc0ad..feb28eb 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter16182.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter16182.dart.expect
@@ -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 @@
     : 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 {
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_interface_method.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_interface_method.dart.expect
index c05b7ff..66af620 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_interface_method.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_interface_method.dart.expect
@@ -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 @@
     : 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;
 }
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_super_method.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_super_method.dart.expect
index aa8ce21..950a81d 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_super_method.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_super_method.dart.expect
@@ -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 @@
     : 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;
 }
diff --git a/runtime/tests/vm/dart/entrypoints/super.dart b/runtime/tests/vm/dart/entrypoints/super.dart
index 352c338..088b193 100644
--- a/runtime/tests/vm/dart/entrypoints/super.dart
+++ b/runtime/tests/vm/dart/entrypoints/super.dart
@@ -49,6 +49,11 @@
 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);
diff --git a/runtime/vm/compiler/frontend/scope_builder.cc b/runtime/vm/compiler/frontend/scope_builder.cc
index 6811b6c..201c724 100644
--- a/runtime/vm/compiler/frontend/scope_builder.cc
+++ b/runtime/vm/compiler/frontend/scope_builder.cc
@@ -1473,6 +1473,13 @@
       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);