Version 2.12.0-58.0.dev

Merge commit 'a157ddbf94ee8905197da14f90aac6e1c17713fd' into 'dev'
diff --git a/pkg/dartdev/test/no_such_file_test.dart b/pkg/dartdev/test/no_such_file_test.dart
index 1641dfc..1d197d7 100644
--- a/pkg/dartdev/test/no_such_file_test.dart
+++ b/pkg/dartdev/test/no_such_file_test.dart
@@ -31,8 +31,7 @@
     p = project();
     final result = p.runSync('--snapshot=abc', ['foo.dart']);
     expect(result.stderr, isNotEmpty);
-    expect(result.stderr,
-        contains("Error when reading 'foo.dart': No such file or directory"));
+    expect(result.stderr, contains("Error when reading 'foo.dart':"));
     expect(result.stdout, isEmpty);
     expect(result.exitCode, 254);
   });
diff --git a/pkg/dds/CHANGELOG.md b/pkg/dds/CHANGELOG.md
index 54979eb3..953ac0b 100644
--- a/pkg/dds/CHANGELOG.md
+++ b/pkg/dds/CHANGELOG.md
@@ -1,3 +1,7 @@
+# 1.6.0
+- Added `errorCode` to `DartDevelopmentServiceException` to communicate the
+  underlying reason of the failure.
+
 # 1.5.1
 - Improve internal error handling for situations with less than graceful
   shutdowns.
diff --git a/pkg/dds/lib/dds.dart b/pkg/dds/lib/dds.dart
index 0f716c6..29cc288 100644
--- a/pkg/dds/lib/dds.dart
+++ b/pkg/dds/lib/dds.dart
@@ -159,9 +159,36 @@
 }
 
 class DartDevelopmentServiceException implements Exception {
-  DartDevelopmentServiceException._(this.message);
+  /// Set when `DartDeveloperService.startDartDevelopmentService` is called and
+  /// the target VM service already has a Dart Developer Service instance
+  /// connected.
+  static const int existingDdsInstanceError = 1;
+
+  /// Set when the connection to the remote VM service terminates unexpectedly
+  /// during Dart Development Service startup.
+  static const int failedToStartError = 2;
+
+  /// Set when a connection error has occurred after startup.
+  static const int connectionError = 3;
+
+  factory DartDevelopmentServiceException._existingDdsInstanceError(
+      String message) {
+    return DartDevelopmentServiceException._(existingDdsInstanceError, message);
+  }
+
+  factory DartDevelopmentServiceException._failedToStartError() {
+    return DartDevelopmentServiceException._(
+        failedToStartError, 'Failed to start Dart Development Service');
+  }
+
+  factory DartDevelopmentServiceException._connectionError(String message) {
+    return DartDevelopmentServiceException._(connectionError, message);
+  }
+
+  DartDevelopmentServiceException._(this.errorCode, this.message);
 
   String toString() => 'DartDevelopmentServiceException: $message';
 
+  final int errorCode;
   final String message;
 }
diff --git a/pkg/dds/lib/src/dds_impl.dart b/pkg/dds/lib/src/dds_impl.dart
index fbb6bba..ad0e46f 100644
--- a/pkg/dds/lib/src/dds_impl.dart
+++ b/pkg/dds/lib/src/dds_impl.dart
@@ -49,17 +49,14 @@
           shutdown();
           if (!started && !completer.isCompleted) {
             completer.completeError(
-              DartDevelopmentServiceException._(
-                'Failed to start Dart Development Service',
-              ),
-            );
+                DartDevelopmentServiceException._failedToStartError());
           }
         },
         onError: (e, st) {
           shutdown();
           if (!completer.isCompleted) {
             completer.completeError(
-              DartDevelopmentServiceException._(e.toString()),
+              DartDevelopmentServiceException._connectionError(e.toString()),
               st,
             );
           }
@@ -118,7 +115,7 @@
     } on json_rpc.RpcException catch (e) {
       await _server.close(force: true);
       // _yieldControlToDDS fails if DDS is not the only VM service client.
-      throw DartDevelopmentServiceException._(
+      throw DartDevelopmentServiceException._existingDdsInstanceError(
         e.data != null ? e.data['details'] : e.toString(),
       );
     }
diff --git a/pkg/dds/pubspec.yaml b/pkg/dds/pubspec.yaml
index a04a63c..d4132a1 100644
--- a/pkg/dds/pubspec.yaml
+++ b/pkg/dds/pubspec.yaml
@@ -3,7 +3,7 @@
   A library used to spawn the Dart Developer Service, used to communicate with
   a Dart VM Service instance.
 
-version: 1.5.1
+version: 1.6.0
 
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/dds
 
diff --git a/pkg/dds/test/handles_connection_closed_before_full_header.dart b/pkg/dds/test/handles_connection_closed_before_full_header.dart
index 65d6436..80f1744 100644
--- a/pkg/dds/test/handles_connection_closed_before_full_header.dart
+++ b/pkg/dds/test/handles_connection_closed_before_full_header.dart
@@ -30,6 +30,7 @@
       await DartDevelopmentService.startDartDevelopmentService(uri);
       fail('Unexpected successful connection.');
     } on DartDevelopmentServiceException catch (e) {
+      expect(e.errorCode, DartDevelopmentServiceException.connectionError);
       expect(e.toString().contains('WebSocketChannelException'), true);
     }
   });
diff --git a/pkg/dds/test/smoke_test.dart b/pkg/dds/test/smoke_test.dart
index 0a7347c..d317893 100644
--- a/pkg/dds/test/smoke_test.dart
+++ b/pkg/dds/test/smoke_test.dart
@@ -107,6 +107,8 @@
       } on DartDevelopmentServiceException catch (e) {
         expect(e.message,
             'Existing VM service clients prevent DDS from taking control.');
+        expect(e.errorCode,
+            DartDevelopmentServiceException.existingDdsInstanceError);
       }
     });
   });
diff --git a/pkg/expect/lib/expect.dart b/pkg/expect/lib/expect.dart
index 3b7a2e5..47ada71 100644
--- a/pkg/expect/lib/expect.dart
+++ b/pkg/expect/lib/expect.dart
@@ -440,6 +440,17 @@
     _fail("$defaultMessage$diff");
   }
 
+  /// Checks that [haystack] contains a given substring [needle].
+  ///
+  /// For example, this succeeds:
+  ///
+  ///     Expect.contains("a", "abcdefg");
+  static void contains(String needle, String haystack) {
+    if (!haystack.contains(needle)) {
+      _fail("String '$needle' not found within '$haystack'");
+    }
+  }
+
   /// Checks that [actual] contains a given list of [substrings] in order.
   ///
   /// For example, this succeeds:
diff --git a/runtime/tests/vm/dart/causal_stacks/async_throws_stack_lazy_non_symbolic_test.dart b/runtime/tests/vm/dart/causal_stacks/async_throws_stack_lazy_non_symbolic_test.dart
index 11c1a33..2ec438e 100644
--- a/runtime/tests/vm/dart/causal_stacks/async_throws_stack_lazy_non_symbolic_test.dart
+++ b/runtime/tests/vm/dart/causal_stacks/async_throws_stack_lazy_non_symbolic_test.dart
@@ -2,7 +2,7 @@
 // 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.
 
-// VMOptions=--dwarf-stack-traces --save-debugging-info=async_lazy_debug.so --lazy-async-stacks --no-causal-async-stacks
+// VMOptions=--dwarf-stack-traces --save-debugging-info=async_lazy_debug.so --lazy-async-stacks --no-causal-async-stacks --no-use-bare-instructions
 
 import 'dart:async';
 import 'dart:io';
diff --git a/runtime/tests/vm/dart/causal_stacks/utils.dart b/runtime/tests/vm/dart/causal_stacks/utils.dart
index ed1549c..a0fbb82 100644
--- a/runtime/tests/vm/dart/causal_stacks/utils.dart
+++ b/runtime/tests/vm/dart/causal_stacks/utils.dart
@@ -1355,7 +1355,7 @@
   final awaitTimeoutExpected = const <String>[
     r'^#0      throwAsync \(.*/utils.dart:21(:3)?\)$',
     r'^<asynchronous suspension>$',
-    r'^#1      Future.timeout.<anonymous closure> \(dart:async/future_impl.dart\)$',
+    r'^#1      Future.timeout.<anonymous closure> \(dart:async/future_impl.dart',
     r'^<asynchronous suspension>$',
     r'^#2      awaitTimeout ',
     r'^<asynchronous suspension>$',
@@ -1386,7 +1386,7 @@
   final awaitWaitExpected = const <String>[
     r'^#0      throwAsync \(.*/utils.dart:21(:3)?\)$',
     r'^<asynchronous suspension>$',
-    r'^#1      Future.wait.<anonymous closure> \(dart:async/future.dart\)$',
+    r'^#1      Future.wait.<anonymous closure> \(dart:async/future.dart',
     r'^<asynchronous suspension>$',
     r'^#2      awaitWait ',
     r'^<asynchronous suspension>$',
diff --git a/runtime/tests/vm/dart/entrypoints/tearoff_prologue_test.dart b/runtime/tests/vm/dart/entrypoints/tearoff_prologue_test.dart
deleted file mode 100644
index 2cf7278..0000000
--- a/runtime/tests/vm/dart/entrypoints/tearoff_prologue_test.dart
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (c) 2016, 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.
-//
-// No type checks are removed here, but we can skip the argument count check.
-// VMOptions=--enable-testing-pragmas --no-background-compilation --optimization-counter-threshold=10
-// VMOptions=--enable-testing-pragmas --no-background-compilation --optimization-counter-threshold=10 -Denable_inlining=true
-// VMOptions=--enable-testing-pragmas --no-background-compilation --optimization-counter-threshold=-1
-
-import "package:expect/expect.dart";
-import "common.dart";
-
-class C<T> {
-  @NeverInline
-  @pragma("vm:testing.unsafe.trace-entrypoints-fn", validateTearoff)
-  @pragma("vm:entry-point")
-  void samir1(T x) {
-    if (x == -1) {
-      throw "oh no";
-    }
-  }
-}
-
-void run(void Function(int) test, int i) {
-  test(i);
-}
-
-main(List<String> args) {
-  var c = new C<int>();
-  var f = c.samir1;
-
-  const int iterations = benchmarkMode ? 100000000 : 100;
-  for (int i = 0; i < iterations; ++i) {
-    run(f, i);
-  }
-
-  entryPoint.expectChecked(iterations);
-  tearoffEntryPoint.expectUnchecked(iterations);
-}
diff --git a/runtime/tests/vm/dart/entrypoints/tearoff_test.dart b/runtime/tests/vm/dart/entrypoints/tearoff_test.dart
deleted file mode 100644
index d0bd88a..0000000
--- a/runtime/tests/vm/dart/entrypoints/tearoff_test.dart
+++ /dev/null
@@ -1,38 +0,0 @@
-// 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.
-//
-// VMOptions=--enable-testing-pragmas --no-background-compilation --optimization-counter-threshold=10
-// VMOptions=--enable-testing-pragmas --no-background-compilation --optimization-counter-threshold=10 -Denable_inlining=true
-// VMOptions=--enable-testing-pragmas --no-background-compilation --optimization-counter-threshold=-1
-
-// Test that typed calls against tearoffs go into the unchecked entrypoint.
-
-import "package:expect/expect.dart";
-import "common.dart";
-
-class C<T> {
-  @NeverInline
-  @pragma("vm:testing.unsafe.trace-entrypoints-fn", validateTearoff)
-  @pragma("vm:entry-point")
-  void target1(T x, String y) {
-    Expect.notEquals(x, -1);
-    Expect.equals(y, "foo");
-  }
-}
-
-void run(void Function(int, String) fn, int i) {
-  fn(i, "foo");
-}
-
-main(List<String> args) {
-  var f = (new C<int>()).target1;
-
-  const int iterations = benchmarkMode ? 100000000 : 100;
-  for (int i = 0; i < iterations; ++i) {
-    run(f, i);
-  }
-
-  entryPoint.expectChecked(iterations);
-  tearoffEntryPoint.expectUnchecked(iterations);
-}
diff --git a/runtime/tests/vm/dart_2/causal_stacks/async_throws_stack_lazy_non_symbolic_test.dart b/runtime/tests/vm/dart_2/causal_stacks/async_throws_stack_lazy_non_symbolic_test.dart
index 11c1a33..2ec438e 100644
--- a/runtime/tests/vm/dart_2/causal_stacks/async_throws_stack_lazy_non_symbolic_test.dart
+++ b/runtime/tests/vm/dart_2/causal_stacks/async_throws_stack_lazy_non_symbolic_test.dart
@@ -2,7 +2,7 @@
 // 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.
 
-// VMOptions=--dwarf-stack-traces --save-debugging-info=async_lazy_debug.so --lazy-async-stacks --no-causal-async-stacks
+// VMOptions=--dwarf-stack-traces --save-debugging-info=async_lazy_debug.so --lazy-async-stacks --no-causal-async-stacks --no-use-bare-instructions
 
 import 'dart:async';
 import 'dart:io';
diff --git a/runtime/tests/vm/dart_2/causal_stacks/utils.dart b/runtime/tests/vm/dart_2/causal_stacks/utils.dart
index a67a475..3b6facb 100644
--- a/runtime/tests/vm/dart_2/causal_stacks/utils.dart
+++ b/runtime/tests/vm/dart_2/causal_stacks/utils.dart
@@ -1355,7 +1355,7 @@
   final awaitTimeoutExpected = const <String>[
     r'^#0      throwAsync \(.*/utils.dart:21(:3)?\)$',
     r'^<asynchronous suspension>$',
-    r'^#1      Future.timeout.<anonymous closure> \(dart:async/future_impl.dart\)$',
+    r'^#1      Future.timeout.<anonymous closure> \(dart:async/future_impl.dart',
     r'^<asynchronous suspension>$',
     r'^#2      awaitTimeout ',
     r'^<asynchronous suspension>$',
@@ -1386,7 +1386,7 @@
   final awaitWaitExpected = const <String>[
     r'^#0      throwAsync \(.*/utils.dart:21(:3)?\)$',
     r'^<asynchronous suspension>$',
-    r'^#1      Future.wait.<anonymous closure> \(dart:async/future.dart\)$',
+    r'^#1      Future.wait.<anonymous closure> \(dart:async/future.dart',
     r'^<asynchronous suspension>$',
     r'^#2      awaitWait ',
     r'^<asynchronous suspension>$',
diff --git a/runtime/tests/vm/dart_2/entrypoints/tearoff_prologue_test.dart b/runtime/tests/vm/dart_2/entrypoints/tearoff_prologue_test.dart
deleted file mode 100644
index 2cf7278..0000000
--- a/runtime/tests/vm/dart_2/entrypoints/tearoff_prologue_test.dart
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (c) 2016, 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.
-//
-// No type checks are removed here, but we can skip the argument count check.
-// VMOptions=--enable-testing-pragmas --no-background-compilation --optimization-counter-threshold=10
-// VMOptions=--enable-testing-pragmas --no-background-compilation --optimization-counter-threshold=10 -Denable_inlining=true
-// VMOptions=--enable-testing-pragmas --no-background-compilation --optimization-counter-threshold=-1
-
-import "package:expect/expect.dart";
-import "common.dart";
-
-class C<T> {
-  @NeverInline
-  @pragma("vm:testing.unsafe.trace-entrypoints-fn", validateTearoff)
-  @pragma("vm:entry-point")
-  void samir1(T x) {
-    if (x == -1) {
-      throw "oh no";
-    }
-  }
-}
-
-void run(void Function(int) test, int i) {
-  test(i);
-}
-
-main(List<String> args) {
-  var c = new C<int>();
-  var f = c.samir1;
-
-  const int iterations = benchmarkMode ? 100000000 : 100;
-  for (int i = 0; i < iterations; ++i) {
-    run(f, i);
-  }
-
-  entryPoint.expectChecked(iterations);
-  tearoffEntryPoint.expectUnchecked(iterations);
-}
diff --git a/runtime/tests/vm/dart_2/entrypoints/tearoff_test.dart b/runtime/tests/vm/dart_2/entrypoints/tearoff_test.dart
deleted file mode 100644
index d0bd88a..0000000
--- a/runtime/tests/vm/dart_2/entrypoints/tearoff_test.dart
+++ /dev/null
@@ -1,38 +0,0 @@
-// 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.
-//
-// VMOptions=--enable-testing-pragmas --no-background-compilation --optimization-counter-threshold=10
-// VMOptions=--enable-testing-pragmas --no-background-compilation --optimization-counter-threshold=10 -Denable_inlining=true
-// VMOptions=--enable-testing-pragmas --no-background-compilation --optimization-counter-threshold=-1
-
-// Test that typed calls against tearoffs go into the unchecked entrypoint.
-
-import "package:expect/expect.dart";
-import "common.dart";
-
-class C<T> {
-  @NeverInline
-  @pragma("vm:testing.unsafe.trace-entrypoints-fn", validateTearoff)
-  @pragma("vm:entry-point")
-  void target1(T x, String y) {
-    Expect.notEquals(x, -1);
-    Expect.equals(y, "foo");
-  }
-}
-
-void run(void Function(int, String) fn, int i) {
-  fn(i, "foo");
-}
-
-main(List<String> args) {
-  var f = (new C<int>()).target1;
-
-  const int iterations = benchmarkMode ? 100000000 : 100;
-  for (int i = 0; i < iterations; ++i) {
-    run(f, i);
-  }
-
-  entryPoint.expectChecked(iterations);
-  tearoffEntryPoint.expectUnchecked(iterations);
-}
diff --git a/runtime/vm/compiler/assembler/assembler_arm.h b/runtime/vm/compiler/assembler/assembler_arm.h
index daeb5f7..f853da7 100644
--- a/runtime/vm/compiler/assembler/assembler_arm.h
+++ b/runtime/vm/compiler/assembler/assembler_arm.h
@@ -1023,6 +1023,12 @@
                 JumpDistance distance = kFarJump) {
     b(label, condition);
   }
+  void BranchIfZero(Register rn,
+                    Label* label,
+                    JumpDistance distance = kFarJump) {
+    cmp(rn, Operand(0));
+    b(label, ZERO);
+  }
 
   void MoveRegister(Register rd, Register rm, Condition cond = AL);
 
diff --git a/runtime/vm/compiler/assembler/assembler_arm64.h b/runtime/vm/compiler/assembler/assembler_arm64.h
index 2c85181..3290499 100644
--- a/runtime/vm/compiler/assembler/assembler_arm64.h
+++ b/runtime/vm/compiler/assembler/assembler_arm64.h
@@ -1109,6 +1109,11 @@
                 JumpDistance distance = kFarJump) {
     b(label, condition);
   }
+  void BranchIfZero(Register rn,
+                    Label* label,
+                    JumpDistance distance = kFarJump) {
+    cbz(label, rn);
+  }
 
   void cbz(Label* label, Register rt, OperandSize sz = kEightBytes) {
     EmitCompareAndBranch(CBZ, rt, label, sz);
diff --git a/runtime/vm/compiler/assembler/assembler_ia32.cc b/runtime/vm/compiler/assembler/assembler_ia32.cc
index 521f53f..d07c40f 100644
--- a/runtime/vm/compiler/assembler/assembler_ia32.cc
+++ b/runtime/vm/compiler/assembler/assembler_ia32.cc
@@ -1784,10 +1784,12 @@
 }
 
 void Assembler::LoadFromStack(Register dst, intptr_t depth) {
+  ASSERT(depth >= 0);
   movl(dst, Address(ESP, depth * target::kWordSize));
 }
 
 void Assembler::StoreToStack(Register src, intptr_t depth) {
+  ASSERT(depth >= 0);
   movl(Address(ESP, depth * target::kWordSize), src);
 }
 
diff --git a/runtime/vm/compiler/assembler/assembler_ia32.h b/runtime/vm/compiler/assembler/assembler_ia32.h
index 0c6ed99..dfa7f35 100644
--- a/runtime/vm/compiler/assembler/assembler_ia32.h
+++ b/runtime/vm/compiler/assembler/assembler_ia32.h
@@ -577,6 +577,12 @@
                 JumpDistance distance = kFarJump) {
     j(condition, label, distance);
   }
+  void BranchIfZero(Register src,
+                    Label* label,
+                    JumpDistance distance = kFarJump) {
+    cmpl(src, Immediate(0));
+    j(ZERO, label, distance);
+  }
 
   void LoadFromOffset(Register reg,
                       Register base,
@@ -716,6 +722,11 @@
     cmpxchgl(address, reg);
   }
 
+  void CompareTypeNullabilityWith(Register type, int8_t value) {
+    cmpb(FieldAddress(type, compiler::target::Type::nullability_offset()),
+         Immediate(value));
+  }
+
   void EnterFrame(intptr_t frame_space);
   void LeaveFrame();
   void ReserveAlignedFrameSpace(intptr_t frame_space);
diff --git a/runtime/vm/compiler/assembler/assembler_x64.h b/runtime/vm/compiler/assembler/assembler_x64.h
index 01ed17b..3b5fa5c 100644
--- a/runtime/vm/compiler/assembler/assembler_x64.h
+++ b/runtime/vm/compiler/assembler/assembler_x64.h
@@ -683,6 +683,12 @@
                 JumpDistance distance = kFarJump) {
     j(condition, label, distance);
   }
+  void BranchIfZero(Register src,
+                    Label* label,
+                    JumpDistance distance = kFarJump) {
+    cmpq(src, Immediate(0));
+    j(ZERO, label, distance);
+  }
 
   // Issues a move instruction if 'to' is not the same as 'from'.
   void MoveRegister(Register to, Register from);
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.cc b/runtime/vm/compiler/backend/flow_graph_compiler.cc
index 4902136..cf20201 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.cc
@@ -2286,15 +2286,22 @@
 
 bool FlowGraphCompiler::CheckAssertAssignableTypeTestingABILocations(
     const LocationSummary& locs) {
-  ASSERT(locs.in(0).IsRegister() &&
-         locs.in(0).reg() == TypeTestABI::kInstanceReg);
-  ASSERT((locs.in(1).IsConstant() && locs.in(1).constant().IsAbstractType()) ||
-         (locs.in(1).IsRegister() &&
-          locs.in(1).reg() == TypeTestABI::kDstTypeReg));
-  ASSERT(locs.in(2).IsRegister() &&
-         locs.in(2).reg() == TypeTestABI::kInstantiatorTypeArgumentsReg);
-  ASSERT(locs.in(3).IsRegister() &&
-         locs.in(3).reg() == TypeTestABI::kFunctionTypeArgumentsReg);
+  ASSERT(locs.in(AssertAssignableInstr::kInstancePos).IsRegister() &&
+         locs.in(AssertAssignableInstr::kInstancePos).reg() ==
+             TypeTestABI::kInstanceReg);
+  ASSERT((locs.in(AssertAssignableInstr::kDstTypePos).IsConstant() &&
+          locs.in(AssertAssignableInstr::kDstTypePos)
+              .constant()
+              .IsAbstractType()) ||
+         (locs.in(AssertAssignableInstr::kDstTypePos).IsRegister() &&
+          locs.in(AssertAssignableInstr::kDstTypePos).reg() ==
+              TypeTestABI::kDstTypeReg));
+  ASSERT(locs.in(AssertAssignableInstr::kInstantiatorTAVPos).IsRegister() &&
+         locs.in(AssertAssignableInstr::kInstantiatorTAVPos).reg() ==
+             TypeTestABI::kInstantiatorTypeArgumentsReg);
+  ASSERT(locs.in(AssertAssignableInstr::kFunctionTAVPos).IsRegister() &&
+         locs.in(AssertAssignableInstr::kFunctionTAVPos).reg() ==
+             TypeTestABI::kFunctionTypeArgumentsReg);
   ASSERT(locs.out(0).IsRegister() &&
          locs.out(0).reg() == TypeTestABI::kInstanceReg);
   return true;
@@ -2760,23 +2767,49 @@
   ASSERT(!token_pos.IsClassifying());
   ASSERT(CheckAssertAssignableTypeTestingABILocations(*locs));
 
-  if (!locs->in(1).IsConstant()) {
-    // TODO(dartbug.com/40813): Handle setting up the non-constant case.
-    UNREACHABLE();
-  }
-  const auto& dst_type = AbstractType::Cast(locs->in(1).constant());
-  ASSERT(dst_type.IsFinalized());
+  // Non-null if we have a constant destination type.
+  const auto& dst_type =
+      locs->in(AssertAssignableInstr::kDstTypePos).IsConstant()
+          ? AbstractType::Cast(
+                locs->in(AssertAssignableInstr::kDstTypePos).constant())
+          : Object::null_abstract_type();
 
-  if (dst_type.IsTopTypeForSubtyping()) return;  // No code needed.
+  if (!dst_type.IsNull()) {
+    ASSERT(dst_type.IsFinalized());
+    if (dst_type.IsTopTypeForSubtyping()) return;  // No code needed.
+  }
 
   compiler::Label done;
+  Register type_reg = TypeTestABI::kDstTypeReg;
+  // Generate caller-side checks to perform prior to calling the TTS.
+  if (dst_type.IsNull()) {
+    __ Comment("AssertAssignable for runtime type");
+    // kDstTypeReg should already contain the destination type.
+    const bool null_safety = isolate()->null_safety();
+    GenerateStubCall(token_pos,
+                     StubCode::GetTypeIsTopTypeForSubtyping(null_safety),
+                     PcDescriptorsLayout::kOther, locs, deopt_id);
+    // TypeTestABI::kSubtypeTestCacheReg is 0 if the type is a top type.
+    __ BranchIfZero(TypeTestABI::kSubtypeTestCacheReg, &done,
+                    compiler::Assembler::kNearJump);
 
-  GenerateCallerChecksForAssertAssignable(receiver_type, dst_type, &done);
+    GenerateStubCall(token_pos,
+                     StubCode::GetNullIsAssignableToType(null_safety),
+                     PcDescriptorsLayout::kOther, locs, deopt_id);
+    // TypeTestABI::kSubtypeTestCacheReg is 0 if the object is null and is
+    // assignable.
+    __ BranchIfZero(TypeTestABI::kSubtypeTestCacheReg, &done,
+                    compiler::Assembler::kNearJump);
+  } else {
+    __ Comment("AssertAssignable for compile-time type");
+    GenerateCallerChecksForAssertAssignable(receiver_type, dst_type, &done);
+    if (dst_type.IsTypeParameter()) {
+      // The resolved type parameter is in the scratch register.
+      type_reg = TypeTestABI::kScratchReg;
+    }
+  }
 
-  GenerateTTSCall(token_pos, deopt_id,
-                  dst_type.IsTypeParameter() ? TypeTestABI::kScratchReg
-                                             : TypeTestABI::kDstTypeReg,
-                  dst_type, dst_name, locs);
+  GenerateTTSCall(token_pos, deopt_id, type_reg, dst_type, dst_name, locs);
   __ Bind(&done);
 }
 
@@ -2789,8 +2822,7 @@
                                         const AbstractType& dst_type,
                                         const String& dst_name,
                                         LocationSummary* locs) {
-  // For now, we don't allow dynamic (non-compile-time) dst_type/dst_name.
-  ASSERT(!dst_type.IsNull() && !dst_name.IsNull());
+  ASSERT(!dst_name.IsNull());
   // We use 2 consecutive entries in the pool for the subtype cache and the
   // destination name.  The second entry, namely [dst_name] seems to be unused,
   // but it will be used by the code throwing a TypeError if the type test fails
@@ -2804,9 +2836,11 @@
   ASSERT((sub_type_cache_index + 1) == dst_name_index);
   ASSERT(__ constant_pool_allowed());
 
+  __ Comment("TTSCall");
   // If the dst_type is known at compile time and instantiated, we know the
   // target TTS stub and so can use a PC-relative call when available.
-  if (dst_type.IsInstantiated() && CanPcRelativeCall(dst_type)) {
+  if (!dst_type.IsNull() && dst_type.IsInstantiated() &&
+      CanPcRelativeCall(dst_type)) {
     __ LoadWordFromPoolIndex(TypeTestABI::kSubtypeTestCacheReg,
                              sub_type_cache_index);
     __ GenerateUnRelocatedPcRelativeCall();
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc b/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
index c03a43a..6b69099 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
@@ -342,38 +342,62 @@
   ASSERT(!token_pos.IsClassifying());
   ASSERT(CheckAssertAssignableTypeTestingABILocations(*locs));
 
-  if (!locs->in(1).IsConstant()) {
-    // TODO(dartbug.com/40813): Handle setting up the non-constant case.
-    UNREACHABLE();
+  const auto& dst_type =
+      locs->in(AssertAssignableInstr::kDstTypePos).IsConstant()
+          ? AbstractType::Cast(
+                locs->in(AssertAssignableInstr::kDstTypePos).constant())
+          : Object::null_abstract_type();
+
+  if (!dst_type.IsNull()) {
+    ASSERT(dst_type.IsFinalized());
+    if (dst_type.IsTopTypeForSubtyping()) return;  // No code needed.
   }
 
-  ASSERT(locs->in(1).constant().IsAbstractType());
-  const auto& dst_type = AbstractType::Cast(locs->in(1).constant());
-  ASSERT(dst_type.IsFinalized());
-
-  if (dst_type.IsTopTypeForSubtyping()) return;  // No code needed.
-
   compiler::Label is_assignable, runtime_call;
-  if (Instance::NullIsAssignableTo(dst_type)) {
-    const compiler::Immediate& raw_null =
-        compiler::Immediate(static_cast<intptr_t>(Object::null()));
-    __ cmpl(TypeTestABI::kInstanceReg, raw_null);
-    __ j(EQUAL, &is_assignable);
-  }
+  auto& test_cache = SubtypeTestCache::ZoneHandle(zone());
+  if (dst_type.IsNull()) {
+    __ Comment("AssertAssignable for runtime type");
+    // kDstTypeReg should already contain the destination type.
+    const bool null_safety = Isolate::Current()->null_safety();
+    GenerateStubCall(token_pos,
+                     StubCode::GetTypeIsTopTypeForSubtyping(null_safety),
+                     PcDescriptorsLayout::kOther, locs, deopt_id);
+    // TypeTestABI::kSubtypeTestCacheReg is 0 if the type is a top type.
+    __ BranchIfZero(TypeTestABI::kSubtypeTestCacheReg, &is_assignable,
+                    compiler::Assembler::kNearJump);
 
-  // Generate inline type check, linking to runtime call if not assignable.
-  SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(zone());
-  test_cache = GenerateInlineInstanceof(token_pos, dst_type, &is_assignable,
-                                        &runtime_call);
+    GenerateStubCall(token_pos,
+                     StubCode::GetNullIsAssignableToType(null_safety),
+                     PcDescriptorsLayout::kOther, locs, deopt_id);
+    // TypeTestABI::kSubtypeTestCacheReg is 0 if the object is null and is
+    // assignable.
+    __ BranchIfZero(TypeTestABI::kSubtypeTestCacheReg, &is_assignable,
+                    compiler::Assembler::kNearJump);
+
+    // Use the full-arg version of the cache.
+    test_cache = GenerateCallSubtypeTestStub(kTestTypeSevenArgs, &is_assignable,
+                                             &runtime_call);
+  } else {
+    __ Comment("AssertAssignable for compile-time type");
+
+    if (Instance::NullIsAssignableTo(dst_type)) {
+      __ CompareObject(TypeTestABI::kInstanceReg, Object::null_object());
+      __ BranchIf(EQUAL, &is_assignable);
+    }
+
+    // Generate inline type check, linking to runtime call if not assignable.
+    test_cache = GenerateInlineInstanceof(token_pos, dst_type, &is_assignable,
+                                          &runtime_call);
+  }
 
   __ Bind(&runtime_call);
   __ PushObject(Object::null_object());            // Make room for the result.
   __ pushl(TypeTestABI::kInstanceReg);             // Push the source object.
-  if (locs->in(1).IsConstant()) {
-    __ PushObject(locs->in(1).constant());  // Push the type of the destination.
+  // Push the type of the destination.
+  if (!dst_type.IsNull()) {
+    __ PushObject(dst_type);
   } else {
-    // TODO(dartbug.com/40813): Handle setting up the non-constant case.
-    UNREACHABLE();
+    __ pushl(TypeTestABI::kDstTypeReg);
   }
   __ pushl(TypeTestABI::kInstantiatorTypeArgumentsReg);
   __ pushl(TypeTestABI::kFunctionTypeArgumentsReg);
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 8d8abf9..4042a04 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -1070,8 +1070,8 @@
 }
 
 Instruction* AssertSubtypeInstr::Canonicalize(FlowGraph* flow_graph) {
-  // If all inputs are constant, we can instantiate the sub and super type and
-  // remove this instruction if the subtype test succeeds.
+  // If all inputs needed to check instantation are constant, instantiate the
+  // sub and super type and remove the instruction if the subtype test succeeds.
   if (super_type()->BindsToConstant() && sub_type()->BindsToConstant() &&
       instantiator_type_arguments()->BindsToConstant() &&
       function_type_arguments()->BindsToConstant()) {
@@ -5286,7 +5286,7 @@
 void AssertAssignableInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
   compiler->GenerateAssertAssignable(value()->Type(), token_pos(), deopt_id(),
                                      dst_name(), locs());
-  ASSERT(locs()->in(0).reg() == locs()->out(0).reg());
+  ASSERT(locs()->in(kInstancePos).reg() == locs()->out(0).reg());
 }
 
 LocationSummary* AssertSubtypeInstr::MakeLocationSummary(Zone* zone,
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index 6e4fe72..c1a0446 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -3620,6 +3620,13 @@
   static const char* KindToCString(Kind kind);
   static bool ParseKind(const char* str, Kind* out);
 
+  enum {
+    kInstancePos = 0,
+    kDstTypePos = 1,
+    kInstantiatorTAVPos = 2,
+    kFunctionTAVPos = 3,
+  };
+
   AssertAssignableInstr(TokenPosition token_pos,
                         Value* value,
                         Value* dst_type,
@@ -3633,10 +3640,10 @@
         dst_name_(dst_name),
         kind_(kind) {
     ASSERT(!dst_name.IsNull());
-    SetInputAt(0, value);
-    SetInputAt(1, dst_type);
-    SetInputAt(2, instantiator_type_arguments);
-    SetInputAt(3, function_type_arguments);
+    SetInputAt(kInstancePos, value);
+    SetInputAt(kDstTypePos, dst_type);
+    SetInputAt(kInstantiatorTAVPos, instantiator_type_arguments);
+    SetInputAt(kFunctionTAVPos, function_type_arguments);
   }
 
   virtual intptr_t statistics_tag() const;
@@ -3645,10 +3652,12 @@
   virtual CompileType ComputeType() const;
   virtual bool RecomputeType();
 
-  Value* value() const { return inputs_[0]; }
-  Value* dst_type() const { return inputs_[1]; }
-  Value* instantiator_type_arguments() const { return inputs_[2]; }
-  Value* function_type_arguments() const { return inputs_[3]; }
+  Value* value() const { return inputs_[kInstancePos]; }
+  Value* dst_type() const { return inputs_[kDstTypePos]; }
+  Value* instantiator_type_arguments() const {
+    return inputs_[kInstantiatorTAVPos];
+  }
+  Value* function_type_arguments() const { return inputs_[kFunctionTAVPos]; }
 
   virtual TokenPosition token_pos() const { return token_pos_; }
   const String& dst_name() const { return dst_name_; }
diff --git a/runtime/vm/compiler/backend/il_arm.cc b/runtime/vm/compiler/backend/il_arm.cc
index b7fd68e..04b1857 100644
--- a/runtime/vm/compiler/backend/il_arm.cc
+++ b/runtime/vm/compiler/backend/il_arm.cc
@@ -790,12 +790,14 @@
 
   LocationSummary* summary = new (zone) LocationSummary(
       zone, kNumInputs, kNumTemps, LocationSummary::kCallCalleeSafe);
-  summary->set_in(0, Location::RegisterLocation(TypeTestABI::kInstanceReg));
-  summary->set_in(1, dst_type_loc);
-  summary->set_in(2, Location::RegisterLocation(
-                         TypeTestABI::kInstantiatorTypeArgumentsReg));
+  summary->set_in(kInstancePos,
+                  Location::RegisterLocation(TypeTestABI::kInstanceReg));
+  summary->set_in(kDstTypePos, dst_type_loc);
   summary->set_in(
-      3, Location::RegisterLocation(TypeTestABI::kFunctionTypeArgumentsReg));
+      kInstantiatorTAVPos,
+      Location::RegisterLocation(TypeTestABI::kInstantiatorTypeArgumentsReg));
+  summary->set_in(kFunctionTAVPos, Location::RegisterLocation(
+                                       TypeTestABI::kFunctionTypeArgumentsReg));
   summary->set_out(0, Location::SameAsFirstInput());
 
   // Let's reserve all registers except for the input ones.
diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc
index eeebf97..50100ea 100644
--- a/runtime/vm/compiler/backend/il_arm64.cc
+++ b/runtime/vm/compiler/backend/il_arm64.cc
@@ -688,12 +688,14 @@
 
   LocationSummary* summary = new (zone) LocationSummary(
       zone, kNumInputs, kNumTemps, LocationSummary::kCallCalleeSafe);
-  summary->set_in(0, Location::RegisterLocation(TypeTestABI::kInstanceReg));
-  summary->set_in(1, dst_type_loc);
-  summary->set_in(2, Location::RegisterLocation(
-                         TypeTestABI::kInstantiatorTypeArgumentsReg));
+  summary->set_in(kInstancePos,
+                  Location::RegisterLocation(TypeTestABI::kInstanceReg));
+  summary->set_in(kDstTypePos, dst_type_loc);
   summary->set_in(
-      3, Location::RegisterLocation(TypeTestABI::kFunctionTypeArgumentsReg));
+      kInstantiatorTAVPos,
+      Location::RegisterLocation(TypeTestABI::kInstantiatorTypeArgumentsReg));
+  summary->set_in(kFunctionTAVPos, Location::RegisterLocation(
+                                       TypeTestABI::kFunctionTypeArgumentsReg));
   summary->set_out(0, Location::SameAsFirstInput());
 
   // Let's reserve all registers except for the input ones.
diff --git a/runtime/vm/compiler/backend/il_ia32.cc b/runtime/vm/compiler/backend/il_ia32.cc
index 54356ba..985299e 100644
--- a/runtime/vm/compiler/backend/il_ia32.cc
+++ b/runtime/vm/compiler/backend/il_ia32.cc
@@ -496,13 +496,15 @@
   const intptr_t kNumTemps = 0;
   LocationSummary* summary = new (zone)
       LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kCall);
-  summary->set_in(0, Location::RegisterLocation(TypeTestABI::kInstanceReg));
+  summary->set_in(kInstancePos,
+                  Location::RegisterLocation(TypeTestABI::kInstanceReg));
+  summary->set_in(kDstTypePos, LocationFixedRegisterOrConstant(
+                                   dst_type(), TypeTestABI::kDstTypeReg));
   summary->set_in(
-      1, LocationFixedRegisterOrConstant(dst_type(), TypeTestABI::kDstTypeReg));
-  summary->set_in(2, Location::RegisterLocation(
-                         TypeTestABI::kInstantiatorTypeArgumentsReg));
-  summary->set_in(
-      3, Location::RegisterLocation(TypeTestABI::kFunctionTypeArgumentsReg));
+      kInstantiatorTAVPos,
+      Location::RegisterLocation(TypeTestABI::kInstantiatorTypeArgumentsReg));
+  summary->set_in(kFunctionTAVPos, Location::RegisterLocation(
+                                       TypeTestABI::kFunctionTypeArgumentsReg));
   summary->set_out(0, Location::SameAsFirstInput());
   return summary;
 }
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index 92d8205..34af406 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -627,12 +627,14 @@
 
   LocationSummary* summary = new (zone) LocationSummary(
       zone, kNumInputs, kNumTemps, LocationSummary::kCallCalleeSafe);
-  summary->set_in(0, Location::RegisterLocation(TypeTestABI::kInstanceReg));
-  summary->set_in(1, dst_type_loc);
-  summary->set_in(2, Location::RegisterLocation(
-                         TypeTestABI::kInstantiatorTypeArgumentsReg));
+  summary->set_in(kInstancePos,
+                  Location::RegisterLocation(TypeTestABI::kInstanceReg));
+  summary->set_in(kDstTypePos, dst_type_loc);
   summary->set_in(
-      3, Location::RegisterLocation(TypeTestABI::kFunctionTypeArgumentsReg));
+      kInstantiatorTAVPos,
+      Location::RegisterLocation(TypeTestABI::kInstantiatorTypeArgumentsReg));
+  summary->set_in(kFunctionTAVPos, Location::RegisterLocation(
+                                       TypeTestABI::kFunctionTypeArgumentsReg));
   summary->set_out(0, Location::SameAsFirstInput());
 
   // Let's reserve all registers except for the input ones.
diff --git a/runtime/vm/compiler/backend/range_analysis.cc b/runtime/vm/compiler/backend/range_analysis.cc
index 4018099..bc8f0ca 100644
--- a/runtime/vm/compiler/backend/range_analysis.cc
+++ b/runtime/vm/compiler/backend/range_analysis.cc
@@ -2766,10 +2766,7 @@
       break;
 
     case Slot::Kind::kClosureData_default_type_arguments_info:
-      *range = Range(
-          RangeBoundary::FromConstant(0),
-          RangeBoundary::FromConstant(
-              (1 << Function::NumParentTypeParametersField::kNextBit) - 1));
+      *range = Range(RangeBoundary::FromConstant(0), RangeBoundary::MaxSmi());
   }
 }
 
diff --git a/runtime/vm/compiler/backend/typed_data_aot_test.cc b/runtime/vm/compiler/backend/typed_data_aot_test.cc
index 30d58db..72f41e3 100644
--- a/runtime/vm/compiler/backend/typed_data_aot_test.cc
+++ b/runtime/vm/compiler/backend/typed_data_aot_test.cc
@@ -491,10 +491,6 @@
   ILMatcher cursor(flow_graph, entry, /*trace=*/true);
   RELEASE_ASSERT(cursor.TryMatch(
       {
-          kMatchAndMoveGoto,
-          kMatchAndMoveBranchFalse,
-          kMatchAndMoveAssertAssignable,
-          kMatchAndMoveGoto,
           kMatchAndMoveLoadField,
           kMatchAndMoveGenericCheckBound,
           kMatchAndMoveLoadUntagged,
diff --git a/runtime/vm/compiler/compiler_sources.gni b/runtime/vm/compiler/compiler_sources.gni
index ad10b9c..1496c09 100644
--- a/runtime/vm/compiler/compiler_sources.gni
+++ b/runtime/vm/compiler/compiler_sources.gni
@@ -181,7 +181,6 @@
   "cha_test.cc",
   "ffi/native_type_vm_test.cc",
   "frontend/kernel_binary_flowgraph_test.cc",
-  "frontend/multiple_entrypoints_test.cc",
   "write_barrier_elimination_test.cc",
 ]
 
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index c5a05cd..397daaf 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -722,31 +722,6 @@
   return body;
 }
 
-// If we run in checked mode or strong mode, we have to check the type of the
-// passed arguments.
-//
-// TODO(#34162): If we're building an extra entry-point to skip
-// type checks, we should substitute Redefinition nodes for the AssertAssignable
-// instructions to ensure that the argument types are known.
-void StreamingFlowGraphBuilder::CheckArgumentTypesAsNecessary(
-    const Function& dart_function,
-    intptr_t type_parameters_offset,
-    Fragment* explicit_checks,
-    Fragment* implicit_checks,
-    Fragment* implicit_redefinitions) {
-  if (dart_function.NeedsTypeArgumentTypeChecks()) {
-    B->BuildTypeArgumentTypeChecks(
-        MethodCanSkipTypeChecksForNonCovariantTypeArguments(dart_function)
-            ? TypeChecksToBuild::kCheckCovariantTypeParameterBounds
-            : TypeChecksToBuild::kCheckAllTypeParameterBounds,
-        implicit_checks);
-  }
-  if (dart_function.NeedsArgumentTypeChecks()) {
-    B->BuildArgumentTypeChecks(explicit_checks, implicit_checks,
-                               implicit_redefinitions);
-  }
-}
-
 Fragment StreamingFlowGraphBuilder::ShortcutForUserDefinedEquals(
     const Function& dart_function,
     LocalVariable* first_parameter) {
@@ -948,12 +923,21 @@
   // regular methods.
   const Fragment type_args_handling = TypeArgumentsHandling(dart_function);
 
-  Fragment explicit_type_checks;
   Fragment implicit_type_checks;
+  if (dart_function.NeedsTypeArgumentTypeChecks()) {
+    B->BuildTypeArgumentTypeChecks(
+        dart_function.CanReceiveDynamicInvocation()
+            ? TypeChecksToBuild::kCheckAllTypeParameterBounds
+            : TypeChecksToBuild::kCheckCovariantTypeParameterBounds,
+        &implicit_type_checks);
+  }
+
+  Fragment explicit_type_checks;
   Fragment implicit_redefinitions;
-  CheckArgumentTypesAsNecessary(dart_function, type_parameters_offset,
-                                &explicit_type_checks, &implicit_type_checks,
-                                &implicit_redefinitions);
+  if (dart_function.NeedsArgumentTypeChecks()) {
+    B->BuildArgumentTypeChecks(&explicit_type_checks, &implicit_type_checks,
+                               &implicit_redefinitions);
+  }
 
   // The RawParameter variables should be set to null to avoid retaining more
   // objects than necessary during GC.
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
index ef94bf3..175cc4e 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
@@ -94,11 +94,6 @@
   Fragment ShortcutForUserDefinedEquals(const Function& dart_function,
                                         LocalVariable* first_parameter);
   Fragment TypeArgumentsHandling(const Function& dart_function);
-  void CheckArgumentTypesAsNecessary(const Function& dart_function,
-                                     intptr_t type_parameters_offset,
-                                     Fragment* explicit_checks,
-                                     Fragment* implicit_checks,
-                                     Fragment* implicit_redefinitions);
   Fragment CompleteBodyWithYieldContinuations(Fragment body);
 
   static UncheckedEntryPointStyle ChooseEntryPointStyle(
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index af86e1e..a8bde81 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -1998,11 +1998,11 @@
   }
   String& name = String::Handle(Z);
   for (intptr_t i = 0; i < descriptor.NamedCount(); ++i) {
-    intptr_t parameter_index = descriptor.PositionalCount() + i;
+    const intptr_t parameter_index = descriptor.PositionAt(i);
     name = descriptor.NameAt(i);
     name = Symbols::New(H.thread(), name);
     body += LoadLocal(array);
-    body += IntConstant(receiver_index + descriptor.PositionAt(i));
+    body += IntConstant(receiver_index + parameter_index);
     body += LoadLocal(parsed_function_->ParameterVariable(parameter_index));
     body += StoreIndexed(kArrayCid);
   }
@@ -2069,6 +2069,7 @@
   LocalVariable* num_max_params = nullptr;
   LocalVariable* has_named_params = nullptr;
   LocalVariable* parameter_names = nullptr;
+  LocalVariable* parameter_types = nullptr;
   LocalVariable* type_parameters = nullptr;
   LocalVariable* closure_data = nullptr;
   LocalVariable* default_tav_info = nullptr;
@@ -2349,10 +2350,13 @@
     loop_body += BranchIfEqual(&match, &mismatch);
     loop_body.current = mismatch;
 
-    // We have a match, so go to the next name after incrementing the number
-    // of matched arguments. (No need to check for the required bit, as this
-    // parameter was provided.)
+    // We have a match, so go to the next name after storing the corresponding
+    // parameter index on the stack and incrementing the number of matched
+    // arguments. (No need to check the required bit for provided parameters.)
     Fragment matched(match);
+    matched += LoadLocal(info.vars->current_param_index);
+    matched += StoreLocal(info.vars->named_argument_parameter_indices.At(i));
+    matched += Drop();
     matched += LoadLocal(info.vars->current_num_processed);
     matched += IntConstant(1);
     matched += SmiBinaryOp(Token::kADD, /*is_truncating=*/true);
@@ -2566,6 +2570,60 @@
   return Fragment(loop_init.entry, done);
 }
 
+Fragment FlowGraphBuilder::BuildClosureCallArgumentTypeCheck(
+    const ClosureCallInfo& info,
+    LocalVariable* param_index,
+    intptr_t arg_index,
+    const String& arg_name) {
+  Fragment instructions;
+
+  // Load value.
+  instructions += LoadLocal(parsed_function_->ParameterVariable(arg_index));
+  // Load destination type.
+  instructions += LoadLocal(info.parameter_types);
+  instructions += LoadLocal(param_index);
+  instructions += LoadIndexed(kArrayCid);
+  // Load instantiator type arguments.
+  instructions += LoadLocal(info.instantiator_type_args);
+  // Load the full set of function type arguments.
+  instructions += LoadLocal(info.vars->function_type_args);
+  // Check that the value has the right type.
+  instructions += AssertAssignable(TokenPosition::kNoSource, arg_name,
+                                   AssertAssignableInstr::kParameterCheck);
+  // Make sure to store the result to keep data dependencies accurate.
+  instructions += StoreLocal(parsed_function_->ParameterVariable(arg_index));
+  instructions += Drop();
+
+  return instructions;
+}
+
+Fragment FlowGraphBuilder::BuildClosureCallArgumentTypeChecks(
+    const ClosureCallInfo& info) {
+  Fragment instructions;
+
+  // Only check explicit arguments (i.e., skip the receiver), as the receiver
+  // is always assignable to its type (stored as dynamic).
+  for (intptr_t i = 1; i < info.descriptor.PositionalCount(); i++) {
+    instructions += IntConstant(i);
+    LocalVariable* param_index = MakeTemporary("param_index");
+    // We don't have a compile-time name, so this symbol signals the runtime
+    // that it should recreate the type check using info from the stack.
+    instructions += BuildClosureCallArgumentTypeCheck(
+        info, param_index, i, Symbols::dynamic_assert_assignable_stc_check());
+    instructions += DropTemporary(&param_index);
+  }
+
+  for (intptr_t i = 0; i < info.descriptor.NamedCount(); i++) {
+    const intptr_t arg_index = info.descriptor.PositionAt(i);
+    const auto& arg_name = String::ZoneHandle(Z, info.descriptor.NameAt(i));
+    auto const param_index = info.vars->named_argument_parameter_indices.At(i);
+    instructions += BuildClosureCallArgumentTypeCheck(info, param_index,
+                                                      arg_index, arg_name);
+  }
+
+  return instructions;
+}
+
 Fragment FlowGraphBuilder::BuildDynamicClosureCallChecks(
     LocalVariable* closure) {
   ClosureCallInfo info(closure, BuildThrowNoSuchMethod(),
@@ -2601,11 +2659,13 @@
   body += StrictCompare(Token::kNE_STRICT);
   info.has_named_params = MakeTemporary("has_named_params");
 
-  if (I->null_safety() || info.descriptor.NamedCount() > 0) {
-    body += LoadLocal(info.function);
-    body += LoadNativeField(Slot::Function_parameter_names());
-    info.parameter_names = MakeTemporary("parameter_names");
-  }
+  body += LoadLocal(info.function);
+  body += LoadNativeField(Slot::Function_parameter_names());
+  info.parameter_names = MakeTemporary("parameter_names");
+
+  body += LoadLocal(info.function);
+  body += LoadNativeField(Slot::Function_parameter_types());
+  info.parameter_types = MakeTemporary("parameter_types");
 
   body += LoadLocal(info.function);
   body += LoadNativeField(Slot::Function_type_parameters());
@@ -2688,18 +2748,16 @@
   // and performing any needed type argument checking.
   body += TestClosureFunctionGeneric(info, generic, not_generic);
 
-  // TODO(dartbug.com/40813): Move checks that are currently compiled
-  // in the closure body to here, using the dynamic versions of
-  // AssertAssignable to typecheck the parameters using the runtime types
-  // available in the closure object.
+  // Check that the values provided as arguments are assignable to the types
+  // of the corresponding closure function parameters.
+  body += BuildClosureCallArgumentTypeChecks(info);
 
   // Drop all the read-only temporaries at the end of the fragment.
   body += DropTemporary(&info.parent_function_type_args);
   body += DropTemporary(&info.instantiator_type_args);
   body += DropTemporary(&info.type_parameters);
-  if (info.parameter_names != nullptr) {
-    body += DropTemporary(&info.parameter_names);
-  }
+  body += DropTemporary(&info.parameter_types);
+  body += DropTemporary(&info.parameter_names);
   body += DropTemporary(&info.has_named_params);
   body += DropTemporary(&info.num_max_params);
   body += DropTemporary(&info.num_opt_params);
@@ -2793,8 +2851,10 @@
         Array::ZoneHandle(Z, Array::New(descriptor.NamedCount(), Heap::kNew));
     String& string_handle = String::Handle(Z);
     for (intptr_t i = 0; i < descriptor.NamedCount(); ++i) {
+      const intptr_t named_arg_index =
+          descriptor.PositionAt(i) - descriptor.PositionalCount();
       string_handle = descriptor.NameAt(i);
-      array_handle.SetAt(i, string_handle);
+      array_handle.SetAt(named_arg_index, string_handle);
     }
     argument_names = &array_handle;
   }
@@ -3216,49 +3276,38 @@
   BlockEntryInstr* instruction_cursor =
       BuildPrologue(normal_entry, &prologue_info);
 
-  const Fragment prologue = CheckStackOverflowInPrologue(function.token_pos());
+  Fragment closure(instruction_cursor);
+  closure += CheckStackOverflowInPrologue(function.token_pos());
+  closure += BuildDefaultTypeHandling(function);
 
-  const Fragment default_type_handling = BuildDefaultTypeHandling(function);
-
-  Fragment implicit_checks;
-  if (function.NeedsTypeArgumentTypeChecks() &&
-      (target.is_static() ||
-       MethodCanSkipTypeChecksForNonCovariantTypeArguments(parent))) {
-    BuildTypeArgumentTypeChecks(
-        target.is_static()
-            ? TypeChecksToBuild::kCheckAllTypeParameterBounds
-            : TypeChecksToBuild::kCheckNonCovariantTypeParameterBounds,
-        &implicit_checks);
-  }
-  if (function.NeedsArgumentTypeChecks() &&
-      (target.is_static() ||
-       MethodCanSkipTypeChecksForNonCovariantArguments(parent))) {
-    // We're going to throw away the explicit checks because the target will
-    // always check them.
-    Fragment explicit_checks_unused;
-    BuildArgumentTypeChecks(&explicit_checks_unused, &implicit_checks, nullptr);
-  }
-
-  Fragment body;
+  // For implicit closure functions, any non-covariant checks are either
+  // performed by the type system or a dynamic invocation layer (dynamic closure
+  // call dispatcher, mirror, etc.). Static targets never have covariant
+  // arguments, and for non-static targets, they already perform the covariant
+  // checks internally. Thus, no checks are needed and we just need to invoke
+  // the target with the right receiver (unless static).
+  //
+  // TODO(dartbug.com/44195): Consider replacing the argument pushes + static
+  // call with stack manipulation and a tail call instead.
 
   intptr_t type_args_len = 0;
   if (function.IsGeneric()) {
     type_args_len = function.NumTypeParameters();
     ASSERT(parsed_function_->function_type_arguments() != NULL);
-    body += LoadLocal(parsed_function_->function_type_arguments());
+    closure += LoadLocal(parsed_function_->function_type_arguments());
   }
 
   // Push receiver.
   if (!target.is_static()) {
     // The context has a fixed shape: a single variable which is the
     // closed-over receiver.
-    body += LoadLocal(parsed_function_->ParameterVariable(0));
-    body += LoadNativeField(Slot::Closure_context());
-    body += LoadNativeField(Slot::GetContextVariableSlotFor(
+    closure += LoadLocal(parsed_function_->ParameterVariable(0));
+    closure += LoadNativeField(Slot::Closure_context());
+    closure += LoadNativeField(Slot::GetContextVariableSlotFor(
         thread_, *parsed_function_->receiver_var()));
   }
 
-  body += PushExplicitParameters(function);
+  closure += PushExplicitParameters(function);
 
   // Forward parameters to the target.
   intptr_t argument_count = function.NumParameters() -
@@ -3269,48 +3318,12 @@
   Array& argument_names =
       Array::ZoneHandle(Z, GetOptionalParameterNames(function));
 
-  body += StaticCall(TokenPosition::kNoSource, target, argument_count,
-                     argument_names, ICData::kNoRebind,
-                     /* result_type = */ NULL, type_args_len);
+  closure += StaticCall(TokenPosition::kNoSource, target, argument_count,
+                        argument_names, ICData::kNoRebind,
+                        /* result_type = */ NULL, type_args_len);
 
   // Return the result.
-  body += Return(function.end_token_pos());
-
-  // Setup multiple entrypoints if useful.
-  FunctionEntryInstr* extra_entry = nullptr;
-  if (function.MayHaveUncheckedEntryPoint()) {
-    // The prologue for a closure will always have context handling (e.g.
-    // setting up the receiver variable), but we don't need it on the unchecked
-    // entry because the only time we reference this is for loading the
-    // receiver, which we fetch directly from the context.
-    if (PrologueBuilder::PrologueSkippableOnUncheckedEntry(function)) {
-      // Use separate entry points since we can skip almost everything on the
-      // static entry.
-      extra_entry = BuildSeparateUncheckedEntryPoint(
-          /*normal_entry=*/instruction_cursor,
-          /*normal_prologue=*/prologue + default_type_handling +
-              implicit_checks,
-          /*extra_prologue=*/
-          CheckStackOverflowInPrologue(function.token_pos()),
-          /*shared_prologue=*/Fragment(),
-          /*body=*/body);
-    } else {
-      Fragment shared_prologue(normal_entry, instruction_cursor);
-      shared_prologue += prologue;
-      extra_entry = BuildSharedUncheckedEntryPoint(
-          /*shared_prologue_linked_in=*/shared_prologue,
-          /*skippable_checks=*/default_type_handling + implicit_checks,
-          /*redefinitions_if_skipped=*/Fragment(),
-          /*body=*/body);
-    }
-    RecordUncheckedEntryPoint(graph_entry_, extra_entry);
-  } else {
-    Fragment function(instruction_cursor);
-    function += prologue;
-    function += default_type_handling;
-    function += implicit_checks;
-    function += body;
-  }
+  closure += Return(function.end_token_pos());
 
   return new (Z) FlowGraph(*parsed_function_, graph_entry_, last_used_block_id_,
                            prologue_info);
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.h b/runtime/vm/compiler/frontend/kernel_to_il.h
index cd23744..6d4eaa8 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.h
+++ b/runtime/vm/compiler/frontend/kernel_to_il.h
@@ -120,6 +120,20 @@
   // function is generic.
   Fragment BuildClosureCallTypeArgumentsTypeCheck(const ClosureCallInfo& info);
 
+  // Builds checks for type checking a given argument of the closure call using
+  // parameter information from the closure function retrieved at runtime.
+  //
+  // For named arguments, arg_name is a compile-time constant retrieved from
+  // the saved arguments descriptor. For positional arguments, null is passed.
+  Fragment BuildClosureCallArgumentTypeCheck(const ClosureCallInfo& info,
+                                             LocalVariable* param_index,
+                                             intptr_t arg_index,
+                                             const String& arg_name);
+
+  // Builds checks for type checking the arguments of a call using parameter
+  // information for the function retrieved at runtime from the closure.
+  Fragment BuildClosureCallArgumentTypeChecks(const ClosureCallInfo& info);
+
   // Main entry point for building checks.
   Fragment BuildDynamicClosureCallChecks(LocalVariable* closure);
 
diff --git a/runtime/vm/compiler/frontend/multiple_entrypoints_test.cc b/runtime/vm/compiler/frontend/multiple_entrypoints_test.cc
deleted file mode 100644
index 9ae1679..0000000
--- a/runtime/vm/compiler/frontend/multiple_entrypoints_test.cc
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright (c) 2020, 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.
-
-#include "vm/compiler/backend/il_test_helper.h"
-#include "vm/compiler/compiler_pass.h"
-#include "vm/object.h"
-#include "vm/unit_test.h"
-
-namespace dart {
-
-#if defined(DART_PRECOMPILER)
-
-ISOLATE_UNIT_TEST_CASE(IRTest_MultilpeEntryPoints_Regress43534) {
-  const char* kScript =
-      R"(
-      import 'dart:typed_data';
-
-      @pragma('vm:never-inline')
-      void callWith<T>(void Function(T arg) fun, T arg) {
-        fun(arg);
-      }
-
-      @pragma('vm:never-inline')
-      void use(dynamic arg) {}
-
-      void test() {
-        callWith<Uint8List>((Uint8List list) {
-          use(list);
-        }, Uint8List(10));
-      }
-      )";
-
-  const auto& root_library = Library::Handle(LoadTestScript(kScript));
-  Invoke(root_library, "test");
-  const auto& test_function =
-      Function::Handle(GetFunction(root_library, "test"));
-  const auto& closures = GrowableObjectArray::Handle(
-      Isolate::Current()->object_store()->closure_functions());
-  auto& function = Function::Handle();
-  for (intptr_t i = closures.Length() - 1; 0 <= i; ++i) {
-    function ^= closures.At(i);
-    if (function.parent_function() == test_function.raw()) {
-      break;
-    }
-    function = Function::null();
-  }
-  RELEASE_ASSERT(!function.IsNull());
-  TestPipeline pipeline(function, CompilerPass::kAOT);
-  FlowGraph* flow_graph = pipeline.RunPasses({CompilerPass::kComputeSSA});
-
-  auto entry = flow_graph->graph_entry()->normal_entry();
-  EXPECT(entry != nullptr);
-
-  auto unchecked_entry = flow_graph->graph_entry()->unchecked_entry();
-  EXPECT(unchecked_entry != nullptr);
-
-  AssertAssignableInstr* assert_assignable = nullptr;
-  StaticCallInstr* static_call = nullptr;
-
-  // Normal entry
-  ILMatcher cursor(flow_graph, entry, /*trace=*/true);
-  RELEASE_ASSERT(cursor.TryMatch(
-      {
-          kMatchAndMoveGoto,
-          kMatchAndMoveBranchFalse,
-          {kMatchAndMoveAssertAssignable, &assert_assignable},
-          kMatchAndMoveGoto,
-          {kMatchAndMoveStaticCall, &static_call},
-          kMatchReturn,
-      },
-      kMoveGlob));
-
-  RedefinitionInstr* redefinition = nullptr;
-  StaticCallInstr* static_call2 = nullptr;
-
-  // Unchecked entry
-  ILMatcher cursor2(flow_graph, entry, /*trace=*/true);
-  RELEASE_ASSERT(cursor2.TryMatch(
-      {
-          kMatchAndMoveGoto,
-          kMatchAndMoveBranchTrue,
-          {kMatchAndMoveRedefinition, &redefinition},
-          kMatchAndMoveGoto,
-          {kMatchAndMoveStaticCall, &static_call2},
-          kMatchReturn,
-      },
-      kMoveGlob));
-
-  // Ensure the value the static call uses is a Phi node with 2 inputs:
-  //   a) Normal entry: AssertAssignable
-  //   b) Unchecked entry: Redefinition
-  RELEASE_ASSERT(static_call->ArgumentAt(0)->IsPhi());
-  auto phi = static_call->ArgumentAt(0)->AsPhi();
-  auto input_a = phi->InputAt(0)->definition();
-  auto input_b = phi->InputAt(1)->definition();
-  RELEASE_ASSERT(input_a->IsRedefinition());
-  RELEASE_ASSERT(input_b->IsAssertAssignable());
-  RELEASE_ASSERT(input_a == redefinition);
-  RELEASE_ASSERT(input_b == assert_assignable);
-  RELEASE_ASSERT(static_call == static_call2);
-}
-
-#endif  // defined(DART_PRECOMPILER)
-
-}  // namespace dart
diff --git a/runtime/vm/compiler/frontend/scope_builder.cc b/runtime/vm/compiler/frontend/scope_builder.cc
index 4470cee..ab4762a 100644
--- a/runtime/vm/compiler/frontend/scope_builder.cc
+++ b/runtime/vm/compiler/frontend/scope_builder.cc
@@ -15,44 +15,6 @@
 #define T (type_translator_)
 #define I Isolate::Current()
 
-// Returns true if the given method can skip type checks for all type arguments
-// that are not covariant or generic covariant in its implementation.
-bool MethodCanSkipTypeChecksForNonCovariantTypeArguments(
-    const Function& method) {
-  // Dart 2 type system at non-dynamic call sites statically guarantees that
-  // argument values match declared parameter types for all non-covariant
-  // and non-generic-covariant parameters. The same applies to type parameters
-  // bounds for type parameters of generic functions.
-  //
-  // Normally dynamic call sites will call dyn:* forwarders which perform type
-  // checks.
-  //
-  // Though for some kinds of methods (e.g. ffi trampolines called from native
-  // code) we do have to perform type checks for all parameters.
-  return !method.CanReceiveDynamicInvocation();
-}
-
-// Returns true if the given method can skip type checks for all arguments
-// that are not covariant or generic covariant in its implementation.
-bool MethodCanSkipTypeChecksForNonCovariantArguments(const Function& method) {
-  // Dart 2 type system at non-dynamic call sites statically guarantees that
-  // argument values match declarated parameter types for all non-covariant
-  // and non-generic-covariant parameters. The same applies to type parameters
-  // bounds for type parameters of generic functions.
-  //
-  // Normally dynamic call sites will call dyn:* forwarders which perform type
-  // checks.
-  //
-  // Though for some kinds of methods (e.g. ffi trampolines called from native
-  // code) we do have to perform type checks for all parameters.
-  //
-  // TODO(dartbug.com/40813): Remove the closure case when argument checks have
-  // been fully moved out of closures.
-  return !method.CanReceiveDynamicInvocation() &&
-         !(method.IsClosureFunction() &&
-           Function::ClosureBodiesContainNonCovariantArgumentChecks());
-}
-
 ScopeBuilder::ScopeBuilder(ParsedFunction* parsed_function)
     : result_(NULL),
       parsed_function_(parsed_function),
@@ -134,7 +96,6 @@
   }
 
   if (parsed_function_->has_arg_desc_var()) {
-    needs_expr_temp_ = true;
     scope_->AddVariable(parsed_function_->arg_desc_var());
   }
 
@@ -230,47 +191,34 @@
         result_->type_arguments_variable = variable;
       }
 
-      ParameterTypeCheckMode type_check_mode = kTypeCheckAllParameters;
+      ParameterTypeCheckMode type_check_mode =
+          kTypeCheckForNonDynamicallyInvokedMethod;
       if (function.IsSyncGenClosure()) {
         // Don't type check the parameter of sync-yielding since these calls are
         // all synthetic and types should always match.
-        ASSERT((function.NumParameters() - function.NumImplicitParameters()) ==
-               3);
+        ASSERT_EQUAL(
+            function.NumParameters() - function.NumImplicitParameters(), 3);
         ASSERT(
             Class::Handle(
                 AbstractType::Handle(function.ParameterTypeAt(1)).type_class())
                 .ScrubbedName() == Symbols::_SyncIterator().raw());
         type_check_mode = kTypeCheckForStaticFunction;
-      } else if (function.IsNonImplicitClosureFunction()) {
-        type_check_mode = kTypeCheckAllParameters;
+      } else if (function.is_static()) {
+        // In static functions we don't check anything.
+        type_check_mode = kTypeCheckForStaticFunction;
       } else if (function.IsImplicitClosureFunction()) {
-        if (MethodCanSkipTypeChecksForNonCovariantTypeArguments(
-                Function::Handle(Z, function.parent_function())) &&
-            MethodCanSkipTypeChecksForNonCovariantArguments(
-                Function::Handle(Z, function.parent_function()))) {
-          // This is a tear-off of an instance method that can not be reached
-          // from any dynamic invocation. The method would not check any
-          // parameters except covariant ones and those annotated with
-          // generic-covariant-impl. Which means that we have to check
-          // the rest in the tear-off itself.
-          type_check_mode =
-              kTypeCheckEverythingNotCheckedInNonDynamicallyInvokedMethod;
-        }
-      } else {
-        if (function.is_static()) {
-          // In static functions we don't check anything.
-          type_check_mode = kTypeCheckForStaticFunction;
-        } else if (MethodCanSkipTypeChecksForNonCovariantTypeArguments(
-                       function) &&
-                   MethodCanSkipTypeChecksForNonCovariantArguments(function)) {
-          // If the current function is never a target of a dynamic invocation
-          // and this parameter is not marked with generic-covariant-impl
-          // (which means that among all super-interfaces no type parameters
-          // ever occur at the position of this parameter) then we don't need
-          // to check this parameter on the callee side, because strong mode
-          // guarantees that it was checked at the caller side.
-          type_check_mode = kTypeCheckForNonDynamicallyInvokedMethod;
-        }
+        // All non-covariant checks are either performed by the type system,
+        // or by a dynamic closure call dispatcher/mirror if dynamically
+        // invoked. For covariant checks, static targets never have covariant
+        // arguments and dynamic targets do their own covariant checking.
+        // Thus, implicit closure functions perform no checking internally.
+        type_check_mode = kTypeCheckForImplicitClosureFunction;
+      } else if (function.CanReceiveDynamicInvocation()) {
+        // If the current function can be the direct target of a dynamic
+        // invocation, that is, dynamic calls do not go through a dynamic
+        // invocation forwarder or dynamic closure call dispatcher, then we must
+        // check non-covariant parameters as well as covariant ones.
+        type_check_mode = kTypeCheckAllParameters;
       }
 
       // Continue reading FunctionNode:
@@ -336,9 +284,7 @@
         }
         scope_->InsertParameterAt(pos++, result_->setter_value);
 
-        if (is_method &&
-            MethodCanSkipTypeChecksForNonCovariantTypeArguments(function) &&
-            MethodCanSkipTypeChecksForNonCovariantArguments(function)) {
+        if (is_method && !function.CanReceiveDynamicInvocation()) {
           if (field.is_covariant()) {
             result_->setter_value->set_is_explicit_covariant_parameter();
           } else if (!field.is_generic_covariant_impl() ||
@@ -460,6 +406,9 @@
 #define ADD_VAR(Name, _, __) scope_->AddVariable(vars->Name);
         FOR_EACH_DYNAMIC_CLOSURE_CALL_VARIABLE(ADD_VAR);
 #undef ADD_VAR
+        for (auto const& v : vars->named_argument_parameter_indices) {
+          scope_->AddVariable(v);
+        }
       }
     }
       FALL_THROUGH;
@@ -480,7 +429,10 @@
       UNREACHABLE();
   }
   if (needs_expr_temp_) {
-    scope_->AddVariable(parsed_function_->EnsureExpressionTemp());
+    parsed_function_->EnsureExpressionTemp();
+  }
+  if (parsed_function_->has_expression_temp_var()) {
+    scope_->AddVariable(parsed_function_->expression_temp_var());
   }
   if (parsed_function_->function().MayHaveUncheckedEntryPoint()) {
     scope_->AddVariable(parsed_function_->EnsureEntryPointsTemp());
@@ -1539,7 +1491,8 @@
       FunctionNodeHelper::kPositionalParameters);
 
   ProcedureAttributesMetadata default_attrs;
-  AddPositionalAndNamedParameters(0, kTypeCheckAllParameters, default_attrs);
+  AddPositionalAndNamedParameters(0, kTypeCheckForNonDynamicallyInvokedMethod,
+                                  default_attrs);
 
   // "Peek" is now done.
   helper_.SetOffset(offset);
@@ -1639,6 +1592,17 @@
         variable->set_type_check_mode(LocalVariable::kTypeCheckedByCaller);
       }
       break;
+    case kTypeCheckForImplicitClosureFunction:
+      if (needs_covariant_check_in_method) {
+        // Don't type check covariant parameters - they will be checked by
+        // a function we forward to. Their types however are not known.
+        variable->set_type_check_mode(LocalVariable::kSkipTypeCheck);
+      } else {
+        // All non-covariant checks are either checked by the type system or
+        // by a dynamic closure call dispatcher.
+        variable->set_type_check_mode(LocalVariable::kTypeCheckedByCaller);
+      }
+      break;
     case kTypeCheckForStaticFunction:
       variable->set_type_check_mode(LocalVariable::kTypeCheckedByCaller);
       break;
diff --git a/runtime/vm/compiler/frontend/scope_builder.h b/runtime/vm/compiler/frontend/scope_builder.h
index 33e24b3..c8680c3 100644
--- a/runtime/vm/compiler/frontend/scope_builder.h
+++ b/runtime/vm/compiler/frontend/scope_builder.h
@@ -73,6 +73,10 @@
 
     // No parameters will be checked.
     kTypeCheckForStaticFunction,
+
+    // No non-covariant checks are performed, and any covariant checks are
+    // performed by the target.
+    kTypeCheckForImplicitClosureFunction,
   };
 
   // This assumes that the reader is at a FunctionNode,
@@ -230,15 +234,6 @@
   DISALLOW_COPY_AND_ASSIGN(ScopeBuildingResult);
 };
 
-// Returns true if the given method can skip type checks for all type arguments
-// that are not covariant or generic covariant in its implementation.
-bool MethodCanSkipTypeChecksForNonCovariantTypeArguments(
-    const Function& method);
-
-// Returns true if the given method can skip type checks for all arguments
-// that are not covariant or generic covariant in its implementation.
-bool MethodCanSkipTypeChecksForNonCovariantArguments(const Function& method);
-
 }  // namespace kernel
 }  // namespace dart
 
diff --git a/runtime/vm/compiler/runtime_api.h b/runtime/vm/compiler/runtime_api.h
index 90f6566..25908f1 100644
--- a/runtime/vm/compiler/runtime_api.h
+++ b/runtime/vm/compiler/runtime_api.h
@@ -893,6 +893,7 @@
   static word NextFieldOffset();
   static word parameterized_class_id_offset();
   static word index_offset();
+  static word nullability_offset();
 };
 
 class LibraryPrefix : public AllStatic {
diff --git a/runtime/vm/compiler/runtime_offsets_extracted.h b/runtime/vm/compiler/runtime_offsets_extracted.h
index 9cd38b3..2f93972 100644
--- a/runtime/vm/compiler/runtime_offsets_extracted.h
+++ b/runtime/vm/compiler/runtime_offsets_extracted.h
@@ -395,6 +395,8 @@
 static constexpr dart::compiler::target::word
     TypeParameter_parameterized_class_id_offset = 32;
 static constexpr dart::compiler::target::word TypeParameter_index_offset = 40;
+static constexpr dart::compiler::target::word TypeParameter_nullability_offset =
+    43;
 static constexpr dart::compiler::target::word
     TypeArguments_instantiations_offset = 4;
 static constexpr dart::compiler::target::word TypeArguments_length_offset = 8;
@@ -915,6 +917,8 @@
 static constexpr dart::compiler::target::word
     TypeParameter_parameterized_class_id_offset = 64;
 static constexpr dart::compiler::target::word TypeParameter_index_offset = 72;
+static constexpr dart::compiler::target::word TypeParameter_nullability_offset =
+    75;
 static constexpr dart::compiler::target::word
     TypeArguments_instantiations_offset = 8;
 static constexpr dart::compiler::target::word TypeArguments_length_offset = 16;
@@ -1431,6 +1435,8 @@
 static constexpr dart::compiler::target::word
     TypeParameter_parameterized_class_id_offset = 32;
 static constexpr dart::compiler::target::word TypeParameter_index_offset = 40;
+static constexpr dart::compiler::target::word TypeParameter_nullability_offset =
+    43;
 static constexpr dart::compiler::target::word
     TypeArguments_instantiations_offset = 4;
 static constexpr dart::compiler::target::word TypeArguments_length_offset = 8;
@@ -1948,6 +1954,8 @@
 static constexpr dart::compiler::target::word
     TypeParameter_parameterized_class_id_offset = 64;
 static constexpr dart::compiler::target::word TypeParameter_index_offset = 72;
+static constexpr dart::compiler::target::word TypeParameter_nullability_offset =
+    75;
 static constexpr dart::compiler::target::word
     TypeArguments_instantiations_offset = 8;
 static constexpr dart::compiler::target::word TypeArguments_length_offset = 16;
@@ -2464,6 +2472,8 @@
 static constexpr dart::compiler::target::word
     TypeParameter_parameterized_class_id_offset = 32;
 static constexpr dart::compiler::target::word TypeParameter_index_offset = 40;
+static constexpr dart::compiler::target::word TypeParameter_nullability_offset =
+    43;
 static constexpr dart::compiler::target::word
     TypeArguments_instantiations_offset = 4;
 static constexpr dart::compiler::target::word TypeArguments_length_offset = 8;
@@ -2978,6 +2988,8 @@
 static constexpr dart::compiler::target::word
     TypeParameter_parameterized_class_id_offset = 64;
 static constexpr dart::compiler::target::word TypeParameter_index_offset = 72;
+static constexpr dart::compiler::target::word TypeParameter_nullability_offset =
+    75;
 static constexpr dart::compiler::target::word
     TypeArguments_instantiations_offset = 8;
 static constexpr dart::compiler::target::word TypeArguments_length_offset = 16;
@@ -3488,6 +3500,8 @@
 static constexpr dart::compiler::target::word
     TypeParameter_parameterized_class_id_offset = 32;
 static constexpr dart::compiler::target::word TypeParameter_index_offset = 40;
+static constexpr dart::compiler::target::word TypeParameter_nullability_offset =
+    43;
 static constexpr dart::compiler::target::word
     TypeArguments_instantiations_offset = 4;
 static constexpr dart::compiler::target::word TypeArguments_length_offset = 8;
@@ -3999,6 +4013,8 @@
 static constexpr dart::compiler::target::word
     TypeParameter_parameterized_class_id_offset = 64;
 static constexpr dart::compiler::target::word TypeParameter_index_offset = 72;
+static constexpr dart::compiler::target::word TypeParameter_nullability_offset =
+    75;
 static constexpr dart::compiler::target::word
     TypeArguments_instantiations_offset = 8;
 static constexpr dart::compiler::target::word TypeArguments_length_offset = 16;
@@ -4549,6 +4565,8 @@
 static constexpr dart::compiler::target::word AOT_TypeParameter_index_offset =
     40;
 static constexpr dart::compiler::target::word
+    AOT_TypeParameter_nullability_offset = 43;
+static constexpr dart::compiler::target::word
     AOT_TypeArguments_instantiations_offset = 4;
 static constexpr dart::compiler::target::word AOT_TypeArguments_length_offset =
     8;
@@ -5122,6 +5140,8 @@
 static constexpr dart::compiler::target::word AOT_TypeParameter_index_offset =
     72;
 static constexpr dart::compiler::target::word
+    AOT_TypeParameter_nullability_offset = 75;
+static constexpr dart::compiler::target::word
     AOT_TypeArguments_instantiations_offset = 8;
 static constexpr dart::compiler::target::word AOT_TypeArguments_length_offset =
     16;
@@ -5699,6 +5719,8 @@
 static constexpr dart::compiler::target::word AOT_TypeParameter_index_offset =
     72;
 static constexpr dart::compiler::target::word
+    AOT_TypeParameter_nullability_offset = 75;
+static constexpr dart::compiler::target::word
     AOT_TypeArguments_instantiations_offset = 8;
 static constexpr dart::compiler::target::word AOT_TypeArguments_length_offset =
     16;
@@ -6270,6 +6292,8 @@
 static constexpr dart::compiler::target::word AOT_TypeParameter_index_offset =
     40;
 static constexpr dart::compiler::target::word
+    AOT_TypeParameter_nullability_offset = 43;
+static constexpr dart::compiler::target::word
     AOT_TypeArguments_instantiations_offset = 4;
 static constexpr dart::compiler::target::word AOT_TypeArguments_length_offset =
     8;
@@ -6836,6 +6860,8 @@
 static constexpr dart::compiler::target::word AOT_TypeParameter_index_offset =
     72;
 static constexpr dart::compiler::target::word
+    AOT_TypeParameter_nullability_offset = 75;
+static constexpr dart::compiler::target::word
     AOT_TypeArguments_instantiations_offset = 8;
 static constexpr dart::compiler::target::word AOT_TypeArguments_length_offset =
     16;
@@ -7406,6 +7432,8 @@
 static constexpr dart::compiler::target::word AOT_TypeParameter_index_offset =
     72;
 static constexpr dart::compiler::target::word
+    AOT_TypeParameter_nullability_offset = 75;
+static constexpr dart::compiler::target::word
     AOT_TypeArguments_instantiations_offset = 8;
 static constexpr dart::compiler::target::word AOT_TypeArguments_length_offset =
     16;
diff --git a/runtime/vm/compiler/runtime_offsets_list.h b/runtime/vm/compiler/runtime_offsets_list.h
index 98ab25f..8b5aa0a 100644
--- a/runtime/vm/compiler/runtime_offsets_list.h
+++ b/runtime/vm/compiler/runtime_offsets_list.h
@@ -267,6 +267,7 @@
   FIELD(Type, nullability_offset)                                              \
   FIELD(TypeParameter, parameterized_class_id_offset)                          \
   FIELD(TypeParameter, index_offset)                                           \
+  FIELD(TypeParameter, nullability_offset)                                     \
   FIELD(TypeArguments, instantiations_offset)                                  \
   FIELD(TypeArguments, length_offset)                                          \
   FIELD(TypeArguments, nullability_offset)                                     \
diff --git a/runtime/vm/compiler/stub_code_compiler.cc b/runtime/vm/compiler/stub_code_compiler.cc
index 7536df5..5456c29 100644
--- a/runtime/vm/compiler/stub_code_compiler.cc
+++ b/runtime/vm/compiler/stub_code_compiler.cc
@@ -189,6 +189,251 @@
   __ Ret();
 }
 
+// For use in GenerateTypeIsTopTypeForSubtyping and
+// GenerateNullIsAssignableToType.
+static void EnsureIsTypeOrTypeParameter(Assembler* assembler,
+                                        Register type_reg,
+                                        Register scratch_reg) {
+#if defined(DEBUG)
+  compiler::Label is_type_param_or_type;
+  __ LoadClassIdMayBeSmi(scratch_reg, type_reg);
+  __ CompareImmediate(scratch_reg, kTypeParameterCid);
+  __ BranchIf(EQUAL, &is_type_param_or_type, compiler::Assembler::kNearJump);
+  __ CompareImmediate(scratch_reg, kTypeCid);
+  __ BranchIf(EQUAL, &is_type_param_or_type, compiler::Assembler::kNearJump);
+  // Type references show up in F-bounded polymorphism, which is limited
+  // to classes. Thus, TypeRefs only appear in places like class type
+  // arguments or the bounds of uninstantiated class type parameters.
+  //
+  // Since this stub is currently used only by the dynamic versions of
+  // AssertSubtype and AssertAssignable, where kDstType is either the bound of
+  // a function type parameter or the type of a function parameter
+  // (respectively), we should never see a TypeRef here. This check is here
+  // in case this changes and we need to update this stub.
+  __ Stop("not a type or type parameter");
+  __ Bind(&is_type_param_or_type);
+#endif
+}
+
+// Version of AbstractType::IsTopTypeForSubtyping() used when the type is not
+// known at compile time. Must be kept in sync.
+//
+// Inputs:
+// - TypeTestABI::kDstTypeReg: Destination type.
+//
+// Non-preserved scratch registers:
+// - TypeTestABI::kScratchReg (only on non-IA32 architectures)
+//
+// Outputs:
+// - TypeTestABI::kSubtypeTestCacheReg: 0 if the value is guaranteed assignable,
+//   non-zero otherwise.
+//
+// All registers other than outputs and non-preserved scratches are preserved.
+static void GenerateTypeIsTopTypeForSubtyping(Assembler* assembler,
+                                              bool null_safety) {
+  // The only case where the original value of kSubtypeTestCacheReg is needed
+  // after the stub call is on IA32, where it's currently preserved on the stack
+  // before calling the stub (as it's also CODE_REG on that architecture), so we
+  // both use it as a scratch and clobber it for the return value.
+  const Register scratch1_reg = TypeTestABI::kSubtypeTestCacheReg;
+  // We reuse the first scratch register as the output register because we're
+  // always guaranteed to have a type in it (starting with kDstType), and all
+  // non-Smi ObjectPtrs are non-zero values.
+  const Register output_reg = scratch1_reg;
+#if defined(TARGET_ARCH_IA32)
+  // The remaining scratch registers are preserved and restored before exit on
+  // IA32. Because  we have few registers to choose from (which are all used in
+  // TypeTestABI), use specific TestTypeABI registers.
+  const Register scratch2_reg = TypeTestABI::kFunctionTypeArgumentsReg;
+  // Preserve non-output scratch registers.
+  __ PushRegister(scratch2_reg);
+#else
+  const Register scratch2_reg = TypeTestABI::kScratchReg;
+#endif
+  static_assert(scratch1_reg != scratch2_reg,
+                "both scratch registers are the same");
+
+  compiler::Label check_top_type, is_top_type, done;
+  // Initialize scratch1_reg with the type to check (which also sets the
+  // output register to a non-zero value). scratch1_reg (and thus the output
+  // register) will always have a type in it from here on out.
+  __ MoveRegister(scratch1_reg, TypeTestABI::kDstTypeReg);
+  __ Bind(&check_top_type);
+  // scratch1_reg: Current type to check.
+  EnsureIsTypeOrTypeParameter(assembler, scratch1_reg, scratch2_reg);
+  compiler::Label is_type_ref;
+  __ CompareClassId(scratch1_reg, kTypeParameterCid, scratch2_reg);
+  // Type parameters can't be top types themselves, though a particular
+  // instantiation may result in a top type.
+  __ BranchIf(EQUAL, &done);
+  __ LoadField(
+      scratch2_reg,
+      compiler::FieldAddress(scratch1_reg,
+                             compiler::target::Type::type_class_id_offset()));
+  __ SmiUntag(scratch2_reg);
+  __ CompareImmediate(scratch2_reg, kDynamicCid);
+  __ BranchIf(EQUAL, &is_top_type, compiler::Assembler::kNearJump);
+  __ CompareImmediate(scratch2_reg, kVoidCid);
+  __ BranchIf(EQUAL, &is_top_type, compiler::Assembler::kNearJump);
+  compiler::Label unwrap_future_or;
+  __ CompareImmediate(scratch2_reg, kFutureOrCid);
+  __ BranchIf(EQUAL, &unwrap_future_or, compiler::Assembler::kNearJump);
+  __ CompareImmediate(scratch2_reg, kInstanceCid);
+  __ BranchIf(NOT_EQUAL, &done, compiler::Assembler::kNearJump);
+  if (null_safety) {
+    // Instance type isn't a top type if non-nullable in null safe mode.
+    __ CompareTypeNullabilityWith(
+        scratch1_reg, static_cast<int8_t>(Nullability::kNonNullable));
+    __ BranchIf(EQUAL, &done, compiler::Assembler::kNearJump);
+  }
+  __ Bind(&is_top_type);
+  __ LoadImmediate(output_reg, 0);
+  __ Bind(&done);
+#if defined(TARGET_ARCH_IA32)
+  // Restore preserved scratch registers.
+  __ PopRegister(scratch2_reg);
+#endif
+  __ Ret();
+  // An uncommon case, so off the main trunk of the function.
+  __ Bind(&unwrap_future_or);
+  __ LoadField(scratch2_reg,
+               compiler::FieldAddress(
+                   scratch1_reg, compiler::target::Type::arguments_offset()));
+  __ CompareObject(scratch2_reg, Object::null_object());
+  // If the arguments are null, then unwrapping gives dynamic, a top type.
+  __ BranchIf(EQUAL, &is_top_type, compiler::Assembler::kNearJump);
+  __ LoadField(
+      scratch1_reg,
+      compiler::FieldAddress(
+          scratch2_reg, compiler::target::TypeArguments::type_at_offset(0)));
+  __ Jump(&check_top_type, compiler::Assembler::kNearJump);
+}
+
+void StubCodeCompiler::GenerateTypeIsTopTypeForSubtypingStub(
+    Assembler* assembler) {
+  GenerateTypeIsTopTypeForSubtyping(assembler,
+                                    /*null_safety=*/false);
+}
+
+void StubCodeCompiler::GenerateTypeIsTopTypeForSubtypingNullSafeStub(
+    Assembler* assembler) {
+  GenerateTypeIsTopTypeForSubtyping(assembler,
+                                    /*null_safety=*/true);
+}
+
+// Version of Instance::NullIsAssignableTo() used when the destination type is
+// not known at compile time. Must be kept in sync.
+//
+// Inputs:
+// - TypeTestABI::kInstanceReg: Object to check for assignability.
+// - TypeTestABI::kDstTypeReg: Destination type.
+//
+// Non-preserved non-output scratch registers:
+// - TypeTestABI::kScratchReg (only on non-IA32 architectures)
+//
+// Outputs:
+// - TypeTestABI::kSubtypeTestCacheReg: 0 if the value is guaranteed assignable,
+//   non-zero otherwise.
+//
+// All registers other than outputs and non-preserved scratches are preserved.
+static void GenerateNullIsAssignableToType(Assembler* assembler,
+                                           bool null_safety) {
+  // The only case where the original value of kSubtypeTestCacheReg is needed
+  // after the stub call is on IA32, where it's currently preserved on the stack
+  // before calling the stub (as it's also CODE_REG on that architecture), so we
+  // both use it as a scratch and clobber it for the return value.
+  const Register scratch1_reg = TypeTestABI::kSubtypeTestCacheReg;
+  // We reuse the first scratch register as the output register because we're
+  // always guaranteed to have a type in it (starting with kDstType), and all
+  // non-Smi ObjectPtrs are non-zero values.
+  const Register output_reg = scratch1_reg;
+#if defined(TARGET_ARCH_IA32)
+  // The remaining scratch registers are preserved and restored before exit on
+  // IA32. Because  we have few registers to choose from (which are all used in
+  // TypeTestABI), use specific TestTypeABI registers.
+  const Register scratch2_reg = TypeTestABI::kFunctionTypeArgumentsReg;
+  // Preserve non-output scratch registers.
+  __ PushRegister(scratch2_reg);
+#else
+  const Register scratch2_reg = TypeTestABI::kScratchReg;
+#endif
+  static_assert(scratch1_reg != scratch2_reg,
+                "code assumes distinct scratch registers");
+
+  compiler::Label is_assignable, done;
+  // Initialize the first scratch register (and thus the output register) with
+  // the destination type. We do this before the check to ensure the output
+  // register has a non-zero value if !null_safety and kInstanceReg is not null.
+  __ MoveRegister(scratch1_reg, TypeTestABI::kDstTypeReg);
+  __ CompareObject(TypeTestABI::kInstanceReg, Object::null_object());
+  if (null_safety) {
+    compiler::Label check_null_assignable;
+    // Skip checking the type if not null.
+    __ BranchIf(NOT_EQUAL, &done, compiler::Assembler::kNearJump);
+    __ Bind(&check_null_assignable);
+    // scratch1_reg: Current type to check.
+    EnsureIsTypeOrTypeParameter(assembler, scratch1_reg, scratch2_reg);
+    compiler::Label is_not_type;
+    __ CompareClassId(scratch1_reg, kTypeCid, scratch2_reg);
+    __ BranchIf(NOT_EQUAL, &is_not_type, compiler::Assembler::kNearJump);
+    __ CompareTypeNullabilityWith(
+        scratch1_reg, static_cast<int8_t>(Nullability::kNonNullable));
+    __ BranchIf(NOT_EQUAL, &is_assignable, compiler::Assembler::kNearJump);
+    // FutureOr is a special case because it may have the non-nullable bit set,
+    // but FutureOr<T> functions as the union of T and Future<T>, so it must be
+    // unwrapped to see if T is nullable.
+    __ LoadField(
+        scratch2_reg,
+        compiler::FieldAddress(scratch1_reg,
+                               compiler::target::Type::type_class_id_offset()));
+    __ SmiUntag(scratch2_reg);
+    __ CompareImmediate(scratch2_reg, kFutureOrCid);
+    __ BranchIf(NOT_EQUAL, &done, compiler::Assembler::kNearJump);
+    __ LoadField(scratch2_reg,
+                 compiler::FieldAddress(
+                     scratch1_reg, compiler::target::Type::arguments_offset()));
+    __ CompareObject(scratch2_reg, Object::null_object());
+    // If the arguments are null, then unwrapping gives the dynamic type,
+    // which can take null.
+    __ BranchIf(EQUAL, &is_assignable, compiler::Assembler::kNearJump);
+    __ LoadField(
+        scratch1_reg,
+        compiler::FieldAddress(
+            scratch2_reg, compiler::target::TypeArguments::type_at_offset(0)));
+    __ Jump(&check_null_assignable, compiler::Assembler::kNearJump);
+    __ Bind(&is_not_type);
+    // Null is assignable to a type parameter only if it is nullable.
+    __ LoadFieldFromOffset(
+        scratch2_reg, scratch1_reg,
+        compiler::target::TypeParameter::nullability_offset(), kByte);
+    __ CompareImmediate(scratch2_reg,
+                        static_cast<int8_t>(Nullability::kNonNullable));
+    __ BranchIf(EQUAL, &done, compiler::Assembler::kNearJump);
+  } else {
+    // Null in non-null-safe mode is always assignable.
+    __ BranchIf(NOT_EQUAL, &done, compiler::Assembler::kNearJump);
+  }
+  __ Bind(&is_assignable);
+  __ LoadImmediate(output_reg, 0);
+  __ Bind(&done);
+#if defined(TARGET_ARCH_IA32)
+  // Restore preserved scratch registers.
+  __ PopRegister(scratch2_reg);
+#endif
+  __ Ret();
+}
+
+void StubCodeCompiler::GenerateNullIsAssignableToTypeStub(
+    Assembler* assembler) {
+  GenerateNullIsAssignableToType(assembler,
+                                 /*null_safety=*/false);
+}
+
+void StubCodeCompiler::GenerateNullIsAssignableToTypeNullSafeStub(
+    Assembler* assembler) {
+  GenerateNullIsAssignableToType(assembler,
+                                 /*null_safety=*/true);
+}
 #if !defined(TARGET_ARCH_IA32)
 // The <X>TypeTestStubs are used to test whether a given value is of a given
 // type. All variants have the same calling convention:
diff --git a/runtime/vm/dart_entry.cc b/runtime/vm/dart_entry.cc
index 63db37e..d98eaa1 100644
--- a/runtime/vm/dart_entry.cc
+++ b/runtime/vm/dart_entry.cc
@@ -438,7 +438,7 @@
         buffer->AddString(", ");
       }
       str = NameAt(i);
-      buffer->Printf("'%s'", str.ToCString());
+      buffer->Printf("'%s' (%" Pd ")", str.ToCString(), PositionAt(i));
     }
     buffer->Printf("]");
   }
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index eb9c647..c1a8c93 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -3513,8 +3513,7 @@
   invocation.SetParameterTypeAt(0, Object::dynamic_type());
   invocation.SetParameterNameAt(0, Symbols::This());
   // Remaining positional parameters.
-  intptr_t i = 1;
-  for (; i < desc.PositionalCount(); i++) {
+  for (intptr_t i = 1; i < desc.PositionalCount(); i++) {
     invocation.SetParameterTypeAt(i, Object::dynamic_type());
     char name[64];
     Utils::SNPrint(name, 64, ":p%" Pd, i);
@@ -3523,10 +3522,11 @@
   }
 
   // Named parameters.
-  for (; i < desc.Count(); i++) {
-    invocation.SetParameterTypeAt(i, Object::dynamic_type());
-    intptr_t index = i - desc.PositionalCount();
-    invocation.SetParameterNameAt(i, String::Handle(zone, desc.NameAt(index)));
+  for (intptr_t i = 0; i < desc.NamedCount(); i++) {
+    const intptr_t param_index = desc.PositionAt(i);
+    const auto& param_name = String::Handle(zone, desc.NameAt(i));
+    invocation.SetParameterTypeAt(param_index, Object::dynamic_type());
+    invocation.SetParameterNameAt(param_index, param_name);
   }
   invocation.TruncateUnusedParameterFlags();
   invocation.set_result_type(Object::dynamic_type());
@@ -7007,13 +7007,13 @@
   const auto& closure_data =
       ClosureData::Handle(ClosureData::RawCast(raw_ptr()->data_));
   ASSERT(!closure_data.IsNull());
+  intptr_t updated_info = closure_data.default_type_arguments_info();
   auto kind = DefaultTypeArgumentsKindFor(value);
   ASSERT(kind != DefaultTypeArgumentsKind::kInvalid);
-  const intptr_t num_parent_type_params = NumParentTypeParameters();
-  const intptr_t default_type_args_info =
-      DefaultTypeArgumentsKindField::encode(kind) |
-      NumParentTypeParametersField::encode(num_parent_type_params);
-  closure_data.set_default_type_arguments_info(default_type_args_info);
+  updated_info = DefaultTypeArgumentsKindField::update(kind, updated_info);
+  updated_info = NumParentTypeParametersField::update(NumParentTypeParameters(),
+                                                      updated_info);
+  closure_data.set_default_type_arguments_info(updated_info);
   // We could just store null for the ksharesFunction/kSharesInstantiator cases,
   // assuming all clients retrieve the DefaultTypeArgumentsKind to distinguish.
   closure_data.set_default_type_arguments(value);
@@ -9644,15 +9644,20 @@
 }
 
 bool Function::PrologueNeedsArgumentsDescriptor() const {
+  // These functions have a saved compile-time arguments descriptor that is
+  // used in lieu of the runtime arguments descriptor in generated IL.
+  if (IsInvokeFieldDispatcher() || IsNoSuchMethodDispatcher()) {
+    return false;
+  }
   // The prologue of those functions need to examine the arg descriptor for
   // various purposes.
-  return IsGeneric() || HasOptionalParameters();
+  return IsGeneric() || HasOptionalParameters() ||
+         CanReceiveDynamicInvocation();
 }
 
 bool Function::MayHaveUncheckedEntryPoint() const {
   return FLAG_enable_multiple_entrypoints &&
-         (NeedsTypeArgumentTypeChecks() || NeedsArgumentTypeChecks() ||
-          IsImplicitClosureFunction());
+         (NeedsTypeArgumentTypeChecks() || NeedsArgumentTypeChecks());
 }
 
 const char* Function::ToCString() const {
@@ -9751,6 +9756,13 @@
 }
 
 intptr_t ClosureData::default_type_arguments_info() const {
+  const SmiPtr value = raw_ptr()->default_type_arguments_info_;
+  if (value == Smi::null()) {
+    static_assert(Function::DefaultTypeArgumentsKindField::decode(0) ==
+                      Function::DefaultTypeArgumentsKind::kInvalid,
+                  "Returning valid value for null Smi");
+    return 0;
+  }
   return Smi::Value(raw_ptr()->default_type_arguments_info_);
 }
 
@@ -18297,6 +18309,8 @@
   return other.IsLegacy() && (other.IsObjectType() || other.IsNeverType());
 }
 
+// Must be kept in sync with GenerateNullIsAssignableToType in
+// stub_code_compiler.cc if any changes are made.
 bool Instance::NullIsAssignableTo(const AbstractType& other) {
   Thread* thread = Thread::Current();
   Isolate* isolate = thread->isolate();
@@ -19115,6 +19129,8 @@
   return false;
 }
 
+// Must be kept in sync with GenerateTypeIsTopTypeForSubtyping in
+// stub_code_compiler.cc if any changes are made.
 bool AbstractType::IsTopTypeForSubtyping() const {
   const classid_t cid = type_class_id();
   if (cid == kDynamicCid || cid == kVoidCid) {
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index eb290ea..6538625 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -2694,23 +2694,20 @@
                     (1 << kDefaultTypeArgumentsKindFieldSize),
                 "Wrong bit size chosen for default TAV kind field");
 
-  // Fields encoded in an integer stored alongside a default TAV.
+  // Fields encoded in an integer stored alongside a default TAV. The size of
+  // the integer should be <= the size of a target Smi.
   using DefaultTypeArgumentsKindField =
       BitField<intptr_t,
                DefaultTypeArgumentsKind,
                0,
                kDefaultTypeArgumentsKindFieldSize>;
-  // If more space is needed, we can almost certainly reduce the size of this
-  // field.
+  // Just use the rest of the space for the number of parent type parameters.
   using NumParentTypeParametersField =
       BitField<intptr_t,
-               uint16_t,
+               intptr_t,
                DefaultTypeArgumentsKindField::kNextBit,
-               kBitsPerByte * sizeof(uint16_t)>;
-
-  static_assert(NumParentTypeParametersField::kNextBit <=
-                    compiler::target::kSmiBits,
-                "Default TAV info does not fit in a target Smi");
+               compiler::target::kSmiBits -
+                   DefaultTypeArgumentsKindField::kNextBit>;
 
   // Returns a canonicalized vector of the type parameters instantiated
   // to bounds. If non-generic, the empty type arguments vector is returned.
@@ -2839,10 +2836,6 @@
     return (kind() == FunctionLayout::kConstructor) && is_static();
   }
 
-  static bool ClosureBodiesContainNonCovariantArgumentChecks() {
-    return FLAG_precompiled_mode || FLAG_lazy_dispatchers;
-  }
-
   // Whether this function can receive an invocation where the number and names
   // of arguments have not been checked.
   bool CanReceiveDynamicInvocation() const { return IsFfiTrampoline(); }
@@ -2910,13 +2903,11 @@
   }
   bool IsInFactoryScope() const;
 
-  bool NeedsArgumentTypeChecks() const {
-    return (IsClosureFunction() &&
-            ClosureBodiesContainNonCovariantArgumentChecks()) ||
-           !(is_static() || (kind() == FunctionLayout::kConstructor));
+  bool NeedsTypeArgumentTypeChecks() const {
+    return !(is_static() || (kind() == FunctionLayout::kConstructor));
   }
 
-  bool NeedsTypeArgumentTypeChecks() const {
+  bool NeedsArgumentTypeChecks() const {
     return !(is_static() || (kind() == FunctionLayout::kConstructor));
   }
 
@@ -6889,12 +6880,10 @@
   ArrayPtr cache() const;
 
  private:
-  // A VM heap allocated preinitialized empty subtype entry array.
-  static ArrayPtr cached_array_;
-
   void set_cache(const Array& value) const;
 
-  intptr_t TestEntryLength() const;
+  // A VM heap allocated preinitialized empty subtype entry array.
+  static ArrayPtr cached_array_;
 
   FINAL_HEAP_OBJECT_IMPLEMENTATION(SubtypeTestCache, Object);
   friend class Class;
@@ -8163,6 +8152,9 @@
     return TypeParameterLayout::DeclarationBit::decode(raw_ptr()->flags_);
   }
   void SetDeclaration(bool value) const;
+  static intptr_t nullability_offset() {
+    return OFFSET_OF(TypeParameterLayout, nullability_);
+  }
   virtual Nullability nullability() const {
     return static_cast<Nullability>(raw_ptr()->nullability_);
   }
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index f4bf134..c9cf923 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -74,20 +74,17 @@
                     Symbols::CurrentContextVar(), Object::dynamic_type());
   current_context_var_ = temp;
 
-  const bool reify_generic_argument = function.IsGeneric();
-
-  const bool load_optional_arguments = function.HasOptionalParameters();
-
-  const bool check_arguments = function.CanReceiveDynamicInvocation();
-
-  const bool need_argument_descriptor =
-      load_optional_arguments || check_arguments || reify_generic_argument;
-
-  if (need_argument_descriptor) {
+  if (function.PrologueNeedsArgumentsDescriptor()) {
     arg_desc_var_ = new (zone())
         LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
                       Symbols::ArgDescVar(), Object::dynamic_type());
   }
+
+  // The code generated by the prologue builder for loading optional arguments
+  // requires the expression temporary variable.
+  if (function.HasOptionalParameters()) {
+    EnsureExpressionTemp();
+  }
 }
 
 void ParsedFunction::AddToGuardedFields(const Field* field) const {
@@ -339,19 +336,33 @@
 ParsedFunction::EnsureDynamicClosureCallVars() {
   ASSERT(function().IsDynamicClosureCallDispatcher(thread()));
   if (dynamic_closure_call_vars_ != nullptr) return dynamic_closure_call_vars_;
-  dynamic_closure_call_vars_ = new (zone()) DynamicClosureCallVars();
+  const auto& saved_args_desc =
+      Array::Handle(zone(), function().saved_args_desc());
+  const ArgumentsDescriptor descriptor(saved_args_desc);
 
+  dynamic_closure_call_vars_ =
+      new (zone()) DynamicClosureCallVars(zone(), descriptor.NamedCount());
+
+  auto const pos = function().token_pos();
   const auto& type_Dynamic = Object::dynamic_type();
   const auto& type_Function =
       Type::ZoneHandle(zone(), Type::DartFunctionType());
   const auto& type_Smi = Type::ZoneHandle(zone(), Type::SmiType());
 #define INIT_FIELD(Name, TypeName, Symbol)                                     \
-  dynamic_closure_call_vars_->Name = new (zone())                              \
-      LocalVariable(function().token_pos(), function().token_pos(),            \
-                    Symbols::DynamicCall##Symbol##Var(), type_##TypeName);
+  dynamic_closure_call_vars_->Name = new (zone()) LocalVariable(               \
+      pos, pos, Symbols::DynamicCall##Symbol##Var(), type_##TypeName);
   FOR_EACH_DYNAMIC_CLOSURE_CALL_VARIABLE(INIT_FIELD);
 #undef INIT_FIELD
 
+  for (intptr_t i = 0; i < descriptor.NamedCount(); i++) {
+    auto const name = OS::SCreate(
+        zone(), ":dyn_call_named_argument_%" Pd "_parameter_index", i);
+    auto const var = new (zone()) LocalVariable(
+        pos, pos, String::ZoneHandle(zone(), Symbols::New(thread(), name)),
+        type_Smi);
+    dynamic_closure_call_vars_->named_argument_parameter_indices.Add(var);
+  }
+
   return dynamic_closure_call_vars_;
 }
 
diff --git a/runtime/vm/parser.h b/runtime/vm/parser.h
index 76b6249..2aa73d8 100644
--- a/runtime/vm/parser.h
+++ b/runtime/vm/parser.h
@@ -237,6 +237,9 @@
   // Variables needed for the InvokeFieldDispatcher for dynamic closure calls,
   // because they are both read and written to by the builders.
   struct DynamicClosureCallVars : ZoneAllocated {
+    DynamicClosureCallVars(Zone* zone, intptr_t num_named)
+        : named_argument_parameter_indices(zone, num_named) {}
+
 #define FOR_EACH_DYNAMIC_CLOSURE_CALL_VARIABLE(V)                              \
   V(current_function, Function, CurrentFunction)                               \
   V(current_num_processed, Smi, CurrentNumProcessed)                           \
@@ -246,6 +249,10 @@
 #define DEFINE_FIELD(Name, _, __) LocalVariable* Name = nullptr;
     FOR_EACH_DYNAMIC_CLOSURE_CALL_VARIABLE(DEFINE_FIELD)
 #undef DEFINE_FIELD
+
+    // An array of local variables, one for each named parameter in the
+    // saved arguments descriptor.
+    ZoneGrowableArray<LocalVariable*> named_argument_parameter_indices;
   };
 
   DynamicClosureCallVars* dynamic_closure_call_vars() const {
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index 93c3f4f..db0d99f 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -806,10 +806,10 @@
   ASSERT(mode == kTypeCheckFromInline);
 #endif
 
-  ASSERT(!dst_type.IsDynamicType());  // No need to check assignment.
-  // A null instance is already detected and allowed in inlined code, unless
-  // strong checking is enabled.
+  // These are guaranteed on the calling side.
+  ASSERT(!dst_type.IsDynamicType());
   ASSERT(!src_instance.IsNull() || isolate->null_safety());
+
   const bool is_instance_of = src_instance.IsAssignableTo(
       dst_type, instantiator_type_arguments, function_type_arguments);
 
@@ -819,16 +819,6 @@
                    Bool::Get(is_instance_of));
   }
   if (!is_instance_of) {
-    // Throw a dynamic type error.
-    const TokenPosition location = GetCallerLocation();
-    const AbstractType& src_type =
-        AbstractType::Handle(zone, src_instance.GetType(Heap::kNew));
-    if (!dst_type.IsInstantiated()) {
-      // Instantiate dst_type before reporting the error.
-      dst_type = dst_type.InstantiateFrom(instantiator_type_arguments,
-                                          function_type_arguments, kAllFree,
-                                          Heap::kNew);
-    }
     if (dst_name.IsNull()) {
 #if !defined(TARGET_ARCH_IA32)
       // Can only come here from type testing stub.
@@ -852,6 +842,56 @@
 #endif
     }
 
+    if (dst_name.raw() ==
+        Symbols::dynamic_assert_assignable_stc_check().raw()) {
+#if !defined(TARGET_ARCH_IA32)
+      // Can only come here from type testing stub via dynamic AssertAssignable.
+      ASSERT(mode != kTypeCheckFromInline);
+#endif
+      // This was a dynamic closure call where the destination name was not
+      // known at compile-time. Thus, fetch the original arguments and arguments
+      // descriptor and re-do the type check  in the runtime, which causes the
+      // error with the proper destination name to be thrown.
+      DartFrameIterator iterator(thread,
+                                 StackFrameIterator::kNoCrossThreadIteration);
+      StackFrame* caller_frame = iterator.NextFrame();
+      const auto& dispatcher =
+          Function::Handle(zone, caller_frame->LookupDartFunction());
+      ASSERT(dispatcher.IsInvokeFieldDispatcher());
+      const auto& orig_arguments_desc =
+          Array::Handle(zone, dispatcher.saved_args_desc());
+      const ArgumentsDescriptor args_desc(orig_arguments_desc);
+      const intptr_t arg_count = args_desc.CountWithTypeArgs();
+      const auto& orig_arguments = Array::Handle(zone, Array::New(arg_count));
+      auto& obj = Object::Handle(zone);
+      for (intptr_t i = 0; i < arg_count; i++) {
+        obj = *reinterpret_cast<ObjectPtr*>(
+            ParamAddress(caller_frame->fp(), arg_count - i));
+        orig_arguments.SetAt(i, obj);
+      }
+      const auto& receiver = Closure::CheckedHandle(
+          zone, orig_arguments.At(args_desc.FirstArgIndex()));
+      const auto& function = Function::Handle(zone, receiver.function());
+      const auto& result = Object::Handle(
+          zone, function.DoArgumentTypesMatch(orig_arguments, args_desc));
+      if (result.IsError()) {
+        Exceptions::PropagateError(Error::Cast(result));
+      }
+      // IsAssignableTo returned false, so we should have thrown a type
+      // error in DoArgumentsTypesMatch.
+      UNREACHABLE();
+    }
+
+    // Throw a dynamic type error.
+    const TokenPosition location = GetCallerLocation();
+    const AbstractType& src_type =
+        AbstractType::Handle(zone, src_instance.GetType(Heap::kNew));
+    if (!dst_type.IsInstantiated()) {
+      // Instantiate dst_type before reporting the error.
+      dst_type = dst_type.InstantiateFrom(instantiator_type_arguments,
+                                          function_type_arguments, kAllFree,
+                                          Heap::kNew);
+    }
     Exceptions::CreateAndThrowTypeError(location, src_type, dst_type, dst_name);
     UNREACHABLE();
   }
diff --git a/runtime/vm/stub_code.cc b/runtime/vm/stub_code.cc
index 7f8f3a3..3dea626 100644
--- a/runtime/vm/stub_code.cc
+++ b/runtime/vm/stub_code.cc
@@ -277,6 +277,16 @@
   return Code::null();
 }
 
+const Code& StubCode::GetTypeIsTopTypeForSubtyping(bool null_safety) {
+  return null_safety ? StubCode::TypeIsTopTypeForSubtypingNullSafe()
+                     : StubCode::TypeIsTopTypeForSubtyping();
+}
+
+const Code& StubCode::GetNullIsAssignableToType(bool null_safety) {
+  return null_safety ? StubCode::NullIsAssignableToTypeNullSafe()
+                     : StubCode::NullIsAssignableToType();
+}
+
 #if !defined(TARGET_ARCH_IA32)
 CodePtr StubCode::GetBuildMethodExtractorStub(
     compiler::ObjectPoolBuilder* pool) {
diff --git a/runtime/vm/stub_code.h b/runtime/vm/stub_code.h
index a946cd5..d54ee02 100644
--- a/runtime/vm/stub_code.h
+++ b/runtime/vm/stub_code.h
@@ -68,6 +68,10 @@
   static CodePtr GetBuildMethodExtractorStub(compiler::ObjectPoolBuilder* pool);
 #endif
 
+  static const Code& GetTypeIsTopTypeForSubtyping(bool null_safety);
+
+  static const Code& GetNullIsAssignableToType(bool null_safety);
+
 #if !defined(DART_PRECOMPILED_RUNTIME)
   // Generate the stub and finalize the generated code into the stub
   // code executable area.
diff --git a/runtime/vm/stub_code_list.h b/runtime/vm/stub_code_list.h
index 3d9a9b9..c08bfd6 100644
--- a/runtime/vm/stub_code_list.h
+++ b/runtime/vm/stub_code_list.h
@@ -86,6 +86,10 @@
   V(OneArgUnoptimizedStaticCall)                                               \
   V(TwoArgsUnoptimizedStaticCall)                                              \
   V(AssertSubtype)                                                             \
+  V(TypeIsTopTypeForSubtyping)                                                 \
+  V(TypeIsTopTypeForSubtypingNullSafe)                                         \
+  V(NullIsAssignableToType)                                                    \
+  V(NullIsAssignableToTypeNullSafe)                                            \
   V(Subtype1TestCache)                                                         \
   V(Subtype3TestCache)                                                         \
   V(Subtype5TestCache)                                                         \
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index a1db78b..bbee27e 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -463,6 +463,9 @@
   V(controller, "controller")                                                  \
   V(current_character, ":current_character")                                   \
   V(current_position, ":current_position")                                     \
+  V(dynamic_assert_assignable_stc_check,                                       \
+    ":dynamic_assert_assignable_stc_check")                                    \
+  V(getID, "getID")                                                            \
   V(hashCode, "get:hashCode")                                                  \
   V(identityHashCode, "identityHashCode")                                      \
   V(index_temp, ":index_temp")                                                 \
diff --git a/tests/language/closure/dynamic_test.dart b/tests/language/closure/dynamic_test.dart
new file mode 100644
index 0000000..eb954ec
--- /dev/null
+++ b/tests/language/closure/dynamic_test.dart
@@ -0,0 +1,63 @@
+// Copyright (c) 2020, 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.
+//
+// Test that dynamic invocation of closures works as expected, including
+// appropriate type checks.
+//
+// VMOptions=--lazy-dispatchers
+// VMOptions=--no-lazy-dispatchers
+
+import 'package:expect/expect.dart';
+
+class A {
+  final int nonce_;
+
+  const A(this.nonce_);
+}
+
+class B {
+  final int nonce_;
+
+  const B(this.nonce_);
+}
+
+class C extends A {
+  const C(int nonce) : super(nonce);
+}
+
+void main() {
+  dynamic f = (String a1, int a2, A a3,
+      {String n1 = "default_named", int n2 = -1, A n3 = const A(-1)}) {};
+
+  f("test_fixed", 1, A(1), n1: "test_named", n2: 2, n3: A(2));
+
+  // Test named argument permutations
+  f("test_fixed", 1, A(1), n1: "test_named", n3: A(2), n2: 2);
+  f("test_fixed", 1, A(1), n2: 2, n1: "test_named", n3: A(2));
+  f("test_fixed", 1, A(1), n2: 2, n3: A(2), n1: "test_named");
+  f("test_fixed", 1, A(1), n3: A(2), n1: "test_named", n2: 2);
+  f("test_fixed", 1, A(1), n3: A(2), n2: 2, n1: "test_named");
+
+  // Test subclasses match the type
+  f("test_fixed", 1, C(1), n1: "test_named", n2: 2, n3: A(2));
+  f("test_fixed", 1, A(1), n1: "test_named", n2: 2, n3: C(2));
+
+  // Should fail with no such method errors
+  Expect.throwsNoSuchMethodError(() => f());
+  Expect.throwsNoSuchMethodError(() => f("test_fixed", 1, A(1), n4: 4));
+
+  // Should fail with type errors
+  Expect.throwsTypeError(
+      () => f(100, 1, A(1), n1: "test_named", n2: 2, n3: A(2)));
+  Expect.throwsTypeError(
+      () => f("test_fixed", 1.1, A(1), n1: "test_named", n2: 2, n3: A(2)));
+  Expect.throwsTypeError(
+      () => f("test_fixed", 1, B(1), n1: "test_named", n2: 2, n3: A(2)));
+  Expect.throwsTypeError(
+      () => f("test_fixed", 1, A(1), n1: 100, n2: 2, n3: A(2)));
+  Expect.throwsTypeError(
+      () => f("test_fixed", 1, A(1), n1: "test_named", n2: 2.2, n3: A(2)));
+  Expect.throwsTypeError(
+      () => f("test_fixed", 1, A(1), n1: "test_named", n2: 2, n3: B(2)));
+}
diff --git a/tests/language/stack_trace/stack_trace_test.dart b/tests/language/stack_trace/stack_trace_test.dart
index 5e4d7c6..b1e60a9 100644
--- a/tests/language/stack_trace/stack_trace_test.dart
+++ b/tests/language/stack_trace/stack_trace_test.dart
@@ -88,6 +88,7 @@
   var config = 0;
 
   @pragma("vm:entry-point") // Prevent obfuscation
+  @pragma("vm:never-inline") // Prevent inlining
   issue12940() {
     throw "Progy";
   }
@@ -121,7 +122,7 @@
       try {
         d();
       } catch (e, s) {
-        Expect.isTrue(s.toString().contains("issue12940"));
+        Expect.contains("issue12940", s.toString());
       }
     }
   }
diff --git a/tests/language_2/closure/dynamic_test.dart b/tests/language_2/closure/dynamic_test.dart
new file mode 100644
index 0000000..b130a9b
--- /dev/null
+++ b/tests/language_2/closure/dynamic_test.dart
@@ -0,0 +1,62 @@
+// Copyright (c) 2020, 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.
+//
+// Test that dynamic invocation of closures works as expected, including
+// appropriate type checks.
+//
+// VMOptions=--lazy-dispatchers
+// VMOptions=--no-lazy-dispatchers
+
+import 'package:expect/expect.dart';
+
+class A {
+  final int nonce_;
+
+  A(this.nonce_);
+}
+
+class B {
+  final int nonce_;
+
+  B(this.nonce_);
+}
+
+class C extends A {
+  C(int nonce) : super(nonce);
+}
+
+void main() {
+  dynamic f = (String a1, int a2, A a3, {String n1, int n2, A n3}) {};
+
+  f("test_fixed", 1, A(1), n1: "test_named", n2: 2, n3: A(2));
+
+// Test named argument permutations
+  f("test_fixed", 1, A(1), n1: "test_named", n3: A(2), n2: 2);
+  f("test_fixed", 1, A(1), n2: 2, n1: "test_named", n3: A(2));
+  f("test_fixed", 1, A(1), n2: 2, n3: A(2), n1: "test_named");
+  f("test_fixed", 1, A(1), n3: A(2), n1: "test_named", n2: 2);
+  f("test_fixed", 1, A(1), n3: A(2), n2: 2, n1: "test_named");
+
+// Test subclasses match the type
+  f("test_fixed", 1, C(1), n1: "test_named", n2: 2, n3: A(2));
+  f("test_fixed", 1, A(1), n1: "test_named", n2: 2, n3: C(2));
+
+// Should fail with no such method errors
+  Expect.throwsNoSuchMethodError(() => f());
+  Expect.throwsNoSuchMethodError(() => f("test_fixed", 1, A(1), n4: 4));
+
+// Should fail with type errors
+  Expect.throwsTypeError(
+      () => f(100, 1, A(1), n1: "test_named", n2: 2, n3: A(2)));
+  Expect.throwsTypeError(
+      () => f("test_fixed", 1.1, A(1), n1: "test_named", n2: 2, n3: A(2)));
+  Expect.throwsTypeError(
+      () => f("test_fixed", 1, B(1), n1: "test_named", n2: 2, n3: A(2)));
+  Expect.throwsTypeError(
+      () => f("test_fixed", 1, A(1), n1: 100, n2: 2, n3: A(2)));
+  Expect.throwsTypeError(
+      () => f("test_fixed", 1, A(1), n1: "test_named", n2: 2.2, n3: A(2)));
+  Expect.throwsTypeError(
+      () => f("test_fixed", 1, A(1), n1: "test_named", n2: 2, n3: B(2)));
+}
diff --git a/tests/language_2/stack_trace/stack_trace_test.dart b/tests/language_2/stack_trace/stack_trace_test.dart
index 5e4d7c6..b1e60a9 100644
--- a/tests/language_2/stack_trace/stack_trace_test.dart
+++ b/tests/language_2/stack_trace/stack_trace_test.dart
@@ -88,6 +88,7 @@
   var config = 0;
 
   @pragma("vm:entry-point") // Prevent obfuscation
+  @pragma("vm:never-inline") // Prevent inlining
   issue12940() {
     throw "Progy";
   }
@@ -121,7 +122,7 @@
       try {
         d();
       } catch (e, s) {
-        Expect.isTrue(s.toString().contains("issue12940"));
+        Expect.contains("issue12940", s.toString());
       }
     }
   }
diff --git a/tools/VERSION b/tools/VERSION
index 5a845c02..3da93db 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 57
+PRERELEASE 58
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index 582dd82..6cca3d0 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -748,7 +748,7 @@
         "builder-tag": "vm_nnbd"
       }
     },
-    "dartkp-weak-asserts-(linux|mac)-(debug|product|release)-simarm64": {
+    "dartkp-weak-asserts-(linux|mac)-(debug|product|release)-(simarm|simarm64)": {
       "options": {
         "enable-asserts": true,
         "use-elf": true,
@@ -778,7 +778,7 @@
         "builder-tag": "vm_nnbd"
       }
     },
-    "dartkp-strong-(linux|mac)-(debug|product|release)-simarm64": {
+    "dartkp-strong-(linux|mac)-(debug|product|release)-(simarm|simarm64)": {
       "options": {
         "use-elf": true,
         "builder-tag": "vm_nnbd"