Version 2.12.0-32.0.dev

Merge commit '4cd0926703de6e0c0505f1c6736fa77fa54b0254' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index 041bc3f..24ce857 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -148,7 +148,7 @@
       "name": "cli_util",
       "rootUri": "../third_party/pkg/cli_util",
       "packageUri": "lib/",
-      "languageVersion": "2.0"
+      "languageVersion": "2.12"
     },
     {
       "name": "clock",
@@ -704,7 +704,7 @@
       "name": "usage",
       "rootUri": "../third_party/pkg/usage",
       "packageUri": "lib/",
-      "languageVersion": "2.0"
+      "languageVersion": "2.12"
     },
     {
       "name": "vector_math",
@@ -764,7 +764,7 @@
       "name": "yaml",
       "rootUri": "../third_party/pkg/yaml",
       "packageUri": "lib/",
-      "languageVersion": "2.4"
+      "languageVersion": "2.12"
     }
   ]
 }
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c1ed2a0..23f93f2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -100,19 +100,28 @@
 * The top level `pub` executable has been deprecated. Use `dart pub` instead.
   See [dart tool][].
 * New command `dart pub add` that adds  new dependencies to your `pubspec.yaml`.
-  
+
   And a corresponding `dart pub remove` that removes dependencies.
 * New option `dart pub outdated --mode=null-safety` that will analyze your
   dependencies for null-safety.
 * `dart pub publish` will now check your pubspec keys for likely typos.
 * New command `dart pub login` that logs in to pub.dev.
 * The `--server` option to `dart pub publish` and `dart pub uploader` have been
-  deprecated. Use `publish_to` in your `pubspec.yaml` or set the 
+  deprecated. Use `publish_to` in your `pubspec.yaml` or set the
   `$PUB_HOSTED_URL` environment variable.
 
 [#44072]: https://github.com/dart-lang/sdk/issues/44072
 [dart tool]: https://dart.dev/tools/dart-tool
 
+## 2.10.4 - 2020-11-12
+
+This is a patch release that fixes a crash in the Dart VM (issues [#43941][],
+[flutter/flutter#43620][], and [Dart-Code/Dart-Code#2814][]).
+
+[#43941]: https://github.com/dart-lang/sdk/issues/43941
+[flutter/flutter#43620]: https://github.com/flutter/flutter/issues/43620
+[Dart-Code/Dart-Code#2814]: https://github.com/Dart-Code/Dart-Code/issues/2814
+
 ## 2.10.3 - 2020-10-29
 
 This is a patch release that fixes the following issues:
diff --git a/DEPS b/DEPS
index 1784f7a..a93ac4f 100644
--- a/DEPS
+++ b/DEPS
@@ -78,7 +78,7 @@
   "browser-compat-data_tag": "v1.0.22",
   "charcode_rev": "bcd8a12c315b7a83390e4865ad847ecd9344cba2",
   "chrome_rev" : "19997",
-  "cli_util_rev" : "335ed165887d0ec97c2a09173ebf22dcf56a6c4e",
+  "cli_util_rev" : "50cc840b146615899e97b892578848401b2028d5",
   "clock_rev" : "a494269254ba978e7ef8f192c5f7fec3fc05b9d3",
   "collection_rev": "e4bb038ce2d8e66fb15818aa40685c68d53692ab",
   "convert_rev": "dd3bd28f63be7cb8ab961f38bc73229e4473b555",
@@ -118,7 +118,7 @@
   "jinja2_rev": "2222b31554f03e62600cd7e383376a7c187967a1",
   "json_rpc_2_rev": "b8dfe403fd8528fd14399dee3a6527b55802dd4d",
   "linter_tag": "0.1.124",
-  "logging_rev": "9d2a7fdd05b09bc06474881152b5baaf38fd1329",
+  "logging_rev": "e2f633b543ef89c54688554b15ca3d7e425b86a2",
   "markupsafe_rev": "8f45f5cfa0009d2a70589bcda0349b8cb2b72783",
   "markdown_rev": "6f89681d59541ddb1cf3a58efbdaa2304ffc3f51",
   "matcher_rev": "9cae8faa7868bf3a88a7ba45eb0bd128e66ac515",
@@ -159,14 +159,14 @@
   "test_rev": "e37a93bbeae23b215972d1659ac865d71287ff6a",
   "tflite_native_rev": "0.4.0+1",
   "typed_data_tag": "f94fc57b8e8c0e4fe4ff6cfd8290b94af52d3719",
-  "usage_tag": "3.4.0",
+  "usage_tag": "16fbfd90c58f16e016a295a880bc722d2547d2c9",
   "vector_math_rev": "0c9f5d68c047813a6dcdeb88ba7a42daddf25025",
   "watcher_rev": "64e254eba16f56d41f10d72c0b1cb24e130e1f8b",
   "webdriver_rev": "5a8d6805d9cf8a3cbb4fcd64849b538b7491e50e",
   "web_components_rev": "8f57dac273412a7172c8ade6f361b407e2e4ed02",
   "web_socket_channel_rev": "490061ef0e22d3c8460ad2802f9948219365ad6b",
   "WebCore_rev": "fb11e887f77919450e497344da570d780e078bc8",
-  "yaml_rev": "e5de429147a6b0fcb7e8ddb3c8e4674dc5dd0ecc",
+  "yaml_rev": "925c406f8bdb06ce7935f0a7d03187b36c6b62d0",
   "zlib_rev": "c44fb7248079cc3d5563b14b3f758aee60d6b415",
   "crashpad_rev": "bf327d8ceb6a669607b0dbab5a83a275d03f99ed",
   "minichromium_rev": "8d641e30a8b12088649606b912c2bc4947419ccc",
diff --git a/pkg/analysis_server/lib/plugin/edit/assist/assist_dart.dart b/pkg/analysis_server/lib/plugin/edit/assist/assist_dart.dart
index 54b682a..3154306 100644
--- a/pkg/analysis_server/lib/plugin/edit/assist/assist_dart.dart
+++ b/pkg/analysis_server/lib/plugin/edit/assist/assist_dart.dart
@@ -3,12 +3,17 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/instrumentation/service.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_workspace.dart';
 
 /// An object used to provide context information for Dart assist contributors.
 ///
 /// Clients may not extend, implement or mix-in this class.
 abstract class DartAssistContext {
+  /// Return the instrumentation service used to report errors that prevent a
+  /// fix from being composed.
+  InstrumentationService get instrumentationService;
+
   /// The resolution result in which assist operates.
   ResolvedUnitResult get resolveResult;
 
diff --git a/pkg/analysis_server/lib/plugin/edit/fix/fix_dart.dart b/pkg/analysis_server/lib/plugin/edit/fix/fix_dart.dart
index ede6d26..4792bed 100644
--- a/pkg/analysis_server/lib/plugin/edit/fix/fix_dart.dart
+++ b/pkg/analysis_server/lib/plugin/edit/fix/fix_dart.dart
@@ -5,12 +5,17 @@
 import 'package:analysis_server/plugin/edit/fix/fix_core.dart';
 import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart';
 import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/instrumentation/service.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_workspace.dart';
 
 /// An object used to provide context information for [DartFixContributor]s.
 ///
 /// Clients may not extend, implement or mix-in this class.
 abstract class DartFixContext implements FixContext {
+  /// Return the instrumentation service used to report errors that prevent a
+  /// fix from being composed.
+  InstrumentationService get instrumentationService;
+
   /// The resolution result in which fix operates.
   ResolvedUnitResult get resolveResult;
 
diff --git a/pkg/analysis_server/lib/src/cider/fixes.dart b/pkg/analysis_server/lib/src/cider/fixes.dart
index 099287a..60f649e 100644
--- a/pkg/analysis_server/lib/src/cider/fixes.dart
+++ b/pkg/analysis_server/lib/src/cider/fixes.dart
@@ -7,6 +7,7 @@
 import 'package:analysis_server/src/services/correction/fix.dart';
 import 'package:analysis_server/src/services/correction/fix_internal.dart';
 import 'package:analyzer/error/error.dart';
+import 'package:analyzer/instrumentation/service.dart';
 import 'package:analyzer/src/dart/analysis/performance_logger.dart';
 import 'package:analyzer/src/dart/micro/resolve_file.dart';
 import 'package:meta/meta.dart';
@@ -43,6 +44,7 @@
         if (errorLine == requestLine) {
           var workspace = DartChangeWorkspace([resolvedUnit.session]);
           var context = DartFixContextImpl(
+            InstrumentationService.NULL_SERVICE,
             workspace,
             resolvedUnit,
             error,
diff --git a/pkg/analysis_server/lib/src/context_manager.dart b/pkg/analysis_server/lib/src/context_manager.dart
index 5c09be3..c37c64c 100644
--- a/pkg/analysis_server/lib/src/context_manager.dart
+++ b/pkg/analysis_server/lib/src/context_manager.dart
@@ -887,9 +887,9 @@
   }
 
   void _checkForPackagespecUpdate(String path, ContextInfo info) {
-    // Check to see if this is the .packages file for this context and if so,
-    // update the context's source factory.
-    if (pathContext.basename(path) == PACKAGE_SPEC_NAME) {
+    // Check to see if this is `.dart_tool/package_config.json` or `.packages`
+    // file for this context and if so, update the context's source factory.
+    if (_isPackageConfigJsonFilePath(path) || _isDotPackagesFilePath(path)) {
       var driver = info.analysisDriver;
       if (driver == null) {
         // I suspect that this happens as a result of a race condition: server
@@ -1249,6 +1249,9 @@
     if (info.hasDependency(path)) {
       _recomputeFolderDisposition(info);
     }
+
+    _checkForPackagespecUpdate(path, info);
+
     // maybe excluded globally
     if (_isExcluded(path) ||
         _isContainedInDotFolder(info.folder.path, path) ||
@@ -1283,7 +1286,7 @@
                 return;
               }
             }
-            if (_isPackagespec(path)) {
+            if (_isDotPackagesFilePath(path)) {
               // Check for a sibling pubspec.yaml file.
               if (!resourceProvider
                   .getFile(pathContext.join(directoryPath, PUBSPEC_NAME))
@@ -1323,7 +1326,7 @@
                 return;
               }
             }
-            if (_isPackagespec(path)) {
+            if (_isDotPackagesFilePath(path)) {
               // Check for a sibling pubspec.yaml file.
               if (!resourceProvider
                   .getFile(pathContext.join(directoryPath, PUBSPEC_NAME))
@@ -1352,7 +1355,6 @@
           }
         }
     }
-    _checkForPackagespecUpdate(path, info);
     _checkForAnalysisOptionsUpdate(path, info);
     _checkForDataFileUpdate(path, info);
     _checkForPubspecUpdate(path, info);
@@ -1388,6 +1390,10 @@
   /// to specify data-driven fixes.
   bool _isDataFile(String path) => pathContext.basename(path) == dataFileName;
 
+  bool _isDotPackagesFilePath(String path) {
+    return pathContext.basename(path) == PACKAGE_SPEC_NAME;
+  }
+
   /// Returns `true` if the given [path] is excluded by [excludedPaths].
   bool _isExcluded(String path) => _isExcludedBy(excludedPaths, path);
 
@@ -1415,8 +1421,12 @@
 
   bool _isManifest(String path) => pathContext.basename(path) == MANIFEST_NAME;
 
-  bool _isPackagespec(String path) =>
-      pathContext.basename(path) == PACKAGE_SPEC_NAME;
+  bool _isPackageConfigJsonFilePath(String path) {
+    var components = pathContext.split(path);
+    return components.length > 2 &&
+        components[components.length - 1] == 'package_config.json' &&
+        components[components.length - 2] == '.dart_tool';
+  }
 
   bool _isPubspec(String path) => pathContext.basename(path) == PUBSPEC_NAME;
 
@@ -1477,8 +1487,13 @@
     var builder = callbacks.createContextBuilder(info.folder);
     var options = builder.getAnalysisOptions(contextRoot,
         contextRoot: driver.contextRoot);
+    var packages = builder.createPackageMap(contextRoot);
     var factory = builder.createSourceFactory(contextRoot);
-    driver.configure(analysisOptions: options, sourceFactory: factory);
+    driver.configure(
+      analysisOptions: options,
+      packages: packages,
+      sourceFactory: factory,
+    );
     callbacks.analysisOptionsUpdated(driver);
   }
 
diff --git a/pkg/analysis_server/lib/src/edit/edit_domain.dart b/pkg/analysis_server/lib/src/edit/edit_domain.dart
index e2fc729..62a8d6a 100644
--- a/pkg/analysis_server/lib/src/edit/edit_domain.dart
+++ b/pkg/analysis_server/lib/src/edit/edit_domain.dart
@@ -106,7 +106,8 @@
       }
 
       var workspace = DartChangeWorkspace(server.currentSessions);
-      var processor = BulkFixProcessor(workspace);
+      var processor =
+          BulkFixProcessor(server.instrumentationService, workspace);
 
       String sdkPath;
       var sdk = server.findSdk();
@@ -232,6 +233,7 @@
     server.requestStatistics?.addItemTimeNow(request, 'resolvedUnit');
     if (result != null) {
       var context = DartAssistContextImpl(
+        server.instrumentationService,
         DartChangeWorkspace(server.currentSessions),
         result,
         offset,
@@ -643,7 +645,8 @@
         var errorLine = lineInfo.getLocation(error.offset).lineNumber;
         if (errorLine == requestLine) {
           var workspace = DartChangeWorkspace(server.currentSessions);
-          var context = DartFixContextImpl(workspace, result, error, (name) {
+          var context = DartFixContextImpl(
+              server.instrumentationService, workspace, result, error, (name) {
             var tracker = server.declarationsTracker;
             var provider = TopLevelDeclarationsProvider(tracker);
             return provider.get(
diff --git a/pkg/analysis_server/lib/src/edit/fix/fix_error_task.dart b/pkg/analysis_server/lib/src/edit/fix/fix_error_task.dart
index d15e724..b78a73b 100644
--- a/pkg/analysis_server/lib/src/edit/fix/fix_error_task.dart
+++ b/pkg/analysis_server/lib/src/edit/fix/fix_error_task.dart
@@ -11,6 +11,7 @@
 import 'package:analysis_server/src/services/correction/fix_internal.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/error/error.dart';
+import 'package:analyzer/instrumentation/service.dart';
 import 'package:analyzer/src/error/codes.dart';
 
 /// A processor used by [EditDartFix] to manage [FixErrorTask]s.
@@ -45,6 +46,7 @@
   Future<void> fixError(ResolvedUnitResult result, AnalysisError error) async {
     final workspace = DartChangeWorkspace(listener.server.currentSessions);
     final dartContext = DartFixContextImpl(
+      InstrumentationService.NULL_SERVICE,
       workspace,
       result,
       error,
diff --git a/pkg/analysis_server/lib/src/edit/fix/prefer_mixin_fix.dart b/pkg/analysis_server/lib/src/edit/fix/prefer_mixin_fix.dart
index 6ab084c..870c33f 100644
--- a/pkg/analysis_server/lib/src/edit/fix/prefer_mixin_fix.dart
+++ b/pkg/analysis_server/lib/src/edit/fix/prefer_mixin_fix.dart
@@ -15,6 +15,7 @@
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/error/error.dart';
 import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/instrumentation/service.dart';
 import 'package:analyzer/src/dart/ast/utilities.dart';
 import 'package:analyzer/src/lint/registry.dart';
 
@@ -34,6 +35,7 @@
           declaration.name.name == elem.name) {
         var processor = AssistProcessor(
           DartAssistContextImpl(
+              InstrumentationService.NULL_SERVICE,
               DartChangeWorkspace(listener.server.currentSessions),
               result,
               declaration.name.offset,
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
index 0e5a949..9d6fb0a 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
@@ -168,6 +168,7 @@
 
     try {
       var context = DartAssistContextImpl(
+        server.instrumentationService,
         DartChangeWorkspace(server.currentSessions),
         unit,
         offset,
@@ -237,7 +238,8 @@
         var errorLine = lineInfo.getLocation(error.offset).lineNumber - 1;
         if (errorLine >= range.start.line && errorLine <= range.end.line) {
           var workspace = DartChangeWorkspace(server.currentSessions);
-          var context = DartFixContextImpl(workspace, unit, error, (name) {
+          var context = DartFixContextImpl(
+              server.instrumentationService, workspace, unit, error, (name) {
             var tracker = server.declarationsTracker;
             return TopLevelDeclarationsProvider(tracker).get(
               unit.session.analysisContext,
diff --git a/pkg/analysis_server/lib/src/lsp/mapping.dart b/pkg/analysis_server/lib/src/lsp/mapping.dart
index 717b6cd..039b503 100644
--- a/pkg/analysis_server/lib/src/lsp/mapping.dart
+++ b/pkg/analysis_server/lib/src/lsp/mapping.dart
@@ -156,8 +156,9 @@
       case server.DeclarationKind.CONSTRUCTOR:
         return const [lsp.SymbolKind.Constructor];
       case server.DeclarationKind.ENUM:
-      case server.DeclarationKind.ENUM_CONSTANT:
         return const [lsp.SymbolKind.Enum];
+      case server.DeclarationKind.ENUM_CONSTANT:
+        return const [lsp.SymbolKind.EnumMember, lsp.SymbolKind.Enum];
       case server.DeclarationKind.FIELD:
         return const [lsp.SymbolKind.Field];
       case server.DeclarationKind.FUNCTION:
@@ -380,8 +381,9 @@
       case server.ElementKind.CONSTRUCTOR_INVOCATION:
         return const [lsp.SymbolKind.Constructor];
       case server.ElementKind.ENUM:
-      case server.ElementKind.ENUM_CONSTANT:
         return const [lsp.SymbolKind.Enum];
+      case server.ElementKind.ENUM_CONSTANT:
+        return const [lsp.SymbolKind.EnumMember, lsp.SymbolKind.Enum];
       case server.ElementKind.EXTENSION:
         return const [lsp.SymbolKind.Namespace];
       case server.ElementKind.FIELD:
diff --git a/pkg/analysis_server/lib/src/services/completion/statement/statement_completion.dart b/pkg/analysis_server/lib/src/services/completion/statement/statement_completion.dart
index 808cb44..2984122 100644
--- a/pkg/analysis_server/lib/src/services/completion/statement/statement_completion.dart
+++ b/pkg/analysis_server/lib/src/services/completion/statement/statement_completion.dart
@@ -203,7 +203,24 @@
 
   void _addReplaceEdit(SourceRange range, String text) {
     var edit = SourceEdit(range.offset, range.length, text);
-    doSourceChange_addElementEdit(change, unitElement, edit);
+    // TODO(brianwilkerson) The commented out function call has been inlined in
+    //  order to work around a situation in which _complete_doStatement creates
+    //  a conflicting edit that happens to work because of the order in which
+    //  the edits are applied. The implementation needs to be cleaned up in
+    //  order to prevent the conflicting edit from being generated.
+    // doSourceChange_addElementEdit(change, unitElement, edit);
+    var fileEdit = change.getFileEdit(unitElement.source.fullName);
+    if (fileEdit == null) {
+      fileEdit = SourceFileEdit(file, 0);
+      change.addFileEdit(fileEdit);
+    }
+    var edits = fileEdit.edits;
+    var length = edits.length;
+    var index = 0;
+    while (index < length && edits[index].offset > edit.offset) {
+      index++;
+    }
+    edits.insert(index, edit);
   }
 
   void _appendEmptyBraces(SourceBuilder sb, [bool needsExitMark = false]) {
diff --git a/pkg/analysis_server/lib/src/services/correction/assist.dart b/pkg/analysis_server/lib/src/services/correction/assist.dart
index 50910b8..04dc79d 100644
--- a/pkg/analysis_server/lib/src/services/correction/assist.dart
+++ b/pkg/analysis_server/lib/src/services/correction/assist.dart
@@ -4,12 +4,16 @@
 
 import 'package:analysis_server/plugin/edit/assist/assist_dart.dart';
 import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/instrumentation/service.dart';
 import 'package:analyzer_plugin/utilities/assist/assist.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_workspace.dart';
 
 /// The implementation of [DartAssistContext].
 class DartAssistContextImpl implements DartAssistContext {
   @override
+  final InstrumentationService instrumentationService;
+
+  @override
   final ChangeWorkspace workspace;
 
   @override
@@ -21,8 +25,8 @@
   @override
   final int selectionLength;
 
-  DartAssistContextImpl(this.workspace, this.resolveResult,
-      this.selectionOffset, this.selectionLength);
+  DartAssistContextImpl(this.instrumentationService, this.workspace,
+      this.resolveResult, this.selectionOffset, this.selectionLength);
 }
 
 /// An enumeration of possible assist kinds.
diff --git a/pkg/analysis_server/lib/src/services/correction/assist_internal.dart b/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
index 6f994e4..d304168 100644
--- a/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
@@ -73,6 +73,7 @@
 import 'package:analyzer_plugin/utilities/assist/assist.dart'
     hide AssistContributor;
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+import 'package:analyzer_plugin/utilities/change_builder/conflicting_edit_exception.dart';
 
 /// The computer for Dart assists.
 class AssistProcessor extends BaseProcessor {
@@ -151,16 +152,16 @@
     SurroundWith.newInstance,
   ];
 
-  final DartAssistContext context;
+  final DartAssistContext assistContext;
 
   final List<Assist> assists = <Assist>[];
 
-  AssistProcessor(this.context)
+  AssistProcessor(this.assistContext)
       : super(
-          selectionOffset: context.selectionOffset,
-          selectionLength: context.selectionLength,
-          resolvedResult: context.resolveResult,
-          workspace: context.workspace,
+          selectionOffset: assistContext.selectionOffset,
+          selectionLength: assistContext.selectionLength,
+          resolvedResult: assistContext.resolveResult,
+          workspace: assistContext.workspace,
         );
 
   Future<List<Assist>> compute() async {
@@ -241,36 +242,42 @@
     if (!setupSuccess) {
       return;
     }
+
+    Future<void> compute(CorrectionProducer producer) async {
+      producer.configure(context);
+      var builder = ChangeBuilder(
+          workspace: context.workspace, eol: context.utils.endOfLine);
+      try {
+        await producer.compute(builder);
+        _addAssistFromBuilder(builder, producer.assistKind,
+            args: producer.assistArguments);
+      } on ConflictingEditException catch (exception, stackTrace) {
+        // Handle the exception by (a) not adding an assist based on the
+        // producer and (b) logging the exception.
+        assistContext.instrumentationService
+            .logException(exception, stackTrace);
+      }
+    }
+
     for (var generator in generators) {
       var ruleNames = lintRuleMap[generator] ?? {};
       if (!_containsErrorCode(ruleNames)) {
         var producer = generator();
-        producer.configure(context);
-
-        var builder = ChangeBuilder(
-            workspace: context.workspace, eol: context.utils.endOfLine);
-        await producer.compute(builder);
-        _addAssistFromBuilder(builder, producer.assistKind,
-            args: producer.assistArguments);
+        await compute(producer);
       }
     }
     for (var multiGenerator in multiGenerators) {
       var multiProducer = multiGenerator();
       multiProducer.configure(context);
       for (var producer in multiProducer.producers) {
-        var builder = ChangeBuilder(
-            workspace: context.workspace, eol: context.utils.endOfLine);
-        producer.configure(context);
-        await producer.compute(builder);
-        _addAssistFromBuilder(builder, producer.assistKind,
-            args: producer.assistArguments);
+        await compute(producer);
       }
     }
   }
 
   bool _containsErrorCode(Set<String> errorCodes) {
     final fileOffset = node.offset;
-    for (var error in context.resolveResult.errors) {
+    for (var error in assistContext.resolveResult.errors) {
       final errorSource = error.source;
       if (file == errorSource.fullName) {
         if (fileOffset >= error.offset &&
diff --git a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
index dfece89..e739602 100644
--- a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
+++ b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
@@ -66,11 +66,13 @@
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/error/error.dart';
 import 'package:analyzer/exception/exception.dart';
+import 'package:analyzer/instrumentation/service.dart';
 import 'package:analyzer/source/error_processor.dart';
 import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer/src/generated/engine.dart' show AnalysisEngine;
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+import 'package:analyzer_plugin/utilities/change_builder/conflicting_edit_exception.dart';
 
 /// A fix producer that produces changes to fix multiple diagnostics.
 class BulkFixProcessor {
@@ -338,6 +340,9 @@
         RemoveNonNullAssertion.newInstance,
   };
 
+  /// The service used to report errors when building fixes.
+  final InstrumentationService instrumentationService;
+
   /// Information about the workspace containing the libraries in which changes
   /// will be produced.
   final DartChangeWorkspace workspace;
@@ -348,7 +353,7 @@
 
   /// Initialize a newly created processor to create fixes for diagnostics in
   /// libraries in the [workspace].
-  BulkFixProcessor(this.workspace) {
+  BulkFixProcessor(this.instrumentationService, this.workspace) {
     builder = ChangeBuilder(workspace: workspace);
   }
 
@@ -378,6 +383,7 @@
     var analysisOptions = result.session.analysisContext.analysisOptions;
     for (var unitResult in result.units) {
       final fixContext = DartFixContextImpl(
+        instrumentationService,
         workspace,
         unitResult,
         null,
@@ -415,7 +421,14 @@
 
     Future<void> compute(CorrectionProducer producer) async {
       producer.configure(context);
-      await producer.compute(builder);
+      try {
+        var localBuilder = builder.copy();
+        await producer.compute(localBuilder);
+        builder = localBuilder;
+      } on ConflictingEditException {
+        // If a conflicting edit was added in [compute], then the [localBuilder]
+        // is discarded and we revert to the previous state of the builder.
+      }
     }
 
     var errorCode = diagnostic.errorCode;
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_required_keyword.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_required_keyword.dart
index bc71685..8e20c54 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/add_required_keyword.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/add_required_keyword.dart
@@ -4,6 +4,8 @@
 
 import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
 import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/source/source_range.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
 import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
 
@@ -14,7 +16,25 @@
   @override
   Future<void> compute(ChangeBuilder builder) async {
     await builder.addDartFileEdit(file, (builder) {
-      builder.addSimpleInsertion(node.parent.offset, 'required ');
+      var insertOffset = node.parent.offset;
+
+      var parent = node.parent;
+      if (parent is FormalParameter) {
+        var metadata = parent.metadata;
+        // Check for redundant `@required` annotations.
+        if (metadata.isNotEmpty) {
+          for (var annotation in metadata) {
+            if (annotation.elementAnnotation.isRequired) {
+              var length = annotation.endToken.next.offset -
+                  annotation.beginToken.offset;
+              builder.addDeletion(SourceRange(annotation.offset, length));
+              break;
+            }
+          }
+          insertOffset = metadata.endToken.next.offset;
+        }
+      }
+      builder.addSimpleInsertion(insertOffset, 'required ');
     });
   }
 
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index fe6cbc4..8bd57b2 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -7,6 +7,7 @@
 import 'package:analysis_server/src/services/linter/lint_names.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/error/error.dart';
+import 'package:analyzer/instrumentation/service.dart';
 import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer/src/generated/parser.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_workspace.dart';
@@ -115,6 +116,9 @@
 /// The implementation of [DartFixContext].
 class DartFixContextImpl implements DartFixContext {
   @override
+  final InstrumentationService instrumentationService;
+
+  @override
   final ChangeWorkspace workspace;
 
   @override
@@ -126,8 +130,8 @@
   final List<TopLevelDeclaration> Function(String name)
       getTopLevelDeclarationsFunction;
 
-  DartFixContextImpl(this.workspace, this.resolveResult, this.error,
-      this.getTopLevelDeclarationsFunction);
+  DartFixContextImpl(this.instrumentationService, this.workspace,
+      this.resolveResult, this.error, this.getTopLevelDeclarationsFunction);
 
   @override
   List<TopLevelDeclaration> getTopLevelDeclarations(String name) {
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/code_fragment_parser.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/code_fragment_parser.dart
index 649597a..716c97e 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/code_fragment_parser.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/code_fragment_parser.dart
@@ -3,8 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analysis_server/src/services/correction/fix/data_driven/accessor.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/expression.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/parameter_reference.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_error_code.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/variable_scope.dart';
 import 'package:analyzer/error/listener.dart';
 
 /// A parser for the textual representation of a code fragment.
@@ -12,18 +14,37 @@
   /// The error reporter to which diagnostics will be reported.
   final ErrorReporter errorReporter;
 
+  /// The scope in which variables can be looked up.
+  VariableScope variableScope;
+
   /// The amount to be added to translate from offsets within the content to
   /// offsets within the file.
   int delta;
 
   /// The tokens being parsed.
-  List<_Token> tokens;
+  /* late */ List<_Token> tokens;
+
+  /// The index in the [tokens] of the next token to be consumed.
+  int currentIndex = 0;
 
   /// The accessors that have been parsed.
   List<Accessor> accessors = [];
 
   /// Initialize a newly created parser to report errors to the [errorReporter].
-  CodeFragmentParser(this.errorReporter);
+  CodeFragmentParser(this.errorReporter, {VariableScope scope})
+      : variableScope = scope ?? VariableScope(null, {});
+
+  /// Return the current token, or `null` if the end of the tokens has been
+  /// reached.
+  _Token get currentToken =>
+      currentIndex < tokens.length ? tokens[currentIndex] : null;
+
+  /// Advance to the next token.
+  void advance() {
+    if (currentIndex < tokens.length) {
+      currentIndex++;
+    }
+  }
 
   /// Parse the [content] into a list of accessors. Add the [delta] to translate
   /// from offsets within the content to offsets within the file.
@@ -51,6 +72,29 @@
     return accessors;
   }
 
+  /// Parse the [content] into a condition. Add the [delta] to translate
+  /// from offsets within the content to offsets within the file.
+  ///
+  /// <content> ::=
+  ///   <logicalExpression>
+  Expression parseCondition(String content, int delta) {
+    this.delta = delta;
+    tokens = _CodeFragmentScanner(content, delta, errorReporter).scan();
+    if (tokens == null) {
+      // The error has already been reported.
+      return null;
+    }
+    currentIndex = 0;
+    var expression = _parseLogicalAndExpression();
+    if (currentIndex < tokens.length) {
+      var token = tokens[currentIndex];
+      errorReporter.reportErrorForOffset(TransformSetErrorCode.unexpectedToken,
+          token.offset + delta, token.length, [token.kind.displayName]);
+      return null;
+    }
+    return expression;
+  }
+
   /// Return the token at the given [index] if it exists and if it has one of
   /// the [validKinds]. Report an error and return `null` if those conditions
   /// aren't met.
@@ -155,25 +199,113 @@
       return tokens.length;
     }
   }
+
+  /// Parse a logical expression.
+  ///
+  /// <equalityExpression> ::=
+  ///   <primaryExpression> (<comparisonOperator> <primaryExpression>)?
+  /// <comparisonOperator> ::=
+  ///   '==' | '!='
+  Expression _parseEqualityExpression() {
+    var expression = _parsePrimaryExpression();
+    if (expression == null) {
+      return null;
+    }
+    if (currentIndex >= tokens.length) {
+      return expression;
+    }
+    var kind = currentToken.kind;
+    if (kind == _TokenKind.equal || kind == _TokenKind.notEqual) {
+      advance();
+      var operator =
+          kind == _TokenKind.equal ? Operator.equal : Operator.notEqual;
+      var rightOperand = _parsePrimaryExpression();
+      if (rightOperand == null) {
+        return null;
+      }
+      expression = BinaryExpression(expression, operator, rightOperand);
+    }
+    return expression;
+  }
+
+  /// Parse a logical expression.
+  ///
+  /// <logicalExpression> ::=
+  ///   <equalityExpression> ('&&' <equalityExpression>)*
+  Expression _parseLogicalAndExpression() {
+    var expression = _parseEqualityExpression();
+    if (expression == null) {
+      return null;
+    }
+    if (currentIndex >= tokens.length) {
+      return expression;
+    }
+    var kind = currentToken.kind;
+    while (kind == _TokenKind.and) {
+      advance();
+      var rightOperand = _parseEqualityExpression();
+      if (rightOperand == null) {
+        return null;
+      }
+      expression = BinaryExpression(expression, Operator.and, rightOperand);
+      if (currentIndex >= tokens.length) {
+        return expression;
+      }
+      kind = currentToken.kind;
+    }
+    return expression;
+  }
+
+  /// Parse a logical expression.
+  ///
+  /// <primaryExpression> ::=
+  ///   <identifier> | <string>
+  Expression _parsePrimaryExpression() {
+    var token = currentToken;
+    var kind = token.kind;
+    if (kind == _TokenKind.identifier) {
+      advance();
+      var variableName = token.lexeme;
+      var generator = variableScope.lookup(variableName);
+      if (generator == null) {
+        errorReporter.reportErrorForOffset(
+            TransformSetErrorCode.undefinedVariable,
+            token.offset + delta,
+            token.length,
+            [variableName]);
+        return null;
+      }
+      return VariableReference(generator);
+    } else if (kind == _TokenKind.string) {
+      advance();
+      var lexeme = token.lexeme;
+      var value = lexeme.substring(1, lexeme.length - 1);
+      return LiteralString(value);
+    }
+    errorReporter.reportErrorForOffset(TransformSetErrorCode.expectedPrimary,
+        token.offset + delta, token.length);
+    return null;
+  }
 }
 
 /// A scanner for the textual representation of a code fragment.
 class _CodeFragmentScanner {
   static final int $0 = '0'.codeUnitAt(0);
-
   static final int $9 = '9'.codeUnitAt(0);
-
   static final int $a = 'a'.codeUnitAt(0);
-
   static final int $z = 'z'.codeUnitAt(0);
-
   static final int $A = 'A'.codeUnitAt(0);
   static final int $Z = 'Z'.codeUnitAt(0);
+
+  static final int ampersand = '&'.codeUnitAt(0);
+  static final int bang = '!'.codeUnitAt(0);
   static final int closeSquareBracket = ']'.codeUnitAt(0);
   static final int carriageReturn = '\r'.codeUnitAt(0);
+  static final int equal = '='.codeUnitAt(0);
   static final int newline = '\n'.codeUnitAt(0);
   static final int openSquareBracket = '['.codeUnitAt(0);
   static final int period = '.'.codeUnitAt(0);
+  static final int singleQuote = "'".codeUnitAt(0);
   static final int space = ' '.codeUnitAt(0);
 
   /// The string being scanned.
@@ -195,8 +327,15 @@
   /// Return the tokens in the content, or `null` if there is an error in the
   /// content that prevents it from being scanned.
   List<_Token> scan() {
-    if (content.isEmpty) {}
     var length = content.length;
+
+    int peekAt(int offset) {
+      if (offset > length) {
+        return -1;
+      }
+      return content.codeUnitAt(offset);
+    }
+
     var offset = _skipWhitespace(0);
     var tokens = <_Token>[];
     while (offset < length) {
@@ -210,6 +349,33 @@
       } else if (char == period) {
         tokens.add(_Token(offset, _TokenKind.period, '.'));
         offset++;
+      } else if (char == ampersand) {
+        if (peekAt(offset + 1) != ampersand) {
+          return _reportInvalidCharacter(offset);
+        }
+        tokens.add(_Token(offset, _TokenKind.and, '&&'));
+        offset += 2;
+      } else if (char == bang) {
+        if (peekAt(offset + 1) != equal) {
+          return _reportInvalidCharacter(offset);
+        }
+        tokens.add(_Token(offset, _TokenKind.notEqual, '!='));
+        offset += 2;
+      } else if (char == equal) {
+        if (peekAt(offset + 1) != equal) {
+          return _reportInvalidCharacter(offset);
+        }
+        tokens.add(_Token(offset, _TokenKind.equal, '=='));
+        offset += 2;
+      } else if (char == singleQuote) {
+        var start = offset;
+        offset++;
+        while (offset < length && content.codeUnitAt(offset) != singleQuote) {
+          offset++;
+        }
+        offset++;
+        tokens.add(
+            _Token(start, _TokenKind.string, content.substring(start, offset)));
       } else if (_isLetter(char)) {
         var start = offset;
         offset++;
@@ -227,12 +393,7 @@
         tokens.add(_Token(
             start, _TokenKind.integer, content.substring(start, offset)));
       } else {
-        errorReporter.reportErrorForOffset(
-            TransformSetErrorCode.invalidCharacter,
-            offset + delta,
-            1,
-            [content.substring(offset, offset + 1)]);
-        return null;
+        return _reportInvalidCharacter(offset);
       }
       offset = _skipWhitespace(offset);
     }
@@ -250,6 +411,13 @@
   bool _isWhitespace(int char) =>
       char == space || char == newline || char == carriageReturn;
 
+  /// Report the presence of an invalid character at the given [offset].
+  Null _reportInvalidCharacter(int offset) {
+    errorReporter.reportErrorForOffset(TransformSetErrorCode.invalidCharacter,
+        offset + delta, 1, [content.substring(offset, offset + 1)]);
+    return null;
+  }
+
   /// Return the index of the first character at or after the given [offset]
   /// that isn't a whitespace character.
   int _skipWhitespace(int offset) {
@@ -284,26 +452,38 @@
 
 /// An indication of the kind of a token.
 enum _TokenKind {
+  and,
   closeSquareBracket,
+  equal,
   identifier,
   integer,
+  notEqual,
   openSquareBracket,
   period,
+  string,
 }
 
 extension on _TokenKind {
   String get displayName {
     switch (this) {
+      case _TokenKind.and:
+        return "'&&'";
       case _TokenKind.closeSquareBracket:
         return "']'";
+      case _TokenKind.equal:
+        return "'=='";
       case _TokenKind.identifier:
         return 'an identifier';
       case _TokenKind.integer:
         return 'an integer';
+      case _TokenKind.notEqual:
+        return "'!='";
       case _TokenKind.openSquareBracket:
         return "'['";
       case _TokenKind.period:
         return "'.'";
+      case _TokenKind.string:
+        return 'a string';
     }
     return '';
   }
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/expression.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/expression.dart
new file mode 100644
index 0000000..ef1e04c
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/expression.dart
@@ -0,0 +1,50 @@
+// 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.
+
+import 'package:analysis_server/src/services/correction/fix/data_driven/value_generator.dart';
+
+/// A binary expression.
+class BinaryExpression extends Expression {
+  /// The left operand.
+  final Expression leftOperand;
+
+  /// The operator.
+  final Operator operator;
+
+  /// The right operand.
+  final Expression rightOperand;
+
+  /// Initialize a newly created binary expression consisting of the
+  /// [leftOperand], [operator], and [rightOperand].
+  BinaryExpression(this.leftOperand, this.operator, this.rightOperand);
+}
+
+/// An expression.
+abstract class Expression {}
+
+/// A literal string.
+class LiteralString extends Expression {
+  /// The value of the literal string.
+  final String value;
+
+  /// Initialize a newly created literal string to have the given [value].
+  LiteralString(this.value);
+}
+
+/// An operator used in a binary expression.
+enum Operator {
+  and,
+  equal,
+  notEqual,
+}
+
+/// A reference to a variable.
+class VariableReference extends Expression {
+  /// The generator used to generate the value of the variable.
+  final ValueGenerator generator;
+
+  /// Initialize a newly created variable reference to reference the variable
+  /// whose value is computed by the [generator].
+  VariableReference(this.generator);
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_error_code.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_error_code.dart
index 7ea8635..f22360a 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_error_code.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_error_code.dart
@@ -18,6 +18,12 @@
       "The key '{0}' can't be used when '{1}' is also used.");
 
   /**
+   * No parameters.
+   */
+  static const TransformSetErrorCode expectedPrimary = TransformSetErrorCode(
+      'expected_primary', "Expected either an identifier or a string literal.");
+
+  /**
    * Parameters:
    * 0: the character that is invalid
    */
@@ -97,7 +103,14 @@
    * 0: the missing key
    */
   static const TransformSetErrorCode undefinedVariable = TransformSetErrorCode(
-      'undefined_variable', "The variable '{0}' is not defined.");
+      'undefined_variable', "The variable '{0}' isn't defined.");
+
+  /**
+   * Parameters:
+   * 0: the token that was unexpectedly found
+   */
+  static const TransformSetErrorCode unexpectedToken =
+      TransformSetErrorCode('unexpected_token', "Didn't expect to find {0}.");
 
   /**
    * Parameters:
@@ -122,7 +135,7 @@
   /**
    * Parameters:
    * 0: a description of the expected kind of token
-   * 1: a description of the actial kind of token
+   * 1: a description of the actual kind of token
    */
   static const TransformSetErrorCode wrongToken = TransformSetErrorCode(
       'wrong_token', "Expected to find {0}, but found {1}.");
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/variable_scope.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/variable_scope.dart
new file mode 100644
index 0000000..4e5bd68
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/variable_scope.dart
@@ -0,0 +1,25 @@
+// 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.
+
+import 'package:analysis_server/src/services/correction/fix/data_driven/value_generator.dart';
+
+/// A scope in which the generators associated with variables can be looked up.
+class VariableScope {
+  /// The outer scope in which this scope is nested.
+  final VariableScope outerScope;
+
+  /// A table mapping variable names to generators.
+  final Map<String, ValueGenerator> _generators;
+
+  /// Initialize a newly created variable scope defining the variables in the
+  /// [_generators] map. Any variables not defined locally will be looked up in
+  /// the [outerScope].
+  VariableScope(this.outerScope, this._generators);
+
+  /// Return the generator used to generate the value of the variable with the
+  /// given [variableName], or `null` if the variable is not defined.
+  ValueGenerator lookup(String variableName) {
+    return _generators[variableName] ?? outerScope?.lookup(variableName);
+  }
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index 30ab805..881714d 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -160,6 +160,7 @@
 import 'package:analyzer_plugin/protocol/protocol_common.dart'
     hide AnalysisError, Element, ElementKind;
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+import 'package:analyzer_plugin/utilities/change_builder/conflicting_edit_exception.dart';
 import 'package:analyzer_plugin/utilities/fixes/fixes.dart' hide FixContributor;
 
 /// A function that can be executed to create a multi-correction producer.
@@ -209,13 +210,14 @@
     // one.
     // For each fix, put the fix into the HashMap.
     for (var i = 0; i < allAnalysisErrors.length; i++) {
-      final FixContext fixContextI = DartFixContextImpl(
+      final FixContext fixContext = DartFixContextImpl(
+        context.instrumentationService,
         context.workspace,
         context.resolveResult,
         allAnalysisErrors[i],
         (name) => [],
       );
-      var processorI = FixProcessor(fixContextI);
+      var processorI = FixProcessor(fixContext);
       var fixesListI = await processorI.compute();
       for (var f in fixesListI) {
         if (!map.containsKey(f.kind)) {
@@ -227,11 +229,11 @@
     }
 
     // For each FixKind in the HashMap, union each list together, then return
-    // the set of unioned Fixes.
+    // the set of unioned fixes.
     var result = <Fix>[];
-    map.forEach((FixKind kind, List<Fix> fixesListJ) {
-      if (fixesListJ.first.kind.canBeAppliedTogether()) {
-        var unionFix = _unionFixList(fixesListJ);
+    map.forEach((FixKind kind, List<Fix> fixesList) {
+      if (fixesList.first.kind.canBeAppliedTogether()) {
+        var unionFix = _unionFixList(fixesList);
         if (unionFix != null) {
           result.add(unionFix);
         }
@@ -1134,9 +1136,15 @@
       producer.configure(context);
       var builder = ChangeBuilder(
           workspace: context.workspace, eol: context.utils.endOfLine);
-      await producer.compute(builder);
-      _addFixFromBuilder(builder, producer.fixKind,
-          args: producer.fixArguments);
+      try {
+        await producer.compute(builder);
+        _addFixFromBuilder(builder, producer.fixKind,
+            args: producer.fixArguments);
+      } on ConflictingEditException catch (exception, stackTrace) {
+        // Handle the exception by (a) not adding a fix based on the producer
+        // and (b) logging the exception.
+        fixContext.instrumentationService.logException(exception, stackTrace);
+      }
     }
 
     var errorCode = error.errorCode;
diff --git a/pkg/analysis_server/test/context_manager_test.dart b/pkg/analysis_server/test/context_manager_test.dart
index 28ba464..a6b4251 100644
--- a/pkg/analysis_server/test/context_manager_test.dart
+++ b/pkg/analysis_server/test/context_manager_test.dart
@@ -23,6 +23,7 @@
 import 'package:analyzer/src/generated/source_io.dart';
 import 'package:analyzer/src/services/lint.dart';
 import 'package:analyzer/src/test_utilities/mock_sdk.dart';
+import 'package:analyzer/src/test_utilities/package_config_file_builder.dart';
 import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
 import 'package:analyzer/src/util/glob.dart';
 import 'package:linter/src/rules.dart';
@@ -1490,6 +1491,34 @@
     });
   }
 
+  Future<void> test_watch_modifyPackageConfigJson() {
+    var packageConfigPath = '$projPath/.dart_tool/package_config.json';
+    var filePath = convertPath('$projPath/bin/main.dart');
+
+    resourceProvider.newFile(packageConfigPath, '');
+    resourceProvider.newFile(filePath, 'library main;');
+
+    manager.setRoots(<String>[projPath], <String>[]);
+
+    var filePaths = callbacks.currentFilePaths;
+    expect(filePaths, hasLength(1));
+    expect(filePaths, contains(filePath));
+    expect(_currentPackageMap, isEmpty);
+
+    // update .dart_tool/package_config.json
+    callbacks.now++;
+    resourceProvider.modifyFile(
+      packageConfigPath,
+      (PackageConfigFileBuilder()..add(name: 'my', rootPath: '../'))
+          .toContent(toUriStr: toUriStr),
+    );
+
+    return pumpEventQueue().then((_) {
+      // verify new package info
+      expect(_currentPackageMap.keys, unorderedEquals(['my']));
+    });
+  }
+
   Future<void> test_watch_modifyPackagespec() {
     var packagesPath = convertPath('$projPath/.packages');
     var filePath = convertPath('$projPath/bin/main.dart');
diff --git a/pkg/analysis_server/test/edit/bulk_fixes_test.dart b/pkg/analysis_server/test/edit/bulk_fixes_test.dart
index 7f22771..0b79983 100644
--- a/pkg/analysis_server/test/edit/bulk_fixes_test.dart
+++ b/pkg/analysis_server/test/edit/bulk_fixes_test.dart
@@ -150,6 +150,9 @@
 
   @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/44080')
   Future<void> test_unnecessaryNew_collectionLiteral_overlap() async {
+    // The test case currently drops the 'new' but does not convert the code to
+    // use a set literal. The code is no longer mangled, but we need to run the
+    // BulkFixProcessor iteratively to solve the second case.
     addAnalysisOptionsFile('''
 linter:
   rules:
diff --git a/pkg/analysis_server/test/lsp/document_symbols_test.dart b/pkg/analysis_server/test/lsp/document_symbols_test.dart
index f66964d..fa1e3e0 100644
--- a/pkg/analysis_server/test/lsp/document_symbols_test.dart
+++ b/pkg/analysis_server/test/lsp/document_symbols_test.dart
@@ -18,6 +18,67 @@
 
 @reflectiveTest
 class DocumentSymbolsTest extends AbstractLspAnalysisServerTest {
+  Future<void> test_enumMember_notSupported() async {
+    const content = '''
+    enum Theme {
+      light,
+    }
+    ''';
+    newFile(mainFilePath, content: content);
+    await initialize();
+
+    final result = await getDocumentSymbols(mainFileUri.toString());
+    final symbols = result.map(
+      (docsymbols) => throw 'Expected SymbolInformations, got DocumentSymbols',
+      (symbolInfos) => symbolInfos,
+    );
+    expect(symbols, hasLength(2));
+
+    final themeEnum = symbols[0];
+    expect(themeEnum.name, equals('Theme'));
+    expect(themeEnum.kind, equals(SymbolKind.Enum));
+    expect(themeEnum.containerName, isNull);
+
+    final enumValue = symbols[1];
+    expect(enumValue.name, equals('light'));
+    // EnumMember is not in the original LSP list, so unless the client explicitly
+    // advertises support, we will fall back to Enum.
+    expect(enumValue.kind, equals(SymbolKind.Enum));
+    expect(enumValue.containerName, 'Theme');
+  }
+
+  Future<void> test_enumMember_supported() async {
+    const content = '''
+    enum Theme {
+      light,
+    }
+    ''';
+    newFile(mainFilePath, content: content);
+    await initialize(
+      textDocumentCapabilities: withDocumentSymbolKinds(
+        emptyTextDocumentClientCapabilities,
+        [SymbolKind.Enum, SymbolKind.EnumMember],
+      ),
+    );
+
+    final result = await getDocumentSymbols(mainFileUri.toString());
+    final symbols = result.map(
+      (docsymbols) => throw 'Expected SymbolInformations, got DocumentSymbols',
+      (symbolInfos) => symbolInfos,
+    );
+    expect(symbols, hasLength(2));
+
+    final themeEnum = symbols[0];
+    expect(themeEnum.name, equals('Theme'));
+    expect(themeEnum.kind, equals(SymbolKind.Enum));
+    expect(themeEnum.containerName, isNull);
+
+    final enumValue = symbols[1];
+    expect(enumValue.name, equals('light'));
+    expect(enumValue.kind, equals(SymbolKind.EnumMember));
+    expect(enumValue.containerName, 'Theme');
+  }
+
   Future<void> test_flat() async {
     const content = '''
     String topLevel = '';
diff --git a/pkg/analysis_server/test/lsp/server_abstract.dart b/pkg/analysis_server/test/lsp/server_abstract.dart
index a21044d..342e9f1 100644
--- a/pkg/analysis_server/test/lsp/server_abstract.dart
+++ b/pkg/analysis_server/test/lsp/server_abstract.dart
@@ -333,6 +333,17 @@
     });
   }
 
+  TextDocumentClientCapabilities withDocumentSymbolKinds(
+    TextDocumentClientCapabilities source,
+    List<SymbolKind> kinds,
+  ) {
+    return extendTextDocumentCapabilities(source, {
+      'documentSymbol': {
+        'symbolKind': {'valueSet': kinds.map((k) => k.toJson()).toList()}
+      }
+    });
+  }
+
   TextDocumentClientCapabilities withHierarchicalDocumentSymbolSupport(
     TextDocumentClientCapabilities source,
   ) {
diff --git a/pkg/analysis_server/test/services/completion/statement/statement_completion_test.dart b/pkg/analysis_server/test/services/completion/statement/statement_completion_test.dart
index 47f0b08..504dc74 100644
--- a/pkg/analysis_server/test/services/completion/statement/statement_completion_test.dart
+++ b/pkg/analysis_server/test/services/completion/statement/statement_completion_test.dart
@@ -61,8 +61,7 @@
   }
 
   Future<void> _computeCompletion(int offset) async {
-    var result = await session.getResolvedUnit(testFile);
-    var context = StatementCompletionContext(result, offset);
+    var context = StatementCompletionContext(testAnalysisResult, offset);
     var processor = StatementCompletionProcessor(context);
     var completion = await processor.compute();
     change = completion.change;
diff --git a/pkg/analysis_server/test/services/correction/change_test.dart b/pkg/analysis_server/test/services/correction/change_test.dart
index ea70891..90b2dd9 100644
--- a/pkg/analysis_server/test/services/correction/change_test.dart
+++ b/pkg/analysis_server/test/services/correction/change_test.dart
@@ -25,7 +25,7 @@
   void test_addEdit() {
     var change = SourceChange('msg');
     var edit1 = SourceEdit(1, 2, 'a');
-    var edit2 = SourceEdit(1, 2, 'b');
+    var edit2 = SourceEdit(4, 2, 'b');
     expect(change.edits, hasLength(0));
     change.addEdit('/a.dart', 0, edit1);
     expect(change.edits, hasLength(1));
diff --git a/pkg/analysis_server/test/src/services/correction/assist/assist_processor.dart b/pkg/analysis_server/test/src/services/correction/assist/assist_processor.dart
index b6ac127..2235fe2 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/assist_processor.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/assist_processor.dart
@@ -6,6 +6,7 @@
 import 'package:analysis_server/src/services/correction/assist.dart';
 import 'package:analysis_server/src/services/correction/assist_internal.dart';
 import 'package:analysis_server/src/services/correction/change_workspace.dart';
+import 'package:analyzer/instrumentation/service.dart';
 import 'package:analyzer/src/test_utilities/platform.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart'
     hide AnalysisError;
@@ -192,6 +193,7 @@
 
   Future<List<Assist>> _computeAssists() async {
     var context = DartAssistContextImpl(
+      InstrumentationService.NULL_SERVICE,
       workspace,
       testAnalysisResult,
       _offset,
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_required_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_required_test.dart
index 1f22076..d5b03dc 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_required_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_required_test.dart
@@ -45,7 +45,7 @@
   @override
   FixKind get kind => DartFixKind.ADD_REQUIRED2;
 
-  Future<void> test_withAssert() async {
+  Future<void> test_nonNullable() async {
     await resolveTestCode('''
 void function({String param}) {}
 ''');
@@ -53,4 +53,124 @@
 void function({required String param}) {}
 ''');
   }
+
+  Future<void> test_withRequiredAnnotation() async {
+    writeTestPackageConfig(meta: true);
+
+    await resolveTestCode('''
+import 'package:meta/meta.dart';
+
+void function({@required String param}) {}
+''');
+    await assertHasFix('''
+import 'package:meta/meta.dart';
+
+void function({required String param}) {}
+''');
+  }
+
+  Future<void> test_withRequiredAnnotation_constructor() async {
+    writeTestPackageConfig(meta: true);
+
+    await resolveTestCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  String foo;
+  A({@required this.foo});
+}
+''');
+    await assertHasFix('''
+import 'package:meta/meta.dart';
+
+class A {
+  String foo;
+  A({required this.foo});
+}
+''');
+  }
+
+  Future<void> test_withRequiredAnnotation_functionParam() async {
+    writeTestPackageConfig(meta: true);
+
+    await resolveTestCode('''
+import 'package:meta/meta.dart';
+
+void f({@required int g(String)}) { }
+''');
+    await assertHasFix('''
+import 'package:meta/meta.dart';
+
+void f({required int g(String)}) { }
+''');
+  }
+
+  Future<void> test_withRequiredAnnotationInList_first() async {
+    writeTestPackageConfig(meta: true);
+
+    await resolveTestCode('''
+import 'package:meta/meta.dart';
+
+class Foo {
+  const Foo();
+}
+
+const foo = Foo();
+
+void function({@required @foo String param}) {}
+''');
+    await assertHasFix('''
+import 'package:meta/meta.dart';
+
+class Foo {
+  const Foo();
+}
+
+const foo = Foo();
+
+void function({@foo required String param}) {}
+''');
+  }
+
+  Future<void> test_withRequiredAnnotationInList_last() async {
+    writeTestPackageConfig(meta: true);
+
+    await resolveTestCode('''
+import 'package:meta/meta.dart';
+
+class Foo {
+  const Foo();
+}
+
+const foo = Foo();
+
+void function({@foo @required String param}) {}
+''');
+    await assertHasFix('''
+import 'package:meta/meta.dart';
+
+class Foo {
+  const Foo();
+}
+
+const foo = Foo();
+
+void function({@foo required String param}) {}
+''');
+  }
+
+  Future<void> test_withRequiredAnnotationWithReason() async {
+    writeTestPackageConfig(meta: true);
+
+    await resolveTestCode('''
+import 'package:meta/meta.dart';
+
+void function({@Required('reason') String param}) {}
+''');
+    await assertHasFix('''
+import 'package:meta/meta.dart';
+
+void function({required String param}) {}
+''');
+  }
 }
diff --git a/pkg/analysis_server/test/src/services/correction/fix/bulk/bulk_fix_processor.dart b/pkg/analysis_server/test/src/services/correction/fix/bulk/bulk_fix_processor.dart
index de7282e..3e9e14c 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/bulk/bulk_fix_processor.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/bulk/bulk_fix_processor.dart
@@ -4,6 +4,7 @@
 
 import 'package:analysis_server/src/services/correction/bulk_fix_processor.dart';
 import 'package:analysis_server/src/services/correction/change_workspace.dart';
+import 'package:analyzer/instrumentation/service.dart';
 import 'package:analyzer/src/dart/analysis/byte_store.dart';
 import 'package:analyzer/src/services/available_declarations.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
@@ -65,7 +66,8 @@
     var analysisContext = contextFor(testFile);
     tracker.addContext(analysisContext);
     var changeBuilder =
-        await BulkFixProcessor(workspace).fixErrors([analysisContext]);
+        await BulkFixProcessor(InstrumentationService.NULL_SERVICE, workspace)
+            .fixErrors([analysisContext]);
     return changeBuilder.sourceChange;
   }
 
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/code_fragment_parser_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/code_fragment_parser_test.dart
index 75e883e..75f13ff 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/code_fragment_parser_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/code_fragment_parser_test.dart
@@ -5,6 +5,9 @@
 import 'package:_fe_analyzer_shared/src/base/errors.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/accessor.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/code_fragment_parser.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/expression.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/value_generator.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/variable_scope.dart';
 import 'package:analyzer/error/listener.dart';
 import 'package:matcher/matcher.dart';
 import 'package:test/test.dart';
@@ -15,7 +18,8 @@
 
 void main() {
   defineReflectiveSuite(() {
-    defineReflectiveTests(CodeFragmentParserTest);
+    defineReflectiveTests(AccessorsTest);
+    defineReflectiveTests(ConditionTest);
   });
 }
 
@@ -23,20 +27,36 @@
   List<Accessor> assertErrors(
       String content, List<ExpectedError> expectedErrors) {
     var errorListener = GatheringErrorListener();
-    var errorReporter = ErrorReporter(errorListener, MockSource());
-    var accessors = CodeFragmentParser(errorReporter).parse(content, 0);
+    var accessors = _parser(errorListener).parse(content, 0);
     errorListener.assertErrors(expectedErrors);
     return accessors;
   }
 
+  Expression assertErrorsInCondition(String content, List<String> variables,
+      List<ExpectedError> expectedErrors) {
+    var errorListener = GatheringErrorListener();
+    var expression =
+        _parser(errorListener, variables: variables).parseCondition(content, 0);
+    errorListener.assertErrors(expectedErrors);
+    return expression;
+  }
+
   List<Accessor> assertNoErrors(String content) {
     var errorListener = GatheringErrorListener();
-    var errorReporter = ErrorReporter(errorListener, MockSource());
-    var accessors = CodeFragmentParser(errorReporter).parse(content, 0);
+    var accessors = _parser(errorListener).parse(content, 0);
     errorListener.assertNoErrors();
     return accessors;
   }
 
+  Expression assertNoErrorsInCondition(String content,
+      {List<String> variables}) {
+    var errorListener = GatheringErrorListener();
+    var expression =
+        _parser(errorListener, variables: variables).parseCondition(content, 0);
+    errorListener.assertNoErrors();
+    return expression;
+  }
+
   ExpectedError error(ErrorCode code, int offset, int length,
           {String message,
           Pattern messageContains,
@@ -46,10 +66,23 @@
           message: message,
           messageContains: messageContains,
           expectedContextMessages: contextMessages);
+
+  CodeFragmentParser _parser(GatheringErrorListener listener,
+      {List<String> variables}) {
+    var errorReporter = ErrorReporter(listener, MockSource());
+    var map = <String, ValueGenerator>{};
+    if (variables != null) {
+      for (var variableName in variables) {
+        map[variableName] = CodeFragment([]);
+      }
+    }
+    var scope = VariableScope(null, map);
+    return CodeFragmentParser(errorReporter, scope: scope);
+  }
 }
 
 @reflectiveTest
-class CodeFragmentParserTest extends AbstractCodeFragmentParserTest {
+class AccessorsTest extends AbstractCodeFragmentParserTest {
   void test_arguments_arguments_arguments() {
     var accessors = assertNoErrors('arguments[0].arguments[1].arguments[2]');
     expect(accessors, hasLength(3));
@@ -83,3 +116,30 @@
     expect(accessors[0], isA<TypeArgumentAccessor>());
   }
 }
+
+@reflectiveTest
+class ConditionTest extends AbstractCodeFragmentParserTest {
+  void test_and() {
+    var expression = assertNoErrorsInCondition("'a' != 'b' && 'c' != 'd'")
+        as BinaryExpression;
+    expect(expression.leftOperand, isA<BinaryExpression>());
+    expect(expression.operator, Operator.and);
+    expect(expression.rightOperand, isA<BinaryExpression>());
+  }
+
+  void test_equal() {
+    var expression = assertNoErrorsInCondition('a == b', variables: ['a', 'b'])
+        as BinaryExpression;
+    expect(expression.leftOperand, isA<VariableReference>());
+    expect(expression.operator, Operator.equal);
+    expect(expression.rightOperand, isA<VariableReference>());
+  }
+
+  void test_notEqual() {
+    var expression = assertNoErrorsInCondition("a != 'b'", variables: ['a'])
+        as BinaryExpression;
+    expect(expression.leftOperand, isA<VariableReference>());
+    expect(expression.operator, Operator.notEqual);
+    expect(expression.rightOperand, isA<LiteralString>());
+  }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/missing_token_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/missing_token_test.dart
index ec8ac42..c1ee9bd 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/missing_token_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/missing_token_test.dart
@@ -21,6 +21,12 @@
     ]);
   }
 
+  void test_empty() {
+    assertErrors('', [
+      error(TransformSetErrorCode.missingToken, 0, 0),
+    ]);
+  }
+
   void test_identifier_afterPeriod() {
     assertErrors('arguments[2].', [
       error(TransformSetErrorCode.missingToken, 12, 1),
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/wrong_token_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/wrong_token_test.dart
index a047759..ed35ea9 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/wrong_token_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/wrong_token_test.dart
@@ -9,12 +9,12 @@
 
 void main() {
   defineReflectiveSuite(() {
-    defineReflectiveTests(MissingTokenTest);
+    defineReflectiveTests(WrongTokenTest);
   });
 }
 
 @reflectiveTest
-class MissingTokenTest extends AbstractCodeFragmentParserTest {
+class WrongTokenTest extends AbstractCodeFragmentParserTest {
   void test_closeBracket() {
     assertErrors('arguments[2 3', [
       error(TransformSetErrorCode.wrongToken, 12, 1),
diff --git a/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart b/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart
index b474c5a..321da9d 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart
@@ -9,6 +9,7 @@
 import 'package:analysis_server/src/services/correction/fix_internal.dart';
 import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/error/error.dart';
+import 'package:analyzer/instrumentation/service.dart';
 import 'package:analyzer/src/dart/analysis/byte_store.dart';
 import 'package:analyzer/src/dart/analysis/experiments.dart';
 import 'package:analyzer/src/dart/error/lint_codes.dart';
@@ -277,6 +278,7 @@
     tracker.addContext(analysisContext);
 
     var context = DartFixContextImpl(
+      InstrumentationService.NULL_SERVICE,
       workspace,
       testAnalysisResult,
       error,
diff --git a/pkg/analysis_server/test/src/utilities/mock_packages.dart b/pkg/analysis_server/test/src/utilities/mock_packages.dart
index 2bfb8d4..1a34e4a 100644
--- a/pkg/analysis_server/test/src/utilities/mock_packages.dart
+++ b/pkg/analysis_server/test/src/utilities/mock_packages.dart
@@ -2,12 +2,11 @@
 // 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.
 
+import 'package:analysis_tool/package_root.dart' as package_root;
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/file_system/memory_file_system.dart';
 import 'package:analyzer/file_system/physical_file_system.dart';
 
-import '../../utils/package_root.dart' as package_root;
-
 /// Helper for copying files from "tests/mock_packages" to memory file system.
 class MockPackages {
   static final MockPackages instance = MockPackages._();
diff --git a/pkg/analysis_server/test/verify_sorted_test.dart b/pkg/analysis_server/test/verify_sorted_test.dart
index 858f7c4..b59ef2b 100644
--- a/pkg/analysis_server/test/verify_sorted_test.dart
+++ b/pkg/analysis_server/test/verify_sorted_test.dart
@@ -5,6 +5,7 @@
 import 'dart:io';
 
 import 'package:analysis_server/src/services/correction/sort_members.dart';
+import 'package:analysis_tool/package_root.dart';
 import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/analysis/session.dart';
@@ -13,8 +14,6 @@
 import 'package:meta/meta.dart';
 import 'package:test/test.dart';
 
-import 'utils/package_root.dart';
-
 void main() {
   group('analysis_server', () {
     buildTestsForAnalysisServer();
diff --git a/pkg/analysis_server/test/verify_tests_test.dart b/pkg/analysis_server/test/verify_tests_test.dart
index 573eb05..73aa656 100644
--- a/pkg/analysis_server/test/verify_tests_test.dart
+++ b/pkg/analysis_server/test/verify_tests_test.dart
@@ -2,87 +2,42 @@
 // 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.
 
-import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
-import 'package:analyzer/dart/analysis/results.dart';
-import 'package:analyzer/dart/analysis/session.dart';
-import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analysis_tool/package_root.dart' as package_root;
+import 'package:analysis_tool/verify_tests.dart';
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/file_system/physical_file_system.dart';
-import 'package:path/path.dart' as path;
-import 'package:test/test.dart';
-
-import 'utils/package_root.dart' as package_root;
 
 void main() {
   var provider = PhysicalResourceProvider.INSTANCE;
   var packageRoot = provider.pathContext.normalize(package_root.packageRoot);
-  var analysisServerPath =
-      provider.pathContext.join(packageRoot, 'analysis_server');
-  var testDirPath = provider.pathContext.join(analysisServerPath, 'test');
-  var mocksDirPath = provider.pathContext.join(testDirPath, 'mock_packages');
-
-  var collection = AnalysisContextCollection(
-    resourceProvider: provider,
-    includedPaths: <String>[testDirPath],
-    excludedPaths: <String>[mocksDirPath],
-  );
-  var contexts = collection.contexts;
-  if (contexts.length != 1) {
-    fail('The test directory contains multiple analysis contexts.');
-  }
-
-  buildTestsIn(
-      contexts[0].currentSession, testDirPath, provider.getFolder(testDirPath));
+  var pathToAnalyze = provider.pathContext.join(packageRoot, 'analysis_server');
+  var testDirPath = provider.pathContext.join(pathToAnalyze, 'test');
+  _VerifyTests(testDirPath, excludedPaths: [
+    (provider.pathContext.join(testDirPath, 'mock_packages'))
+  ]).build();
 }
 
-void buildTestsIn(
-    AnalysisSession session, String testDirPath, Folder directory) {
-  var testFileNames = <String>[];
-  File testAllFile;
-  var children = directory.getChildren();
-  children.sort((first, second) => first.shortName.compareTo(second.shortName));
-  for (var child in children) {
-    if (child is Folder) {
-      if (child.shortName != 'integration' &&
-          child.getChildAssumingFile('test_all.dart').exists) {
-        testFileNames.add('${child.shortName}/test_all.dart');
-      }
-      buildTestsIn(session, testDirPath, child);
-    } else if (child is File) {
-      var name = child.shortName;
-      if (name == 'test_all.dart') {
-        testAllFile = child;
-      } else if (name.endsWith('_test.dart')) {
-        testFileNames.add(name);
-      }
+class _VerifyTests extends VerifyTests {
+  _VerifyTests(String testDirPath, {List<String> excludedPaths})
+      : super(testDirPath, excludedPaths: excludedPaths);
+
+  @override
+  bool isExpensive(Resource resource) => resource.shortName == 'integration';
+
+  @override
+  bool isOkAsAdditionalTestAllImport(Folder folder, String uri) {
+    if (folder.path == folder.provider.pathContext.join(testDirPath, 'lsp') &&
+        uri == '../src/lsp/lsp_packet_transformer_test.dart') {
+      // `lsp/test_all.dart` also runs this one test in `lsp/src` for
+      // convenience.
+      return true;
     }
+    if (folder.path == testDirPath &&
+        uri == '../tool/spec/check_all_test.dart') {
+      // The topmost `test_all.dart` also runs this one test in `tool` for
+      // convenience.
+      return true;
+    }
+    return super.isOkAsAdditionalTestAllImport(folder, uri);
   }
-  var relativePath = path.relative(directory.path, from: testDirPath);
-  test(relativePath, () {
-    if (testFileNames.isEmpty) {
-      return;
-    }
-    if (testAllFile == null) {
-      fail('Missing "test_all.dart" in $relativePath');
-    }
-    var result = session.getParsedUnit(testAllFile.path);
-    if (result.state != ResultState.VALID) {
-      fail('Could not parse ${testAllFile.path}');
-    }
-    var importedFiles = <String>[];
-    for (var directive in result.unit.directives) {
-      if (directive is ImportDirective) {
-        importedFiles.add(directive.uri.stringValue);
-      }
-    }
-    var missingFiles = <String>[];
-    for (var testFileName in testFileNames) {
-      if (!importedFiles.contains(testFileName)) {
-        missingFiles.add(testFileName);
-      }
-    }
-    if (missingFiles.isNotEmpty) {
-      fail('Tests missing from "test_all.dart": ${missingFiles.join(', ')}');
-    }
-  });
 }
diff --git a/pkg/analysis_server/tool/completion_metrics/relevance_table_generator.dart b/pkg/analysis_server/tool/completion_metrics/relevance_table_generator.dart
index b746fb6..74ef351 100644
--- a/pkg/analysis_server/tool/completion_metrics/relevance_table_generator.dart
+++ b/pkg/analysis_server/tool/completion_metrics/relevance_table_generator.dart
@@ -8,6 +8,7 @@
 import 'package:analysis_server/src/protocol_server.dart' show ElementKind;
 import 'package:analysis_server/src/services/completion/dart/feature_computer.dart';
 import 'package:analysis_server/src/utilities/flutter.dart';
+import 'package:analysis_tool/package_root.dart' as package_root;
 import 'package:analysis_tool/tools.dart';
 import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
 import 'package:analyzer/dart/analysis/context_root.dart';
@@ -26,8 +27,6 @@
 import 'package:args/args.dart';
 import 'package:meta/meta.dart';
 
-import '../../test/utils/package_root.dart' as package_root;
-
 /// Compute metrics to determine whether they should be used to compute a
 /// relevance score for completion suggestions.
 Future<void> main(List<String> args) async {
diff --git a/pkg/analysis_server_client/pubspec.yaml b/pkg/analysis_server_client/pubspec.yaml
index 9ee4b0e..465884d 100644
--- a/pkg/analysis_server_client/pubspec.yaml
+++ b/pkg/analysis_server_client/pubspec.yaml
@@ -15,4 +15,6 @@
     path: ../analyzer
   analysis_server:
     path: ../analysis_server
+  analysis_tool:
+    path: ../analysis_tool
   test: ^1.14.2
diff --git a/pkg/analysis_server_client/test/utils/package_root.dart b/pkg/analysis_server_client/test/utils/package_root.dart
deleted file mode 100644
index e8befb2..0000000
--- a/pkg/analysis_server_client/test/utils/package_root.dart
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2017, 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.
-
-import 'dart:io';
-
-import 'package:path/path.dart' as pathos;
-
-/// Returns a path to the directory containing source code for packages such as
-/// kernel, front_end, and analyzer.
-String get packageRoot {
-  // If the package root directory is specified on the command line using
-  // -DpkgRoot=..., use it.
-  const pkgRootVar =
-      bool.hasEnvironment('pkgRoot') ? String.fromEnvironment('pkgRoot') : null;
-  if (pkgRootVar != null) {
-    var path = pathos.join(Directory.current.path, pkgRootVar);
-    if (!path.endsWith(pathos.separator)) path += pathos.separator;
-    return path;
-  }
-  // Otherwise try to guess based on the script path.
-  var scriptPath = pathos.fromUri(Platform.script);
-  var parts = pathos.split(scriptPath);
-  var pkgIndex = parts.indexOf('pkg');
-  if (pkgIndex != -1) {
-    return pathos.joinAll(parts.sublist(0, pkgIndex + 1)) + pathos.separator;
-  }
-  throw StateError('Unable to find sdk/pkg/ in $scriptPath');
-}
diff --git a/pkg/analysis_server_client/test/verify_sorted_test.dart b/pkg/analysis_server_client/test/verify_sorted_test.dart
index 88f936f..66a848a 100644
--- a/pkg/analysis_server_client/test/verify_sorted_test.dart
+++ b/pkg/analysis_server_client/test/verify_sorted_test.dart
@@ -5,6 +5,7 @@
 import 'dart:io';
 
 import 'package:analysis_server/src/services/correction/sort_members.dart';
+import 'package:analysis_tool/package_root.dart';
 import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/analysis/session.dart';
@@ -12,8 +13,6 @@
 import 'package:analyzer/file_system/physical_file_system.dart';
 import 'package:test/test.dart';
 
-import 'utils/package_root.dart';
-
 void main() {
   var provider = PhysicalResourceProvider.INSTANCE;
   var normalizedRoot = provider.pathContext.normalize(packageRoot);
diff --git a/pkg/analysis_server/test/utils/package_root.dart b/pkg/analysis_tool/lib/package_root.dart
similarity index 100%
rename from pkg/analysis_server/test/utils/package_root.dart
rename to pkg/analysis_tool/lib/package_root.dart
diff --git a/pkg/analysis_tool/lib/verify_tests.dart b/pkg/analysis_tool/lib/verify_tests.dart
new file mode 100644
index 0000000..ca6fe74
--- /dev/null
+++ b/pkg/analysis_tool/lib/verify_tests.dart
@@ -0,0 +1,137 @@
+// 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.
+
+import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
+import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/analysis/session.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/file_system/physical_file_system.dart';
+import 'package:path/path.dart' as path;
+import 'package:test/test.dart';
+
+/// Helper class to test that `test_all.dart` files are properly set up in the
+/// `analyzer` package (and related packages).
+class VerifyTests {
+  /// Path to the package's `test` subdirectory.
+  final String testDirPath;
+
+  /// Paths to exclude from analysis completely.
+  final List<String> excludedPaths;
+
+  VerifyTests(this.testDirPath, {this.excludedPaths});
+
+  /// Build tests.
+  void build() {
+    var provider = PhysicalResourceProvider.INSTANCE;
+    var collection = AnalysisContextCollection(
+        resourceProvider: provider,
+        includedPaths: <String>[testDirPath],
+        excludedPaths: excludedPaths);
+    var contexts = collection.contexts;
+    if (contexts.length != 1) {
+      fail('The test directory contains multiple analysis contexts.');
+    }
+
+    _buildTestsIn(contexts[0].currentSession, testDirPath,
+        provider.getFolder(testDirPath));
+  }
+
+  /// May be overridden in a derived class to indicate whether the test file or
+  /// directory indicated by [resource] is so expensive to run that it shouldn't
+  /// be included in `test_all.dart` files.
+  ///
+  /// Default behavior is not to consider any test files or directories
+  /// expensive.
+  bool isExpensive(Resource resource) => false;
+
+  /// May be overridden in a derived class to indicate whether it is ok for a
+  /// `test_all.dart` file in [folder] to import [uri], even if there is no
+  /// corresponding test file inside [folder].
+  ///
+  /// Default behavior is to allow imports of test framework URIs.
+  bool isOkAsAdditionalTestAllImport(Folder folder, String uri) => const [
+        'package:test/test.dart',
+        'package:test_reflective_loader/test_reflective_loader.dart'
+      ].contains(uri);
+
+  /// May be overridden in a derived class to indicate whether it is ok for
+  /// a `test_all.dart` file to be missing from [folder].
+  ///
+  /// Default beahvior is not to allow `test_all.dart` to be missing from any
+  /// folder.
+  bool isOkForTestAllToBeMissing(Folder folder) => false;
+
+  void _buildTestsIn(
+      AnalysisSession session, String testDirPath, Folder directory) {
+    var testFileNames = <String>[];
+    File testAllFile;
+    var children = directory.getChildren();
+    children
+        .sort((first, second) => first.shortName.compareTo(second.shortName));
+    for (var child in children) {
+      if (child is Folder) {
+        if (child.getChildAssumingFile('test_all.dart').exists &&
+            !isExpensive(child)) {
+          testFileNames.add('${child.shortName}/test_all.dart');
+        }
+        _buildTestsIn(session, testDirPath, child);
+      } else if (child is File) {
+        var name = child.shortName;
+        if (name == 'test_all.dart') {
+          testAllFile = child;
+        } else if (name.endsWith('_test.dart') && !isExpensive(child)) {
+          testFileNames.add(name);
+        }
+      }
+    }
+    var relativePath = path.relative(directory.path, from: testDirPath);
+    test(relativePath, () {
+      if (testFileNames.isEmpty) {
+        return;
+      }
+      if (testAllFile == null) {
+        if (!isOkForTestAllToBeMissing(directory)) {
+          fail('Missing "test_all.dart" in $relativePath');
+        } else {
+          // Ok that the `test_all.dart` file is missing; there's nothing else to
+          // check.
+          return;
+        }
+      }
+      if (isOkForTestAllToBeMissing(directory)) {
+        fail('Found "test_all.dart" in $relativePath but did not expect one');
+      }
+      var result = session.getParsedUnit(testAllFile.path);
+      if (result.state != ResultState.VALID) {
+        fail('Could not parse ${testAllFile.path}');
+      }
+      var importedFiles = <String>[];
+      for (var directive in result.unit.directives) {
+        if (directive is ImportDirective) {
+          importedFiles.add(directive.uri.stringValue);
+        }
+      }
+      var missingFiles = <String>[];
+      for (var testFileName in testFileNames) {
+        if (!importedFiles.contains(testFileName)) {
+          missingFiles.add(testFileName);
+        }
+      }
+      if (missingFiles.isNotEmpty) {
+        fail('Tests missing from "test_all.dart": ${missingFiles.join(', ')}');
+      }
+      var extraImports = <String>[];
+      for (var importedFile in importedFiles) {
+        if (!testFileNames.contains(importedFile) &&
+            !isOkAsAdditionalTestAllImport(directory, importedFile)) {
+          extraImports.add(importedFile);
+        }
+      }
+      if (extraImports.isNotEmpty) {
+        fail('Extra tests in "test_all.dart": ${extraImports.join(', ')}');
+      }
+    });
+  }
+}
diff --git a/pkg/analysis_tool/pubspec.yaml b/pkg/analysis_tool/pubspec.yaml
index 32199b3..f4763fd 100644
--- a/pkg/analysis_tool/pubspec.yaml
+++ b/pkg/analysis_tool/pubspec.yaml
@@ -6,5 +6,7 @@
   sdk: '>=2.1.0 <3.0.0'
 
 dependencies:
+  analyzer: any
   html: any
   path: any
+  test: any
diff --git a/pkg/analyzer/lib/dart/analysis/features.dart b/pkg/analyzer/lib/dart/analysis/features.dart
index e086608..5390ffa 100644
--- a/pkg/analyzer/lib/dart/analysis/features.dart
+++ b/pkg/analyzer/lib/dart/analysis/features.dart
@@ -32,6 +32,10 @@
   /// Feature information for the triple-shift operator.
   static final triple_shift = ExperimentalFeatures.triple_shift;
 
+  /// Feature information for non-function type aliases.
+  static final nonfunction_type_aliases =
+      ExperimentalFeatures.nonfunction_type_aliases;
+
   /// Feature information for variance.
   static final variance = ExperimentalFeatures.variance;
 
diff --git a/pkg/analyzer/lib/dart/ast/ast.dart b/pkg/analyzer/lib/dart/ast/ast.dart
index 7ee7034..9f49d07 100644
--- a/pkg/analyzer/lib/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/dart/ast/ast.dart
@@ -2933,12 +2933,18 @@
   set equals(Token token);
 
   /// Return the type of function being defined by the alias.
+  ///
+  /// When the non-function type aliases feature is enabled and the denoted
+  /// type is not a [GenericFunctionType], return `null`.
   GenericFunctionType get functionType;
 
   /// Set the type of function being defined by the alias to the given
   /// [functionType].
   set functionType(GenericFunctionType functionType);
 
+  /// Return the type being defined by the alias.
+  TypeAnnotation get type;
+
   /// Return the type parameters for the function type, or `null` if the
   /// function type does not have any type parameters.
   TypeParameterList get typeParameters;
diff --git a/pkg/analyzer/lib/dart/ast/ast_factory.dart b/pkg/analyzer/lib/dart/ast/ast_factory.dart
index 65b253e..4dcba21 100644
--- a/pkg/analyzer/lib/dart/ast/ast_factory.dart
+++ b/pkg/analyzer/lib/dart/ast/ast_factory.dart
@@ -511,7 +511,7 @@
       SimpleIdentifier name,
       TypeParameterList typeParameters,
       Token equals,
-      GenericFunctionType functionType,
+      TypeAnnotation type,
       Token semicolon);
 
   /// Returns a newly created import show combinator.
diff --git a/pkg/analyzer/lib/src/dart/ast/ast.dart b/pkg/analyzer/lib/src/dart/ast/ast.dart
index 428ee8a..9a4bcad 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast.dart
@@ -19,7 +19,6 @@
 import 'package:analyzer/src/generated/engine.dart' show AnalysisEngine;
 import 'package:analyzer/src/generated/engine.dart';
 import 'package:analyzer/src/generated/java_engine.dart';
-import 'package:analyzer/src/generated/parser.dart';
 import 'package:analyzer/src/generated/source.dart' show LineInfo, Source;
 import 'package:analyzer/src/generated/utilities_dart.dart';
 
@@ -1012,13 +1011,15 @@
   Token get endToken => _block.endToken;
 
   @override
-  bool get isAsynchronous => keyword != null && keyword.lexeme == Parser.ASYNC;
+  bool get isAsynchronous =>
+      keyword != null && keyword.lexeme == Keyword.ASYNC.lexeme;
 
   @override
   bool get isGenerator => star != null;
 
   @override
-  bool get isSynchronous => keyword == null || keyword.lexeme != Parser.ASYNC;
+  bool get isSynchronous =>
+      keyword == null || keyword.lexeme != Keyword.ASYNC.lexeme;
 
   @override
   E accept<E>(AstVisitor<E> visitor) => visitor.visitBlockFunctionBody(this);
@@ -5363,6 +5364,9 @@
 ///        metadata 'typedef' [SimpleIdentifier] [TypeParameterList]? =
 ///        [FunctionType] ';'
 class GenericTypeAliasImpl extends TypeAliasImpl implements GenericTypeAlias {
+  /// The type being defined by the alias.
+  TypeAnnotationImpl _type;
+
   /// The type parameters for the function type, or `null` if the function
   /// type does not have any type parameters.
   TypeParameterListImpl _typeParameters;
@@ -5370,9 +5374,6 @@
   @override
   Token equals;
 
-  /// The type of function being defined by the alias.
-  GenericFunctionTypeImpl _functionType;
-
   /// Returns a newly created generic type alias. Either or both of the
   /// [comment] and [metadata] can be `null` if the variable list does not have
   /// the corresponding attribute. The [typeParameters] can be `null` if there
@@ -5384,11 +5385,11 @@
       SimpleIdentifierImpl name,
       TypeParameterListImpl typeParameters,
       this.equals,
-      GenericFunctionTypeImpl functionType,
+      TypeAnnotationImpl type,
       Token semicolon)
       : super(comment, metadata, typedefToken, name, semicolon) {
     _typeParameters = _becomeParentOf(typeParameters);
-    _functionType = _becomeParentOf(functionType);
+    _type = _becomeParentOf(type);
   }
 
   @override
@@ -5398,20 +5399,31 @@
     ..add(name)
     ..add(_typeParameters)
     ..add(equals)
-    ..add(_functionType);
+    ..add(_type);
 
   @override
   Element get declaredElement => name.staticElement;
 
+  /// The type of function being defined by the alias.
+  ///
+  /// If the non-function type aliases feature is enabled, a type alias may have
+  /// a [_type] which is not a [GenericFunctionTypeImpl].  In that case `null`
+  /// is returned.
   @override
-  GenericFunctionType get functionType => _functionType;
+  GenericFunctionType get functionType {
+    var t = _type;
+    return t is GenericFunctionTypeImpl ? t : null;
+  }
 
   @override
   set functionType(GenericFunctionType functionType) {
-    _functionType = _becomeParentOf(functionType as GenericFunctionTypeImpl);
+    _type = _becomeParentOf(functionType as TypeAnnotationImpl);
   }
 
   @override
+  TypeAnnotation get type => _type;
+
+  @override
   TypeParameterList get typeParameters => _typeParameters;
 
   @override
@@ -5429,7 +5441,7 @@
     super.visitChildren(visitor);
     name?.accept(visitor);
     _typeParameters?.accept(visitor);
-    _functionType?.accept(visitor);
+    _type?.accept(visitor);
   }
 }
 
diff --git a/pkg/analyzer/lib/src/dart/ast/ast_factory.dart b/pkg/analyzer/lib/src/dart/ast/ast_factory.dart
index 8e1fd6a..f1a91b6 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast_factory.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast_factory.dart
@@ -575,10 +575,10 @@
           SimpleIdentifier name,
           TypeParameterList typeParameters,
           Token equals,
-          GenericFunctionType functionType,
+          TypeAnnotation type,
           Token semicolon) =>
       GenericTypeAliasImpl(comment, metadata, typedefKeyword, name,
-          typeParameters, equals, functionType, semicolon);
+          typeParameters, equals, type, semicolon);
 
   @override
   HideCombinator hideCombinator(
diff --git a/pkg/analyzer/lib/src/fasta/ast_builder.dart b/pkg/analyzer/lib/src/fasta/ast_builder.dart
index ccfec81..5204bac 100644
--- a/pkg/analyzer/lib/src/fasta/ast_builder.dart
+++ b/pkg/analyzer/lib/src/fasta/ast_builder.dart
@@ -128,6 +128,9 @@
   /// `true` if triple-shift behavior is enabled
   final bool enableTripleShift;
 
+  /// `true` if nonfunction-type-aliases behavior is enabled
+  final bool enableNonFunctionTypeAliases;
+
   /// `true` if variance behavior is enabled
   final bool enableVariance;
 
@@ -143,6 +146,8 @@
         enableControlFlowCollections =
             _featureSet.isEnabled(Feature.control_flow_collections),
         enableTripleShift = _featureSet.isEnabled(Feature.triple_shift),
+        enableNonFunctionTypeAliases =
+            _featureSet.isEnabled(Feature.nonfunction_type_aliases),
         enableVariance = _featureSet.isEnabled(Feature.variance),
         uri = uri ?? fileUri;
 
@@ -1532,10 +1537,8 @@
       SimpleIdentifier name = pop();
       List<Annotation> metadata = pop();
       Comment comment = _findComment(metadata, typedefKeyword);
-      if (type is! GenericFunctionType) {
-        // This error is also reported in the OutlineBuilder.
+      if (type is! GenericFunctionType && !enableNonFunctionTypeAliases) {
         handleRecoverableError(messageTypedefNotFunction, equals, equals);
-        type = null;
       }
       declarations.add(ast.genericTypeAlias(
           comment,
@@ -1544,7 +1547,7 @@
           name,
           templateParameters,
           equals,
-          type as GenericFunctionType,
+          type,
           semicolon));
     }
   }
@@ -3042,8 +3045,11 @@
 
       // Determine if this is a set or map based on type args and content
       final typeArgCount = typeArguments?.arguments?.length;
-      bool isSet =
-          typeArgCount == 1 ? true : typeArgCount != null ? false : null;
+      bool isSet = typeArgCount == 1
+          ? true
+          : typeArgCount != null
+              ? false
+              : null;
       isSet ??= hasSetEntry;
 
       // Build the set or map
diff --git a/pkg/analyzer/lib/src/generated/element_resolver.dart b/pkg/analyzer/lib/src/generated/element_resolver.dart
index 8a09582..5864421 100644
--- a/pkg/analyzer/lib/src/generated/element_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/element_resolver.dart
@@ -443,7 +443,7 @@
 
   @override
   void visitSuperConstructorInvocation(SuperConstructorInvocation node) {
-    ClassElementImpl enclosingClass = _resolver.enclosingClass;
+    var enclosingClass = _resolver.enclosingClass;
     if (enclosingClass == null) {
       // TODO(brianwilkerson) Report this error.
       return;
@@ -646,7 +646,7 @@
     element = _resolver.toLegacyElement(element);
 
     if (element is PropertyAccessorElement && identifier.inSetterContext()) {
-      PropertyAccessorElement setter = lookupResult.setter;
+      var setter = lookupResult.setter;
       if (setter == null) {
         //
         // Check to see whether there might be a locally defined getter and
@@ -734,7 +734,8 @@
   /// Resolve each of the annotations in the given list of [annotations].
   static void _resolveAnnotations(NodeList<Annotation> annotations) {
     for (Annotation annotation in annotations) {
-      ElementAnnotationImpl elementAnnotation = annotation.elementAnnotation;
+      var elementAnnotation =
+          annotation.elementAnnotation as ElementAnnotationImpl;
       elementAnnotation.element = annotation.element;
     }
   }
diff --git a/pkg/analyzer/lib/src/generated/engine.dart b/pkg/analyzer/lib/src/generated/engine.dart
index f1baaf4..e0368a4 100644
--- a/pkg/analyzer/lib/src/generated/engine.dart
+++ b/pkg/analyzer/lib/src/generated/engine.dart
@@ -386,7 +386,7 @@
   FeatureSet get contextFeatures => _contextFeatures;
 
   set contextFeatures(FeatureSet featureSet) {
-    _contextFeatures = featureSet;
+    _contextFeatures = featureSet as ExperimentStatus;
     nonPackageFeatureSet = featureSet;
   }
 
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 98b30f2..b1123a3 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -22,6 +22,7 @@
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
 import 'package:analyzer/src/dart/element/type.dart';
+import 'package:analyzer/src/dart/element/type_provider.dart';
 import 'package:analyzer/src/dart/element/type_system.dart';
 import 'package:analyzer/src/dart/resolver/scope.dart';
 import 'package:analyzer/src/dart/resolver/variance.dart';
@@ -240,8 +241,8 @@
     _isInStaticVariableDeclaration = false;
     _isInConstructorInitializer = false;
     _intType = _typeProvider.intType;
-    _typeSystem = _currentLibrary.typeSystem;
-    _options = _currentLibrary.context.analysisOptions;
+    _typeSystem = _currentLibrary.typeSystem as TypeSystemImpl;
+    _options = _currentLibrary.context.analysisOptions as AnalysisOptionsImpl;
     _typeArgumentsVerifier =
         TypeArgumentsVerifier(_options, _currentLibrary, _errorReporter);
     _constructorFieldsVerifier = ConstructorFieldsVerifier(
@@ -249,7 +250,7 @@
       errorReporter: _errorReporter,
     );
     _returnTypeVerifier = ReturnTypeVerifier(
-      typeProvider: _typeProvider,
+      typeProvider: _typeProvider as TypeProviderImpl,
       typeSystem: _typeSystem,
       errorReporter: _errorReporter,
     );
@@ -268,7 +269,7 @@
     assert(_enclosingClass == null);
     assert(_enclosingEnum == null);
     assert(_enclosingExecutable.element == null);
-    _enclosingClass = classElement;
+    _enclosingClass = classElement as ClassElementImpl;
   }
 
   /// The language team is thinking about adding abstract fields, or external
@@ -329,14 +330,14 @@
       _checkForArgumentTypeNotAssignableForArgument(rhs);
     }
     if (operatorType == TokenType.QUESTION_QUESTION_EQ) {
-      _checkForDeadNullCoalesce(node.readType, node.rightHandSide);
+      _checkForDeadNullCoalesce(node.readType as TypeImpl, node.rightHandSide);
     }
     _checkForAssignmentToFinal(lhs);
     if (lhs is IndexExpression) {
       _checkIndexExpressionIndex(
         lhs.index,
-        readElement: node.readElement,
-        writeElement: node.writeElement,
+        readElement: node.readElement as ExecutableElement,
+        writeElement: node.writeElement as ExecutableElement,
       );
     }
     super.visitAssignmentExpression(node);
@@ -371,7 +372,8 @@
     }
 
     if (type == TokenType.QUESTION_QUESTION) {
-      _checkForDeadNullCoalesce(node.leftOperand.staticType, node.rightOperand);
+      _checkForDeadNullCoalesce(
+          node.leftOperand.staticType as TypeImpl, node.rightOperand);
     }
 
     _checkForUseOfVoidResult(node.leftOperand);
@@ -427,7 +429,7 @@
     ClassElementImpl outerClass = _enclosingClass;
     try {
       _isInNativeClass = node.nativeClause != null;
-      _enclosingClass = node.declaredElement;
+      _enclosingClass = node.declaredElement as ClassElementImpl;
 
       List<ClassMember> members = node.members;
       _duplicateDefinitionVerifier.checkClass(node);
@@ -465,7 +467,7 @@
         node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME);
     ClassElementImpl outerClassElement = _enclosingClass;
     try {
-      _enclosingClass = node.declaredElement;
+      _enclosingClass = node.declaredElement as ClassElementImpl;
       _checkClassInheritance(
           node, node.superclass, node.withClause, node.implementsClause);
       _checkForMainFunction(node.name);
@@ -567,7 +569,7 @@
 
   @override
   void visitExportDirective(ExportDirective node) {
-    ExportElement exportElement = node.element;
+    var exportElement = node.element as ExportElement;
     if (exportElement != null) {
       LibraryElement exportedLibrary = exportElement.exportedLibrary;
       _checkForAmbiguousExport(node, exportElement, exportedLibrary);
@@ -711,7 +713,8 @@
         GetterSetterTypesVerifier(
           typeSystem: _typeSystem,
           errorReporter: _errorReporter,
-        ).checkGetter(node.name, node.declaredElement);
+        ).checkGetter(
+            node.name, node.declaredElement as PropertyAccessorElement);
       }
       if (node.isSetter) {
         FunctionExpression functionExpression = node.functionExpression;
@@ -808,7 +811,8 @@
     _checkForBuiltInIdentifierAsName(
         node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME);
     _checkForMainFunction(node.name);
-    _checkForTypeAliasCannotReferenceItself(node, node.declaredElement);
+    _checkForTypeAliasCannotReferenceItself(
+        node, node.declaredElement as FunctionTypeAliasElement);
     super.visitGenericTypeAlias(node);
   }
 
@@ -820,7 +824,7 @@
 
   @override
   void visitImportDirective(ImportDirective node) {
-    ImportElement importElement = node.element;
+    var importElement = node.element as ImportElement;
     if (node.prefix != null) {
       _checkForBuiltInIdentifierAsName(
           node.prefix, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_PREFIX_NAME);
@@ -910,7 +914,8 @@
         GetterSetterTypesVerifier(
           typeSystem: _typeSystem,
           errorReporter: _errorReporter,
-        ).checkGetter(node.name, node.declaredElement);
+        ).checkGetter(
+            node.name, node.declaredElement as PropertyAccessorElement);
       }
       if (node.isSetter) {
         _checkForWrongNumberOfParametersForSetter(node.name, node.parameters);
@@ -952,7 +957,7 @@
     // TODO(scheglov) Verify for all mixin errors.
     ClassElementImpl outerClass = _enclosingClass;
     try {
-      _enclosingClass = node.declaredElement;
+      _enclosingClass = node.declaredElement as ClassElementImpl;
 
       List<ClassMember> members = node.members;
       _duplicateDefinitionVerifier.checkMixin(node);
@@ -1009,8 +1014,8 @@
     if (operand is IndexExpression) {
       _checkIndexExpressionIndex(
         operand.index,
-        readElement: node.readElement,
-        writeElement: node.writeElement,
+        readElement: node.readElement as ExecutableElement,
+        writeElement: node.writeElement as ExecutableElement,
       );
     }
     super.visitPostfixExpression(node);
@@ -1042,8 +1047,8 @@
     if (operand is IndexExpression) {
       _checkIndexExpressionIndex(
         operand.index,
-        readElement: node.readElement,
-        writeElement: node.writeElement,
+        readElement: node.readElement as ExecutableElement,
+        writeElement: node.writeElement as ExecutableElement,
       );
     }
     super.visitPrefixExpression(node);
@@ -1570,7 +1575,8 @@
   }) {
     DartType staticParameterType = parameter?.type;
     if (promoteParameterToNullable && staticParameterType != null) {
-      staticParameterType = _typeSystem.makeNullable(staticParameterType);
+      staticParameterType =
+          _typeSystem.makeNullable(staticParameterType as TypeImpl);
     }
     _checkForArgumentTypeNotAssignableWithExpectedTypes(argument,
         staticParameterType, CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE);
@@ -3295,7 +3301,7 @@
   /// the [mixinIndex] position in the mixins list are satisfied by the
   /// [_enclosingClass], or a previous mixin.
   bool _checkForMixinSuperclassConstraints(int mixinIndex, TypeName mixinName) {
-    InterfaceType mixinType = mixinName.type;
+    InterfaceType mixinType = mixinName.type as InterfaceType;
     for (var constraint in mixinType.superclassConstraints) {
       var superType = _enclosingClass.supertype as InterfaceTypeImpl;
       if (_currentLibrary.isNonNullableByDefault) {
@@ -3330,7 +3336,7 @@
   /// implementations of all the super-invoked members of the [mixinElement].
   bool _checkForMixinSuperInvokedMembers(int mixinIndex, TypeName mixinName,
       ClassElement mixinElement, InterfaceType mixinType) {
-    ClassElementImpl mixinElementImpl = mixinElement;
+    var mixinElementImpl = mixinElement as ClassElementImpl;
     if (mixinElementImpl.superInvokedNames.isEmpty) {
       return false;
     }
@@ -4903,7 +4909,7 @@
     if (withClause == null) {
       return;
     }
-    ClassElement classElement = node.declaredElement;
+    var classElement = node.declaredElement as ClassElement;
     var supertype = classElement.supertype;
 
     var interfacesMerger = InterfacesMerger(_typeSystem);
@@ -5308,7 +5314,7 @@
       if (fieldDeclaration is FieldDeclaration) {
         for (VariableDeclaration field in fieldDeclaration.fields.variables) {
           if (field.initializer == null) {
-            fields.add(field.declaredElement);
+            fields.add(field.declaredElement as FieldElement);
           }
         }
       }
diff --git a/pkg/analyzer/lib/src/generated/parser_fasta.dart b/pkg/analyzer/lib/src/generated/parser_fasta.dart
index a3a03ad..c43c75f 100644
--- a/pkg/analyzer/lib/src/generated/parser_fasta.dart
+++ b/pkg/analyzer/lib/src/generated/parser_fasta.dart
@@ -72,7 +72,7 @@
     currentToken = fastaParser
         .parseMetadata(fastaParser.syntheticPreviousToken(currentToken))
         .next;
-    return astBuilder.pop();
+    return astBuilder.pop() as Annotation;
   }
 
   @override
@@ -82,7 +82,7 @@
     currentToken = fastaParser
         .parseArguments(fastaParser.syntheticPreviousToken(currentToken))
         .next;
-    MethodInvocation invocation = astBuilder.pop();
+    var invocation = astBuilder.pop() as MethodInvocation;
     return invocation.argumentList.arguments[0];
   }
 
@@ -92,7 +92,9 @@
         .parseArguments(fastaParser.syntheticPreviousToken(currentToken))
         .next;
     var result = astBuilder.pop();
-    return result is MethodInvocation ? result.argumentList : result;
+    return result is MethodInvocation
+        ? result.argumentList
+        : result as ArgumentList;
   }
 
   @override
@@ -124,7 +126,7 @@
       null /* leftBracket */,
       <ClassMember>[],
       null /* rightBracket */,
-    );
+    ) as ClassDeclarationImpl;
     // TODO(danrubel): disambiguate between class and mixin
     currentToken = fastaParser.parseClassMember(currentToken, className);
     //currentToken = fastaParser.parseMixinMember(currentToken);
@@ -138,7 +140,7 @@
     currentToken = fastaParser
         .parseCombinatorStar(fastaParser.syntheticPreviousToken(currentToken))
         .next;
-    return astBuilder.pop();
+    return astBuilder.pop() as List<Combinator>;
   }
 
   @override
@@ -150,7 +152,7 @@
   @override
   CompilationUnit parseCompilationUnit2() {
     currentToken = fastaParser.parseUnit(currentToken);
-    return astBuilder.pop();
+    return astBuilder.pop() as CompilationUnit;
   }
 
   @override
@@ -161,7 +163,7 @@
     currentToken = fastaParser
         .parseConditionalUri(fastaParser.syntheticPreviousToken(currentToken))
         .next;
-    return astBuilder.pop();
+    return astBuilder.pop() as Configuration;
   }
 
   @override
@@ -176,7 +178,7 @@
   @override
   CompilationUnit parseDirectives2() {
     currentToken = fastaParser.parseDirectives(currentToken);
-    return astBuilder.pop();
+    return astBuilder.pop() as CompilationUnit;
   }
 
   @override
@@ -184,7 +186,7 @@
     currentToken = fastaParser
         .parseDottedName(fastaParser.syntheticPreviousToken(currentToken))
         .next;
-    return astBuilder.pop();
+    return astBuilder.pop() as DottedName;
   }
 
   @override
@@ -201,7 +203,7 @@
     currentToken = fastaParser
         .parseExpression(fastaParser.syntheticPreviousToken(currentToken))
         .next;
-    return astBuilder.pop();
+    return astBuilder.pop() as Expression;
   }
 
   @override
@@ -216,7 +218,7 @@
                 ? fasta.MemberKind.GeneralizedFunctionType
                 : fasta.MemberKind.NonStaticMethod)
         .next;
-    return astBuilder.pop();
+    return astBuilder.pop() as FormalParameterList;
   }
 
   @override
@@ -226,11 +228,12 @@
         fastaParser.syntheticPreviousToken(currentToken));
     currentToken =
         fastaParser.parseFunctionBody(currentToken, inExpression, mayBeEmpty);
-    return astBuilder.pop();
+    return astBuilder.pop() as FunctionBody;
   }
 
   @override
-  FunctionExpression parseFunctionExpression() => parseExpression2();
+  FunctionExpression parseFunctionExpression() =>
+      parseExpression2() as FunctionExpression;
 
   @override
   Expression parseLogicalAndExpression() => parseExpression2();
@@ -242,13 +245,14 @@
   Expression parseMultiplicativeExpression() => parseExpression2();
 
   @override
-  InstanceCreationExpression parseNewExpression() => parseExpression2();
+  InstanceCreationExpression parseNewExpression() =>
+      parseExpression2() as InstanceCreationExpression;
 
   @override
   Expression parsePostfixExpression() => parseExpression2();
 
   @override
-  Identifier parsePrefixedIdentifier() => parseExpression2();
+  Identifier parsePrefixedIdentifier() => parseExpression2() as Identifier;
 
   @override
   Expression parsePrimaryExpression() {
@@ -256,7 +260,7 @@
         .parsePrimary(fastaParser.syntheticPreviousToken(currentToken),
             fasta.IdentifierContext.expression)
         .next;
-    return astBuilder.pop();
+    return astBuilder.pop() as Expression;
   }
 
   @override
@@ -271,7 +275,7 @@
   @override
   SimpleIdentifier parseSimpleIdentifier(
           {bool allowKeyword = false, bool isDeclaration = false}) =>
-      parseExpression2();
+      parseExpression2() as SimpleIdentifier;
 
   @override
   Statement parseStatement(Token token) {
@@ -284,14 +288,14 @@
     currentToken = fastaParser
         .parseStatement(fastaParser.syntheticPreviousToken(currentToken))
         .next;
-    return astBuilder.pop();
+    return astBuilder.pop() as Statement;
   }
 
   @override
-  StringLiteral parseStringLiteral() => parseExpression2();
+  StringLiteral parseStringLiteral() => parseExpression2() as StringLiteral;
 
   @override
-  SymbolLiteral parseSymbolLiteral() => parseExpression2();
+  SymbolLiteral parseSymbolLiteral() => parseExpression2() as SymbolLiteral;
 
   @override
   Expression parseThrowExpression() => parseExpression2();
@@ -312,7 +316,7 @@
         .computeType(previous, true, !inExpression)
         .parseType(previous, fastaParser)
         .next;
-    return astBuilder.pop();
+    return astBuilder.pop() as TypeAnnotation;
   }
 
   @override
@@ -322,7 +326,7 @@
         .computeTypeParamOrArg(previous)
         .parseArguments(previous, fastaParser)
         .next;
-    return astBuilder.pop();
+    return astBuilder.pop() as TypeArgumentList;
   }
 
   @override
@@ -332,7 +336,7 @@
         .computeType(previous, true, !inExpression)
         .parseType(previous, fastaParser)
         .next;
-    return astBuilder.pop();
+    return astBuilder.pop() as TypeName;
   }
 
   @override
@@ -352,7 +356,7 @@
         .computeTypeParamOrArg(token, true)
         .parseVariables(token, fastaParser)
         .next;
-    return astBuilder.pop();
+    return astBuilder.pop() as TypeParameterList;
   }
 
   @override
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 4cbf579..56ed8d4 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -275,7 +275,7 @@
             inheritanceManager,
             definingLibrary,
             source,
-            definingLibrary.typeSystem,
+            definingLibrary.typeSystem as TypeSystemImpl,
             typeProvider,
             errorListener,
             featureSet ??
@@ -301,7 +301,8 @@
       MigrationResolutionHooks migrationResolutionHooks)
       : _featureSet = featureSet,
         migrationResolutionHooks = migrationResolutionHooks,
-        super(definingLibrary, source, typeProvider, errorListener,
+        super(definingLibrary, source, typeProvider as TypeProviderImpl,
+            errorListener,
             nameScope: nameScope) {
     _promoteManager = TypePromotionManager(typeSystem);
 
@@ -463,7 +464,8 @@
     }
 
     if (element is VariableElement) {
-      var assigned = _flowAnalysis.isDefinitelyAssigned(node, element);
+      var assigned = _flowAnalysis.isDefinitelyAssigned(
+          node, element as PromotableElement);
       var unassigned = _flowAnalysis.isDefinitelyUnassigned(node, element);
 
       if (element.isLate) {
@@ -546,7 +548,7 @@
         _flowAnalysis.flow.nullAwareAccess_end();
       } while (identical(_unfinishedNullShorts.last, node));
       if (node is! CascadeExpression && !discardType) {
-        node.staticType = typeSystem.makeNullable(node.staticType);
+        node.staticType = typeSystem.makeNullable(node.staticType as TypeImpl);
       }
     }
   }
@@ -670,7 +672,7 @@
       if (element is PropertyAccessorElement && element.isGetter) {
         readType = element.returnType;
       } else if (element is VariableElement) {
-        readType = localVariableTypeProvider.getType(node);
+        readType = localVariableTypeProvider.getType(node as SimpleIdentifier);
       }
     }
 
@@ -764,7 +766,7 @@
   /// Otherwise, return the original element.
   T toLegacyElement<T extends Element>(T element) {
     if (_isNonNullableByDefault) return element;
-    return Member.legacy(element);
+    return Member.legacy(element) as T;
   }
 
   /// If in a legacy library, return the legacy version of the [type].
@@ -877,7 +879,7 @@
 
   @override
   void visitAssignmentExpression(AssignmentExpression node) {
-    _assignmentExpressionResolver.resolve(node);
+    _assignmentExpressionResolver.resolve(node as AssignmentExpressionImpl);
   }
 
   @override
@@ -892,7 +894,7 @@
 
   @override
   void visitBinaryExpression(BinaryExpression node) {
-    _binaryExpressionResolver.resolve(node);
+    _binaryExpressionResolver.resolve(node as BinaryExpressionImpl);
   }
 
   @override
@@ -1127,7 +1129,7 @@
       _promoteManager.exitFunctionBody();
     }
 
-    ConstructorElementImpl constructor = node.declaredElement;
+    var constructor = node.declaredElement as ConstructorElementImpl;
     constructor.constantInitializers =
         _createCloner().cloneNodeList(node.initializers);
 
@@ -1315,12 +1317,12 @@
 
   @override
   void visitForElementInScope(ForElement node) {
-    _forResolver.resolveElement(node);
+    _forResolver.resolveElement(node as ForElementImpl);
   }
 
   @override
   void visitForStatementInScope(ForStatement node) {
-    _forResolver.resolveStatement(node);
+    _forResolver.resolveStatement(node as ForStatementImpl);
     nullSafetyDeadCodeVerifier?.flowEnd(node.body);
   }
 
@@ -1415,7 +1417,8 @@
   @override
   void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
     node.function?.accept(this);
-    _functionExpressionInvocationResolver.resolve(node);
+    _functionExpressionInvocationResolver
+        .resolve(node as FunctionExpressionInvocationImpl);
   }
 
   @override
@@ -1540,7 +1543,7 @@
     );
 
     var element = result.readElement;
-    node.staticElement = element;
+    node.staticElement = element as MethodElement;
 
     InferenceContext.setType(node.index, result.indexContextType);
     node.index?.accept(this);
@@ -1708,7 +1711,7 @@
 
   @override
   void visitPostfixExpression(PostfixExpression node) {
-    _postfixExpressionResolver.resolve(node);
+    _postfixExpressionResolver.resolve(node as PostfixExpressionImpl);
   }
 
   @override
@@ -1718,7 +1721,7 @@
 
   @override
   void visitPrefixExpression(PrefixExpression node) {
-    _prefixExpressionResolver.resolve(node);
+    _prefixExpressionResolver.resolve(node as PrefixExpressionImpl);
   }
 
   @override
@@ -1927,8 +1930,8 @@
         var catchClause = catchClauses[i];
         nullSafetyDeadCodeVerifier.verifyCatchClause(catchClause);
         flow.tryCatchStatement_catchBegin(
-          catchClause.exceptionParameter?.staticElement,
-          catchClause.stackTraceParameter?.staticElement,
+          catchClause.exceptionParameter?.staticElement as PromotableElement,
+          catchClause.stackTraceParameter?.staticElement as PromotableElement,
         );
         catchClause.accept(this);
         flow.tryCatchStatement_catchEnd();
@@ -1952,7 +1955,7 @@
 
   @override
   void visitVariableDeclaration(VariableDeclaration node) {
-    _variableDeclarationResolver.resolve(node);
+    _variableDeclarationResolver.resolve(node as VariableDeclarationImpl);
 
     var declaredElement = node.declaredElement;
     if (node.parent.parent is ForParts) {
@@ -1966,10 +1969,12 @@
       var initializerStaticType = initializer.staticType;
       if (declaredType == null) {
         if (initializerStaticType is TypeParameterType) {
-          _flowAnalysis?.flow?.promote(declaredElement, initializerStaticType);
+          _flowAnalysis?.flow?.promote(
+              declaredElement as PromotableElement, initializerStaticType);
         }
       } else if (!parent.isFinal) {
-        _flowAnalysis?.flow?.write(declaredElement, initializerStaticType,
+        _flowAnalysis?.flow?.write(
+            declaredElement as PromotableElement, initializerStaticType,
             viaInitializer: true);
       }
     }
@@ -2102,7 +2107,7 @@
         // as computing constant values. It is stored in two places.
         var constructorElement = ConstructorMember.from(
           rawElement,
-          inferred.returnType,
+          inferred.returnType as InterfaceType,
         );
         constructorElement = toLegacyElement(constructorElement);
         constructor.staticElement = constructorElement;
@@ -2111,7 +2116,7 @@
 
     if (inferred == null) {
       var type = originalElement?.type;
-      type = toLegacyTypeIfOptOut(type);
+      type = toLegacyTypeIfOptOut(type) as FunctionType;
       InferenceContext.setType(node.argumentList, type);
     }
   }
@@ -2141,7 +2146,8 @@
       }
     }
 
-    _functionExpressionInvocationResolver.resolve(node);
+    _functionExpressionInvocationResolver
+        .resolve(node as FunctionExpressionInvocationImpl);
 
     nullShortingTermination(node);
   }
@@ -2804,7 +2810,7 @@
   void visitGenericTypeAlias(GenericTypeAlias node) {
     Scope outerScope = nameScope;
     try {
-      FunctionTypeAliasElement element = node.declaredElement;
+      var element = node.declaredElement as FunctionTypeAliasElement;
       nameScope = TypeParameterScope(nameScope, element.typeParameters);
       super.visitGenericTypeAlias(node);
 
@@ -3053,7 +3059,8 @@
   VariableResolverVisitor(LibraryElement definingLibrary, Source source,
       TypeProvider typeProvider, AnalysisErrorListener errorListener,
       {Scope nameScope})
-      : super(definingLibrary, source, typeProvider, errorListener,
+      : super(definingLibrary, source, typeProvider as TypeProviderImpl,
+            errorListener,
             nameScope: nameScope);
 
   @override
diff --git a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
index 6a45033..a51dcbc 100644
--- a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
+++ b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
@@ -411,7 +411,7 @@
         isConst: node.isConst);
 
     if (inferred != null && inferred != originalElement.type) {
-      inferred = _resolver.toLegacyTypeIfOptOut(inferred);
+      inferred = _resolver.toLegacyTypeIfOptOut(inferred) as FunctionType;
       // Fix up the parameter elements based on inferred method.
       arguments.correspondingStaticParameters =
           ResolverVisitor.resolveArgumentsToParameters(
@@ -421,7 +421,7 @@
       // computing constant values. It is stored in two places.
       var constructorElement = ConstructorMember.from(
         rawElement,
-        inferred.returnType,
+        inferred.returnType as InterfaceType,
       );
       constructorElement = _resolver.toLegacyElement(constructorElement);
       constructor.staticElement = constructorElement;
diff --git a/pkg/analyzer/lib/src/generated/testing/element_factory.dart b/pkg/analyzer/lib/src/generated/testing/element_factory.dart
index c0c1b6e..786a77f 100644
--- a/pkg/analyzer/lib/src/generated/testing/element_factory.dart
+++ b/pkg/analyzer/lib/src/generated/testing/element_factory.dart
@@ -162,10 +162,10 @@
     // Build the enum.
     //
     EnumElementImpl enumElement = EnumElementImpl(enumName, -1);
-    InterfaceTypeImpl enumType = enumElement.instantiate(
+    var enumType = enumElement.instantiate(
       typeArguments: const [],
       nullabilitySuffix: NullabilitySuffix.star,
-    );
+    ) as InterfaceTypeImpl;
     //
     // Populate the fields.
     //
@@ -462,11 +462,12 @@
     if (isConst) {
       ConstTopLevelVariableElementImpl constant =
           ConstTopLevelVariableElementImpl(name, -1);
+      var typeElement = type.element as ClassElement;
       InstanceCreationExpression initializer =
           AstTestFactory.instanceCreationExpression2(
-              Keyword.CONST, AstTestFactory.typeName(type.element));
+              Keyword.CONST, AstTestFactory.typeName(typeElement));
       if (type is InterfaceType) {
-        ConstructorElement element = type.element.unnamedConstructor;
+        ConstructorElement element = typeElement.unnamedConstructor;
         initializer.constructorName.staticElement = element;
       }
       constant.constantInitializer = initializer;
diff --git a/pkg/analyzer/lib/src/generated/type_promotion_manager.dart b/pkg/analyzer/lib/src/generated/type_promotion_manager.dart
index 1858a9b..3ac9ea4 100644
--- a/pkg/analyzer/lib/src/generated/type_promotion_manager.dart
+++ b/pkg/analyzer/lib/src/generated/type_promotion_manager.dart
@@ -132,7 +132,8 @@
   void _clearTypePromotionsIfAccessedInClosureAndPotentiallyMutated(
       AstNode target) {
     for (Element element in _promotedElements) {
-      if (_currentFunctionBody.isPotentiallyMutatedInScope(element)) {
+      if (_currentFunctionBody
+          .isPotentiallyMutatedInScope(element as VariableElement)) {
         if (_isVariableAccessedInClosure(element, target)) {
           _setType(element, null);
         }
diff --git a/pkg/analyzer/lib/src/generated/utilities_general.dart b/pkg/analyzer/lib/src/generated/utilities_general.dart
index cbbbaa9..025dc92 100644
--- a/pkg/analyzer/lib/src/generated/utilities_general.dart
+++ b/pkg/analyzer/lib/src/generated/utilities_general.dart
@@ -83,14 +83,14 @@
   }
 
   /// Combines together two hash codes.
-  static int hash2(Object a, Object b) => finish(combine(combine(0, a), b));
+  static int hash2(int a, int b) => finish(combine(combine(0, a), b));
 
   /// Combines together three hash codes.
-  static int hash3(Object a, Object b, Object c) =>
+  static int hash3(int a, int b, int c) =>
       finish(combine(combine(combine(0, a), b), c));
 
   /// Combines together four hash codes.
-  static int hash4(Object a, Object b, Object c, Object d) =>
+  static int hash4(int a, int b, int c, int d) =>
       finish(combine(combine(combine(combine(0, a), b), c), d));
 }
 
diff --git a/pkg/analyzer/lib/src/lint/config.dart b/pkg/analyzer/lib/src/lint/config.dart
index f18198a..4286bd5 100644
--- a/pkg/analyzer/lib/src/lint/config.dart
+++ b/pkg/analyzer/lib/src/lint/config.dart
@@ -20,7 +20,8 @@
 /// Process the given option [fileContents] and produce a corresponding
 /// [LintConfig].
 LintConfig processAnalysisOptionsFile(String fileContents, {String fileUrl}) {
-  var yaml = loadYamlNode(fileContents, sourceUrl: fileUrl);
+  var yaml = loadYamlNode(fileContents,
+      sourceUrl: fileUrl != null ? Uri.parse(fileUrl) : null);
   if (yaml is YamlMap) {
     return parseConfig(yaml);
   }
@@ -101,7 +102,8 @@
   }
 
   void _parse(String src, {String sourceUrl}) {
-    var yaml = loadYamlNode(src, sourceUrl: sourceUrl);
+    var yaml = loadYamlNode(src,
+        sourceUrl: sourceUrl != null ? Uri.parse(sourceUrl) : null);
     if (yaml is YamlMap) {
       _parseYaml(yaml);
     }
diff --git a/pkg/analyzer/test/generated/strong_mode_test.dart b/pkg/analyzer/test/generated/strong_mode_test.dart
index 90c2bb1..c291ea0 100644
--- a/pkg/analyzer/test/generated/strong_mode_test.dart
+++ b/pkg/analyzer/test/generated/strong_mode_test.dart
@@ -114,14 +114,15 @@
       if (body is ExpressionFunctionBody) {
         returnExp = body.expression;
       } else {
-        ReturnStatement stmt = (body as BlockFunctionBody).block.statements[0];
+        var stmt =
+            (body as BlockFunctionBody).block.statements[0] as ReturnStatement;
         returnExp = stmt.expression;
       }
       DartType type = returnExp.staticType;
       if (returnExp is AwaitExpression) {
         type = returnExp.expression.staticType;
       }
-      typeTest(type);
+      typeTest(type as InterfaceType);
     }
 
     check("f0", _isFutureOfDynamic);
@@ -168,14 +169,15 @@
       if (body is ExpressionFunctionBody) {
         returnExp = body.expression;
       } else {
-        ReturnStatement stmt = (body as BlockFunctionBody).block.statements[0];
+        var stmt =
+            (body as BlockFunctionBody).block.statements[0] as ReturnStatement;
         returnExp = stmt.expression;
       }
       DartType type = returnExp.staticType;
       if (returnExp is AwaitExpression) {
         type = returnExp.expression.staticType;
       }
-      typeTest(type);
+      typeTest(type as InterfaceType);
     }
 
     check("f0", _isFutureOfDynamic);
@@ -209,9 +211,9 @@
     List<Statement> statements =
         AstFinder.getStatementsInTopLevelFunction(unit, "main");
     CascadeExpression fetch(int i) {
-      VariableDeclarationStatement stmt = statements[i];
+      var stmt = statements[i] as VariableDeclarationStatement;
       VariableDeclaration decl = stmt.variables.variables[0];
-      CascadeExpression exp = decl.initializer;
+      var exp = decl.initializer as CascadeExpression;
       return exp;
     }
 
@@ -219,10 +221,10 @@
 
     CascadeExpression cascade = fetch(0);
     _isInstantiationOf(_hasElement(elementA))([_isInt])(cascade.staticType);
-    MethodInvocation invoke = cascade.cascadeSections[0];
-    FunctionExpression function = invoke.argumentList.arguments[1];
+    var invoke = cascade.cascadeSections[0] as MethodInvocation;
+    var function = invoke.argumentList.arguments[1] as FunctionExpression;
     ExecutableElement f0 = function.declaredElement;
-    _isListOf(_isInt)(f0.type.returnType);
+    _isListOf(_isInt)(f0.type.returnType as InterfaceType);
     expect(f0.type.normalParameterTypes[0], typeProvider.intType);
   }
 
@@ -240,7 +242,7 @@
 
     List<Statement> statements =
         AstFinder.getStatementsInTopLevelFunction(unit, "test");
-    VariableDeclarationStatement stmt = statements[0];
+    var stmt = statements[0] as VariableDeclarationStatement;
     VariableDeclaration decl = stmt.variables.variables[0];
     Expression call = decl.initializer;
     _isInt(call.staticType);
@@ -260,7 +262,7 @@
 
     List<Statement> statements =
         AstFinder.getStatementsInTopLevelFunction(unit, "test");
-    VariableDeclarationStatement stmt = statements[0];
+    var stmt = statements[0] as VariableDeclarationStatement;
     VariableDeclaration decl = stmt.variables.variables[0];
     Expression call = decl.initializer;
     _isInt(call.staticType);
@@ -277,7 +279,7 @@
 
     List<Statement> statements =
         AstFinder.getStatementsInTopLevelFunction(unit, "test");
-    VariableDeclarationStatement stmt = statements[0];
+    var stmt = statements[0] as VariableDeclarationStatement;
     VariableDeclaration decl = stmt.variables.variables[0];
     Expression call = decl.initializer;
     _isInt(call.staticType);
@@ -299,7 +301,7 @@
 
     List<Statement> statements =
         AstFinder.getStatementsInTopLevelFunction(unit, "test");
-    VariableDeclarationStatement stmt = statements[0];
+    var stmt = statements[0] as VariableDeclarationStatement;
     VariableDeclaration decl = stmt.variables.variables[0];
     Expression call = decl.initializer;
     _isInt(call.staticType);
@@ -323,7 +325,7 @@
 
     List<Statement> statements =
         AstFinder.getStatementsInTopLevelFunction(unit, "test");
-    VariableDeclarationStatement stmt = statements[0];
+    var stmt = statements[0] as VariableDeclarationStatement;
     VariableDeclaration decl = stmt.variables.variables[0];
     Expression call = decl.initializer;
     _isDynamic(call.staticType);
@@ -339,9 +341,9 @@
     await assertNoErrorsInCode(code);
     ConstructorDeclaration constructor =
         AstFinder.getConstructorInClass(unit, "A", null);
-    ConstructorFieldInitializer assignment = constructor.initializers[0];
+    var assignment = constructor.initializers[0] as ConstructorFieldInitializer;
     Expression exp = assignment.expression;
-    _isListOf(_isString)(exp.staticType);
+    _isListOf(_isString)(exp.staticType as InterfaceType);
   }
 
   test_factoryConstructor_propagation() async {
@@ -358,9 +360,9 @@
 
     ConstructorDeclaration constructor =
         AstFinder.getConstructorInClass(unit, "A", null);
-    BlockFunctionBody body = constructor.body;
-    ReturnStatement stmt = body.block.statements[0];
-    InstanceCreationExpression exp = stmt.expression;
+    var body = constructor.body as BlockFunctionBody;
+    var stmt = body.block.statements[0] as ReturnStatement;
+    var exp = stmt.expression as InstanceCreationExpression;
     ClassElement elementB = AstFinder.getClass(unit, "B").declaredElement;
     ClassElement elementA = AstFinder.getClass(unit, "A").declaredElement;
     expect(exp.constructorName.type.type.element, elementB);
@@ -380,7 +382,7 @@
 
     VariableDeclaration field = AstFinder.getFieldInClass(unit, "A", "f0");
 
-    _isListOf(_isString)(field.initializer.staticType);
+    _isListOf(_isString)(field.initializer.staticType as InterfaceType);
   }
 
   test_functionDeclaration_body_propagation() async {
@@ -403,22 +405,23 @@
     Asserter<InterfaceType> assertListOfInt = _isListOf(_isInt);
 
     FunctionDeclaration test1 = AstFinder.getTopLevelFunction(unit, "test1");
-    ExpressionFunctionBody body = test1.functionExpression.body;
-    assertListOfInt(body.expression.staticType);
+    var body = test1.functionExpression.body as ExpressionFunctionBody;
+    assertListOfInt(body.expression.staticType as InterfaceType);
 
     List<Statement> statements =
         AstFinder.getStatementsInTopLevelFunction(unit, "test2");
 
     FunctionDeclaration inner =
         (statements[0] as FunctionDeclarationStatement).functionDeclaration;
-    BlockFunctionBody body0 = inner.functionExpression.body;
-    ReturnStatement return0 = body0.block.statements[0];
+    var body0 = inner.functionExpression.body as BlockFunctionBody;
+    var return0 = body0.block.statements[0] as ReturnStatement;
     Expression anon0 = return0.expression;
-    FunctionType type0 = anon0.staticType;
+    var type0 = anon0.staticType as FunctionType;
     expect(type0.returnType, typeProvider.intType);
     expect(type0.normalParameterTypes[0], typeProvider.stringType);
 
-    FunctionExpression anon1 = (statements[1] as ReturnStatement).expression;
+    var anon1 =
+        (statements[1] as ReturnStatement).expression as FunctionExpression;
     FunctionType type1 = anon1.declaredElement.type;
     expect(type1.returnType, typeProvider.intType);
     expect(type1.normalParameterTypes[0], typeProvider.intType);
@@ -450,9 +453,9 @@
     List<Statement> statements =
         AstFinder.getStatementsInTopLevelFunction(unit, "main");
     DartType literal(int i) {
-      VariableDeclarationStatement stmt = statements[i];
+      var stmt = statements[i] as VariableDeclarationStatement;
       VariableDeclaration decl = stmt.variables.variables[0];
-      FunctionExpression exp = decl.initializer;
+      var exp = decl.initializer as FunctionExpression;
       return exp.declaredElement.type;
     }
 
@@ -488,9 +491,9 @@
     List<Statement> statements =
         AstFinder.getStatementsInTopLevelFunction(unit, "main");
     DartType literal(int i) {
-      VariableDeclarationStatement stmt = statements[i];
+      var stmt = statements[i] as VariableDeclarationStatement;
       VariableDeclaration decl = stmt.variables.variables[0];
-      FunctionExpression exp = decl.initializer;
+      var exp = decl.initializer as FunctionExpression;
       return exp.declaredElement.type;
     }
 
@@ -525,9 +528,9 @@
     List<Statement> statements =
         AstFinder.getStatementsInTopLevelFunction(unit, "main");
     Expression functionReturnValue(int i) {
-      VariableDeclarationStatement stmt = statements[i];
+      var stmt = statements[i] as VariableDeclarationStatement;
       VariableDeclaration decl = stmt.variables.variables[0];
-      FunctionExpression exp = decl.initializer;
+      var exp = decl.initializer as FunctionExpression;
       FunctionBody body = exp.body;
       if (body is ExpressionFunctionBody) {
         return body.expression;
@@ -538,10 +541,10 @@
     }
 
     Asserter<InterfaceType> assertListOfString = _isListOf(_isString);
-    assertListOfString(functionReturnValue(0).staticType);
-    assertListOfString(functionReturnValue(1).staticType);
-    assertListOfString(functionReturnValue(2).staticType);
-    assertListOfString(functionReturnValue(3).staticType);
+    assertListOfString(functionReturnValue(0).staticType as InterfaceType);
+    assertListOfString(functionReturnValue(1).staticType as InterfaceType);
+    assertListOfString(functionReturnValue(2).staticType as InterfaceType);
+    assertListOfString(functionReturnValue(3).staticType as InterfaceType);
   }
 
   test_functionLiteral_functionExpressionInvocation_typedArguments() async {
@@ -567,9 +570,9 @@
     List<Statement> statements =
         AstFinder.getStatementsInTopLevelFunction(unit, "main");
     DartType literal(int i) {
-      ExpressionStatement stmt = statements[i];
-      FunctionExpressionInvocation invk = stmt.expression;
-      FunctionExpression exp = invk.argumentList.arguments[0];
+      var stmt = statements[i] as ExpressionStatement;
+      var invk = stmt.expression as FunctionExpressionInvocation;
+      var exp = invk.argumentList.arguments[0] as FunctionExpression;
       return exp.declaredElement.type;
     }
 
@@ -602,9 +605,9 @@
     List<Statement> statements =
         AstFinder.getStatementsInTopLevelFunction(unit, "main");
     DartType literal(int i) {
-      ExpressionStatement stmt = statements[i];
-      FunctionExpressionInvocation invk = stmt.expression;
-      FunctionExpression exp = invk.argumentList.arguments[0];
+      var stmt = statements[i] as ExpressionStatement;
+      var invk = stmt.expression as FunctionExpressionInvocation;
+      var exp = invk.argumentList.arguments[0] as FunctionExpression;
       return exp.declaredElement.type;
     }
 
@@ -636,9 +639,9 @@
     List<Statement> statements =
         AstFinder.getStatementsInTopLevelFunction(unit, "main");
     DartType literal(int i) {
-      ExpressionStatement stmt = statements[i];
-      MethodInvocation invk = stmt.expression;
-      FunctionExpression exp = invk.argumentList.arguments[0];
+      var stmt = statements[i] as ExpressionStatement;
+      var invk = stmt.expression as MethodInvocation;
+      var exp = invk.argumentList.arguments[0] as FunctionExpression;
       return exp.declaredElement.type;
     }
 
@@ -669,9 +672,9 @@
     List<Statement> statements =
         AstFinder.getStatementsInTopLevelFunction(unit, "main");
     DartType literal(int i) {
-      ExpressionStatement stmt = statements[i];
-      MethodInvocation invk = stmt.expression;
-      FunctionExpression exp = invk.argumentList.arguments[0];
+      var stmt = statements[i] as ExpressionStatement;
+      var invk = stmt.expression as MethodInvocation;
+      var exp = invk.argumentList.arguments[0] as FunctionExpression;
       return exp.declaredElement.type;
     }
 
@@ -705,9 +708,9 @@
     List<Statement> statements =
         AstFinder.getStatementsInTopLevelFunction(unit, "main");
     DartType literal(int i) {
-      ExpressionStatement stmt = statements[i];
-      MethodInvocation invk = stmt.expression;
-      FunctionExpression exp = invk.argumentList.arguments[0];
+      var stmt = statements[i] as ExpressionStatement;
+      var invk = stmt.expression as MethodInvocation;
+      var exp = invk.argumentList.arguments[0] as FunctionExpression;
       return exp.declaredElement.type;
     }
 
@@ -740,9 +743,9 @@
     List<Statement> statements =
         AstFinder.getStatementsInTopLevelFunction(unit, "main");
     DartType literal(int i) {
-      ExpressionStatement stmt = statements[i];
-      MethodInvocation invk = stmt.expression;
-      FunctionExpression exp = invk.argumentList.arguments[0];
+      var stmt = statements[i] as ExpressionStatement;
+      var invk = stmt.expression as MethodInvocation;
+      var exp = invk.argumentList.arguments[0] as FunctionExpression;
       return exp.declaredElement.type;
     }
 
@@ -778,9 +781,9 @@
     List<Statement> statements =
         AstFinder.getStatementsInTopLevelFunction(unit, "main");
     Expression functionReturnValue(int i) {
-      VariableDeclarationStatement stmt = statements[i];
+      var stmt = statements[i] as VariableDeclarationStatement;
       VariableDeclaration decl = stmt.variables.variables[0];
-      FunctionExpression exp = decl.initializer;
+      var exp = decl.initializer as FunctionExpression;
       FunctionBody body = exp.body;
       if (body is ExpressionFunctionBody) {
         return body.expression;
@@ -803,7 +806,7 @@
     FutureOr<T> mk<T>(Future<T> x) => x;
     test() => mk(new Future<int>.value(42));
     ''');
-    _isFutureOrOfInt(invoke.staticType);
+    _isFutureOrOfInt(invoke.staticType as InterfaceType);
   }
 
   test_futureOr_assignFromValue() async {
@@ -812,7 +815,7 @@
     FutureOr<T> mk<T>(T x) => x;
     test() => mk(42);
     ''');
-    _isFutureOrOfInt(invoke.staticType);
+    _isFutureOrOfInt(invoke.staticType as InterfaceType);
   }
 
   test_futureOr_asyncExpressionBody() async {
@@ -821,7 +824,7 @@
     Future<T> mk<T>(FutureOr<T> x) async => x;
     test() => mk(42);
     ''');
-    _isFutureOfInt(invoke.staticType);
+    _isFutureOfInt(invoke.staticType as InterfaceType);
   }
 
   test_futureOr_asyncReturn() async {
@@ -830,7 +833,7 @@
     Future<T> mk<T>(FutureOr<T> x) async { return x; }
     test() => mk(42);
     ''');
-    _isFutureOfInt(invoke.staticType);
+    _isFutureOfInt(invoke.staticType as InterfaceType);
   }
 
   test_futureOr_await() async {
@@ -839,7 +842,7 @@
     Future<T> mk<T>(FutureOr<T> x) async => await x;
     test() => mk(42);
     ''');
-    _isFutureOfInt(invoke.staticType);
+    _isFutureOfInt(invoke.staticType as InterfaceType);
   }
 
   test_futureOr_downwards1() async {
@@ -849,7 +852,7 @@
     Future<T> mk<T>(FutureOr<T> x) => null;
     Future<int> test() => mk(new Future<int>.value(42));
     ''');
-    _isFutureOfInt(invoke.staticType);
+    _isFutureOfInt(invoke.staticType as InterfaceType);
   }
 
   test_futureOr_downwards2() async {
@@ -859,7 +862,7 @@
     Future<T> mk<T>(FutureOr<T> x) => null;
     FutureOr<int> test() => mk(new Future<int>.value(42));
     ''');
-    _isFutureOfInt(invoke.staticType);
+    _isFutureOfInt(invoke.staticType as InterfaceType);
   }
 
   test_futureOr_downwards3() async {
@@ -869,8 +872,9 @@
     Future<T> mk<T>(FutureOr<T> x) => null;
     Future<int> test() => mk(new Future.value(42));
     ''');
-    _isFutureOfInt(invoke.staticType);
-    _isFutureOfInt(invoke.argumentList.arguments[0].staticType);
+    _isFutureOfInt(invoke.staticType as InterfaceType);
+    _isFutureOfInt(
+        invoke.argumentList.arguments[0].staticType as InterfaceType);
   }
 
   test_futureOr_downwards4() async {
@@ -880,8 +884,9 @@
     Future<T> mk<T>(FutureOr<T> x) => null;
     FutureOr<int> test() => mk(new Future.value(42));
     ''');
-    _isFutureOfInt(invoke.staticType);
-    _isFutureOfInt(invoke.argumentList.arguments[0].staticType);
+    _isFutureOfInt(invoke.staticType as InterfaceType);
+    _isFutureOfInt(
+        invoke.argumentList.arguments[0].staticType as InterfaceType);
   }
 
   test_futureOr_downwards5() async {
@@ -891,8 +896,9 @@
     Future<T> mk<T>(FutureOr<T> x) => null;
     FutureOr<num> test() => mk(new Future.value(42));
     ''');
-    _isFutureOf([_isNum])(invoke.staticType);
-    _isFutureOf([_isNum])(invoke.argumentList.arguments[0].staticType);
+    _isFutureOf([_isNum])(invoke.staticType as InterfaceType);
+    _isFutureOf([_isNum])(
+        invoke.argumentList.arguments[0].staticType as InterfaceType);
   }
 
   test_futureOr_downwards6() async {
@@ -902,8 +908,9 @@
     T mk<T>(T x) => null;
     FutureOr<int> test() => mk(new Future.value(42));
     ''');
-    _isFutureOrOfInt(invoke.staticType);
-    _isFutureOfInt(invoke.argumentList.arguments[0].staticType);
+    _isFutureOrOfInt(invoke.staticType as InterfaceType);
+    _isFutureOfInt(
+        invoke.argumentList.arguments[0].staticType as InterfaceType);
   }
 
   test_futureOr_downwards7() async {
@@ -913,8 +920,9 @@
       T mk<T extends Future<int>>(T x) => null;
       FutureOr<int> test() => mk(new Future.value(42));
     ''');
-    _isFutureOfInt(invoke.staticType);
-    _isFutureOfInt(invoke.argumentList.arguments[0].staticType);
+    _isFutureOfInt(invoke.staticType as InterfaceType);
+    _isFutureOfInt(
+        invoke.argumentList.arguments[0].staticType as InterfaceType);
   }
 
   test_futureOr_downwards8() async {
@@ -926,8 +934,9 @@
     T mk<T extends Future<Object>>(T x) => null;
     FutureOr<int> test() => mk(new Future.value(42));
     ''');
-    _isFutureOfInt(invoke.staticType);
-    _isFutureOfInt(invoke.argumentList.arguments[0].staticType);
+    _isFutureOfInt(invoke.staticType as InterfaceType);
+    _isFutureOfInt(
+        invoke.argumentList.arguments[0].staticType as InterfaceType);
   }
 
   test_futureOr_downwards9() async {
@@ -937,7 +946,7 @@
     List<T> mk<T>(T x) => null;
     FutureOr<List<int>> test() => mk(3);
     ''');
-    _isListOf(_isInt)(invoke.staticType);
+    _isListOf(_isInt)(invoke.staticType as InterfaceType);
     _isInt(invoke.argumentList.arguments[0].staticType);
   }
 
@@ -987,7 +996,7 @@
     ''');
     _isFunction2Of(_isInt, _isNull)(
         invoke.argumentList.arguments[0].staticType);
-    _isFutureOfNull(invoke.staticType);
+    _isFutureOfNull(invoke.staticType as InterfaceType);
   }
 
   test_futureOr_no_return_value() async {
@@ -998,7 +1007,7 @@
     ''');
     _isFunction2Of(_isInt, _isNull)(
         invoke.argumentList.arguments[0].staticType);
-    _isFutureOfNull(invoke.staticType);
+    _isFutureOfNull(invoke.staticType as InterfaceType);
   }
 
   test_futureOr_return_null() async {
@@ -1009,7 +1018,7 @@
     ''');
     _isFunction2Of(_isInt, _isNull)(
         invoke.argumentList.arguments[0].staticType);
-    _isFutureOfNull(invoke.staticType);
+    _isFutureOfNull(invoke.staticType as InterfaceType);
   }
 
   test_futureOr_upwards1() async {
@@ -1019,7 +1028,7 @@
     Future<T> mk<T>(FutureOr<T> x) => null;
     dynamic test() => mk(new Future<int>.value(42));
     ''');
-    _isFutureOfInt(invoke.staticType);
+    _isFutureOfInt(invoke.staticType as InterfaceType);
   }
 
   test_futureOr_upwards2() async {
@@ -1031,7 +1040,7 @@
     ''', expectedErrors: [
       error(CompileTimeErrorCode.COULD_NOT_INFER, 111, 2),
     ]);
-    _isFutureOfInt(invoke.staticType);
+    _isFutureOfInt(invoke.staticType as InterfaceType);
   }
 
   test_futureOrNull_no_return() async {
@@ -1042,7 +1051,7 @@
     ''');
     _isFunction2Of(_isInt, _isNull)(
         invoke.argumentList.arguments[0].staticType);
-    _isFutureOfNull(invoke.staticType);
+    _isFutureOfNull(invoke.staticType as InterfaceType);
   }
 
   test_futureOrNull_no_return_value() async {
@@ -1053,7 +1062,7 @@
     ''');
     _isFunction2Of(_isInt, _isNull)(
         invoke.argumentList.arguments[0].staticType);
-    _isFutureOfNull(invoke.staticType);
+    _isFutureOfNull(invoke.staticType as InterfaceType);
   }
 
   test_futureOrNull_return_null() async {
@@ -1064,7 +1073,7 @@
     ''');
     _isFunction2Of(_isInt, _isNull)(
         invoke.argumentList.arguments[0].staticType);
-    _isFutureOfNull(invoke.staticType);
+    _isFutureOfNull(invoke.staticType as InterfaceType);
   }
 
   test_generic_partial() async {
@@ -1106,7 +1115,7 @@
     List<Statement> statements =
         AstFinder.getStatementsInTopLevelFunction(unit, "test");
     void check(int i) {
-      VariableDeclarationStatement stmt = statements[i];
+      var stmt = statements[i] as VariableDeclarationStatement;
       VariableDeclaration decl = stmt.variables.variables[0];
       Expression init = decl.initializer;
       _isInstantiationOf(_hasElement(elementA))([_isInt])(init.staticType);
@@ -1332,9 +1341,9 @@
     var body = AstFinder.getTopLevelFunction(unit, '_mergeSort')
         .functionExpression
         .body as BlockFunctionBody;
-    var stmts = body.block.statements;
+    var stmts = body.block.statements.cast<ExpressionStatement>();
     for (ExpressionStatement stmt in stmts) {
-      MethodInvocation invoke = stmt.expression;
+      var invoke = stmt.expression as MethodInvocation;
       assertInvokeType(invoke,
           'void Function(T Function(T), int Function(T, T), T Function(T))');
     }
@@ -1359,9 +1368,9 @@
     var body = AstFinder.getTopLevelFunction(unit, '_mergeSort')
         .functionExpression
         .body as BlockFunctionBody;
-    var stmts = body.block.statements;
+    var stmts = body.block.statements.cast<ExpressionStatement>();
     for (ExpressionStatement stmt in stmts) {
-      MethodInvocation invoke = stmt.expression;
+      var invoke = stmt.expression as MethodInvocation;
       assertInvokeType(
           invoke, 'void Function(List<T>, int Function(T, T), List<T>)');
     }
@@ -1386,9 +1395,9 @@
     var body = AstFinder.getTopLevelFunction(unit, '_mergeSort')
         .functionExpression
         .body as BlockFunctionBody;
-    var stmts = body.block.statements;
+    var stmts = body.block.statements.cast<ExpressionStatement>();
     for (ExpressionStatement stmt in stmts) {
-      MethodInvocation invoke = stmt.expression;
+      var invoke = stmt.expression as MethodInvocation;
       assertInvokeType(invoke, 'void Function(T, int Function(T, T), T)');
     }
   }
@@ -1459,26 +1468,26 @@
     await assertNoErrorsInCode(code);
 
     Asserter<InterfaceType> assertListOfInt = _isListOf(_isInt);
-    Asserter<InterfaceType> assertMapOfIntToListOfInt =
-        _isMapOf(_isInt, (DartType type) => assertListOfInt(type));
+    Asserter<InterfaceType> assertMapOfIntToListOfInt = _isMapOf(
+        _isInt, (DartType type) => assertListOfInt(type as InterfaceType));
 
     VariableDeclaration mapB = AstFinder.getFieldInClass(unit, "B", "map");
     MethodDeclaration mapC = AstFinder.getMethodInClass(unit, "C", "map");
-    assertMapOfIntToListOfInt(mapB.declaredElement.type);
-    assertMapOfIntToListOfInt(mapC.declaredElement.returnType);
+    assertMapOfIntToListOfInt(mapB.declaredElement.type as InterfaceType);
+    assertMapOfIntToListOfInt(mapC.declaredElement.returnType as InterfaceType);
 
-    SetOrMapLiteral mapLiteralB = mapB.initializer;
-    SetOrMapLiteral mapLiteralC =
-        (mapC.body as ExpressionFunctionBody).expression;
-    assertMapOfIntToListOfInt(mapLiteralB.staticType);
-    assertMapOfIntToListOfInt(mapLiteralC.staticType);
+    var mapLiteralB = mapB.initializer as SetOrMapLiteral;
+    var mapLiteralC =
+        (mapC.body as ExpressionFunctionBody).expression as SetOrMapLiteral;
+    assertMapOfIntToListOfInt(mapLiteralB.staticType as InterfaceType);
+    assertMapOfIntToListOfInt(mapLiteralC.staticType as InterfaceType);
 
-    ListLiteral listLiteralB =
-        (mapLiteralB.elements[0] as MapLiteralEntry).value;
-    ListLiteral listLiteralC =
-        (mapLiteralC.elements[0] as MapLiteralEntry).value;
-    assertListOfInt(listLiteralB.staticType);
-    assertListOfInt(listLiteralC.staticType);
+    var listLiteralB =
+        (mapLiteralB.elements[0] as MapLiteralEntry).value as ListLiteral;
+    var listLiteralC =
+        (mapLiteralC.elements[0] as MapLiteralEntry).value as ListLiteral;
+    assertListOfInt(listLiteralB.staticType as InterfaceType);
+    assertListOfInt(listLiteralC.staticType as InterfaceType);
   }
 
   test_instanceCreation() async {
@@ -1671,7 +1680,8 @@
 
     {
       List<Statement> statements =
-          AstFinder.getStatementsInTopLevelFunction(unit, "test0");
+          AstFinder.getStatementsInTopLevelFunction(unit, "test0")
+              .cast<VariableDeclarationStatement>();
 
       hasType(assertAOf([_isInt, _isString]), rhs(statements[0]));
       hasType(assertAOf([_isInt, _isString]), rhs(statements[0]));
@@ -1684,14 +1694,15 @@
 
     {
       List<Statement> statements =
-          AstFinder.getStatementsInTopLevelFunction(unit, "test1");
+          AstFinder.getStatementsInTopLevelFunction(unit, "test1")
+              .cast<VariableDeclarationStatement>();
       hasType(assertAOf([_isInt, _isString]), rhs(statements[0]));
       hasType(assertAOf([_isInt, _isString]), rhs(statements[1]));
     }
 
     {
       List<Statement> statements =
-          AstFinder.getStatementsInTopLevelFunction(unit, "test2");
+          AstFinder.getStatementsInTopLevelFunction(unit, "test2").cast<VariableDeclarationStatement>();
       hasType(assertBOf([_isString, _isInt]), rhs(statements[0]));
       hasType(assertBOf([_isString, _isInt]), rhs(statements[1]));
       hasType(assertBOf([_isString, _isInt]), rhs(statements[2]));
@@ -1702,14 +1713,14 @@
 
     {
       List<Statement> statements =
-          AstFinder.getStatementsInTopLevelFunction(unit, "test3");
+          AstFinder.getStatementsInTopLevelFunction(unit, "test3").cast<VariableDeclarationStatement>();
       hasType(assertBOf([_isString, _isInt]), rhs(statements[0]));
       hasType(assertBOf([_isString, _isInt]), rhs(statements[1]));
     }
 
     {
       List<Statement> statements =
-          AstFinder.getStatementsInTopLevelFunction(unit, "test4");
+          AstFinder.getStatementsInTopLevelFunction(unit, "test4").cast<VariableDeclarationStatement>();
       hasType(assertCOf([_isInt]), rhs(statements[0]));
       hasType(assertCOf([_isInt]), rhs(statements[1]));
       hasType(assertCOf([_isInt]), rhs(statements[2]));
@@ -1720,7 +1731,7 @@
 
     {
       List<Statement> statements =
-          AstFinder.getStatementsInTopLevelFunction(unit, "test5");
+          AstFinder.getStatementsInTopLevelFunction(unit, "test5").cast<VariableDeclarationStatement>();
       hasType(assertCOf([_isInt]), rhs(statements[0]));
       hasType(assertCOf([_isInt]), rhs(statements[1]));
     }
@@ -1730,7 +1741,7 @@
       // context.  We could choose a tighter type, but currently
       // we just use dynamic.
       List<Statement> statements =
-          AstFinder.getStatementsInTopLevelFunction(unit, "test6");
+          AstFinder.getStatementsInTopLevelFunction(unit, "test6").cast<VariableDeclarationStatement>();
       hasType(assertDOf([_isDynamic, _isString]), rhs(statements[0]));
       hasType(assertDOf([_isDynamic, _isString]), rhs(statements[1]));
       hasType(assertDOf([_isInt, _isString]), rhs(statements[2]));
@@ -1741,20 +1752,20 @@
 
     {
       List<Statement> statements =
-          AstFinder.getStatementsInTopLevelFunction(unit, "test7");
+          AstFinder.getStatementsInTopLevelFunction(unit, "test7").cast<VariableDeclarationStatement>();
       hasType(assertDOf([_isDynamic, _isString]), rhs(statements[0]));
       hasType(assertDOf([_isDynamic, _isString]), rhs(statements[1]));
     }
 
     {
       List<Statement> statements =
-          AstFinder.getStatementsInTopLevelFunction(unit, "test8");
+          AstFinder.getStatementsInTopLevelFunction(unit, "test8").cast<VariableDeclarationStatement>();
       hasType(assertEOf([_isInt, _isString]), rhs(statements[0]));
     }
 
     {
       List<Statement> statements =
-          AstFinder.getStatementsInTopLevelFunction(unit, "test9");
+          AstFinder.getStatementsInTopLevelFunction(unit, "test9").cast<VariableDeclarationStatement>();
       hasType(assertFOf([_isInt, _isString]), rhs(statements[0]));
       hasType(assertFOf([_isInt, _isString]), rhs(statements[1]));
       hasType(assertFOf([_isInt, _isString]), rhs(statements[2]));
@@ -1784,9 +1795,9 @@
     List<Statement> statements =
         AstFinder.getStatementsInTopLevelFunction(unit, "main");
     ListLiteral literal(int i) {
-      VariableDeclarationStatement stmt = statements[i];
+      var stmt = statements[i] as VariableDeclarationStatement;
       VariableDeclaration decl = stmt.variables.variables[0];
-      ListLiteral exp = decl.initializer;
+      var exp = decl.initializer as ListLiteral;
       return exp;
     }
 
@@ -1794,14 +1805,14 @@
     Asserter<InterfaceType> assertListOfListOfInt =
         _isListOf((DartType type) => assertListOfInt(type));
 
-    assertListOfListOfInt(literal(0).staticType);
-    assertListOfListOfInt(literal(1).staticType);
-    assertListOfListOfInt(literal(2).staticType);
-    assertListOfListOfInt(literal(3).staticType);
+    assertListOfListOfInt(literal(0).staticType as InterfaceType);
+    assertListOfListOfInt(literal(1).staticType as InterfaceType);
+    assertListOfListOfInt(literal(2).staticType as InterfaceType);
+    assertListOfListOfInt(literal(3).staticType as InterfaceType);
 
-    assertListOfInt((literal(1).elements[0] as Expression).staticType);
-    assertListOfInt((literal(2).elements[0] as Expression).staticType);
-    assertListOfInt((literal(3).elements[0] as Expression).staticType);
+    assertListOfInt((literal(1).elements[0] as Expression).staticType as InterfaceType);
+    assertListOfInt((literal(2).elements[0] as Expression).staticType as InterfaceType);
+    assertListOfInt((literal(3).elements[0] as Expression).staticType as InterfaceType);
   }
 
   test_listLiteral_simple() async {
@@ -1825,18 +1836,18 @@
     List<Statement> statements =
         AstFinder.getStatementsInTopLevelFunction(unit, "main");
     DartType literal(int i) {
-      VariableDeclarationStatement stmt = statements[i];
+      var stmt = statements[i] as VariableDeclarationStatement;
       VariableDeclaration decl = stmt.variables.variables[0];
-      ListLiteral exp = decl.initializer;
+      var exp = decl.initializer as ListLiteral;
       return exp.staticType;
     }
 
     Asserter<InterfaceType> assertListOfInt = _isListOf(_isInt);
 
-    assertListOfInt(literal(0));
-    assertListOfInt(literal(1));
-    assertListOfInt(literal(2));
-    assertListOfInt(literal(3));
+    assertListOfInt(literal(0) as InterfaceType);
+    assertListOfInt(literal(1) as InterfaceType);
+    assertListOfInt(literal(2) as InterfaceType);
+    assertListOfInt(literal(3) as InterfaceType);
   }
 
   test_listLiteral_simple_const() async {
@@ -1860,18 +1871,18 @@
     List<Statement> statements =
         AstFinder.getStatementsInTopLevelFunction(unit, "main");
     DartType literal(int i) {
-      VariableDeclarationStatement stmt = statements[i];
+      var stmt = statements[i] as VariableDeclarationStatement;
       VariableDeclaration decl = stmt.variables.variables[0];
-      ListLiteral exp = decl.initializer;
+      var exp = decl.initializer as ListLiteral;
       return exp.staticType;
     }
 
     Asserter<InterfaceType> assertListOfInt = _isListOf(_isInt);
 
-    assertListOfInt(literal(0));
-    assertListOfInt(literal(1));
-    assertListOfInt(literal(2));
-    assertListOfInt(literal(3));
+    assertListOfInt(literal(0) as InterfaceType);
+    assertListOfInt(literal(1) as InterfaceType);
+    assertListOfInt(literal(2) as InterfaceType);
+    assertListOfInt(literal(3) as InterfaceType);
   }
 
   test_listLiteral_simple_disabled() async {
@@ -1897,16 +1908,16 @@
     List<Statement> statements =
         AstFinder.getStatementsInTopLevelFunction(unit, "main");
     DartType literal(int i) {
-      VariableDeclarationStatement stmt = statements[i];
+      var stmt = statements[i] as VariableDeclarationStatement;
       VariableDeclaration decl = stmt.variables.variables[0];
-      ListLiteral exp = decl.initializer;
+      var exp = decl.initializer as ListLiteral;
       return exp.staticType;
     }
 
-    _isListOf(_isNum)(literal(0));
-    _isListOf(_isNum)(literal(1));
-    _isListOf(_isString)(literal(2));
-    _isListOf(_isDynamic)(literal(3));
+    _isListOf(_isNum)(literal(0) as InterfaceType);
+    _isListOf(_isNum)(literal(1) as InterfaceType);
+    _isListOf(_isString)(literal(2) as InterfaceType);
+    _isListOf(_isDynamic)(literal(3) as InterfaceType);
   }
 
   test_listLiteral_simple_subtype() async {
diff --git a/pkg/analyzer/test/src/fasta/message_coverage_test.dart b/pkg/analyzer/test/src/fasta/message_coverage_test.dart
index ad28b0e..8b309c8 100644
--- a/pkg/analyzer/test/src/fasta/message_coverage_test.dart
+++ b/pkg/analyzer/test/src/fasta/message_coverage_test.dart
@@ -4,6 +4,7 @@
 
 import 'dart:io' as io;
 
+import 'package:analysis_tool/package_root.dart' as package_root;
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/visitor.dart';
 import 'package:path/path.dart' as path;
@@ -12,7 +13,6 @@
 import 'package:yaml/yaml.dart';
 
 import '../../generated/parser_fasta_test.dart';
-import '../../utils/package_root.dart' as package_root;
 
 main() {
   defineReflectiveSuite(() {
diff --git a/pkg/analyzer/test/src/summary2/ast_text_printer_integration_test.dart b/pkg/analyzer/test/src/summary2/ast_text_printer_integration_test.dart
index 6d415cf..867debe 100644
--- a/pkg/analyzer/test/src/summary2/ast_text_printer_integration_test.dart
+++ b/pkg/analyzer/test/src/summary2/ast_text_printer_integration_test.dart
@@ -4,10 +4,10 @@
 
 import 'dart:io';
 
+import 'package:analysis_tool/package_root.dart' as package_root;
 import 'package:analyzer/file_system/physical_file_system.dart';
 import 'package:test/test.dart';
 
-import '../../utils/package_root.dart' as package_root;
 import '../dart/ast/parse_base.dart';
 import 'ast_text_printer_test.dart';
 
diff --git a/pkg/analyzer/test/utils/package_root.dart b/pkg/analyzer/test/utils/package_root.dart
deleted file mode 100644
index 8055970..0000000
--- a/pkg/analyzer/test/utils/package_root.dart
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) 2017, 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.
-
-import 'dart:io';
-
-import 'package:path/path.dart' as pathos;
-
-/// Returns a path to the directory containing source code for packages such as
-/// kernel, front_end, and analyzer.
-String get packageRoot {
-  // If the package root directory is specified on the command line using
-  // -DpkgRoot=..., use it.
-  String pkgRootVar = const bool.hasEnvironment('pkgRoot')
-      ? const String.fromEnvironment('pkgRoot')
-      : null;
-  if (pkgRootVar != null) {
-    String path = pathos.join(Directory.current.path, pkgRootVar);
-    if (!path.endsWith(pathos.separator)) path += pathos.separator;
-    return path;
-  }
-  // Otherwise try to guess based on the script path.
-  String scriptPath = pathos.fromUri(Platform.script);
-  List<String> parts = pathos.split(scriptPath);
-  int pkgIndex = parts.indexOf('pkg');
-  if (pkgIndex != -1) {
-    return pathos.joinAll(parts.sublist(0, pkgIndex + 1)) + pathos.separator;
-  }
-  throw StateError('Unable to find sdk/pkg/ in $scriptPath');
-}
diff --git a/pkg/analyzer/test/verify_docs_test.dart b/pkg/analyzer/test/verify_docs_test.dart
index 5107cd6..2254409 100644
--- a/pkg/analyzer/test/verify_docs_test.dart
+++ b/pkg/analyzer/test/verify_docs_test.dart
@@ -4,6 +4,7 @@
 
 import 'dart:convert';
 
+import 'package:analysis_tool/package_root.dart' as package_root;
 import 'package:analyzer/dart/analysis/analysis_context.dart';
 import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
 import 'package:analyzer/dart/analysis/results.dart';
@@ -14,8 +15,6 @@
 import 'package:analyzer/src/error/codes.dart';
 import 'package:test/test.dart';
 
-import 'utils/package_root.dart' as package_root;
-
 main() async {
   SnippetTester tester = SnippetTester();
   await tester.verify();
diff --git a/pkg/analyzer/test/verify_tests_test.dart b/pkg/analyzer/test/verify_tests_test.dart
index a25248d..d1e1e17 100644
--- a/pkg/analyzer/test/verify_tests_test.dart
+++ b/pkg/analyzer/test/verify_tests_test.dart
@@ -2,90 +2,28 @@
 // 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.
 
-import 'package:analyzer/dart/analysis/analysis_context.dart';
-import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
-import 'package:analyzer/dart/analysis/results.dart';
-import 'package:analyzer/dart/analysis/session.dart';
-import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analysis_tool/package_root.dart' as package_root;
+import 'package:analysis_tool/verify_tests.dart';
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/file_system/physical_file_system.dart';
-import 'package:path/path.dart' as path;
-import 'package:test/test.dart';
-
-import 'utils/package_root.dart' as package_root;
 
 main() {
-  PhysicalResourceProvider provider = PhysicalResourceProvider.INSTANCE;
-  String packageRoot = provider.pathContext.normalize(package_root.packageRoot);
-  String analyzerPath = provider.pathContext.join(packageRoot, 'analyzer');
-  String testDirPath = provider.pathContext.join(analyzerPath, 'test');
-
-  AnalysisContextCollection collection = AnalysisContextCollection(
-      includedPaths: <String>[testDirPath], resourceProvider: provider);
-  List<AnalysisContext> contexts = collection.contexts;
-  if (contexts.length != 1) {
-    fail('The test directory contains multiple analysis contexts.');
-  }
-
-  buildTestsIn(
-      contexts[0].currentSession, testDirPath, provider.getFolder(testDirPath));
+  var provider = PhysicalResourceProvider.INSTANCE;
+  var packageRoot = provider.pathContext.normalize(package_root.packageRoot);
+  var pathToAnalyze = provider.pathContext.join(packageRoot, 'analyzer');
+  var testDirPath = provider.pathContext.join(pathToAnalyze, 'test');
+  _VerifyTests(testDirPath).build();
 }
 
-void buildTestsIn(
-    AnalysisSession session, String testDirPath, Folder directory) {
-  List<String> testFileNames = [];
-  File testAllFile;
-  List<Resource> children = directory.getChildren();
-  children.sort((first, second) => first.shortName.compareTo(second.shortName));
-  for (Resource child in children) {
-    if (child is Folder) {
-      if (child.getChildAssumingFile('test_all.dart').exists) {
-        testFileNames.add('${child.shortName}/test_all.dart');
-      }
-      buildTestsIn(session, testDirPath, child);
-    } else if (child is File) {
-      String name = child.shortName;
-      if (name == 'test_all.dart') {
-        testAllFile = child;
-      } else if (name.endsWith('_integration_test.dart')) {
-        // ignored
-      } else if (name.endsWith('_test.dart')) {
-        testFileNames.add(name);
-      }
-    }
-  }
-  String relativePath = path.relative(directory.path, from: testDirPath);
-  test(relativePath, () {
-    if (testFileNames.isEmpty) {
-      return;
-    }
-    if (testAllFile == null) {
-      if (relativePath != 'id_tests') {
-        fail('Missing "test_all.dart" in $relativePath');
-      } else {
-        // The tests in the id_tests folder don't have a test_all.dart file
-        // because they don't use the package:test framework.
-        return;
-      }
-    }
-    ParsedUnitResult result = session.getParsedUnit(testAllFile.path);
-    if (result.state != ResultState.VALID) {
-      fail('Could not parse ${testAllFile.path}');
-    }
-    List<String> importedFiles = [];
-    for (var directive in result.unit.directives) {
-      if (directive is ImportDirective) {
-        importedFiles.add(directive.uri.stringValue);
-      }
-    }
-    List<String> missingFiles = [];
-    for (String testFileName in testFileNames) {
-      if (!importedFiles.contains(testFileName)) {
-        missingFiles.add(testFileName);
-      }
-    }
-    if (missingFiles.isNotEmpty) {
-      fail('Tests missing from "test_all.dart": ${missingFiles.join(', ')}');
-    }
-  });
+class _VerifyTests extends VerifyTests {
+  _VerifyTests(String testDirPath, {List<String> excludedPaths})
+      : super(testDirPath, excludedPaths: excludedPaths);
+
+  @override
+  bool isExpensive(Resource resource) =>
+      resource.shortName.endsWith('_integration_test.dart');
+
+  @override
+  bool isOkForTestAllToBeMissing(Folder folder) =>
+      folder.shortName == 'id_tests';
 }
diff --git a/pkg/analyzer/tool/diagnostics/generate.dart b/pkg/analyzer/tool/diagnostics/generate.dart
index 5ed14e0..18dcbd2 100644
--- a/pkg/analyzer/tool/diagnostics/generate.dart
+++ b/pkg/analyzer/tool/diagnostics/generate.dart
@@ -4,6 +4,7 @@
 
 import 'dart:io';
 
+import 'package:analysis_tool/package_root.dart' as package_root;
 import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/analysis/session.dart';
@@ -12,8 +13,6 @@
 import 'package:analyzer/file_system/physical_file_system.dart';
 import 'package:path/src/context.dart';
 
-import '../../test/utils/package_root.dart' as package_root;
-
 /// Generate the file `diagnostics.md` based on the documentation associated
 /// with the declarations of the error codes.
 void main() async {
diff --git a/pkg/analyzer/tool/experiments/generate.dart b/pkg/analyzer/tool/experiments/generate.dart
index 6ef4373..ac315ad 100644
--- a/pkg/analyzer/tool/experiments/generate.dart
+++ b/pkg/analyzer/tool/experiments/generate.dart
@@ -4,12 +4,11 @@
 
 import 'package:_fe_analyzer_shared/src/scanner/characters.dart'
     show $MINUS, $_;
+import 'package:analysis_tool/package_root.dart' as pkg_root;
 import 'package:analysis_tool/tools.dart';
 import 'package:path/path.dart';
 import 'package:yaml/yaml.dart' show YamlMap, loadYaml;
 
-import '../../test/utils/package_root.dart' as pkg_root;
-
 main() async {
   await GeneratedContent.generateAll(
       normalize(join(pkg_root.packageRoot, 'analyzer')), allTargets);
diff --git a/pkg/analyzer/tool/messages/generate.dart b/pkg/analyzer/tool/messages/generate.dart
index 31d96ef..23e066e 100644
--- a/pkg/analyzer/tool/messages/generate.dart
+++ b/pkg/analyzer/tool/messages/generate.dart
@@ -13,14 +13,13 @@
 import 'dart:io';
 
 import 'package:_fe_analyzer_shared/src/scanner/scanner.dart';
+import 'package:analysis_tool/package_root.dart' as pkg_root;
 import 'package:analysis_tool/tools.dart';
 import 'package:analyzer/error/error.dart';
 import 'package:analyzer/src/dart/error/syntactic_errors.dart';
 import 'package:path/path.dart';
 import 'package:yaml/yaml.dart' show loadYaml;
 
-import '../../test/utils/package_root.dart' as pkg_root;
-
 main() async {
   String analyzerPkgPath = normalize(join(pkg_root.packageRoot, 'analyzer'));
   String frontEndPkgPath = normalize(join(pkg_root.packageRoot, 'front_end'));
diff --git a/pkg/analyzer/tool/summary/check_test.dart b/pkg/analyzer/tool/summary/check_test.dart
index e93bb33..34edda7 100644
--- a/pkg/analyzer/tool/summary/check_test.dart
+++ b/pkg/analyzer/tool/summary/check_test.dart
@@ -2,10 +2,10 @@
 // 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.
 
+import 'package:analysis_tool/package_root.dart' as package_root;
 import 'package:analysis_tool/tools.dart';
 import 'package:path/path.dart';
 
-import '../../test/utils/package_root.dart' as package_root;
 import 'generate.dart';
 
 /// Check that the target file has been code generated.  If it hasn't tell the
diff --git a/pkg/analyzer_plugin/lib/src/protocol/protocol_internal.dart b/pkg/analyzer_plugin/lib/src/protocol/protocol_internal.dart
index a1d5445..5615b1b 100644
--- a/pkg/analyzer_plugin/lib/src/protocol/protocol_internal.dart
+++ b/pkg/analyzer_plugin/lib/src/protocol/protocol_internal.dart
@@ -8,6 +8,7 @@
 import 'package:analyzer_plugin/protocol/protocol.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:analyzer_plugin/protocol/protocol_generated.dart';
+import 'package:analyzer_plugin/utilities/change_builder/conflicting_edit_exception.dart';
 
 final Map<String, RefactoringKind> REQUEST_ID_REFACTORING_KINDS =
     HashMap<String, RefactoringKind>();
@@ -18,13 +19,46 @@
   edits.forEach(sourceFileEdit.add);
 }
 
-/// Adds the given [sourceEdit] to the list in [sourceFileEdit].
+/// Adds the given [sourceEdit] to the list in [sourceFileEdit] while preserving
+/// two invariants:
+/// - the list is sorted such that edits with a larger offset appear earlier in
+///   the list, and
+/// - no two edits in the list overlap each other.
+///
+/// If the invariants can't be preserved, then a [ConflictingEditException] is
+/// thrown.
 void addEditForSource(SourceFileEdit sourceFileEdit, SourceEdit sourceEdit) {
   var edits = sourceFileEdit.edits;
+  var length = edits.length;
   var index = 0;
-  while (index < edits.length && edits[index].offset > sourceEdit.offset) {
+  while (index < length && edits[index].offset > sourceEdit.offset) {
     index++;
   }
+  if (index > 0) {
+    var previousEdit = edits[index - 1];
+    // The [previousEdit] has an offset that is strictly greater than the offset
+    // of the [sourceEdit] so we only need to look at the end of the
+    // [sourceEdit] to know whether they overlap.
+    if (sourceEdit.offset + sourceEdit.length > previousEdit.offset) {
+      throw ConflictingEditException(
+          newEdit: sourceEdit, existingEdit: previousEdit);
+    }
+  }
+  if (index < length) {
+    var nextEdit = edits[index];
+    // The [nextEdit] has an offset that is less than or equal to the offset of
+    // the [sourceEdit]. If they're equal, then we consider it to be a conflict.
+    // Otherwise the offset of [nextEdit] is strictly less than the offset of
+    // the [sourceEdit] so we need to look at the end of the [nextEdit] to know
+    // whether they overlap.
+    if ((sourceEdit.offset == nextEdit.offset &&
+            sourceEdit.length > 0 &&
+            nextEdit.length > 0) ||
+        nextEdit.offset + nextEdit.length > sourceEdit.offset) {
+      throw ConflictingEditException(
+          newEdit: sourceEdit, existingEdit: nextEdit);
+    }
+  }
   edits.insert(index, sourceEdit);
 }
 
diff --git a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
index ed00442..58d3a20 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
@@ -128,6 +128,43 @@
     }
   }
 
+  @override
+  ChangeBuilder copy() {
+    var copy = ChangeBuilderImpl(workspace: workspace, eol: eol);
+    for (var entry in _linkedEditGroups.entries) {
+      copy._linkedEditGroups[entry.key] = _copyLinkedEditGroup(entry.value);
+    }
+    copy._selection = _copyPosition(_selection);
+    copy._selectionRange = _selectionRange;
+    copy._lockedPositions.addAll(_lockedPositions);
+    for (var entry in _genericFileEditBuilders.entries) {
+      copy._genericFileEditBuilders[entry.key] = entry.value.copyWith(copy);
+    }
+    //
+    // The file edit builders for libraries (those whose [libraryChangeBuilder]
+    // is `null`) are copied first so that the copies exist when we copy the
+    // builders for parts and the structure can be preserved.
+    //
+    var editBuilderMap = <DartFileEditBuilderImpl, DartFileEditBuilderImpl>{};
+    for (var entry in _dartFileEditBuilders.entries) {
+      var oldBuilder = entry.value;
+      if (oldBuilder.libraryChangeBuilder == null) {
+        var newBuilder = oldBuilder.copyWith(copy);
+        copy._dartFileEditBuilders[entry.key] = newBuilder;
+        editBuilderMap[oldBuilder] = newBuilder;
+      }
+    }
+    for (var entry in _dartFileEditBuilders.entries) {
+      var oldBuilder = entry.value;
+      if (oldBuilder.libraryChangeBuilder != null) {
+        var newBuilder =
+            oldBuilder.copyWith(copy, editBuilderMap: editBuilderMap);
+        copy._dartFileEditBuilders[entry.key] = newBuilder;
+      }
+    }
+    return copy;
+  }
+
   /// Create and return a [DartFileEditBuilder] that can be used to build edits
   /// to the Dart file with the given [path].
   Future<DartFileEditBuilderImpl> createDartFileEditBuilder(String path) async {
@@ -188,6 +225,17 @@
     _selection = position;
   }
 
+  /// Return a copy of the linked edit [group].
+  LinkedEditGroup _copyLinkedEditGroup(LinkedEditGroup group) {
+    return LinkedEditGroup(group.positions.map(_copyPosition).toList(),
+        group.length, group.suggestions.toList());
+  }
+
+  /// Return a copy of the [position].
+  Position _copyPosition(Position position) {
+    return position == null ? null : Position(position.file, position.offset);
+  }
+
   void _setSelectionRange(SourceRange range) {
     _selectionRange = range;
   }
@@ -393,6 +441,13 @@
     }
   }
 
+  FileEditBuilderImpl copyWith(ChangeBuilderImpl changeBuilder) {
+    var copy =
+        FileEditBuilderImpl(changeBuilder, fileEdit.file, fileEdit.fileStamp);
+    copy.fileEdit.edits.addAll(fileEdit.edits);
+    return copy;
+  }
+
   EditBuilderImpl createEditBuilder(int offset, int length) {
     return EditBuilderImpl(this, offset, length);
   }
diff --git a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
index 4c68006..993cf7e 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
@@ -1312,6 +1312,23 @@
   }
 
   @override
+  DartFileEditBuilderImpl copyWith(ChangeBuilderImpl changeBuilder,
+      {Map<DartFileEditBuilderImpl, DartFileEditBuilderImpl> editBuilderMap =
+          const {}}) {
+    var copy = DartFileEditBuilderImpl(changeBuilder, resolvedUnit,
+        fileEdit.fileStamp, editBuilderMap[libraryChangeBuilder]);
+    copy.fileEdit.edits.addAll(fileEdit.edits);
+    copy.importPrefixGenerator = importPrefixGenerator;
+    for (var entry in librariesToImport.entries) {
+      copy.librariesToImport[entry.key] = entry.value;
+    }
+    for (var entry in librariesToRelativelyImport.entries) {
+      copy.librariesToRelativelyImport[entry.key] = entry.value;
+    }
+    return copy;
+  }
+
+  @override
   DartEditBuilderImpl createEditBuilder(int offset, int length) {
     return DartEditBuilderImpl(this, offset, length);
   }
diff --git a/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_core.dart b/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_core.dart
index 0d310b5..d4e9f29 100644
--- a/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_core.dart
+++ b/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_core.dart
@@ -56,6 +56,10 @@
   Future<void> addGenericFileEdit(
       String path, void Function(FileEditBuilder builder) buildFileEdit);
 
+  /// Return a copy of this change builder that is constructed in such as was
+  /// that changes to the copy will not effect this change builder.
+  ChangeBuilder copy();
+
   /// Set the selection for the change being built to the given [position].
   void setSelection(Position position);
 }
diff --git a/pkg/analyzer_plugin/lib/utilities/change_builder/conflicting_edit_exception.dart b/pkg/analyzer_plugin/lib/utilities/change_builder/conflicting_edit_exception.dart
new file mode 100644
index 0000000..db5a55f
--- /dev/null
+++ b/pkg/analyzer_plugin/lib/utilities/change_builder/conflicting_edit_exception.dart
@@ -0,0 +1,24 @@
+// 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.
+
+import 'package:analyzer_plugin/protocol/protocol_common.dart';
+import 'package:meta/meta.dart';
+
+/// An exception that is thrown when a change builder is asked to include an
+/// edit that conflicts with a previous edit.
+class ConflictingEditException implements Exception {
+  /// The new edit that was being added.
+  final SourceEdit newEdit;
+
+  /// The existing edit with which it conflicts.
+  final SourceEdit existingEdit;
+
+  /// Initialize a newly created exception indicating that the [newEdit].
+  ConflictingEditException(
+      {@required this.newEdit, @required this.existingEdit});
+
+  @override
+  String toString() =>
+      'ConflictingEditException: $newEdit conflicts with $existingEdit';
+}
diff --git a/pkg/analyzer_plugin/pubspec.yaml b/pkg/analyzer_plugin/pubspec.yaml
index f763f70..50a1e05 100644
--- a/pkg/analyzer_plugin/pubspec.yaml
+++ b/pkg/analyzer_plugin/pubspec.yaml
@@ -10,13 +10,13 @@
 dependencies:
   analyzer: '>=0.39.12 <0.41.0'
   dart_style: '^1.2.0'
+  meta: ^1.2.3
   pub_semver: '^1.3.2'
 
 dev_dependencies:
   analysis_tool:
     path: ../analysis_tool
   html: '>=0.13.1 <0.15.0'
-  meta: '^1.1.8'
   path: '^1.4.1'
   test_reflective_loader: ^0.1.8
   test: ^1.0.0
diff --git a/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_core_test.dart b/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_core_test.dart
index 711cf55..0e27f7f 100644
--- a/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_core_test.dart
+++ b/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_core_test.dart
@@ -6,6 +6,7 @@
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:analyzer_plugin/src/utilities/change_builder/change_builder_core.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+import 'package:analyzer_plugin/utilities/change_builder/conflicting_edit_exception.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -20,6 +21,75 @@
 
 @reflectiveTest
 class ChangeBuilderImplTest {
+  void test_copy_empty() {
+    var builder = ChangeBuilderImpl();
+    var copy = builder.copy() as ChangeBuilderImpl;
+    expect(identical(copy, builder), isFalse);
+    expect(copy.workspace, builder.workspace);
+    expect(copy.eol, builder.eol);
+  }
+
+  Future<void> test_copy_newEdit() async {
+    var builder = ChangeBuilderImpl();
+    await builder.addGenericFileEdit('/test.dart', (builder) {
+      builder.addSimpleInsertion(0, 'x');
+    });
+    var copy = builder.copy() as ChangeBuilderImpl;
+    await copy.addGenericFileEdit('/test.dart', (builder) {
+      builder.addSimpleInsertion(10, 'x');
+    });
+    var change = builder.sourceChange;
+    expect(change.edits[0].edits, hasLength(1));
+  }
+
+  Future<void> test_copy_newFile() async {
+    var builder = ChangeBuilderImpl();
+    await builder.addGenericFileEdit('/test1.dart', (builder) {
+      builder.addSimpleInsertion(0, 'x');
+    });
+    var copy = builder.copy() as ChangeBuilderImpl;
+    await copy.addGenericFileEdit('/test2.dart', (builder) {
+      builder.addSimpleInsertion(0, 'x');
+    });
+    var change = builder.sourceChange;
+    expect(change.edits, hasLength(1));
+  }
+
+  Future<void> test_copy_newLinkedEditGroup() async {
+    var builder = ChangeBuilderImpl();
+    await builder.addGenericFileEdit('/test.dart', (builder) {
+      builder.addLinkedPosition(SourceRange(1, 2), 'a');
+    });
+    var copy = builder.copy() as ChangeBuilderImpl;
+    await copy.addGenericFileEdit('/test.dart', (builder) {
+      builder.addLinkedPosition(SourceRange(3, 4), 'b');
+    });
+    var change = builder.sourceChange;
+    expect(change.linkedEditGroups, hasLength(1));
+  }
+
+  Future<void> test_copy_newLinkedPosition() async {
+    var builder = ChangeBuilderImpl();
+    await builder.addGenericFileEdit('/test.dart', (builder) {
+      builder.addLinkedPosition(SourceRange(1, 2), 'a');
+    });
+    var copy = builder.copy() as ChangeBuilderImpl;
+    await copy.addGenericFileEdit('/test.dart', (builder) {
+      builder.addLinkedPosition(SourceRange(3, 4), 'a');
+    });
+    var change = builder.sourceChange;
+    expect(change.linkedEditGroups[0].positions, hasLength(1));
+  }
+
+  Future<void> test_copy_selection() async {
+    var builder = ChangeBuilderImpl();
+    builder.setSelection(Position('/test.dart', 5));
+    var copy = builder.copy() as ChangeBuilderImpl;
+    copy.setSelection(Position('/test.dart', 10));
+    var change = builder.sourceChange;
+    expect(change.selection.offset, 5);
+  }
+
   Future<void> test_createFileEditBuilder() async {
     var builder = ChangeBuilderImpl();
     var path = '/test.dart';
@@ -259,6 +329,71 @@
     expect(edits[0].replacement, isEmpty);
   }
 
+  Future<void> test_addDeletion_adjacent_lowerOffsetFirst() async {
+    // TODO(brianwilkerson) This should also merge the deletions, but is written
+    //  to ensure that existing uses of FileEditBuilder continue to work even
+    //  without that change.
+    var firstOffset = 23;
+    var firstLength = 7;
+    var secondOffset = 30;
+    var secondLength = 5;
+    var builder = ChangeBuilderImpl();
+    await builder.addGenericFileEdit(path, (builder) {
+      builder.addDeletion(SourceRange(firstOffset, firstLength));
+      builder.addDeletion(SourceRange(secondOffset, secondLength));
+    });
+    var edits = builder.sourceChange.edits[0].edits;
+    expect(edits, hasLength(2));
+    expect(edits[0].offset, secondOffset);
+    expect(edits[0].length, secondLength);
+    expect(edits[0].replacement, isEmpty);
+    expect(edits[1].offset, firstOffset);
+    expect(edits[1].length, firstLength);
+    expect(edits[1].replacement, isEmpty);
+  }
+
+  Future<void> test_addDeletion_adjacent_lowerOffsetSecond() async {
+    // TODO(brianwilkerson) This should also merge the deletions, but is written
+    //  to ensure that existing uses of FileEditBuilder continue to work even
+    //  without that change.
+    var firstOffset = 23;
+    var firstLength = 7;
+    var secondOffset = 30;
+    var secondLength = 5;
+    var builder = ChangeBuilderImpl();
+    await builder.addGenericFileEdit(path, (builder) {
+      builder.addDeletion(SourceRange(secondOffset, secondLength));
+      builder.addDeletion(SourceRange(firstOffset, firstLength));
+    });
+    var edits = builder.sourceChange.edits[0].edits;
+    expect(edits, hasLength(2));
+    expect(edits[0].offset, secondOffset);
+    expect(edits[0].length, secondLength);
+    expect(edits[0].replacement, isEmpty);
+    expect(edits[1].offset, firstOffset);
+    expect(edits[1].length, firstLength);
+    expect(edits[1].replacement, isEmpty);
+  }
+
+  @failingTest
+  Future<void> test_addDeletion_overlapping() async {
+    // This support is not yet implemented.
+    var firstOffset = 23;
+    var firstLength = 7;
+    var secondOffset = 27;
+    var secondLength = 8;
+    var builder = ChangeBuilderImpl();
+    await builder.addGenericFileEdit(path, (builder) {
+      builder.addDeletion(SourceRange(firstOffset, firstLength));
+      builder.addDeletion(SourceRange(secondOffset, secondLength));
+    });
+    var edits = builder.sourceChange.edits[0].edits;
+    expect(edits, hasLength(1));
+    expect(edits[0].offset, firstOffset);
+    expect(edits[0].length, secondOffset + secondLength - firstOffset);
+    expect(edits[0].replacement, isEmpty);
+  }
+
   Future<void> test_addInsertion() async {
     var builder = ChangeBuilderImpl();
     await builder.addGenericFileEdit(path, (builder) {
@@ -307,6 +442,24 @@
     expect(edits[0].replacement, text);
   }
 
+  Future<void> test_addSimpleInsertion_sameOffset() async {
+    var offset = 23;
+    var text = 'xyz';
+    var builder = ChangeBuilderImpl();
+    await builder.addGenericFileEdit(path, (builder) {
+      builder.addSimpleInsertion(offset, text);
+      builder.addSimpleInsertion(offset, 'abc');
+    });
+    var edits = builder.sourceChange.edits[0].edits;
+    expect(edits, hasLength(2));
+    expect(edits[0].offset, offset);
+    expect(edits[0].length, 0);
+    expect(edits[0].replacement, 'abc');
+    expect(edits[1].offset, offset);
+    expect(edits[1].length, 0);
+    expect(edits[1].replacement, text);
+  }
+
   Future<void> test_addSimpleReplacement() async {
     var offset = 23;
     var length = 7;
@@ -322,6 +475,64 @@
     expect(edits[0].replacement, text);
   }
 
+  Future<void> test_addSimpleReplacement_adjacent() async {
+    var firstOffset = 23;
+    var firstLength = 7;
+    var secondOffset = firstOffset + firstLength;
+    var secondLength = 5;
+    var text = 'xyz';
+    var builder = ChangeBuilderImpl();
+    await builder.addGenericFileEdit(path, (builder) {
+      builder.addSimpleReplacement(SourceRange(firstOffset, firstLength), text);
+      builder.addSimpleReplacement(
+          SourceRange(secondOffset, secondLength), text);
+    });
+    var edits = builder.sourceChange.edits[0].edits;
+    expect(edits, hasLength(2));
+    expect(edits[0].offset, secondOffset);
+    expect(edits[0].length, secondLength);
+    expect(edits[0].replacement, text);
+    expect(edits[1].offset, firstOffset);
+    expect(edits[1].length, firstLength);
+    expect(edits[1].replacement, text);
+  }
+
+  Future<void> test_addSimpleReplacement_overlapsHead() async {
+    var offset = 23;
+    var length = 7;
+    var text = 'xyz';
+    var builder = ChangeBuilderImpl();
+    await builder.addGenericFileEdit(path, (builder) {
+      builder.addSimpleReplacement(SourceRange(offset, length), text);
+      expect(() {
+        builder.addSimpleReplacement(SourceRange(offset - 2, length), text);
+      }, throwsA(isA<ConflictingEditException>()));
+    });
+    var edits = builder.sourceChange.edits[0].edits;
+    expect(edits, hasLength(1));
+    expect(edits[0].offset, offset);
+    expect(edits[0].length, length);
+    expect(edits[0].replacement, text);
+  }
+
+  Future<void> test_addSimpleReplacement_overlapsTail() async {
+    var offset = 23;
+    var length = 7;
+    var text = 'xyz';
+    var builder = ChangeBuilderImpl();
+    await builder.addGenericFileEdit(path, (builder) {
+      builder.addSimpleReplacement(SourceRange(offset, length), text);
+      expect(() {
+        builder.addSimpleReplacement(SourceRange(offset + 2, length), text);
+      }, throwsA(isA<ConflictingEditException>()));
+    });
+    var edits = builder.sourceChange.edits[0].edits;
+    expect(edits, hasLength(1));
+    expect(edits[0].offset, offset);
+    expect(edits[0].length, length);
+    expect(edits[0].replacement, text);
+  }
+
   Future<void> test_createEditBuilder() async {
     var builder = ChangeBuilderImpl();
     await builder.addGenericFileEdit(path, (builder) {
diff --git a/pkg/analyzer_plugin/test/utils/package_root.dart b/pkg/analyzer_plugin/test/utils/package_root.dart
deleted file mode 100644
index 8ef4cfc..0000000
--- a/pkg/analyzer_plugin/test/utils/package_root.dart
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) 2017, 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.
-
-import 'dart:io';
-
-import 'package:path/path.dart' as pathos;
-
-/// Returns a path to the directory containing source code for packages such as
-/// kernel, front_end, and analyzer.
-String get packageRoot {
-  // If the package root directory is specified on the command line using
-  // -DpkgRoot=..., use it.
-  var pkgRootVar = bool.hasEnvironment('pkgRoot')
-      ? const String.fromEnvironment('pkgRoot')
-      : null;
-  if (pkgRootVar != null) {
-    var path = pathos.join(Directory.current.path, pkgRootVar);
-    if (!path.endsWith(pathos.separator)) path += pathos.separator;
-    return path;
-  }
-  // Otherwise try to guess based on the script path.
-  var scriptPath = pathos.fromUri(Platform.script);
-  var parts = pathos.split(scriptPath);
-  var pkgIndex = parts.indexOf('pkg');
-  if (pkgIndex != -1) {
-    return pathos.joinAll(parts.sublist(0, pkgIndex + 1)) + pathos.separator;
-  }
-  throw StateError('Unable to find sdk/pkg/ in $scriptPath');
-}
diff --git a/pkg/analyzer_plugin/test/verify_tests_test.dart b/pkg/analyzer_plugin/test/verify_tests_test.dart
index 2a05ff6..3ea9290 100644
--- a/pkg/analyzer_plugin/test/verify_tests_test.dart
+++ b/pkg/analyzer_plugin/test/verify_tests_test.dart
@@ -2,84 +2,34 @@
 // 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.
 
-import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
-import 'package:analyzer/dart/analysis/results.dart';
-import 'package:analyzer/dart/analysis/session.dart';
-import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analysis_tool/package_root.dart' as package_root;
+import 'package:analysis_tool/verify_tests.dart';
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/file_system/physical_file_system.dart';
-import 'package:path/path.dart' as path;
-import 'package:test/test.dart';
-
-import 'utils/package_root.dart' as package_root;
 
 void main() {
   var provider = PhysicalResourceProvider.INSTANCE;
   var packageRoot = provider.pathContext.normalize(package_root.packageRoot);
-  var analysisServerPath =
-      provider.pathContext.join(packageRoot, 'analyzer_plugin');
-  var testDirPath = provider.pathContext.join(analysisServerPath, 'test');
-
-  var collection = AnalysisContextCollection(
-      includedPaths: <String>[testDirPath], resourceProvider: provider);
-  var contexts = collection.contexts;
-  if (contexts.length != 1) {
-    fail('The test directory contains multiple analysis contexts.');
-  }
-
-  buildTestsIn(
-      contexts[0].currentSession, testDirPath, provider.getFolder(testDirPath));
+  var pathToAnalyze = provider.pathContext.join(packageRoot, 'analyzer_plugin');
+  var testDirPath = provider.pathContext.join(pathToAnalyze, 'test');
+  _VerifyTests(testDirPath).build();
 }
 
-void buildTestsIn(
-    AnalysisSession session, String testDirPath, Folder directory) {
-  var testFileNames = <String>[];
-  File testAllFile;
-  var children = directory.getChildren();
-  children.sort((first, second) => first.shortName.compareTo(second.shortName));
-  for (var child in children) {
-    if (child is Folder) {
-      if (child.shortName == 'integration') {
-        continue;
-      } else if (child.getChildAssumingFile('test_all.dart').exists) {
-        testFileNames.add('${child.shortName}/test_all.dart');
-      }
-      buildTestsIn(session, testDirPath, child);
-    } else if (child is File) {
-      var name = child.shortName;
-      if (name == 'test_all.dart') {
-        testAllFile = child;
-      } else if (name.endsWith('_test.dart')) {
-        testFileNames.add(name);
-      }
+class _VerifyTests extends VerifyTests {
+  _VerifyTests(String testDirPath, {List<String> excludedPaths})
+      : super(testDirPath, excludedPaths: excludedPaths);
+
+  @override
+  bool isExpensive(Resource resource) => resource.shortName == 'integration';
+
+  @override
+  bool isOkAsAdditionalTestAllImport(Folder folder, String uri) {
+    if (folder.path == testDirPath &&
+        uri == '../tool/spec/check_all_test.dart') {
+      // The topmost `test_all.dart` also runs this one test in `tool` for
+      // convenience.
+      return true;
     }
+    return super.isOkAsAdditionalTestAllImport(folder, uri);
   }
-  var relativePath = path.relative(directory.path, from: testDirPath);
-  test(relativePath, () {
-    if (testFileNames.isEmpty) {
-      return;
-    }
-    if (testAllFile == null) {
-      fail('Missing "test_all.dart" in $relativePath');
-    }
-    var result = session.getParsedUnit(testAllFile.path);
-    if (result.state != ResultState.VALID) {
-      fail('Could not parse ${testAllFile.path}');
-    }
-    var importedFiles = <String>[];
-    for (var directive in result.unit.directives) {
-      if (directive is ImportDirective) {
-        importedFiles.add(directive.uri.stringValue);
-      }
-    }
-    var missingFiles = <String>[];
-    for (var testFileName in testFileNames) {
-      if (!importedFiles.contains(testFileName)) {
-        missingFiles.add(testFileName);
-      }
-    }
-    if (missingFiles.isNotEmpty) {
-      fail('Tests missing from "test_all.dart": ${missingFiles.join(', ')}');
-    }
-  });
 }
diff --git a/pkg/compiler/lib/src/common_elements.dart b/pkg/compiler/lib/src/common_elements.dart
index 7b7387c..8d84fe7 100644
--- a/pkg/compiler/lib/src/common_elements.dart
+++ b/pkg/compiler/lib/src/common_elements.dart
@@ -595,6 +595,13 @@
   /// compilation.
   bool isNamedListConstructor(String name, ConstructorEntity element);
 
+  /// Returns `true` if [element] is the named constructor of `JSArray`,
+  /// e.g. `JSArray.fixed`.
+  ///
+  /// This will not resolve the constructor if it hasn't been seen yet during
+  /// compilation.
+  bool isNamedJSArrayConstructor(String name, ConstructorEntity element);
+
   bool isDefaultEqualityImplementation(MemberEntity element);
 
   /// Returns `true` if [selector] applies to `JSIndexable.length`.
@@ -885,6 +892,14 @@
   bool isNamedListConstructor(String name, ConstructorEntity element) =>
       element.name == name && element.enclosingClass == listClass;
 
+  /// Returns `true` if [element] is the [name]d constructor of `JSArray`.
+  ///
+  /// This will not resolve the constructor if it hasn't been seen yet during
+  /// compilation.
+  @override
+  bool isNamedJSArrayConstructor(String name, ConstructorEntity element) =>
+      element.name == name && element.enclosingClass == jsArrayClass;
+
   @override
   DynamicType get dynamicType => _env.dynamicType;
 
diff --git a/pkg/compiler/lib/src/inferrer/builder_kernel.dart b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
index afc24a3..55d7ab6 100644
--- a/pkg/compiler/lib/src/inferrer/builder_kernel.dart
+++ b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
@@ -1296,10 +1296,11 @@
       // We have something like `List.empty(growable: true)`.
       TypeInformation baseType =
           _listBaseType(arguments, defaultGrowable: false);
+      TypeInformation elementType = _types.nonNullEmpty(); // No elements!
       return _inferrer.concreteTypes.putIfAbsent(
           node,
           () => _types.allocateList(
-              baseType, node, _analyzedMember, _types.nonNullEmpty(), 0));
+              baseType, node, _analyzedMember, elementType, 0));
     }
     if (commonElements.isNamedListConstructor('of', constructor) ||
         commonElements.isNamedListConstructor('from', constructor)) {
@@ -1314,6 +1315,44 @@
           () => _types.allocateList(
               baseType, node, _analyzedMember, elementType));
     }
+
+    // `JSArray.fixed` corresponds to `new Array(length)`, which is a list
+    // filled with `null`.
+    if (commonElements.isNamedJSArrayConstructor('fixed', constructor)) {
+      int length = _findLength(arguments);
+      TypeInformation elementType = _types.nullType;
+      return _inferrer.concreteTypes.putIfAbsent(
+          node,
+          () => _types.allocateList(_types.fixedListType, node, _analyzedMember,
+              elementType, length));
+    }
+
+    // `JSArray.allocateFixed` creates an array with 'no elements'. The contract
+    // is that the caller will assign a value to each member before any element
+    // is accessed. We can start tracking the element type as 'bottom'.
+    if (commonElements.isNamedJSArrayConstructor(
+        'allocateFixed', constructor)) {
+      int length = _findLength(arguments);
+      TypeInformation elementType = _types.nonNullEmpty();
+      return _inferrer.concreteTypes.putIfAbsent(
+          node,
+          () => _types.allocateList(_types.fixedListType, node, _analyzedMember,
+              elementType, length));
+    }
+
+    // `JSArray.allocateGrowable` creates an array with 'no elements'. The
+    // contract is that the caller will assign a value to each member before any
+    // element is accessed. We can start tracking the element type as 'bottom'.
+    if (commonElements.isNamedJSArrayConstructor(
+        'allocateGrowable', constructor)) {
+      int length = _findLength(arguments);
+      TypeInformation elementType = _types.nonNullEmpty();
+      return _inferrer.concreteTypes.putIfAbsent(
+          node,
+          () => _types.allocateList(_types.growableListType, node,
+              _analyzedMember, elementType, length));
+    }
+
     if (_isConstructorOfTypedArraySubclass(constructor)) {
       // We have something like `Uint32List(len)`.
       int length = _findLength(arguments);
diff --git a/pkg/compiler/lib/src/kernel/dart2js_target.dart b/pkg/compiler/lib/src/kernel/dart2js_target.dart
index 07cb160..ce387d5 100644
--- a/pkg/compiler/lib/src/kernel/dart2js_target.dart
+++ b/pkg/compiler/lib/src/kernel/dart2js_target.dart
@@ -17,6 +17,7 @@
 import 'package:kernel/target/targets.dart';
 
 import 'invocation_mirror_constants.dart';
+import 'transformations/lowering.dart' as lowering show transformLibraries;
 
 const Iterable<String> _allowedDartSchemePaths = const <String>[
   'async',
@@ -66,7 +67,7 @@
   bool get enableNoSuchMethodForwarders => true;
 
   @override
-  bool get supportsLateFields => false;
+  int get enabledLateLowerings => LateLowering.all;
 
   @override
   bool get supportsLateLoweringSentinel => false;
@@ -83,6 +84,12 @@
   List<String> get extraRequiredLibraries => _requiredLibraries[name];
 
   @override
+  List<String> get extraIndexedLibraries => const [
+        'dart:_interceptors',
+        'dart:_js_helper',
+      ];
+
+  @override
   bool mayDefineRestrictedType(Uri uri) =>
       uri.scheme == 'dart' &&
       (uri.path == 'core' || uri.path == '_interceptors');
@@ -118,6 +125,9 @@
               diagnosticReporter as DiagnosticReporter<Message, LocatedMessage>)
           .visitLibrary(library);
     }
+    lowering.transformLibraries(
+        libraries, coreTypes, hierarchy, flags.enableNullSafety);
+    logger?.call("Lowering transformations performed");
   }
 
   @override
diff --git a/pkg/compiler/lib/src/kernel/transformations/factory_specializer.dart b/pkg/compiler/lib/src/kernel/transformations/factory_specializer.dart
new file mode 100644
index 0000000..ebc00ae
--- /dev/null
+++ b/pkg/compiler/lib/src/kernel/transformations/factory_specializer.dart
@@ -0,0 +1,39 @@
+// 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.
+
+import 'package:kernel/kernel.dart';
+import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
+import 'package:kernel/core_types.dart';
+import 'list_factory_specializer.dart';
+
+typedef SpecializerTransformer = TreeNode Function(
+    StaticInvocation node, Member contextMember);
+
+abstract class BaseSpecializer {
+  // Populated in constructors of subclasses.
+  final Map<Member, SpecializerTransformer> transformers = {};
+}
+
+class FactorySpecializer extends BaseSpecializer {
+  final ListFactorySpecializer _listFactorySpecializer;
+
+  FactorySpecializer(CoreTypes coreTypes, ClassHierarchy hierarchy)
+      : _listFactorySpecializer = ListFactorySpecializer(coreTypes, hierarchy) {
+    transformers.addAll(_listFactorySpecializer.transformers);
+  }
+
+  TreeNode transformStaticInvocation(
+      StaticInvocation invocation, Member contextMember) {
+    final target = invocation.target;
+    if (target == null) {
+      return invocation;
+    }
+
+    final transformer = transformers[target];
+    if (transformer != null) {
+      return transformer(invocation, contextMember);
+    }
+    return invocation;
+  }
+}
diff --git a/pkg/compiler/lib/src/kernel/transformations/list_factory_specializer.dart b/pkg/compiler/lib/src/kernel/transformations/list_factory_specializer.dart
new file mode 100644
index 0000000..e61d1dd
--- /dev/null
+++ b/pkg/compiler/lib/src/kernel/transformations/list_factory_specializer.dart
@@ -0,0 +1,343 @@
+// 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.
+
+import 'package:kernel/ast.dart';
+import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
+import 'package:kernel/clone.dart' show CloneVisitorNotMembers;
+import 'package:kernel/core_types.dart' show CoreTypes;
+import 'factory_specializer.dart';
+
+/// Replaces invocation of List factory constructors.
+///
+/// Expands `List.generate` to a loop when the function argument is a function
+/// expression (immediate closure).
+///
+class ListFactorySpecializer extends BaseSpecializer {
+  final CoreTypes coreTypes;
+  final ClassHierarchy hierarchy;
+
+  final Class _intClass;
+  final Class _jsArrayClass;
+  final Procedure _listGenerateFactory;
+  final Procedure _arrayAllocateFixedFactory;
+  final Procedure _arrayAllocateGrowableFactory;
+
+  ListFactorySpecializer(this.coreTypes, this.hierarchy)
+      : _listGenerateFactory =
+            coreTypes.index.getMember('dart:core', 'List', 'generate'),
+        _arrayAllocateFixedFactory = coreTypes.index
+            .getMember('dart:_interceptors', 'JSArray', 'allocateFixed'),
+        _arrayAllocateGrowableFactory = coreTypes.index
+            .getMember('dart:_interceptors', 'JSArray', 'allocateGrowable'),
+        _jsArrayClass =
+            coreTypes.index.getClass('dart:_interceptors', 'JSArray'),
+        _intClass = coreTypes.index.getClass('dart:core', 'int') {
+    assert(_listGenerateFactory.isFactory);
+    assert(_arrayAllocateGrowableFactory.isFactory);
+    assert(_arrayAllocateFixedFactory.isFactory);
+    transformers.addAll({
+      _listGenerateFactory: transformListGenerateFactory,
+    });
+  }
+
+  Member _intPlus;
+  Member get intPlus =>
+      _intPlus ??= hierarchy.getInterfaceMember(_intClass, Name('+'));
+
+  Member _intLess;
+  Member get intLess =>
+      _intLess ??= hierarchy.getInterfaceMember(_intClass, Name('<'));
+
+  Member _jsArrayIndexSet;
+  Member get jsArrayIndexSet => _jsArrayIndexSet ??=
+      hierarchy.getInterfaceMember(_jsArrayClass, Name('[]='));
+
+  /// Replace calls to `List.generate(length, (i) => e)` with an expansion
+  ///
+  ///     BlockExpression
+  ///       Block
+  ///         var _length = <length>;
+  ///         var _list = List.allocate(_length);
+  ///         for (var _i = 0; _i < _length; _i++) {
+  ///           _list[_i] = e;
+  ///         }
+  ///       => _list
+  ///
+  /// Declines to expand if:
+  ///  - the function argument is not a simple closure,
+  ///  - the `growable:` argument cannot be determined.
+  TreeNode transformListGenerateFactory(
+      StaticInvocation node, Member contextMember) {
+    final args = node.arguments;
+    assert(args.positional.length == 2);
+    final length = args.positional[0];
+    final generator = args.positional[1];
+    final bool growable =
+        _getConstantNamedOptionalArgument(args, 'growable', true);
+    if (growable == null) return node;
+
+    if (generator is! FunctionExpression) return node;
+
+    if (!ListGenerateLoopBodyInliner.suitableFunctionExpression(generator)) {
+      return node;
+    }
+
+    final intType = contextMember.isNonNullableByDefault
+        ? coreTypes.intLegacyRawType
+        : coreTypes.intNonNullableRawType;
+
+    // If the length is a constant, use the constant directly so that the
+    // inferrer can see the constant length.
+    int /*?*/ lengthConstant = _getLengthArgument(args);
+    VariableDeclaration lengthVariable;
+
+    Expression getLength() {
+      if (lengthConstant != null) return IntLiteral(lengthConstant);
+      lengthVariable ??= VariableDeclaration('_length',
+          initializer: length, isFinal: true, type: intType)
+        ..fileOffset = node.fileOffset;
+      return VariableGet(lengthVariable)..fileOffset = node.fileOffset;
+    }
+
+    TreeNode allocation = StaticInvocation(
+        growable ? _arrayAllocateGrowableFactory : _arrayAllocateFixedFactory,
+        Arguments(
+          [getLength()],
+          types: args.types,
+        ))
+      ..fileOffset = node.fileOffset;
+
+    final listVariable = VariableDeclaration(
+      _listNameFromContext(node),
+      initializer: allocation,
+      isFinal: true,
+      type: InterfaceType(
+          _jsArrayClass, Nullability.nonNullable, [...args.types]),
+    )..fileOffset = node.fileOffset;
+
+    final indexVariable = VariableDeclaration(
+      _indexNameFromContext(generator),
+      initializer: IntLiteral(0),
+      type: intType,
+    )..fileOffset = node.fileOffset;
+    indexVariable.fileOffset = (generator as FunctionExpression)
+        .function
+        .positionalParameters
+        .first
+        .fileOffset;
+
+    final loop = ForStatement(
+      // initializers: _i = 0
+      [indexVariable],
+      // condition: _i < _length
+      MethodInvocation(
+        VariableGet(indexVariable)..fileOffset = node.fileOffset,
+        Name('<'),
+        Arguments([getLength()]),
+      )..interfaceTarget = intLess,
+      // updates: _i++
+      [
+        VariableSet(
+          indexVariable,
+          MethodInvocation(
+            VariableGet(indexVariable)..fileOffset = node.fileOffset,
+            Name('+'),
+            Arguments([IntLiteral(1)]),
+          )..interfaceTarget = intPlus,
+        )..fileOffset = node.fileOffset,
+      ],
+      // body, e.g. _list[_i] = expression;
+      _loopBody(node.fileOffset, listVariable, indexVariable, generator),
+    )..fileOffset = node.fileOffset;
+
+    return BlockExpression(
+      Block([
+        if (lengthVariable != null) lengthVariable,
+        listVariable,
+        loop,
+      ]),
+      VariableGet(listVariable)..fileOffset = node.fileOffset,
+    );
+  }
+
+  Statement _loopBody(
+      int constructorFileOffset,
+      VariableDeclaration listVariable,
+      VariableDeclaration indexVariable,
+      FunctionExpression generator) {
+    final inliner = ListGenerateLoopBodyInliner(
+        this, constructorFileOffset, listVariable, generator.function);
+    inliner.bind(indexVariable);
+    return inliner.run();
+  }
+
+  /// Returns constant value of the first argument in [args], or null if it is
+  /// not a constant.
+  int /*?*/ _getLengthArgument(Arguments args) {
+    if (args.positional.length < 1) return null;
+    final value = args.positional.first;
+    if (value is IntLiteral) {
+      return value.value;
+    } else if (value is ConstantExpression) {
+      final constant = value.constant;
+      if (constant is IntConstant) {
+        return constant.value;
+      }
+    }
+    return null;
+  }
+
+  /// Returns constant value of the only named optional argument in [args], or
+  /// null if it is not a bool constant. Returns [defaultValue] if optional
+  /// argument is not passed. Argument is asserted to have the given [name].
+  bool /*?*/ _getConstantNamedOptionalArgument(
+      Arguments args, String name, bool defaultValue) {
+    if (args.named.isEmpty) {
+      return defaultValue;
+    }
+    final namedArg = args.named.single;
+    assert(namedArg.name == name);
+    final value = namedArg.value;
+    if (value is BoolLiteral) {
+      return value.value;
+    } else if (value is ConstantExpression) {
+      final constant = value.constant;
+      if (constant is BoolConstant) {
+        return constant.value;
+      }
+    }
+    return null;
+  }
+
+  /// Choose a name for the `_list` temporary. If the `List.generate` expression
+  /// is an initializer for a variable, use that name so that dart2js can try to
+  /// use one JavaScript variable with the source name for 'both' variables.
+  String _listNameFromContext(Expression node) {
+    TreeNode parent = node.parent;
+    if (parent is VariableDeclaration) return parent.name;
+    return '_list';
+  }
+
+  String _indexNameFromContext(FunctionExpression generator) {
+    final function = generator.function;
+    String /*?*/ candidate = function.positionalParameters.first.name;
+    if (candidate == null || candidate == '' || candidate == '_') return '_i';
+    return candidate;
+  }
+}
+
+/// Inliner for function expressions of `List.generate` calls.
+class ListGenerateLoopBodyInliner extends CloneVisitorNotMembers {
+  final ListFactorySpecializer listFactorySpecializer;
+
+  /// Offset for the constructor call, used for all nodes that carry the value of the list.
+  final int constructorFileOffset;
+  final VariableDeclaration listVariable;
+  final FunctionNode function;
+  VariableDeclaration argument;
+  VariableDeclaration parameter;
+  int functionNestingLevel = 0;
+
+  ListGenerateLoopBodyInliner(this.listFactorySpecializer,
+      this.constructorFileOffset, this.listVariable, this.function);
+
+  static bool suitableFunctionExpression(FunctionExpression node) {
+    final function = node.function;
+    // These conditions should be satisfied by language rules.
+    if (function.typeParameters.isNotEmpty) return false;
+    if (function.requiredParameterCount != 1) return false;
+    if (function.positionalParameters.length != 1) return false;
+    if (function.namedParameters.isNotEmpty) return false;
+
+    final body = function.body;
+    // For now, only arrow functions.
+    if (body is ReturnStatement) return true;
+
+    return false;
+  }
+
+  void bind(VariableDeclaration argument) {
+    // The [argument] is the loop index variable. In the general case this needs
+    // to be copied to a variable for the closure parameter as that is a
+    // separate location that may be mutated.  In the usual case the closure
+    // parameter is not modified. We use the same name for the parameter and
+    // argument to help dart2js allocate both locations to the same JavaScript
+    // variable. The argument is usually named after the closure parameter.
+    final closureParameter = function.positionalParameters.single;
+    parameter = VariableDeclaration(argument.name,
+        initializer: VariableGet(argument)..fileOffset = argument.fileOffset,
+        type: closureParameter.type)
+      ..fileOffset = closureParameter.fileOffset;
+    this.argument = argument;
+    variables[closureParameter] = parameter;
+  }
+
+  Statement run() {
+    final body = cloneInContext(function.body);
+    return Block([parameter, body]);
+  }
+
+  @override
+  visitReturnStatement(ReturnStatement node) {
+    // Do the default for return statements in nested functions.
+    if (functionNestingLevel > 0) return super.visitReturnStatement(node);
+
+    // We don't use a variable for the returned value. In the simple case it is
+    // not necessary, and it is not clear that the rules for definite assignment
+    // are not a perfect match for the locations of return statements. Instead
+    // we expand
+    //
+    //     return expression;
+    //
+    // to
+    //
+    //     list[index] = expression;
+    //
+    // TODO(sra): Currently this inliner accepts only arrow functions (a single
+    // return). If a wider variety is accepted, we might need to break after the
+    // assignment to 'exit' the inlined code.
+
+    final expression = node.expression;
+    final value = expression == null ? NullLiteral() : clone(expression);
+    // TODO(sra): Indicate that this indexed setter is safe.
+    return ExpressionStatement(
+      MethodInvocation(
+        VariableGet(listVariable)..fileOffset = constructorFileOffset,
+        Name('[]='),
+        Arguments([
+          VariableGet(argument)..fileOffset = node.fileOffset,
+          value,
+        ]),
+      )
+        ..interfaceTarget = listFactorySpecializer.jsArrayIndexSet
+        ..isInvariant = true
+        ..isBoundsSafe = true
+        ..fileOffset = constructorFileOffset,
+    );
+  }
+
+  /// Nested functions.
+  @override
+  visitFunctionNode(FunctionNode node) {
+    functionNestingLevel++;
+    final cloned = super.visitFunctionNode(node);
+    functionNestingLevel--;
+    return cloned;
+  }
+
+  @override
+  visitVariableGet(VariableGet node) {
+    // Unmapped variables are from an outer scope.
+    var mapped = variables[node.variable] ?? node.variable;
+    return VariableGet(mapped, visitOptionalType(node.promotedType))
+      ..fileOffset = node.fileOffset;
+  }
+
+  @override
+  visitVariableSet(VariableSet node) {
+    // Unmapped variables are from an outer scope.
+    var mapped = variables[node.variable] ?? node.variable;
+    return VariableSet(mapped, clone(node.value))..fileOffset = node.fileOffset;
+  }
+}
diff --git a/pkg/compiler/lib/src/kernel/transformations/lowering.dart b/pkg/compiler/lib/src/kernel/transformations/lowering.dart
new file mode 100644
index 0000000..72c4f15
--- /dev/null
+++ b/pkg/compiler/lib/src/kernel/transformations/lowering.dart
@@ -0,0 +1,56 @@
+// 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.
+
+import 'package:kernel/ast.dart';
+import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
+import 'package:kernel/core_types.dart' show CoreTypes;
+import 'package:kernel/type_environment.dart'
+    show StaticTypeContext, TypeEnvironment;
+import 'factory_specializer.dart';
+
+/// dart2js-specific lowering transformations and optimizations combined into a
+/// single transformation pass.
+///
+/// Each transformation is applied locally to AST nodes of certain types after
+/// transforming children nodes.
+void transformLibraries(List<Library> libraries, CoreTypes coreTypes,
+    ClassHierarchy hierarchy, bool nullSafety) {
+  final transformer = _Lowering(coreTypes, hierarchy, nullSafety);
+  libraries.forEach(transformer.visitLibrary);
+}
+
+class _Lowering extends Transformer {
+  final TypeEnvironment env;
+  final bool nullSafety;
+  final FactorySpecializer factorySpecializer;
+
+  Member _currentMember;
+  StaticTypeContext _cachedStaticTypeContext;
+
+  _Lowering(CoreTypes coreTypes, ClassHierarchy hierarchy, this.nullSafety)
+      : env = TypeEnvironment(coreTypes, hierarchy),
+        factorySpecializer = FactorySpecializer(coreTypes, hierarchy);
+
+  // ignore: unused_element
+  StaticTypeContext get _staticTypeContext =>
+      _cachedStaticTypeContext ??= StaticTypeContext(_currentMember, env);
+
+  @override
+  defaultMember(Member node) {
+    _currentMember = node;
+    _cachedStaticTypeContext = null;
+
+    final result = super.defaultMember(node);
+
+    _currentMember = null;
+    _cachedStaticTypeContext = null;
+    return result;
+  }
+
+  @override
+  visitStaticInvocation(StaticInvocation node) {
+    node.transformChildren(this);
+    return factorySpecializer.transformStaticInvocation(node, _currentMember);
+  }
+}
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index fb0b04b..1032865 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -3882,7 +3882,10 @@
       if (_abstractValueDomain.isFixedArray(resultType).isDefinitelyTrue) {
         // These constructors all take a length as the first argument.
         if (_commonElements.isNamedListConstructor('filled', function) ||
-            _commonElements.isNamedListConstructor('generate', function)) {
+            _commonElements.isNamedListConstructor('generate', function) ||
+            _commonElements.isNamedJSArrayConstructor('fixed', function) ||
+            _commonElements.isNamedJSArrayConstructor(
+                'allocateFixed', function)) {
           isFixedList = true;
         }
       }
@@ -4963,22 +4966,28 @@
 
     AbstractValue resultType =
         _typeInferenceMap.resultTypeOfSelector(selector, receiverType);
+    HInvokeDynamic invoke;
     if (selector.isGetter) {
-      push(new HInvokeDynamicGetter(selector, receiverType, element, inputs,
-          isIntercepted, resultType, sourceInformation));
+      invoke = HInvokeDynamicGetter(selector, receiverType, element, inputs,
+          isIntercepted, resultType, sourceInformation);
     } else if (selector.isSetter) {
-      push(new HInvokeDynamicSetter(selector, receiverType, element, inputs,
-          isIntercepted, resultType, sourceInformation));
+      invoke = HInvokeDynamicSetter(selector, receiverType, element, inputs,
+          isIntercepted, resultType, sourceInformation);
     } else if (selector.isClosureCall) {
       assert(!isIntercepted);
-      push(new HInvokeClosure(
+      invoke = HInvokeClosure(
           selector, receiverType, inputs, resultType, typeArguments)
-        ..sourceInformation = sourceInformation);
+        ..sourceInformation = sourceInformation;
     } else {
-      push(new HInvokeDynamicMethod(selector, receiverType, inputs, resultType,
+      invoke = HInvokeDynamicMethod(selector, receiverType, inputs, resultType,
           typeArguments, sourceInformation,
-          isIntercepted: isIntercepted));
+          isIntercepted: isIntercepted);
     }
+    if (node is ir.MethodInvocation) {
+      invoke.isInvariant = node.isInvariant;
+      invoke.isBoundsSafe = node.isBoundsSafe;
+    }
+    push(invoke);
   }
 
   HInstruction _invokeJsInteropFunction(
diff --git a/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart b/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart
index 49e534a..92745f6 100644
--- a/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart
+++ b/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart
@@ -192,6 +192,9 @@
       HInstruction value,
       JCommonElements commonElements,
       JClosedWorld closedWorld) {
+    if (instruction.isInvariant) {
+      return true;
+    }
     // Handle typed arrays by recognizing the exact implementation of `[]=` and
     // checking if [value] has the appropriate type.
     if (instruction.element != null) {
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index 6a551f89..77c24d3 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -1709,6 +1709,17 @@
   AbstractValue _receiverType;
   final AbstractValue _originalReceiverType;
 
+  /// `true` if the type parameters at the call known to be invariant with
+  /// respect to the type parameters of the receiver instance. This corresponds
+  /// to the [ir.MethodInvocation.isInvariant] property and may be updated with
+  /// additional analysis.
+  bool isInvariant = false;
+
+  /// `true` for an indexed getter or setter if the index is known to be in
+  /// range. This corresponds to the [ir.MethodInvocation.isBoundsSafe] property
+  /// but and may updated with additional analysis.
+  bool isBoundsSafe = false;
+
   // Cached target when non-nullable receiver type and selector determine a
   // single target. This is in effect a direct call (except for a possible
   // `null` receiver). The element should only be set if the inputs are correct
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index aff9860..ad03cf6 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -679,12 +679,15 @@
         if (applies(commonElements.jsArrayRemoveLast)) {
           target = commonElements.jsArrayRemoveLast;
         } else if (applies(commonElements.jsArrayAdd)) {
-          // The codegen special cases array calls, but does not
-          // inline argument type checks.
-          if (!_closedWorld.annotationsData
+          // Codegen special cases array calls to `Array.push`, but does not
+          // inline argument type checks. We lower if the check always passes
+          // (due to invariance or being a top-type), or if the check is not
+          // emitted.
+          if (node.isInvariant ||
+              input is HLiteralList ||
+              !_closedWorld.annotationsData
                   .getParameterCheckPolicy(commonElements.jsArrayAdd)
-                  .isEmitted ||
-              input is HLiteralList) {
+                  .isEmitted) {
             target = commonElements.jsArrayAdd;
           }
         }
diff --git a/pkg/compiler/pubspec.yaml b/pkg/compiler/pubspec.yaml
index 58607bb..6a7ec65 100644
--- a/pkg/compiler/pubspec.yaml
+++ b/pkg/compiler/pubspec.yaml
@@ -34,6 +34,7 @@
   package_config: any
   path: any
   source_maps: any
+  test: any
   # Unpublished packages that can be used via path dependency
   async_helper:
     path: ../async_helper
diff --git a/pkg/compiler/test/inference/data/list2.dart b/pkg/compiler/test/inference/data/list2.dart
index 16b21d8..81d7d8f 100644
--- a/pkg/compiler/test/inference/data/list2.dart
+++ b/pkg/compiler/test/inference/data/list2.dart
@@ -23,6 +23,7 @@
     listGenerateGrowable,
     listGenerateFixed,
     listGenerateEither,
+    listGenerateBigClosure,
     listOfDefault,
     listOfGrowable,
     listOfFixed,
@@ -72,24 +73,37 @@
 // -------- List.generate --------
 
 /*member: listGenerateDefault:Container([exact=JSExtendableArray], element: [exact=JSString], length: 8)*/
-get listGenerateDefault => List.generate(
-    8, /*[exact=JSString]*/ (/*[subclass=JSPositiveInt]*/ i) => 'x$i');
+get listGenerateDefault => List
+    . /*update: Container([exact=JSExtendableArray], element: [exact=JSString], length: 8)*/ generate(
+        8, (i) => 'x$i');
 
 /*member: listGenerateGrowable:Container([exact=JSExtendableArray], element: [exact=JSString], length: 8)*/
-get listGenerateGrowable => List.generate(
-    8, /*[exact=JSString]*/ (/*[subclass=JSPositiveInt]*/ i) => 'g$i',
-    growable: true);
+get listGenerateGrowable => List
+    . /*update: Container([exact=JSExtendableArray], element: [exact=JSString], length: 8)*/ generate(
+        8, (i) => 'g$i',
+        growable: true);
 
 /*member: listGenerateFixed:Container([exact=JSFixedArray], element: [exact=JSString], length: 8)*/
-get listGenerateFixed => List.generate(
-    8, /*[exact=JSString]*/ (/*[subclass=JSPositiveInt]*/ i) => 'f$i',
-    growable: false);
+get listGenerateFixed => List
+    . /*update: Container([exact=JSFixedArray], element: [exact=JSString], length: 8)*/ generate(
+        8, (i) => 'f$i',
+        growable: false);
 
 /*member: listGenerateEither:Container([subclass=JSMutableArray], element: [exact=JSString], length: 8)*/
 get listGenerateEither => List.generate(
     8, /*[exact=JSString]*/ (/*[subclass=JSPositiveInt]*/ i) => 'e$i',
     growable: boolFlag);
 
+/*member: listGenerateBigClosure:Container([exact=JSExtendableArray], element: [exact=JSString], length: 8)*/
+get listGenerateBigClosure => List.generate(
+      8,
+      /*[exact=JSString]*/ (/*[subclass=JSPositiveInt]*/ i) {
+        if (i /*invoke: [subclass=JSPositiveInt]*/ == 1) return 'one';
+        if (i /*invoke: [subclass=JSPositiveInt]*/ == 2) return 'two';
+        return '$i';
+      },
+    );
+
 // -------- List.of --------
 
 /*member: listOfDefault:Container([exact=JSExtendableArray], element: [null|subclass=Object], length: null)*/
diff --git a/pkg/compiler/test/kernel/common_test_utils.dart b/pkg/compiler/test/kernel/common_test_utils.dart
new file mode 100644
index 0000000..ed7c9e5
--- /dev/null
+++ b/pkg/compiler/test/kernel/common_test_utils.dart
@@ -0,0 +1,135 @@
+// 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.
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:front_end/src/api_unstable/dart2js.dart'
+    show
+        CompilerOptions,
+        DiagnosticMessage,
+        computePlatformBinariesLocation,
+        kernelForProgram,
+        parseExperimentalArguments,
+        parseExperimentalFlags;
+import 'package:kernel/ast.dart';
+import 'package:kernel/text/ast_to_text.dart' show Printer;
+import 'package:kernel/target/targets.dart';
+import 'package:test/test.dart';
+
+import 'package:compiler/src/kernel/dart2js_target.dart' show Dart2jsTarget;
+
+/// Environment define to update expectation files on failures.
+const kUpdateExpectations = 'updateExpectations';
+
+/// Environment define to dump actual results alongside expectations.
+const kDumpActualResult = 'dump.actual.result';
+
+class TestingDart2jsTarget extends Dart2jsTarget {
+  TestingDart2jsTarget(TargetFlags flags) : super('dart2js', flags);
+}
+
+Future<Component> compileTestCaseToKernelProgram(Uri sourceUri,
+    {Target target,
+    bool enableSuperMixins = false,
+    List<String> experimentalFlags,
+    Map<String, String> environmentDefines}) async {
+  final platformKernel =
+      computePlatformBinariesLocation().resolve('dart2js_platform.dill');
+  target ??= TestingDart2jsTarget(TargetFlags());
+  environmentDefines ??= <String, String>{};
+  final options = CompilerOptions()
+    ..target = target
+    ..additionalDills = <Uri>[platformKernel]
+    ..environmentDefines = environmentDefines
+    ..explicitExperimentalFlags =
+        parseExperimentalFlags(parseExperimentalArguments(experimentalFlags),
+            onError: (String message) {
+      throw message;
+    })
+    ..onDiagnostic = (DiagnosticMessage message) {
+      fail("Compilation error: ${message.plainTextFormatted.join('\n')}");
+    };
+
+  final Component component =
+      (await kernelForProgram(sourceUri, options)).component;
+
+  // Make sure the library name is the same and does not depend on the order
+  // of test cases.
+  component.mainMethod.enclosingLibrary.name = '#lib';
+
+  return component;
+}
+
+String kernelLibraryToString(Library library) {
+  final buffer = StringBuffer();
+  Printer(buffer, showMetadata: true).writeLibraryFile(library);
+  return buffer
+      .toString()
+      .replaceAll(library.importUri.toString(), library.name);
+}
+
+String kernelComponentToString(Component component) {
+  final buffer = StringBuffer();
+  Printer(buffer, showMetadata: true).writeComponentFile(component);
+  final mainLibrary = component.mainMethod.enclosingLibrary;
+  return buffer
+      .toString()
+      .replaceAll(mainLibrary.importUri.toString(), mainLibrary.name);
+}
+
+class Difference {
+  final int line;
+  final String actual;
+  final String expected;
+
+  Difference(this.line, this.actual, this.expected);
+}
+
+Difference findFirstDifference(String actual, String expected) {
+  final actualLines = actual.split('\n');
+  final expectedLines = expected.split('\n');
+  int i = 0;
+  for (; i < actualLines.length && i < expectedLines.length; ++i) {
+    if (actualLines[i] != expectedLines[i]) {
+      return Difference(i + 1, actualLines[i], expectedLines[i]);
+    }
+  }
+  return Difference(i + 1, i < actualLines.length ? actualLines[i] : '<END>',
+      i < expectedLines.length ? expectedLines[i] : '<END>');
+}
+
+void compareResultWithExpectationsFile(Uri source, String actual) {
+  final expectFile = File(source.toFilePath() + '.expect');
+  final expected = expectFile.existsSync() ? expectFile.readAsStringSync() : '';
+
+  if (actual != expected) {
+    if (bool.fromEnvironment(kUpdateExpectations)) {
+      expectFile.writeAsStringSync(actual);
+      print("  Updated $expectFile");
+    } else {
+      if (bool.fromEnvironment(kDumpActualResult)) {
+        File(source.toFilePath() + '.actual').writeAsStringSync(actual);
+      }
+      Difference diff = findFirstDifference(actual, expected);
+      fail("""
+
+Result is different for the test case $source
+
+The first difference is at line ${diff.line}.
+Actual:   ${diff.actual}
+Expected: ${diff.expected}
+
+This failure can be caused by changes in the front-end if it starts generating
+different kernel AST for the same Dart programs.
+
+In order to re-generate expectations run tests with -D$kUpdateExpectations=true VM option:
+
+  sdk/bin/dart -D$kUpdateExpectations=true pkg/compiler/test/kernel/goldens_test.dart
+
+In order to dump actual results into .actual files run tests with -D$kDumpActualResult=true VM option.
+""");
+    }
+  }
+}
diff --git a/pkg/compiler/test/kernel/data/list_generate_1.dart b/pkg/compiler/test/kernel/data/list_generate_1.dart
new file mode 100644
index 0000000..3365cf5
--- /dev/null
+++ b/pkg/compiler/test/kernel/data/list_generate_1.dart
@@ -0,0 +1,15 @@
+// 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.
+
+var list1 = List<int>.generate(10, (i) => i);
+var list2 = List<int>.generate(10, (i) => i, growable: true);
+var list3 = List<int>.generate(10, (i) => i, growable: false);
+var list4 = List<int>.generate(10, (i) => i, growable: someGrowable);
+
+bool someGrowable = true;
+
+void main() {
+  someGrowable = !someGrowable;
+  print([list1, list2, list3, list4]);
+}
diff --git a/pkg/compiler/test/kernel/data/list_generate_1.dart.expect b/pkg/compiler/test/kernel/data/list_generate_1.dart.expect
new file mode 100644
index 0000000..be2484b
--- /dev/null
+++ b/pkg/compiler/test/kernel/data/list_generate_1.dart.expect
@@ -0,0 +1,32 @@
+library #lib;
+import self as self;
+import "dart:core" as core;
+import "dart:_interceptors" as _in;
+
+static field core::List<core::int*>* list1 = block {
+  final _in::JSArray<core::int*> _list = _in::JSArray::allocateGrowable<core::int*>(10);
+  for (core::int i = 0; i.{core::num::<}(10); i = i.{core::num::+}(1)) {
+    core::int* i = i;
+    _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i);
+  }
+} =>_list;
+static field core::List<core::int*>* list2 = block {
+  final _in::JSArray<core::int*> _list = _in::JSArray::allocateGrowable<core::int*>(10);
+  for (core::int i = 0; i.{core::num::<}(10); i = i.{core::num::+}(1)) {
+    core::int* i = i;
+    _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i);
+  }
+} =>_list;
+static field core::List<core::int*>* list3 = block {
+  final _in::JSArray<core::int*> _list = _in::JSArray::allocateFixed<core::int*>(10);
+  for (core::int i = 0; i.{core::num::<}(10); i = i.{core::num::+}(1)) {
+    core::int* i = i;
+    _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i);
+  }
+} =>_list;
+static field core::List<core::int*>* list4 = core::List::generate<core::int*>(10, (core::int* i) → core::int* => i, growable: self::someGrowable);
+static field core::bool* someGrowable = true;
+static method main() → void {
+  self::someGrowable = !self::someGrowable;
+  core::print(<core::List<core::int*>*>[self::list1, self::list2, self::list3, self::list4]);
+}
diff --git a/pkg/compiler/test/kernel/data/list_generate_2.dart b/pkg/compiler/test/kernel/data/list_generate_2.dart
new file mode 100644
index 0000000..af9196d
--- /dev/null
+++ b/pkg/compiler/test/kernel/data/list_generate_2.dart
@@ -0,0 +1,8 @@
+// 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.
+
+void main() {
+  // 'nested' List.generate calls.
+  print(List.generate(10, (i) => List.generate(i, (i) => i + 1)));
+}
diff --git a/pkg/compiler/test/kernel/data/list_generate_2.dart.expect b/pkg/compiler/test/kernel/data/list_generate_2.dart.expect
new file mode 100644
index 0000000..e2a2196
--- /dev/null
+++ b/pkg/compiler/test/kernel/data/list_generate_2.dart.expect
@@ -0,0 +1,21 @@
+library #lib;
+import self as self;
+import "dart:core" as core;
+import "dart:_interceptors" as _in;
+
+static method main() → void {
+  core::print( block {
+    final _in::JSArray<core::List<core::int*>*> _list = _in::JSArray::allocateGrowable<core::List<core::int*>*>(10);
+    for (core::int i = 0; i.{core::num::<}(10); i = i.{core::num::+}(1)) {
+      core::int* i = i;
+      _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, block {
+        final core::int _length = i;
+        final _in::JSArray<core::int*> _list = _in::JSArray::allocateGrowable<core::int*>(_length);
+        for (core::int i = 0; i.{core::num::<}(_length); i = i.{core::num::+}(1)) {
+          core::int* i = i;
+          _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i.{core::num::+}(1));
+        }
+      } =>_list);
+    }
+  } =>_list);
+}
diff --git a/pkg/compiler/test/kernel/goldens_test.dart b/pkg/compiler/test/kernel/goldens_test.dart
new file mode 100644
index 0000000..a945729
--- /dev/null
+++ b/pkg/compiler/test/kernel/goldens_test.dart
@@ -0,0 +1,46 @@
+// 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.
+
+import 'dart:io';
+
+import 'package:kernel/target/targets.dart';
+import 'package:kernel/ast.dart';
+import 'package:kernel/kernel.dart';
+import 'package:test/test.dart';
+
+import 'common_test_utils.dart';
+
+final String testRootDir = Platform.script.resolve('.').toFilePath();
+
+runTestCase(
+    Uri source, List<String> experimentalFlags, bool enableNullSafety) async {
+  final target =
+      TestingDart2jsTarget(TargetFlags(enableNullSafety: enableNullSafety));
+  Component component = await compileTestCaseToKernelProgram(source,
+      target: target, experimentalFlags: experimentalFlags);
+
+  String actual = kernelLibraryToString(component.mainMethod.enclosingLibrary);
+
+  compareResultWithExpectationsFile(source, actual);
+}
+
+main() {
+  group('goldens', () {
+    final testCasesDir = new Directory(testRootDir + '/data');
+
+    for (var entry
+        in testCasesDir.listSync(recursive: true, followLinks: false)) {
+      final path = entry.path;
+      if (path.endsWith('.dart')) {
+        final bool enableNullSafety = path.endsWith('_nnbd_strong.dart');
+        final bool enableNNBD = enableNullSafety || path.endsWith('_nnbd.dart');
+        final List<String> experimentalFlags = [
+          if (enableNNBD) 'non-nullable',
+        ];
+        test(path,
+            () => runTestCase(entry.uri, experimentalFlags, enableNullSafety));
+      }
+    }
+  }, timeout: Timeout.none);
+}
diff --git a/pkg/dartdev/lib/src/commands/test.dart b/pkg/dartdev/lib/src/commands/test.dart
index b4fe29c..2e34c97 100644
--- a/pkg/dartdev/lib/src/commands/test.dart
+++ b/pkg/dartdev/lib/src/commands/test.dart
@@ -5,10 +5,9 @@
 import 'dart:async';
 
 import 'package:args/args.dart';
+import 'package:pub/pub.dart';
 
 import '../core.dart';
-import '../experiments.dart';
-import '../sdk.dart';
 import '../vm_interop_handler.dart';
 
 /// Implement `dart test`.
@@ -19,110 +18,38 @@
 
   TestCommand() : super(cmdName, 'Run tests in this package.');
 
+  // This argument parser is here solely to ensure that VM specific flags are
+  // provided before any command and to provide a more consistent help message
+  // with the rest of the tool.
   @override
-  final ArgParser argParser = ArgParser.allowAnything();
-
-  @override
-  void printUsage() {
-    _runImpl(['-h']);
+  ArgParser createArgParser() {
+    return ArgParser.allowAnything();
   }
 
   @override
   FutureOr<int> run() async {
-    return _runImpl(argResults.arguments.toList());
-  }
-
-  int _runImpl(List<String> testArgs) {
-    if (!Sdk.checkArtifactExists(sdk.pubSnapshot)) {
-      return 255;
+    if (argResults.rest.contains('-h') || argResults.rest.contains('--help')) {
+      printUsage();
+      return 0;
     }
-
-    final pubSnapshot = sdk.pubSnapshot;
-
-    bool isHelpCommand = testArgs.contains('--help') || testArgs.contains('-h');
-
-    // Check for no pubspec.yaml file.
     if (!project.hasPubspecFile) {
-      _printNoPubspecMessage(isHelpCommand);
-      return 65;
-    }
-
-    // Handle the case of no .dart_tool/package_config.json file.
-    if (!project.hasPackageConfigFile) {
-      _printRunPubGetInstructions(isHelpCommand);
-      return 65;
-    }
-
-    // "Could not find package "test". Did you forget to add a dependency?"
-    if (!project.packageConfig.hasDependency('test')) {
-      _printMissingDepInstructions(isHelpCommand);
-      return 65;
-    }
-    List<String> enabledExperiments = [];
-    if (!(testArgs.length == 1 && testArgs[0] == '-h')) {
-      enabledExperiments = argResults.enabledExperiments;
-    }
-    final args = [
-      'run',
-      if (enabledExperiments.isNotEmpty)
-        '--$experimentFlagName=${enabledExperiments.join(',')}',
-      'test',
-      ...testArgs,
-    ];
-
-    log.trace('$pubSnapshot ${args.join(' ')}');
-    VmInteropHandler.run(pubSnapshot, args);
-    return 0;
-  }
-
-  void _printNoPubspecMessage(bool wasHelpCommand) {
-    log.stdout('''
+      log.stdout('''
 No pubspec.yaml file found; please run this command from the root of your project.
 ''');
 
-    if (wasHelpCommand) {
-      log.stdout(_terseHelp);
-      log.stdout('');
+      printUsage();
+      return 65;
     }
-
-    log.stdout(_usageHelp);
-  }
-
-  void _printRunPubGetInstructions(bool wasHelpCommand) {
-    log.stdout('''
-No .dart_tool/package_config.json file found, please run 'dart pub get'.
-''');
-
-    if (wasHelpCommand) {
-      log.stdout(_terseHelp);
-      log.stdout('');
+    try {
+      final testExecutable = await getExecutableForCommand('test:test');
+      log.trace('dart $testExecutable ${argResults.rest.join(' ')}');
+      VmInteropHandler.run(testExecutable, argResults.rest);
+      return 0;
+    } on CommandResolutionFailedException catch (e) {
+      print(e.message);
+      print('You need to add a dependency on package:test.');
+      print('Try running `dart pub add test`.');
+      return 65;
     }
-
-    log.stdout(_usageHelp);
-  }
-
-  void _printMissingDepInstructions(bool wasHelpCommand) {
-    final ansi = log.ansi;
-
-    log.stdout('''
-No dependency on package:test found. In order to run tests, you need to add a dependency
-on package:test in your pubspec.yaml file:
-
-${ansi.emphasized('dev_dependencies:\n  test: ^1.0.0')}
-
-See https://pub.dev/packages/test/install for more information on adding package:test,
-and https://dart.dev/guides/testing for general information on testing.
-''');
-
-    if (wasHelpCommand) {
-      log.stdout(_terseHelp);
-      log.stdout('');
-    }
-
-    log.stdout(_usageHelp);
   }
 }
-
-const String _terseHelp = 'Run tests in this package.';
-
-const String _usageHelp = 'Usage: dart test [files or directories...]';
diff --git a/pkg/dartdev/test/commands/test_test.dart b/pkg/dartdev/test/commands/test_test.dart
index cf7aea9..ff85982 100644
--- a/pkg/dartdev/test/commands/test_test.dart
+++ b/pkg/dartdev/test/commands/test_test.dart
@@ -21,10 +21,7 @@
   test('--help', () {
     p = project();
 
-    var result = p.runSync('pub', ['get']);
-    expect(result.exitCode, 0);
-
-    result = p.runSync('test', ['--help']);
+    final result = p.runSync('test', ['--help']);
 
     expect(result.exitCode, 0);
     expect(result.stdout, contains(' tests in this package'));
@@ -34,10 +31,7 @@
   test('dart help test', () {
     p = project();
 
-    var result = p.runSync('pub', ['get']);
-    expect(result.exitCode, 0);
-
-    result = p.runSync('help', ['test']);
+    final result = p.runSync('help', ['test']);
 
     expect(result.exitCode, 0);
     expect(result.stdout, contains(' tests in this package'));
@@ -49,22 +43,30 @@
     var pubspec = File(path.join(p.dirPath, 'pubspec.yaml'));
     pubspec.deleteSync();
 
-    var result = p.runSync('help', ['test']);
+    var result = p.runSync('test', []);
 
-    expect(result.exitCode, 0);
-    expect(result.stdout, contains('No pubspec.yaml file found'));
     expect(result.stderr, isEmpty);
+    expect(result.stdout, contains('No pubspec.yaml file found'));
+    expect(result.exitCode, 65);
   });
 
-  test('no .dart_tool/package_config.json', () {
+  test('runs test', () {
     p = project();
+    p.file('test/foo_test.dart', '''
+import 'package:test/test.dart';
 
-    var result = p.runSync('help', ['test']);
+void main() {
+  test('', () {
+    expect(1,1);
+  });
+}
+''');
 
-    expect(result.exitCode, 0);
-    expect(result.stdout,
-        contains('No .dart_tool/package_config.json file found'));
+    // An implicit `pub get` will happen.
+    final result = p.runSync('test', ['--no-color', '--reporter', 'expanded']);
     expect(result.stderr, isEmpty);
+    expect(result.stdout, contains('All tests passed!'));
+    expect(result.exitCode, 0);
   });
 
   test('no package:test dependency', () {
@@ -74,16 +76,31 @@
 environment:
   sdk: '>=2.10.0 <3.0.0'
 ''');
+    p.file('test/foo_test.dart', '''
+import 'package:test/test.dart';
 
-    var result = p.runSync('pub', ['get']);
-    expect(result.exitCode, 0);
+void main() {
+  test('', () {
+    expect(1,1);
+  });
+}
+''');
 
-    result = p.runSync('test', []);
-    expect(result.exitCode, 65);
+    final result = p.runSync('test', []);
     expect(
       result.stdout,
-      contains('In order to run tests, you need to add a dependency'),
+      contains('You need to add a dependency on package:test'),
     );
+    expect(result.stderr, isEmpty);
+    expect(result.exitCode, 65);
+
+    final resultPubAdd = p.runSync('pub', ['add', 'test']);
+
+    expect(resultPubAdd.exitCode, 0);
+    final result2 = p.runSync('test', ['--no-color', '--reporter', 'expanded']);
+    expect(result2.stderr, isEmpty);
+    expect(result2.stdout, contains('All tests passed!'));
+    expect(result2.exitCode, 0);
   });
 
   test('has package:test dependency', () {
@@ -100,10 +117,7 @@
 }
 ''');
 
-    var result = p.runSync('pub', ['get']);
-    expect(result.exitCode, 0);
-
-    result = p.runSync('test', ['--no-color', '--reporter', 'expanded']);
+    final result = p.runSync('test', ['--no-color', '--reporter', 'expanded']);
     expect(result.exitCode, 0);
     expect(result.stdout, contains('All tests passed!'));
     expect(result.stderr, isEmpty);
@@ -123,11 +137,10 @@
 }
 ''');
 
-    var result = p.runSync('pub', ['get']);
-    expect(result.exitCode, 0);
-
-    result = p.runSync('--enable-experiment=non-nullable',
-        ['test', '--no-color', '--reporter', 'expanded']);
+    final result = p.runSync(
+      '--enable-experiment=non-nullable',
+      ['test', '--no-color', '--reporter', 'expanded'],
+    );
     expect(result.exitCode, 1);
   });
 }
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index 16cd2fe..371f472 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -2639,6 +2639,7 @@
       {bool emitNullability = true}) {
     var c = type.classNode;
     _declareBeforeUse(c);
+    js_ast.Expression typeRep;
 
     // Type parameters don't matter as JS interop types cannot be reified.
     // We have to use lazy JS types because until we have proper module
@@ -2654,17 +2655,25 @@
     // Anonymous JS types do not have a corresponding concrete JS type so we
     // have to use a helper to define them.
     if (isJSAnonymousType(c)) {
-      return runtimeCall(
+      typeRep = runtimeCall(
           'anonymousJSType(#)', [js.escapedString(getLocalClassName(c))]);
+    } else {
+      var jsName = _jsNameWithoutGlobal(c);
+      if (jsName != null) {
+        typeRep = runtimeCall('lazyJSType(() => #, #)',
+            [_emitJSInteropForGlobal(jsName), js.escapedString(jsName)]);
+      }
     }
-    var jsName = _jsNameWithoutGlobal(c);
-    if (jsName != null) {
-      return runtimeCall('lazyJSType(() => #, #)',
-          [_emitJSInteropForGlobal(jsName), js.escapedString(jsName)]);
+
+    if (typeRep != null) {
+      // JS types are not currently cached in the type table like other types
+      // are below.
+      return emitNullability
+          ? _emitNullabilityWrapper(typeRep, type.nullability)
+          : typeRep;
     }
 
     var args = type.typeArguments;
-    js_ast.Expression typeRep;
     Iterable<js_ast.Expression> jsArgs;
     if (args.any((a) => a != const DynamicType())) {
       jsArgs = args.map(_emitType);
diff --git a/pkg/dev_compiler/lib/src/kernel/target.dart b/pkg/dev_compiler/lib/src/kernel/target.dart
index f389110..eb2f225 100644
--- a/pkg/dev_compiler/lib/src/kernel/target.dart
+++ b/pkg/dev_compiler/lib/src/kernel/target.dart
@@ -34,7 +34,7 @@
   bool get enableSuperMixins => true;
 
   @override
-  bool get supportsLateFields => false;
+  int get enabledLateLowerings => LateLowering.all;
 
   @override
   bool get supportsLateLoweringSentinel => false;
diff --git a/pkg/dev_compiler/tool/dart2js_nnbd_sdk_error_golden.txt b/pkg/dev_compiler/tool/dart2js_nnbd_sdk_error_golden.txt
index bf80618..d85029a 100644
--- a/pkg/dev_compiler/tool/dart2js_nnbd_sdk_error_golden.txt
+++ b/pkg/dev_compiler/tool/dart2js_nnbd_sdk_error_golden.txt
@@ -1,12 +1,12 @@
-ERROR|COMPILE_TIME_ERROR|INCONSISTENT_INHERITANCE|lib/_internal/js_runtime/lib/interceptors.dart|1659|7|5|Superinterfaces don't have a valid override for '&': JSNumber.& (num Function(num)), int.& (int Function(int)).
-ERROR|COMPILE_TIME_ERROR|INCONSISTENT_INHERITANCE|lib/_internal/js_runtime/lib/interceptors.dart|1659|7|5|Superinterfaces don't have a valid override for '<<': JSNumber.<< (num Function(num)), int.<< (int Function(int)).
-ERROR|COMPILE_TIME_ERROR|INCONSISTENT_INHERITANCE|lib/_internal/js_runtime/lib/interceptors.dart|1659|7|5|Superinterfaces don't have a valid override for '>>': JSNumber.>> (num Function(num)), int.>> (int Function(int)).
-ERROR|COMPILE_TIME_ERROR|INCONSISTENT_INHERITANCE|lib/_internal/js_runtime/lib/interceptors.dart|1659|7|5|Superinterfaces don't have a valid override for '\|': JSNumber.\| (num Function(num)), int.\| (int Function(int)).
-ERROR|COMPILE_TIME_ERROR|INCONSISTENT_INHERITANCE|lib/_internal/js_runtime/lib/interceptors.dart|1659|7|5|Superinterfaces don't have a valid override for '^': JSNumber.^ (num Function(num)), int.^ (int Function(int)).
-ERROR|COMPILE_TIME_ERROR|RETURN_OF_INVALID_TYPE|lib/_internal/js_runtime/lib/interceptors.dart|1514|14|45|A value of type 'double' can't be returned from method '%' because it has a return type of 'JSNumber'.
-ERROR|COMPILE_TIME_ERROR|RETURN_OF_INVALID_TYPE|lib/_internal/js_runtime/lib/interceptors.dart|1516|14|45|A value of type 'double' can't be returned from method '%' because it has a return type of 'JSNumber'.
-ERROR|COMPILE_TIME_ERROR|UNDEFINED_OPERATOR|lib/_internal/js_runtime/lib/interceptors.dart|1676|28|1|The operator '&' isn't defined for the type 'JSInt'.
-ERROR|COMPILE_TIME_ERROR|UNDEFINED_OPERATOR|lib/_internal/js_runtime/lib/interceptors.dart|1678|27|1|The operator '&' isn't defined for the type 'JSInt'.
-ERROR|COMPILE_TIME_ERROR|UNDEFINED_OPERATOR|lib/_internal/js_runtime/lib/interceptors.dart|1681|17|1|The operator '&' isn't defined for the type 'JSInt'.
-ERROR|COMPILE_TIME_ERROR|UNDEFINED_OPERATOR|lib/_internal/js_runtime/lib/interceptors.dart|1686|18|1|The operator '&' isn't defined for the type 'JSInt'.
-ERROR|COMPILE_TIME_ERROR|UNDEFINED_OPERATOR|lib/_internal/js_runtime/lib/interceptors.dart|1686|44|1|The operator '&' isn't defined for the type 'JSInt'.
+ERROR|COMPILE_TIME_ERROR|INCONSISTENT_INHERITANCE|lib/_internal/js_runtime/lib/interceptors.dart|1706|7|5|Superinterfaces don't have a valid override for '&': JSNumber.& (num Function(num)), int.& (int Function(int)).
+ERROR|COMPILE_TIME_ERROR|INCONSISTENT_INHERITANCE|lib/_internal/js_runtime/lib/interceptors.dart|1706|7|5|Superinterfaces don't have a valid override for '<<': JSNumber.<< (num Function(num)), int.<< (int Function(int)).
+ERROR|COMPILE_TIME_ERROR|INCONSISTENT_INHERITANCE|lib/_internal/js_runtime/lib/interceptors.dart|1706|7|5|Superinterfaces don't have a valid override for '>>': JSNumber.>> (num Function(num)), int.>> (int Function(int)).
+ERROR|COMPILE_TIME_ERROR|INCONSISTENT_INHERITANCE|lib/_internal/js_runtime/lib/interceptors.dart|1706|7|5|Superinterfaces don't have a valid override for '\|': JSNumber.\| (num Function(num)), int.\| (int Function(int)).
+ERROR|COMPILE_TIME_ERROR|INCONSISTENT_INHERITANCE|lib/_internal/js_runtime/lib/interceptors.dart|1706|7|5|Superinterfaces don't have a valid override for '^': JSNumber.^ (num Function(num)), int.^ (int Function(int)).
+ERROR|COMPILE_TIME_ERROR|RETURN_OF_INVALID_TYPE|lib/_internal/js_runtime/lib/interceptors.dart|1561|14|45|A value of type 'double' can't be returned from method '%' because it has a return type of 'JSNumber'.
+ERROR|COMPILE_TIME_ERROR|RETURN_OF_INVALID_TYPE|lib/_internal/js_runtime/lib/interceptors.dart|1563|14|45|A value of type 'double' can't be returned from method '%' because it has a return type of 'JSNumber'.
+ERROR|COMPILE_TIME_ERROR|UNDEFINED_OPERATOR|lib/_internal/js_runtime/lib/interceptors.dart|1723|28|1|The operator '&' isn't defined for the type 'JSInt'.
+ERROR|COMPILE_TIME_ERROR|UNDEFINED_OPERATOR|lib/_internal/js_runtime/lib/interceptors.dart|1725|27|1|The operator '&' isn't defined for the type 'JSInt'.
+ERROR|COMPILE_TIME_ERROR|UNDEFINED_OPERATOR|lib/_internal/js_runtime/lib/interceptors.dart|1728|17|1|The operator '&' isn't defined for the type 'JSInt'.
+ERROR|COMPILE_TIME_ERROR|UNDEFINED_OPERATOR|lib/_internal/js_runtime/lib/interceptors.dart|1733|18|1|The operator '&' isn't defined for the type 'JSInt'.
+ERROR|COMPILE_TIME_ERROR|UNDEFINED_OPERATOR|lib/_internal/js_runtime/lib/interceptors.dart|1733|44|1|The operator '&' isn't defined for the type 'JSInt'.
diff --git a/pkg/expect/lib/expect.dart b/pkg/expect/lib/expect.dart
index a030354..3b7a2e5 100644
--- a/pkg/expect/lib/expect.dart
+++ b/pkg/expect/lib/expect.dart
@@ -618,19 +618,6 @@
         f, (error) => error.toString().startsWith('ReachabilityError'), reason);
   }
 
-  /// Checks that [f] throws an appropriate error on a null argument.
-  ///
-  /// With sound null safety, this is expected to be a [TypeError] when casting
-  /// the `null` to some non-nullable type. In weak mode, that cast is ignored
-  /// and some later explicit validation should handle it and [ArgumentError].
-  static void throwsNullCheckError(void f()) {
-    if (hasSoundNullSafety) {
-      throwsTypeError(f);
-    } else {
-      throwsArgumentError(f);
-    }
-  }
-
   static void throwsRangeError(void f(), [String reason = "RangeError"]) {
     Expect.throws(f, (error) => error is RangeError, reason);
   }
diff --git a/pkg/front_end/lib/src/fasta/builder/field_builder.dart b/pkg/front_end/lib/src/fasta/builder/field_builder.dart
index 63f82eb..658e1d0 100644
--- a/pkg/front_end/lib/src/fasta/builder/field_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/field_builder.dart
@@ -136,7 +136,10 @@
           isCovariant: isCovariant,
           isNonNullableByDefault: library.isNonNullableByDefault);
     } else if (isLate &&
-        !libraryBuilder.loader.target.backendTarget.supportsLateFields) {
+        libraryBuilder.loader.target.backendTarget.isLateFieldLoweringEnabled(
+            hasInitializer: hasInitializer,
+            isFinal: isFinal,
+            isStatic: (isStatic || isTopLevel))) {
       if (hasInitializer) {
         if (isFinal) {
           _fieldEncoding = new LateFinalFieldWithInitializerEncoding(
@@ -229,6 +232,8 @@
     }
   }
 
+  bool get isLateLowered => _fieldEncoding.isLateLowering;
+
   bool _typeEnsured = false;
   Set<ClassMember> _overrideDependencies;
 
@@ -570,6 +575,9 @@
   /// Ensures that the signatures all members created by this field encoding
   /// are fully typed.
   void completeSignature(CoreTypes coreTypes);
+
+  /// Returns `true` if this encoding is a late lowering.
+  bool get isLateLowering;
 }
 
 class RegularFieldEncoding implements FieldEncoding {
@@ -695,6 +703,9 @@
       fieldBuilder.isAssignable
           ? <ClassMember>[new SourceFieldMember(fieldBuilder, forSetter: true)]
           : const <ClassMember>[];
+
+  @override
+  bool get isLateLowering => false;
 }
 
 class SourceFieldMember extends BuilderClassMember {
@@ -1146,6 +1157,9 @@
     }
     return list;
   }
+
+  @override
+  bool get isLateLowering => true;
 }
 
 mixin NonFinalLate on AbstractLateFieldEncoding {
@@ -1684,6 +1698,9 @@
                   forSetter: true, isInternalImplementation: false)
             ]
           : const <ClassMember>[];
+
+  @override
+  bool get isLateLowering => false;
 }
 
 enum _SynthesizedFieldMemberKind {
diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
index f64bdf2..2b8458e 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -5886,7 +5886,10 @@
       }
     }
     if (node.isLate &&
-        !inferrer.library.loader.target.backendTarget.supportsLateFields) {
+        inferrer.library.loader.target.backendTarget.isLateLocalLoweringEnabled(
+            hasInitializer: node.hasDeclaredInitializer,
+            isFinal: node.isFinal,
+            isPotentiallyNullable: node.type.isPotentiallyNullable)) {
       int fileOffset = node.fileOffset;
 
       List<Statement> result = <Statement>[];
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
index a1167d5..4cd7a7e 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -1184,8 +1184,8 @@
           name,
           isInstanceMember: fieldBuilder.isClassInstanceMember,
           className: builder.name,
-          isSynthesized: fieldBuilder.isLate &&
-              !builder.library.loader.target.backendTarget.supportsLateFields,
+          isSynthesized:
+              fieldBuilder is SourceFieldBuilder && fieldBuilder.isLateLowered,
         ));
       });
       builder.forEach((String name, Builder builder) {
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index e4d81e4..54f31d9 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -2119,7 +2119,10 @@
     Procedure getterReferenceFrom;
     Procedure setterReferenceFrom;
     final bool fieldIsLateWithLowering = (modifiers & lateMask) != 0 &&
-        !loader.target.backendTarget.supportsLateFields;
+        loader.target.backendTarget.isLateFieldLoweringEnabled(
+            hasInitializer: hasInitializer,
+            isFinal: (modifiers & finalMask) != 0,
+            isStatic: isTopLevel || (modifiers & staticMask) != 0);
     final bool isInstanceMember = currentTypeParameterScopeBuilder.kind ==
             TypeParameterScopeKind.classDeclaration &&
         (modifiers & staticMask) == 0;
diff --git a/pkg/front_end/test/fasta/testing/suite.dart b/pkg/front_end/test/fasta/testing/suite.dart
index c9d68e8..3788cba 100644
--- a/pkg/front_end/test/fasta/testing/suite.dart
+++ b/pkg/front_end/test/fasta/testing/suite.dart
@@ -105,6 +105,7 @@
         DiagnosticReporter,
         NoneConstantsBackend,
         NoneTarget,
+        LateLowering,
         Target,
         TargetFlags;
 
@@ -205,7 +206,7 @@
 /// test folders.
 class FolderOptions {
   final Map<ExperimentalFlag, bool> _explicitExperimentalFlags;
-  final bool forceLateLowering;
+  final int forceLateLowerings;
   final bool forceLateLoweringSentinel;
   final bool forceStaticFieldLowering;
   final bool forceNoExplicitGetterCalls;
@@ -216,7 +217,7 @@
   final String overwriteCurrentSdkVersion;
 
   FolderOptions(this._explicitExperimentalFlags,
-      {this.forceLateLowering: false,
+      {this.forceLateLowerings: LateLowering.none,
       this.forceLateLoweringSentinel: false,
       this.forceStaticFieldLowering: false,
       this.forceNoExplicitGetterCalls: false,
@@ -226,7 +227,7 @@
       this.target: "vm",
       // can be null
       this.overwriteCurrentSdkVersion})
-      : assert(forceLateLowering != null),
+      : assert(forceLateLowerings != null),
         assert(forceLateLoweringSentinel != null),
         assert(forceStaticFieldLowering != null),
         assert(forceNoExplicitGetterCalls != null),
@@ -376,7 +377,7 @@
   FolderOptions _computeFolderOptions(Directory directory) {
     FolderOptions folderOptions = _folderOptions[directory.uri];
     if (folderOptions == null) {
-      bool forceLateLowering = false;
+      int forceLateLowering = LateLowering.none;
       bool forceLateLoweringSentinel = false;
       bool forceStaticFieldLowering = false;
       bool forceNoExplicitGetterCalls = false;
@@ -386,7 +387,7 @@
       String target = "vm";
       if (directory.uri == baseUri) {
         folderOptions = new FolderOptions({},
-            forceLateLowering: forceLateLowering,
+            forceLateLowerings: forceLateLowering,
             forceLateLoweringSentinel: forceLateLoweringSentinel,
             forceStaticFieldLowering: forceStaticFieldLowering,
             forceNoExplicitGetterCalls: forceNoExplicitGetterCalls,
@@ -410,8 +411,12 @@
                   line.substring(overwriteCurrentSdkVersion.length);
             } else if (line.startsWith(Flags.forceLateLoweringSentinel)) {
               forceLateLoweringSentinel = true;
+            } else if (line.startsWith('${Flags.forceLateLowering}=')) {
+              int mask = int.parse(
+                  line.substring('${Flags.forceLateLowering}='.length));
+              forceLateLowering = mask;
             } else if (line.startsWith(Flags.forceLateLowering)) {
-              forceLateLowering = true;
+              forceLateLowering = LateLowering.all;
             } else if (line.startsWith(Flags.forceStaticFieldLowering)) {
               forceStaticFieldLowering = true;
             } else if (line.startsWith(Flags.forceNoExplicitGetterCalls)) {
@@ -466,7 +471,7 @@
                   onError: (String message) => throw new ArgumentError(message),
                   onWarning: (String message) =>
                       throw new ArgumentError(message)),
-              forceLateLowering: forceLateLowering,
+              forceLateLowerings: forceLateLowering,
               forceLateLoweringSentinel: forceLateLoweringSentinel,
               forceStaticFieldLowering: forceStaticFieldLowering,
               forceNoExplicitGetterCalls: forceNoExplicitGetterCalls,
@@ -743,7 +748,7 @@
         StdioProcess process;
         try {
           var args = <String>[];
-          if (experimentalFlags[ExperimentalFlag.nonNullable]) {
+          if (experimentalFlags[ExperimentalFlag.nonNullable] == true) {
             args.add("--enable-experiment=non-nullable");
             if (!context.weak) {
               args.add("--sound-null-safety");
@@ -1134,7 +1139,7 @@
     UriTranslator uriTranslator =
         await context.computeUriTranslator(description);
     TargetFlags targetFlags = new TargetFlags(
-      forceLateLoweringForTesting: testOptions.forceLateLowering,
+      forceLateLoweringsForTesting: testOptions.forceLateLowerings,
       forceLateLoweringSentinelForTesting:
           testOptions.forceLateLoweringSentinel,
       forceStaticFieldLoweringForTesting: testOptions.forceStaticFieldLowering,
diff --git a/pkg/front_end/test/incremental_load_from_dill_suite.dart b/pkg/front_end/test/incremental_load_from_dill_suite.dart
index a6237db..45cc440 100644
--- a/pkg/front_end/test/incremental_load_from_dill_suite.dart
+++ b/pkg/front_end/test/incremental_load_from_dill_suite.dart
@@ -68,7 +68,7 @@
         Version;
 
 import 'package:kernel/target/targets.dart'
-    show NoneTarget, Target, TargetFlags;
+    show NoneTarget, LateLowering, Target, TargetFlags;
 
 import 'package:kernel/text/ast_to_text.dart' show Printer, componentToString;
 
@@ -392,7 +392,8 @@
     final Uri sdkRoot = computePlatformBinariesLocation(forceBuildDir: true);
 
     TargetFlags targetFlags = new TargetFlags(
-        forceLateLoweringForTesting: forceLateLoweringForTesting,
+        forceLateLoweringsForTesting:
+            forceLateLoweringForTesting ? LateLowering.all : LateLowering.none,
         trackWidgetCreation: trackWidgetCreation);
     Target target = new VmTarget(targetFlags);
     String sdkSummary = "vm_platform_strong.dill";
diff --git a/pkg/front_end/test/lint_test.status b/pkg/front_end/test/lint_test.status
index 10f7b68..2728954 100644
--- a/pkg/front_end/test/lint_test.status
+++ b/pkg/front_end/test/lint_test.status
@@ -39,3 +39,8 @@
 front_end/lib/src/fasta/type_inference/type_constraint_gatherer/ImportsTwice: Fail
 front_end/lib/src/fasta/type_inference/type_inferrer/ImportsTwice: Fail
 front_end/lib/src/testing/id_testing_helper/Exports: Fail
+kernel/lib/ast/ImportsTwice: Fail
+kernel/lib/ast/Exports: Fail
+kernel/lib/kernel/Exports: Fail
+kernel/lib/testing/type_parser_environment/ImportsTwice: Fail
+kernel/lib/text/ast_to_text/ImportsTwice: Fail
diff --git a/pkg/front_end/test/predicates/predicate_test.dart b/pkg/front_end/test/predicates/predicate_test.dart
index 89ddb9f..d2143dd 100644
--- a/pkg/front_end/test/predicates/predicate_test.dart
+++ b/pkg/front_end/test/predicates/predicate_test.dart
@@ -25,7 +25,8 @@
             explicitExperimentalFlags: const {
               ExperimentalFlag.nonNullable: true
             },
-            targetFlags: const TargetFlags(forceLateLoweringForTesting: true))
+            targetFlags: const TargetFlags(
+                forceLateLoweringsForTesting: LateLowering.all))
       ]));
 }
 
diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt
index d50f907..204181e 100644
--- a/pkg/front_end/test/spell_checking_list_tests.txt
+++ b/pkg/front_end/test/spell_checking_list_tests.txt
@@ -55,6 +55,7 @@
 besides
 beta
 bigger
+bitmask
 bkonyi
 bla
 blah
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_22.yaml.world.1.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_22.yaml.world.1.expect
index 994228a..3749032 100644
--- a/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_22.yaml.world.1.expect
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_22.yaml.world.1.expect
@@ -479,7 +479,7 @@
       return 1.{dart.core::int::unary-}();
     }
     method /*isNonNullableByDefault, from org-dartlang-sdk:///sdk/lib/collection/list.dart */ insert(dart.core::int index, generic-covariant-impl dart.core::int* element) → void {
-      dart.core::ArgumentError::checkNotNull<dart.core::int>(index, "index");
+      dart._internal::checkNotNullable<dart.core::int>(index, "index");
       dart.core::int length = this.{dart.core::List::length};
       dart.core::RangeError::checkValueInInterval(index, 0, length, "index");
       this.{dart.collection::ListMixin::add}(element);
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_22.yaml.world.2.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_22.yaml.world.2.expect
index 994228a..3749032 100644
--- a/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_22.yaml.world.2.expect
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_22.yaml.world.2.expect
@@ -479,7 +479,7 @@
       return 1.{dart.core::int::unary-}();
     }
     method /*isNonNullableByDefault, from org-dartlang-sdk:///sdk/lib/collection/list.dart */ insert(dart.core::int index, generic-covariant-impl dart.core::int* element) → void {
-      dart.core::ArgumentError::checkNotNull<dart.core::int>(index, "index");
+      dart._internal::checkNotNullable<dart.core::int>(index, "index");
       dart.core::int length = this.{dart.core::List::length};
       dart.core::RangeError::checkValueInInterval(index, 0, length, "index");
       this.{dart.collection::ListMixin::add}(element);
diff --git a/pkg/front_end/testcases/late_lowering/late_lowering_bitmasks.dart b/pkg/front_end/testcases/late_lowering/late_lowering_bitmasks.dart
new file mode 100644
index 0000000..b5a6079
--- /dev/null
+++ b/pkg/front_end/testcases/late_lowering/late_lowering_bitmasks.dart
@@ -0,0 +1,36 @@
+// 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.
+
+// A declaration for each kind of late field/local lowering enabled by
+// target bitmask.
+
+main() {}
+
+method() {
+  late int? nullableUninitializedNonFinalLocal;
+  late int nonNullableUninitializedNonFinalLocal;
+  late final int? nullableUninitializedFinalLocal;
+  late final int nonNullableUninitializedFinalLocal;
+  late int? nullableInitializedNonFinalLocal = 0;
+  late int nonNullableInitializedNonFinalLocal = 0;
+  late final int? nullableInitializedFinalLocal = 0;
+  late final int nonNullableInitializedFinalLocal = 0;
+}
+
+late int uninitializedNonFinalTopLevelField;
+late final int uninitializedFinalTopLevelField;
+late int initializedNonFinalTopLevelField = 0;
+late final int initializedFinalTopLevelField = 0;
+
+class Class {
+  static late int uninitializedNonFinalStaticField;
+  static late final int uninitializedFinalStaticField;
+  static late int initializedNonFinalStaticField = 0;
+  static late final int initializedFinalStaticField = 0;
+
+  late int uninitializedNonFinalInstanceField;
+  late final int uninitializedFinalInstanceField;
+  late int initializedNonFinalInstanceField = 0;
+  late final int initializedFinalInstanceField = 0;
+}
diff --git a/pkg/front_end/testcases/late_lowering/late_lowering_bitmasks.dart.outline.expect b/pkg/front_end/testcases/late_lowering/late_lowering_bitmasks.dart.outline.expect
new file mode 100644
index 0000000..9c49adf
--- /dev/null
+++ b/pkg/front_end/testcases/late_lowering/late_lowering_bitmasks.dart.outline.expect
@@ -0,0 +1,45 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Class extends core::Object {
+  static field core::int? _#uninitializedNonFinalStaticField;
+  static field core::int? _#uninitializedFinalStaticField;
+  static field core::int? _#initializedNonFinalStaticField;
+  static field core::int? _#initializedFinalStaticField;
+  field core::int? _#Class#uninitializedNonFinalInstanceField;
+  field core::int? _#Class#uninitializedFinalInstanceField;
+  field core::int? _#Class#initializedNonFinalInstanceField;
+  field core::int? _#Class#initializedFinalInstanceField;
+  synthetic constructor •() → self::Class
+    ;
+  static get uninitializedNonFinalStaticField() → core::int;
+  static set uninitializedNonFinalStaticField(core::int #t1) → void;
+  static get uninitializedFinalStaticField() → core::int;
+  static set uninitializedFinalStaticField(core::int #t2) → void;
+  static get initializedNonFinalStaticField() → core::int;
+  static set initializedNonFinalStaticField(core::int #t3) → void;
+  static get initializedFinalStaticField() → core::int;
+  get uninitializedNonFinalInstanceField() → core::int;
+  set uninitializedNonFinalInstanceField(core::int #t4) → void;
+  get uninitializedFinalInstanceField() → core::int;
+  set uninitializedFinalInstanceField(core::int #t5) → void;
+  get initializedNonFinalInstanceField() → core::int;
+  set initializedNonFinalInstanceField(core::int #t6) → void;
+  get initializedFinalInstanceField() → core::int;
+}
+static field core::int? _#uninitializedNonFinalTopLevelField;
+static field core::int? _#uninitializedFinalTopLevelField;
+static field core::int? _#initializedNonFinalTopLevelField;
+static field core::int? _#initializedFinalTopLevelField;
+static method main() → dynamic
+  ;
+static method method() → dynamic
+  ;
+static get uninitializedNonFinalTopLevelField() → core::int;
+static set uninitializedNonFinalTopLevelField(core::int #t7) → void;
+static get uninitializedFinalTopLevelField() → core::int;
+static set uninitializedFinalTopLevelField(core::int #t8) → void;
+static get initializedNonFinalTopLevelField() → core::int;
+static set initializedNonFinalTopLevelField(core::int #t9) → void;
+static get initializedFinalTopLevelField() → core::int;
diff --git a/pkg/front_end/testcases/late_lowering/late_lowering_bitmasks.dart.strong.expect b/pkg/front_end/testcases/late_lowering/late_lowering_bitmasks.dart.strong.expect
new file mode 100644
index 0000000..b67f124
--- /dev/null
+++ b/pkg/front_end/testcases/late_lowering/late_lowering_bitmasks.dart.strong.expect
@@ -0,0 +1,138 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:_internal" as _in;
+
+class Class extends core::Object {
+  static field core::int? _#uninitializedNonFinalStaticField = null;
+  static field core::int? _#uninitializedFinalStaticField = null;
+  static field core::int? _#initializedNonFinalStaticField = null;
+  static field core::int? _#initializedFinalStaticField = null;
+  field core::int? _#Class#uninitializedNonFinalInstanceField = null;
+  field core::int? _#Class#uninitializedFinalInstanceField = null;
+  field core::int? _#Class#initializedNonFinalInstanceField = null;
+  field core::int? _#Class#initializedFinalInstanceField = null;
+  synthetic constructor •() → self::Class
+    : super core::Object::•()
+    ;
+  static get uninitializedNonFinalStaticField() → core::int
+    return let final core::int? #t1 = self::Class::_#uninitializedNonFinalStaticField in #t1.==(null) ?{core::int} throw new _in::LateError::fieldNI("uninitializedNonFinalStaticField") : #t1{core::int};
+  static set uninitializedNonFinalStaticField(core::int #t2) → void
+    self::Class::_#uninitializedNonFinalStaticField = #t2;
+  static get uninitializedFinalStaticField() → core::int
+    return let final core::int? #t3 = self::Class::_#uninitializedFinalStaticField in #t3.==(null) ?{core::int} throw new _in::LateError::fieldNI("uninitializedFinalStaticField") : #t3{core::int};
+  static set uninitializedFinalStaticField(core::int #t4) → void
+    if(self::Class::_#uninitializedFinalStaticField.==(null))
+      self::Class::_#uninitializedFinalStaticField = #t4;
+    else
+      throw new _in::LateError::fieldAI("uninitializedFinalStaticField");
+  static get initializedNonFinalStaticField() → core::int
+    return let final core::int? #t5 = self::Class::_#initializedNonFinalStaticField in #t5.==(null) ?{core::int} self::Class::_#initializedNonFinalStaticField = 0 : #t5{core::int};
+  static set initializedNonFinalStaticField(core::int #t6) → void
+    self::Class::_#initializedNonFinalStaticField = #t6;
+  static get initializedFinalStaticField() → core::int
+    return let final core::int? #t7 = self::Class::_#initializedFinalStaticField in #t7.==(null) ?{core::int} let final core::int #t8 = 0 in self::Class::_#initializedFinalStaticField.==(null) ?{core::int} self::Class::_#initializedFinalStaticField = #t8 : throw new _in::LateError::fieldADI("initializedFinalStaticField") : #t7{core::int};
+  get uninitializedNonFinalInstanceField() → core::int
+    return let final core::int? #t9 = this.{self::Class::_#Class#uninitializedNonFinalInstanceField} in #t9.==(null) ?{core::int} throw new _in::LateError::fieldNI("uninitializedNonFinalInstanceField") : #t9{core::int};
+  set uninitializedNonFinalInstanceField(core::int #t10) → void
+    this.{self::Class::_#Class#uninitializedNonFinalInstanceField} = #t10;
+  get uninitializedFinalInstanceField() → core::int
+    return let final core::int? #t11 = this.{self::Class::_#Class#uninitializedFinalInstanceField} in #t11.==(null) ?{core::int} throw new _in::LateError::fieldNI("uninitializedFinalInstanceField") : #t11{core::int};
+  set uninitializedFinalInstanceField(core::int #t12) → void
+    if(this.{self::Class::_#Class#uninitializedFinalInstanceField}.==(null))
+      this.{self::Class::_#Class#uninitializedFinalInstanceField} = #t12;
+    else
+      throw new _in::LateError::fieldAI("uninitializedFinalInstanceField");
+  get initializedNonFinalInstanceField() → core::int
+    return let final core::int? #t13 = this.{self::Class::_#Class#initializedNonFinalInstanceField} in #t13.==(null) ?{core::int} this.{self::Class::_#Class#initializedNonFinalInstanceField} = 0 : #t13{core::int};
+  set initializedNonFinalInstanceField(core::int #t14) → void
+    this.{self::Class::_#Class#initializedNonFinalInstanceField} = #t14;
+  get initializedFinalInstanceField() → core::int
+    return let final core::int? #t15 = this.{self::Class::_#Class#initializedFinalInstanceField} in #t15.==(null) ?{core::int} let final core::int #t16 = 0 in this.{self::Class::_#Class#initializedFinalInstanceField}.==(null) ?{core::int} this.{self::Class::_#Class#initializedFinalInstanceField} = #t16 : throw new _in::LateError::fieldADI("initializedFinalInstanceField") : #t15{core::int};
+}
+static field core::int? _#uninitializedNonFinalTopLevelField = null;
+static field core::int? _#uninitializedFinalTopLevelField = null;
+static field core::int? _#initializedNonFinalTopLevelField = null;
+static field core::int? _#initializedFinalTopLevelField = null;
+static method main() → dynamic {}
+static method method() → dynamic {
+  core::int? nullableUninitializedNonFinalLocal;
+  core::bool #nullableUninitializedNonFinalLocal#isSet = false;
+  function #nullableUninitializedNonFinalLocal#get() → core::int?
+    return #nullableUninitializedNonFinalLocal#isSet ?{core::int?} nullableUninitializedNonFinalLocal : throw new _in::LateError::localNI("nullableUninitializedNonFinalLocal");
+  function #nullableUninitializedNonFinalLocal#set(core::int? #t17) → dynamic {
+    #nullableUninitializedNonFinalLocal#isSet = true;
+    return nullableUninitializedNonFinalLocal = #t17;
+  }
+  core::int? nonNullableUninitializedNonFinalLocal;
+  function #nonNullableUninitializedNonFinalLocal#get() → core::int
+    return let final core::int? #t18 = nonNullableUninitializedNonFinalLocal in #t18.==(null) ?{core::int} throw new _in::LateError::localNI("nonNullableUninitializedNonFinalLocal") : #t18{core::int};
+  function #nonNullableUninitializedNonFinalLocal#set(core::int #t19) → dynamic
+    return nonNullableUninitializedNonFinalLocal = #t19;
+  final core::int? nullableUninitializedFinalLocal;
+  core::bool #nullableUninitializedFinalLocal#isSet = false;
+  function #nullableUninitializedFinalLocal#get() → core::int?
+    return #nullableUninitializedFinalLocal#isSet ?{core::int?} nullableUninitializedFinalLocal : throw new _in::LateError::localNI("nullableUninitializedFinalLocal");
+  function #nullableUninitializedFinalLocal#set(core::int? #t20) → dynamic
+    if(#nullableUninitializedFinalLocal#isSet)
+      throw new _in::LateError::localAI("nullableUninitializedFinalLocal");
+    else {
+      #nullableUninitializedFinalLocal#isSet = true;
+      return nullableUninitializedFinalLocal = #t20;
+    }
+  final core::int? nonNullableUninitializedFinalLocal;
+  function #nonNullableUninitializedFinalLocal#get() → core::int
+    return let final core::int? #t21 = nonNullableUninitializedFinalLocal in #t21.==(null) ?{core::int} throw new _in::LateError::localNI("nonNullableUninitializedFinalLocal") : #t21{core::int};
+  function #nonNullableUninitializedFinalLocal#set(core::int #t22) → dynamic
+    if(nonNullableUninitializedFinalLocal.==(null))
+      return nonNullableUninitializedFinalLocal = #t22;
+    else
+      throw new _in::LateError::localAI("nonNullableUninitializedFinalLocal");
+  core::int? nullableInitializedNonFinalLocal;
+  core::bool #nullableInitializedNonFinalLocal#isSet = false;
+  function #nullableInitializedNonFinalLocal#get() → core::int? {
+    if(!#nullableInitializedNonFinalLocal#isSet) {
+      nullableInitializedNonFinalLocal = 0;
+      #nullableInitializedNonFinalLocal#isSet = true;
+    }
+    return nullableInitializedNonFinalLocal;
+  }
+  function #nullableInitializedNonFinalLocal#set(core::int? #t23) → dynamic {
+    #nullableInitializedNonFinalLocal#isSet = true;
+    return nullableInitializedNonFinalLocal = #t23;
+  }
+  core::int? nonNullableInitializedNonFinalLocal;
+  function #nonNullableInitializedNonFinalLocal#get() → core::int
+    return let final core::int? #t24 = nonNullableInitializedNonFinalLocal in #t24.==(null) ?{core::int} nonNullableInitializedNonFinalLocal = 0 : #t24{core::int};
+  function #nonNullableInitializedNonFinalLocal#set(core::int #t25) → dynamic
+    return nonNullableInitializedNonFinalLocal = #t25;
+  final core::int? nullableInitializedFinalLocal;
+  core::bool #nullableInitializedFinalLocal#isSet = false;
+  function #nullableInitializedFinalLocal#get() → core::int? {
+    if(!#nullableInitializedFinalLocal#isSet) {
+      nullableInitializedFinalLocal = 0;
+      #nullableInitializedFinalLocal#isSet = true;
+    }
+    return nullableInitializedFinalLocal;
+  }
+  final core::int? nonNullableInitializedFinalLocal;
+  function #nonNullableInitializedFinalLocal#get() → core::int
+    return let final core::int? #t26 = nonNullableInitializedFinalLocal in #t26.==(null) ?{core::int} nonNullableInitializedFinalLocal = 0 : #t26{core::int};
+}
+static get uninitializedNonFinalTopLevelField() → core::int
+  return let final core::int? #t27 = self::_#uninitializedNonFinalTopLevelField in #t27.==(null) ?{core::int} throw new _in::LateError::fieldNI("uninitializedNonFinalTopLevelField") : #t27{core::int};
+static set uninitializedNonFinalTopLevelField(core::int #t28) → void
+  self::_#uninitializedNonFinalTopLevelField = #t28;
+static get uninitializedFinalTopLevelField() → core::int
+  return let final core::int? #t29 = self::_#uninitializedFinalTopLevelField in #t29.==(null) ?{core::int} throw new _in::LateError::fieldNI("uninitializedFinalTopLevelField") : #t29{core::int};
+static set uninitializedFinalTopLevelField(core::int #t30) → void
+  if(self::_#uninitializedFinalTopLevelField.==(null))
+    self::_#uninitializedFinalTopLevelField = #t30;
+  else
+    throw new _in::LateError::fieldAI("uninitializedFinalTopLevelField");
+static get initializedNonFinalTopLevelField() → core::int
+  return let final core::int? #t31 = self::_#initializedNonFinalTopLevelField in #t31.==(null) ?{core::int} self::_#initializedNonFinalTopLevelField = 0 : #t31{core::int};
+static set initializedNonFinalTopLevelField(core::int #t32) → void
+  self::_#initializedNonFinalTopLevelField = #t32;
+static get initializedFinalTopLevelField() → core::int
+  return let final core::int? #t33 = self::_#initializedFinalTopLevelField in #t33.==(null) ?{core::int} let final core::int #t34 = 0 in self::_#initializedFinalTopLevelField.==(null) ?{core::int} self::_#initializedFinalTopLevelField = #t34 : throw new _in::LateError::fieldADI("initializedFinalTopLevelField") : #t33{core::int};
diff --git a/pkg/front_end/testcases/late_lowering/late_lowering_bitmasks.dart.strong.transformed.expect b/pkg/front_end/testcases/late_lowering/late_lowering_bitmasks.dart.strong.transformed.expect
new file mode 100644
index 0000000..b3a9855
--- /dev/null
+++ b/pkg/front_end/testcases/late_lowering/late_lowering_bitmasks.dart.strong.transformed.expect
@@ -0,0 +1,145 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:_internal" as _in;
+
+class Class extends core::Object {
+  static field core::int? _#uninitializedNonFinalStaticField = null;
+  static field core::int? _#uninitializedFinalStaticField = null;
+  static field core::int? _#initializedNonFinalStaticField = null;
+  static field core::int? _#initializedFinalStaticField = null;
+  field core::int? _#Class#uninitializedNonFinalInstanceField = null;
+  field core::int? _#Class#uninitializedFinalInstanceField = null;
+  field core::int? _#Class#initializedNonFinalInstanceField = null;
+  field core::int? _#Class#initializedFinalInstanceField = null;
+  synthetic constructor •() → self::Class
+    : super core::Object::•()
+    ;
+  static get uninitializedNonFinalStaticField() → core::int
+    return let final core::int? #t1 = self::Class::_#uninitializedNonFinalStaticField in #t1.==(null) ?{core::int} throw new _in::LateError::fieldNI("uninitializedNonFinalStaticField") : #t1{core::int};
+  static set uninitializedNonFinalStaticField(core::int #t2) → void
+    self::Class::_#uninitializedNonFinalStaticField = #t2;
+  static get uninitializedFinalStaticField() → core::int
+    return let final core::int? #t3 = self::Class::_#uninitializedFinalStaticField in #t3.==(null) ?{core::int} throw new _in::LateError::fieldNI("uninitializedFinalStaticField") : #t3{core::int};
+  static set uninitializedFinalStaticField(core::int #t4) → void
+    if(self::Class::_#uninitializedFinalStaticField.==(null))
+      self::Class::_#uninitializedFinalStaticField = #t4;
+    else
+      throw new _in::LateError::fieldAI("uninitializedFinalStaticField");
+  static get initializedNonFinalStaticField() → core::int
+    return let final core::int? #t5 = self::Class::_#initializedNonFinalStaticField in #t5.==(null) ?{core::int} self::Class::_#initializedNonFinalStaticField = 0 : #t5{core::int};
+  static set initializedNonFinalStaticField(core::int #t6) → void
+    self::Class::_#initializedNonFinalStaticField = #t6;
+  static get initializedFinalStaticField() → core::int
+    return let final core::int? #t7 = self::Class::_#initializedFinalStaticField in #t7.==(null) ?{core::int} let final core::int #t8 = 0 in self::Class::_#initializedFinalStaticField.==(null) ?{core::int} self::Class::_#initializedFinalStaticField = #t8 : throw new _in::LateError::fieldADI("initializedFinalStaticField") : #t7{core::int};
+  get uninitializedNonFinalInstanceField() → core::int
+    return let final core::int? #t9 = this.{self::Class::_#Class#uninitializedNonFinalInstanceField} in #t9.==(null) ?{core::int} throw new _in::LateError::fieldNI("uninitializedNonFinalInstanceField") : #t9{core::int};
+  set uninitializedNonFinalInstanceField(core::int #t10) → void
+    this.{self::Class::_#Class#uninitializedNonFinalInstanceField} = #t10;
+  get uninitializedFinalInstanceField() → core::int
+    return let final core::int? #t11 = this.{self::Class::_#Class#uninitializedFinalInstanceField} in #t11.==(null) ?{core::int} throw new _in::LateError::fieldNI("uninitializedFinalInstanceField") : #t11{core::int};
+  set uninitializedFinalInstanceField(core::int #t12) → void
+    if(this.{self::Class::_#Class#uninitializedFinalInstanceField}.==(null))
+      this.{self::Class::_#Class#uninitializedFinalInstanceField} = #t12;
+    else
+      throw new _in::LateError::fieldAI("uninitializedFinalInstanceField");
+  get initializedNonFinalInstanceField() → core::int
+    return let final core::int? #t13 = this.{self::Class::_#Class#initializedNonFinalInstanceField} in #t13.==(null) ?{core::int} this.{self::Class::_#Class#initializedNonFinalInstanceField} = 0 : #t13{core::int};
+  set initializedNonFinalInstanceField(core::int #t14) → void
+    this.{self::Class::_#Class#initializedNonFinalInstanceField} = #t14;
+  get initializedFinalInstanceField() → core::int
+    return let final core::int? #t15 = this.{self::Class::_#Class#initializedFinalInstanceField} in #t15.==(null) ?{core::int} let final core::int #t16 = 0 in this.{self::Class::_#Class#initializedFinalInstanceField}.==(null) ?{core::int} this.{self::Class::_#Class#initializedFinalInstanceField} = #t16 : throw new _in::LateError::fieldADI("initializedFinalInstanceField") : #t15{core::int};
+}
+static field core::int? _#uninitializedNonFinalTopLevelField = null;
+static field core::int? _#uninitializedFinalTopLevelField = null;
+static field core::int? _#initializedNonFinalTopLevelField = null;
+static field core::int? _#initializedFinalTopLevelField = null;
+static method main() → dynamic {}
+static method method() → dynamic {
+  core::int? nullableUninitializedNonFinalLocal;
+  core::bool #nullableUninitializedNonFinalLocal#isSet = false;
+  function #nullableUninitializedNonFinalLocal#get() → core::int?
+    return #nullableUninitializedNonFinalLocal#isSet ?{core::int?} nullableUninitializedNonFinalLocal : throw new _in::LateError::localNI("nullableUninitializedNonFinalLocal");
+  function #nullableUninitializedNonFinalLocal#set(core::int? #t17) → dynamic {
+    #nullableUninitializedNonFinalLocal#isSet = true;
+    return nullableUninitializedNonFinalLocal = #t17;
+  }
+  core::int? nonNullableUninitializedNonFinalLocal;
+  function #nonNullableUninitializedNonFinalLocal#get() → core::int
+    return let final core::int? #t18 = nonNullableUninitializedNonFinalLocal in #t18.==(null) ?{core::int} throw new _in::LateError::localNI("nonNullableUninitializedNonFinalLocal") : #t18{core::int};
+  function #nonNullableUninitializedNonFinalLocal#set(core::int #t19) → dynamic
+    return nonNullableUninitializedNonFinalLocal = #t19;
+  final core::int? nullableUninitializedFinalLocal;
+  core::bool #nullableUninitializedFinalLocal#isSet = false;
+  function #nullableUninitializedFinalLocal#get() → core::int?
+    return #nullableUninitializedFinalLocal#isSet ?{core::int?} nullableUninitializedFinalLocal : throw new _in::LateError::localNI("nullableUninitializedFinalLocal");
+  function #nullableUninitializedFinalLocal#set(core::int? #t20) → dynamic
+    if(#nullableUninitializedFinalLocal#isSet)
+      throw new _in::LateError::localAI("nullableUninitializedFinalLocal");
+    else {
+      #nullableUninitializedFinalLocal#isSet = true;
+      return nullableUninitializedFinalLocal = #t20;
+    }
+  final core::int? nonNullableUninitializedFinalLocal;
+  function #nonNullableUninitializedFinalLocal#get() → core::int
+    return let final core::int? #t21 = nonNullableUninitializedFinalLocal in #t21.==(null) ?{core::int} throw new _in::LateError::localNI("nonNullableUninitializedFinalLocal") : #t21{core::int};
+  function #nonNullableUninitializedFinalLocal#set(core::int #t22) → dynamic
+    if(nonNullableUninitializedFinalLocal.==(null))
+      return nonNullableUninitializedFinalLocal = #t22;
+    else
+      throw new _in::LateError::localAI("nonNullableUninitializedFinalLocal");
+  core::int? nullableInitializedNonFinalLocal;
+  core::bool #nullableInitializedNonFinalLocal#isSet = false;
+  function #nullableInitializedNonFinalLocal#get() → core::int? {
+    if(!#nullableInitializedNonFinalLocal#isSet) {
+      nullableInitializedNonFinalLocal = 0;
+      #nullableInitializedNonFinalLocal#isSet = true;
+    }
+    return nullableInitializedNonFinalLocal;
+  }
+  function #nullableInitializedNonFinalLocal#set(core::int? #t23) → dynamic {
+    #nullableInitializedNonFinalLocal#isSet = true;
+    return nullableInitializedNonFinalLocal = #t23;
+  }
+  core::int? nonNullableInitializedNonFinalLocal;
+  function #nonNullableInitializedNonFinalLocal#get() → core::int
+    return let final core::int? #t24 = nonNullableInitializedNonFinalLocal in #t24.==(null) ?{core::int} nonNullableInitializedNonFinalLocal = 0 : #t24{core::int};
+  function #nonNullableInitializedNonFinalLocal#set(core::int #t25) → dynamic
+    return nonNullableInitializedNonFinalLocal = #t25;
+  final core::int? nullableInitializedFinalLocal;
+  core::bool #nullableInitializedFinalLocal#isSet = false;
+  function #nullableInitializedFinalLocal#get() → core::int? {
+    if(!#nullableInitializedFinalLocal#isSet) {
+      nullableInitializedFinalLocal = 0;
+      #nullableInitializedFinalLocal#isSet = true;
+    }
+    return nullableInitializedFinalLocal;
+  }
+  final core::int? nonNullableInitializedFinalLocal;
+  function #nonNullableInitializedFinalLocal#get() → core::int
+    return let final core::int? #t26 = nonNullableInitializedFinalLocal in #t26.==(null) ?{core::int} nonNullableInitializedFinalLocal = 0 : #t26{core::int};
+}
+static get uninitializedNonFinalTopLevelField() → core::int
+  return let final core::int? #t27 = self::_#uninitializedNonFinalTopLevelField in #t27.==(null) ?{core::int} throw new _in::LateError::fieldNI("uninitializedNonFinalTopLevelField") : #t27{core::int};
+static set uninitializedNonFinalTopLevelField(core::int #t28) → void
+  self::_#uninitializedNonFinalTopLevelField = #t28;
+static get uninitializedFinalTopLevelField() → core::int
+  return let final core::int? #t29 = self::_#uninitializedFinalTopLevelField in #t29.==(null) ?{core::int} throw new _in::LateError::fieldNI("uninitializedFinalTopLevelField") : #t29{core::int};
+static set uninitializedFinalTopLevelField(core::int #t30) → void
+  if(self::_#uninitializedFinalTopLevelField.==(null))
+    self::_#uninitializedFinalTopLevelField = #t30;
+  else
+    throw new _in::LateError::fieldAI("uninitializedFinalTopLevelField");
+static get initializedNonFinalTopLevelField() → core::int
+  return let final core::int? #t31 = self::_#initializedNonFinalTopLevelField in #t31.==(null) ?{core::int} self::_#initializedNonFinalTopLevelField = 0 : #t31{core::int};
+static set initializedNonFinalTopLevelField(core::int #t32) → void
+  self::_#initializedNonFinalTopLevelField = #t32;
+static get initializedFinalTopLevelField() → core::int
+  return let final core::int? #t33 = self::_#initializedFinalTopLevelField in #t33.==(null) ?{core::int} let final core::int #t34 = 0 in self::_#initializedFinalTopLevelField.==(null) ?{core::int} self::_#initializedFinalTopLevelField = #t34 : throw new _in::LateError::fieldADI("initializedFinalTopLevelField") : #t33{core::int};
+
+
+Extra constant evaluation status:
+Evaluated: VariableGet @ org-dartlang-testcase:///late_lowering_bitmasks.dart:30:25 -> IntConstant(0)
+Evaluated: VariableGet @ org-dartlang-testcase:///late_lowering_bitmasks.dart:35:18 -> IntConstant(0)
+Evaluated: VariableGet @ org-dartlang-testcase:///late_lowering_bitmasks.dart:24:16 -> IntConstant(0)
+Extra constant evaluation: evaluated: 224, effectively constant: 3
diff --git a/pkg/front_end/testcases/late_lowering/late_lowering_bitmasks.dart.textual_outline.expect b/pkg/front_end/testcases/late_lowering/late_lowering_bitmasks.dart.textual_outline.expect
new file mode 100644
index 0000000..83c95c1
--- /dev/null
+++ b/pkg/front_end/testcases/late_lowering/late_lowering_bitmasks.dart.textual_outline.expect
@@ -0,0 +1,28 @@
+main() {}
+method() {}
+late int ;
+uninitializedNonFinalTopLevelField;
+late ;
+final int uninitializedFinalTopLevelField;
+late int ;
+initializedNonFinalTopLevelField = 0;
+late ;
+final int initializedFinalTopLevelField = 0;
+class Class {
+  static late int ;
+  uninitializedNonFinalStaticField;
+  static late ;
+  final int uninitializedFinalStaticField;
+  static late int ;
+  initializedNonFinalStaticField = 0;
+  static late ;
+  final int initializedFinalStaticField = 0;
+  late int ;
+  uninitializedNonFinalInstanceField;
+  late ;
+  final int uninitializedFinalInstanceField;
+  late int ;
+  initializedNonFinalInstanceField = 0;
+  late ;
+  final int initializedFinalInstanceField = 0;
+}
diff --git a/pkg/front_end/testcases/late_lowering/late_lowering_bitmasks.dart.weak.expect b/pkg/front_end/testcases/late_lowering/late_lowering_bitmasks.dart.weak.expect
new file mode 100644
index 0000000..0bc36f5
--- /dev/null
+++ b/pkg/front_end/testcases/late_lowering/late_lowering_bitmasks.dart.weak.expect
@@ -0,0 +1,227 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:_internal" as _in;
+
+class Class extends core::Object {
+  static field core::int? _#uninitializedNonFinalStaticField = null;
+  static field core::bool _#uninitializedNonFinalStaticField#isSet = false;
+  static field core::int? _#uninitializedFinalStaticField = null;
+  static field core::bool _#uninitializedFinalStaticField#isSet = false;
+  static field core::int? _#initializedNonFinalStaticField = null;
+  static field core::bool _#initializedNonFinalStaticField#isSet = false;
+  static field core::int? _#initializedFinalStaticField = null;
+  static field core::bool _#initializedFinalStaticField#isSet = false;
+  field core::int? _#Class#uninitializedNonFinalInstanceField = null;
+  field core::bool _#Class#uninitializedNonFinalInstanceField#isSet = false;
+  field core::int? _#Class#uninitializedFinalInstanceField = null;
+  field core::bool _#Class#uninitializedFinalInstanceField#isSet = false;
+  field core::int? _#Class#initializedNonFinalInstanceField = null;
+  field core::bool _#Class#initializedNonFinalInstanceField#isSet = false;
+  field core::int? _#Class#initializedFinalInstanceField = null;
+  field core::bool _#Class#initializedFinalInstanceField#isSet = false;
+  synthetic constructor •() → self::Class
+    : super core::Object::•()
+    ;
+  static get uninitializedNonFinalStaticField() → core::int
+    return self::Class::_#uninitializedNonFinalStaticField#isSet ?{core::int} let final core::int? #t1 = self::Class::_#uninitializedNonFinalStaticField in #t1{core::int} : throw new _in::LateError::fieldNI("uninitializedNonFinalStaticField");
+  static set uninitializedNonFinalStaticField(core::int #t2) → void {
+    self::Class::_#uninitializedNonFinalStaticField#isSet = true;
+    self::Class::_#uninitializedNonFinalStaticField = #t2;
+  }
+  static get uninitializedFinalStaticField() → core::int
+    return self::Class::_#uninitializedFinalStaticField#isSet ?{core::int} let final core::int? #t3 = self::Class::_#uninitializedFinalStaticField in #t3{core::int} : throw new _in::LateError::fieldNI("uninitializedFinalStaticField");
+  static set uninitializedFinalStaticField(core::int #t4) → void
+    if(self::Class::_#uninitializedFinalStaticField#isSet)
+      throw new _in::LateError::fieldAI("uninitializedFinalStaticField");
+    else {
+      self::Class::_#uninitializedFinalStaticField#isSet = true;
+      self::Class::_#uninitializedFinalStaticField = #t4;
+    }
+  static get initializedNonFinalStaticField() → core::int {
+    if(!self::Class::_#initializedNonFinalStaticField#isSet) {
+      self::Class::_#initializedNonFinalStaticField = 0;
+      self::Class::_#initializedNonFinalStaticField#isSet = true;
+    }
+    return let final core::int? #t5 = self::Class::_#initializedNonFinalStaticField in #t5{core::int};
+  }
+  static set initializedNonFinalStaticField(core::int #t6) → void {
+    self::Class::_#initializedNonFinalStaticField#isSet = true;
+    self::Class::_#initializedNonFinalStaticField = #t6;
+  }
+  static get initializedFinalStaticField() → core::int {
+    if(!self::Class::_#initializedFinalStaticField#isSet) {
+      final core::int #t7 = 0;
+      if(self::Class::_#initializedFinalStaticField#isSet)
+        throw new _in::LateError::fieldADI("initializedFinalStaticField");
+      self::Class::_#initializedFinalStaticField = #t7;
+      self::Class::_#initializedFinalStaticField#isSet = true;
+    }
+    return let final core::int? #t8 = self::Class::_#initializedFinalStaticField in #t8{core::int};
+  }
+  get uninitializedNonFinalInstanceField() → core::int
+    return this.{self::Class::_#Class#uninitializedNonFinalInstanceField#isSet} ?{core::int} let final core::int? #t9 = this.{self::Class::_#Class#uninitializedNonFinalInstanceField} in #t9{core::int} : throw new _in::LateError::fieldNI("uninitializedNonFinalInstanceField");
+  set uninitializedNonFinalInstanceField(core::int #t10) → void {
+    this.{self::Class::_#Class#uninitializedNonFinalInstanceField#isSet} = true;
+    this.{self::Class::_#Class#uninitializedNonFinalInstanceField} = #t10;
+  }
+  get uninitializedFinalInstanceField() → core::int
+    return this.{self::Class::_#Class#uninitializedFinalInstanceField#isSet} ?{core::int} let final core::int? #t11 = this.{self::Class::_#Class#uninitializedFinalInstanceField} in #t11{core::int} : throw new _in::LateError::fieldNI("uninitializedFinalInstanceField");
+  set uninitializedFinalInstanceField(core::int #t12) → void
+    if(this.{self::Class::_#Class#uninitializedFinalInstanceField#isSet})
+      throw new _in::LateError::fieldAI("uninitializedFinalInstanceField");
+    else {
+      this.{self::Class::_#Class#uninitializedFinalInstanceField#isSet} = true;
+      this.{self::Class::_#Class#uninitializedFinalInstanceField} = #t12;
+    }
+  get initializedNonFinalInstanceField() → core::int {
+    if(!this.{self::Class::_#Class#initializedNonFinalInstanceField#isSet}) {
+      this.{self::Class::_#Class#initializedNonFinalInstanceField} = 0;
+      this.{self::Class::_#Class#initializedNonFinalInstanceField#isSet} = true;
+    }
+    return let final core::int? #t13 = this.{self::Class::_#Class#initializedNonFinalInstanceField} in #t13{core::int};
+  }
+  set initializedNonFinalInstanceField(core::int #t14) → void {
+    this.{self::Class::_#Class#initializedNonFinalInstanceField#isSet} = true;
+    this.{self::Class::_#Class#initializedNonFinalInstanceField} = #t14;
+  }
+  get initializedFinalInstanceField() → core::int {
+    if(!this.{self::Class::_#Class#initializedFinalInstanceField#isSet}) {
+      final core::int #t15 = 0;
+      if(this.{self::Class::_#Class#initializedFinalInstanceField#isSet})
+        throw new _in::LateError::fieldADI("initializedFinalInstanceField");
+      this.{self::Class::_#Class#initializedFinalInstanceField} = #t15;
+      this.{self::Class::_#Class#initializedFinalInstanceField#isSet} = true;
+    }
+    return let final core::int? #t16 = this.{self::Class::_#Class#initializedFinalInstanceField} in #t16{core::int};
+  }
+}
+static field core::int? _#uninitializedNonFinalTopLevelField = null;
+static field core::bool _#uninitializedNonFinalTopLevelField#isSet = false;
+static field core::int? _#uninitializedFinalTopLevelField = null;
+static field core::bool _#uninitializedFinalTopLevelField#isSet = false;
+static field core::int? _#initializedNonFinalTopLevelField = null;
+static field core::bool _#initializedNonFinalTopLevelField#isSet = false;
+static field core::int? _#initializedFinalTopLevelField = null;
+static field core::bool _#initializedFinalTopLevelField#isSet = false;
+static method main() → dynamic {}
+static method method() → dynamic {
+  core::int? nullableUninitializedNonFinalLocal;
+  core::bool #nullableUninitializedNonFinalLocal#isSet = false;
+  function #nullableUninitializedNonFinalLocal#get() → core::int?
+    return #nullableUninitializedNonFinalLocal#isSet ?{core::int?} nullableUninitializedNonFinalLocal : throw new _in::LateError::localNI("nullableUninitializedNonFinalLocal");
+  function #nullableUninitializedNonFinalLocal#set(core::int? #t17) → dynamic {
+    #nullableUninitializedNonFinalLocal#isSet = true;
+    return nullableUninitializedNonFinalLocal = #t17;
+  }
+  core::int? nonNullableUninitializedNonFinalLocal;
+  core::bool #nonNullableUninitializedNonFinalLocal#isSet = false;
+  function #nonNullableUninitializedNonFinalLocal#get() → core::int
+    return #nonNullableUninitializedNonFinalLocal#isSet ?{core::int} nonNullableUninitializedNonFinalLocal{core::int} : throw new _in::LateError::localNI("nonNullableUninitializedNonFinalLocal");
+  function #nonNullableUninitializedNonFinalLocal#set(core::int #t18) → dynamic {
+    #nonNullableUninitializedNonFinalLocal#isSet = true;
+    return nonNullableUninitializedNonFinalLocal = #t18;
+  }
+  final core::int? nullableUninitializedFinalLocal;
+  core::bool #nullableUninitializedFinalLocal#isSet = false;
+  function #nullableUninitializedFinalLocal#get() → core::int?
+    return #nullableUninitializedFinalLocal#isSet ?{core::int?} nullableUninitializedFinalLocal : throw new _in::LateError::localNI("nullableUninitializedFinalLocal");
+  function #nullableUninitializedFinalLocal#set(core::int? #t19) → dynamic
+    if(#nullableUninitializedFinalLocal#isSet)
+      throw new _in::LateError::localAI("nullableUninitializedFinalLocal");
+    else {
+      #nullableUninitializedFinalLocal#isSet = true;
+      return nullableUninitializedFinalLocal = #t19;
+    }
+  final core::int? nonNullableUninitializedFinalLocal;
+  core::bool #nonNullableUninitializedFinalLocal#isSet = false;
+  function #nonNullableUninitializedFinalLocal#get() → core::int
+    return #nonNullableUninitializedFinalLocal#isSet ?{core::int} nonNullableUninitializedFinalLocal{core::int} : throw new _in::LateError::localNI("nonNullableUninitializedFinalLocal");
+  function #nonNullableUninitializedFinalLocal#set(core::int #t20) → dynamic
+    if(#nonNullableUninitializedFinalLocal#isSet)
+      throw new _in::LateError::localAI("nonNullableUninitializedFinalLocal");
+    else {
+      #nonNullableUninitializedFinalLocal#isSet = true;
+      return nonNullableUninitializedFinalLocal = #t20;
+    }
+  core::int? nullableInitializedNonFinalLocal;
+  core::bool #nullableInitializedNonFinalLocal#isSet = false;
+  function #nullableInitializedNonFinalLocal#get() → core::int? {
+    if(!#nullableInitializedNonFinalLocal#isSet) {
+      nullableInitializedNonFinalLocal = 0;
+      #nullableInitializedNonFinalLocal#isSet = true;
+    }
+    return nullableInitializedNonFinalLocal;
+  }
+  function #nullableInitializedNonFinalLocal#set(core::int? #t21) → dynamic {
+    #nullableInitializedNonFinalLocal#isSet = true;
+    return nullableInitializedNonFinalLocal = #t21;
+  }
+  core::int? nonNullableInitializedNonFinalLocal;
+  core::bool #nonNullableInitializedNonFinalLocal#isSet = false;
+  function #nonNullableInitializedNonFinalLocal#get() → core::int {
+    if(!#nonNullableInitializedNonFinalLocal#isSet) {
+      nonNullableInitializedNonFinalLocal = 0;
+      #nonNullableInitializedNonFinalLocal#isSet = true;
+    }
+    return nonNullableInitializedNonFinalLocal{core::int};
+  }
+  function #nonNullableInitializedNonFinalLocal#set(core::int #t22) → dynamic {
+    #nonNullableInitializedNonFinalLocal#isSet = true;
+    return nonNullableInitializedNonFinalLocal = #t22;
+  }
+  final core::int? nullableInitializedFinalLocal;
+  core::bool #nullableInitializedFinalLocal#isSet = false;
+  function #nullableInitializedFinalLocal#get() → core::int? {
+    if(!#nullableInitializedFinalLocal#isSet) {
+      nullableInitializedFinalLocal = 0;
+      #nullableInitializedFinalLocal#isSet = true;
+    }
+    return nullableInitializedFinalLocal;
+  }
+  final core::int? nonNullableInitializedFinalLocal;
+  core::bool #nonNullableInitializedFinalLocal#isSet = false;
+  function #nonNullableInitializedFinalLocal#get() → core::int {
+    if(!#nonNullableInitializedFinalLocal#isSet) {
+      nonNullableInitializedFinalLocal = 0;
+      #nonNullableInitializedFinalLocal#isSet = true;
+    }
+    return nonNullableInitializedFinalLocal{core::int};
+  }
+}
+static get uninitializedNonFinalTopLevelField() → core::int
+  return self::_#uninitializedNonFinalTopLevelField#isSet ?{core::int} let final core::int? #t23 = self::_#uninitializedNonFinalTopLevelField in #t23{core::int} : throw new _in::LateError::fieldNI("uninitializedNonFinalTopLevelField");
+static set uninitializedNonFinalTopLevelField(core::int #t24) → void {
+  self::_#uninitializedNonFinalTopLevelField#isSet = true;
+  self::_#uninitializedNonFinalTopLevelField = #t24;
+}
+static get uninitializedFinalTopLevelField() → core::int
+  return self::_#uninitializedFinalTopLevelField#isSet ?{core::int} let final core::int? #t25 = self::_#uninitializedFinalTopLevelField in #t25{core::int} : throw new _in::LateError::fieldNI("uninitializedFinalTopLevelField");
+static set uninitializedFinalTopLevelField(core::int #t26) → void
+  if(self::_#uninitializedFinalTopLevelField#isSet)
+    throw new _in::LateError::fieldAI("uninitializedFinalTopLevelField");
+  else {
+    self::_#uninitializedFinalTopLevelField#isSet = true;
+    self::_#uninitializedFinalTopLevelField = #t26;
+  }
+static get initializedNonFinalTopLevelField() → core::int {
+  if(!self::_#initializedNonFinalTopLevelField#isSet) {
+    self::_#initializedNonFinalTopLevelField = 0;
+    self::_#initializedNonFinalTopLevelField#isSet = true;
+  }
+  return let final core::int? #t27 = self::_#initializedNonFinalTopLevelField in #t27{core::int};
+}
+static set initializedNonFinalTopLevelField(core::int #t28) → void {
+  self::_#initializedNonFinalTopLevelField#isSet = true;
+  self::_#initializedNonFinalTopLevelField = #t28;
+}
+static get initializedFinalTopLevelField() → core::int {
+  if(!self::_#initializedFinalTopLevelField#isSet) {
+    final core::int #t29 = 0;
+    if(self::_#initializedFinalTopLevelField#isSet)
+      throw new _in::LateError::fieldADI("initializedFinalTopLevelField");
+    self::_#initializedFinalTopLevelField = #t29;
+    self::_#initializedFinalTopLevelField#isSet = true;
+  }
+  return let final core::int? #t30 = self::_#initializedFinalTopLevelField in #t30{core::int};
+}
diff --git a/pkg/front_end/testcases/late_lowering/late_lowering_bitmasks.dart.weak.transformed.expect b/pkg/front_end/testcases/late_lowering/late_lowering_bitmasks.dart.weak.transformed.expect
new file mode 100644
index 0000000..0bc36f5
--- /dev/null
+++ b/pkg/front_end/testcases/late_lowering/late_lowering_bitmasks.dart.weak.transformed.expect
@@ -0,0 +1,227 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:_internal" as _in;
+
+class Class extends core::Object {
+  static field core::int? _#uninitializedNonFinalStaticField = null;
+  static field core::bool _#uninitializedNonFinalStaticField#isSet = false;
+  static field core::int? _#uninitializedFinalStaticField = null;
+  static field core::bool _#uninitializedFinalStaticField#isSet = false;
+  static field core::int? _#initializedNonFinalStaticField = null;
+  static field core::bool _#initializedNonFinalStaticField#isSet = false;
+  static field core::int? _#initializedFinalStaticField = null;
+  static field core::bool _#initializedFinalStaticField#isSet = false;
+  field core::int? _#Class#uninitializedNonFinalInstanceField = null;
+  field core::bool _#Class#uninitializedNonFinalInstanceField#isSet = false;
+  field core::int? _#Class#uninitializedFinalInstanceField = null;
+  field core::bool _#Class#uninitializedFinalInstanceField#isSet = false;
+  field core::int? _#Class#initializedNonFinalInstanceField = null;
+  field core::bool _#Class#initializedNonFinalInstanceField#isSet = false;
+  field core::int? _#Class#initializedFinalInstanceField = null;
+  field core::bool _#Class#initializedFinalInstanceField#isSet = false;
+  synthetic constructor •() → self::Class
+    : super core::Object::•()
+    ;
+  static get uninitializedNonFinalStaticField() → core::int
+    return self::Class::_#uninitializedNonFinalStaticField#isSet ?{core::int} let final core::int? #t1 = self::Class::_#uninitializedNonFinalStaticField in #t1{core::int} : throw new _in::LateError::fieldNI("uninitializedNonFinalStaticField");
+  static set uninitializedNonFinalStaticField(core::int #t2) → void {
+    self::Class::_#uninitializedNonFinalStaticField#isSet = true;
+    self::Class::_#uninitializedNonFinalStaticField = #t2;
+  }
+  static get uninitializedFinalStaticField() → core::int
+    return self::Class::_#uninitializedFinalStaticField#isSet ?{core::int} let final core::int? #t3 = self::Class::_#uninitializedFinalStaticField in #t3{core::int} : throw new _in::LateError::fieldNI("uninitializedFinalStaticField");
+  static set uninitializedFinalStaticField(core::int #t4) → void
+    if(self::Class::_#uninitializedFinalStaticField#isSet)
+      throw new _in::LateError::fieldAI("uninitializedFinalStaticField");
+    else {
+      self::Class::_#uninitializedFinalStaticField#isSet = true;
+      self::Class::_#uninitializedFinalStaticField = #t4;
+    }
+  static get initializedNonFinalStaticField() → core::int {
+    if(!self::Class::_#initializedNonFinalStaticField#isSet) {
+      self::Class::_#initializedNonFinalStaticField = 0;
+      self::Class::_#initializedNonFinalStaticField#isSet = true;
+    }
+    return let final core::int? #t5 = self::Class::_#initializedNonFinalStaticField in #t5{core::int};
+  }
+  static set initializedNonFinalStaticField(core::int #t6) → void {
+    self::Class::_#initializedNonFinalStaticField#isSet = true;
+    self::Class::_#initializedNonFinalStaticField = #t6;
+  }
+  static get initializedFinalStaticField() → core::int {
+    if(!self::Class::_#initializedFinalStaticField#isSet) {
+      final core::int #t7 = 0;
+      if(self::Class::_#initializedFinalStaticField#isSet)
+        throw new _in::LateError::fieldADI("initializedFinalStaticField");
+      self::Class::_#initializedFinalStaticField = #t7;
+      self::Class::_#initializedFinalStaticField#isSet = true;
+    }
+    return let final core::int? #t8 = self::Class::_#initializedFinalStaticField in #t8{core::int};
+  }
+  get uninitializedNonFinalInstanceField() → core::int
+    return this.{self::Class::_#Class#uninitializedNonFinalInstanceField#isSet} ?{core::int} let final core::int? #t9 = this.{self::Class::_#Class#uninitializedNonFinalInstanceField} in #t9{core::int} : throw new _in::LateError::fieldNI("uninitializedNonFinalInstanceField");
+  set uninitializedNonFinalInstanceField(core::int #t10) → void {
+    this.{self::Class::_#Class#uninitializedNonFinalInstanceField#isSet} = true;
+    this.{self::Class::_#Class#uninitializedNonFinalInstanceField} = #t10;
+  }
+  get uninitializedFinalInstanceField() → core::int
+    return this.{self::Class::_#Class#uninitializedFinalInstanceField#isSet} ?{core::int} let final core::int? #t11 = this.{self::Class::_#Class#uninitializedFinalInstanceField} in #t11{core::int} : throw new _in::LateError::fieldNI("uninitializedFinalInstanceField");
+  set uninitializedFinalInstanceField(core::int #t12) → void
+    if(this.{self::Class::_#Class#uninitializedFinalInstanceField#isSet})
+      throw new _in::LateError::fieldAI("uninitializedFinalInstanceField");
+    else {
+      this.{self::Class::_#Class#uninitializedFinalInstanceField#isSet} = true;
+      this.{self::Class::_#Class#uninitializedFinalInstanceField} = #t12;
+    }
+  get initializedNonFinalInstanceField() → core::int {
+    if(!this.{self::Class::_#Class#initializedNonFinalInstanceField#isSet}) {
+      this.{self::Class::_#Class#initializedNonFinalInstanceField} = 0;
+      this.{self::Class::_#Class#initializedNonFinalInstanceField#isSet} = true;
+    }
+    return let final core::int? #t13 = this.{self::Class::_#Class#initializedNonFinalInstanceField} in #t13{core::int};
+  }
+  set initializedNonFinalInstanceField(core::int #t14) → void {
+    this.{self::Class::_#Class#initializedNonFinalInstanceField#isSet} = true;
+    this.{self::Class::_#Class#initializedNonFinalInstanceField} = #t14;
+  }
+  get initializedFinalInstanceField() → core::int {
+    if(!this.{self::Class::_#Class#initializedFinalInstanceField#isSet}) {
+      final core::int #t15 = 0;
+      if(this.{self::Class::_#Class#initializedFinalInstanceField#isSet})
+        throw new _in::LateError::fieldADI("initializedFinalInstanceField");
+      this.{self::Class::_#Class#initializedFinalInstanceField} = #t15;
+      this.{self::Class::_#Class#initializedFinalInstanceField#isSet} = true;
+    }
+    return let final core::int? #t16 = this.{self::Class::_#Class#initializedFinalInstanceField} in #t16{core::int};
+  }
+}
+static field core::int? _#uninitializedNonFinalTopLevelField = null;
+static field core::bool _#uninitializedNonFinalTopLevelField#isSet = false;
+static field core::int? _#uninitializedFinalTopLevelField = null;
+static field core::bool _#uninitializedFinalTopLevelField#isSet = false;
+static field core::int? _#initializedNonFinalTopLevelField = null;
+static field core::bool _#initializedNonFinalTopLevelField#isSet = false;
+static field core::int? _#initializedFinalTopLevelField = null;
+static field core::bool _#initializedFinalTopLevelField#isSet = false;
+static method main() → dynamic {}
+static method method() → dynamic {
+  core::int? nullableUninitializedNonFinalLocal;
+  core::bool #nullableUninitializedNonFinalLocal#isSet = false;
+  function #nullableUninitializedNonFinalLocal#get() → core::int?
+    return #nullableUninitializedNonFinalLocal#isSet ?{core::int?} nullableUninitializedNonFinalLocal : throw new _in::LateError::localNI("nullableUninitializedNonFinalLocal");
+  function #nullableUninitializedNonFinalLocal#set(core::int? #t17) → dynamic {
+    #nullableUninitializedNonFinalLocal#isSet = true;
+    return nullableUninitializedNonFinalLocal = #t17;
+  }
+  core::int? nonNullableUninitializedNonFinalLocal;
+  core::bool #nonNullableUninitializedNonFinalLocal#isSet = false;
+  function #nonNullableUninitializedNonFinalLocal#get() → core::int
+    return #nonNullableUninitializedNonFinalLocal#isSet ?{core::int} nonNullableUninitializedNonFinalLocal{core::int} : throw new _in::LateError::localNI("nonNullableUninitializedNonFinalLocal");
+  function #nonNullableUninitializedNonFinalLocal#set(core::int #t18) → dynamic {
+    #nonNullableUninitializedNonFinalLocal#isSet = true;
+    return nonNullableUninitializedNonFinalLocal = #t18;
+  }
+  final core::int? nullableUninitializedFinalLocal;
+  core::bool #nullableUninitializedFinalLocal#isSet = false;
+  function #nullableUninitializedFinalLocal#get() → core::int?
+    return #nullableUninitializedFinalLocal#isSet ?{core::int?} nullableUninitializedFinalLocal : throw new _in::LateError::localNI("nullableUninitializedFinalLocal");
+  function #nullableUninitializedFinalLocal#set(core::int? #t19) → dynamic
+    if(#nullableUninitializedFinalLocal#isSet)
+      throw new _in::LateError::localAI("nullableUninitializedFinalLocal");
+    else {
+      #nullableUninitializedFinalLocal#isSet = true;
+      return nullableUninitializedFinalLocal = #t19;
+    }
+  final core::int? nonNullableUninitializedFinalLocal;
+  core::bool #nonNullableUninitializedFinalLocal#isSet = false;
+  function #nonNullableUninitializedFinalLocal#get() → core::int
+    return #nonNullableUninitializedFinalLocal#isSet ?{core::int} nonNullableUninitializedFinalLocal{core::int} : throw new _in::LateError::localNI("nonNullableUninitializedFinalLocal");
+  function #nonNullableUninitializedFinalLocal#set(core::int #t20) → dynamic
+    if(#nonNullableUninitializedFinalLocal#isSet)
+      throw new _in::LateError::localAI("nonNullableUninitializedFinalLocal");
+    else {
+      #nonNullableUninitializedFinalLocal#isSet = true;
+      return nonNullableUninitializedFinalLocal = #t20;
+    }
+  core::int? nullableInitializedNonFinalLocal;
+  core::bool #nullableInitializedNonFinalLocal#isSet = false;
+  function #nullableInitializedNonFinalLocal#get() → core::int? {
+    if(!#nullableInitializedNonFinalLocal#isSet) {
+      nullableInitializedNonFinalLocal = 0;
+      #nullableInitializedNonFinalLocal#isSet = true;
+    }
+    return nullableInitializedNonFinalLocal;
+  }
+  function #nullableInitializedNonFinalLocal#set(core::int? #t21) → dynamic {
+    #nullableInitializedNonFinalLocal#isSet = true;
+    return nullableInitializedNonFinalLocal = #t21;
+  }
+  core::int? nonNullableInitializedNonFinalLocal;
+  core::bool #nonNullableInitializedNonFinalLocal#isSet = false;
+  function #nonNullableInitializedNonFinalLocal#get() → core::int {
+    if(!#nonNullableInitializedNonFinalLocal#isSet) {
+      nonNullableInitializedNonFinalLocal = 0;
+      #nonNullableInitializedNonFinalLocal#isSet = true;
+    }
+    return nonNullableInitializedNonFinalLocal{core::int};
+  }
+  function #nonNullableInitializedNonFinalLocal#set(core::int #t22) → dynamic {
+    #nonNullableInitializedNonFinalLocal#isSet = true;
+    return nonNullableInitializedNonFinalLocal = #t22;
+  }
+  final core::int? nullableInitializedFinalLocal;
+  core::bool #nullableInitializedFinalLocal#isSet = false;
+  function #nullableInitializedFinalLocal#get() → core::int? {
+    if(!#nullableInitializedFinalLocal#isSet) {
+      nullableInitializedFinalLocal = 0;
+      #nullableInitializedFinalLocal#isSet = true;
+    }
+    return nullableInitializedFinalLocal;
+  }
+  final core::int? nonNullableInitializedFinalLocal;
+  core::bool #nonNullableInitializedFinalLocal#isSet = false;
+  function #nonNullableInitializedFinalLocal#get() → core::int {
+    if(!#nonNullableInitializedFinalLocal#isSet) {
+      nonNullableInitializedFinalLocal = 0;
+      #nonNullableInitializedFinalLocal#isSet = true;
+    }
+    return nonNullableInitializedFinalLocal{core::int};
+  }
+}
+static get uninitializedNonFinalTopLevelField() → core::int
+  return self::_#uninitializedNonFinalTopLevelField#isSet ?{core::int} let final core::int? #t23 = self::_#uninitializedNonFinalTopLevelField in #t23{core::int} : throw new _in::LateError::fieldNI("uninitializedNonFinalTopLevelField");
+static set uninitializedNonFinalTopLevelField(core::int #t24) → void {
+  self::_#uninitializedNonFinalTopLevelField#isSet = true;
+  self::_#uninitializedNonFinalTopLevelField = #t24;
+}
+static get uninitializedFinalTopLevelField() → core::int
+  return self::_#uninitializedFinalTopLevelField#isSet ?{core::int} let final core::int? #t25 = self::_#uninitializedFinalTopLevelField in #t25{core::int} : throw new _in::LateError::fieldNI("uninitializedFinalTopLevelField");
+static set uninitializedFinalTopLevelField(core::int #t26) → void
+  if(self::_#uninitializedFinalTopLevelField#isSet)
+    throw new _in::LateError::fieldAI("uninitializedFinalTopLevelField");
+  else {
+    self::_#uninitializedFinalTopLevelField#isSet = true;
+    self::_#uninitializedFinalTopLevelField = #t26;
+  }
+static get initializedNonFinalTopLevelField() → core::int {
+  if(!self::_#initializedNonFinalTopLevelField#isSet) {
+    self::_#initializedNonFinalTopLevelField = 0;
+    self::_#initializedNonFinalTopLevelField#isSet = true;
+  }
+  return let final core::int? #t27 = self::_#initializedNonFinalTopLevelField in #t27{core::int};
+}
+static set initializedNonFinalTopLevelField(core::int #t28) → void {
+  self::_#initializedNonFinalTopLevelField#isSet = true;
+  self::_#initializedNonFinalTopLevelField = #t28;
+}
+static get initializedFinalTopLevelField() → core::int {
+  if(!self::_#initializedFinalTopLevelField#isSet) {
+    final core::int #t29 = 0;
+    if(self::_#initializedFinalTopLevelField#isSet)
+      throw new _in::LateError::fieldADI("initializedFinalTopLevelField");
+    self::_#initializedFinalTopLevelField = #t29;
+    self::_#initializedFinalTopLevelField#isSet = true;
+  }
+  return let final core::int? #t30 = self::_#initializedFinalTopLevelField in #t30{core::int};
+}
diff --git a/pkg/front_end/testcases/late_lowering/skip_late_final_uninitialized_instance_fields/folder.options b/pkg/front_end/testcases/late_lowering/skip_late_final_uninitialized_instance_fields/folder.options
new file mode 100644
index 0000000..d9c5c2f
--- /dev/null
+++ b/pkg/front_end/testcases/late_lowering/skip_late_final_uninitialized_instance_fields/folder.options
@@ -0,0 +1 @@
+--force-late-lowering=0xEFFF
diff --git a/pkg/front_end/testcases/late_lowering/skip_late_final_uninitialized_instance_fields/main.dart b/pkg/front_end/testcases/late_lowering/skip_late_final_uninitialized_instance_fields/main.dart
new file mode 100644
index 0000000..b5a6079
--- /dev/null
+++ b/pkg/front_end/testcases/late_lowering/skip_late_final_uninitialized_instance_fields/main.dart
@@ -0,0 +1,36 @@
+// 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.
+
+// A declaration for each kind of late field/local lowering enabled by
+// target bitmask.
+
+main() {}
+
+method() {
+  late int? nullableUninitializedNonFinalLocal;
+  late int nonNullableUninitializedNonFinalLocal;
+  late final int? nullableUninitializedFinalLocal;
+  late final int nonNullableUninitializedFinalLocal;
+  late int? nullableInitializedNonFinalLocal = 0;
+  late int nonNullableInitializedNonFinalLocal = 0;
+  late final int? nullableInitializedFinalLocal = 0;
+  late final int nonNullableInitializedFinalLocal = 0;
+}
+
+late int uninitializedNonFinalTopLevelField;
+late final int uninitializedFinalTopLevelField;
+late int initializedNonFinalTopLevelField = 0;
+late final int initializedFinalTopLevelField = 0;
+
+class Class {
+  static late int uninitializedNonFinalStaticField;
+  static late final int uninitializedFinalStaticField;
+  static late int initializedNonFinalStaticField = 0;
+  static late final int initializedFinalStaticField = 0;
+
+  late int uninitializedNonFinalInstanceField;
+  late final int uninitializedFinalInstanceField;
+  late int initializedNonFinalInstanceField = 0;
+  late final int initializedFinalInstanceField = 0;
+}
diff --git a/pkg/front_end/testcases/late_lowering/skip_late_final_uninitialized_instance_fields/main.dart.outline.expect b/pkg/front_end/testcases/late_lowering/skip_late_final_uninitialized_instance_fields/main.dart.outline.expect
new file mode 100644
index 0000000..b65cd70
--- /dev/null
+++ b/pkg/front_end/testcases/late_lowering/skip_late_final_uninitialized_instance_fields/main.dart.outline.expect
@@ -0,0 +1,43 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Class extends core::Object {
+  static field core::int? _#uninitializedNonFinalStaticField;
+  static field core::int? _#uninitializedFinalStaticField;
+  static field core::int? _#initializedNonFinalStaticField;
+  static field core::int? _#initializedFinalStaticField;
+  late field core::int uninitializedNonFinalInstanceField;
+  field core::int? _#Class#uninitializedFinalInstanceField;
+  field core::int? _#Class#initializedNonFinalInstanceField;
+  field core::int? _#Class#initializedFinalInstanceField;
+  synthetic constructor •() → self::Class
+    ;
+  static get uninitializedNonFinalStaticField() → core::int;
+  static set uninitializedNonFinalStaticField(core::int #t1) → void;
+  static get uninitializedFinalStaticField() → core::int;
+  static set uninitializedFinalStaticField(core::int #t2) → void;
+  static get initializedNonFinalStaticField() → core::int;
+  static set initializedNonFinalStaticField(core::int #t3) → void;
+  static get initializedFinalStaticField() → core::int;
+  get uninitializedFinalInstanceField() → core::int;
+  set uninitializedFinalInstanceField(core::int #t4) → void;
+  get initializedNonFinalInstanceField() → core::int;
+  set initializedNonFinalInstanceField(core::int #t5) → void;
+  get initializedFinalInstanceField() → core::int;
+}
+static field core::int? _#uninitializedNonFinalTopLevelField;
+static field core::int? _#uninitializedFinalTopLevelField;
+static field core::int? _#initializedNonFinalTopLevelField;
+static field core::int? _#initializedFinalTopLevelField;
+static method main() → dynamic
+  ;
+static method method() → dynamic
+  ;
+static get uninitializedNonFinalTopLevelField() → core::int;
+static set uninitializedNonFinalTopLevelField(core::int #t6) → void;
+static get uninitializedFinalTopLevelField() → core::int;
+static set uninitializedFinalTopLevelField(core::int #t7) → void;
+static get initializedNonFinalTopLevelField() → core::int;
+static set initializedNonFinalTopLevelField(core::int #t8) → void;
+static get initializedFinalTopLevelField() → core::int;
diff --git a/pkg/front_end/testcases/late_lowering/skip_late_final_uninitialized_instance_fields/main.dart.strong.expect b/pkg/front_end/testcases/late_lowering/skip_late_final_uninitialized_instance_fields/main.dart.strong.expect
new file mode 100644
index 0000000..d63b6e3
--- /dev/null
+++ b/pkg/front_end/testcases/late_lowering/skip_late_final_uninitialized_instance_fields/main.dart.strong.expect
@@ -0,0 +1,134 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:_internal" as _in;
+
+class Class extends core::Object {
+  static field core::int? _#uninitializedNonFinalStaticField = null;
+  static field core::int? _#uninitializedFinalStaticField = null;
+  static field core::int? _#initializedNonFinalStaticField = null;
+  static field core::int? _#initializedFinalStaticField = null;
+  late field core::int uninitializedNonFinalInstanceField;
+  field core::int? _#Class#uninitializedFinalInstanceField = null;
+  field core::int? _#Class#initializedNonFinalInstanceField = null;
+  field core::int? _#Class#initializedFinalInstanceField = null;
+  synthetic constructor •() → self::Class
+    : super core::Object::•()
+    ;
+  static get uninitializedNonFinalStaticField() → core::int
+    return let final core::int? #t1 = self::Class::_#uninitializedNonFinalStaticField in #t1.==(null) ?{core::int} throw new _in::LateError::fieldNI("uninitializedNonFinalStaticField") : #t1{core::int};
+  static set uninitializedNonFinalStaticField(core::int #t2) → void
+    self::Class::_#uninitializedNonFinalStaticField = #t2;
+  static get uninitializedFinalStaticField() → core::int
+    return let final core::int? #t3 = self::Class::_#uninitializedFinalStaticField in #t3.==(null) ?{core::int} throw new _in::LateError::fieldNI("uninitializedFinalStaticField") : #t3{core::int};
+  static set uninitializedFinalStaticField(core::int #t4) → void
+    if(self::Class::_#uninitializedFinalStaticField.==(null))
+      self::Class::_#uninitializedFinalStaticField = #t4;
+    else
+      throw new _in::LateError::fieldAI("uninitializedFinalStaticField");
+  static get initializedNonFinalStaticField() → core::int
+    return let final core::int? #t5 = self::Class::_#initializedNonFinalStaticField in #t5.==(null) ?{core::int} self::Class::_#initializedNonFinalStaticField = 0 : #t5{core::int};
+  static set initializedNonFinalStaticField(core::int #t6) → void
+    self::Class::_#initializedNonFinalStaticField = #t6;
+  static get initializedFinalStaticField() → core::int
+    return let final core::int? #t7 = self::Class::_#initializedFinalStaticField in #t7.==(null) ?{core::int} let final core::int #t8 = 0 in self::Class::_#initializedFinalStaticField.==(null) ?{core::int} self::Class::_#initializedFinalStaticField = #t8 : throw new _in::LateError::fieldADI("initializedFinalStaticField") : #t7{core::int};
+  get uninitializedFinalInstanceField() → core::int
+    return let final core::int? #t9 = this.{self::Class::_#Class#uninitializedFinalInstanceField} in #t9.==(null) ?{core::int} throw new _in::LateError::fieldNI("uninitializedFinalInstanceField") : #t9{core::int};
+  set uninitializedFinalInstanceField(core::int #t10) → void
+    if(this.{self::Class::_#Class#uninitializedFinalInstanceField}.==(null))
+      this.{self::Class::_#Class#uninitializedFinalInstanceField} = #t10;
+    else
+      throw new _in::LateError::fieldAI("uninitializedFinalInstanceField");
+  get initializedNonFinalInstanceField() → core::int
+    return let final core::int? #t11 = this.{self::Class::_#Class#initializedNonFinalInstanceField} in #t11.==(null) ?{core::int} this.{self::Class::_#Class#initializedNonFinalInstanceField} = 0 : #t11{core::int};
+  set initializedNonFinalInstanceField(core::int #t12) → void
+    this.{self::Class::_#Class#initializedNonFinalInstanceField} = #t12;
+  get initializedFinalInstanceField() → core::int
+    return let final core::int? #t13 = this.{self::Class::_#Class#initializedFinalInstanceField} in #t13.==(null) ?{core::int} let final core::int #t14 = 0 in this.{self::Class::_#Class#initializedFinalInstanceField}.==(null) ?{core::int} this.{self::Class::_#Class#initializedFinalInstanceField} = #t14 : throw new _in::LateError::fieldADI("initializedFinalInstanceField") : #t13{core::int};
+}
+static field core::int? _#uninitializedNonFinalTopLevelField = null;
+static field core::int? _#uninitializedFinalTopLevelField = null;
+static field core::int? _#initializedNonFinalTopLevelField = null;
+static field core::int? _#initializedFinalTopLevelField = null;
+static method main() → dynamic {}
+static method method() → dynamic {
+  core::int? nullableUninitializedNonFinalLocal;
+  core::bool #nullableUninitializedNonFinalLocal#isSet = false;
+  function #nullableUninitializedNonFinalLocal#get() → core::int?
+    return #nullableUninitializedNonFinalLocal#isSet ?{core::int?} nullableUninitializedNonFinalLocal : throw new _in::LateError::localNI("nullableUninitializedNonFinalLocal");
+  function #nullableUninitializedNonFinalLocal#set(core::int? #t15) → dynamic {
+    #nullableUninitializedNonFinalLocal#isSet = true;
+    return nullableUninitializedNonFinalLocal = #t15;
+  }
+  core::int? nonNullableUninitializedNonFinalLocal;
+  function #nonNullableUninitializedNonFinalLocal#get() → core::int
+    return let final core::int? #t16 = nonNullableUninitializedNonFinalLocal in #t16.==(null) ?{core::int} throw new _in::LateError::localNI("nonNullableUninitializedNonFinalLocal") : #t16{core::int};
+  function #nonNullableUninitializedNonFinalLocal#set(core::int #t17) → dynamic
+    return nonNullableUninitializedNonFinalLocal = #t17;
+  final core::int? nullableUninitializedFinalLocal;
+  core::bool #nullableUninitializedFinalLocal#isSet = false;
+  function #nullableUninitializedFinalLocal#get() → core::int?
+    return #nullableUninitializedFinalLocal#isSet ?{core::int?} nullableUninitializedFinalLocal : throw new _in::LateError::localNI("nullableUninitializedFinalLocal");
+  function #nullableUninitializedFinalLocal#set(core::int? #t18) → dynamic
+    if(#nullableUninitializedFinalLocal#isSet)
+      throw new _in::LateError::localAI("nullableUninitializedFinalLocal");
+    else {
+      #nullableUninitializedFinalLocal#isSet = true;
+      return nullableUninitializedFinalLocal = #t18;
+    }
+  final core::int? nonNullableUninitializedFinalLocal;
+  function #nonNullableUninitializedFinalLocal#get() → core::int
+    return let final core::int? #t19 = nonNullableUninitializedFinalLocal in #t19.==(null) ?{core::int} throw new _in::LateError::localNI("nonNullableUninitializedFinalLocal") : #t19{core::int};
+  function #nonNullableUninitializedFinalLocal#set(core::int #t20) → dynamic
+    if(nonNullableUninitializedFinalLocal.==(null))
+      return nonNullableUninitializedFinalLocal = #t20;
+    else
+      throw new _in::LateError::localAI("nonNullableUninitializedFinalLocal");
+  core::int? nullableInitializedNonFinalLocal;
+  core::bool #nullableInitializedNonFinalLocal#isSet = false;
+  function #nullableInitializedNonFinalLocal#get() → core::int? {
+    if(!#nullableInitializedNonFinalLocal#isSet) {
+      nullableInitializedNonFinalLocal = 0;
+      #nullableInitializedNonFinalLocal#isSet = true;
+    }
+    return nullableInitializedNonFinalLocal;
+  }
+  function #nullableInitializedNonFinalLocal#set(core::int? #t21) → dynamic {
+    #nullableInitializedNonFinalLocal#isSet = true;
+    return nullableInitializedNonFinalLocal = #t21;
+  }
+  core::int? nonNullableInitializedNonFinalLocal;
+  function #nonNullableInitializedNonFinalLocal#get() → core::int
+    return let final core::int? #t22 = nonNullableInitializedNonFinalLocal in #t22.==(null) ?{core::int} nonNullableInitializedNonFinalLocal = 0 : #t22{core::int};
+  function #nonNullableInitializedNonFinalLocal#set(core::int #t23) → dynamic
+    return nonNullableInitializedNonFinalLocal = #t23;
+  final core::int? nullableInitializedFinalLocal;
+  core::bool #nullableInitializedFinalLocal#isSet = false;
+  function #nullableInitializedFinalLocal#get() → core::int? {
+    if(!#nullableInitializedFinalLocal#isSet) {
+      nullableInitializedFinalLocal = 0;
+      #nullableInitializedFinalLocal#isSet = true;
+    }
+    return nullableInitializedFinalLocal;
+  }
+  final core::int? nonNullableInitializedFinalLocal;
+  function #nonNullableInitializedFinalLocal#get() → core::int
+    return let final core::int? #t24 = nonNullableInitializedFinalLocal in #t24.==(null) ?{core::int} nonNullableInitializedFinalLocal = 0 : #t24{core::int};
+}
+static get uninitializedNonFinalTopLevelField() → core::int
+  return let final core::int? #t25 = self::_#uninitializedNonFinalTopLevelField in #t25.==(null) ?{core::int} throw new _in::LateError::fieldNI("uninitializedNonFinalTopLevelField") : #t25{core::int};
+static set uninitializedNonFinalTopLevelField(core::int #t26) → void
+  self::_#uninitializedNonFinalTopLevelField = #t26;
+static get uninitializedFinalTopLevelField() → core::int
+  return let final core::int? #t27 = self::_#uninitializedFinalTopLevelField in #t27.==(null) ?{core::int} throw new _in::LateError::fieldNI("uninitializedFinalTopLevelField") : #t27{core::int};
+static set uninitializedFinalTopLevelField(core::int #t28) → void
+  if(self::_#uninitializedFinalTopLevelField.==(null))
+    self::_#uninitializedFinalTopLevelField = #t28;
+  else
+    throw new _in::LateError::fieldAI("uninitializedFinalTopLevelField");
+static get initializedNonFinalTopLevelField() → core::int
+  return let final core::int? #t29 = self::_#initializedNonFinalTopLevelField in #t29.==(null) ?{core::int} self::_#initializedNonFinalTopLevelField = 0 : #t29{core::int};
+static set initializedNonFinalTopLevelField(core::int #t30) → void
+  self::_#initializedNonFinalTopLevelField = #t30;
+static get initializedFinalTopLevelField() → core::int
+  return let final core::int? #t31 = self::_#initializedFinalTopLevelField in #t31.==(null) ?{core::int} let final core::int #t32 = 0 in self::_#initializedFinalTopLevelField.==(null) ?{core::int} self::_#initializedFinalTopLevelField = #t32 : throw new _in::LateError::fieldADI("initializedFinalTopLevelField") : #t31{core::int};
diff --git a/pkg/front_end/testcases/late_lowering/skip_late_final_uninitialized_instance_fields/main.dart.strong.transformed.expect b/pkg/front_end/testcases/late_lowering/skip_late_final_uninitialized_instance_fields/main.dart.strong.transformed.expect
new file mode 100644
index 0000000..23aeb4d
--- /dev/null
+++ b/pkg/front_end/testcases/late_lowering/skip_late_final_uninitialized_instance_fields/main.dart.strong.transformed.expect
@@ -0,0 +1,141 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:_internal" as _in;
+
+class Class extends core::Object {
+  static field core::int? _#uninitializedNonFinalStaticField = null;
+  static field core::int? _#uninitializedFinalStaticField = null;
+  static field core::int? _#initializedNonFinalStaticField = null;
+  static field core::int? _#initializedFinalStaticField = null;
+  late field core::int uninitializedNonFinalInstanceField;
+  field core::int? _#Class#uninitializedFinalInstanceField = null;
+  field core::int? _#Class#initializedNonFinalInstanceField = null;
+  field core::int? _#Class#initializedFinalInstanceField = null;
+  synthetic constructor •() → self::Class
+    : super core::Object::•()
+    ;
+  static get uninitializedNonFinalStaticField() → core::int
+    return let final core::int? #t1 = self::Class::_#uninitializedNonFinalStaticField in #t1.==(null) ?{core::int} throw new _in::LateError::fieldNI("uninitializedNonFinalStaticField") : #t1{core::int};
+  static set uninitializedNonFinalStaticField(core::int #t2) → void
+    self::Class::_#uninitializedNonFinalStaticField = #t2;
+  static get uninitializedFinalStaticField() → core::int
+    return let final core::int? #t3 = self::Class::_#uninitializedFinalStaticField in #t3.==(null) ?{core::int} throw new _in::LateError::fieldNI("uninitializedFinalStaticField") : #t3{core::int};
+  static set uninitializedFinalStaticField(core::int #t4) → void
+    if(self::Class::_#uninitializedFinalStaticField.==(null))
+      self::Class::_#uninitializedFinalStaticField = #t4;
+    else
+      throw new _in::LateError::fieldAI("uninitializedFinalStaticField");
+  static get initializedNonFinalStaticField() → core::int
+    return let final core::int? #t5 = self::Class::_#initializedNonFinalStaticField in #t5.==(null) ?{core::int} self::Class::_#initializedNonFinalStaticField = 0 : #t5{core::int};
+  static set initializedNonFinalStaticField(core::int #t6) → void
+    self::Class::_#initializedNonFinalStaticField = #t6;
+  static get initializedFinalStaticField() → core::int
+    return let final core::int? #t7 = self::Class::_#initializedFinalStaticField in #t7.==(null) ?{core::int} let final core::int #t8 = 0 in self::Class::_#initializedFinalStaticField.==(null) ?{core::int} self::Class::_#initializedFinalStaticField = #t8 : throw new _in::LateError::fieldADI("initializedFinalStaticField") : #t7{core::int};
+  get uninitializedFinalInstanceField() → core::int
+    return let final core::int? #t9 = this.{self::Class::_#Class#uninitializedFinalInstanceField} in #t9.==(null) ?{core::int} throw new _in::LateError::fieldNI("uninitializedFinalInstanceField") : #t9{core::int};
+  set uninitializedFinalInstanceField(core::int #t10) → void
+    if(this.{self::Class::_#Class#uninitializedFinalInstanceField}.==(null))
+      this.{self::Class::_#Class#uninitializedFinalInstanceField} = #t10;
+    else
+      throw new _in::LateError::fieldAI("uninitializedFinalInstanceField");
+  get initializedNonFinalInstanceField() → core::int
+    return let final core::int? #t11 = this.{self::Class::_#Class#initializedNonFinalInstanceField} in #t11.==(null) ?{core::int} this.{self::Class::_#Class#initializedNonFinalInstanceField} = 0 : #t11{core::int};
+  set initializedNonFinalInstanceField(core::int #t12) → void
+    this.{self::Class::_#Class#initializedNonFinalInstanceField} = #t12;
+  get initializedFinalInstanceField() → core::int
+    return let final core::int? #t13 = this.{self::Class::_#Class#initializedFinalInstanceField} in #t13.==(null) ?{core::int} let final core::int #t14 = 0 in this.{self::Class::_#Class#initializedFinalInstanceField}.==(null) ?{core::int} this.{self::Class::_#Class#initializedFinalInstanceField} = #t14 : throw new _in::LateError::fieldADI("initializedFinalInstanceField") : #t13{core::int};
+}
+static field core::int? _#uninitializedNonFinalTopLevelField = null;
+static field core::int? _#uninitializedFinalTopLevelField = null;
+static field core::int? _#initializedNonFinalTopLevelField = null;
+static field core::int? _#initializedFinalTopLevelField = null;
+static method main() → dynamic {}
+static method method() → dynamic {
+  core::int? nullableUninitializedNonFinalLocal;
+  core::bool #nullableUninitializedNonFinalLocal#isSet = false;
+  function #nullableUninitializedNonFinalLocal#get() → core::int?
+    return #nullableUninitializedNonFinalLocal#isSet ?{core::int?} nullableUninitializedNonFinalLocal : throw new _in::LateError::localNI("nullableUninitializedNonFinalLocal");
+  function #nullableUninitializedNonFinalLocal#set(core::int? #t15) → dynamic {
+    #nullableUninitializedNonFinalLocal#isSet = true;
+    return nullableUninitializedNonFinalLocal = #t15;
+  }
+  core::int? nonNullableUninitializedNonFinalLocal;
+  function #nonNullableUninitializedNonFinalLocal#get() → core::int
+    return let final core::int? #t16 = nonNullableUninitializedNonFinalLocal in #t16.==(null) ?{core::int} throw new _in::LateError::localNI("nonNullableUninitializedNonFinalLocal") : #t16{core::int};
+  function #nonNullableUninitializedNonFinalLocal#set(core::int #t17) → dynamic
+    return nonNullableUninitializedNonFinalLocal = #t17;
+  final core::int? nullableUninitializedFinalLocal;
+  core::bool #nullableUninitializedFinalLocal#isSet = false;
+  function #nullableUninitializedFinalLocal#get() → core::int?
+    return #nullableUninitializedFinalLocal#isSet ?{core::int?} nullableUninitializedFinalLocal : throw new _in::LateError::localNI("nullableUninitializedFinalLocal");
+  function #nullableUninitializedFinalLocal#set(core::int? #t18) → dynamic
+    if(#nullableUninitializedFinalLocal#isSet)
+      throw new _in::LateError::localAI("nullableUninitializedFinalLocal");
+    else {
+      #nullableUninitializedFinalLocal#isSet = true;
+      return nullableUninitializedFinalLocal = #t18;
+    }
+  final core::int? nonNullableUninitializedFinalLocal;
+  function #nonNullableUninitializedFinalLocal#get() → core::int
+    return let final core::int? #t19 = nonNullableUninitializedFinalLocal in #t19.==(null) ?{core::int} throw new _in::LateError::localNI("nonNullableUninitializedFinalLocal") : #t19{core::int};
+  function #nonNullableUninitializedFinalLocal#set(core::int #t20) → dynamic
+    if(nonNullableUninitializedFinalLocal.==(null))
+      return nonNullableUninitializedFinalLocal = #t20;
+    else
+      throw new _in::LateError::localAI("nonNullableUninitializedFinalLocal");
+  core::int? nullableInitializedNonFinalLocal;
+  core::bool #nullableInitializedNonFinalLocal#isSet = false;
+  function #nullableInitializedNonFinalLocal#get() → core::int? {
+    if(!#nullableInitializedNonFinalLocal#isSet) {
+      nullableInitializedNonFinalLocal = 0;
+      #nullableInitializedNonFinalLocal#isSet = true;
+    }
+    return nullableInitializedNonFinalLocal;
+  }
+  function #nullableInitializedNonFinalLocal#set(core::int? #t21) → dynamic {
+    #nullableInitializedNonFinalLocal#isSet = true;
+    return nullableInitializedNonFinalLocal = #t21;
+  }
+  core::int? nonNullableInitializedNonFinalLocal;
+  function #nonNullableInitializedNonFinalLocal#get() → core::int
+    return let final core::int? #t22 = nonNullableInitializedNonFinalLocal in #t22.==(null) ?{core::int} nonNullableInitializedNonFinalLocal = 0 : #t22{core::int};
+  function #nonNullableInitializedNonFinalLocal#set(core::int #t23) → dynamic
+    return nonNullableInitializedNonFinalLocal = #t23;
+  final core::int? nullableInitializedFinalLocal;
+  core::bool #nullableInitializedFinalLocal#isSet = false;
+  function #nullableInitializedFinalLocal#get() → core::int? {
+    if(!#nullableInitializedFinalLocal#isSet) {
+      nullableInitializedFinalLocal = 0;
+      #nullableInitializedFinalLocal#isSet = true;
+    }
+    return nullableInitializedFinalLocal;
+  }
+  final core::int? nonNullableInitializedFinalLocal;
+  function #nonNullableInitializedFinalLocal#get() → core::int
+    return let final core::int? #t24 = nonNullableInitializedFinalLocal in #t24.==(null) ?{core::int} nonNullableInitializedFinalLocal = 0 : #t24{core::int};
+}
+static get uninitializedNonFinalTopLevelField() → core::int
+  return let final core::int? #t25 = self::_#uninitializedNonFinalTopLevelField in #t25.==(null) ?{core::int} throw new _in::LateError::fieldNI("uninitializedNonFinalTopLevelField") : #t25{core::int};
+static set uninitializedNonFinalTopLevelField(core::int #t26) → void
+  self::_#uninitializedNonFinalTopLevelField = #t26;
+static get uninitializedFinalTopLevelField() → core::int
+  return let final core::int? #t27 = self::_#uninitializedFinalTopLevelField in #t27.==(null) ?{core::int} throw new _in::LateError::fieldNI("uninitializedFinalTopLevelField") : #t27{core::int};
+static set uninitializedFinalTopLevelField(core::int #t28) → void
+  if(self::_#uninitializedFinalTopLevelField.==(null))
+    self::_#uninitializedFinalTopLevelField = #t28;
+  else
+    throw new _in::LateError::fieldAI("uninitializedFinalTopLevelField");
+static get initializedNonFinalTopLevelField() → core::int
+  return let final core::int? #t29 = self::_#initializedNonFinalTopLevelField in #t29.==(null) ?{core::int} self::_#initializedNonFinalTopLevelField = 0 : #t29{core::int};
+static set initializedNonFinalTopLevelField(core::int #t30) → void
+  self::_#initializedNonFinalTopLevelField = #t30;
+static get initializedFinalTopLevelField() → core::int
+  return let final core::int? #t31 = self::_#initializedFinalTopLevelField in #t31.==(null) ?{core::int} let final core::int #t32 = 0 in self::_#initializedFinalTopLevelField.==(null) ?{core::int} self::_#initializedFinalTopLevelField = #t32 : throw new _in::LateError::fieldADI("initializedFinalTopLevelField") : #t31{core::int};
+
+
+Extra constant evaluation status:
+Evaluated: VariableGet @ org-dartlang-testcase:///main.dart:30:25 -> IntConstant(0)
+Evaluated: VariableGet @ org-dartlang-testcase:///main.dart:35:18 -> IntConstant(0)
+Evaluated: VariableGet @ org-dartlang-testcase:///main.dart:24:16 -> IntConstant(0)
+Extra constant evaluation: evaluated: 212, effectively constant: 3
diff --git a/pkg/front_end/testcases/late_lowering/skip_late_final_uninitialized_instance_fields/main.dart.textual_outline.expect b/pkg/front_end/testcases/late_lowering/skip_late_final_uninitialized_instance_fields/main.dart.textual_outline.expect
new file mode 100644
index 0000000..83c95c1
--- /dev/null
+++ b/pkg/front_end/testcases/late_lowering/skip_late_final_uninitialized_instance_fields/main.dart.textual_outline.expect
@@ -0,0 +1,28 @@
+main() {}
+method() {}
+late int ;
+uninitializedNonFinalTopLevelField;
+late ;
+final int uninitializedFinalTopLevelField;
+late int ;
+initializedNonFinalTopLevelField = 0;
+late ;
+final int initializedFinalTopLevelField = 0;
+class Class {
+  static late int ;
+  uninitializedNonFinalStaticField;
+  static late ;
+  final int uninitializedFinalStaticField;
+  static late int ;
+  initializedNonFinalStaticField = 0;
+  static late ;
+  final int initializedFinalStaticField = 0;
+  late int ;
+  uninitializedNonFinalInstanceField;
+  late ;
+  final int uninitializedFinalInstanceField;
+  late int ;
+  initializedNonFinalInstanceField = 0;
+  late ;
+  final int initializedFinalInstanceField = 0;
+}
diff --git a/pkg/front_end/testcases/late_lowering/skip_late_final_uninitialized_instance_fields/main.dart.weak.expect b/pkg/front_end/testcases/late_lowering/skip_late_final_uninitialized_instance_fields/main.dart.weak.expect
new file mode 100644
index 0000000..91d5d03
--- /dev/null
+++ b/pkg/front_end/testcases/late_lowering/skip_late_final_uninitialized_instance_fields/main.dart.weak.expect
@@ -0,0 +1,220 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:_internal" as _in;
+
+class Class extends core::Object {
+  static field core::int? _#uninitializedNonFinalStaticField = null;
+  static field core::bool _#uninitializedNonFinalStaticField#isSet = false;
+  static field core::int? _#uninitializedFinalStaticField = null;
+  static field core::bool _#uninitializedFinalStaticField#isSet = false;
+  static field core::int? _#initializedNonFinalStaticField = null;
+  static field core::bool _#initializedNonFinalStaticField#isSet = false;
+  static field core::int? _#initializedFinalStaticField = null;
+  static field core::bool _#initializedFinalStaticField#isSet = false;
+  late field core::int uninitializedNonFinalInstanceField;
+  field core::int? _#Class#uninitializedFinalInstanceField = null;
+  field core::bool _#Class#uninitializedFinalInstanceField#isSet = false;
+  field core::int? _#Class#initializedNonFinalInstanceField = null;
+  field core::bool _#Class#initializedNonFinalInstanceField#isSet = false;
+  field core::int? _#Class#initializedFinalInstanceField = null;
+  field core::bool _#Class#initializedFinalInstanceField#isSet = false;
+  synthetic constructor •() → self::Class
+    : super core::Object::•()
+    ;
+  static get uninitializedNonFinalStaticField() → core::int
+    return self::Class::_#uninitializedNonFinalStaticField#isSet ?{core::int} let final core::int? #t1 = self::Class::_#uninitializedNonFinalStaticField in #t1{core::int} : throw new _in::LateError::fieldNI("uninitializedNonFinalStaticField");
+  static set uninitializedNonFinalStaticField(core::int #t2) → void {
+    self::Class::_#uninitializedNonFinalStaticField#isSet = true;
+    self::Class::_#uninitializedNonFinalStaticField = #t2;
+  }
+  static get uninitializedFinalStaticField() → core::int
+    return self::Class::_#uninitializedFinalStaticField#isSet ?{core::int} let final core::int? #t3 = self::Class::_#uninitializedFinalStaticField in #t3{core::int} : throw new _in::LateError::fieldNI("uninitializedFinalStaticField");
+  static set uninitializedFinalStaticField(core::int #t4) → void
+    if(self::Class::_#uninitializedFinalStaticField#isSet)
+      throw new _in::LateError::fieldAI("uninitializedFinalStaticField");
+    else {
+      self::Class::_#uninitializedFinalStaticField#isSet = true;
+      self::Class::_#uninitializedFinalStaticField = #t4;
+    }
+  static get initializedNonFinalStaticField() → core::int {
+    if(!self::Class::_#initializedNonFinalStaticField#isSet) {
+      self::Class::_#initializedNonFinalStaticField = 0;
+      self::Class::_#initializedNonFinalStaticField#isSet = true;
+    }
+    return let final core::int? #t5 = self::Class::_#initializedNonFinalStaticField in #t5{core::int};
+  }
+  static set initializedNonFinalStaticField(core::int #t6) → void {
+    self::Class::_#initializedNonFinalStaticField#isSet = true;
+    self::Class::_#initializedNonFinalStaticField = #t6;
+  }
+  static get initializedFinalStaticField() → core::int {
+    if(!self::Class::_#initializedFinalStaticField#isSet) {
+      final core::int #t7 = 0;
+      if(self::Class::_#initializedFinalStaticField#isSet)
+        throw new _in::LateError::fieldADI("initializedFinalStaticField");
+      self::Class::_#initializedFinalStaticField = #t7;
+      self::Class::_#initializedFinalStaticField#isSet = true;
+    }
+    return let final core::int? #t8 = self::Class::_#initializedFinalStaticField in #t8{core::int};
+  }
+  get uninitializedFinalInstanceField() → core::int
+    return this.{self::Class::_#Class#uninitializedFinalInstanceField#isSet} ?{core::int} let final core::int? #t9 = this.{self::Class::_#Class#uninitializedFinalInstanceField} in #t9{core::int} : throw new _in::LateError::fieldNI("uninitializedFinalInstanceField");
+  set uninitializedFinalInstanceField(core::int #t10) → void
+    if(this.{self::Class::_#Class#uninitializedFinalInstanceField#isSet})
+      throw new _in::LateError::fieldAI("uninitializedFinalInstanceField");
+    else {
+      this.{self::Class::_#Class#uninitializedFinalInstanceField#isSet} = true;
+      this.{self::Class::_#Class#uninitializedFinalInstanceField} = #t10;
+    }
+  get initializedNonFinalInstanceField() → core::int {
+    if(!this.{self::Class::_#Class#initializedNonFinalInstanceField#isSet}) {
+      this.{self::Class::_#Class#initializedNonFinalInstanceField} = 0;
+      this.{self::Class::_#Class#initializedNonFinalInstanceField#isSet} = true;
+    }
+    return let final core::int? #t11 = this.{self::Class::_#Class#initializedNonFinalInstanceField} in #t11{core::int};
+  }
+  set initializedNonFinalInstanceField(core::int #t12) → void {
+    this.{self::Class::_#Class#initializedNonFinalInstanceField#isSet} = true;
+    this.{self::Class::_#Class#initializedNonFinalInstanceField} = #t12;
+  }
+  get initializedFinalInstanceField() → core::int {
+    if(!this.{self::Class::_#Class#initializedFinalInstanceField#isSet}) {
+      final core::int #t13 = 0;
+      if(this.{self::Class::_#Class#initializedFinalInstanceField#isSet})
+        throw new _in::LateError::fieldADI("initializedFinalInstanceField");
+      this.{self::Class::_#Class#initializedFinalInstanceField} = #t13;
+      this.{self::Class::_#Class#initializedFinalInstanceField#isSet} = true;
+    }
+    return let final core::int? #t14 = this.{self::Class::_#Class#initializedFinalInstanceField} in #t14{core::int};
+  }
+}
+static field core::int? _#uninitializedNonFinalTopLevelField = null;
+static field core::bool _#uninitializedNonFinalTopLevelField#isSet = false;
+static field core::int? _#uninitializedFinalTopLevelField = null;
+static field core::bool _#uninitializedFinalTopLevelField#isSet = false;
+static field core::int? _#initializedNonFinalTopLevelField = null;
+static field core::bool _#initializedNonFinalTopLevelField#isSet = false;
+static field core::int? _#initializedFinalTopLevelField = null;
+static field core::bool _#initializedFinalTopLevelField#isSet = false;
+static method main() → dynamic {}
+static method method() → dynamic {
+  core::int? nullableUninitializedNonFinalLocal;
+  core::bool #nullableUninitializedNonFinalLocal#isSet = false;
+  function #nullableUninitializedNonFinalLocal#get() → core::int?
+    return #nullableUninitializedNonFinalLocal#isSet ?{core::int?} nullableUninitializedNonFinalLocal : throw new _in::LateError::localNI("nullableUninitializedNonFinalLocal");
+  function #nullableUninitializedNonFinalLocal#set(core::int? #t15) → dynamic {
+    #nullableUninitializedNonFinalLocal#isSet = true;
+    return nullableUninitializedNonFinalLocal = #t15;
+  }
+  core::int? nonNullableUninitializedNonFinalLocal;
+  core::bool #nonNullableUninitializedNonFinalLocal#isSet = false;
+  function #nonNullableUninitializedNonFinalLocal#get() → core::int
+    return #nonNullableUninitializedNonFinalLocal#isSet ?{core::int} nonNullableUninitializedNonFinalLocal{core::int} : throw new _in::LateError::localNI("nonNullableUninitializedNonFinalLocal");
+  function #nonNullableUninitializedNonFinalLocal#set(core::int #t16) → dynamic {
+    #nonNullableUninitializedNonFinalLocal#isSet = true;
+    return nonNullableUninitializedNonFinalLocal = #t16;
+  }
+  final core::int? nullableUninitializedFinalLocal;
+  core::bool #nullableUninitializedFinalLocal#isSet = false;
+  function #nullableUninitializedFinalLocal#get() → core::int?
+    return #nullableUninitializedFinalLocal#isSet ?{core::int?} nullableUninitializedFinalLocal : throw new _in::LateError::localNI("nullableUninitializedFinalLocal");
+  function #nullableUninitializedFinalLocal#set(core::int? #t17) → dynamic
+    if(#nullableUninitializedFinalLocal#isSet)
+      throw new _in::LateError::localAI("nullableUninitializedFinalLocal");
+    else {
+      #nullableUninitializedFinalLocal#isSet = true;
+      return nullableUninitializedFinalLocal = #t17;
+    }
+  final core::int? nonNullableUninitializedFinalLocal;
+  core::bool #nonNullableUninitializedFinalLocal#isSet = false;
+  function #nonNullableUninitializedFinalLocal#get() → core::int
+    return #nonNullableUninitializedFinalLocal#isSet ?{core::int} nonNullableUninitializedFinalLocal{core::int} : throw new _in::LateError::localNI("nonNullableUninitializedFinalLocal");
+  function #nonNullableUninitializedFinalLocal#set(core::int #t18) → dynamic
+    if(#nonNullableUninitializedFinalLocal#isSet)
+      throw new _in::LateError::localAI("nonNullableUninitializedFinalLocal");
+    else {
+      #nonNullableUninitializedFinalLocal#isSet = true;
+      return nonNullableUninitializedFinalLocal = #t18;
+    }
+  core::int? nullableInitializedNonFinalLocal;
+  core::bool #nullableInitializedNonFinalLocal#isSet = false;
+  function #nullableInitializedNonFinalLocal#get() → core::int? {
+    if(!#nullableInitializedNonFinalLocal#isSet) {
+      nullableInitializedNonFinalLocal = 0;
+      #nullableInitializedNonFinalLocal#isSet = true;
+    }
+    return nullableInitializedNonFinalLocal;
+  }
+  function #nullableInitializedNonFinalLocal#set(core::int? #t19) → dynamic {
+    #nullableInitializedNonFinalLocal#isSet = true;
+    return nullableInitializedNonFinalLocal = #t19;
+  }
+  core::int? nonNullableInitializedNonFinalLocal;
+  core::bool #nonNullableInitializedNonFinalLocal#isSet = false;
+  function #nonNullableInitializedNonFinalLocal#get() → core::int {
+    if(!#nonNullableInitializedNonFinalLocal#isSet) {
+      nonNullableInitializedNonFinalLocal = 0;
+      #nonNullableInitializedNonFinalLocal#isSet = true;
+    }
+    return nonNullableInitializedNonFinalLocal{core::int};
+  }
+  function #nonNullableInitializedNonFinalLocal#set(core::int #t20) → dynamic {
+    #nonNullableInitializedNonFinalLocal#isSet = true;
+    return nonNullableInitializedNonFinalLocal = #t20;
+  }
+  final core::int? nullableInitializedFinalLocal;
+  core::bool #nullableInitializedFinalLocal#isSet = false;
+  function #nullableInitializedFinalLocal#get() → core::int? {
+    if(!#nullableInitializedFinalLocal#isSet) {
+      nullableInitializedFinalLocal = 0;
+      #nullableInitializedFinalLocal#isSet = true;
+    }
+    return nullableInitializedFinalLocal;
+  }
+  final core::int? nonNullableInitializedFinalLocal;
+  core::bool #nonNullableInitializedFinalLocal#isSet = false;
+  function #nonNullableInitializedFinalLocal#get() → core::int {
+    if(!#nonNullableInitializedFinalLocal#isSet) {
+      nonNullableInitializedFinalLocal = 0;
+      #nonNullableInitializedFinalLocal#isSet = true;
+    }
+    return nonNullableInitializedFinalLocal{core::int};
+  }
+}
+static get uninitializedNonFinalTopLevelField() → core::int
+  return self::_#uninitializedNonFinalTopLevelField#isSet ?{core::int} let final core::int? #t21 = self::_#uninitializedNonFinalTopLevelField in #t21{core::int} : throw new _in::LateError::fieldNI("uninitializedNonFinalTopLevelField");
+static set uninitializedNonFinalTopLevelField(core::int #t22) → void {
+  self::_#uninitializedNonFinalTopLevelField#isSet = true;
+  self::_#uninitializedNonFinalTopLevelField = #t22;
+}
+static get uninitializedFinalTopLevelField() → core::int
+  return self::_#uninitializedFinalTopLevelField#isSet ?{core::int} let final core::int? #t23 = self::_#uninitializedFinalTopLevelField in #t23{core::int} : throw new _in::LateError::fieldNI("uninitializedFinalTopLevelField");
+static set uninitializedFinalTopLevelField(core::int #t24) → void
+  if(self::_#uninitializedFinalTopLevelField#isSet)
+    throw new _in::LateError::fieldAI("uninitializedFinalTopLevelField");
+  else {
+    self::_#uninitializedFinalTopLevelField#isSet = true;
+    self::_#uninitializedFinalTopLevelField = #t24;
+  }
+static get initializedNonFinalTopLevelField() → core::int {
+  if(!self::_#initializedNonFinalTopLevelField#isSet) {
+    self::_#initializedNonFinalTopLevelField = 0;
+    self::_#initializedNonFinalTopLevelField#isSet = true;
+  }
+  return let final core::int? #t25 = self::_#initializedNonFinalTopLevelField in #t25{core::int};
+}
+static set initializedNonFinalTopLevelField(core::int #t26) → void {
+  self::_#initializedNonFinalTopLevelField#isSet = true;
+  self::_#initializedNonFinalTopLevelField = #t26;
+}
+static get initializedFinalTopLevelField() → core::int {
+  if(!self::_#initializedFinalTopLevelField#isSet) {
+    final core::int #t27 = 0;
+    if(self::_#initializedFinalTopLevelField#isSet)
+      throw new _in::LateError::fieldADI("initializedFinalTopLevelField");
+    self::_#initializedFinalTopLevelField = #t27;
+    self::_#initializedFinalTopLevelField#isSet = true;
+  }
+  return let final core::int? #t28 = self::_#initializedFinalTopLevelField in #t28{core::int};
+}
diff --git a/pkg/front_end/testcases/late_lowering/skip_late_final_uninitialized_instance_fields/main.dart.weak.transformed.expect b/pkg/front_end/testcases/late_lowering/skip_late_final_uninitialized_instance_fields/main.dart.weak.transformed.expect
new file mode 100644
index 0000000..91d5d03
--- /dev/null
+++ b/pkg/front_end/testcases/late_lowering/skip_late_final_uninitialized_instance_fields/main.dart.weak.transformed.expect
@@ -0,0 +1,220 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:_internal" as _in;
+
+class Class extends core::Object {
+  static field core::int? _#uninitializedNonFinalStaticField = null;
+  static field core::bool _#uninitializedNonFinalStaticField#isSet = false;
+  static field core::int? _#uninitializedFinalStaticField = null;
+  static field core::bool _#uninitializedFinalStaticField#isSet = false;
+  static field core::int? _#initializedNonFinalStaticField = null;
+  static field core::bool _#initializedNonFinalStaticField#isSet = false;
+  static field core::int? _#initializedFinalStaticField = null;
+  static field core::bool _#initializedFinalStaticField#isSet = false;
+  late field core::int uninitializedNonFinalInstanceField;
+  field core::int? _#Class#uninitializedFinalInstanceField = null;
+  field core::bool _#Class#uninitializedFinalInstanceField#isSet = false;
+  field core::int? _#Class#initializedNonFinalInstanceField = null;
+  field core::bool _#Class#initializedNonFinalInstanceField#isSet = false;
+  field core::int? _#Class#initializedFinalInstanceField = null;
+  field core::bool _#Class#initializedFinalInstanceField#isSet = false;
+  synthetic constructor •() → self::Class
+    : super core::Object::•()
+    ;
+  static get uninitializedNonFinalStaticField() → core::int
+    return self::Class::_#uninitializedNonFinalStaticField#isSet ?{core::int} let final core::int? #t1 = self::Class::_#uninitializedNonFinalStaticField in #t1{core::int} : throw new _in::LateError::fieldNI("uninitializedNonFinalStaticField");
+  static set uninitializedNonFinalStaticField(core::int #t2) → void {
+    self::Class::_#uninitializedNonFinalStaticField#isSet = true;
+    self::Class::_#uninitializedNonFinalStaticField = #t2;
+  }
+  static get uninitializedFinalStaticField() → core::int
+    return self::Class::_#uninitializedFinalStaticField#isSet ?{core::int} let final core::int? #t3 = self::Class::_#uninitializedFinalStaticField in #t3{core::int} : throw new _in::LateError::fieldNI("uninitializedFinalStaticField");
+  static set uninitializedFinalStaticField(core::int #t4) → void
+    if(self::Class::_#uninitializedFinalStaticField#isSet)
+      throw new _in::LateError::fieldAI("uninitializedFinalStaticField");
+    else {
+      self::Class::_#uninitializedFinalStaticField#isSet = true;
+      self::Class::_#uninitializedFinalStaticField = #t4;
+    }
+  static get initializedNonFinalStaticField() → core::int {
+    if(!self::Class::_#initializedNonFinalStaticField#isSet) {
+      self::Class::_#initializedNonFinalStaticField = 0;
+      self::Class::_#initializedNonFinalStaticField#isSet = true;
+    }
+    return let final core::int? #t5 = self::Class::_#initializedNonFinalStaticField in #t5{core::int};
+  }
+  static set initializedNonFinalStaticField(core::int #t6) → void {
+    self::Class::_#initializedNonFinalStaticField#isSet = true;
+    self::Class::_#initializedNonFinalStaticField = #t6;
+  }
+  static get initializedFinalStaticField() → core::int {
+    if(!self::Class::_#initializedFinalStaticField#isSet) {
+      final core::int #t7 = 0;
+      if(self::Class::_#initializedFinalStaticField#isSet)
+        throw new _in::LateError::fieldADI("initializedFinalStaticField");
+      self::Class::_#initializedFinalStaticField = #t7;
+      self::Class::_#initializedFinalStaticField#isSet = true;
+    }
+    return let final core::int? #t8 = self::Class::_#initializedFinalStaticField in #t8{core::int};
+  }
+  get uninitializedFinalInstanceField() → core::int
+    return this.{self::Class::_#Class#uninitializedFinalInstanceField#isSet} ?{core::int} let final core::int? #t9 = this.{self::Class::_#Class#uninitializedFinalInstanceField} in #t9{core::int} : throw new _in::LateError::fieldNI("uninitializedFinalInstanceField");
+  set uninitializedFinalInstanceField(core::int #t10) → void
+    if(this.{self::Class::_#Class#uninitializedFinalInstanceField#isSet})
+      throw new _in::LateError::fieldAI("uninitializedFinalInstanceField");
+    else {
+      this.{self::Class::_#Class#uninitializedFinalInstanceField#isSet} = true;
+      this.{self::Class::_#Class#uninitializedFinalInstanceField} = #t10;
+    }
+  get initializedNonFinalInstanceField() → core::int {
+    if(!this.{self::Class::_#Class#initializedNonFinalInstanceField#isSet}) {
+      this.{self::Class::_#Class#initializedNonFinalInstanceField} = 0;
+      this.{self::Class::_#Class#initializedNonFinalInstanceField#isSet} = true;
+    }
+    return let final core::int? #t11 = this.{self::Class::_#Class#initializedNonFinalInstanceField} in #t11{core::int};
+  }
+  set initializedNonFinalInstanceField(core::int #t12) → void {
+    this.{self::Class::_#Class#initializedNonFinalInstanceField#isSet} = true;
+    this.{self::Class::_#Class#initializedNonFinalInstanceField} = #t12;
+  }
+  get initializedFinalInstanceField() → core::int {
+    if(!this.{self::Class::_#Class#initializedFinalInstanceField#isSet}) {
+      final core::int #t13 = 0;
+      if(this.{self::Class::_#Class#initializedFinalInstanceField#isSet})
+        throw new _in::LateError::fieldADI("initializedFinalInstanceField");
+      this.{self::Class::_#Class#initializedFinalInstanceField} = #t13;
+      this.{self::Class::_#Class#initializedFinalInstanceField#isSet} = true;
+    }
+    return let final core::int? #t14 = this.{self::Class::_#Class#initializedFinalInstanceField} in #t14{core::int};
+  }
+}
+static field core::int? _#uninitializedNonFinalTopLevelField = null;
+static field core::bool _#uninitializedNonFinalTopLevelField#isSet = false;
+static field core::int? _#uninitializedFinalTopLevelField = null;
+static field core::bool _#uninitializedFinalTopLevelField#isSet = false;
+static field core::int? _#initializedNonFinalTopLevelField = null;
+static field core::bool _#initializedNonFinalTopLevelField#isSet = false;
+static field core::int? _#initializedFinalTopLevelField = null;
+static field core::bool _#initializedFinalTopLevelField#isSet = false;
+static method main() → dynamic {}
+static method method() → dynamic {
+  core::int? nullableUninitializedNonFinalLocal;
+  core::bool #nullableUninitializedNonFinalLocal#isSet = false;
+  function #nullableUninitializedNonFinalLocal#get() → core::int?
+    return #nullableUninitializedNonFinalLocal#isSet ?{core::int?} nullableUninitializedNonFinalLocal : throw new _in::LateError::localNI("nullableUninitializedNonFinalLocal");
+  function #nullableUninitializedNonFinalLocal#set(core::int? #t15) → dynamic {
+    #nullableUninitializedNonFinalLocal#isSet = true;
+    return nullableUninitializedNonFinalLocal = #t15;
+  }
+  core::int? nonNullableUninitializedNonFinalLocal;
+  core::bool #nonNullableUninitializedNonFinalLocal#isSet = false;
+  function #nonNullableUninitializedNonFinalLocal#get() → core::int
+    return #nonNullableUninitializedNonFinalLocal#isSet ?{core::int} nonNullableUninitializedNonFinalLocal{core::int} : throw new _in::LateError::localNI("nonNullableUninitializedNonFinalLocal");
+  function #nonNullableUninitializedNonFinalLocal#set(core::int #t16) → dynamic {
+    #nonNullableUninitializedNonFinalLocal#isSet = true;
+    return nonNullableUninitializedNonFinalLocal = #t16;
+  }
+  final core::int? nullableUninitializedFinalLocal;
+  core::bool #nullableUninitializedFinalLocal#isSet = false;
+  function #nullableUninitializedFinalLocal#get() → core::int?
+    return #nullableUninitializedFinalLocal#isSet ?{core::int?} nullableUninitializedFinalLocal : throw new _in::LateError::localNI("nullableUninitializedFinalLocal");
+  function #nullableUninitializedFinalLocal#set(core::int? #t17) → dynamic
+    if(#nullableUninitializedFinalLocal#isSet)
+      throw new _in::LateError::localAI("nullableUninitializedFinalLocal");
+    else {
+      #nullableUninitializedFinalLocal#isSet = true;
+      return nullableUninitializedFinalLocal = #t17;
+    }
+  final core::int? nonNullableUninitializedFinalLocal;
+  core::bool #nonNullableUninitializedFinalLocal#isSet = false;
+  function #nonNullableUninitializedFinalLocal#get() → core::int
+    return #nonNullableUninitializedFinalLocal#isSet ?{core::int} nonNullableUninitializedFinalLocal{core::int} : throw new _in::LateError::localNI("nonNullableUninitializedFinalLocal");
+  function #nonNullableUninitializedFinalLocal#set(core::int #t18) → dynamic
+    if(#nonNullableUninitializedFinalLocal#isSet)
+      throw new _in::LateError::localAI("nonNullableUninitializedFinalLocal");
+    else {
+      #nonNullableUninitializedFinalLocal#isSet = true;
+      return nonNullableUninitializedFinalLocal = #t18;
+    }
+  core::int? nullableInitializedNonFinalLocal;
+  core::bool #nullableInitializedNonFinalLocal#isSet = false;
+  function #nullableInitializedNonFinalLocal#get() → core::int? {
+    if(!#nullableInitializedNonFinalLocal#isSet) {
+      nullableInitializedNonFinalLocal = 0;
+      #nullableInitializedNonFinalLocal#isSet = true;
+    }
+    return nullableInitializedNonFinalLocal;
+  }
+  function #nullableInitializedNonFinalLocal#set(core::int? #t19) → dynamic {
+    #nullableInitializedNonFinalLocal#isSet = true;
+    return nullableInitializedNonFinalLocal = #t19;
+  }
+  core::int? nonNullableInitializedNonFinalLocal;
+  core::bool #nonNullableInitializedNonFinalLocal#isSet = false;
+  function #nonNullableInitializedNonFinalLocal#get() → core::int {
+    if(!#nonNullableInitializedNonFinalLocal#isSet) {
+      nonNullableInitializedNonFinalLocal = 0;
+      #nonNullableInitializedNonFinalLocal#isSet = true;
+    }
+    return nonNullableInitializedNonFinalLocal{core::int};
+  }
+  function #nonNullableInitializedNonFinalLocal#set(core::int #t20) → dynamic {
+    #nonNullableInitializedNonFinalLocal#isSet = true;
+    return nonNullableInitializedNonFinalLocal = #t20;
+  }
+  final core::int? nullableInitializedFinalLocal;
+  core::bool #nullableInitializedFinalLocal#isSet = false;
+  function #nullableInitializedFinalLocal#get() → core::int? {
+    if(!#nullableInitializedFinalLocal#isSet) {
+      nullableInitializedFinalLocal = 0;
+      #nullableInitializedFinalLocal#isSet = true;
+    }
+    return nullableInitializedFinalLocal;
+  }
+  final core::int? nonNullableInitializedFinalLocal;
+  core::bool #nonNullableInitializedFinalLocal#isSet = false;
+  function #nonNullableInitializedFinalLocal#get() → core::int {
+    if(!#nonNullableInitializedFinalLocal#isSet) {
+      nonNullableInitializedFinalLocal = 0;
+      #nonNullableInitializedFinalLocal#isSet = true;
+    }
+    return nonNullableInitializedFinalLocal{core::int};
+  }
+}
+static get uninitializedNonFinalTopLevelField() → core::int
+  return self::_#uninitializedNonFinalTopLevelField#isSet ?{core::int} let final core::int? #t21 = self::_#uninitializedNonFinalTopLevelField in #t21{core::int} : throw new _in::LateError::fieldNI("uninitializedNonFinalTopLevelField");
+static set uninitializedNonFinalTopLevelField(core::int #t22) → void {
+  self::_#uninitializedNonFinalTopLevelField#isSet = true;
+  self::_#uninitializedNonFinalTopLevelField = #t22;
+}
+static get uninitializedFinalTopLevelField() → core::int
+  return self::_#uninitializedFinalTopLevelField#isSet ?{core::int} let final core::int? #t23 = self::_#uninitializedFinalTopLevelField in #t23{core::int} : throw new _in::LateError::fieldNI("uninitializedFinalTopLevelField");
+static set uninitializedFinalTopLevelField(core::int #t24) → void
+  if(self::_#uninitializedFinalTopLevelField#isSet)
+    throw new _in::LateError::fieldAI("uninitializedFinalTopLevelField");
+  else {
+    self::_#uninitializedFinalTopLevelField#isSet = true;
+    self::_#uninitializedFinalTopLevelField = #t24;
+  }
+static get initializedNonFinalTopLevelField() → core::int {
+  if(!self::_#initializedNonFinalTopLevelField#isSet) {
+    self::_#initializedNonFinalTopLevelField = 0;
+    self::_#initializedNonFinalTopLevelField#isSet = true;
+  }
+  return let final core::int? #t25 = self::_#initializedNonFinalTopLevelField in #t25{core::int};
+}
+static set initializedNonFinalTopLevelField(core::int #t26) → void {
+  self::_#initializedNonFinalTopLevelField#isSet = true;
+  self::_#initializedNonFinalTopLevelField = #t26;
+}
+static get initializedFinalTopLevelField() → core::int {
+  if(!self::_#initializedFinalTopLevelField#isSet) {
+    final core::int #t27 = 0;
+    if(self::_#initializedFinalTopLevelField#isSet)
+      throw new _in::LateError::fieldADI("initializedFinalTopLevelField");
+    self::_#initializedFinalTopLevelField = #t27;
+    self::_#initializedFinalTopLevelField#isSet = true;
+  }
+  return let final core::int? #t28 = self::_#initializedFinalTopLevelField in #t28{core::int};
+}
diff --git a/pkg/front_end/testcases/textual_outline.status b/pkg/front_end/testcases/textual_outline.status
index 1d7593c..ab8fac6 100644
--- a/pkg/front_end/testcases/textual_outline.status
+++ b/pkg/front_end/testcases/textual_outline.status
@@ -163,6 +163,7 @@
 late_lowering/late_final_nullable_field_with_initializer: FormatterCrash
 late_lowering/late_final_nullable_field_without_initializer: FormatterCrash
 late_lowering/late_future_or: FormatterCrash
+late_lowering/late_lowering_bitmasks: FormatterCrash
 late_lowering/late_nullable_field_with_initializer: FormatterCrash
 late_lowering/late_nullable_field_without_initializer: FormatterCrash
 late_lowering/later: FormatterCrash
@@ -170,6 +171,7 @@
 late_lowering/override_getter_setter: FormatterCrash
 late_lowering/uninitialized_non_nullable_late_fields: FormatterCrash
 late_lowering_sentinel/late_fields: FormatterCrash
+late_lowering/skip_late_final_uninitialized_instance_fields/main: FormatterCrash
 nnbd/abstract_field_errors: FormatterCrash
 nnbd/covariant_late_field: FormatterCrash
 nnbd/definitely_unassigned: FormatterCrash
diff --git a/pkg/front_end/testing.json b/pkg/front_end/testing.json
index deda925..32f3042 100644
--- a/pkg/front_end/testing.json
+++ b/pkg/front_end/testing.json
@@ -284,9 +284,11 @@
       "status": "test/lint_test.status",
       "pattern": [
         "_fe_analyzer_shared/lib/.*\\.dart$",
+        "kernel/lib/.*\\.dart$",
         "front_end/lib/.*\\.dart$"
       ],
       "exclude": [
+        "kernel/lib/transformations/.*\\.dart$",
         "_fe_analyzer_shared/lib/src/messages/codes_generated\\.dart$",
         "front_end/lib/src/fasta/fasta_codes_cfe_generated\\.dart$"
       ]
diff --git a/pkg/front_end/tool/_fasta/command_line.dart b/pkg/front_end/tool/_fasta/command_line.dart
index 0404554..ed79435 100644
--- a/pkg/front_end/tool/_fasta/command_line.dart
+++ b/pkg/front_end/tool/_fasta/command_line.dart
@@ -51,7 +51,7 @@
     show SchemeBasedFileSystem;
 
 import 'package:kernel/target/targets.dart'
-    show Target, getTarget, TargetFlags, targets;
+    show LateLowering, Target, getTarget, TargetFlags, targets;
 
 class CommandLineProblem {
   final Message message;
@@ -242,7 +242,9 @@
           onWarning: print);
 
   final TargetFlags flags = new TargetFlags(
-      forceLateLoweringForTesting: options[Flags.forceLateLowering],
+      forceLateLoweringsForTesting: options[Flags.forceLateLowering]
+          ? LateLowering.all
+          : LateLowering.none,
       forceStaticFieldLoweringForTesting:
           options[Flags.forceStaticFieldLowering],
       forceNoExplicitGetterCallsForTesting:
diff --git a/pkg/kernel/analysis_options.yaml b/pkg/kernel/analysis_options.yaml
index af1bb97..68fdd50 100644
--- a/pkg/kernel/analysis_options.yaml
+++ b/pkg/kernel/analysis_options.yaml
@@ -1,3 +1,19 @@
 analyzer:
   exclude:
     - testcases/**
+
+linter:
+  rules:
+    - curly_braces_in_flow_control_structures
+    - prefer_adjacent_string_concatenation
+    - unawaited_futures
+    - recursive_getters
+    - avoid_empty_else
+    - empty_statements
+    - list_remove_unrelated_type
+    - iterable_contains_unrelated_type
+    - valid_regexps
+    - package_api_docs
+    - lines_longer_than_80_chars
+    - unrelated_type_equality_checks
+    # - always_specify_types
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index ce1824e..c1268c8 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -64,6 +64,8 @@
 ///
 library kernel.ast;
 
+import 'dart:core';
+import 'dart:core' as core show MapEntry;
 import 'dart:collection' show ListBase;
 import 'dart:convert' show utf8;
 
@@ -1030,7 +1032,7 @@
   }
 
   List<Supertype> superclassConstraints() {
-    var constraints = <Supertype>[];
+    List<Supertype> constraints = <Supertype>[];
 
     // Not a mixin declaration.
     if (!isMixinDeclaration) return constraints;
@@ -1077,7 +1079,7 @@
   /// done automatically when accessing the lists.
   void ensureLoaded() {
     if (lazyBuilder != null) {
-      var lazyBuilderLocal = lazyBuilder;
+      void Function() lazyBuilderLocal = lazyBuilder;
       lazyBuilder = null;
       lazyBuilderLocal();
     }
@@ -2770,7 +2772,7 @@
 
   void _buildLazy() {
     if (lazyBuilder != null) {
-      var lazyBuilderLocal = lazyBuilder;
+      void Function() lazyBuilderLocal = lazyBuilder;
       lazyBuilder = null;
       lazyBuilderLocal();
     }
@@ -2829,9 +2831,10 @@
     named.sort();
     // We need create a copy of the list of type parameters, otherwise
     // transformations like erasure don't work.
-    var typeParametersCopy = new List<TypeParameter>.from(parent is Constructor
-        ? parent.enclosingClass.typeParameters
-        : typeParameters);
+    List<TypeParameter> typeParametersCopy = new List<TypeParameter>.from(
+        parent is Constructor
+            ? parent.enclosingClass.typeParameters
+            : typeParameters);
     return new FunctionType(
         positionalParameters.map(_getTypeOfVariable).toList(growable: false),
         returnType,
@@ -3062,7 +3065,7 @@
       return context.typeEnvironment.coreTypes
           .rawType(superclass, context.nonNullable);
     }
-    var type = getStaticType(context);
+    DartType type = getStaticType(context);
     while (type is TypeParameterType) {
       TypeParameterType typeParameterType = type;
       type =
@@ -3292,10 +3295,10 @@
 
   @override
   DartType getStaticTypeInternal(StaticTypeContext context) {
-    var interfaceTarget = this.interfaceTarget;
+    Member interfaceTarget = this.interfaceTarget;
     if (interfaceTarget != null) {
       Class superclass = interfaceTarget.enclosingClass;
-      var receiverType =
+      InterfaceType receiverType =
           receiver.getStaticTypeAsInstanceOf(superclass, context);
       return Substitution.fromInterfaceType(receiverType)
           .substituteType(interfaceTarget.getterType);
@@ -3847,7 +3850,7 @@
   }
 
   DartType getStaticTypeInternal(StaticTypeContext context) {
-    var interfaceTarget = this.interfaceTarget;
+    Member interfaceTarget = this.interfaceTarget;
     if (interfaceTarget != null) {
       if (interfaceTarget is Procedure &&
           context.typeEnvironment
@@ -3857,9 +3860,9 @@
             arguments.positional[0].getStaticType(context));
       }
       Class superclass = interfaceTarget.enclosingClass;
-      var receiverType =
+      DartType receiverType =
           receiver.getStaticTypeAsInstanceOf(superclass, context);
-      var getterType = Substitution.fromInterfaceType(receiverType)
+      DartType getterType = Substitution.fromInterfaceType(receiverType)
           .substituteType(interfaceTarget.getterType);
       if (getterType is FunctionType) {
         Substitution substitution;
@@ -3898,7 +3901,7 @@
       return const DynamicType();
     }
     if (name.text == 'call') {
-      var receiverType = receiver.getStaticType(context);
+      DartType receiverType = receiver.getStaticType(context);
       if (receiverType is FunctionType) {
         if (receiverType.typeParameters.length != arguments.types.length) {
           return const BottomType();
@@ -7923,7 +7926,7 @@
     int upper = namedParameters.length - 1;
     while (lower <= upper) {
       int pivot = (lower + upper) ~/ 2;
-      var namedParameter = namedParameters[pivot];
+      NamedType namedParameter = namedParameters[pivot];
       int comparison = name.compareTo(namedParameter.name);
       if (comparison == 0) {
         return namedParameter.type;
@@ -8499,9 +8502,10 @@
       throw new StateError("Can't compute nullability from an absent bound.");
     }
 
-    // If a type parameter's nullability depends on itself, it is deemed 'undetermined'.
-    // Currently, it's possible if the type parameter has a possibly nested FutureOr containing that type parameter.
-    // If there are other ways for such a dependency to exist, they should be checked here.
+    // If a type parameter's nullability depends on itself, it is deemed
+    // 'undetermined'. Currently, it's possible if the type parameter has a
+    // possibly nested FutureOr containing that type parameter.  If there are
+    // other ways for such a dependency to exist, they should be checked here.
     bool nullabilityDependsOnItself = false;
     {
       DartType type = typeParameter.bound;
@@ -9446,8 +9450,8 @@
 
   DartType getType(StaticTypeContext context) {
     final FunctionType type = tearOffConstant.getType(context);
-    final mapping = <TypeParameter, DartType>{};
-    for (final parameter in type.typeParameters) {
+    final Map<TypeParameter, DartType> mapping = <TypeParameter, DartType>{};
+    for (final TypeParameter parameter in type.typeParameters) {
       mapping[parameter] = types[mapping.length];
     }
     return substitute(type.withoutTypeParameters, mapping);
@@ -9859,7 +9863,7 @@
 }
 
 void visitIterable(Iterable<Node> nodes, Visitor visitor) {
-  for (var node in nodes) {
+  for (Node node in nodes) {
     node.accept(visitor);
   }
 }
@@ -9867,7 +9871,7 @@
 void transformTypeList(List<DartType> nodes, Transformer visitor) {
   int storeIndex = 0;
   for (int i = 0; i < nodes.length; ++i) {
-    var result = visitor.visitDartType(nodes[i]);
+    DartType result = visitor.visitDartType(nodes[i]);
     if (result != null) {
       nodes[storeIndex] = result;
       ++storeIndex;
@@ -9881,7 +9885,7 @@
 void transformSupertypeList(List<Supertype> nodes, Transformer visitor) {
   int storeIndex = 0;
   for (int i = 0; i < nodes.length; ++i) {
-    var result = visitor.visitSupertype(nodes[i]);
+    Supertype result = visitor.visitSupertype(nodes[i]);
     if (result != null) {
       nodes[storeIndex] = result;
       ++storeIndex;
@@ -9895,7 +9899,7 @@
 void transformList(List<TreeNode> nodes, Transformer visitor, TreeNode parent) {
   int storeIndex = 0;
   for (int i = 0; i < nodes.length; ++i) {
-    var result = nodes[i].accept(visitor);
+    TreeNode result = nodes[i].accept(visitor);
     if (result != null) {
       nodes[storeIndex] = result;
       result.parent = parent;
@@ -9999,7 +10003,7 @@
       return -1;
     }
     RangeError.checkValueInInterval(line, 1, lineStarts.length, 'line');
-    var offset = lineStarts[line - 1] + column - 1;
+    int offset = lineStarts[line - 1] + column - 1;
     RangeError.checkValueInInterval(offset, 0, lineStarts.last, 'offset');
     return offset;
   }
@@ -10174,15 +10178,15 @@
     return combine2Finish(object2.hashCode, object2.hashCode, 0);
   }
 
-  static int combineListHash(List list, [int hash = 1]) {
-    for (var item in list) {
+  static int combineListHash(List<Object> list, [int hash = 1]) {
+    for (Object item in list) {
       hash = _Hash.combine(item.hashCode, hash);
     }
     return hash;
   }
 
   static int combineList(List<int> hashes, int hash) {
-    for (var item in hashes) {
+    for (int item in hashes) {
       hash = combine(item, hash);
     }
     return hash;
@@ -10192,7 +10196,7 @@
     if (map == null || map.isEmpty) return hash;
     List<int> entryHashes = List(map.length);
     int i = 0;
-    for (var entry in map.entries) {
+    for (core.MapEntry entry in map.entries) {
       entryHashes[i++] = combine(entry.key.hashCode, entry.value.hashCode);
     }
     entryHashes.sort();
@@ -10216,8 +10220,12 @@
 }
 
 int mapHashCodeOrdered(Map map, [int hash = 2]) {
-  for (final Object x in map.keys) hash = _Hash.combine(x.hashCode, hash);
-  for (final Object x in map.values) hash = _Hash.combine(x.hashCode, hash);
+  for (final Object x in map.keys) {
+    hash = _Hash.combine(x.hashCode, hash);
+  }
+  for (final Object x in map.values) {
+    hash = _Hash.combine(x.hashCode, hash);
+  }
   return _Hash.finish(hash);
 }
 
@@ -10256,7 +10264,7 @@
 /// Annotation describing information which is not part of Dart semantics; in
 /// other words, if this information (or any information it refers to) changes,
 /// static analysis and runtime behavior of the library are unaffected.
-const informative = null;
+const Null informative = null;
 
 Location _getLocationInComponent(Component component, Uri fileUri, int offset) {
   if (component != null) {
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index c0af19e..70dd2ef 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -64,7 +64,7 @@
 }
 
 class _ComponentIndex {
-  static const numberOfFixedFields = 10;
+  static const int numberOfFixedFields = 10;
 
   int binaryOffsetForSourceTable;
   int binaryOffsetForCanonicalNames;
@@ -145,7 +145,7 @@
   int readByte() => _bytes[_byteOffset++];
 
   int readUInt() {
-    var byte = readByte();
+    int byte = readByte();
     if (byte & 0x80 == 0) {
       // 0xxxxxxx
       return byte;
@@ -354,7 +354,8 @@
         }
         return new InstanceConstant(classReference, typeArguments, fieldValues);
       case ConstantTag.PartialInstantiationConstant:
-        final tearOffConstant = readConstantReference() as TearOffConstant;
+        final TearOffConstant tearOffConstant =
+            readConstantReference() as TearOffConstant;
         final int length = readUInt();
         final List<DartType> types = new List<DartType>(length);
         for (int i = 0; i < length; i++) {
@@ -400,7 +401,7 @@
   }
 
   String readStringOrNullIfEmpty() {
-    var string = readStringReference();
+    String string = readStringReference();
     return string.isEmpty ? null : string;
   }
 
@@ -428,7 +429,7 @@
 
   void _fillTreeNodeList(
       List<TreeNode> list, TreeNode buildObject(int index), TreeNode parent) {
-    var length = readUInt();
+    int length = readUInt();
     list.length = length;
     for (int i = 0; i < length; ++i) {
       TreeNode object = buildObject(i);
@@ -437,7 +438,7 @@
   }
 
   void _fillNonTreeNodeList(List<Node> list, Node buildObject()) {
-    var length = readUInt();
+    int length = readUInt();
     list.length = length;
     for (int i = 0; i < length; ++i) {
       Node object = buildObject();
@@ -465,7 +466,7 @@
     for (int i = 0; i < length; ++i) {
       int biasedParentIndex = readUInt();
       String name = readStringReference();
-      var parent =
+      CanonicalName parent =
           biasedParentIndex == 0 ? linkRoot : _linkTable[biasedParentIndex - 1];
       _linkTable[i] = parent.getChild(name);
     }
@@ -497,7 +498,7 @@
   }
 
   void _readAndVerifySdkHash() {
-    final sdkHash = ascii.decode(readBytes(sdkHashLength));
+    final String sdkHash = ascii.decode(readBytes(sdkHashLength));
     if (!isValidSdkHash(sdkHash)) {
       throw InvalidKernelSdkVersionError(sdkHash);
     }
@@ -519,8 +520,8 @@
         "BinaryBuilder.readComponent", () {
       _checkEmptyInput();
 
-      // Check that we have a .dill file and it has the correct version before we
-      // start decoding it.  Otherwise we will fail for cryptic reasons.
+      // Check that we have a .dill file and it has the correct version before
+      // we start decoding it.  Otherwise we will fail for cryptic reasons.
       int offset = _byteOffset;
       int magic = readUint32();
       if (magic != Tag.ComponentFile) {
@@ -578,8 +579,8 @@
     }
   }
 
-  /// Reads a single component file from the input and loads it into [component],
-  /// overwriting and reusing any existing data in the component.
+  /// Reads a single component file from the input and loads it into
+  /// [component], overwriting and reusing any existing data in the component.
   ///
   /// When linking with a non-empty component, canonical names must have been
   /// computed ahead of time.
@@ -674,9 +675,9 @@
     result.libraryOffsets = new List<int>(result.libraryCount + 1);
     result.componentFileSizeInBytes = readUint32();
     if (result.componentFileSizeInBytes != componentFileSize) {
-      throw "Malformed binary: This component file's component index indicates that"
-          " the file size should be $componentFileSize but other component indexes"
-          " has indicated that the size should be "
+      throw "Malformed binary: This component file's component index indicates "
+          "that the file size should be $componentFileSize but other component "
+          "indexes has indicated that the size should be "
           "${result.componentFileSizeInBytes}.";
     }
 
@@ -868,7 +869,7 @@
   }
 
   CanonicalName readCanonicalNameReference() {
-    var index = readUInt();
+    int index = readUInt();
     if (index == 0) return null;
     return _linkTable[index - 1];
   }
@@ -891,7 +892,7 @@
   }
 
   Reference readClassReference({bool allowNull: false}) {
-    var name = readCanonicalNameReference();
+    CanonicalName name = readCanonicalNameReference();
     if (name == null && !allowNull) {
       throw 'Expected a class reference to be valid but was `null`.';
     }
@@ -899,7 +900,7 @@
   }
 
   Reference readMemberReference({bool allowNull: false}) {
-    var name = readCanonicalNameReference();
+    CanonicalName name = readCanonicalNameReference();
     if (name == null && !allowNull) {
       throw 'Expected a member reference to be valid but was `null`.';
     }
@@ -907,13 +908,13 @@
   }
 
   Reference readInstanceMemberReference({bool allowNull: false}) {
-    var reference = readMemberReference(allowNull: allowNull);
+    Reference reference = readMemberReference(allowNull: allowNull);
     readMemberReference(allowNull: true); // Skip origin
     return reference;
   }
 
   Reference getMemberReferenceFromInt(int index, {bool allowNull: false}) {
-    var name = getCanonicalNameReferenceFromInt(index);
+    CanonicalName name = getCanonicalNameReferenceFromInt(index);
     if (name == null && !allowNull) {
       throw 'Expected a member reference to be valid but was `null`.';
     }
@@ -965,7 +966,7 @@
     int languageVersionMajor = readUInt();
     int languageVersionMinor = readUInt();
 
-    var canonicalName = readCanonicalNameReference();
+    CanonicalName canonicalName = readCanonicalNameReference();
     Reference reference = canonicalName.getReference();
     Library library = reference.node;
     if (alwaysCreateNewNamedNodes) {
@@ -1041,12 +1042,12 @@
   }
 
   LibraryDependency readLibraryDependency(Library library) {
-    var fileOffset = readOffset();
-    var flags = readByte();
-    var annotations = readExpressionList();
-    var targetLibrary = readLibraryReference(allowNull: true);
-    var prefixName = readStringOrNullIfEmpty();
-    var names = readCombinatorList();
+    int fileOffset = readOffset();
+    int flags = readByte();
+    List<Expression> annotations = readExpressionList();
+    Reference targetLibrary = readLibraryReference(allowNull: true);
+    String prefixName = readStringOrNullIfEmpty();
+    List<Combinator> names = readCombinatorList();
     return new LibraryDependency.byReference(
         flags, annotations, targetLibrary, prefixName, names)
       ..fileOffset = fileOffset
@@ -1066,8 +1067,8 @@
   }
 
   Combinator readCombinator() {
-    var isShow = readByte() == 1;
-    var names = readStringReferenceList();
+    bool isShow = readByte() == 1;
+    List<String> names = readStringReferenceList();
     return new Combinator(isShow, names);
   }
 
@@ -1096,8 +1097,8 @@
   }
 
   Typedef readTypedef() {
-    var canonicalName = readCanonicalNameReference();
-    var reference = canonicalName.getReference();
+    CanonicalName canonicalName = readCanonicalNameReference();
+    Reference reference = canonicalName.getReference();
     Typedef node = reference.node;
     if (alwaysCreateNewNamedNodes) {
       node = null;
@@ -1110,7 +1111,7 @@
     String name = readStringReference();
     node.annotations = readAnnotationList(node);
     readAndPushTypeParameterList(node.typeParameters, node);
-    var type = readDartType();
+    DartType type = readDartType();
     readAndPushTypeParameterList(node.typeParametersOfFunctionType, node);
     node.positionalParameters.clear();
     node.positionalParameters.addAll(readAndPushVariableDeclarationList());
@@ -1143,8 +1144,8 @@
     }
     _byteOffset = savedByteOffset;
 
-    var canonicalName = readCanonicalNameReference();
-    var reference = canonicalName.getReference();
+    CanonicalName canonicalName = readCanonicalNameReference();
+    Reference reference = canonicalName.getReference();
     Class node = reference.node;
     if (alwaysCreateNewNamedNodes) {
       node = null;
@@ -1153,14 +1154,14 @@
       node = new Class(reference: reference)..dirty = false;
     }
 
-    var fileUri = readUriReference();
+    Uri fileUri = readUriReference();
     node.startFileOffset = readOffset();
     node.fileOffset = readOffset();
     node.fileEndOffset = readOffset();
     int flags = readByte();
     node.flags = flags;
-    var name = readStringOrNullIfEmpty();
-    var annotations = readAnnotationList(node);
+    String name = readStringOrNullIfEmpty();
+    List<Expression> annotations = readAnnotationList(node);
     assert(() {
       debugPath.add(node.name ?? 'normal-class');
       return true;
@@ -1169,8 +1170,8 @@
     assert(typeParameterStack.length == 0);
 
     readAndPushTypeParameterList(node.typeParameters, node);
-    var supertype = readSupertypeOption();
-    var mixedInType = readSupertypeOption();
+    Supertype supertype = readSupertypeOption();
+    Supertype mixedInType = readSupertypeOption();
     _fillNonTreeNodeList(node.implementedTypes, readSupertype);
     if (_disableLazyClassReading) {
       readClassPartialContent(node, procedureOffsets);
@@ -1297,18 +1298,18 @@
       node = new Field(null,
           getterReference: getterReference, setterReference: setterReference);
     }
-    var fileUri = readUriReference();
+    Uri fileUri = readUriReference();
     int fileOffset = readOffset();
     int fileEndOffset = readOffset();
     int flags = readUInt();
-    var name = readName();
-    var annotations = readAnnotationList(node);
+    Name name = readName();
+    List<Expression> annotations = readAnnotationList(node);
     assert(() {
       debugPath.add(node.name?.text ?? 'field');
       return true;
     }());
-    var type = readDartType();
-    var initializer = readExpressionOption();
+    DartType type = readDartType();
+    Expression initializer = readExpressionOption();
     int transformerFlags = getAndResetTransformerFlags();
     assert(((_) => true)(debugPath.removeLast()));
     node.fileOffset = fileOffset;
@@ -1327,8 +1328,8 @@
   Constructor readConstructor() {
     int tag = readByte();
     assert(tag == Tag.Constructor);
-    var canonicalName = readCanonicalNameReference();
-    var reference = canonicalName.getReference();
+    CanonicalName canonicalName = readCanonicalNameReference();
+    Reference reference = canonicalName.getReference();
     Constructor node = reference.node;
     if (alwaysCreateNewNamedNodes) {
       node = null;
@@ -1336,23 +1337,23 @@
     if (node == null) {
       node = new Constructor(null, reference: reference);
     }
-    var fileUri = readUriReference();
-    var startFileOffset = readOffset();
-    var fileOffset = readOffset();
-    var fileEndOffset = readOffset();
-    var flags = readByte();
-    var name = readName();
-    var annotations = readAnnotationList(node);
+    Uri fileUri = readUriReference();
+    int startFileOffset = readOffset();
+    int fileOffset = readOffset();
+    int fileEndOffset = readOffset();
+    int flags = readByte();
+    Name name = readName();
+    List<Expression> annotations = readAnnotationList(node);
     assert(() {
       debugPath.add(node.name?.text ?? 'constructor');
       return true;
     }());
-    var function = readFunctionNode();
+    FunctionNode function = readFunctionNode();
     pushVariableDeclarations(function.positionalParameters);
     pushVariableDeclarations(function.namedParameters);
     _fillTreeNodeList(node.initializers, (index) => readInitializer(), node);
     variableStack.length = 0;
-    var transformerFlags = getAndResetTransformerFlags();
+    int transformerFlags = getAndResetTransformerFlags();
     assert(((_) => true)(debugPath.removeLast()));
     node.startFileOffset = startFileOffset;
     node.fileOffset = fileOffset;
@@ -1369,26 +1370,26 @@
   Procedure readProcedure(int endOffset) {
     int tag = readByte();
     assert(tag == Tag.Procedure);
-    var canonicalName = readCanonicalNameReference();
-    var reference = canonicalName.getReference();
+    CanonicalName canonicalName = readCanonicalNameReference();
+    Reference reference = canonicalName.getReference();
     Procedure node = reference.node;
     if (alwaysCreateNewNamedNodes) {
       node = null;
     }
-    var fileUri = readUriReference();
-    var startFileOffset = readOffset();
-    var fileOffset = readOffset();
-    var fileEndOffset = readOffset();
+    Uri fileUri = readUriReference();
+    int startFileOffset = readOffset();
+    int fileOffset = readOffset();
+    int fileEndOffset = readOffset();
     int kindIndex = readByte();
-    var kind = ProcedureKind.values[kindIndex];
+    ProcedureKind kind = ProcedureKind.values[kindIndex];
     if (node == null) {
       node = new Procedure(null, kind, null, reference: reference);
     } else {
       assert(node.kind == kind);
     }
-    var flags = readUInt();
-    var name = readName();
-    var annotations = readAnnotationList(node);
+    int flags = readUInt();
+    Name name = readName();
+    List<Expression> annotations = readAnnotationList(node);
     assert(() {
       debugPath.add(node.name?.text ?? 'procedure');
       return true;
@@ -1398,13 +1399,15 @@
     bool readFunctionNodeNow =
         (kind == ProcedureKind.Factory && functionNodeSize <= 50) ||
             _disableLazyReading;
-    var forwardingStubSuperTargetReference =
+    Reference forwardingStubSuperTargetReference =
         readMemberReference(allowNull: true);
-    var forwardingStubInterfaceTargetReference =
+    Reference forwardingStubInterfaceTargetReference =
         readMemberReference(allowNull: true);
-    var memberSignatureTargetReference = readMemberReference(allowNull: true);
-    var function = readFunctionNodeOption(!readFunctionNodeNow, endOffset);
-    var transformerFlags = getAndResetTransformerFlags();
+    Reference memberSignatureTargetReference =
+        readMemberReference(allowNull: true);
+    FunctionNode function =
+        readFunctionNodeOption(!readFunctionNodeNow, endOffset);
+    int transformerFlags = getAndResetTransformerFlags();
     assert(((_) => true)(debugPath.removeLast()));
     node.startFileOffset = startFileOffset;
     node.fileOffset = fileOffset;
@@ -1434,8 +1437,8 @@
   RedirectingFactoryConstructor readRedirectingFactoryConstructor() {
     int tag = readByte();
     assert(tag == Tag.RedirectingFactoryConstructor);
-    var canonicalName = readCanonicalNameReference();
-    var reference = canonicalName.getReference();
+    CanonicalName canonicalName = readCanonicalNameReference();
+    Reference reference = canonicalName.getReference();
     RedirectingFactoryConstructor node = reference.node;
     if (alwaysCreateNewNamedNodes) {
       node = null;
@@ -1443,25 +1446,25 @@
     if (node == null) {
       node = new RedirectingFactoryConstructor(null, reference: reference);
     }
-    var fileUri = readUriReference();
-    var fileOffset = readOffset();
-    var fileEndOffset = readOffset();
-    var flags = readByte();
-    var name = readName();
-    var annotations = readAnnotationList(node);
+    Uri fileUri = readUriReference();
+    int fileOffset = readOffset();
+    int fileEndOffset = readOffset();
+    int flags = readByte();
+    Name name = readName();
+    List<Expression> annotations = readAnnotationList(node);
     assert(() {
       debugPath.add(node.name?.text ?? 'redirecting-factory-constructor');
       return true;
     }());
-    var targetReference = readMemberReference();
-    var typeArguments = readDartTypeList();
+    Reference targetReference = readMemberReference();
+    List<DartType> typeArguments = readDartTypeList();
     int typeParameterStackHeight = typeParameterStack.length;
-    var typeParameters = readAndPushTypeParameterList();
+    List<TypeParameter> typeParameters = readAndPushTypeParameterList();
     readUInt(); // Total parameter count.
-    var requiredParameterCount = readUInt();
+    int requiredParameterCount = readUInt();
     int variableStackHeight = variableStack.length;
-    var positional = readAndPushVariableDeclarationList();
-    var named = readAndPushVariableDeclarationList();
+    List<VariableDeclaration> positional = readAndPushVariableDeclarationList();
+    List<VariableDeclaration> named = readAndPushVariableDeclarationList();
     variableStack.length = variableStackHeight;
     typeParameterStack.length = typeParameterStackHeight;
     debugPath.removeLast();
@@ -1487,14 +1490,14 @@
       case Tag.InvalidInitializer:
         return new InvalidInitializer();
       case Tag.FieldInitializer:
-        var reference = readMemberReference();
-        var value = readExpression();
+        Reference reference = readMemberReference();
+        Expression value = readExpression();
         return new FieldInitializer.byReference(reference, value)
           ..isSynthetic = isSynthetic;
       case Tag.SuperInitializer:
         int offset = readOffset();
-        var reference = readMemberReference();
-        var arguments = readArguments();
+        Reference reference = readMemberReference();
+        Arguments arguments = readArguments();
         return new SuperInitializer.byReference(reference, arguments)
           ..isSynthetic = isSynthetic
           ..fileOffset = offset;
@@ -1528,13 +1531,13 @@
     AsyncMarker asyncMarker = AsyncMarker.values[readByte()];
     AsyncMarker dartAsyncMarker = AsyncMarker.values[readByte()];
     int typeParameterStackHeight = typeParameterStack.length;
-    var typeParameters = readAndPushTypeParameterList();
+    List<TypeParameter> typeParameters = readAndPushTypeParameterList();
     readUInt(); // total parameter count.
-    var requiredParameterCount = readUInt();
+    int requiredParameterCount = readUInt();
     int variableStackHeight = variableStack.length;
-    var positional = readAndPushVariableDeclarationList();
-    var named = readAndPushVariableDeclarationList();
-    var returnType = readDartType();
+    List<VariableDeclaration> positional = readAndPushVariableDeclarationList();
+    List<VariableDeclaration> named = readAndPushVariableDeclarationList();
+    DartType returnType = readDartType();
     int oldLabelStackBase = labelStackBase;
     int oldSwitchCaseStackBase = switchCaseStackBase;
 
@@ -1543,7 +1546,7 @@
           2; // e.g. outline has Tag.Something and Tag.EmptyStatement
     }
 
-    var body;
+    Statement body;
     if (!lazyLoadBody) {
       labelStackBase = labelStack.length;
       switchCaseStackBase = switchCaseStack.length;
@@ -1764,20 +1767,20 @@
           ..fileOffset = offset;
       case Tag.ListConcatenation:
         int offset = readOffset();
-        var typeArgument = readDartType();
+        DartType typeArgument = readDartType();
         return new ListConcatenation(readExpressionList(),
             typeArgument: typeArgument)
           ..fileOffset = offset;
       case Tag.SetConcatenation:
         int offset = readOffset();
-        var typeArgument = readDartType();
+        DartType typeArgument = readDartType();
         return new SetConcatenation(readExpressionList(),
             typeArgument: typeArgument)
           ..fileOffset = offset;
       case Tag.MapConcatenation:
         int offset = readOffset();
-        var keyType = readDartType();
-        var valueType = readDartType();
+        DartType keyType = readDartType();
+        DartType valueType = readDartType();
         return new MapConcatenation(readExpressionList(),
             keyType: keyType, valueType: valueType)
           ..fileOffset = offset;
@@ -1852,39 +1855,39 @@
         return new Throw(readExpression())..fileOffset = offset;
       case Tag.ListLiteral:
         int offset = readOffset();
-        var typeArgument = readDartType();
+        DartType typeArgument = readDartType();
         return new ListLiteral(readExpressionList(),
             typeArgument: typeArgument, isConst: false)
           ..fileOffset = offset;
       case Tag.ConstListLiteral:
         int offset = readOffset();
-        var typeArgument = readDartType();
+        DartType typeArgument = readDartType();
         return new ListLiteral(readExpressionList(),
             typeArgument: typeArgument, isConst: true)
           ..fileOffset = offset;
       case Tag.SetLiteral:
         int offset = readOffset();
-        var typeArgument = readDartType();
+        DartType typeArgument = readDartType();
         return new SetLiteral(readExpressionList(),
             typeArgument: typeArgument, isConst: false)
           ..fileOffset = offset;
       case Tag.ConstSetLiteral:
         int offset = readOffset();
-        var typeArgument = readDartType();
+        DartType typeArgument = readDartType();
         return new SetLiteral(readExpressionList(),
             typeArgument: typeArgument, isConst: true)
           ..fileOffset = offset;
       case Tag.MapLiteral:
         int offset = readOffset();
-        var keyType = readDartType();
-        var valueType = readDartType();
+        DartType keyType = readDartType();
+        DartType valueType = readDartType();
         return new MapLiteral(readMapEntryList(),
             keyType: keyType, valueType: valueType, isConst: false)
           ..fileOffset = offset;
       case Tag.ConstMapLiteral:
         int offset = readOffset();
-        var keyType = readDartType();
-        var valueType = readDartType();
+        DartType keyType = readDartType();
+        DartType valueType = readDartType();
         return new MapLiteral(readMapEntryList(),
             keyType: keyType, valueType: valueType, isConst: true)
           ..fileOffset = offset;
@@ -1894,21 +1897,21 @@
         int offset = readOffset();
         return new FunctionExpression(readFunctionNode())..fileOffset = offset;
       case Tag.Let:
-        var variable = readVariableDeclaration();
+        VariableDeclaration variable = readVariableDeclaration();
         int stackHeight = variableStack.length;
         pushVariableDeclaration(variable);
-        var body = readExpression();
+        Expression body = readExpression();
         variableStack.length = stackHeight;
         return new Let(variable, body);
       case Tag.BlockExpression:
         int stackHeight = variableStack.length;
-        var statements = readStatementList();
-        var value = readExpression();
+        List<Statement> statements = readStatementList();
+        Expression value = readExpression();
         variableStack.length = stackHeight;
         return new BlockExpression(new Block(statements), value);
       case Tag.Instantiation:
-        var expression = readExpression();
-        var typeArguments = readDartTypeList();
+        Expression expression = readExpression();
+        List<DartType> typeArguments = readDartTypeList();
         return new Instantiation(expression, typeArguments);
       case Tag.ConstantExpression:
         int offset = readOffset();
@@ -1945,7 +1948,7 @@
   }
 
   Statement readStatementOrNullIfEmpty() {
-    var node = readStatement();
+    Statement node = readStatement();
     if (node is EmptyStatement) {
       return null;
     } else {
@@ -1974,7 +1977,7 @@
             conditionEndOffset: readOffset(),
             message: readExpressionOption());
       case Tag.LabeledStatement:
-        var label = new LabeledStatement(null);
+        LabeledStatement label = new LabeledStatement(null);
         labelStack.add(label);
         label.body = readStatement()..parent = label;
         labelStack.removeLast();
@@ -1985,20 +1988,21 @@
         return new BreakStatement(labelStack[labelStackBase + index])
           ..fileOffset = offset;
       case Tag.WhileStatement:
-        var offset = readOffset();
+        int offset = readOffset();
         return new WhileStatement(readExpression(), readStatement())
           ..fileOffset = offset;
       case Tag.DoStatement:
-        var offset = readOffset();
+        int offset = readOffset();
         return new DoStatement(readStatement(), readExpression())
           ..fileOffset = offset;
       case Tag.ForStatement:
         int variableStackHeight = variableStack.length;
-        var offset = readOffset();
-        var variables = readAndPushVariableDeclarationList();
-        var condition = readExpressionOption();
-        var updates = readExpressionList();
-        var body = readStatement();
+        int offset = readOffset();
+        List<VariableDeclaration> variables =
+            readAndPushVariableDeclarationList();
+        Expression condition = readExpressionOption();
+        List<Expression> updates = readExpressionList();
+        Statement body = readStatement();
         variableStack.length = variableStackHeight;
         return new ForStatement(variables, condition, updates, body)
           ..fileOffset = offset;
@@ -2006,18 +2010,18 @@
       case Tag.AsyncForInStatement:
         bool isAsync = tag == Tag.AsyncForInStatement;
         int variableStackHeight = variableStack.length;
-        var offset = readOffset();
-        var bodyOffset = readOffset();
-        var variable = readAndPushVariableDeclaration();
-        var iterable = readExpression();
-        var body = readStatement();
+        int offset = readOffset();
+        int bodyOffset = readOffset();
+        VariableDeclaration variable = readAndPushVariableDeclaration();
+        Expression iterable = readExpression();
+        Statement body = readStatement();
         variableStack.length = variableStackHeight;
         return new ForInStatement(variable, iterable, body, isAsync: isAsync)
           ..fileOffset = offset
           ..bodyOffset = bodyOffset;
       case Tag.SwitchStatement:
-        var offset = readOffset();
-        var expression = readExpression();
+        int offset = readOffset();
+        Expression expression = readExpression();
         int count = readUInt();
         List<SwitchCase> cases =
             new List<SwitchCase>.filled(count, null, growable: true);
@@ -2058,14 +2062,14 @@
             isNative: flags & YieldStatement.FlagNative != 0)
           ..fileOffset = offset;
       case Tag.VariableDeclaration:
-        var variable = readVariableDeclaration();
+        VariableDeclaration variable = readVariableDeclaration();
         variableStack.add(variable); // Will be popped by the enclosing scope.
         return variable;
       case Tag.FunctionDeclaration:
         int offset = readOffset();
-        var variable = readVariableDeclaration();
+        VariableDeclaration variable = readVariableDeclaration();
         variableStack.add(variable); // Will be popped by the enclosing scope.
-        var function = readFunctionNode();
+        FunctionNode function = readFunctionNode();
         return new FunctionDeclaration(variable, function)..fileOffset = offset;
       default:
         throw fail('unexpected statement tag: $tag');
@@ -2095,11 +2099,11 @@
 
   Catch readCatch() {
     int variableStackHeight = variableStack.length;
-    var offset = readOffset();
-    var guard = readDartType();
-    var exception = readAndPushVariableDeclarationOption();
-    var stackTrace = readAndPushVariableDeclarationOption();
-    var body = readStatement();
+    int offset = readOffset();
+    DartType guard = readDartType();
+    VariableDeclaration exception = readAndPushVariableDeclarationOption();
+    VariableDeclaration stackTrace = readAndPushVariableDeclarationOption();
+    Statement body = readStatement();
     variableStack.length = variableStackHeight;
     return new Catch(exception, body, guard: guard, stackTrace: stackTrace)
       ..fileOffset = offset;
@@ -2107,9 +2111,9 @@
 
   Block readBlock() {
     int stackHeight = variableStack.length;
-    var offset = readOffset();
-    var endOffset = readOffset();
-    var body = readStatementList();
+    int offset = readOffset();
+    int endOffset = readOffset();
+    List<Statement> body = readStatementList();
     variableStack.length = stackHeight;
     return new Block(body)
       ..fileOffset = offset
@@ -2118,7 +2122,7 @@
 
   AssertBlock readAssertBlock() {
     int stackHeight = variableStack.length;
-    var body = readStatementList();
+    List<Statement> body = readStatementList();
     variableStack.length = stackHeight;
     return new AssertBlock(body);
   }
@@ -2232,14 +2236,14 @@
       case Tag.FunctionType:
         int typeParameterStackHeight = typeParameterStack.length;
         int nullabilityIndex = readByte();
-        var typeParameters = readAndPushTypeParameterList();
-        var requiredParameterCount = readUInt();
-        var totalParameterCount = readUInt();
-        var positional = readDartTypeList();
-        var named = readNamedTypeList();
-        var typedefType = readDartTypeOption();
+        List<TypeParameter> typeParameters = readAndPushTypeParameterList();
+        int requiredParameterCount = readUInt();
+        int totalParameterCount = readUInt();
+        List<DartType> positional = readDartTypeList();
+        List<NamedType> named = readNamedTypeList();
+        DartType typedefType = readDartTypeOption();
         assert(positional.length + named.length == totalParameterCount);
-        var returnType = readDartType();
+        DartType returnType = readDartType();
         typeParameterStack.length = typeParameterStackHeight;
         return new FunctionType(
             positional, returnType, Nullability.values[nullabilityIndex],
@@ -2249,14 +2253,14 @@
             typedefType: typedefType);
       case Tag.SimpleFunctionType:
         int nullabilityIndex = readByte();
-        var positional = readDartTypeList();
-        var returnType = readDartType();
+        List<DartType> positional = readDartTypeList();
+        DartType returnType = readDartType();
         return new FunctionType(
             positional, returnType, Nullability.values[nullabilityIndex]);
       case Tag.TypeParameterType:
         int declaredNullabilityIndex = readByte();
         int index = readUInt();
-        var bound = readDartTypeOption();
+        DartType bound = readDartTypeOption();
         return new TypeParameterType(typeParameterStack[index],
             Nullability.values[declaredNullabilityIndex], bound);
       default:
@@ -2301,10 +2305,10 @@
   }
 
   Arguments readArguments() {
-    var numArguments = readUInt();
-    var typeArguments = readDartTypeList();
-    var positional = readExpressionList();
-    var named = readNamedExpressionList();
+    int numArguments = readUInt();
+    List<DartType> typeArguments = readDartTypeList();
+    List<Expression> positional = readExpressionList();
+    List<NamedExpression> named = readNamedExpressionList();
     assert(numArguments == positional.length + named.length);
     return new Arguments(positional, types: typeArguments, named: named);
   }
@@ -2338,7 +2342,7 @@
   }
 
   VariableDeclaration readAndPushVariableDeclaration() {
-    var variable = readVariableDeclaration();
+    VariableDeclaration variable = readVariableDeclaration();
     variableStack.add(variable);
     return variable;
   }
@@ -2348,15 +2352,18 @@
     int fileEqualsOffset = readOffset();
     // The [VariableDeclaration] instance is not created at this point yet,
     // so `null` is temporarily set as the parent of the annotation nodes.
-    var annotations = readAnnotationList(null);
+    List<Expression> annotations = readAnnotationList(null);
     int flags = readByte();
-    var node = new VariableDeclaration(readStringOrNullIfEmpty(),
-        type: readDartType(), initializer: readExpressionOption(), flags: flags)
+    VariableDeclaration node = new VariableDeclaration(
+        readStringOrNullIfEmpty(),
+        type: readDartType(),
+        initializer: readExpressionOption(),
+        flags: flags)
       ..fileOffset = offset
       ..fileEqualsOffset = fileEqualsOffset;
     if (annotations.isNotEmpty) {
       for (int i = 0; i < annotations.length; ++i) {
-        var annotation = annotations[i];
+        Expression annotation = annotations[i];
         annotation.parent = node;
       }
       node.annotations = annotations;
@@ -2395,27 +2402,28 @@
 
     // Read the length of metadataMappings.
     _byteOffset -= 4;
-    final subSectionCount = readUint32();
+    final int subSectionCount = readUint32();
 
     int endOffset = _byteOffset - 4; // End offset of the current subsection.
-    for (var i = 0; i < subSectionCount; i++) {
+    for (int i = 0; i < subSectionCount; i++) {
       // RList<Pair<UInt32, UInt32>> nodeOffsetToMetadataOffset
       _byteOffset = endOffset - 4;
-      final mappingLength = readUint32();
-      final mappingStart = (endOffset - 4) - 4 * 2 * mappingLength;
+      final int mappingLength = readUint32();
+      final int mappingStart = (endOffset - 4) - 4 * 2 * mappingLength;
       _byteOffset = mappingStart - 4;
 
       // UInt32 tag (fixed size StringReference)
-      final tag = _stringTable[readUint32()];
+      final String tag = _stringTable[readUint32()];
 
-      final repository = component.metadata[tag];
+      final MetadataRepository repository = component.metadata[tag];
       if (repository != null) {
         // Read nodeOffsetToMetadataOffset mapping.
-        final mapping = <int, int>{};
+        final Map<int, int> mapping = <int, int>{};
         _byteOffset = mappingStart;
-        for (var j = 0; j < mappingLength; j++) {
-          final nodeOffset = readUint32();
-          final metadataOffset = binaryOffsetForMetadataPayloads + readUint32();
+        for (int j = 0; j < mappingLength; j++) {
+          final int nodeOffset = readUint32();
+          final int metadataOffset =
+              binaryOffsetForMetadataPayloads + readUint32();
           mapping[nodeOffset] = metadataOffset;
         }
 
@@ -2432,7 +2440,7 @@
     final int savedOffset = _byteOffset;
     _byteOffset = offset;
 
-    final metadata = repository.readFromBinary(node, this);
+    final Object metadata = repository.readFromBinary(node, this);
 
     _byteOffset = savedOffset;
     return metadata;
@@ -2458,9 +2466,9 @@
       return node;
     }
 
-    for (var subsection in _subsections) {
+    for (_MetadataSubsection subsection in _subsections) {
       // First check if there is any metadata associated with this node.
-      final metadataOffset = subsection.mapping[nodeOffset];
+      final int metadataOffset = subsection.mapping[nodeOffset];
       if (metadataOffset != null) {
         subsection.repository.mapping[node] =
             _readMetadata(node, subsection.repository, metadataOffset);
@@ -2472,136 +2480,137 @@
 
   @override
   DartType readDartType({bool forSupertype = false}) {
-    final nodeOffset = _byteOffset;
-    final result = super.readDartType(forSupertype: forSupertype);
+    final int nodeOffset = _byteOffset;
+    final DartType result = super.readDartType(forSupertype: forSupertype);
     return _associateMetadata(result, nodeOffset);
   }
 
   @override
   Library readLibrary(Component component, int endOffset) {
-    final nodeOffset = _byteOffset;
-    final result = super.readLibrary(component, endOffset);
+    final int nodeOffset = _byteOffset;
+    final Library result = super.readLibrary(component, endOffset);
     return _associateMetadata(result, nodeOffset);
   }
 
   @override
   Typedef readTypedef() {
-    final nodeOffset = _byteOffset;
-    final result = super.readTypedef();
+    final int nodeOffset = _byteOffset;
+    final Typedef result = super.readTypedef();
     return _associateMetadata(result, nodeOffset);
   }
 
   @override
   Class readClass(int endOffset) {
-    final nodeOffset = _byteOffset;
-    final result = super.readClass(endOffset);
+    final int nodeOffset = _byteOffset;
+    final Class result = super.readClass(endOffset);
     return _associateMetadata(result, nodeOffset);
   }
 
   @override
   Extension readExtension() {
-    final nodeOffset = _byteOffset;
-    final result = super.readExtension();
+    final int nodeOffset = _byteOffset;
+    final Extension result = super.readExtension();
     return _associateMetadata(result, nodeOffset);
   }
 
   @override
   Field readField() {
-    final nodeOffset = _byteOffset;
-    final result = super.readField();
+    final int nodeOffset = _byteOffset;
+    final Field result = super.readField();
     return _associateMetadata(result, nodeOffset);
   }
 
   @override
   Constructor readConstructor() {
-    final nodeOffset = _byteOffset;
-    final result = super.readConstructor();
+    final int nodeOffset = _byteOffset;
+    final Constructor result = super.readConstructor();
     return _associateMetadata(result, nodeOffset);
   }
 
   @override
   Procedure readProcedure(int endOffset) {
-    final nodeOffset = _byteOffset;
-    final result = super.readProcedure(endOffset);
+    final int nodeOffset = _byteOffset;
+    final Procedure result = super.readProcedure(endOffset);
     return _associateMetadata(result, nodeOffset);
   }
 
   @override
   RedirectingFactoryConstructor readRedirectingFactoryConstructor() {
-    final nodeOffset = _byteOffset;
-    final result = super.readRedirectingFactoryConstructor();
+    final int nodeOffset = _byteOffset;
+    final RedirectingFactoryConstructor result =
+        super.readRedirectingFactoryConstructor();
     return _associateMetadata(result, nodeOffset);
   }
 
   @override
   Initializer readInitializer() {
-    final nodeOffset = _byteOffset;
-    final result = super.readInitializer();
+    final int nodeOffset = _byteOffset;
+    final Initializer result = super.readInitializer();
     return _associateMetadata(result, nodeOffset);
   }
 
   @override
   FunctionNode readFunctionNode(
       {bool lazyLoadBody: false, int outerEndOffset: -1}) {
-    final nodeOffset = _byteOffset;
-    final result = super.readFunctionNode(
+    final int nodeOffset = _byteOffset;
+    final FunctionNode result = super.readFunctionNode(
         lazyLoadBody: lazyLoadBody, outerEndOffset: outerEndOffset);
     return _associateMetadata(result, nodeOffset);
   }
 
   @override
   Expression readExpression() {
-    final nodeOffset = _byteOffset;
-    final result = super.readExpression();
+    final int nodeOffset = _byteOffset;
+    final Expression result = super.readExpression();
     return _associateMetadata(result, nodeOffset);
   }
 
   @override
   Arguments readArguments() {
-    final nodeOffset = _byteOffset;
-    final result = super.readArguments();
+    final int nodeOffset = _byteOffset;
+    final Arguments result = super.readArguments();
     return _associateMetadata(result, nodeOffset);
   }
 
   @override
   NamedExpression readNamedExpression() {
-    final nodeOffset = _byteOffset;
-    final result = super.readNamedExpression();
+    final int nodeOffset = _byteOffset;
+    final NamedExpression result = super.readNamedExpression();
     return _associateMetadata(result, nodeOffset);
   }
 
   @override
   VariableDeclaration readVariableDeclaration() {
-    final nodeOffset = _byteOffset;
-    final result = super.readVariableDeclaration();
+    final int nodeOffset = _byteOffset;
+    final VariableDeclaration result = super.readVariableDeclaration();
     return _associateMetadata(result, nodeOffset);
   }
 
   @override
   Statement readStatement() {
-    final nodeOffset = _byteOffset;
-    final result = super.readStatement();
+    final int nodeOffset = _byteOffset;
+    final Statement result = super.readStatement();
     return _associateMetadata(result, nodeOffset);
   }
 
   @override
   Combinator readCombinator() {
-    final nodeOffset = _byteOffset;
-    final result = super.readCombinator();
+    final int nodeOffset = _byteOffset;
+    final Combinator result = super.readCombinator();
     return _associateMetadata(result, nodeOffset);
   }
 
   @override
   LibraryDependency readLibraryDependency(Library library) {
-    final nodeOffset = _byteOffset;
-    final result = super.readLibraryDependency(library);
+    final int nodeOffset = _byteOffset;
+    final LibraryDependency result = super.readLibraryDependency(library);
     return _associateMetadata(result, nodeOffset);
   }
 
   @override
   LibraryPart readLibraryPart(Library library) {
-    final nodeOffset = _byteOffset;
-    final result = super.readLibraryPart(library);
+    final int nodeOffset = _byteOffset;
+    final LibraryPart result = super.readLibraryPart(library);
     return _associateMetadata(result, nodeOffset);
   }
 
@@ -2619,7 +2628,7 @@
 
   @override
   Supertype readSupertype() {
-    final nodeOffset = _byteOffset;
+    final int nodeOffset = _byteOffset;
     InterfaceType type = super.readDartType();
     return _associateMetadata(
         new Supertype.byReference(type.className, type.typeArguments),
@@ -2628,8 +2637,8 @@
 
   @override
   Name readName() {
-    final nodeOffset = _byteOffset;
-    final result = super.readName();
+    final int nodeOffset = _byteOffset;
+    final Name result = super.readName();
     return _associateMetadata(result, nodeOffset);
   }
 
diff --git a/pkg/kernel/lib/binary/ast_to_binary.dart b/pkg/kernel/lib/binary/ast_to_binary.dart
index e7bd343..038a584 100644
--- a/pkg/kernel/lib/binary/ast_to_binary.dart
+++ b/pkg/kernel/lib/binary/ast_to_binary.dart
@@ -287,92 +287,92 @@
   }
 
   void writeNodeList(List<Node> nodes) {
-    final len = nodes.length;
+    final int len = nodes.length;
     writeUInt30(len);
     for (int i = 0; i < len; i++) {
-      final node = nodes[i];
+      final Node node = nodes[i];
       writeNode(node);
     }
   }
 
   void writeProcedureNodeList(List<Procedure> nodes) {
-    final len = nodes.length;
+    final int len = nodes.length;
     writeUInt30(len);
     for (int i = 0; i < len; i++) {
-      final node = nodes[i];
+      final Procedure node = nodes[i];
       writeProcedureNode(node);
     }
   }
 
   void writeFieldNodeList(List<Field> nodes) {
-    final len = nodes.length;
+    final int len = nodes.length;
     writeUInt30(len);
     for (int i = 0; i < len; i++) {
-      final node = nodes[i];
+      final Field node = nodes[i];
       writeFieldNode(node);
     }
   }
 
   void writeClassNodeList(List<Class> nodes) {
-    final len = nodes.length;
+    final int len = nodes.length;
     writeUInt30(len);
     for (int i = 0; i < len; i++) {
-      final node = nodes[i];
+      final Class node = nodes[i];
       writeClassNode(node);
     }
   }
 
   void writeExtensionNodeList(List<Extension> nodes) {
-    final len = nodes.length;
+    final int len = nodes.length;
     writeUInt30(len);
     for (int i = 0; i < len; i++) {
-      final node = nodes[i];
+      final Extension node = nodes[i];
       writeExtensionNode(node);
     }
   }
 
   void writeConstructorNodeList(List<Constructor> nodes) {
-    final len = nodes.length;
+    final int len = nodes.length;
     writeUInt30(len);
     for (int i = 0; i < len; i++) {
-      final node = nodes[i];
+      final Constructor node = nodes[i];
       writeConstructorNode(node);
     }
   }
 
   void writeRedirectingFactoryConstructorNodeList(
       List<RedirectingFactoryConstructor> nodes) {
-    final len = nodes.length;
+    final int len = nodes.length;
     writeUInt30(len);
     for (int i = 0; i < len; i++) {
-      final node = nodes[i];
+      final RedirectingFactoryConstructor node = nodes[i];
       writeRedirectingFactoryConstructorNode(node);
     }
   }
 
   void writeSwitchCaseNodeList(List<SwitchCase> nodes) {
-    final len = nodes.length;
+    final int len = nodes.length;
     writeUInt30(len);
     for (int i = 0; i < len; i++) {
-      final node = nodes[i];
+      final SwitchCase node = nodes[i];
       writeSwitchCaseNode(node);
     }
   }
 
   void writeCatchNodeList(List<Catch> nodes) {
-    final len = nodes.length;
+    final int len = nodes.length;
     writeUInt30(len);
     for (int i = 0; i < len; i++) {
-      final node = nodes[i];
+      final Catch node = nodes[i];
       writeCatchNode(node);
     }
   }
 
   void writeTypedefNodeList(List<Typedef> nodes) {
-    final len = nodes.length;
+    final int len = nodes.length;
     writeUInt30(len);
     for (int i = 0; i < len; i++) {
-      final node = nodes[i];
+      final Typedef node = nodes[i];
       writeTypedefNode(node);
     }
   }
@@ -538,7 +538,7 @@
     Timeline.timeSync("BinaryPrinter.writeComponentFile", () {
       compilationMode = component.mode;
       computeCanonicalNames(component);
-      final componentOffset = getBufferOffset();
+      final int componentOffset = getBufferOffset();
       writeUInt32(Tag.ComponentFile);
       writeUInt32(Tag.BinaryFormatVersion);
       writeBytes(ascii.encode(expectedSdkHash));
@@ -605,8 +605,8 @@
 
   void _writeNodeMetadataImpl(Node node, int nodeOffset) {
     for (_MetadataSubsection subsection in _metadataSubsections) {
-      final repository = subsection.repository;
-      final value = repository.mapping[node];
+      final MetadataRepository<Object> repository = subsection.repository;
+      final Object value = repository.mapping[node];
       if (value == null) {
         continue;
       }
@@ -695,7 +695,7 @@
       writeUInt32(stringIndexer.put(subsection.repository.tag));
 
       // RList<Pair<UInt32, UInt32>> nodeOffsetToMetadataOffset
-      final mappingLength = subsection.metadataMapping.length;
+      final int mappingLength = subsection.metadataMapping.length;
       for (int i = 0; i < mappingLength; i += 2) {
         writeUInt32(subsection.metadataMapping[i]); // node offset
         writeUInt32(subsection.metadataMapping[i + 1]); // metadata offset
@@ -1105,10 +1105,10 @@
   }
 
   void writeAnnotationList(List<Expression> annotations) {
-    final len = annotations.length;
+    final int len = annotations.length;
     writeUInt30(len);
     for (int i = 0; i < len; i++) {
-      final annotation = annotations[i];
+      final Expression annotation = annotations[i];
       writeAnnotation(annotation);
     }
   }
diff --git a/pkg/kernel/lib/binary/tag.dart b/pkg/kernel/lib/binary/tag.dart
index 5b46f8e..fced0fd 100644
--- a/pkg/kernel/lib/binary/tag.dart
+++ b/pkg/kernel/lib/binary/tag.dart
@@ -177,7 +177,7 @@
 // If null, local development setting (e.g. run gen_kernel.dart from source),
 // we put 0x00..00 into when producing, do not validate when consuming.
 String get expectedSdkHash {
-  final sdkHash =
+  final String sdkHash =
       const String.fromEnvironment('sdk_hash', defaultValue: sdkHashNull);
   if (sdkHash.length != sdkHashLength) {
     throw '-Dsdk_hash=<hash> must be a ${sdkHashLength} byte string!';
diff --git a/pkg/kernel/lib/canonical_name.dart b/pkg/kernel/lib/canonical_name.dart
index 526c32e..6a1a469 100644
--- a/pkg/kernel/lib/canonical_name.dart
+++ b/pkg/kernel/lib/canonical_name.dart
@@ -105,7 +105,7 @@
   }
 
   CanonicalName getChild(String name) {
-    var map = _children ??= <String, CanonicalName>{};
+    Map<String, CanonicalName> map = _children ??= <String, CanonicalName>{};
     return map[name] ??= new CanonicalName._(this, name);
   }
 
diff --git a/pkg/kernel/lib/class_hierarchy.dart b/pkg/kernel/lib/class_hierarchy.dart
index cb03a01..d14cfc3 100644
--- a/pkg/kernel/lib/class_hierarchy.dart
+++ b/pkg/kernel/lib/class_hierarchy.dart
@@ -388,7 +388,8 @@
     int index = _topDownSortIndex++;
     subInfo.topDownIndex = index;
     _classesByTopDownIndex[index] = subInfo.classInfo.classNode;
-    var subtypeSetBuilder = new _IntervalListBuilder()..addSingleton(index);
+    _IntervalListBuilder subtypeSetBuilder = new _IntervalListBuilder()
+      ..addSingleton(index);
     for (_ClassInfo subtype in subInfo.classInfo.directExtenders) {
       _ClassInfoSubtype subtypeInfo = _infoMap[subtype.classNode];
       _topDownSortVisit(subtypeInfo);
@@ -535,8 +536,10 @@
 
   @override
   Iterable<Class> getOrderedClasses(Iterable<Class> unordered) {
-    var unorderedSet = unordered.toSet();
-    for (Class c in unordered) _infoMap[c]?.used = true;
+    Set<Class> unorderedSet = unordered.toSet();
+    for (Class c in unordered) {
+      _infoMap[c]?.used = true;
+    }
     return _infoMap.keys.where(unorderedSet.contains);
   }
 
@@ -564,16 +567,16 @@
 
   List<_ClassInfo> _getRankedSuperclassInfos(_ClassInfo info) {
     if (info.leastUpperBoundInfos != null) return info.leastUpperBoundInfos;
-    var heap = new _LubHeap()..add(info);
-    var chain = <_ClassInfo>[];
+    _LubHeap heap = new _LubHeap()..add(info);
+    List<_ClassInfo> chain = <_ClassInfo>[];
     info.leastUpperBoundInfos = chain;
     _ClassInfo lastInfo = null;
     while (heap.isNotEmpty) {
-      var nextInfo = heap.remove();
+      _ClassInfo nextInfo = heap.remove();
       if (identical(nextInfo, lastInfo)) continue;
       chain.add(nextInfo);
       lastInfo = nextInfo;
-      var classNode = nextInfo.classNode;
+      Class classNode = nextInfo.classNode;
       void addToHeap(Supertype supertype) {
         heap.add(infoFor(supertype.classNode));
       }
@@ -682,11 +685,11 @@
         if (currentDepth == 0) return candidate;
         ++numCandidatesAtThisDepth;
       } else {
-        var superType1 = identical(info1, next)
+        InterfaceType superType1 = identical(info1, next)
             ? type1
             : Substitution.fromInterfaceType(type1).substituteType(
                 info1.genericSuperType[next.classNode].asInterfaceType);
-        var superType2 = identical(info2, next)
+        InterfaceType superType2 = identical(info2, next)
             ? type2
             : Substitution.fromInterfaceType(type2).substituteType(
                 info2.genericSuperType[next.classNode].asInterfaceType);
@@ -798,10 +801,11 @@
       callback(Member declaredMember, Member interfaceMember, bool isSetter),
       {bool crossGettersSetters: false}) {
     _ClassInfo info = infoFor(class_);
-    for (var supertype in class_.supers) {
-      var superclass = supertype.classNode;
-      var superGetters = getInterfaceMembers(superclass);
-      var superSetters = getInterfaceMembers(superclass, setters: true);
+    for (Supertype supertype in class_.supers) {
+      Class superclass = supertype.classNode;
+      List<Member> superGetters = getInterfaceMembers(superclass);
+      List<Member> superSetters =
+          getInterfaceMembers(superclass, setters: true);
       _reportOverrides(_buildDeclaredMembers(class_, info, setters: false),
           superGetters, callback);
       _reportOverrides(_buildDeclaredMembers(class_, info, setters: true),
@@ -837,7 +841,7 @@
 
   @override
   List<Supertype> genericSupertypesOf(Class class_) {
-    final supertypes = infoFor(class_).genericSuperType;
+    Map<Class, Supertype> supertypes = infoFor(class_).genericSuperType;
     if (supertypes == null) return const <Supertype>[];
     return supertypes.values.toList();
   }
@@ -1017,19 +1021,22 @@
 
     for (_ClassInfo info in _infoMap.values) {
       for (_ClassInfo subInfo in info.directExtenders) {
-        if (!_infoMap.containsKey(subInfo.classNode))
+        if (!_infoMap.containsKey(subInfo.classNode)) {
           throw new StateError(
               "Found $subInfo (${subInfo.classNode}) in directExtenders");
+        }
       }
       for (_ClassInfo subInfo in info.directMixers) {
-        if (!_infoMap.containsKey(subInfo.classNode))
+        if (!_infoMap.containsKey(subInfo.classNode)) {
           throw new StateError(
               "Found $subInfo (${subInfo.classNode}) in directMixers");
+        }
       }
       for (_ClassInfo subInfo in info.directImplementers) {
-        if (!_infoMap.containsKey(subInfo.classNode))
+        if (!_infoMap.containsKey(subInfo.classNode)) {
           throw new StateError(
               "Found $subInfo (${subInfo.classNode}) in directImplementers");
+        }
       }
     }
     return true;
@@ -1045,14 +1052,14 @@
     if (type.classNode == superclass) {
       return superclass.asThisSupertype;
     }
-    var map = infoFor(type.classNode)?.genericSuperType;
+    Map<Class, Supertype> map = infoFor(type.classNode)?.genericSuperType;
     return map == null ? null : map[superclass];
   }
 
   void _initialize(List<Library> libraries) {
     // Build the class ordering based on a topological sort.
-    for (var library in libraries) {
-      for (var classNode in library.classes) {
+    for (Library library in libraries) {
+      for (Class classNode in library.classes) {
         _topologicalSortVisit(classNode, new Set<Class>());
       }
       knownLibraries.add(library);
@@ -1079,7 +1086,7 @@
       if (class_.mixedInType != null) {
         _infoMap[class_.mixedInType.classNode].directMixers.add(info);
       }
-      for (var supertype in class_.implementedTypes) {
+      for (Supertype supertype in class_.implementedTypes) {
         _infoMap[supertype.classNode].directImplementers.add(info);
       }
       _collectSupersForClass(class_);
@@ -1114,7 +1121,7 @@
   int _topSortIndex = 0;
   int _topologicalSortVisit(Class classNode, Set<Class> beingVisited,
       {List<Class> orderedList}) {
-    var info = _infoMap[classNode];
+    _ClassInfo info = _infoMap[classNode];
     if (info != null) {
       return info.depth;
     }
@@ -1138,7 +1145,7 @@
           _topologicalSortVisit(classNode.mixedInType.classNode, beingVisited,
               orderedList: orderedList));
     }
-    for (var supertype in classNode.implementedTypes) {
+    for (Supertype supertype in classNode.implementedTypes) {
       superDepth = max(
           superDepth,
           _topologicalSortVisit(supertype.classNode, beingVisited,
@@ -1376,7 +1383,7 @@
       // Copy over all transitive generic super types, and substitute the
       // free variables with those provided in [supertype].
       Class superclass = supertype.classNode;
-      var substitution = Substitution.fromPairs(
+      Substitution substitution = Substitution.fromPairs(
           superclass.typeParameters, supertype.typeArguments);
       subInfo.genericSuperType ??= <Class, Supertype>{};
       subInfo.genericSuperTypes ??= <Class, List<Supertype>>{};
@@ -1396,9 +1403,9 @@
   void _collectSupersForClass(Class class_) {
     _ClassInfo info = _infoMap[class_];
 
-    var superclassSetBuilder = new _IntervalListBuilder()
+    _IntervalListBuilder superclassSetBuilder = new _IntervalListBuilder()
       ..addSingleton(info.topologicalIndex);
-    var supertypeSetBuilder = new _IntervalListBuilder()
+    _IntervalListBuilder supertypeSetBuilder = new _IntervalListBuilder()
       ..addSingleton(info.topologicalIndex);
 
     if (class_.supertype != null) {
@@ -1429,9 +1436,9 @@
   /// The more numbers are condensed near the beginning, the more efficient the
   /// internal data structure is.
   List<int> getExpenseHistogram() {
-    var result = <int>[];
+    List<int> result = <int>[];
     for (Class class_ in _infoMap.keys) {
-      var info = _infoMap[class_];
+      _ClassInfo info = _infoMap[class_];
       int intervals = info.supertypeIntervalList.length ~/ 2;
       if (intervals >= result.length) {
         int oldLength = result.length;
@@ -1452,7 +1459,7 @@
     int intervals = 0;
     int sizes = 0;
     for (Class class_ in _infoMap.keys) {
-      var info = _infoMap[class_];
+      _ClassInfo info = _infoMap[class_];
       intervals += (info.superclassIntervalList.length +
               info.supertypeIntervalList.length) ~/
           2;
@@ -1521,7 +1528,7 @@
       }
     }
     // Copy the results over to a typed array of the correct length.
-    var result = new Uint32List(storeIndex);
+    Uint32List result = new Uint32List(storeIndex);
     for (int i = 0; i < storeIndex; ++i) {
       result[i] = events[i];
     }
diff --git a/pkg/kernel/lib/clone.dart b/pkg/kernel/lib/clone.dart
index 950d052..b457805 100644
--- a/pkg/kernel/lib/clone.dart
+++ b/pkg/kernel/lib/clone.dart
@@ -103,6 +103,24 @@
     return result;
   }
 
+  /// Root entry point for cloning a subtree within the same context where the
+  /// file offsets are valid.
+  T cloneInContext<T extends TreeNode>(T node) {
+    assert(_activeFileUri == null);
+    _activeFileUri = _activeFileUriFromContext(node);
+    final TreeNode result = clone<T>(node);
+    _activeFileUri = null;
+    return result;
+  }
+
+  Uri _activeFileUriFromContext(TreeNode node) {
+    while (node != null) {
+      if (node is FileUriNode && node.fileUri != null) return node.fileUri;
+      node = node.parent;
+    }
+    return null;
+  }
+
   DartType visitType(DartType type) {
     return substitute(type, typeSubstitution);
   }
@@ -318,7 +336,7 @@
   }
 
   visitLet(Let node) {
-    var newVariable = clone(node.variable);
+    VariableDeclaration newVariable = clone(node.variable);
     return new Let(newVariable, clone(node.body));
   }
 
@@ -370,13 +388,13 @@
   }
 
   visitForStatement(ForStatement node) {
-    var variables = node.variables.map(clone).toList();
+    List<VariableDeclaration> variables = node.variables.map(clone).toList();
     return new ForStatement(variables, cloneOptional(node.condition),
         node.updates.map(clone).toList(), clone(node.body));
   }
 
   visitForInStatement(ForInStatement node) {
-    var newVariable = clone(node.variable);
+    VariableDeclaration newVariable = clone(node.variable);
     return new ForInStatement(
         newVariable, clone(node.iterable), clone(node.body),
         isAsync: node.isAsync)
@@ -396,7 +414,7 @@
   }
 
   visitSwitchCase(SwitchCase node) {
-    var switchCase = switchCases[node];
+    SwitchCase switchCase = switchCases[node];
     switchCase.body = clone(node.body)..parent = switchCase;
     return switchCase;
   }
@@ -420,8 +438,8 @@
   }
 
   visitCatch(Catch node) {
-    var newException = cloneOptional(node.exception);
-    var newStackTrace = cloneOptional(node.stackTrace);
+    VariableDeclaration newException = cloneOptional(node.exception);
+    VariableDeclaration newStackTrace = cloneOptional(node.stackTrace);
     return new Catch(newException, clone(node.body),
         stackTrace: newStackTrace, guard: visitType(node.guard));
   }
@@ -446,7 +464,7 @@
   }
 
   visitFunctionDeclaration(FunctionDeclaration node) {
-    var newVariable = clone(node.variable);
+    VariableDeclaration newVariable = clone(node.variable);
     return new FunctionDeclaration(newVariable, clone(node.function));
   }
 
@@ -487,9 +505,11 @@
 
   visitFunctionNode(FunctionNode node) {
     prepareTypeParameters(node.typeParameters);
-    var typeParameters = node.typeParameters.map(clone).toList();
-    var positional = node.positionalParameters.map(clone).toList();
-    var named = node.namedParameters.map(clone).toList();
+    List<TypeParameter> typeParameters =
+        node.typeParameters.map(clone).toList();
+    List<VariableDeclaration> positional =
+        node.positionalParameters.map(clone).toList();
+    List<VariableDeclaration> named = node.namedParameters.map(clone).toList();
     return new FunctionNode(cloneFunctionNodeBody(node),
         typeParameters: typeParameters,
         positionalParameters: positional,
diff --git a/pkg/kernel/lib/error_formatter.dart b/pkg/kernel/lib/error_formatter.dart
index 7bb8974..42265e7 100644
--- a/pkg/kernel/lib/error_formatter.dart
+++ b/pkg/kernel/lib/error_formatter.dart
@@ -37,15 +37,15 @@
     String sourceLine = null;
 
     // Try finding original source line.
-    final fileOffset = _findFileOffset(where);
+    final int fileOffset = _findFileOffset(where);
     if (fileOffset != TreeNode.noOffset) {
-      final fileUri = _fileUriOf(context);
+      final Uri fileUri = _fileUriOf(context);
 
-      final component = context.enclosingComponent;
-      final source = component.uriToSource[fileUri];
-      final location = component.getLocation(fileUri, fileOffset);
-      final lineStart = source.lineStarts[location.line - 1];
-      final lineEnd = (location.line < source.lineStarts.length)
+      final Component component = context.enclosingComponent;
+      final Source source = component.uriToSource[fileUri];
+      final Location location = component.getLocation(fileUri, fileOffset);
+      final int lineStart = source.lineStarts[location.line - 1];
+      final int lineEnd = (location.line < source.lineStarts.length)
           ? source.lineStarts[location.line]
           : (source.source.length - 1);
       if (lineStart < source.source.length &&
@@ -58,16 +58,17 @@
     }
 
     // Find the name of the enclosing member.
-    var name = "", body = context;
+    String name = "";
+    dynamic body = context;
     if (context is Class || context is Library) {
       name = context.name;
     } else if (context is Procedure || context is Constructor) {
-      final parent = context.parent;
-      final parentName =
+      final dynamic parent = context.parent;
+      final String parentName =
           parent is Class ? parent.name : (parent as Library).name;
       name = "${parentName}::${context.name.text}";
     } else {
-      final field = context as Field;
+      final Field field = context as Field;
       if (where is Field) {
         name = "${field.parent}.${field.name}";
       } else {
@@ -114,7 +115,7 @@
   }
 
   static Member _findEnclosingMember(TreeNode n) {
-    var context = n;
+    TreeNode context = n;
     while (context is! Member) {
       context = context.parent;
     }
@@ -125,7 +126,7 @@
 /// Extension of a [Printer] that highlights the given node using ANSI
 /// escape sequences.
 class HighlightingPrinter extends Printer {
-  final highlight;
+  final Node highlight;
 
   HighlightingPrinter(this.highlight)
       : super(new StringBuffer(), syntheticNames: globalDebuggingNames);
@@ -133,8 +134,8 @@
   @override
   bool shouldHighlight(Node node) => highlight == node;
 
-  static const kHighlightStart = ansiRed;
-  static const kHighlightEnd = ansiReset;
+  static const String kHighlightStart = ansiRed;
+  static const String kHighlightEnd = ansiReset;
 
   @override
   void startHighlight(Node node) {
@@ -150,7 +151,7 @@
   /// representation of the [highlight] node.
   static String stringifyContainingLines(Node node, Node highlight) {
     if (node == highlight) {
-      final firstLine = debugNodeToString(node).split('\n').first;
+      final String firstLine = debugNodeToString(node).split('\n').first;
       return "${kHighlightStart}${firstLine}${kHighlightEnd}";
     }
 
@@ -161,7 +162,7 @@
   }
 
   static Iterable<String> _onlyHighlightedLines(String text) sync* {
-    for (var line
+    for (String line
         in text.split('\n').skipWhile((l) => !l.contains(kHighlightStart))) {
       yield line;
       if (line.contains(kHighlightEnd)) {
@@ -171,7 +172,7 @@
   }
 }
 
-const ansiBlue = "\u001b[1;34m";
-const ansiYellow = "\u001b[1;33m";
-const ansiRed = "\u001b[1;31m";
-const ansiReset = "\u001b[0;0m";
+const String ansiBlue = "\u001b[1;34m";
+const String ansiYellow = "\u001b[1;33m";
+const String ansiRed = "\u001b[1;31m";
+const String ansiReset = "\u001b[0;0m";
diff --git a/pkg/kernel/lib/external_name.dart b/pkg/kernel/lib/external_name.dart
index 6577041..157981d 100644
--- a/pkg/kernel/lib/external_name.dart
+++ b/pkg/kernel/lib/external_name.dart
@@ -20,7 +20,7 @@
     return null;
   }
   for (final Expression annotation in procedure.annotations) {
-    final value = _getExternalNameValue(annotation);
+    final String value = _getExternalNameValue(annotation);
     if (value != null) {
       return value;
     }
@@ -30,9 +30,9 @@
 
 /// Returns native extension URIs for given [library].
 List<String> getNativeExtensionUris(Library library) {
-  final uris = <String>[];
-  for (var annotation in library.annotations) {
-    final value = _getExternalNameValue(annotation);
+  final List<String> uris = <String>[];
+  for (Expression annotation in library.annotations) {
+    final String value = _getExternalNameValue(annotation);
     if (value != null) {
       uris.add(value);
     }
@@ -46,7 +46,7 @@
       return (annotation.arguments.positional.single as StringLiteral).value;
     }
   } else if (annotation is ConstantExpression) {
-    final constant = annotation.constant;
+    final Constant constant = annotation.constant;
     if (constant is InstanceConstant) {
       if (_isExternalName(constant.classNode)) {
         return (constant.fieldValues.values.single as StringConstant).value;
diff --git a/pkg/kernel/lib/import_table.dart b/pkg/kernel/lib/import_table.dart
index 06d9051..051d4ab 100644
--- a/pkg/kernel/lib/import_table.dart
+++ b/pkg/kernel/lib/import_table.dart
@@ -76,8 +76,8 @@
   void addLibraryImport(Library target) {
     if (target == referenceLibrary) return; // Self-reference is special.
     if (target == null) return;
-    var referenceUri = referenceLibrary.importUri;
-    var targetUri = target.importUri;
+    Uri referenceUri = referenceLibrary.importUri;
+    Uri targetUri = target.importUri;
     if (targetUri == null) {
       throw '$referenceUri cannot refer to library without an import URI';
     }
diff --git a/pkg/kernel/lib/kernel.dart b/pkg/kernel/lib/kernel.dart
index 8504ef3..f30a6cd 100644
--- a/pkg/kernel/lib/kernel.dart
+++ b/pkg/kernel/lib/kernel.dart
@@ -39,14 +39,14 @@
 }
 
 Future writeComponentToBinary(Component component, String path) {
-  var sink;
+  IOSink sink;
   if (path == 'null' || path == 'stdout') {
     sink = stdout.nonBlocking;
   } else {
     sink = new File(path).openWrite();
   }
 
-  var future;
+  Future future;
   try {
     new BinaryPrinter(sink).writeComponentFile(component);
   } finally {
diff --git a/pkg/kernel/lib/library_index.dart b/pkg/kernel/lib/library_index.dart
index 8dbb8ee..d8e36e7 100644
--- a/pkg/kernel/lib/library_index.dart
+++ b/pkg/kernel/lib/library_index.dart
@@ -22,9 +22,9 @@
 
   /// Indexes the libraries with the URIs given in [libraryUris].
   LibraryIndex(Component component, Iterable<String> libraryUris) {
-    var libraryUriSet = libraryUris.toSet();
-    for (var library in component.libraries) {
-      var uri = '${library.importUri}';
+    Set<String> libraryUriSet = libraryUris.toSet();
+    for (Library library in component.libraries) {
+      String uri = '${library.importUri}';
       if (libraryUriSet.contains(uri)) {
         _libraries[uri] = new _ClassTable(library);
       }
@@ -37,7 +37,7 @@
 
   /// Indexes `dart:` libraries.
   LibraryIndex.coreLibraries(Component component) {
-    for (var library in component.libraries) {
+    for (Library library in component.libraries) {
       if (library.importUri.scheme == 'dart') {
         _libraries['${library.importUri}'] = new _ClassTable(library);
       }
@@ -49,7 +49,7 @@
   /// Consider using another constructor to only index the libraries that
   /// are needed.
   LibraryIndex.all(Component component) {
-    for (var library in component.libraries) {
+    for (Library library in component.libraries) {
       _libraries['${library.importUri}'] = new _ClassTable(library);
     }
   }
@@ -139,10 +139,10 @@
     if (_classes == null) {
       _classes = <String, _MemberTable>{};
       _classes[LibraryIndex.topLevel] = new _MemberTable.topLevel(this);
-      for (var class_ in library.classes) {
+      for (Class class_ in library.classes) {
         _classes[class_.name] = new _MemberTable.fromClass(this, class_);
       }
-      for (var extension_ in library.extensions) {
+      for (Extension extension_ in library.extensions) {
         _classes[extension_.name] =
             new _MemberTable.fromExtension(this, extension_);
       }
@@ -163,7 +163,7 @@
   }
 
   _MemberTable _getClassIndex(String name) {
-    var indexer = classes[name];
+    _MemberTable indexer = classes[name];
     if (indexer == null) {
       throw "Class '$name' not found in $containerName";
     }
@@ -237,17 +237,20 @@
 
   String getDisambiguatedExtensionName(
       ExtensionMemberDescriptor extensionMember) {
-    if (extensionMember.kind == ExtensionMemberKind.TearOff)
+    if (extensionMember.kind == ExtensionMemberKind.TearOff) {
       return LibraryIndex.tearoffPrefix + extensionMember.name.text;
-    if (extensionMember.kind == ExtensionMemberKind.Getter)
+    }
+    if (extensionMember.kind == ExtensionMemberKind.Getter) {
       return LibraryIndex.getterPrefix + extensionMember.name.text;
-    if (extensionMember.kind == ExtensionMemberKind.Setter)
+    }
+    if (extensionMember.kind == ExtensionMemberKind.Setter) {
       return LibraryIndex.setterPrefix + extensionMember.name.text;
+    }
     return extensionMember.name.text;
   }
 
   void addExtensionMember(ExtensionMemberDescriptor extensionMember) {
-    final replacement = extensionMember.member.node;
+    final NamedNode replacement = extensionMember.member.node;
     if (replacement is! Member) return;
     Member member = replacement;
     if (member.name.isPrivate && member.name.library != library) {
@@ -256,7 +259,7 @@
       return;
     }
 
-    final name = getDisambiguatedExtensionName(extensionMember);
+    final String name = getDisambiguatedExtensionName(extensionMember);
     _members[name] = replacement;
   }
 
@@ -271,12 +274,12 @@
   }
 
   Member getMember(String name) {
-    var member = members[name];
+    Member member = members[name];
     if (member == null) {
       String message = "A member with disambiguated name '$name' was not found "
           "in $containerName";
-      var getter = LibraryIndex.getterPrefix + name;
-      var setter = LibraryIndex.setterPrefix + name;
+      String getter = LibraryIndex.getterPrefix + name;
+      String setter = LibraryIndex.setterPrefix + name;
       if (members[getter] != null || members[setter] != null) {
         throw "$message. Did you mean '$getter' or '$setter'?";
       }
diff --git a/pkg/kernel/lib/naive_type_checker.dart b/pkg/kernel/lib/naive_type_checker.dart
index 2eb77b3..6f8d66d 100644
--- a/pkg/kernel/lib/naive_type_checker.dart
+++ b/pkg/kernel/lib/naive_type_checker.dart
@@ -41,9 +41,9 @@
   @override
   void checkOverride(
       Class host, Member ownMember, Member superMember, bool isSetter) {
-    final ownMemberIsFieldOrAccessor =
+    final bool ownMemberIsFieldOrAccessor =
         ownMember is Field || (ownMember as Procedure).isAccessor;
-    final superMemberIsFieldOrAccessor =
+    final bool superMemberIsFieldOrAccessor =
         superMember is Field || (superMember as Procedure).isAccessor;
 
     // TODO: move to error reporting code
@@ -51,7 +51,7 @@
       if (m is Field) {
         return 'field';
       } else {
-        final p = m as Procedure;
+        final Procedure p = m as Procedure;
         if (p.isGetter) {
           return 'getter';
         } else if (p.isSetter) {
@@ -75,7 +75,7 @@
       if (isSetter) {
         final DartType ownType = setterType(host, ownMember);
         final DartType superType = setterType(host, superMember);
-        final isCovariant = ownMember is Field
+        final bool isCovariant = ownMember is Field
             ? ownMember.isCovariant
             : ownMember.function.positionalParameters[0].isCovariant;
         if (!_isValidParameterOverride(isCovariant, ownType, superType)) {
@@ -99,7 +99,7 @@
         }
       }
     } else {
-      final msg = _checkFunctionOverride(host, ownMember, superMember);
+      final String msg = _checkFunctionOverride(host, ownMember, superMember);
       if (msg != null) {
         return failures.reportInvalidOverride(ownMember, superMember, msg);
       }
@@ -119,7 +119,7 @@
   }
 
   Substitution _makeSubstitutionForMember(Class host, Member member) {
-    final hostType =
+    final Supertype hostType =
         hierarchy.getClassAsInstanceOf(host, member.enclosingClass);
     return Substitution.fromSupertype(hostType);
   }
@@ -158,10 +158,11 @@
     }
 
     if (ownFunction.typeParameters.isNotEmpty) {
-      final typeParameterMap = <TypeParameter, DartType>{};
+      final Map<TypeParameter, DartType> typeParameterMap =
+          <TypeParameter, DartType>{};
       for (int i = 0; i < ownFunction.typeParameters.length; ++i) {
-        var subParameter = ownFunction.typeParameters[i];
-        var superParameter = superFunction.typeParameters[i];
+        TypeParameter subParameter = ownFunction.typeParameters[i];
+        TypeParameter superParameter = superFunction.typeParameters[i];
         typeParameterMap[subParameter] = new TypeParameterType.forAlphaRenaming(
             subParameter, superParameter);
       }
@@ -169,9 +170,9 @@
       ownSubstitution = Substitution.combine(
           ownSubstitution, Substitution.fromMap(typeParameterMap));
       for (int i = 0; i < ownFunction.typeParameters.length; ++i) {
-        var subParameter = ownFunction.typeParameters[i];
-        var superParameter = superFunction.typeParameters[i];
-        var subBound = ownSubstitution.substituteType(subParameter.bound);
+        TypeParameter subParameter = ownFunction.typeParameters[i];
+        TypeParameter superParameter = superFunction.typeParameters[i];
+        DartType subBound = ownSubstitution.substituteType(subParameter.bound);
         if (!_isSubtypeOf(
             superSubstitution.substituteType(superParameter.bound), subBound)) {
           return 'type parameters have incompatible bounds';
@@ -181,13 +182,15 @@
 
     if (!_isSubtypeOf(ownSubstitution.substituteType(ownFunction.returnType),
         superSubstitution.substituteType(superFunction.returnType))) {
-      return 'return type of override ${ownFunction.returnType} is not a subtype'
-          ' of ${superFunction.returnType}';
+      return 'return type of override ${ownFunction.returnType} is not a'
+          ' subtype of ${superFunction.returnType}';
     }
 
     for (int i = 0; i < superFunction.positionalParameters.length; ++i) {
-      final ownParameter = ownFunction.positionalParameters[i];
-      final superParameter = superFunction.positionalParameters[i];
+      final VariableDeclaration ownParameter =
+          ownFunction.positionalParameters[i];
+      final VariableDeclaration superParameter =
+          superFunction.positionalParameters[i];
       if (!_isValidParameterOverride(
           ownParameter.isCovariant,
           ownSubstitution.substituteType(ownParameter.type),
@@ -206,11 +209,13 @@
 
     // Note: FunctionNode.namedParameters are not sorted so we convert them
     // to map to make lookup faster.
-    final ownParameters = new Map<String, VariableDeclaration>.fromIterable(
-        ownFunction.namedParameters,
-        key: (v) => v.name);
+    final Map<String, VariableDeclaration> ownParameters =
+        new Map<String, VariableDeclaration>.fromIterable(
+            ownFunction.namedParameters,
+            key: (v) => v.name);
     for (VariableDeclaration superParameter in superFunction.namedParameters) {
-      final ownParameter = ownParameters[superParameter.name];
+      final VariableDeclaration ownParameter =
+          ownParameters[superParameter.name];
       if (ownParameter == null) {
         return 'override is missing ${superParameter.name} parameter';
       }
diff --git a/pkg/kernel/lib/src/bounds_checks.dart b/pkg/kernel/lib/src/bounds_checks.dart
index 7c5ee41..ac64f2c 100644
--- a/pkg/kernel/lib/src/bounds_checks.dart
+++ b/pkg/kernel/lib/src/bounds_checks.dart
@@ -438,7 +438,7 @@
   assert(bottomType == const NeverType(Nullability.nonNullable) ||
       bottomType is NullType);
   List<TypeArgumentIssue> result;
-  var substitutionMap = <TypeParameter, DartType>{};
+  Map<TypeParameter, DartType> substitutionMap = <TypeParameter, DartType>{};
   for (int i = 0; i < arguments.length; ++i) {
     substitutionMap[parameters[i]] = arguments[i];
   }
@@ -519,10 +519,10 @@
     return new TypedefType(
         type.typedefNode, type.nullability, replacedTypeArguments);
   } else if (type is FunctionType) {
-    var replacedReturnType = convertSuperBoundedToRegularBounded(
+    DartType replacedReturnType = convertSuperBoundedToRegularBounded(
         clientLibrary, typeEnvironment, type.returnType, bottomType,
         isCovariant: isCovariant);
-    var replacedPositionalParameters =
+    List<DartType> replacedPositionalParameters =
         new List<DartType>(type.positionalParameters.length);
     for (int i = 0; i < replacedPositionalParameters.length; i++) {
       replacedPositionalParameters[i] = convertSuperBoundedToRegularBounded(
@@ -532,7 +532,7 @@
           bottomType,
           isCovariant: !isCovariant);
     }
-    var replacedNamedParameters =
+    List<NamedType> replacedNamedParameters =
         new List<NamedType>(type.namedParameters.length);
     for (int i = 0; i < replacedNamedParameters.length; i++) {
       replacedNamedParameters[i] = new NamedType(
diff --git a/pkg/kernel/lib/src/merge_visitor.dart b/pkg/kernel/lib/src/merge_visitor.dart
index 4b1e68b..a3909da 100644
--- a/pkg/kernel/lib/src/merge_visitor.dart
+++ b/pkg/kernel/lib/src/merge_visitor.dart
@@ -283,8 +283,9 @@
       TypedefType a, TypedefType b, Nullability nullability) {
     assert(a.typedefNode == b.typedefNode);
     assert(a.typeArguments.length == b.typeArguments.length);
-    if (a.typeArguments.isEmpty)
+    if (a.typeArguments.isEmpty) {
       return new TypedefType(a.typedefNode, nullability);
+    }
     List<DartType> newTypeArguments =
         new List<DartType>(a.typeArguments.length);
     for (int i = 0; i < a.typeArguments.length; i++) {
diff --git a/pkg/kernel/lib/src/tool/batch_util.dart b/pkg/kernel/lib/src/tool/batch_util.dart
index c3cccb6..d0482f5 100644
--- a/pkg/kernel/lib/src/tool/batch_util.dart
+++ b/pkg/kernel/lib/src/tool/batch_util.dart
@@ -22,7 +22,7 @@
 Future runBatch(BatchCallback callback) async {
   int totalTests = 0;
   int testsFailed = 0;
-  var watch = new Stopwatch()..start();
+  Stopwatch watch = new Stopwatch()..start();
   print('>>> BATCH START');
   Stream input = stdin.transform(utf8.decoder).transform(new LineSplitter());
   await for (String line in input) {
@@ -33,9 +33,9 @@
       break;
     }
     ++totalTests;
-    var arguments = line.split(new RegExp(r'\s+'));
+    List<String> arguments = line.split(new RegExp(r'\s+'));
     try {
-      var outcome = await callback(arguments);
+      CompilerOutcome outcome = await callback(arguments);
       stderr.writeln('>>> EOF STDERR');
       if (outcome == CompilerOutcome.Ok) {
         print('>>> TEST PASS ${watch.elapsedMilliseconds}ms');
diff --git a/pkg/kernel/lib/src/tool/command_line_util.dart b/pkg/kernel/lib/src/tool/command_line_util.dart
index d6f3f61..1abadb2 100644
--- a/pkg/kernel/lib/src/tool/command_line_util.dart
+++ b/pkg/kernel/lib/src/tool/command_line_util.dart
@@ -19,8 +19,8 @@
   static requireVariableArgumentCount(
       List<int> ok, List<String> args, void Function() usage) {
     if (!ok.contains(args.length)) {
-      print(
-          "Expected the argument count to be one of ${ok}, got ${args.length}.");
+      print("Expected the argument count to be one of ${ok}, got "
+          "${args.length}.");
       usage();
     }
   }
diff --git a/pkg/kernel/lib/target/targets.dart b/pkg/kernel/lib/target/targets.dart
index 93ea3c9..62f7def 100644
--- a/pkg/kernel/lib/target/targets.dart
+++ b/pkg/kernel/lib/target/targets.dart
@@ -13,7 +13,7 @@
 
 class TargetFlags {
   final bool trackWidgetCreation;
-  final bool forceLateLoweringForTesting;
+  final int forceLateLoweringsForTesting;
   final bool forceLateLoweringSentinelForTesting;
   final bool forceStaticFieldLoweringForTesting;
   final bool forceNoExplicitGetterCallsForTesting;
@@ -21,7 +21,7 @@
 
   const TargetFlags(
       {this.trackWidgetCreation = false,
-      this.forceLateLoweringForTesting = false,
+      this.forceLateLoweringsForTesting = LateLowering.none,
       this.forceLateLoweringSentinelForTesting = false,
       this.forceStaticFieldLoweringForTesting = false,
       this.forceNoExplicitGetterCallsForTesting = false,
@@ -31,7 +31,7 @@
     if (identical(this, other)) return true;
     return other is TargetFlags &&
         trackWidgetCreation == other.trackWidgetCreation &&
-        forceLateLoweringForTesting == other.forceLateLoweringForTesting &&
+        forceLateLoweringsForTesting == other.forceLateLoweringsForTesting &&
         forceLateLoweringSentinelForTesting ==
             other.forceLateLoweringSentinelForTesting &&
         forceStaticFieldLoweringForTesting ==
@@ -45,7 +45,7 @@
     int hash = 485786;
     hash = 0x3fffffff & (hash * 31 + (hash ^ trackWidgetCreation.hashCode));
     hash = 0x3fffffff &
-        (hash * 31 + (hash ^ forceLateLoweringForTesting.hashCode));
+        (hash * 31 + (hash ^ forceLateLoweringsForTesting.hashCode));
     hash = 0x3fffffff &
         (hash * 31 + (hash ^ forceLateLoweringSentinelForTesting.hashCode));
     hash = 0x3fffffff &
@@ -281,12 +281,43 @@
   /// literals (for const set literals).
   bool get supportsSetLiterals => true;
 
-  /// Whether late fields and variable are support by this target.
+  /// Bit mask of [LateLowering] values for the late lowerings that should
+  /// be performed by the CFE.
   ///
-  /// If `false`, late fields and variables are lowered fields, getter, setters
-  /// etc. that provide equivalent semantics. See `pkg/kernel/nnbd_api.md` for
-  /// details.
-  bool get supportsLateFields;
+  /// For the selected lowerings, late fields and variables are encoded using
+  /// fields, getter, setters etc. in a way that provide equivalent semantics.
+  /// See `pkg/kernel/nnbd_api.md` for details.
+  int get enabledLateLowerings;
+
+  /// Returns `true` if the CFE should lower a late field given it
+  /// [hasInitializer], [isFinal], and [isStatic].
+  ///
+  /// This is determined by the [enabledLateLowerings] mask.
+  bool isLateFieldLoweringEnabled(
+      {bool hasInitializer, bool isFinal, bool isStatic}) {
+    assert(hasInitializer != null);
+    assert(isFinal != null);
+    assert(isStatic != null);
+    int mask = LateLowering.getFieldLowering(
+        hasInitializer: hasInitializer, isFinal: isFinal, isStatic: isStatic);
+    return enabledLateLowerings & mask != 0;
+  }
+
+  /// Returns `true` if the CFE should lower a late local variable given it
+  /// [hasInitializer], [isFinal], and its type [isPotentiallyNullable].
+  ///
+  /// This is determined by the [enabledLateLowerings] mask.
+  bool isLateLocalLoweringEnabled(
+      {bool hasInitializer, bool isFinal, bool isPotentiallyNullable}) {
+    assert(hasInitializer != null);
+    assert(isFinal != null);
+    assert(isPotentiallyNullable != null);
+    int mask = LateLowering.getLocalLowering(
+        hasInitializer: hasInitializer,
+        isFinal: isFinal,
+        isPotentiallyNullable: isPotentiallyNullable);
+    return enabledLateLowerings & mask != 0;
+  }
 
   /// If `true`, the backend supports creation and checking of a sentinel value
   /// for uninitialized late fields and variables through the `createSentinel`
@@ -359,7 +390,7 @@
   NoneTarget(this.flags);
 
   @override
-  bool get supportsLateFields => !flags.forceLateLoweringForTesting;
+  int get enabledLateLowerings => flags.forceLateLoweringsForTesting;
 
   @override
   bool get supportsLateLoweringSentinel =>
@@ -417,3 +448,97 @@
       // TODO(johnniwinther): Should this vary with the use case?
       const NoneConstantsBackend(supportsUnevaluatedConstants: true);
 }
+
+class LateLowering {
+  static const int nullableUninitializedNonFinalLocal = 1 << 0;
+  static const int nonNullableUninitializedNonFinalLocal = 1 << 1;
+  static const int nullableUninitializedFinalLocal = 1 << 2;
+  static const int nonNullableUninitializedFinalLocal = 1 << 3;
+  static const int nullableInitializedNonFinalLocal = 1 << 4;
+  static const int nonNullableInitializedNonFinalLocal = 1 << 5;
+  static const int nullableInitializedFinalLocal = 1 << 6;
+  static const int nonNullableInitializedFinalLocal = 1 << 7;
+  static const int uninitializedNonFinalStaticField = 1 << 8;
+  static const int uninitializedFinalStaticField = 1 << 9;
+  static const int initializedNonFinalStaticField = 1 << 10;
+  static const int initializedFinalStaticField = 1 << 11;
+  static const int uninitializedNonFinalInstanceField = 1 << 12;
+  static const int uninitializedFinalInstanceField = 1 << 13;
+  static const int initializedNonFinalInstanceField = 1 << 14;
+  static const int initializedFinalInstanceField = 1 << 15;
+
+  static const int none = 0;
+  static const int all = (1 << 16) - 1;
+
+  static int getLocalLowering(
+      {bool hasInitializer, bool isFinal, bool isPotentiallyNullable}) {
+    assert(hasInitializer != null);
+    assert(isFinal != null);
+    assert(isPotentiallyNullable != null);
+    if (hasInitializer) {
+      if (isFinal) {
+        if (isPotentiallyNullable) {
+          return nullableInitializedFinalLocal;
+        } else {
+          return nonNullableInitializedFinalLocal;
+        }
+      } else {
+        if (isPotentiallyNullable) {
+          return nullableInitializedNonFinalLocal;
+        } else {
+          return nonNullableInitializedNonFinalLocal;
+        }
+      }
+    } else {
+      if (isFinal) {
+        if (isPotentiallyNullable) {
+          return nullableUninitializedFinalLocal;
+        } else {
+          return nonNullableUninitializedFinalLocal;
+        }
+      } else {
+        if (isPotentiallyNullable) {
+          return nullableUninitializedNonFinalLocal;
+        } else {
+          return nonNullableUninitializedNonFinalLocal;
+        }
+      }
+    }
+  }
+
+  static int getFieldLowering(
+      {bool hasInitializer, bool isFinal, bool isStatic}) {
+    assert(hasInitializer != null);
+    assert(isFinal != null);
+    assert(isStatic != null);
+    if (hasInitializer) {
+      if (isFinal) {
+        if (isStatic) {
+          return initializedFinalStaticField;
+        } else {
+          return initializedFinalInstanceField;
+        }
+      } else {
+        if (isStatic) {
+          return initializedNonFinalStaticField;
+        } else {
+          return initializedNonFinalInstanceField;
+        }
+      }
+    } else {
+      if (isFinal) {
+        if (isStatic) {
+          return uninitializedFinalStaticField;
+        } else {
+          return uninitializedFinalInstanceField;
+        }
+      } else {
+        if (isStatic) {
+          return uninitializedNonFinalStaticField;
+        } else {
+          return uninitializedNonFinalInstanceField;
+        }
+      }
+    }
+  }
+}
diff --git a/pkg/kernel/lib/testing/mock_sdk_component.dart b/pkg/kernel/lib/testing/mock_sdk_component.dart
index 02ee6c8..180ad92 100644
--- a/pkg/kernel/lib/testing/mock_sdk_component.dart
+++ b/pkg/kernel/lib/testing/mock_sdk_component.dart
@@ -4,11 +4,12 @@
 
 import 'package:kernel/ast.dart';
 
-/// Returns a [Component] object containing empty definitions of core SDK classes.
+/// Returns a [Component] object containing empty definitions of core SDK
+/// classes.
 Component createMockSdkComponent() {
-  var coreLib = new Library(Uri.parse('dart:core'), name: 'dart.core');
-  var asyncLib = new Library(Uri.parse('dart:async'), name: 'dart.async');
-  var internalLib =
+  Library coreLib = new Library(Uri.parse('dart:core'), name: 'dart.core');
+  Library asyncLib = new Library(Uri.parse('dart:async'), name: 'dart.async');
+  Library internalLib =
       new Library(Uri.parse('dart:_internal'), name: 'dart._internal');
 
   Class addClass(Library lib, Class c) {
@@ -16,8 +17,9 @@
     return c;
   }
 
-  var objectClass = addClass(coreLib, new Class(name: 'Object'));
-  var objectType = new InterfaceType(objectClass, coreLib.nonNullable);
+  Class objectClass = addClass(coreLib, new Class(name: 'Object'));
+  InterfaceType objectType =
+      new InterfaceType(objectClass, coreLib.nonNullable);
 
   TypeParameter typeParam(String name, [DartType bound]) {
     return new TypeParameter(name, bound ?? objectType);
@@ -36,12 +38,12 @@
 
   addClass(coreLib, class_('Null'));
   addClass(coreLib, class_('bool'));
-  var num = addClass(coreLib, class_('num'));
+  Class num = addClass(coreLib, class_('num'));
   addClass(coreLib, class_('String'));
-  var iterable =
+  Class iterable =
       addClass(coreLib, class_('Iterable', typeParameters: [typeParam('T')]));
   {
-    var T = typeParam('T');
+    TypeParameter T = typeParam('T');
     addClass(
         coreLib,
         class_('List', typeParameters: [
diff --git a/pkg/kernel/lib/text/ast_to_text.dart b/pkg/kernel/lib/text/ast_to_text.dart
index e05a4b6..e109f0b 100644
--- a/pkg/kernel/lib/text/ast_to_text.dart
+++ b/pkg/kernel/lib/text/ast_to_text.dart
@@ -44,7 +44,7 @@
       //
       if (constant is InstanceConstant) {
         // Avoid visiting `InstanceConstant.classReference`.
-        for (final value in constant.fieldValues.values) {
+        for (final Constant value in constant.fieldValues.values) {
           // Name everything in post-order visit of DAG.
           getName(value);
         }
@@ -76,8 +76,8 @@
   final Set<String> usedNames = new Set<String>();
 
   String disambiguate(T key1, U key2, String proposeName()) {
-    getNewName() {
-      var proposedName = proposeName();
+    String getNewName() {
+      String proposedName = proposeName();
       if (usedNames.add(proposedName)) return proposedName;
       int i = 2;
       while (!usedNames.add('$proposedName$i')) {
@@ -209,13 +209,13 @@
 
   final RegExp pathSeparator = new RegExp('[\\/]');
 
-  nameLibraryPrefix(Library node, {String proposedName}) {
+  String nameLibraryPrefix(Library node, {String proposedName}) {
     return prefixes.disambiguate(node.reference, node.reference.canonicalName,
         () {
       if (proposedName != null) return proposedName;
       if (node.name != null) return abbreviateName(node.name);
       if (node.importUri != null) {
-        var path = node.importUri.hasEmptyPath
+        String path = node.importUri.hasEmptyPath
             ? '${node.importUri}'
             : node.importUri.pathSegments.last;
         if (path.endsWith('.dart')) {
@@ -233,7 +233,7 @@
       if (proposedName != null) return proposedName;
       CanonicalName canonicalName = name ?? node.canonicalName;
       if (canonicalName?.name != null) {
-        var path = canonicalName.name;
+        String path = canonicalName.name;
         int slash = path.lastIndexOf(pathSeparator);
         if (slash >= 0) {
           path = path.substring(slash + 1);
@@ -436,14 +436,14 @@
   }
 
   void printLibraryImportTable(LibraryImportTable imports) {
-    for (var library in imports.importedLibraries) {
-      var importPath = imports.getImportPath(library);
+    for (Library library in imports.importedLibraries) {
+      String importPath = imports.getImportPath(library);
       if (importPath == "") {
-        var prefix =
+        String prefix =
             syntheticNames.nameLibraryPrefix(library, proposedName: 'self');
         endLine('import self as $prefix;');
       } else {
-        var prefix = syntheticNames.nameLibraryPrefix(library);
+        String prefix = syntheticNames.nameLibraryPrefix(library);
         endLine('import "$importPath" as $prefix;');
       }
     }
@@ -476,7 +476,7 @@
     write('additionalExports = (');
     for (int i = 0; i < additionalExports.length; i++) {
       Reference reference = additionalExports[i];
-      var node = reference.node;
+      NamedNode node = reference.node;
       if (node is Class) {
         Library nodeLibrary = node.enclosingLibrary;
         String prefix = syntheticNames.nameLibraryPrefix(nodeLibrary);
@@ -514,7 +514,7 @@
 
   void writeComponentFile(Component component) {
     ImportTable imports = new ComponentImportTable(component);
-    var inner = createInner(imports, component.metadata);
+    Printer inner = createInner(imports, component.metadata);
     writeWord('main');
     writeSpaced('=');
     inner.writeMemberReferenceFromReference(component.mainMethodName);
@@ -523,7 +523,7 @@
       inner.writeMetadata(component);
     }
     writeComponentProblems(component);
-    for (var library in component.libraries) {
+    for (Library library in component.libraries) {
       if (showMetadata) {
         inner.writeMetadata(library);
       }
@@ -536,7 +536,7 @@
         writeSpaced('from');
         writeWord('"${library.importUri}"');
       }
-      var prefix = syntheticNames.nameLibraryPrefix(library);
+      String prefix = syntheticNames.nameLibraryPrefix(library);
       writeSpaced('as');
       writeWord(prefix);
       endLine(' {');
@@ -552,7 +552,7 @@
   void writeConstantTable(Component component) {
     if (syntheticNames.constants.map.isEmpty) return;
     ImportTable imports = new ComponentImportTable(component);
-    var inner = createInner(imports, component.metadata);
+    Printer inner = createInner(imports, component.metadata);
     writeWord('constants ');
     endLine(' {');
     ++inner.indentation;
@@ -619,7 +619,7 @@
     if (node == null) {
       writeSymbol("<Null>");
     } else {
-      final highlight = shouldHighlight(node);
+      final bool highlight = shouldHighlight(node);
       if (highlight) {
         startHighlight(node);
       }
@@ -647,8 +647,8 @@
 
   void writeMetadata(TreeNode node) {
     if (metadata != null) {
-      for (var md in metadata.values) {
-        final nodeMetadata = md.mapping[node];
+      for (MetadataRepository<Object> md in metadata.values) {
+        final Object nodeMetadata = md.mapping[node];
         if (nodeMetadata != null) {
           writeWord("[@${md.tag}=${nodeMetadata}]");
         }
@@ -931,7 +931,7 @@
   void writeList<T>(Iterable<T> nodes, void callback(T x),
       {String separator: ','}) {
     bool first = true;
-    for (var node in nodes) {
+    for (T node in nodes) {
       if (first) {
         first = false;
       } else {
@@ -948,8 +948,9 @@
   String getClassReferenceFromReference(Reference reference) {
     if (reference == null) return '<No Class>';
     if (reference.node != null) return getClassReference(reference.asClass);
-    if (reference.canonicalName != null)
+    if (reference.canonicalName != null) {
       return getCanonicalNameString(reference.canonicalName);
+    }
     throw "Neither node nor canonical name found";
   }
 
@@ -960,8 +961,9 @@
   String getMemberReferenceFromReference(Reference reference) {
     if (reference == null) return '<No Member>';
     if (reference.node != null) return getMemberReference(reference.asMember);
-    if (reference.canonicalName != null)
+    if (reference.canonicalName != null) {
       return getCanonicalNameString(reference.canonicalName);
+    }
     throw "Neither node nor canonical name found";
   }
 
@@ -970,8 +972,9 @@
     if (name.name.startsWith('@')) throw 'unexpected @';
 
     libraryString(CanonicalName lib) {
-      if (lib.reference?.node != null)
+      if (lib.reference?.node != null) {
         return getLibraryReference(lib.reference.asLibrary);
+      }
       return syntheticNames.nameCanonicalNameAsLibraryPrefix(
           lib.reference, lib);
     }
@@ -983,7 +986,9 @@
     if (name.parent.parent.isRoot) return classString(name);
 
     CanonicalName atNode = name.parent;
-    while (!atNode.name.startsWith('@')) atNode = atNode.parent;
+    while (!atNode.name.startsWith('@')) {
+      atNode = atNode.parent;
+    }
 
     String parent = "";
     if (atNode.parent.parent.isRoot) {
@@ -1001,7 +1006,7 @@
   }
 
   void writeVariableReference(VariableDeclaration variable) {
-    final highlight = shouldHighlight(variable);
+    final bool highlight = shouldHighlight(variable);
     if (highlight) {
       startHighlight(variable);
     }
@@ -1016,7 +1021,7 @@
   }
 
   void writeExpression(Expression node, [int minimumPrecedence]) {
-    final highlight = shouldHighlight(node);
+    final bool highlight = shouldHighlight(node);
     if (highlight) {
       startHighlight(node);
     }
@@ -1252,7 +1257,7 @@
     if (features.isNotEmpty) {
       writeSpaced('/*${features.join(',')}*/');
     }
-    var endLineString = ' {';
+    String endLineString = ' {';
     if (node.enclosingLibrary.fileUri != node.fileUri) {
       endLineString += ' // from ${node.fileUri}';
     }
@@ -1274,7 +1279,7 @@
     writeTypeParameterList(node.typeParameters);
     writeSpaced('on');
     writeType(node.onType);
-    var endLineString = ' {';
+    String endLineString = ' {';
     if (node.enclosingLibrary.fileUri != node.fileUri) {
       endLineString += ' // from ${node.fileUri}';
     }
@@ -1415,7 +1420,7 @@
       writeSpace();
     }
     write('"');
-    for (var part in node.expressions) {
+    for (Expression part in node.expressions) {
       if (part is StringLiteral) {
         writeSymbol(escapeString(part.value));
       } else {
@@ -1677,7 +1682,7 @@
   visitLibraryDependency(LibraryDependency node) {
     writeIndentation();
     writeWord(node.isImport ? 'import' : 'export');
-    var uriString;
+    String uriString;
     if (node.importedLibraryReference?.node != null) {
       uriString = '${node.targetLibrary.importUri}';
     } else {
@@ -1925,7 +1930,7 @@
     writeIndentation();
     writeWord(label);
     endLine(':');
-    for (var expression in node.expressions) {
+    for (Expression expression in node.expressions) {
       writeIndentation();
       writeWord('case');
       writeExpression(expression);
@@ -2072,7 +2077,7 @@
       writeSymbol('>');
     }
     writeSymbol('(');
-    var allArgs =
+    Iterable<TreeNode> allArgs =
         <List<TreeNode>>[node.positional, node.named].expand((x) => x);
     writeList(allArgs, writeNode);
     writeSymbol(')');
diff --git a/pkg/kernel/lib/text/serializer_combinators.dart b/pkg/kernel/lib/text/serializer_combinators.dart
index 39435d7..d25d6ae 100644
--- a/pkg/kernel/lib/text/serializer_combinators.dart
+++ b/pkg/kernel/lib/text/serializer_combinators.dart
@@ -81,8 +81,8 @@
       prefix = "ID";
     }
     String distinctName = "$prefix$separator${nameCount++}";
-    // The following checks for an internal error, not an error caused by the user.
-    // So, an assert is used instead of an exception.
+    // The following checks for an internal error, not an error caused by the
+    // user. So, an assert is used instead of an exception.
     assert(
         lookupDistinctName(node) == null,
         "Can't assign distinct name '${distinctName}' "
@@ -667,7 +667,7 @@
   const Bind(this.pattern, this.term);
 
   Tuple2<P, T> readFrom(Iterator<Object> stream, DeserializationState state) {
-    var bindingState = new DeserializationState(
+    DeserializationState bindingState = new DeserializationState(
         new DeserializationEnvironment(state.environment), state.nameRoot);
     P first = pattern.readFrom(stream, bindingState);
     bindingState.environment.extend();
@@ -677,7 +677,7 @@
 
   void writeTo(
       StringBuffer buffer, Tuple2<P, T> tuple, SerializationState state) {
-    var bindingState =
+    SerializationState bindingState =
         new SerializationState(new SerializationEnvironment(state.environment));
     pattern.writeTo(buffer, tuple.first, bindingState);
     bindingState.environment.extend();
@@ -699,7 +699,7 @@
 
   Tuple2<P, T> readFrom(Iterator<Object> stream, DeserializationState state) {
     P first = pattern1.readFrom(stream, state);
-    var closedState = new DeserializationState(
+    DeserializationState closedState = new DeserializationState(
         new DeserializationEnvironment(state.environment)
           ..binders.addAll(state.environment.binders)
           ..extend(),
@@ -712,7 +712,7 @@
   void writeTo(
       StringBuffer buffer, Tuple2<P, T> tuple, SerializationState state) {
     pattern1.writeTo(buffer, tuple.first, state);
-    var closedState =
+    SerializationState closedState =
         new SerializationState(new SerializationEnvironment(state.environment)
           ..binders.addAll(state.environment.binders)
           ..extend());
diff --git a/pkg/kernel/lib/text/text_reader.dart b/pkg/kernel/lib/text/text_reader.dart
index e066fde..a6867e7 100644
--- a/pkg/kernel/lib/text/text_reader.dart
+++ b/pkg/kernel/lib/text/text_reader.dart
@@ -40,7 +40,7 @@
   void skipToEndOfNested() {
     if (current is TextIterator) {
       TextIterator it = current;
-      while (it.moveNext());
+      while (it.moveNext()) {}
       index = it.index + 1;
     }
   }
diff --git a/pkg/kernel/lib/transformations/track_widget_constructor_locations.dart b/pkg/kernel/lib/transformations/track_widget_constructor_locations.dart
index 7585b5c..12901a9 100644
--- a/pkg/kernel/lib/transformations/track_widget_constructor_locations.dart
+++ b/pkg/kernel/lib/transformations/track_widget_constructor_locations.dart
@@ -11,8 +11,8 @@
 
 // Parameter name used to track were widget constructor calls were made from.
 //
-// The parameter name contains a randomly generated hex string to avoid collision
-// with user generated parameters.
+// The parameter name contains a randomly generated hex string to avoid
+// collision with user generated parameters.
 const String _creationLocationParameterName =
     r'$creationLocationd_0dea112b090073317d4';
 
@@ -433,11 +433,12 @@
         // arguments but it is possible users could add classes with optional
         // positional arguments.
         //
-        // constructor.initializers.add(new AssertInitializer(new AssertStatement(
-        //   new IsExpression(
-        //       new VariableGet(variable), _locationClass.thisType),
-        //   conditionStartOffset: constructor.fileOffset,
-        //   conditionEndOffset: constructor.fileOffset,
+        // constructor.initializers.add(new AssertInitializer(
+        //   new AssertStatement(
+        //     new IsExpression(
+        //         new VariableGet(variable), _locationClass.thisType),
+        //     conditionStartOffset: constructor.fileOffset,
+        //     conditionEndOffset: constructor.fileOffset,
         // )));
       }
     }
@@ -569,7 +570,8 @@
           initializer: new NullLiteral());
       if (_hasNamedParameter(
           constructor.function, _creationLocationParameterName)) {
-        // Constructor was already rewritten. TODO(jacobr): is this case actually hit?
+        // Constructor was already rewritten.
+        // TODO(jacobr): is this case actually hit?
         return;
       }
       if (!_maybeAddNamedParameter(constructor.function, variable)) {
diff --git a/pkg/kernel/lib/type_algebra.dart b/pkg/kernel/lib/type_algebra.dart
index b9ca3949..4ec2480 100644
--- a/pkg/kernel/lib/type_algebra.dart
+++ b/pkg/kernel/lib/type_algebra.dart
@@ -34,11 +34,11 @@
 
 Map<TypeParameter, DartType> getUpperBoundSubstitutionMap(Class host) {
   if (host.typeParameters.isEmpty) return const <TypeParameter, DartType>{};
-  var result = <TypeParameter, DartType>{};
-  for (var parameter in host.typeParameters) {
+  Map<TypeParameter, DartType> result = <TypeParameter, DartType>{};
+  for (TypeParameter parameter in host.typeParameters) {
     result[parameter] = const DynamicType();
   }
-  for (var parameter in host.typeParameters) {
+  for (TypeParameter parameter in host.typeParameters) {
     result[parameter] = substitute(parameter.bound, result);
   }
   return result;
@@ -63,8 +63,8 @@
 DartType substituteDeep(
     DartType type, Map<TypeParameter, DartType> substitution) {
   if (substitution.isEmpty) return type;
-  var substitutor = new _DeepTypeSubstitutor(substitution);
-  var result = substitutor.visit(type);
+  _DeepTypeSubstitutor substitutor = new _DeepTypeSubstitutor(substitution);
+  DartType result = substitutor.visit(type);
   return substitutor.isInfinite ? null : result;
 }
 
@@ -93,37 +93,16 @@
   return new _FreeFunctionTypeVariableVisitor().visit(type);
 }
 
-/// Given a set of type variables, finds a substitution of those variables such
-/// that the two given types becomes equal, or returns `null` if no such
-/// substitution exists.
-///
-/// For example, unifying `List<T>` with `List<String>`, where `T` is a
-/// quantified variable, yields the substitution `T = String`.
-///
-/// If successful, this equation holds:
-///
-///     substitute(type1, substitution) == substitute(type2, substitution)
-///
-/// The unification can fail for two reasons:
-/// - incompatible types, e.g. `List<T>` cannot be unified with `Set<T>`.
-/// - infinite types: e.g. `T` cannot be unified with `List<T>` because it
-///   would create the infinite type `List<List<List<...>>>`.
-Map<TypeParameter, DartType> unifyTypes(
-    DartType type1, DartType type2, Set<TypeParameter> quantifiedVariables) {
-  var unifier = new _TypeUnification(type1, type2, quantifiedVariables);
-  return unifier.success ? unifier.substitution : null;
-}
-
 /// Generates a fresh copy of the given type parameters, with their bounds
 /// substituted to reference the new parameters.
 ///
 /// The returned object contains the fresh type parameter list as well as a
 /// mapping to be used for replacing other types to use the new type parameters.
 FreshTypeParameters getFreshTypeParameters(List<TypeParameter> typeParameters) {
-  var freshParameters = new List<TypeParameter>.generate(
+  List<TypeParameter> freshParameters = new List<TypeParameter>.generate(
       typeParameters.length, (i) => new TypeParameter(typeParameters[i].name),
       growable: true);
-  var map = <TypeParameter, DartType>{};
+  Map<TypeParameter, DartType> map = <TypeParameter, DartType>{};
   for (int i = 0; i < typeParameters.length; ++i) {
     map[typeParameters[i]] = new TypeParameterType.forAlphaRenaming(
         typeParameters[i], freshParameters[i]);
@@ -255,11 +234,11 @@
   /// been replaced by dynamic.
   static Substitution upperBoundForClass(Class class_) {
     if (class_.typeParameters.isEmpty) return _NullSubstitution.instance;
-    var upper = <TypeParameter, DartType>{};
-    for (var parameter in class_.typeParameters) {
+    Map<TypeParameter, DartType> upper = <TypeParameter, DartType>{};
+    for (TypeParameter parameter in class_.typeParameters) {
       upper[parameter] = const DynamicType();
     }
-    for (var parameter in class_.typeParameters) {
+    for (TypeParameter parameter in class_.typeParameters) {
       upper[parameter] = substitute(parameter.bound, upper);
     }
     return fromUpperAndLowerBounds(upper, {});
@@ -483,14 +462,14 @@
   Supertype visitSupertype(Supertype node) {
     if (node.typeArguments.isEmpty) return node;
     int before = useCounter;
-    var typeArguments = node.typeArguments.map(visit).toList();
+    List<DartType> typeArguments = node.typeArguments.map(visit).toList();
     if (useCounter == before) return node;
     return new Supertype(node.classNode, typeArguments);
   }
 
   NamedType visitNamedType(NamedType node) {
     int before = useCounter;
-    var type = visit(node.type);
+    DartType type = visit(node.type);
     if (useCounter == before) return node;
     return new NamedType(node.name, type, isRequired: node.isRequired);
   }
@@ -508,7 +487,7 @@
   DartType visitInterfaceType(InterfaceType node) {
     if (node.typeArguments.isEmpty) return node;
     int before = useCounter;
-    var typeArguments = node.typeArguments.map(visit).toList();
+    List<DartType> typeArguments = node.typeArguments.map(visit).toList();
     if (useCounter == before) return node;
     return new InterfaceType(node.classNode, node.nullability, typeArguments);
   }
@@ -523,7 +502,7 @@
   DartType visitTypedefType(TypedefType node) {
     if (node.typeArguments.isEmpty) return node;
     int before = useCounter;
-    var typeArguments = node.typeArguments.map(visit).toList();
+    List<DartType> typeArguments = node.typeArguments.map(visit).toList();
     if (useCounter == before) return node;
     return new TypedefType(node.typedefNode, node.nullability, typeArguments);
   }
@@ -554,19 +533,21 @@
         "Function type variables cannot be substituted while still attached "
         "to the function. Perform substitution on "
         "`FunctionType.withoutTypeParameters` instead.");
-    var inner = node.typeParameters.isEmpty ? this : newInnerEnvironment();
+    _TypeSubstitutor inner =
+        node.typeParameters.isEmpty ? this : newInnerEnvironment();
     int before = this.useCounter;
     // Invert the variance when translating parameters.
     inner.invertVariance();
-    var typeParameters = inner.freshTypeParameters(node.typeParameters);
-    var positionalParameters = node.positionalParameters.isEmpty
+    List<TypeParameter> typeParameters =
+        inner.freshTypeParameters(node.typeParameters);
+    List<DartType> positionalParameters = node.positionalParameters.isEmpty
         ? const <DartType>[]
         : node.positionalParameters.map(inner.visit).toList();
-    var namedParameters = node.namedParameters.isEmpty
+    List<NamedType> namedParameters = node.namedParameters.isEmpty
         ? const <NamedType>[]
         : node.namedParameters.map(inner.visitNamedType).toList();
     inner.invertVariance();
-    var returnType = inner.visit(node.returnType);
+    DartType returnType = inner.visit(node.returnType);
     DartType typedefType =
         node.typedefType == null ? null : inner.visit(node.typedefType);
     if (this.useCounter == before) return node;
@@ -578,7 +559,7 @@
   }
 
   void bumpCountersUntil(_TypeSubstitutor target) {
-    var node = this;
+    _TypeSubstitutor node = this;
     while (node != target) {
       ++node.useCounter;
       node = node.outer;
@@ -587,7 +568,7 @@
   }
 
   DartType getSubstitute(TypeParameter variable) {
-    var environment = this;
+    _TypeSubstitutor environment = this;
     while (environment != null) {
       DartType replacement = environment.lookup(variable, covariantContext);
       if (replacement != null) {
@@ -646,170 +627,6 @@
   }
 }
 
-class _TypeUnification {
-  // Acyclic invariant: There are no cycles in the map, that is, all types can
-  //   be resolved to finite types by substituting all contained type variables.
-  //
-  // The acyclic invariant holds everywhere except during cycle detection.
-  //
-  // It is not checked that the substitution satisfies the bound on the type
-  // parameter.
-  final Map<TypeParameter, DartType> substitution = <TypeParameter, DartType>{};
-
-  /// Variables that may be assigned freely in order to obtain unification.
-  ///
-  /// These are sometimes referred to as existentially quantified variables.
-  final Set<TypeParameter> quantifiedVariables;
-
-  /// Variables that are bound by a function type inside one of the types.
-  /// These may not occur in a substitution, because these variables are not in
-  /// scope at the existentially quantified variables.
-  ///
-  /// For example, suppose we are trying to satisfy the equation:
-  ///
-  ///     ∃S. <E>(E, S) => E  =  <E>(E, E) => E
-  ///
-  /// That is, we must choose `S` such that the generic function type
-  /// `<E>(E, S) => E` becomes `<E>(E, E) => E`.  Choosing `S = E` is not a
-  /// valid solution, because `E` is not in scope where `S` is quantified.
-  /// The two function types cannot be unified.
-  final Set<TypeParameter> _universallyQuantifiedVariables =
-      new Set<TypeParameter>();
-
-  bool success = true;
-
-  _TypeUnification(DartType type1, DartType type2, this.quantifiedVariables) {
-    _unify(type1, type2);
-    if (success && substitution.length >= 2) {
-      for (var key in substitution.keys) {
-        substitution[key] = substituteDeep(substitution[key], substitution);
-      }
-    }
-  }
-
-  DartType _substituteHead(TypeParameterType type) {
-    for (int i = 0; i <= substitution.length; ++i) {
-      DartType nextType = substitution[type.parameter];
-      if (nextType == null) return type;
-      if (nextType is TypeParameterType) {
-        type = nextType;
-      } else {
-        return nextType;
-      }
-    }
-    // The cycle should have been found by _trySubstitution when the cycle
-    // was created.
-    throw 'Unexpected cycle found during unification';
-  }
-
-  bool _unify(DartType type1, DartType type2) {
-    if (!success) return false;
-    type1 = type1 is TypeParameterType ? _substituteHead(type1) : type1;
-    type2 = type2 is TypeParameterType ? _substituteHead(type2) : type2;
-    if (type1 is DynamicType && type2 is DynamicType) return true;
-    if (type1 is VoidType && type2 is VoidType) return true;
-    if (type1 is InvalidType && type2 is InvalidType) return true;
-    if (type1 is BottomType && type2 is BottomType) return true;
-    if (type1 is InterfaceType && type2 is InterfaceType) {
-      if (type1.classNode != type2.classNode ||
-          type1.nullability != type2.nullability) {
-        return _fail();
-      }
-      assert(type1.typeArguments.length == type2.typeArguments.length);
-      for (int i = 0; i < type1.typeArguments.length; ++i) {
-        if (!_unify(type1.typeArguments[i], type2.typeArguments[i])) {
-          return false;
-        }
-      }
-      return true;
-    }
-    if (type1 is FunctionType && type2 is FunctionType) {
-      if (type1.typeParameters.length != type2.typeParameters.length ||
-          type1.positionalParameters.length !=
-              type2.positionalParameters.length ||
-          type1.namedParameters.length != type2.namedParameters.length ||
-          type1.requiredParameterCount != type2.requiredParameterCount ||
-          type1.nullability != type2.nullability) {
-        return _fail();
-      }
-      // When unifying two generic functions, transform the equation like this:
-      //
-      //    ∃S. <E>(fn1) = <T>(fn2)
-      //      ==>
-      //    ∃S. ∀G. fn1[G/E] = fn2[G/T]
-      //
-      // That is, assume some fixed identical choice of type parameters for both
-      // functions and try to unify the instantiated function types.
-      assert(!type1.typeParameters.any(quantifiedVariables.contains));
-      assert(!type2.typeParameters.any(quantifiedVariables.contains));
-      var leftInstance = <TypeParameter, DartType>{};
-      var rightInstance = <TypeParameter, DartType>{};
-      for (int i = 0; i < type1.typeParameters.length; ++i) {
-        var instantiator = new TypeParameter(type1.typeParameters[i].name);
-        var instantiatorType = new TypeParameterType.forAlphaRenaming(
-            type1.typeParameters[i], instantiator);
-        leftInstance[type1.typeParameters[i]] = instantiatorType;
-        rightInstance[type2.typeParameters[i]] = instantiatorType;
-        _universallyQuantifiedVariables.add(instantiator);
-      }
-      for (int i = 0; i < type1.typeParameters.length; ++i) {
-        var left = substitute(type1.typeParameters[i].bound, leftInstance);
-        var right = substitute(type2.typeParameters[i].bound, rightInstance);
-        if (!_unify(left, right)) return false;
-      }
-      for (int i = 0; i < type1.positionalParameters.length; ++i) {
-        var left = substitute(type1.positionalParameters[i], leftInstance);
-        var right = substitute(type2.positionalParameters[i], rightInstance);
-        if (!_unify(left, right)) return false;
-      }
-      for (int i = 0; i < type1.namedParameters.length; ++i) {
-        if (type1.namedParameters[i].name != type2.namedParameters[i].name) {
-          return false;
-        }
-        var left = substitute(type1.namedParameters[i].type, leftInstance);
-        var right = substitute(type2.namedParameters[i].type, rightInstance);
-        if (!_unify(left, right)) return false;
-      }
-      var leftReturn = substitute(type1.returnType, leftInstance);
-      var rightReturn = substitute(type2.returnType, rightInstance);
-      if (!_unify(leftReturn, rightReturn)) return false;
-      return true;
-    }
-    if (type1 is TypeParameterType &&
-        type2 is TypeParameterType &&
-        type1.parameter == type2.parameter &&
-        type1.declaredNullability == type2.declaredNullability) {
-      return true;
-    }
-    if (type1 is TypeParameterType &&
-        quantifiedVariables.contains(type1.parameter)) {
-      return _trySubstitution(type1.parameter, type2);
-    }
-    if (type2 is TypeParameterType &&
-        quantifiedVariables.contains(type2.parameter)) {
-      return _trySubstitution(type2.parameter, type1);
-    }
-    return _fail();
-  }
-
-  bool _trySubstitution(TypeParameter variable, DartType type) {
-    if (containsTypeVariable(type, _universallyQuantifiedVariables)) {
-      return _fail();
-    }
-    // Set the plain substitution first and then generate the deep
-    // substitution to detect cycles.
-    substitution[variable] = type;
-    DartType deepSubstitute = substituteDeep(type, substitution);
-    if (deepSubstitute == null) return _fail();
-    substitution[variable] = deepSubstitute;
-    return true;
-  }
-
-  bool _fail() {
-    return success = false;
-  }
-}
-
 class _OccurrenceVisitor implements DartTypeVisitor<bool> {
   final Set<TypeParameter> variables;
 
diff --git a/pkg/kernel/lib/type_checker.dart b/pkg/kernel/lib/type_checker.dart
index 2106e8e..1b79258 100644
--- a/pkg/kernel/lib/type_checker.dart
+++ b/pkg/kernel/lib/type_checker.dart
@@ -25,37 +25,38 @@
       : environment = new TypeEnvironment(coreTypes, hierarchy);
 
   void checkComponent(Component component) {
-    for (var library in component.libraries) {
+    for (Library library in component.libraries) {
       if (ignoreSdk && library.importUri.scheme == 'dart') continue;
-      for (var class_ in library.classes) {
+      for (Class class_ in library.classes) {
         hierarchy.forEachOverridePair(class_,
             (Member ownMember, Member superMember, bool isSetter) {
           checkOverride(class_, ownMember, superMember, isSetter);
         });
       }
     }
-    var visitor = new TypeCheckingVisitor(this, environment, hierarchy);
-    for (var library in component.libraries) {
+    TypeCheckingVisitor visitor =
+        new TypeCheckingVisitor(this, environment, hierarchy);
+    for (Library library in component.libraries) {
       currentLibrary = library;
       if (ignoreSdk && library.importUri.scheme == 'dart') continue;
-      for (var class_ in library.classes) {
+      for (Class class_ in library.classes) {
         currentThisType = coreTypes.thisInterfaceType(
             class_, class_.enclosingLibrary.nonNullable);
-        for (var field in class_.fields) {
+        for (Field field in class_.fields) {
           visitor.visitField(field);
         }
-        for (var constructor in class_.constructors) {
+        for (Constructor constructor in class_.constructors) {
           visitor.visitConstructor(constructor);
         }
-        for (var procedure in class_.procedures) {
+        for (Procedure procedure in class_.procedures) {
           visitor.visitProcedure(procedure);
         }
       }
       currentThisType = null;
-      for (var procedure in library.procedures) {
+      for (Procedure procedure in library.procedures) {
         visitor.visitProcedure(procedure);
       }
-      for (var field in library.fields) {
+      for (Field field in library.fields) {
         visitor.visitField(field);
       }
       currentLibrary = null;
@@ -63,14 +64,16 @@
   }
 
   DartType getterType(Class host, Member member) {
-    var hostType = hierarchy.getClassAsInstanceOf(host, member.enclosingClass);
-    var substitution = Substitution.fromSupertype(hostType);
+    Supertype hostType =
+        hierarchy.getClassAsInstanceOf(host, member.enclosingClass);
+    Substitution substitution = Substitution.fromSupertype(hostType);
     return substitution.substituteType(member.getterType);
   }
 
   DartType setterType(Class host, Member member) {
-    var hostType = hierarchy.getClassAsInstanceOf(host, member.enclosingClass);
-    var substitution = Substitution.fromSupertype(hostType);
+    Supertype hostType =
+        hierarchy.getClassAsInstanceOf(host, member.enclosingClass);
+    Substitution substitution = Substitution.fromSupertype(hostType);
     return substitution.substituteType(member.setterType, contravariant: true);
   }
 
@@ -141,9 +144,9 @@
   }
 
   Expression checkAndDowncastExpression(Expression from, DartType to) {
-    var parent = from.parent;
-    var type = visitExpression(from);
-    var result = checker.checkAndDowncastExpression(from, type, to);
+    TreeNode parent = from.parent;
+    DartType type = visitExpression(from);
+    Expression result = checker.checkAndDowncastExpression(from, type, to);
     result.parent = parent;
     return result;
   }
@@ -210,7 +213,7 @@
   }
 
   void handleFunctionNode(FunctionNode node) {
-    var oldAsyncMarker = currentAsyncMarker;
+    AsyncMarker oldAsyncMarker = currentAsyncMarker;
     currentAsyncMarker = node.asyncMarker;
     node.positionalParameters
         .skip(node.requiredParameterCount)
@@ -223,8 +226,8 @@
   }
 
   void handleNestedFunctionNode(FunctionNode node) {
-    var oldReturn = currentReturnType;
-    var oldYield = currentYieldType;
+    DartType oldReturn = currentReturnType;
+    DartType oldYield = currentYieldType;
     currentReturnType = _getInternalReturnType(node);
     currentYieldType = _getYieldType(node);
     handleFunctionNode(node);
@@ -241,7 +244,7 @@
 
   Substitution getReceiverType(
       TreeNode access, Expression receiver, Member member) {
-    var type = visitExpression(receiver);
+    DartType type = visitExpression(receiver);
     Class superclass = member.enclosingClass;
     if (superclass.supertype == null) {
       return Substitution.empty; // Members on Object are always accessible.
@@ -291,7 +294,7 @@
         fail(arguments, 'Too many positional arguments');
         return const BottomType();
       }
-      var typeArguments = arguments.types;
+      List<DartType> typeArguments = arguments.types;
       if (typeArguments.length != typeParameters.length) {
         fail(arguments, 'Wrong number of type arguments');
         return const BottomType();
@@ -300,18 +303,18 @@
           typeParameters, typeArguments, arguments,
           receiverSubstitution: receiver);
       for (int i = 0; i < arguments.positional.length; ++i) {
-        var expectedType = substitution.substituteType(
+        DartType expectedType = substitution.substituteType(
             functionType.positionalParameters[i],
             contravariant: true);
         arguments.positional[i] =
             checkAndDowncastExpression(arguments.positional[i], expectedType);
       }
       for (int i = 0; i < arguments.named.length; ++i) {
-        var argument = arguments.named[i];
+        NamedExpression argument = arguments.named[i];
         bool found = false;
         for (int j = 0; j < functionType.namedParameters.length; ++j) {
           if (argument.name == functionType.namedParameters[j].name) {
-            var expectedType = substitution.substituteType(
+            DartType expectedType = substitution.substituteType(
                 functionType.namedParameters[j].type,
                 contravariant: true);
             argument.value =
@@ -357,7 +360,7 @@
         while (parent is! FunctionNode) {
           parent = parent.parent;
         }
-        var enclosingFunction = parent as FunctionNode;
+        FunctionNode enclosingFunction = parent as FunctionNode;
         if (enclosingFunction.dartAsyncMarker == AsyncMarker.Sync) {
           parent = enclosingFunction.parent;
           while (parent is! FunctionNode) {
@@ -403,13 +406,14 @@
   Substitution _instantiateFunction(List<TypeParameter> typeParameters,
       List<DartType> typeArguments, TreeNode where,
       {Substitution receiverSubstitution}) {
-    var instantiation = Substitution.fromPairs(typeParameters, typeArguments);
-    var substitution = receiverSubstitution == null
+    Substitution instantiation =
+        Substitution.fromPairs(typeParameters, typeArguments);
+    Substitution substitution = receiverSubstitution == null
         ? instantiation
         : Substitution.combine(receiverSubstitution, instantiation);
     for (int i = 0; i < typeParameters.length; ++i) {
-      var argument = typeArguments[i];
-      var bound = substitution.substituteType(typeParameters[i].bound);
+      DartType argument = typeArguments[i];
+      DartType bound = substitution.substituteType(typeParameters[i].bound);
       checkAssignable(where, argument, bound);
     }
     return substitution;
@@ -484,7 +488,7 @@
 
   @override
   DartType visitLet(Let node) {
-    var value = visitExpression(node.variable.initializer);
+    DartType value = visitExpression(node.variable.initializer);
     if (node.variable.type is DynamicType) {
       node.variable.type = value;
     }
@@ -543,7 +547,7 @@
 
   @override
   DartType visitMapLiteral(MapLiteral node) {
-    for (var entry in node.entries) {
+    for (MapEntry entry in node.entries) {
       entry.key = checkAndDowncastExpression(entry.key, node.keyType);
       entry.value = checkAndDowncastExpression(entry.value, node.valueType);
     }
@@ -571,20 +575,20 @@
       fail(access, 'Wrong number of type arguments');
       return const BottomType();
     }
-    var instantiation =
+    Substitution instantiation =
         Substitution.fromPairs(function.typeParameters, arguments.types);
     for (int i = 0; i < arguments.positional.length; ++i) {
-      var expectedType = instantiation.substituteType(
+      DartType expectedType = instantiation.substituteType(
           function.positionalParameters[i],
           contravariant: true);
       arguments.positional[i] =
           checkAndDowncastExpression(arguments.positional[i], expectedType);
     }
     for (int i = 0; i < arguments.named.length; ++i) {
-      var argument = arguments.named[i];
-      var parameterType = function.getNamedParameter(argument.name);
+      NamedExpression argument = arguments.named[i];
+      DartType parameterType = function.getNamedParameter(argument.name);
       if (parameterType != null) {
-        var expectedType =
+        DartType expectedType =
             instantiation.substituteType(parameterType, contravariant: true);
         argument.value =
             checkAndDowncastExpression(argument.value, expectedType);
@@ -598,9 +602,9 @@
 
   @override
   DartType visitMethodInvocation(MethodInvocation node) {
-    var target = node.interfaceTarget;
+    Member target = node.interfaceTarget;
     if (target == null) {
-      var receiver = visitExpression(node.receiver);
+      DartType receiver = visitExpression(node.receiver);
       if (node.name.text == '==') {
         visitExpression(node.arguments.positional.single);
         return environment.coreTypes.boolLegacyRawType;
@@ -613,8 +617,8 @@
     } else if (target is Procedure &&
         environment.isSpecialCasedBinaryOperator(target)) {
       assert(node.arguments.positional.length == 1);
-      var receiver = visitExpression(node.receiver);
-      var argument = visitExpression(node.arguments.positional[0]);
+      DartType receiver = visitExpression(node.receiver);
+      DartType argument = visitExpression(node.arguments.positional[0]);
       return environment.getTypeOfSpecialCasedBinaryOperator(
           receiver, argument);
     } else {
@@ -626,27 +630,29 @@
   @override
   DartType visitPropertyGet(PropertyGet node) {
     if (node.interfaceTarget == null) {
-      final receiver = visitExpression(node.receiver);
+      final DartType receiver = visitExpression(node.receiver);
       checkUnresolvedInvocation(receiver, node);
       return const DynamicType();
     } else {
-      var receiver = getReceiverType(node, node.receiver, node.interfaceTarget);
+      Substitution receiver =
+          getReceiverType(node, node.receiver, node.interfaceTarget);
       return receiver.substituteType(node.interfaceTarget.getterType);
     }
   }
 
   @override
   DartType visitPropertySet(PropertySet node) {
-    var value = visitExpression(node.value);
+    DartType value = visitExpression(node.value);
     if (node.interfaceTarget != null) {
-      var receiver = getReceiverType(node, node.receiver, node.interfaceTarget);
+      Substitution receiver =
+          getReceiverType(node, node.receiver, node.interfaceTarget);
       checkAssignable(
           node.value,
           value,
           receiver.substituteType(node.interfaceTarget.setterType,
               contravariant: true));
     } else {
-      final receiver = visitExpression(node.receiver);
+      final DartType receiver = visitExpression(node.receiver);
       checkUnresolvedInvocation(receiver, node);
     }
     return value;
@@ -686,7 +692,7 @@
 
   @override
   DartType visitStaticSet(StaticSet node) {
-    var value = visitExpression(node.value);
+    DartType value = visitExpression(node.value);
     checkAssignable(node.value, value, node.target.setterType);
     return value;
   }
@@ -770,16 +776,16 @@
       checkUnresolvedInvocation(currentThisType, node);
       return const DynamicType();
     } else {
-      var receiver = getSuperReceiverType(node.interfaceTarget);
+      Substitution receiver = getSuperReceiverType(node.interfaceTarget);
       return receiver.substituteType(node.interfaceTarget.getterType);
     }
   }
 
   @override
   DartType visitSuperPropertySet(SuperPropertySet node) {
-    var value = visitExpression(node.value);
+    DartType value = visitExpression(node.value);
     if (node.interfaceTarget != null) {
-      var receiver = getSuperReceiverType(node.interfaceTarget);
+      Substitution receiver = getSuperReceiverType(node.interfaceTarget);
       checkAssignable(
           node.value,
           value,
@@ -819,7 +825,7 @@
 
   @override
   DartType visitVariableSet(VariableSet node) {
-    var value = visitExpression(node.value);
+    DartType value = visitExpression(node.value);
     checkAssignable(node.value, value, node.variable.type);
     return value;
   }
@@ -881,7 +887,7 @@
 
   @override
   visitForInStatement(ForInStatement node) {
-    var iterable = visitExpression(node.iterable);
+    DartType iterable = visitExpression(node.iterable);
     // TODO(asgerf): Store interface targets on for-in loops or desugar them,
     // instead of doing the ad-hoc resolution here.
     if (node.isAsync) {
@@ -898,7 +904,7 @@
 
   DartType getIterableElementType(DartType iterable) {
     if (iterable is InterfaceType) {
-      var iteratorGetter =
+      Member iteratorGetter =
           hierarchy.getInterfaceMember(iterable.classNode, iteratorName);
       if (iteratorGetter == null) return const DynamicType();
       List<DartType> castedIterableArguments =
@@ -909,7 +915,7 @@
               castedIterableArguments)
           .substituteType(iteratorGetter.getterType);
       if (iteratorType is InterfaceType) {
-        var currentGetter =
+        Member currentGetter =
             hierarchy.getInterfaceMember(iteratorType.classNode, currentName);
         if (currentGetter == null) return const DynamicType();
         List<DartType> castedIteratorTypeArguments =
@@ -971,7 +977,7 @@
       if (currentReturnType == null) {
         fail(node, 'Return of a value from void method');
       } else {
-        var type = visitExpression(node.expression);
+        DartType type = visitExpression(node.expression);
         if (currentAsyncMarker == AsyncMarker.Async) {
           type = environment.flatten(type);
         }
@@ -983,7 +989,7 @@
   @override
   visitSwitchStatement(SwitchStatement node) {
     visitExpression(node.expression);
-    for (var switchCase in node.cases) {
+    for (SwitchCase switchCase in node.cases) {
       switchCase.expressions.forEach(visitExpression);
       visitStatement(switchCase.body);
     }
@@ -992,7 +998,7 @@
   @override
   visitTryCatch(TryCatch node) {
     visitStatement(node.body);
-    for (var catchClause in node.catches) {
+    for (Catch catchClause in node.catches) {
       visitStatement(catchClause.body);
     }
   }
diff --git a/pkg/kernel/lib/verifier.dart b/pkg/kernel/lib/verifier.dart
index 3ab077d..cc2d51b 100644
--- a/pkg/kernel/lib/verifier.dart
+++ b/pkg/kernel/lib/verifier.dart
@@ -119,7 +119,7 @@
           " but found: '${node.parent.runtimeType}'.",
           context: currentParent);
     }
-    var oldParent = currentParent;
+    TreeNode oldParent = currentParent;
     currentParent = node;
     return oldParent;
   }
@@ -138,7 +138,7 @@
   }
 
   void visitChildren(TreeNode node) {
-    var oldParent = enterParent(node);
+    TreeNode oldParent = enterParent(node);
     node.visitChildren(this);
     exitParent(oldParent);
   }
@@ -175,7 +175,7 @@
 
   void declareTypeParameters(List<TypeParameter> parameters) {
     for (int i = 0; i < parameters.length; ++i) {
-      var parameter = parameters[i];
+      TypeParameter parameter = parameters[i];
       if (parameter.bound == null) {
         problem(
             currentParent, "Missing bound for type parameter '$parameter'.");
@@ -202,27 +202,27 @@
 
   visitComponent(Component component) {
     try {
-      for (var library in component.libraries) {
-        for (var class_ in library.classes) {
+      for (Library library in component.libraries) {
+        for (Class class_ in library.classes) {
           if (!classes.add(class_)) {
             problem(class_, "Class '$class_' declared more than once.");
           }
         }
-        for (var typedef_ in library.typedefs) {
+        for (Typedef typedef_ in library.typedefs) {
           if (!typedefs.add(typedef_)) {
             problem(typedef_, "Typedef '$typedef_' declared more than once.");
           }
         }
         library.members.forEach(declareMember);
-        for (var class_ in library.classes) {
+        for (Class class_ in library.classes) {
           class_.members.forEach(declareMember);
         }
       }
       visitChildren(component);
     } finally {
-      for (var library in component.libraries) {
+      for (Library library in component.libraries) {
         library.members.forEach(undeclareMember);
-        for (var class_ in library.classes) {
+        for (Class class_ in library.classes) {
           class_.members.forEach(undeclareMember);
         }
       }
@@ -238,23 +238,23 @@
 
   visitExtension(Extension node) {
     declareTypeParameters(node.typeParameters);
-    final oldParent = enterParent(node);
+    final TreeNode oldParent = enterParent(node);
     node.visitChildren(this);
     exitParent(oldParent);
     undeclareTypeParameters(node.typeParameters);
   }
 
   void checkTypedef(Typedef node) {
-    var state = typedefState[node];
+    TypedefState state = typedefState[node];
     if (state == TypedefState.Done) return;
     if (state == TypedefState.BeingChecked) {
       problem(node, "The typedef '$node' refers to itself", context: node);
     }
     assert(state == null);
     typedefState[node] = TypedefState.BeingChecked;
-    var savedTypeParameters = typeParametersInScope;
+    Set<TypeParameter> savedTypeParameters = typeParametersInScope;
     typeParametersInScope = node.typeParameters.toSet();
-    var savedParent = currentParent;
+    TreeNode savedParent = currentParent;
     currentParent = node;
     // Visit children without checking the parent pointer on the typedef itself
     // since this can be called from a context other than its true parent.
@@ -272,7 +272,7 @@
 
   visitField(Field node) {
     currentMember = node;
-    var oldParent = enterParent(node);
+    TreeNode oldParent = enterParent(node);
     bool isTopLevel = node.parent == currentLibrary;
     if (isTopLevel && !node.isStatic) {
       problem(node, "The top-level field '${node.name.text}' should be static",
@@ -293,7 +293,7 @@
 
   visitProcedure(Procedure node) {
     currentMember = node;
-    var oldParent = enterParent(node);
+    TreeNode oldParent = enterParent(node);
     classTypeParametersAreInScope = !node.isStatic;
     if (node.isAbstract && node.isExternal) {
       problem(node, "Procedure cannot be both abstract and external.");
@@ -346,7 +346,7 @@
     classTypeParametersAreInScope = true;
     // The constructor member needs special treatment due to parameters being
     // in scope in the initializer list.
-    var oldParent = enterParent(node);
+    TreeNode oldParent = enterParent(node);
     int stackHeight = enterLocalScope();
     visitChildren(node.function);
     visitList(node.initializers, this);
@@ -364,7 +364,7 @@
   visitClass(Class node) {
     currentClass = node;
     declareTypeParameters(node.typeParameters);
-    var oldParent = enterParent(node);
+    TreeNode oldParent = enterParent(node);
     classTypeParametersAreInScope = false;
     visitList(node.annotations, this);
     classTypeParametersAreInScope = true;
@@ -397,7 +397,7 @@
       }
     }
     declareTypeParameters(node.typeParameters);
-    for (var typeParameter in node.typeParameters) {
+    for (TypeParameter typeParameter in node.typeParameters) {
       typeParameter.bound?.accept(this);
       if (typeParameter.annotations.isNotEmpty) {
         problem(
@@ -458,8 +458,10 @@
         break;
       case AsyncMarker.SyncStar:
       case AsyncMarker.AsyncStar:
-        problem(node,
-            "Return statement in function with async marker: $currentAsyncMarker");
+        problem(
+            node,
+            "Return statement in function with async marker: "
+            "$currentAsyncMarker");
         break;
     }
     super.visitReturnStatement(node);
@@ -470,8 +472,10 @@
     switch (currentAsyncMarker) {
       case AsyncMarker.Sync:
       case AsyncMarker.Async:
-        problem(node,
-            "Yield statement in function with async marker: $currentAsyncMarker");
+        problem(
+            node,
+            "Yield statement in function with async marker: "
+            "$currentAsyncMarker");
         break;
       case AsyncMarker.SyncStar:
       case AsyncMarker.AsyncStar:
@@ -490,7 +494,7 @@
   }
 
   visitVariableDeclaration(VariableDeclaration node) {
-    var parent = node.parent;
+    TreeNode parent = node.parent;
     if (parent is! Block &&
         !(parent is Catch && parent.body != node) &&
         !(parent is FunctionNode && parent.body != node) &&
@@ -638,7 +642,7 @@
     }
     namedLoop:
     for (int i = 0; i < arguments.named.length; ++i) {
-      var argument = arguments.named[i];
+      NamedExpression argument = arguments.named[i];
       String name = argument.name;
       for (int j = 0; j < function.namedParameters.length; ++j) {
         if (function.namedParameters[j].name == name) continue namedLoop;
@@ -771,7 +775,7 @@
 
   @override
   visitTypeParameterType(TypeParameterType node) {
-    var parameter = node.parameter;
+    TypeParameter parameter = node.parameter;
     if (!typeParametersInScope.contains(parameter)) {
       problem(
           currentParent,
@@ -904,7 +908,7 @@
           "is '${node.parent.runtimeType}' "
           "but should be '${parent.runtimeType}'.");
     }
-    var oldParent = parent;
+    TreeNode oldParent = parent;
     parent = node;
     node.visitChildren(this);
     parent = oldParent;
diff --git a/pkg/kernel/lib/visitor.dart b/pkg/kernel/lib/visitor.dart
index 1fec5a3..fdabdc6 100644
--- a/pkg/kernel/lib/visitor.dart
+++ b/pkg/kernel/lib/visitor.dart
@@ -414,7 +414,8 @@
 
   /// Visits [node] if not already visited to compute a value for [node].
   ///
-  /// If the value has already been computed the cached value is returned immediately.
+  /// If the value has already been computed the cached value is returned
+  /// immediately.
   ///
   /// Call this method to compute values for subnodes recursively, while only
   /// visiting each subnode once.
diff --git a/pkg/kernel/lib/vm/constants_native_effects.dart b/pkg/kernel/lib/vm/constants_native_effects.dart
index fb52988..67b436b 100644
--- a/pkg/kernel/lib/vm/constants_native_effects.dart
+++ b/pkg/kernel/lib/vm/constants_native_effects.dart
@@ -38,7 +38,8 @@
     }
     // This is a bit fishy, since we merge the key and the value type by
     // putting both into the same list.
-    final kvListConstant = new ListConstant(const DynamicType(), kvListPairs);
+    final ListConstant kvListConstant =
+        new ListConstant(const DynamicType(), kvListPairs);
     assert(immutableMapClass.fields.length == 1);
     final Field kvPairListField = immutableMapClass.fields[0];
     return new InstanceConstant(immutableMapClass.reference, <DartType>[
diff --git a/pkg/kernel/test/class_hierarchy_test.dart b/pkg/kernel/test/class_hierarchy_test.dart
index bd0ad7a..bf078f8 100644
--- a/pkg/kernel/test/class_hierarchy_test.dart
+++ b/pkg/kernel/test/class_hierarchy_test.dart
@@ -453,7 +453,8 @@
 ''');
 
     // The documentation says:
-    // It is possible for two methods to override one another in both directions.
+    // It is possible for two methods to override one another in both
+    // directions.
     _assertOverridePairs(b, ['test::B.foo overrides test::A.foo']);
   }
 
@@ -1227,12 +1228,12 @@
     void callback(
         Member declaredMember, Member interfaceMember, bool isSetter) {
       var suffix = isSetter ? '=' : '';
-      String declaredName =
-          '${qualifiedMemberNameToString(declaredMember, includeLibraryName: true)}'
-          '$suffix';
-      String interfaceName =
-          '${qualifiedMemberNameToString(interfaceMember, includeLibraryName: true)}'
-          '$suffix';
+      String declaredMemberName =
+          qualifiedMemberNameToString(declaredMember, includeLibraryName: true);
+      String declaredName = '${declaredMemberName}$suffix';
+      String interfaceMemberName = qualifiedMemberNameToString(interfaceMember,
+          includeLibraryName: true);
+      String interfaceName = '${interfaceMemberName}$suffix';
       var desc = '$declaredName overrides $interfaceName';
       overrideDescriptions.add(desc);
     }
diff --git a/pkg/kernel/test/non_null_test.dart b/pkg/kernel/test/non_null_test.dart
index 77750c8..542cc09 100644
--- a/pkg/kernel/test/non_null_test.dart
+++ b/pkg/kernel/test/non_null_test.dart
@@ -40,13 +40,18 @@
   'FutureOr<Object*>*': 'FutureOr<Object>',
   'FutureOr<FutureOr<Object?>>': 'FutureOr<FutureOr<Object>>',
   '(List<Object>, {required List<Object> a, List<Object> b}) -> List<Object>':
-      '(List<Object>, {required List<Object> a, List<Object> b}) -> List<Object>',
+      '(List<Object>, {required List<Object> a, List<Object> b})'
+          ' -> List<Object>',
   '(List<Object>, {required List<Object> a, List<Object> b}) ->? List<Object>':
-      '(List<Object>, {required List<Object> a, List<Object> b}) -> List<Object>',
+      '(List<Object>, {required List<Object> a, List<Object> b})'
+          ' -> List<Object>',
   '(List<Object>, {required List<Object> a, List<Object> b}) ->* List<Object>':
-      '(List<Object>, {required List<Object> a, List<Object> b}) -> List<Object>',
-  '(List<Object>?, {required List<Object?> a, List<Object?>? b}) ->? List<Object?>':
-      '(List<Object>?, {required List<Object?> a, List<Object?>? b}) -> List<Object?>',
+      '(List<Object>, {required List<Object> a, List<Object> b})'
+          ' -> List<Object>',
+  '(List<Object>?, {required List<Object?> a, List<Object?>? b})'
+          ' ->? List<Object?>':
+      '(List<Object>?, {required List<Object?> a, List<Object?>? b})'
+          ' -> List<Object?>',
   'X': 'X & Object',
   'X?': 'X & Object',
   'X*': 'X & Object',
diff --git a/pkg/kernel/test/text_serializer_test.dart b/pkg/kernel/test/text_serializer_test.dart
index f4b3631..521aca4 100644
--- a/pkg/kernel/test/text_serializer_test.dart
+++ b/pkg/kernel/test/text_serializer_test.dart
@@ -83,8 +83,8 @@
     test('(const-list (dynamic) ((int 0) (int 1) (int 2)))');
     test('(set (dynamic) ((bool true) (bool false) (int 0)))');
     test('(const-set (dynamic) ((int 0) (int 1) (int 2)))');
-    test(
-        '(map (dynamic) (void) ((int 0) (null) (int 1) (null) (int 2) (null)))');
+    test('(map (dynamic) (void)'
+        ' ((int 0) (null) (int 1) (null) (int 2) (null)))');
     test('(const-map (dynamic) (void) ((int 0) (null) (int 1) (null) '
         '(int 2) (null)))');
     test('(type (-> () () () ((dynamic)) () () (dynamic)))');
diff --git a/pkg/kernel/test/type_hashcode_test.dart b/pkg/kernel/test/type_hashcode_test.dart
index 468deb6..b988507 100644
--- a/pkg/kernel/test/type_hashcode_test.dart
+++ b/pkg/kernel/test/type_hashcode_test.dart
@@ -3,9 +3,79 @@
 // BSD-style license that can be found in the LICENSE file.
 import 'package:kernel/kernel.dart';
 import 'type_parser.dart';
-import 'type_unification_test.dart' show testCases;
 import 'package:test/test.dart';
 
+final List<TestCase> testCases = <TestCase>[
+  successCase('List<T>', 'List<String>', {'T': 'String'}),
+  successCase('List<String>', 'List<T>', {'T': 'String'}),
+  successCase('List<T>', 'List<T>', {'T': null}),
+  successCase('List<S>', 'List<T>', {'S': 'T'}),
+  successCase('List<S>', 'List<T>', {'T': 'S'}),
+  successCase(
+      'List<S>', 'List<T>', {'S': 'T', 'T': null}), // Require left bias.
+  failureCase('List<S>', 'List<T>', []),
+
+  failureCase('List<T>', 'T', ['T']),
+  failureCase('List<List<T>>', 'List<T>', ['T']),
+  failureCase('Map<S, T>', 'Map<List<T>, List<S>>', ['T', 'S']),
+
+  failureCase('Map<Map<S,String>, Map<int,S>>',
+      'Map<Map<int, S>, Map<S, String>>', ['S']),
+  successCase('Map<Map<S, int>, Map<int, S>>', 'Map<Map<int, S>, Map<S, int>>',
+      {'S': 'int'}),
+  successCase('Map<Map<S, String>, Map<int, T>>',
+      'Map<Map<int, T>, Map<S, String>>', {'S': 'int', 'T': 'String'}),
+
+  successCase('Map<S, List<T>>', 'Map<T, List<S>>', {'S': 'T'}),
+  successCase('Map<S, T>', 'Map<S, List<S>>', {'T': 'List<S>'}),
+  successCase('Map<S, T>', 'Map<S, List<S>>', {'T': 'List<S>', 'S': null}),
+  successCase('Map<List<S>, T>', 'Map<T, List<S>>', {'T': 'List<S>'}),
+  successCase(
+      'Map<List<S>, T>', 'Map<T, List<S>>', {'T': 'List<S>', 'S': null}),
+
+  successCase('<E>(E) => E', '<T>(T) => T', {}),
+  successCase('<E>(E, S) => E', '<T>(T, int) => T', {'S': 'int'}),
+  failureCase('<E>(E, S) => E', '<T>(T, T) => T', ['S']),
+  successCase(
+      '<E>(E) => <T>(T) => Map<E,T>', '<E>(E) => <T>(T) => Map<E,T>', {}),
+  successCase('<E>(E,_) => E', '<T>(T,_) => T', {}),
+
+  successCase('(x:int,y:String) => int', '(y:String,x:int) => int', {}),
+  successCase('<S,T>(x:S,y:T) => S', '<S,T>(y:T,x:S) => S', {}),
+  successCase('(x:<T>(T)=>T,y:<S>(S)=>S) => int',
+      '(y:<S>(S)=>S,x:<T>(T)=>T) => int', {}),
+  successCase('(x:<T>(T)=>T,y:<S>(S,S,S)=>S) => int',
+      '(y:<S>(S,S,S)=>S,x:<T>(T)=>T) => int', {}),
+];
+
+class TestCase {
+  String type1;
+  String type2;
+  Iterable<String> quantifiedVariables;
+  Map<String, String> expectedSubstitution; // Null if unification should fail.
+
+  TestCase.success(this.type1, this.type2, this.expectedSubstitution) {
+    quantifiedVariables = expectedSubstitution.keys;
+  }
+
+  TestCase.fail(this.type1, this.type2, this.quantifiedVariables);
+
+  bool get shouldSucceed => expectedSubstitution != null;
+
+  String toString() => '∃ ${quantifiedVariables.join(',')}. $type1 = $type2';
+}
+
+TestCase successCase(String type1, String type2, Map<String, String> expected,
+    {bool debug: false}) {
+  return new TestCase.success(type1, type2, expected);
+}
+
+TestCase failureCase(
+    String type1, String type2, List<String> quantifiedVariables,
+    {bool debug: false}) {
+  return new TestCase.fail(type1, type2, quantifiedVariables);
+}
+
 void checkHashCodeEquality(DartType type1, DartType type2) {
   if (type1 == type2 && type1.hashCode != type2.hashCode) {
     fail('Equal types with different hash codes: $type1 and $type2');
diff --git a/pkg/kernel/test/type_substitution_identity_test.dart b/pkg/kernel/test/type_substitution_identity_test.dart
index d07da2a..d8fabbd 100644
--- a/pkg/kernel/test/type_substitution_identity_test.dart
+++ b/pkg/kernel/test/type_substitution_identity_test.dart
@@ -1,10 +1,11 @@
 // 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.
+
 import 'package:kernel/kernel.dart';
 import 'package:kernel/type_algebra.dart';
 import 'type_parser.dart';
-import 'type_unification_test.dart' show testCases;
+import 'type_hashcode_test.dart' show testCases;
 import 'package:test/test.dart';
 
 checkType(DartType type) {
diff --git a/pkg/kernel/test/type_unification_test.dart b/pkg/kernel/test/type_unification_test.dart
deleted file mode 100644
index 2e79fdb..0000000
--- a/pkg/kernel/test/type_unification_test.dart
+++ /dev/null
@@ -1,140 +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.
-import 'package:kernel/type_algebra.dart';
-import 'package:test/test.dart';
-import 'type_parser.dart';
-import 'dart:io';
-
-final List<TestCase> testCases = <TestCase>[
-  successCase('List<T>', 'List<String>', {'T': 'String'}),
-  successCase('List<String>', 'List<T>', {'T': 'String'}),
-  successCase('List<T>', 'List<T>', {'T': null}),
-  successCase('List<S>', 'List<T>', {'S': 'T'}),
-  successCase('List<S>', 'List<T>', {'T': 'S'}),
-  successCase(
-      'List<S>', 'List<T>', {'S': 'T', 'T': null}), // Require left bias.
-  failureCase('List<S>', 'List<T>', []),
-
-  failureCase('List<T>', 'T', ['T']),
-  failureCase('List<List<T>>', 'List<T>', ['T']),
-  failureCase('Map<S, T>', 'Map<List<T>, List<S>>', ['T', 'S']),
-
-  failureCase('Map<Map<S,String>, Map<int,S>>',
-      'Map<Map<int, S>, Map<S, String>>', ['S']),
-  successCase('Map<Map<S, int>, Map<int, S>>', 'Map<Map<int, S>, Map<S, int>>',
-      {'S': 'int'}),
-  successCase('Map<Map<S, String>, Map<int, T>>',
-      'Map<Map<int, T>, Map<S, String>>', {'S': 'int', 'T': 'String'}),
-
-  successCase('Map<S, List<T>>', 'Map<T, List<S>>', {'S': 'T'}),
-  successCase('Map<S, T>', 'Map<S, List<S>>', {'T': 'List<S>'}),
-  successCase('Map<S, T>', 'Map<S, List<S>>', {'T': 'List<S>', 'S': null}),
-  successCase('Map<List<S>, T>', 'Map<T, List<S>>', {'T': 'List<S>'}),
-  successCase(
-      'Map<List<S>, T>', 'Map<T, List<S>>', {'T': 'List<S>', 'S': null}),
-
-  successCase('<E>(E) => E', '<T>(T) => T', {}),
-  successCase('<E>(E, S) => E', '<T>(T, int) => T', {'S': 'int'}),
-  failureCase('<E>(E, S) => E', '<T>(T, T) => T', ['S']),
-  successCase(
-      '<E>(E) => <T>(T) => Map<E,T>', '<E>(E) => <T>(T) => Map<E,T>', {}),
-  successCase('<E>(E,_) => E', '<T>(T,_) => T', {}),
-
-  successCase('(x:int,y:String) => int', '(y:String,x:int) => int', {}),
-  successCase('<S,T>(x:S,y:T) => S', '<S,T>(y:T,x:S) => S', {}),
-  successCase('(x:<T>(T)=>T,y:<S>(S)=>S) => int',
-      '(y:<S>(S)=>S,x:<T>(T)=>T) => int', {}),
-  successCase('(x:<T>(T)=>T,y:<S>(S,S,S)=>S) => int',
-      '(y:<S>(S,S,S)=>S,x:<T>(T)=>T) => int', {}),
-];
-
-class TestCase {
-  String type1;
-  String type2;
-  Iterable<String> quantifiedVariables;
-  Map<String, String> expectedSubstitution; // Null if unification should fail.
-
-  TestCase.success(this.type1, this.type2, this.expectedSubstitution) {
-    quantifiedVariables = expectedSubstitution.keys;
-  }
-
-  TestCase.fail(this.type1, this.type2, this.quantifiedVariables);
-
-  bool get shouldSucceed => expectedSubstitution != null;
-
-  String toString() => '∃ ${quantifiedVariables.join(',')}. $type1 = $type2';
-}
-
-TestCase successCase(String type1, String type2, Map<String, String> expected,
-    {bool debug: false}) {
-  return new TestCase.success(type1, type2, expected);
-}
-
-TestCase failureCase(
-    String type1, String type2, List<String> quantifiedVariables,
-    {bool debug: false}) {
-  return new TestCase.fail(type1, type2, quantifiedVariables);
-}
-
-int numFailures = 0;
-
-void reportFailure(TestCase testCase, String message) {
-  ++numFailures;
-  fail('$message in `$testCase`');
-}
-
-main() {
-  for (TestCase testCase in testCases) {
-    test('$testCase', () {
-      var env = new LazyTypeEnvironment();
-      var type1 = env.parse(testCase.type1);
-      var type2 = env.parse(testCase.type2);
-      var quantifiedVariables =
-          testCase.quantifiedVariables.map(env.getTypeParameter).toSet();
-      var substitution = unifyTypes(type1, type2, quantifiedVariables);
-      if (testCase.shouldSucceed) {
-        if (substitution == null) {
-          reportFailure(testCase, 'Unification failed');
-        } else {
-          for (var key in testCase.expectedSubstitution.keys) {
-            var typeParameter = env.getTypeParameter(key);
-            if (testCase.expectedSubstitution[key] == null) {
-              if (substitution.containsKey(key)) {
-                var actualType = substitution[typeParameter];
-                reportFailure(
-                    testCase,
-                    'Incorrect substitution '
-                    '`$key = $actualType` should be unbound');
-              }
-            } else {
-              var expectedType = env.parse(testCase.expectedSubstitution[key]);
-              var actualType = substitution[typeParameter];
-              if (actualType != expectedType) {
-                reportFailure(
-                    testCase,
-                    'Incorrect substitution '
-                    '`$key = $actualType` should be `$key = $expectedType`');
-              }
-            }
-          }
-          var boundTypeVariables = testCase.expectedSubstitution.keys
-              .where((name) => testCase.expectedSubstitution[name] != null);
-          if (substitution.length != boundTypeVariables.length) {
-            reportFailure(
-                testCase,
-                'Substituted `${substitution.keys.join(',')}` '
-                'but should only substitute `${boundTypeVariables.join(',')}`');
-          }
-        }
-      } else {
-        if (substitution != null) {
-          reportFailure(testCase, 'Unification was supposed to fail');
-        }
-      }
-    });
-  }
-  if (numFailures > 0) {
-    exit(1);
-  }
-}
diff --git a/pkg/meta/analysis_options.yaml b/pkg/meta/analysis_options.yaml
deleted file mode 100644
index 4163582..0000000
--- a/pkg/meta/analysis_options.yaml
+++ /dev/null
@@ -1,3 +0,0 @@
-analyzer:
-  enable-experiment:
-    - non-nullable
diff --git a/pkg/nnbd_migration/lib/migration_cli.dart b/pkg/nnbd_migration/lib/migration_cli.dart
index 3428443..8833c58 100644
--- a/pkg/nnbd_migration/lib/migration_cli.dart
+++ b/pkg/nnbd_migration/lib/migration_cli.dart
@@ -6,6 +6,7 @@
 import 'dart:io' hide File;
 
 import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
+import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/diagnostic/diagnostic.dart';
 import 'package:analyzer/error/error.dart';
@@ -50,9 +51,10 @@
   final Map<String, LineInfo> lineInfo;
   final Context pathContext;
   final String rootDirectory;
+  final bool allSourcesAlreadyMigrated;
 
-  AnalysisResult(
-      this.errors, this.lineInfo, this.pathContext, this.rootDirectory) {
+  AnalysisResult(this.errors, this.lineInfo, this.pathContext,
+      this.rootDirectory, this.allSourcesAlreadyMigrated) {
     errors.sort((AnalysisError one, AnalysisError two) {
       if (one.source != two.source) {
         return one.source.fullName.compareTo(two.source.fullName);
@@ -635,7 +637,9 @@
     logger.stdout(ansi.emphasized('Analyzing project...'));
     _fixCodeProcessor = _FixCodeProcessor(context, this);
     _dartFixListener = DartFixListener(
-        DriverProviderImpl(resourceProvider, context), _exceptionReported);
+        DriverProviderImpl(resourceProvider, context),
+        _exceptionReported,
+        _fatalErrorReported);
     nonNullableFix = createNonNullableFix(_dartFixListener, resourceProvider,
         _fixCodeProcessor.getLineInfo, computeBindAddress(),
         included: [options.directory],
@@ -803,6 +807,11 @@
     }
   }
 
+  void _fatalErrorReported(String detail) {
+    logger.stderr(detail);
+    throw MigrationExit(1);
+  }
+
   void _exceptionReported(String detail) {
     if (_hasExceptions) return;
     _hasExceptions = true;
@@ -869,6 +878,14 @@
             .stdout('Unresolved URIs found.  Did you forget to run "pub get"?');
         logger.stdout('');
       }
+      if (analysisResult.allSourcesAlreadyMigrated) {
+        logger.stdout('''
+All files appear to have null safety already enabled.  Did you update the
+language version prior to running "dart migrate"?  If so, you need to un-do this
+(and re-run "pub get") prior to performing the migration.
+''');
+        logger.stdout('');
+      }
       logger.stdout(
           'Please fix the analysis issues (or, force generation of migration '
           'suggestions by re-running with '
@@ -1030,7 +1047,11 @@
     _progressBar = ProgressBar(_migrationCli.logger, pathsToProcess.length);
 
     // Process each source file.
+    bool allSourcesAlreadyMigrated = true;
     await processResources((ResolvedUnitResult result) async {
+      if (!result.unit.featureSet.isEnabled(Feature.non_nullable)) {
+        allSourcesAlreadyMigrated = false;
+      }
       _progressBar.tick();
       List<AnalysisError> errors = result.errors
           .where((error) => error.severity == Severity.error)
@@ -1054,8 +1075,12 @@
       }
     }
 
-    return AnalysisResult(analysisErrors, _migrationCli.lineInfo,
-        _migrationCli.pathContext, _migrationCli.options.directory);
+    return AnalysisResult(
+        analysisErrors,
+        _migrationCli.lineInfo,
+        _migrationCli.pathContext,
+        _migrationCli.options.directory,
+        allSourcesAlreadyMigrated);
   }
 
   Future<MigrationState> runLaterPhases() async {
diff --git a/pkg/nnbd_migration/lib/src/front_end/dartfix_listener.dart b/pkg/nnbd_migration/lib/src/front_end/dartfix_listener.dart
index 378b136..1a4e769 100644
--- a/pkg/nnbd_migration/lib/src/front_end/dartfix_listener.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/dartfix_listener.dart
@@ -19,7 +19,10 @@
   /// client.
   final void Function(String detail) reportException;
 
-  DartFixListener(this.server, this.reportException);
+  /// Callback that reports a fatal error to the client.
+  final void Function(String detail) reportFatalError;
+
+  DartFixListener(this.server, this.reportException, this.reportFatalError);
 
   /// Record an edit to be sent to the client.
   ///
@@ -29,11 +32,6 @@
     sourceChange.addEdit(source.fullName, -1, edit);
   }
 
-  /// Record a recommendation to be sent to the client.
-  void addRecommendation(String description, [Location location]) {
-    throw UnimplementedError('TODO(paulberry)');
-  }
-
   /// Record a source change to be sent to the client.
   void addSourceFileEdit(
       String description, Location location, SourceFileEdit fileEdit) {
diff --git a/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart b/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart
index 8a5d668..a691876 100644
--- a/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart
@@ -69,11 +69,6 @@
 
   NullabilityMigration migration;
 
-  /// If this flag has a value of `false`, then something happened to prevent
-  /// at least one package from being marked as non-nullable.
-  /// If this occurs, then don't update any code.
-  bool _packageIsNNBD = true;
-
   Future<MigrationState> Function() rerunFunction;
 
   /// A list of the URLs corresponding to the included roots.
@@ -106,9 +101,6 @@
       InstrumentationListener(migrationSummary: migrationSummary);
 
   Future<void> finalizeUnit(ResolvedUnitResult result) async {
-    if (!_packageIsNNBD) {
-      return;
-    }
     migration.finalizeInput(result);
   }
 
@@ -121,9 +113,6 @@
   }
 
   Future<void> prepareUnit(ResolvedUnitResult result) async {
-    if (!_packageIsNNBD) {
-      return;
-    }
     migration.prepareInput(result);
   }
 
@@ -132,10 +121,6 @@
   /// This means updating the pubspec.yaml file, the package_config.json
   /// file, and the analysis_options.yaml file, each only if necessary.
   void processPackage(Folder pkgFolder) {
-    if (!_packageIsNNBD) {
-      return;
-    }
-
     var pubspecFile = pkgFolder.getChildAssumingFile('pubspec.yaml');
     if (!pubspecFile.exists) {
       // If the pubspec file cannot be found, we do not attempt to change the
@@ -161,9 +146,6 @@
   }
 
   Future<void> processUnit(ResolvedUnitResult result) async {
-    if (!_packageIsNNBD) {
-      return;
-    }
     migration.processInput(result);
   }
 
@@ -217,10 +199,6 @@
   /// Updates the Package Config file to specify a minimum Dart SDK version
   /// which supports null safety.
   void _processConfigFile(Folder pkgFolder, _YamlFile pubspec) {
-    if (!_packageIsNNBD) {
-      return;
-    }
-
     var packageName = pubspec._getName();
     if (packageName == null) {
       return;
@@ -352,7 +330,7 @@
   }
 
   void _processPubspecException(String action, String pubspecPath, error) {
-    listener.addRecommendation('''Failed to $action pubspec file
+    listener.reportFatalError('''Failed to $action pubspec file
   $pubspecPath
   $error
 
@@ -362,7 +340,7 @@
     environment:
       sdk: '$_intendedSdkVersionConstraint';
 ''');
-    _packageIsNNBD = false;
+    throw StateError('listener.reportFatalError should never return');
   }
 
   /// Allows unit tests to shut down any rogue servers that have been started,
diff --git a/pkg/nnbd_migration/pubspec.yaml b/pkg/nnbd_migration/pubspec.yaml
index 5f57739..59072b4 100644
--- a/pkg/nnbd_migration/pubspec.yaml
+++ b/pkg/nnbd_migration/pubspec.yaml
@@ -21,6 +21,8 @@
   yaml: ^2.1.15
 
 dev_dependencies:
+  analysis_tool:
+    path: ../analysis_tool
   http: '>=0.11.3+17 <0.13.0'
   pedantic: ^1.9.0
   test: ^1.6.4
diff --git a/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart b/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart
index 6f87f89..0ca5ea4 100644
--- a/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart
+++ b/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart
@@ -201,7 +201,8 @@
     // Compute the analysis results.
     var server = DriverProviderImpl(resourceProvider, driver.analysisContext);
     // Run the migration engine.
-    var listener = DartFixListener(server, _exceptionReported);
+    var listener =
+        DartFixListener(server, _exceptionReported, _exceptionReported);
     var instrumentationListener = InstrumentationListener();
     var adapter = NullabilityMigrationAdapter(listener);
     var migration = NullabilityMigration(adapter, getLineInfo,
diff --git a/pkg/nnbd_migration/test/front_end/test_all.dart b/pkg/nnbd_migration/test/front_end/test_all.dart
index 9a81148..7c88db8 100644
--- a/pkg/nnbd_migration/test/front_end/test_all.dart
+++ b/pkg/nnbd_migration/test/front_end/test_all.dart
@@ -7,6 +7,7 @@
 import 'info_builder_test.dart' as info_builder;
 import 'instrumentation_renderer_test.dart' as instrumentation_renderer;
 import 'migration_info_test.dart' as migration_info;
+import 'migration_summary_test.dart' as migration_summary;
 import 'navigation_tree_renderer_test.dart' as navigation_tree_renderer;
 import 'offset_mapper_test.dart' as offset_mapper;
 import 'region_renderer_test.dart' as region_renderer;
@@ -17,6 +18,7 @@
     info_builder.main();
     instrumentation_renderer.main();
     migration_info.main();
+    migration_summary.main();
     navigation_tree_renderer.main();
     offset_mapper.main();
     region_renderer.main();
diff --git a/pkg/nnbd_migration/test/migration_cli_test.dart b/pkg/nnbd_migration/test/migration_cli_test.dart
index ce0ec97..35957ce 100644
--- a/pkg/nnbd_migration/test/migration_cli_test.dart
+++ b/pkg/nnbd_migration/test/migration_cli_test.dart
@@ -144,7 +144,8 @@
 
   @override
   Set<String> computePathsToProcess(DriverBasedAnalysisContext context) =>
-      cli._test.overridePathsToProcess ?? super.computePathsToProcess(context);
+      cli._test.overridePathsToProcess ??
+      _sortPaths(super.computePathsToProcess(context));
 
   @override
   NonNullableFix createNonNullableFix(
@@ -185,6 +186,13 @@
   bool shouldBeMigrated(DriverBasedAnalysisContext context, String path) =>
       cli._test.overrideShouldBeMigrated?.call(path) ??
       super.shouldBeMigrated(context, path);
+
+  /// Sorts the paths in [paths] for repeatability of migration tests.
+  Set<String> _sortPaths(Set<String> paths) {
+    var pathList = paths.toList();
+    pathList.sort();
+    return pathList.toSet();
+  }
 }
 
 abstract class _MigrationCliTestBase {
@@ -742,6 +750,10 @@
         contains(
             'analysis errors will result in erroneous migration suggestions'));
     expect(output, contains('Please fix the analysis issues'));
+    expect(
+        output,
+        isNot(
+            contains('All files appear to have null safety already enabled')));
   }
 
   test_lifecycle_ignore_errors_enable() async {
@@ -806,6 +818,30 @@
     expect(output, isNot(contains('package:bar/bar.dart')));
   }
 
+  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/44118')
+  test_lifecycle_issue_44118() async {
+    var projectContents = simpleProject(sourceText: '''
+int f() => null
+''');
+    projectContents['lib/foo.dart'] = '''
+import 'test.dart';
+''';
+    var projectDir = createProjectDir(projectContents);
+    await assertRunFailure([projectDir]);
+    var output = logger.stdoutBuffer.toString();
+    expect(output, contains('1 analysis issue found'));
+    var sep = resourceProvider.pathContext.separator;
+    expect(
+        output,
+        contains("error • Expected to find ';' at lib${sep}test.dart:1:12 • "
+            '(expected_token)'));
+    expect(
+        output,
+        contains(
+            'analysis errors will result in erroneous migration suggestions'));
+    expect(output, contains('Please fix the analysis issues'));
+  }
+
   test_lifecycle_no_preview() async {
     var projectContents = simpleProject();
     var projectDir = createProjectDir(projectContents);
@@ -1459,6 +1495,10 @@
     expect(output,
         contains('Unresolved URIs found.  Did you forget to run "pub get"?'));
     expect(output, contains('Please fix the analysis issues'));
+    expect(
+        output,
+        isNot(
+            contains('All files appear to have null safety already enabled')));
   }
 
   test_migrate_path_absolute() {
@@ -1784,7 +1824,10 @@
     var projectDir = createProjectDir(projectContents);
     var cliRunner = _createCli()
         .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
-    expect(() async => await cliRunner.run(), throwsUnsupportedError);
+    var message = await assertErrorExit(
+        cliRunner, () async => await cliRunner.run(),
+        withUsage: false);
+    expect(message, contains('Failed to parse pubspec file'));
   }
 
   test_pubspec_with_sdk_version_beta() async {
@@ -1869,6 +1912,9 @@
         errorOutput,
         contains("A value of type 'Null' can't be returned from function 'f' "
             "because it has a return type of 'int'"));
+    expect(errorOutput, contains('''
+All files appear to have null safety already enabled.  Did you update the
+language version prior to running "dart migrate"?'''));
   }
 
   String _getHelpText({@required bool verbose}) {
diff --git a/pkg/nnbd_migration/test/preview/preview_site_test.dart b/pkg/nnbd_migration/test/preview/preview_site_test.dart
index f32b003..43fd177 100644
--- a/pkg/nnbd_migration/test/preview/preview_site_test.dart
+++ b/pkg/nnbd_migration/test/preview/preview_site_test.dart
@@ -36,7 +36,8 @@
   }
 
   void setUp() {
-    dartfixListener = DartFixListener(null, _exceptionReported);
+    dartfixListener =
+        DartFixListener(null, _exceptionReported, _exceptionReported);
     resourceProvider = MemoryResourceProvider();
     final migrationInfo = MigrationInfo({}, {}, null, null);
     state = MigrationState(null, null, dartfixListener, null);
@@ -190,7 +191,8 @@
   @override
   void setUp() {
     super.setUp();
-    dartfixListener = DartFixListener(null, _exceptionReported);
+    dartfixListener =
+        DartFixListener(null, _exceptionReported, _exceptionReported);
     final migrationInfo = MigrationInfo({}, {}, null, null);
     state = MigrationState(null, null, dartfixListener, null);
     nodeMapper = state.nodeMapper;
diff --git a/pkg/nnbd_migration/test/test_all.dart b/pkg/nnbd_migration/test/test_all.dart
index b82bafb..c2f9b7f 100644
--- a/pkg/nnbd_migration/test/test_all.dart
+++ b/pkg/nnbd_migration/test/test_all.dart
@@ -26,6 +26,7 @@
 import 'preview/test_all.dart' as preview;
 import 'utilities/test_all.dart' as utilities;
 import 'variables_test.dart' as variables;
+import 'verify_tests_test.dart' as verify_tests;
 
 main() {
   defineReflectiveSuite(() {
@@ -48,5 +49,6 @@
     preview.main();
     utilities.main();
     variables.main();
+    verify_tests.main();
   });
 }
diff --git a/pkg/nnbd_migration/test/verify_tests_test.dart b/pkg/nnbd_migration/test/verify_tests_test.dart
new file mode 100644
index 0000000..af19088
--- /dev/null
+++ b/pkg/nnbd_migration/test/verify_tests_test.dart
@@ -0,0 +1,15 @@
+// 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.
+
+import 'package:analysis_tool/package_root.dart' as package_root;
+import 'package:analysis_tool/verify_tests.dart';
+import 'package:analyzer/file_system/physical_file_system.dart';
+
+void main() {
+  var provider = PhysicalResourceProvider.INSTANCE;
+  var packageRoot = provider.pathContext.normalize(package_root.packageRoot);
+  var pathToAnalyze = provider.pathContext.join(packageRoot, 'nnbd_migration');
+  var testDirPath = provider.pathContext.join(pathToAnalyze, 'test');
+  VerifyTests(testDirPath).build();
+}
diff --git a/pkg/nnbd_migration/tool/summary_stats.dart b/pkg/nnbd_migration/tool/summary_stats.dart
new file mode 100644
index 0000000..79afca0
--- /dev/null
+++ b/pkg/nnbd_migration/tool/summary_stats.dart
@@ -0,0 +1,54 @@
+import 'dart:convert' show jsonDecode;
+import 'dart:io';
+
+void main(List<String> args) {
+  var jsonPath = args[0];
+  var json =
+      jsonDecode(File(jsonPath).readAsStringSync()) as Map<String, Object>;
+  var changes = json['changes'] as Map<String, Object>;
+  var byPath = changes['byPath'] as Map<String, Object>;
+  if (args.isEmpty) {
+    print('''
+Usage: summary_stats.dart <summary_file> [category_name]
+
+Prints statistics of `dart migrate` suggestions summary, created with the
+`--summary` flag of `dart migrate`.
+
+If [category_name] is not given, this prints the total number of suggestions
+which the tool made, per category.
+
+If [category_name] is given, this prints the file names and suggestion counts of
+each file with one or more suggestions categorized as [category_name].
+''');
+  } else if (args.length == 1) {
+    _printTotals(byPath);
+  } else {
+    var category = args[1];
+    _printCategory(byPath, category);
+  }
+}
+
+/// Prints the file names and counts of files with suggestions of [category].
+void _printCategory(Map<String, Object> byPath, String category) {
+  byPath.forEach((String path, Object value) {
+    var counts = value as Map<String, Object>;
+    if (counts.containsKey(category)) {
+      print('$path: ${counts[category]}');
+    }
+  });
+}
+
+/// Prints the total number of suggestions for each category.
+void _printTotals(Map<String, Object> byPath) {
+  var summary = <String, int>{};
+  for (var file in byPath.values.cast<Map<Object, Object>>()) {
+    file.forEach((category, count) {
+      summary.putIfAbsent(category as String, () => 0);
+      summary[category as String] += count as int;
+    });
+  }
+  var categories = summary.keys.toList()..sort();
+  for (var category in categories) {
+    print('$category:  ${summary[category]}');
+  }
+}
diff --git a/pkg/test_runner/lib/src/test_configurations.dart b/pkg/test_runner/lib/src/test_configurations.dart
index 0b3fb76..be34361 100644
--- a/pkg/test_runner/lib/src/test_configurations.dart
+++ b/pkg/test_runner/lib/src/test_configurations.dart
@@ -41,7 +41,6 @@
   Path('tests/dart2js_2'),
   Path('tests/dartdevc'),
   Path('tests/dartdevc_2'),
-  Path('tests/kernel'),
   Path('tests/language'),
   Path('tests/language_2'),
   Path('tests/lib'),
diff --git a/pkg/vm/lib/target/vm.dart b/pkg/vm/lib/target/vm.dart
index 97cf03a..3df5836 100644
--- a/pkg/vm/lib/target/vm.dart
+++ b/pkg/vm/lib/target/vm.dart
@@ -50,7 +50,7 @@
   bool get supportsSetLiterals => false;
 
   @override
-  bool get supportsLateFields => !flags.forceLateLoweringForTesting;
+  int get enabledLateLowerings => flags.forceLateLoweringsForTesting;
 
   @override
   bool get supportsLateLoweringSentinel =>
diff --git a/runtime/lib/isolate.cc b/runtime/lib/isolate.cc
index 905cb26..1ff4a38 100644
--- a/runtime/lib/isolate.cc
+++ b/runtime/lib/isolate.cc
@@ -293,11 +293,8 @@
 class SpawnIsolateTask : public ThreadPool::Task {
  public:
   SpawnIsolateTask(Isolate* parent_isolate,
-                   std::unique_ptr<IsolateSpawnState> state,
-                   bool in_new_isolate_group)
-      : parent_isolate_(parent_isolate),
-        state_(std::move(state)),
-        in_new_isolate_group_(in_new_isolate_group) {
+                   std::unique_ptr<IsolateSpawnState> state)
+      : parent_isolate_(parent_isolate), state_(std::move(state)) {
     parent_isolate->IncrementSpawnCount();
   }
 
@@ -308,68 +305,37 @@
   }
 
   void Run() override {
-    auto group = state_->isolate_group();
+    const char* name = (state_->debug_name() == nullptr)
+                           ? state_->function_name()
+                           : state_->debug_name();
+    ASSERT(name != nullptr);
 
-    // The create isolate group call back is mandatory.  If not provided we
+    auto group = state_->isolate_group();
+    if (!FLAG_enable_isolate_groups || group == nullptr) {
+      RunHeavyweight(name);
+    } else {
+      RunLightweight(name);
+    }
+  }
+
+  void RunHeavyweight(const char* name) {
+    // The create isolate group callback is mandatory.  If not provided we
     // cannot spawn isolates.
-    Dart_IsolateGroupCreateCallback create_group_callback =
-        Isolate::CreateGroupCallback();
+    auto create_group_callback = Isolate::CreateGroupCallback();
     if (create_group_callback == nullptr) {
       FailedSpawn("Isolate spawn is not supported by this Dart embedder\n");
       return;
     }
 
-    // The initialize callback is optional atm, we fall back to creating isolate
-    // groups if it was not provided.
-    Dart_InitializeIsolateCallback initialize_callback =
-        Isolate::InitializeCallback();
-
-    const char* name = (state_->debug_name() == NULL) ? state_->function_name()
-                                                      : state_->debug_name();
-    ASSERT(name != NULL);
-
-    // Create a new isolate.
     char* error = nullptr;
-    Isolate* isolate = nullptr;
-    if (!FLAG_enable_isolate_groups || group == nullptr ||
-        initialize_callback == nullptr || in_new_isolate_group_) {
-      // Make a copy of the state's isolate flags and hand it to the callback.
-      Dart_IsolateFlags api_flags = *(state_->isolate_flags());
-      isolate = reinterpret_cast<Isolate*>((create_group_callback)(
-          state_->script_url(), name, nullptr, state_->package_config(),
-          &api_flags, parent_isolate_->init_callback_data(), &error));
-      parent_isolate_->DecrementSpawnCount();
-      parent_isolate_ = nullptr;
-    } else {
-      if (initialize_callback == nullptr) {
-        FailedSpawn("Isolate spawn is not supported by this embedder.");
-        return;
-      }
 
-#if defined(DART_PRECOMPILED_RUNTIME)
-      isolate = CreateWithinExistingIsolateGroupAOT(group, name, &error);
-#else
-      isolate = CreateWithinExistingIsolateGroup(group, name, &error);
-#endif
-      parent_isolate_->DecrementSpawnCount();
-      parent_isolate_ = nullptr;
-      if (isolate == nullptr) {
-        FailedSpawn(error);
-        free(error);
-        return;
-      }
-
-      void* child_isolate_data = nullptr;
-      bool success = initialize_callback(&child_isolate_data, &error);
-      isolate->set_init_callback_data(child_isolate_data);
-      if (!success) {
-        Dart_ShutdownIsolate();
-        FailedSpawn(error);
-        free(error);
-        return;
-      }
-      Dart_ExitIsolate();
-    }
+    // Make a copy of the state's isolate flags and hand it to the callback.
+    Dart_IsolateFlags api_flags = *(state_->isolate_flags());
+    Isolate* isolate = reinterpret_cast<Isolate*>((create_group_callback)(
+        state_->script_url(), name, nullptr, state_->package_config(),
+        &api_flags, parent_isolate_->init_callback_data(), &error));
+    parent_isolate_->DecrementSpawnCount();
+    parent_isolate_ = nullptr;
 
     if (isolate == nullptr) {
       FailedSpawn(error);
@@ -377,20 +343,66 @@
       return;
     }
 
-    if (state_->origin_id() != ILLEGAL_PORT) {
-      // For isolates spawned using spawnFunction we set the origin_id
-      // to the origin_id of the parent isolate.
-      isolate->set_origin_id(state_->origin_id());
+    Run(isolate);
+  }
+
+  void RunLightweight(const char* name) {
+    // The create isolate initialize callback is mandatory if
+    // --enable-isolate-groups was passed.
+    auto initialize_callback = Isolate::InitializeCallback();
+    if (initialize_callback == nullptr) {
+      FailedSpawn(
+          "Lightweight isolate spawn is not supported by this Dart embedder\n");
+      return;
     }
-    MutexLocker ml(isolate->mutex());
-    state_->set_isolate(isolate);
-    isolate->set_spawn_state(std::move(state_));
-    if (isolate->is_runnable()) {
-      isolate->Run();
+
+    char* error = nullptr;
+
+    auto group = state_->isolate_group();
+#if defined(DART_PRECOMPILED_RUNTIME)
+    Isolate* isolate = CreateWithinExistingIsolateGroupAOT(group, name, &error);
+#else
+    Isolate* isolate = CreateWithinExistingIsolateGroup(group, name, &error);
+#endif
+    parent_isolate_->DecrementSpawnCount();
+    parent_isolate_ = nullptr;
+
+    if (isolate == nullptr) {
+      FailedSpawn(error);
+      free(error);
+      return;
     }
+
+    void* child_isolate_data = nullptr;
+    const bool success = initialize_callback(&child_isolate_data, &error);
+    if (!success) {
+      Dart_ShutdownIsolate();
+      FailedSpawn(error);
+      free(error);
+      return;
+    }
+
+    isolate->set_init_callback_data(child_isolate_data);
+    Dart_ExitIsolate();
+    Run(isolate);
   }
 
  private:
+  void Run(Isolate* child) {
+    state_->set_isolate(child);
+
+    MutexLocker ml(child->mutex());
+    child->set_origin_id(state_->origin_id());
+    child->set_spawn_state(std::move(state_));
+
+    // If the isolate is not marked as runnable, then the embedder might do so
+    // later on and the launch of the isolate will happen inside
+    // `Dart_IsolateMakeRunnable`.
+    if (child->is_runnable()) {
+      child->Run();
+    }
+  }
+
   void FailedSpawn(const char* error) {
     ReportError(error != nullptr
                     ? error
@@ -410,7 +422,6 @@
 
   Isolate* parent_isolate_;
   std::unique_ptr<IsolateSpawnState> state_;
-  bool in_new_isolate_group_;
 
   DISALLOW_COPY_AND_ASSIGN(SpawnIsolateTask);
 };
@@ -466,18 +477,19 @@
           packageConfig.IsNull() ? NULL : String2UTF8(packageConfig);
       const char* utf8_debug_name =
           debugName.IsNull() ? NULL : String2UTF8(debugName);
+      const bool in_new_isolate_group = newIsolateGroup.value();
 
       std::unique_ptr<IsolateSpawnState> state(new IsolateSpawnState(
           port.Id(), isolate->origin_id(), String2UTF8(script_uri), func,
           &message_buffer, utf8_package_config, paused.value(), fatal_errors,
-          on_exit_port, on_error_port, utf8_debug_name, isolate->group()));
+          on_exit_port, on_error_port, utf8_debug_name,
+          in_new_isolate_group ? nullptr : isolate->group()));
 
       // Since this is a call to Isolate.spawn, copy the parent isolate's code.
       state->isolate_flags()->copy_parent_code = true;
 
-      const bool in_new_isolate_group = newIsolateGroup.value();
-      isolate->group()->thread_pool()->Run<SpawnIsolateTask>(
-          isolate, std::move(state), in_new_isolate_group);
+      isolate->group()->thread_pool()->Run<SpawnIsolateTask>(isolate,
+                                                             std::move(state));
       return Object::null();
     }
   }
@@ -581,9 +593,8 @@
   // Since this is a call to Isolate.spawnUri, don't copy the parent's code.
   state->isolate_flags()->copy_parent_code = false;
 
-  const bool in_new_isolate_group = false;
-  isolate->group()->thread_pool()->Run<SpawnIsolateTask>(
-      isolate, std::move(state), in_new_isolate_group);
+  isolate->group()->thread_pool()->Run<SpawnIsolateTask>(isolate,
+                                                         std::move(state));
   return Object::null();
 }
 
diff --git a/runtime/vm/compiler/assembler/assembler_arm.h b/runtime/vm/compiler/assembler/assembler_arm.h
index 1e9d2ff..71c5428f 100644
--- a/runtime/vm/compiler/assembler/assembler_arm.h
+++ b/runtime/vm/compiler/assembler/assembler_arm.h
@@ -768,6 +768,7 @@
     ldr(LR, target);
     blx(LR);
   }
+  void Call(const Code& code) { BranchLink(code); }
 
   void CallCFunction(Address target) { Call(target); }
 
diff --git a/runtime/vm/compiler/assembler/assembler_arm64.h b/runtime/vm/compiler/assembler/assembler_arm64.h
index b74553e..377cc4e 100644
--- a/runtime/vm/compiler/assembler/assembler_arm64.h
+++ b/runtime/vm/compiler/assembler/assembler_arm64.h
@@ -1461,6 +1461,7 @@
     ldr(LR, target);
     blr(LR);
   }
+  void Call(const Code& code) { BranchLink(code); }
 
   void CallCFunction(Address target) { Call(target); }
 
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.cc b/runtime/vm/compiler/backend/flow_graph_compiler.cc
index e943f75..535b34c 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.cc
@@ -2304,6 +2304,38 @@
 }
 
 #if !defined(TARGET_ARCH_IA32)
+// Expected inputs (from TypeTestABI):
+// - kInstanceReg: instance (preserved).
+// - kInstantiatorTypeArgumentsReg: instantiator type arguments
+//   (for test_kind == kTestTypeFourArg or test_kind == kTestTypeSixArg).
+// - kFunctionTypeArgumentsReg: function type arguments
+//   (for test_kind == kTestTypeFourArg or test_kind == kTestTypeSixArg).
+//
+// See the arch-specific GenerateSubtypeNTestCacheStub method to see which
+// registers may need saving across this call.
+SubtypeTestCachePtr FlowGraphCompiler::GenerateCallSubtypeTestStub(
+    TypeTestStubKind test_kind,
+    compiler::Label* is_instance_lbl,
+    compiler::Label* is_not_instance_lbl) {
+  const SubtypeTestCache& type_test_cache =
+      SubtypeTestCache::ZoneHandle(zone(), SubtypeTestCache::New());
+  __ LoadUniqueObject(TypeTestABI::kSubtypeTestCacheReg, type_test_cache);
+  if (test_kind == kTestTypeOneArg) {
+    __ Call(StubCode::Subtype1TestCache());
+  } else if (test_kind == kTestTypeTwoArgs) {
+    __ Call(StubCode::Subtype2TestCache());
+  } else if (test_kind == kTestTypeFourArgs) {
+    __ Call(StubCode::Subtype4TestCache());
+  } else if (test_kind == kTestTypeSixArgs) {
+    __ Call(StubCode::Subtype6TestCache());
+  } else {
+    UNREACHABLE();
+  }
+  GenerateBoolToJump(TypeTestABI::kSubtypeTestCacheResultReg, is_instance_lbl,
+                     is_not_instance_lbl);
+  return type_test_cache.raw();
+}
+
 // Generates an assignable check for a given object. Emits no code if the
 // destination type is known at compile time and is a top type. See
 // GenerateCallerChecksForAssertAssignable for other optimized cases.
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.h b/runtime/vm/compiler/backend/flow_graph_compiler.h
index 1965d97..31d849d 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.h
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.h
@@ -1085,12 +1085,10 @@
   TypeTestStubKind GetTypeTestStubKindForTypeParameter(
       const TypeParameter& type_param);
 
+  // Takes input from TypeTestABI registers (or stack on IA32), see
+  // StubCodeCompiler::GenerateSubtypeNTestCacheStub for caller-save registers.
   SubtypeTestCachePtr GenerateCallSubtypeTestStub(
       TypeTestStubKind test_kind,
-      Register instance_reg,
-      Register instantiator_type_arguments_reg,
-      Register function_type_arguments_reg,
-      Register temp_reg,
       compiler::Label* is_instance_lbl,
       compiler::Label* is_not_instance_lbl);
 
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc b/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc
index 66f966d..5f07a68 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc
@@ -224,51 +224,6 @@
   __ Bind(&fall_through);
 }
 
-// R0: instance (must be preserved).
-// R2: instantiator type arguments (if used).
-// R1: function type arguments (if used).
-// R3: type test cache.
-SubtypeTestCachePtr FlowGraphCompiler::GenerateCallSubtypeTestStub(
-    TypeTestStubKind test_kind,
-    Register instance_reg,
-    Register instantiator_type_arguments_reg,
-    Register function_type_arguments_reg,
-    Register temp_reg,
-    compiler::Label* is_instance_lbl,
-    compiler::Label* is_not_instance_lbl) {
-  ASSERT(instance_reg == R0);
-  ASSERT(temp_reg == kNoRegister);  // Unused on ARM.
-  const SubtypeTestCache& type_test_cache =
-      SubtypeTestCache::ZoneHandle(zone(), SubtypeTestCache::New());
-  __ LoadUniqueObject(R3, type_test_cache);
-  if (test_kind == kTestTypeOneArg) {
-    ASSERT(instantiator_type_arguments_reg == kNoRegister);
-    ASSERT(function_type_arguments_reg == kNoRegister);
-    __ BranchLink(StubCode::Subtype1TestCache());
-  } else if (test_kind == kTestTypeTwoArgs) {
-    ASSERT(instantiator_type_arguments_reg == kNoRegister);
-    ASSERT(function_type_arguments_reg == kNoRegister);
-    __ BranchLink(StubCode::Subtype2TestCache());
-  } else if (test_kind == kTestTypeFourArgs) {
-    ASSERT(instantiator_type_arguments_reg ==
-           TypeTestABI::kInstantiatorTypeArgumentsReg);
-    ASSERT(function_type_arguments_reg ==
-           TypeTestABI::kFunctionTypeArgumentsReg);
-    __ BranchLink(StubCode::Subtype4TestCache());
-  } else if (test_kind == kTestTypeSixArgs) {
-    ASSERT(instantiator_type_arguments_reg ==
-           TypeTestABI::kInstantiatorTypeArgumentsReg);
-    ASSERT(function_type_arguments_reg ==
-           TypeTestABI::kFunctionTypeArgumentsReg);
-    __ BranchLink(StubCode::Subtype6TestCache());
-  } else {
-    UNREACHABLE();
-  }
-  // Result is in R1: null -> not found, otherwise Bool::True or Bool::False.
-  GenerateBoolToJump(R1, is_instance_lbl, is_not_instance_lbl);
-  return type_test_cache.raw();
-}
-
 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if
 // type test is conclusive, otherwise fallthrough if a type test could not
 // be completed.
@@ -326,14 +281,8 @@
   }
 
   // Regular subtype test cache involving instance's type arguments.
-  const Register kInstantiatorTypeArgumentsReg = kNoRegister;
-  const Register kFunctionTypeArgumentsReg = kNoRegister;
-  const Register kTempReg = kNoRegister;
-  // R0: instance (must be preserved).
-  return GenerateCallSubtypeTestStub(
-      kTestTypeTwoArgs, TypeTestABI::kInstanceReg,
-      kInstantiatorTypeArgumentsReg, kFunctionTypeArgumentsReg, kTempReg,
-      is_instance_lbl, is_not_instance_lbl);
+  return GenerateCallSubtypeTestStub(kTestTypeTwoArgs, is_instance_lbl,
+                                     is_not_instance_lbl);
 }
 
 void FlowGraphCompiler::CheckClassIds(Register class_id_reg,
@@ -444,24 +393,18 @@
     __ b(is_instance_lbl, EQ);
   }
 
-  const Register kInstantiatorTypeArgumentsReg = kNoRegister;
-  const Register kFunctionTypeArgumentsReg = kNoRegister;
-  const Register kTempReg = kNoRegister;
-  return GenerateCallSubtypeTestStub(kTestTypeOneArg, TypeTestABI::kInstanceReg,
-                                     kInstantiatorTypeArgumentsReg,
-                                     kFunctionTypeArgumentsReg, kTempReg,
-                                     is_instance_lbl, is_not_instance_lbl);
+  return GenerateCallSubtypeTestStub(kTestTypeOneArg, is_instance_lbl,
+                                     is_not_instance_lbl);
 }
 
 // Generates inlined check if 'type' is a type parameter or type itself
-// R0: instance (preserved).
+// TypeTestABI::kInstanceReg: instance (preserved).
 SubtypeTestCachePtr FlowGraphCompiler::GenerateUninstantiatedTypeTest(
     TokenPosition token_pos,
     const AbstractType& type,
     compiler::Label* is_instance_lbl,
     compiler::Label* is_not_instance_lbl) {
   __ Comment("UninstantiatedTypeTest");
-  const Register kTempReg = kNoRegister;
   ASSERT(!type.IsInstantiated());
   ASSERT(!type.IsFunctionType());
   // Skip check if destination is a dynamic type.
@@ -480,40 +423,40 @@
     // Check if type arguments are null, i.e. equivalent to vector of dynamic.
     __ CompareObject(kTypeArgumentsReg, Object::null_object());
     __ b(is_instance_lbl, EQ);
-    __ ldr(R3, compiler::FieldAddress(
-                   kTypeArgumentsReg,
-                   compiler::target::TypeArguments::type_at_offset(
-                       type_param.index())));
+    __ ldr(
+        TypeTestABI::kScratchReg,
+        compiler::FieldAddress(kTypeArgumentsReg,
+                               compiler::target::TypeArguments::type_at_offset(
+                                   type_param.index())));
     // R3: concrete type of type.
     // Check if type argument is dynamic, Object?, or void.
-    __ CompareObject(R3, Object::dynamic_type());
+    __ CompareObject(TypeTestABI::kScratchReg, Object::dynamic_type());
     __ b(is_instance_lbl, EQ);
     __ CompareObject(
-        R3, Type::ZoneHandle(
-                zone(), isolate()->object_store()->nullable_object_type()));
+        TypeTestABI::kScratchReg,
+        Type::ZoneHandle(zone(),
+                         isolate()->object_store()->nullable_object_type()));
     __ b(is_instance_lbl, EQ);
-    __ CompareObject(R3, Object::void_type());
+    __ CompareObject(TypeTestABI::kScratchReg, Object::void_type());
     __ b(is_instance_lbl, EQ);
 
     // For Smi check quickly against int and num interfaces.
     compiler::Label not_smi;
-    __ tst(R0, compiler::Operand(kSmiTagMask));  // Value is Smi?
+    __ tst(TypeTestABI::kInstanceReg,
+           compiler::Operand(kSmiTagMask));  // Value is Smi?
     __ b(&not_smi, NE);
-    __ CompareObject(R3, Type::ZoneHandle(zone(), Type::IntType()));
+    __ CompareObject(TypeTestABI::kScratchReg,
+                     Type::ZoneHandle(zone(), Type::IntType()));
     __ b(is_instance_lbl, EQ);
-    __ CompareObject(R3, Type::ZoneHandle(zone(), Type::Number()));
+    __ CompareObject(TypeTestABI::kScratchReg,
+                     Type::ZoneHandle(zone(), Type::Number()));
     __ b(is_instance_lbl, EQ);
     // Smi can be handled by type test cache.
     __ Bind(&not_smi);
 
     const auto test_kind = GetTypeTestStubKindForTypeParameter(type_param);
-    const SubtypeTestCache& type_test_cache = SubtypeTestCache::ZoneHandle(
-        zone(), GenerateCallSubtypeTestStub(
-                    test_kind, TypeTestABI::kInstanceReg,
-                    TypeTestABI::kInstantiatorTypeArgumentsReg,
-                    TypeTestABI::kFunctionTypeArgumentsReg, kTempReg,
-                    is_instance_lbl, is_not_instance_lbl));
-    return type_test_cache.raw();
+    return GenerateCallSubtypeTestStub(test_kind, is_instance_lbl,
+                                       is_not_instance_lbl);
   }
   if (type.IsType()) {
     // Smi is FutureOr<T>, when T is a top type or int or num.
@@ -528,11 +471,8 @@
                (1 << TypeTestABI::kInstantiatorTypeArgumentsReg));
     // Uninstantiated type class is known at compile time, but the type
     // arguments are determined at runtime by the instantiator(s).
-    return GenerateCallSubtypeTestStub(
-        kTestTypeFourArgs, TypeTestABI::kInstanceReg,
-        TypeTestABI::kInstantiatorTypeArgumentsReg,
-        TypeTestABI::kFunctionTypeArgumentsReg, kTempReg, is_instance_lbl,
-        is_not_instance_lbl);
+    return GenerateCallSubtypeTestStub(kTestTypeFourArgs, is_instance_lbl,
+                                       is_not_instance_lbl);
   }
   return SubtypeTestCache::null();
 }
@@ -554,12 +494,8 @@
              (1 << TypeTestABI::kInstantiatorTypeArgumentsReg));
   // Uninstantiated type class is known at compile time, but the type
   // arguments are determined at runtime by the instantiator(s).
-  const Register kTempReg = kNoRegister;
-  return GenerateCallSubtypeTestStub(
-      kTestTypeSixArgs, TypeTestABI::kInstanceReg,
-      TypeTestABI::kInstantiatorTypeArgumentsReg,
-      TypeTestABI::kFunctionTypeArgumentsReg, kTempReg, is_instance_lbl,
-      is_not_instance_lbl);
+  return GenerateCallSubtypeTestStub(kTestTypeSixArgs, is_instance_lbl,
+                                     is_not_instance_lbl);
 }
 
 // Inputs:
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc b/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc
index 14cb38c..803667f 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc
@@ -214,50 +214,6 @@
   __ Bind(&fall_through);
 }
 
-// R0: instance (must be preserved).
-// R2: instantiator type arguments (if used).
-// R1: function type arguments (if used).
-SubtypeTestCachePtr FlowGraphCompiler::GenerateCallSubtypeTestStub(
-    TypeTestStubKind test_kind,
-    Register instance_reg,
-    Register instantiator_type_arguments_reg,
-    Register function_type_arguments_reg,
-    Register temp_reg,
-    compiler::Label* is_instance_lbl,
-    compiler::Label* is_not_instance_lbl) {
-  ASSERT(instance_reg == R0);
-  ASSERT(temp_reg == kNoRegister);  // Unused on ARM64.
-  const SubtypeTestCache& type_test_cache =
-      SubtypeTestCache::ZoneHandle(zone(), SubtypeTestCache::New());
-  __ LoadUniqueObject(R3, type_test_cache);
-  if (test_kind == kTestTypeOneArg) {
-    ASSERT(instantiator_type_arguments_reg == kNoRegister);
-    ASSERT(function_type_arguments_reg == kNoRegister);
-    __ BranchLink(StubCode::Subtype1TestCache());
-  } else if (test_kind == kTestTypeTwoArgs) {
-    ASSERT(instantiator_type_arguments_reg == kNoRegister);
-    ASSERT(function_type_arguments_reg == kNoRegister);
-    __ BranchLink(StubCode::Subtype2TestCache());
-  } else if (test_kind == kTestTypeFourArgs) {
-    ASSERT(instantiator_type_arguments_reg ==
-           TypeTestABI::kInstantiatorTypeArgumentsReg);
-    ASSERT(function_type_arguments_reg ==
-           TypeTestABI::kFunctionTypeArgumentsReg);
-    __ BranchLink(StubCode::Subtype4TestCache());
-  } else if (test_kind == kTestTypeSixArgs) {
-    ASSERT(instantiator_type_arguments_reg ==
-           TypeTestABI::kInstantiatorTypeArgumentsReg);
-    ASSERT(function_type_arguments_reg ==
-           TypeTestABI::kFunctionTypeArgumentsReg);
-    __ BranchLink(StubCode::Subtype6TestCache());
-  } else {
-    UNREACHABLE();
-  }
-  // Result is in R1: null -> not found, otherwise Bool::True or Bool::False.
-  GenerateBoolToJump(R1, is_instance_lbl, is_not_instance_lbl);
-  return type_test_cache.raw();
-}
-
 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if
 // type test is conclusive, otherwise fallthrough if a type test could not
 // be completed.
@@ -311,14 +267,8 @@
     }
   }
   // Regular subtype test cache involving instance's type arguments.
-  const Register kInstantiatorTypeArgumentsReg = kNoRegister;
-  const Register kFunctionTypeArgumentsReg = kNoRegister;
-  const Register kTempReg = kNoRegister;
-  // R0: instance (must be preserved).
-  return GenerateCallSubtypeTestStub(
-      kTestTypeTwoArgs, TypeTestABI::kInstanceReg,
-      kInstantiatorTypeArgumentsReg, kFunctionTypeArgumentsReg, kTempReg,
-      is_instance_lbl, is_not_instance_lbl);
+  return GenerateCallSubtypeTestStub(kTestTypeTwoArgs, is_instance_lbl,
+                                     is_not_instance_lbl);
 }
 
 void FlowGraphCompiler::CheckClassIds(Register class_id_reg,
@@ -426,24 +376,18 @@
     __ b(is_instance_lbl, EQ);
   }
 
-  const Register kInstantiatorTypeArgumentsReg = kNoRegister;
-  const Register kFunctionTypeArgumentsReg = kNoRegister;
-  const Register kTempReg = kNoRegister;
-  return GenerateCallSubtypeTestStub(kTestTypeOneArg, TypeTestABI::kInstanceReg,
-                                     kInstantiatorTypeArgumentsReg,
-                                     kFunctionTypeArgumentsReg, kTempReg,
-                                     is_instance_lbl, is_not_instance_lbl);
+  return GenerateCallSubtypeTestStub(kTestTypeOneArg, is_instance_lbl,
+                                     is_not_instance_lbl);
 }
 
 // Generates inlined check if 'type' is a type parameter or type itself
-// R0: instance (preserved).
+// TypeTestABI::kInstanceReg: instance (preserved).
 SubtypeTestCachePtr FlowGraphCompiler::GenerateUninstantiatedTypeTest(
     TokenPosition token_pos,
     const AbstractType& type,
     compiler::Label* is_instance_lbl,
     compiler::Label* is_not_instance_lbl) {
   __ Comment("UninstantiatedTypeTest");
-  const Register kTempReg = kNoRegister;
   ASSERT(!type.IsInstantiated());
   ASSERT(!type.IsFunctionType());
   // Skip check if destination is a dynamic type.
@@ -461,37 +405,35 @@
     // Check if type arguments are null, i.e. equivalent to vector of dynamic.
     __ CompareObject(kTypeArgumentsReg, Object::null_object());
     __ b(is_instance_lbl, EQ);
-    __ LoadFieldFromOffset(R3, kTypeArgumentsReg,
+    __ LoadFieldFromOffset(TypeTestABI::kScratchReg, kTypeArgumentsReg,
                            TypeArguments::type_at_offset(type_param.index()));
     // R3: concrete type of type.
     // Check if type argument is dynamic, Object?, or void.
-    __ CompareObject(R3, Object::dynamic_type());
+    __ CompareObject(TypeTestABI::kScratchReg, Object::dynamic_type());
     __ b(is_instance_lbl, EQ);
     __ CompareObject(
-        R3, Type::ZoneHandle(
-                zone(), isolate()->object_store()->nullable_object_type()));
+        TypeTestABI::kScratchReg,
+        Type::ZoneHandle(zone(),
+                         isolate()->object_store()->nullable_object_type()));
     __ b(is_instance_lbl, EQ);
-    __ CompareObject(R3, Object::void_type());
+    __ CompareObject(TypeTestABI::kScratchReg, Object::void_type());
     __ b(is_instance_lbl, EQ);
 
     // For Smi check quickly against int and num interfaces.
     compiler::Label not_smi;
-    __ BranchIfNotSmi(R0, &not_smi);
-    __ CompareObject(R3, Type::ZoneHandle(zone(), Type::IntType()));
+    __ BranchIfNotSmi(TypeTestABI::kInstanceReg, &not_smi);
+    __ CompareObject(TypeTestABI::kScratchReg,
+                     Type::ZoneHandle(zone(), Type::IntType()));
     __ b(is_instance_lbl, EQ);
-    __ CompareObject(R3, Type::ZoneHandle(zone(), Type::Number()));
+    __ CompareObject(TypeTestABI::kScratchReg,
+                     Type::ZoneHandle(zone(), Type::Number()));
     __ b(is_instance_lbl, EQ);
     // Smi can be handled by type test cache.
     __ Bind(&not_smi);
 
     const auto test_kind = GetTypeTestStubKindForTypeParameter(type_param);
-    const SubtypeTestCache& type_test_cache = SubtypeTestCache::ZoneHandle(
-        zone(), GenerateCallSubtypeTestStub(
-                    test_kind, TypeTestABI::kInstanceReg,
-                    TypeTestABI::kInstantiatorTypeArgumentsReg,
-                    TypeTestABI::kFunctionTypeArgumentsReg, kTempReg,
-                    is_instance_lbl, is_not_instance_lbl));
-    return type_test_cache.raw();
+    return GenerateCallSubtypeTestStub(test_kind, is_instance_lbl,
+                                       is_not_instance_lbl);
   }
   if (type.IsType()) {
     // Smi is FutureOr<T>, when T is a top type or int or num.
@@ -503,11 +445,8 @@
            compiler::Address(SP, 0 * kWordSize, compiler::Address::PairOffset));
     // Uninstantiated type class is known at compile time, but the type
     // arguments are determined at runtime by the instantiator.
-    return GenerateCallSubtypeTestStub(
-        kTestTypeFourArgs, TypeTestABI::kInstanceReg,
-        TypeTestABI::kInstantiatorTypeArgumentsReg,
-        TypeTestABI::kFunctionTypeArgumentsReg, kTempReg, is_instance_lbl,
-        is_not_instance_lbl);
+    return GenerateCallSubtypeTestStub(kTestTypeFourArgs, is_instance_lbl,
+                                       is_not_instance_lbl);
   }
   return SubtypeTestCache::null();
 }
@@ -526,12 +465,8 @@
          compiler::Address(SP, 0 * kWordSize, compiler::Address::PairOffset));
   // Uninstantiated type class is known at compile time, but the type
   // arguments are determined at runtime by the instantiator(s).
-  const Register kTempReg = kNoRegister;
-  return GenerateCallSubtypeTestStub(
-      kTestTypeSixArgs, TypeTestABI::kInstanceReg,
-      TypeTestABI::kInstantiatorTypeArgumentsReg,
-      TypeTestABI::kFunctionTypeArgumentsReg, kTempReg, is_instance_lbl,
-      is_not_instance_lbl);
+  return GenerateCallSubtypeTestStub(kTestTypeSixArgs, is_instance_lbl,
+                                     is_not_instance_lbl);
 }
 
 // Inputs:
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc b/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
index 83f47d6..3525a02 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
@@ -195,52 +195,48 @@
   __ Bind(&fall_through);
 }
 
-// Clobbers ECX.
+// Input registers (from TypeTestABI):
+// - kInstanceReg: instance.
+// - kInstantiatorTypeArgumentsReg: instantiator type arguments
+//   (for test_kind == kTestTypeFourArg or test_kind == kTestTypeSixArg).
+// - kFunctionTypeArgumentsReg: function type arguments
+//   (for test_kind == kTestTypeFourArg or test_kind == kTestTypeSixArg).
+//
+// Only preserves kInstanceReg from TypeTestABI, all other TypeTestABI
+// registers may be used and thus must be saved by the caller.
 SubtypeTestCachePtr FlowGraphCompiler::GenerateCallSubtypeTestStub(
     TypeTestStubKind test_kind,
-    Register instance_reg,
-    Register instantiator_type_arguments_reg,
-    Register function_type_arguments_reg,
-    Register temp_reg,
     compiler::Label* is_instance_lbl,
     compiler::Label* is_not_instance_lbl) {
   const SubtypeTestCache& type_test_cache =
       SubtypeTestCache::ZoneHandle(zone(), SubtypeTestCache::New());
-  const compiler::Immediate& raw_null =
-      compiler::Immediate(static_cast<intptr_t>(Object::null()));
-  __ LoadObject(temp_reg, type_test_cache);
-  __ pushl(temp_reg);      // Subtype test cache.
-  __ pushl(instance_reg);  // Instance.
+  __ LoadObject(TypeTestABI::kSubtypeTestCacheReg, type_test_cache);
+  __ pushl(TypeTestABI::kSubtypeTestCacheReg);
+  __ pushl(TypeTestABI::kInstanceReg);
   if (test_kind == kTestTypeOneArg) {
-    ASSERT(instantiator_type_arguments_reg == kNoRegister);
-    ASSERT(function_type_arguments_reg == kNoRegister);
-    __ pushl(raw_null);
-    __ pushl(raw_null);
+    __ PushObject(Object::null_object());
+    __ PushObject(Object::null_object());
     __ Call(StubCode::Subtype1TestCache());
   } else if (test_kind == kTestTypeTwoArgs) {
-    ASSERT(instantiator_type_arguments_reg == kNoRegister);
-    ASSERT(function_type_arguments_reg == kNoRegister);
-    __ pushl(raw_null);
-    __ pushl(raw_null);
+    __ PushObject(Object::null_object());
+    __ PushObject(Object::null_object());
     __ Call(StubCode::Subtype2TestCache());
   } else if (test_kind == kTestTypeFourArgs) {
-    __ pushl(instantiator_type_arguments_reg);
-    __ pushl(function_type_arguments_reg);
+    __ pushl(TypeTestABI::kInstantiatorTypeArgumentsReg);
+    __ pushl(TypeTestABI::kFunctionTypeArgumentsReg);
     __ Call(StubCode::Subtype4TestCache());
   } else if (test_kind == kTestTypeSixArgs) {
-    __ pushl(instantiator_type_arguments_reg);
-    __ pushl(function_type_arguments_reg);
+    __ pushl(TypeTestABI::kInstantiatorTypeArgumentsReg);
+    __ pushl(TypeTestABI::kFunctionTypeArgumentsReg);
     __ Call(StubCode::Subtype6TestCache());
   } else {
     UNREACHABLE();
   }
-  // Result is in ECX: null -> not found, otherwise Bool::True or Bool::False.
-  ASSERT(instance_reg != ECX);
-  ASSERT(temp_reg != ECX);
   __ Drop(2);
-  __ popl(instance_reg);  // Restore receiver.
-  __ popl(temp_reg);      // Discard.
-  GenerateBoolToJump(ECX, is_instance_lbl, is_not_instance_lbl);
+  __ popl(TypeTestABI::kInstanceReg);  // Restore receiver.
+  __ Drop(1);
+  GenerateBoolToJump(TypeTestABI::kSubtypeTestCacheResultReg, is_instance_lbl,
+                     is_not_instance_lbl);
   return type_test_cache.raw();
 }
 
@@ -300,13 +296,8 @@
     }
   }
   // Regular subtype test cache involving instance's type arguments.
-  const Register kInstantiatorTypeArgumentsReg = kNoRegister;
-  const Register kFunctionTypeArgumentsReg = kNoRegister;
-  const Register kTempReg = EDI;
-  return GenerateCallSubtypeTestStub(
-      kTestTypeTwoArgs, TypeTestABI::kInstanceReg,
-      kInstantiatorTypeArgumentsReg, kFunctionTypeArgumentsReg, kTempReg,
-      is_instance_lbl, is_not_instance_lbl);
+  return GenerateCallSubtypeTestStub(kTestTypeTwoArgs, is_instance_lbl,
+                                     is_not_instance_lbl);
 }
 
 void FlowGraphCompiler::CheckClassIds(Register class_id_reg,
@@ -415,13 +406,8 @@
     __ j(EQUAL, is_instance_lbl);
   }
 
-  const Register kInstantiatorTypeArgumentsReg = kNoRegister;
-  const Register kFunctionTypeArgumentsReg = kNoRegister;
-  const Register kTempReg = EDI;
-  return GenerateCallSubtypeTestStub(kTestTypeOneArg, TypeTestABI::kInstanceReg,
-                                     kInstantiatorTypeArgumentsReg,
-                                     kFunctionTypeArgumentsReg, kTempReg,
-                                     is_instance_lbl, is_not_instance_lbl);
+  return GenerateCallSubtypeTestStub(kTestTypeOneArg, is_instance_lbl,
+                                     is_not_instance_lbl);
 }
 
 // Generates inlined check if 'type' is a type parameter or type itself
@@ -433,18 +419,19 @@
     compiler::Label* is_instance_lbl,
     compiler::Label* is_not_instance_lbl) {
   __ Comment("UninstantiatedTypeTest");
-  const Register kTempReg = EDI;
   ASSERT(!type.IsInstantiated());
   ASSERT(!type.IsFunctionType());
   // Skip check if destination is a dynamic type.
   const compiler::Immediate& raw_null =
       compiler::Immediate(static_cast<intptr_t>(Object::null()));
   if (type.IsTypeParameter()) {
+    const Register kTempReg = EDI;
     const TypeParameter& type_param = TypeParameter::Cast(type);
 
-    __ movl(EDX, compiler::Address(
-                     ESP, 1 * kWordSize));  // Get instantiator type args.
-    __ movl(ECX,
+    __ movl(
+        TypeTestABI::kInstantiatorTypeArgumentsReg,
+        compiler::Address(ESP, 1 * kWordSize));  // Get instantiator type args.
+    __ movl(TypeTestABI::kFunctionTypeArgumentsReg,
             compiler::Address(ESP, 0 * kWordSize));  // Get function type args.
     // EDX: instantiator type arguments.
     // ECX: function type arguments.
@@ -455,39 +442,36 @@
     // Check if type arguments are null, i.e. equivalent to vector of dynamic.
     __ cmpl(kTypeArgumentsReg, raw_null);
     __ j(EQUAL, is_instance_lbl);
-    __ movl(EDI, compiler::FieldAddress(
-                     kTypeArgumentsReg,
-                     TypeArguments::type_at_offset(type_param.index())));
+    __ movl(kTempReg, compiler::FieldAddress(
+                          kTypeArgumentsReg,
+                          TypeArguments::type_at_offset(type_param.index())));
     // EDI: concrete type of type.
     // Check if type argument is dynamic, Object?, or void.
-    __ CompareObject(EDI, Object::dynamic_type());
+    __ CompareObject(kTempReg, Object::dynamic_type());
     __ j(EQUAL, is_instance_lbl);
     __ CompareObject(
-        EDI, Type::ZoneHandle(
-                 zone(), isolate()->object_store()->nullable_object_type()));
+        kTempReg,
+        Type::ZoneHandle(zone(),
+                         isolate()->object_store()->nullable_object_type()));
     __ j(EQUAL, is_instance_lbl);
-    __ CompareObject(EDI, Object::void_type());
+    __ CompareObject(kTempReg, Object::void_type());
     __ j(EQUAL, is_instance_lbl);
 
     // For Smi check quickly against int and num interfaces.
     compiler::Label not_smi;
-    __ testl(EAX, compiler::Immediate(kSmiTagMask));  // Value is Smi?
+    __ testl(TypeTestABI::kInstanceReg,
+             compiler::Immediate(kSmiTagMask));  // Value is Smi?
     __ j(NOT_ZERO, &not_smi, compiler::Assembler::kNearJump);
-    __ CompareObject(EDI, Type::ZoneHandle(zone(), Type::IntType()));
+    __ CompareObject(kTempReg, Type::ZoneHandle(zone(), Type::IntType()));
     __ j(EQUAL, is_instance_lbl);
-    __ CompareObject(EDI, Type::ZoneHandle(zone(), Type::Number()));
+    __ CompareObject(kTempReg, Type::ZoneHandle(zone(), Type::Number()));
     __ j(EQUAL, is_instance_lbl);
     // Smi can be handled by type test cache.
     __ Bind(&not_smi);
 
     const auto test_kind = GetTypeTestStubKindForTypeParameter(type_param);
-    const SubtypeTestCache& type_test_cache = SubtypeTestCache::ZoneHandle(
-        zone(), GenerateCallSubtypeTestStub(
-                    test_kind, TypeTestABI::kInstanceReg,
-                    TypeTestABI::kInstantiatorTypeArgumentsReg,
-                    TypeTestABI::kFunctionTypeArgumentsReg, kTempReg,
-                    is_instance_lbl, is_not_instance_lbl));
-    return type_test_cache.raw();
+    return GenerateCallSubtypeTestStub(test_kind, is_instance_lbl,
+                                       is_not_instance_lbl);
   }
   if (type.IsType()) {
     // Smi is FutureOr<T>, when T is a top type or int or num.
@@ -502,11 +486,8 @@
             compiler::Address(ESP, 0 * kWordSize));
     // Uninstantiated type class is known at compile time, but the type
     // arguments are determined at runtime by the instantiator(s).
-    return GenerateCallSubtypeTestStub(
-        kTestTypeFourArgs, TypeTestABI::kInstanceReg,
-        TypeTestABI::kInstantiatorTypeArgumentsReg,
-        TypeTestABI::kFunctionTypeArgumentsReg, kTempReg, is_instance_lbl,
-        is_not_instance_lbl);
+    return GenerateCallSubtypeTestStub(kTestTypeFourArgs, is_instance_lbl,
+                                       is_not_instance_lbl);
   }
   return SubtypeTestCache::null();
 }
@@ -525,12 +506,8 @@
   __ j(ZERO, is_not_instance_lbl);
   // Uninstantiated type class is known at compile time, but the type
   // arguments are determined at runtime by the instantiator(s).
-  const Register kTempReg = EDI;
-  return GenerateCallSubtypeTestStub(
-      kTestTypeSixArgs, TypeTestABI::kInstanceReg,
-      TypeTestABI::kInstantiatorTypeArgumentsReg,
-      TypeTestABI::kFunctionTypeArgumentsReg, kTempReg, is_instance_lbl,
-      is_not_instance_lbl);
+  return GenerateCallSubtypeTestStub(kTestTypeSixArgs, is_instance_lbl,
+                                     is_not_instance_lbl);
 }
 
 // Inputs:
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc b/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
index 41e5961..80879b0 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
@@ -217,55 +217,6 @@
   __ Bind(&fall_through);
 }
 
-// Call stub to perform subtype test using a cache (see
-// stub_code_x64.cc:GenerateSubtypeNTestCacheStub)
-//
-// Inputs:
-//   - RAX : instance to test against.
-//   - RDX : instantiator type arguments (if necessary).
-//   - RCX : function type arguments (if necessary).
-//
-// Preserves RAX/RCX/RDX.
-SubtypeTestCachePtr FlowGraphCompiler::GenerateCallSubtypeTestStub(
-    TypeTestStubKind test_kind,
-    Register instance_reg,
-    Register instantiator_type_arguments_reg,
-    Register function_type_arguments_reg,
-    Register temp_reg,
-    compiler::Label* is_instance_lbl,
-    compiler::Label* is_not_instance_lbl) {
-  ASSERT(temp_reg == kNoRegister);
-  const SubtypeTestCache& type_test_cache =
-      SubtypeTestCache::ZoneHandle(zone(), SubtypeTestCache::New());
-  __ LoadUniqueObject(R9, type_test_cache);
-  if (test_kind == kTestTypeOneArg) {
-    ASSERT(instantiator_type_arguments_reg == kNoRegister);
-    ASSERT(function_type_arguments_reg == kNoRegister);
-    __ Call(StubCode::Subtype1TestCache());
-  } else if (test_kind == kTestTypeTwoArgs) {
-    ASSERT(instantiator_type_arguments_reg == kNoRegister);
-    ASSERT(function_type_arguments_reg == kNoRegister);
-    __ Call(StubCode::Subtype2TestCache());
-  } else if (test_kind == kTestTypeFourArgs) {
-    ASSERT(instantiator_type_arguments_reg ==
-           TypeTestABI::kInstantiatorTypeArgumentsReg);
-    ASSERT(function_type_arguments_reg ==
-           TypeTestABI::kFunctionTypeArgumentsReg);
-    __ Call(StubCode::Subtype4TestCache());
-  } else if (test_kind == kTestTypeSixArgs) {
-    ASSERT(instantiator_type_arguments_reg ==
-           TypeTestABI::kInstantiatorTypeArgumentsReg);
-    ASSERT(function_type_arguments_reg ==
-           TypeTestABI::kFunctionTypeArgumentsReg);
-    __ Call(StubCode::Subtype6TestCache());
-  } else {
-    UNREACHABLE();
-  }
-  // Result is in R8: null -> not found, otherwise Bool::True or Bool::False.
-  GenerateBoolToJump(R8, is_instance_lbl, is_not_instance_lbl);
-  return type_test_cache.raw();
-}
-
 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if
 // type test is conclusive, otherwise fallthrough if a type test could not
 // be completed.
@@ -324,13 +275,8 @@
   }
 
   // Regular subtype test cache involving instance's type arguments.
-  const Register kInstantiatorTypeArgumentsReg = kNoRegister;
-  const Register kFunctionTypeArgumentsReg = kNoRegister;
-  const Register kTempReg = kNoRegister;
-  return GenerateCallSubtypeTestStub(
-      kTestTypeTwoArgs, TypeTestABI::kInstanceReg,
-      kInstantiatorTypeArgumentsReg, kFunctionTypeArgumentsReg, kTempReg,
-      is_instance_lbl, is_not_instance_lbl);
+  return GenerateCallSubtypeTestStub(kTestTypeTwoArgs, is_instance_lbl,
+                                     is_not_instance_lbl);
 }
 
 void FlowGraphCompiler::CheckClassIds(Register class_id_reg,
@@ -448,29 +394,24 @@
     __ j(EQUAL, is_instance_lbl);
   }
 
-  const Register kInstantiatorTypeArgumentsReg = kNoRegister;
-  const Register kFunctionTypeArgumentsReg = kNoRegister;
-  const Register kTempReg = kNoRegister;
-  return GenerateCallSubtypeTestStub(kTestTypeOneArg, TypeTestABI::kInstanceReg,
-                                     kInstantiatorTypeArgumentsReg,
-                                     kFunctionTypeArgumentsReg, kTempReg,
-                                     is_instance_lbl, is_not_instance_lbl);
+  return GenerateCallSubtypeTestStub(kTestTypeOneArg, is_instance_lbl,
+                                     is_not_instance_lbl);
 }
 
 // Generates inlined check if 'type' is a type parameter or type itself
 //
-// Inputs:
-//   - RAX : instance to test against.
-//   - RDX : instantiator type arguments (if necessary).
-//   - RCX : function type arguments (if necessary).
+// Inputs (from TypeTestABI):
+//   - kInstanceReg : instance to test against.
+//   - kInstantiatorTypeArgumentsReg : instantiator type arguments
+//     (if necessary).
+//   - kFunctionTypeArgumentsReg : function type arguments (if necessary).
 //
-// Preserves RAX/RCX/RDX.
+// Preserves all input registers.
 SubtypeTestCachePtr FlowGraphCompiler::GenerateUninstantiatedTypeTest(
     TokenPosition token_pos,
     const AbstractType& type,
     compiler::Label* is_instance_lbl,
     compiler::Label* is_not_instance_lbl) {
-  const Register kTempReg = kNoRegister;
   __ Comment("UninstantiatedTypeTest");
   ASSERT(!type.IsInstantiated());
   ASSERT(!type.IsFunctionType());
@@ -485,39 +426,39 @@
     // Check if type arguments are null, i.e. equivalent to vector of dynamic.
     __ CompareObject(kTypeArgumentsReg, Object::null_object());
     __ j(EQUAL, is_instance_lbl);
-    __ movq(RDI, compiler::FieldAddress(
-                     kTypeArgumentsReg,
-                     TypeArguments::type_at_offset(type_param.index())));
+    __ movq(TypeTestABI::kScratchReg,
+            compiler::FieldAddress(
+                kTypeArgumentsReg,
+                TypeArguments::type_at_offset(type_param.index())));
     // RDI: Concrete type of type.
     // Check if type argument is dynamic, Object?, or void.
-    __ CompareObject(RDI, Object::dynamic_type());
+    __ CompareObject(TypeTestABI::kScratchReg, Object::dynamic_type());
     __ j(EQUAL, is_instance_lbl);
     __ CompareObject(
-        RDI, Type::ZoneHandle(
-                 zone(), isolate()->object_store()->nullable_object_type()));
+        TypeTestABI::kScratchReg,
+        Type::ZoneHandle(zone(),
+                         isolate()->object_store()->nullable_object_type()));
     __ j(EQUAL, is_instance_lbl);
-    __ CompareObject(RDI, Object::void_type());
+    __ CompareObject(TypeTestABI::kScratchReg, Object::void_type());
     __ j(EQUAL, is_instance_lbl);
 
     // For Smi check quickly against int and num interfaces.
     compiler::Label not_smi;
-    __ testq(RAX, compiler::Immediate(kSmiTagMask));  // Value is Smi?
+    __ testq(TypeTestABI::kInstanceReg,
+             compiler::Immediate(kSmiTagMask));  // Value is Smi?
     __ j(NOT_ZERO, &not_smi, compiler::Assembler::kNearJump);
-    __ CompareObject(RDI, Type::ZoneHandle(zone(), Type::IntType()));
+    __ CompareObject(TypeTestABI::kScratchReg,
+                     Type::ZoneHandle(zone(), Type::IntType()));
     __ j(EQUAL, is_instance_lbl);
-    __ CompareObject(RDI, Type::ZoneHandle(zone(), Type::Number()));
+    __ CompareObject(TypeTestABI::kScratchReg,
+                     Type::ZoneHandle(zone(), Type::Number()));
     __ j(EQUAL, is_instance_lbl);
     // Smi can be handled by type test cache.
     __ Bind(&not_smi);
 
     const auto test_kind = GetTypeTestStubKindForTypeParameter(type_param);
-    const SubtypeTestCache& type_test_cache = SubtypeTestCache::ZoneHandle(
-        zone(), GenerateCallSubtypeTestStub(
-                    test_kind, TypeTestABI::kInstanceReg,
-                    TypeTestABI::kInstantiatorTypeArgumentsReg,
-                    TypeTestABI::kFunctionTypeArgumentsReg, kTempReg,
-                    is_instance_lbl, is_not_instance_lbl));
-    return type_test_cache.raw();
+    return GenerateCallSubtypeTestStub(test_kind, is_instance_lbl,
+                                       is_not_instance_lbl);
   }
   if (type.IsType()) {
     // Smi is FutureOr<T>, when T is a top type or int or num.
@@ -528,11 +469,8 @@
     }
     // Uninstantiated type class is known at compile time, but the type
     // arguments are determined at runtime by the instantiator(s).
-    return GenerateCallSubtypeTestStub(
-        kTestTypeFourArgs, TypeTestABI::kInstanceReg,
-        TypeTestABI::kInstantiatorTypeArgumentsReg,
-        TypeTestABI::kFunctionTypeArgumentsReg, kTempReg, is_instance_lbl,
-        is_not_instance_lbl);
+    return GenerateCallSubtypeTestStub(kTestTypeFourArgs, is_instance_lbl,
+                                       is_not_instance_lbl);
   }
   return SubtypeTestCache::null();
 }
@@ -545,16 +483,12 @@
     const AbstractType& type,
     compiler::Label* is_instance_lbl,
     compiler::Label* is_not_instance_lbl) {
-  const Register kTempReg = kNoRegister;
   __ Comment("FunctionTypeTest");
 
   __ testq(TypeTestABI::kInstanceReg, compiler::Immediate(kSmiTagMask));
   __ j(ZERO, is_not_instance_lbl);
-  return GenerateCallSubtypeTestStub(
-      kTestTypeSixArgs, TypeTestABI::kInstanceReg,
-      TypeTestABI::kInstantiatorTypeArgumentsReg,
-      TypeTestABI::kFunctionTypeArgumentsReg, kTempReg, is_instance_lbl,
-      is_not_instance_lbl);
+  return GenerateCallSubtypeTestStub(kTestTypeSixArgs, is_instance_lbl,
+                                     is_not_instance_lbl);
 }
 
 // Inputs:
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.cc b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
index 5c28b56..b6bd0e6 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.cc
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
@@ -3338,20 +3338,9 @@
     TypeParameterHelper helper(helper_);
     helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kBound);
 
-    // TODO(github.com/dart-lang/kernel/issues/42): This should be handled
-    // by the frontend.
     parameter ^= type_parameters.TypeAt(i);
-    const Tag tag = helper_->PeekTag();  // peek ith bound type.
-    if (tag == kDynamicType) {
-      helper_->SkipDartType();  // read ith bound.
-      parameter.set_bound(
-          Type::Handle(Z, nnbd_mode == NNBDMode::kOptedInLib
-                              ? I->object_store()->nullable_object_type()
-                              : I->object_store()->legacy_object_type()));
-    } else {
-      AbstractType& bound = BuildTypeWithoutFinalization();  // read ith bound.
-      parameter.set_bound(bound);
-    }
+    AbstractType& bound = BuildTypeWithoutFinalization();  // read ith bound.
+    parameter.set_bound(bound);
     helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kDefaultType);
     const AbstractType* default_arg = &Object::dynamic_type();
     if (helper_->ReadTag() == kSomething) {
diff --git a/runtime/vm/compiler/stub_code_compiler.cc b/runtime/vm/compiler/stub_code_compiler.cc
index 4376d54..d1bb15d 100644
--- a/runtime/vm/compiler/stub_code_compiler.cc
+++ b/runtime/vm/compiler/stub_code_compiler.cc
@@ -183,7 +183,7 @@
   __ PushRegister(TypeTestABI::kSubtypeTestCacheReg);
   __ CallRuntime(kInstanceofRuntimeEntry, /*argument_count=*/5);
   __ Drop(5);
-  __ PopRegister(TypeTestABI::kResultReg);
+  __ PopRegister(TypeTestABI::kInstanceOfResultReg);
   __ LeaveStubFrame();
   __ Ret();
 }
diff --git a/runtime/vm/compiler/stub_code_compiler_arm.cc b/runtime/vm/compiler/stub_code_compiler_arm.cc
index 141f378..0b759e0 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm.cc
@@ -2617,41 +2617,52 @@
 }
 
 // Used to check class and type arguments. Arguments passed in registers:
-// LR: return address.
-// R0: instance (must be preserved).
-// R2: instantiator type arguments (only if n >= 4, can be raw_null).
-// R1: function type arguments (only if n >= 4, can be raw_null).
-// R3: target::SubtypeTestCache.
 //
-// Preserves R0/R2.
-// Preserves NOTFP with bare instructions and CODE_REG without.
+// Inputs (mostly from TypeTestABI struct):
+//   - kSubtypeTestCacheReg: SubtypeTestCacheLayout
+//   - kInstanceReg: instance to test against (must be preserved).
+//   - kInstantiatorTypeArgumentsReg: instantiator type arguments (for n=4).
+//   - kFunctionTypeArgumentsReg: function type arguments (for n=4).
+//   - LR: return address.
 //
-// Result in R1: null -> not found, otherwise result (true or false).
+// All TypeTestABI registers are preserved but kSubtypeTestCacheReg and
+// kDstTypeReg, which must be saved by the caller if the original value is
+// needed after the call.
+//
+// Result in SubtypeTestCacheReg::kResultReg: null -> not found, otherwise
+// result (true or false).
 static void GenerateSubtypeNTestCacheStub(Assembler* assembler, int n) {
   ASSERT(n == 1 || n == 2 || n == 4 || n == 6);
 
-  const Register kInstanceCidOrFunction = R8;
-  const Register kInstanceInstantiatorTypeArgumentsReg = R4;
-  const Register kInstanceDelayedFunctionTypeArgumentsReg = PP;
+  // Safe as the original value of TypeTestABI::kSubtypeTestCacheReg is only
+  // used to initialize this register.
+  const Register kCacheArrayReg = TypeTestABI::kSubtypeTestCacheReg;
+  const Register kScratchReg = TypeTestABI::kScratchReg;
+  const Register kInstanceCidOrFunction = TypeTestABI::kDstTypeReg;
+  const Register kInstanceInstantiatorTypeArgumentsReg = R9;
+  // Registers that are only used for n >= 6 and must be preserved if used.
+  Register kInstanceParentFunctionTypeArgumentsReg = kNoRegister;
+  Register kInstanceDelayedFunctionTypeArgumentsReg = kNoRegister;
 
-  Register kInstanceParentFunctionTypeArgumentsReg;
-  Register kNullReg;
-  if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
-    // NOTFP must be preserved, but CODE_REG can be freely used.
-    kInstanceParentFunctionTypeArgumentsReg = NOTFP;
-    kNullReg = CODE_REG;
-  } else {
-    // CODE_REG must be preserved, but NOTFP can be freely used.
-    kInstanceParentFunctionTypeArgumentsReg = CODE_REG;
-    kNullReg = NOTFP;
-  }
-
+  // NOTFP must be preserved for bare payloads, otherwise CODE_REG.
+  const bool use_bare_payloads =
+      FLAG_precompiled_mode && FLAG_use_bare_instructions;
+  // For this, we choose the register that need not be preserved of the pair.
+  const Register kNullReg = use_bare_payloads ? CODE_REG : NOTFP;
   __ LoadObject(kNullReg, NullObject());
 
+  RegList pushed_registers = 0;
   // Free up these 2 registers to be used for 6-value test.
   if (n >= 6) {
-    __ PushList(1 << kInstanceParentFunctionTypeArgumentsReg |
-                1 << kInstanceDelayedFunctionTypeArgumentsReg);
+    // For this, we choose the register that must be preserved of the pair.
+    kInstanceParentFunctionTypeArgumentsReg =
+        use_bare_payloads ? NOTFP : CODE_REG;
+    kInstanceDelayedFunctionTypeArgumentsReg = PP;
+    pushed_registers |= 1 << kInstanceParentFunctionTypeArgumentsReg |
+                        1 << kInstanceDelayedFunctionTypeArgumentsReg;
+  }
+  if (pushed_registers != 0) {
+    __ PushList(pushed_registers);
   }
 
   // Loop initialization (moved up here to avoid having all dependent loads
@@ -2659,10 +2670,10 @@
 
   // We avoid a load-acquire barrier here by relying on the fact that all other
   // loads from the array are data-dependent loads.
-  __ ldr(TypeTestABI::kSubtypeTestCacheReg,
+  __ ldr(kCacheArrayReg,
          FieldAddress(TypeTestABI::kSubtypeTestCacheReg,
                       target::SubtypeTestCache::cache_offset()));
-  __ AddImmediate(TypeTestABI::kSubtypeTestCacheReg,
+  __ AddImmediate(kCacheArrayReg,
                   target::Array::data_offset() - kHeapObjectTag);
 
   Label loop, not_closure;
@@ -2702,16 +2713,19 @@
     __ Bind(&not_closure);
     if (n >= 2) {
       Label has_no_type_arguments;
-      __ LoadClassById(R9, kInstanceCidOrFunction);
+      __ LoadClassById(kScratchReg, kInstanceCidOrFunction);
       __ mov(kInstanceInstantiatorTypeArgumentsReg, Operand(kNullReg));
-      __ ldr(R9,
-             FieldAddress(
-                 R9, target::Class::
-                         host_type_arguments_field_offset_in_words_offset()));
-      __ CompareImmediate(R9, target::Class::kNoTypeArguments);
+      __ ldr(
+          kScratchReg,
+          FieldAddress(kScratchReg,
+                       target::Class::
+                           host_type_arguments_field_offset_in_words_offset()));
+      __ CompareImmediate(kScratchReg, target::Class::kNoTypeArguments);
       __ b(&has_no_type_arguments, EQ);
-      __ add(R9, TypeTestABI::kInstanceReg, Operand(R9, LSL, 2));
-      __ ldr(kInstanceInstantiatorTypeArgumentsReg, FieldAddress(R9, 0));
+      __ add(kScratchReg, TypeTestABI::kInstanceReg,
+             Operand(kScratchReg, LSL, 2));
+      __ ldr(kInstanceInstantiatorTypeArgumentsReg,
+             FieldAddress(kScratchReg, 0));
       __ Bind(&has_no_type_arguments);
 
       if (n >= 6) {
@@ -2726,77 +2740,80 @@
 
   // Loop header.
   __ Bind(&loop);
-  __ ldr(R9, Address(TypeTestABI::kSubtypeTestCacheReg,
-                     target::kWordSize *
-                         target::SubtypeTestCache::kInstanceClassIdOrFunction));
-  __ cmp(R9, Operand(kNullReg));
+  __ ldr(kScratchReg,
+         Address(kCacheArrayReg,
+                 target::kWordSize *
+                     target::SubtypeTestCache::kInstanceClassIdOrFunction));
+  __ cmp(kScratchReg, Operand(kNullReg));
   __ b(&not_found, EQ);
-  __ cmp(R9, Operand(kInstanceCidOrFunction));
+  __ cmp(kScratchReg, Operand(kInstanceCidOrFunction));
   if (n == 1) {
     __ b(&found, EQ);
   } else {
     __ b(&next_iteration, NE);
-    __ ldr(R9, Address(TypeTestABI::kSubtypeTestCacheReg,
-                       target::kWordSize *
-                           target::SubtypeTestCache::kInstanceTypeArguments));
-    __ cmp(R9, Operand(kInstanceInstantiatorTypeArgumentsReg));
+    __ ldr(kScratchReg,
+           Address(kCacheArrayReg,
+                   target::kWordSize *
+                       target::SubtypeTestCache::kInstanceTypeArguments));
+    __ cmp(kScratchReg, Operand(kInstanceInstantiatorTypeArgumentsReg));
     if (n == 2) {
       __ b(&found, EQ);
     } else {
       __ b(&next_iteration, NE);
-      __ ldr(R9,
-             Address(TypeTestABI::kSubtypeTestCacheReg,
+      __ ldr(kScratchReg,
+             Address(kCacheArrayReg,
                      target::kWordSize *
                          target::SubtypeTestCache::kInstantiatorTypeArguments));
-      __ cmp(R9, Operand(TypeTestABI::kInstantiatorTypeArgumentsReg));
+      __ cmp(kScratchReg, Operand(TypeTestABI::kInstantiatorTypeArgumentsReg));
       __ b(&next_iteration, NE);
-      __ ldr(R9, Address(TypeTestABI::kSubtypeTestCacheReg,
-                         target::kWordSize *
-                             target::SubtypeTestCache::kFunctionTypeArguments));
-      __ cmp(R9, Operand(TypeTestABI::kFunctionTypeArgumentsReg));
+      __ ldr(kScratchReg,
+             Address(kCacheArrayReg,
+                     target::kWordSize *
+                         target::SubtypeTestCache::kFunctionTypeArguments));
+      __ cmp(kScratchReg, Operand(TypeTestABI::kFunctionTypeArgumentsReg));
       if (n == 4) {
         __ b(&found, EQ);
       } else {
         ASSERT(n == 6);
         __ b(&next_iteration, NE);
 
-        __ ldr(R9, Address(TypeTestABI::kSubtypeTestCacheReg,
-                           target::kWordSize *
-                               target::SubtypeTestCache::
-                                   kInstanceParentFunctionTypeArguments));
-        __ cmp(R9, Operand(kInstanceParentFunctionTypeArgumentsReg));
+        __ ldr(kScratchReg,
+               Address(kCacheArrayReg,
+                       target::kWordSize *
+                           target::SubtypeTestCache::
+                               kInstanceParentFunctionTypeArguments));
+        __ cmp(kScratchReg, Operand(kInstanceParentFunctionTypeArgumentsReg));
         __ b(&next_iteration, NE);
 
-        __ ldr(R9, Address(TypeTestABI::kSubtypeTestCacheReg,
-                           target::kWordSize *
-                               target::SubtypeTestCache::
-                                   kInstanceDelayedFunctionTypeArguments));
-        __ cmp(R9, Operand(kInstanceDelayedFunctionTypeArgumentsReg));
+        __ ldr(kScratchReg,
+               Address(kCacheArrayReg,
+                       target::kWordSize *
+                           target::SubtypeTestCache::
+                               kInstanceDelayedFunctionTypeArguments));
+        __ cmp(kScratchReg, Operand(kInstanceDelayedFunctionTypeArgumentsReg));
         __ b(&found, EQ);
       }
     }
   }
   __ Bind(&next_iteration);
   __ AddImmediate(
-      TypeTestABI::kSubtypeTestCacheReg,
+      kCacheArrayReg,
       target::kWordSize * target::SubtypeTestCache::kTestEntryLength);
   __ b(&loop);
 
   __ Bind(&found);
-  __ ldr(R1,
-         Address(TypeTestABI::kSubtypeTestCacheReg,
+  __ ldr(TypeTestABI::kSubtypeTestCacheResultReg,
+         Address(kCacheArrayReg,
                  target::kWordSize * target::SubtypeTestCache::kTestResult));
-  if (n >= 6) {
-    __ PopList(1 << kInstanceParentFunctionTypeArgumentsReg |
-               1 << kInstanceDelayedFunctionTypeArgumentsReg);
+  if (pushed_registers != 0) {
+    __ PopList(pushed_registers);
   }
   __ Ret();
 
   __ Bind(&not_found);
-  __ mov(R1, Operand(kNullReg));
-  if (n >= 6) {
-    __ PopList(1 << kInstanceParentFunctionTypeArgumentsReg |
-               1 << kInstanceDelayedFunctionTypeArgumentsReg);
+  __ mov(TypeTestABI::kSubtypeTestCacheResultReg, Operand(kNullReg));
+  if (pushed_registers != 0) {
+    __ PopList(pushed_registers);
   }
   __ Ret();
 }
@@ -2821,20 +2838,22 @@
   GenerateSubtypeNTestCacheStub(assembler, 6);
 }
 
-// Used to test whether a given value is of a given type (different variants,
-// all have the same calling convention).
+// The <X>TypeTestStubs are used to test whether a given value is of a given
+// type. All variants have the same calling convention:
 //
-// Inputs:
-//   - R0 : instance to test against.
-//   - R2 : instantiator type arguments (if needed).
-//   - R1 : function type arguments (if needed).
+// Inputs (from TypeTestABI struct):
+//   - kSubtypeTestCacheReg: RawSubtypeTestCache
+//   - kInstanceReg: instance to test against.
+//   - kInstantiatorTypeArgumentsReg : instantiator type arguments (if needed).
+//   - kFunctionTypeArgumentsReg : function type arguments (if needed).
 //
-//   - R3 : subtype test cache.
+// See GenerateSubtypeNTestCacheStub for registers that may need saving by the
+// caller.
 //
-//   - R8 : type to test against.
-//   - R4 : name of destination variable.
+// Output (from TypeTestABI struct):
+//   - kResultReg: checked instance.
 //
-// Preserves R0/R2.
+// Throws if the check is unsuccessful.
 //
 // Note of warning: The caller will not populate CODE_REG and we have therefore
 // no access to the pool.
@@ -2982,25 +3001,25 @@
   __ CompareObject(TypeTestABI::kSubtypeTestCacheReg, NullObject());
   __ BranchIf(EQUAL, &call_runtime);
 
-  const Register kTmp = R9;
-
   // If this is not a [Type] object, we'll go to the runtime.
   Label is_simple_case, is_complex_case;
-  __ LoadClassId(kTmp, TypeTestABI::kDstTypeReg);
-  __ cmp(kTmp, Operand(kTypeCid));
+  __ LoadClassId(TypeTestABI::kScratchReg, TypeTestABI::kDstTypeReg);
+  __ cmp(TypeTestABI::kScratchReg, Operand(kTypeCid));
   __ BranchIf(NOT_EQUAL, &is_complex_case);
 
   // Check whether this [Type] is instantiated/uninstantiated.
-  __ ldrb(kTmp, FieldAddress(TypeTestABI::kDstTypeReg,
-                             target::Type::type_state_offset()));
-  __ cmp(kTmp,
+  __ ldrb(TypeTestABI::kScratchReg,
+          FieldAddress(TypeTestABI::kDstTypeReg,
+                       target::Type::type_state_offset()));
+  __ cmp(TypeTestABI::kScratchReg,
          Operand(target::AbstractTypeLayout::kTypeStateFinalizedInstantiated));
   __ BranchIf(NOT_EQUAL, &is_complex_case);
 
   // Check whether this [Type] is a function type.
-  __ ldr(kTmp, FieldAddress(TypeTestABI::kDstTypeReg,
-                            target::Type::signature_offset()));
-  __ CompareObject(kTmp, NullObject());
+  __ ldr(
+      TypeTestABI::kScratchReg,
+      FieldAddress(TypeTestABI::kDstTypeReg, target::Type::signature_offset()));
+  __ CompareObject(TypeTestABI::kScratchReg, NullObject());
   __ BranchIf(NOT_EQUAL, &is_complex_case);
 
   // This [Type] could be a FutureOr. Subtype2TestCache does not support Smi.
@@ -3009,14 +3028,14 @@
   // Fall through to &is_simple_case
 
   const intptr_t kRegsToSave = (1 << TypeTestABI::kSubtypeTestCacheReg) |
-                               (1 << TypeTestABI::kDstTypeReg) |
-                               (1 << TypeTestABI::kFunctionTypeArgumentsReg);
+                               (1 << TypeTestABI::kDstTypeReg);
 
   __ Bind(&is_simple_case);
   {
     __ PushList(kRegsToSave);
     __ BranchLink(StubCodeSubtype2TestCache());
-    __ CompareObject(R1, CastHandle<Object>(TrueObject()));
+    __ CompareObject(TypeTestABI::kSubtypeTestCacheResultReg,
+                     CastHandle<Object>(TrueObject()));
     __ PopList(kRegsToSave);
     __ BranchIf(EQUAL, &done);  // Cache said: yes.
     __ Jump(&call_runtime);
@@ -3026,7 +3045,8 @@
   {
     __ PushList(kRegsToSave);
     __ BranchLink(StubCodeSubtype6TestCache());
-    __ CompareObject(R1, CastHandle<Object>(TrueObject()));
+    __ CompareObject(TypeTestABI::kSubtypeTestCacheResultReg,
+                     CastHandle<Object>(TrueObject()));
     __ PopList(kRegsToSave);
     __ BranchIf(EQUAL, &done);  // Cache said: yes.
     // Fall through to runtime_call
diff --git a/runtime/vm/compiler/stub_code_compiler_arm64.cc b/runtime/vm/compiler/stub_code_compiler_arm64.cc
index b749ad9..d64b50d 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm64.cc
@@ -2786,36 +2786,47 @@
 }
 
 // Used to check class and type arguments. Arguments passed in registers:
-// LR: return address.
-// R0: instance (must be preserved).
-// R2: instantiator type arguments (only if n == 4, can be raw_null).
-// R1: function type arguments (only if n == 4, can be raw_null).
-// R3: target::SubtypeTestCache.
 //
-// Preserves R0/R2/R8.
+// Inputs (mostly from TypeTestABI struct):
+//   - kSubtypeTestCacheReg: SubtypeTestCacheLayout
+//   - kInstanceReg: instance to test against.
+//   - kInstantiatorTypeArgumentsReg: instantiator type arguments (for n=4).
+//   - kFunctionTypeArgumentsReg: function type arguments (for n=4).
+//   - LR: return address.
 //
-// Result in R1: null -> not found, otherwise result (true or false).
+// All input registers are preserved except for kSubtypeTestCacheReg, which
+// should be saved by the caller if needed.
+//
+// Result in SubtypeTestCacheReg::kResultReg: null -> not found, otherwise
+// result (true or false).
 static void GenerateSubtypeNTestCacheStub(Assembler* assembler, int n) {
   ASSERT(n == 1 || n == 2 || n == 4 || n == 6);
 
+  // Until we have the result, we use the result register to store the null
+  // value for quick access. This has the side benefit of initializing the
+  // result to null, so it only needs to be changed if found.
+  const Register kNullReg = TypeTestABI::kSubtypeTestCacheResultReg;
+  __ LoadObject(kNullReg, NullObject());
+
+  const Register kCacheArrayReg = TypeTestABI::kSubtypeTestCacheReg;
+  const Register kScratchReg = TypeTestABI::kScratchReg;
   const Register kInstanceCidOrFunction = R6;
-  const Register kInstanceInstantiatorTypeArgumentsReg = R4;
+  const Register kInstanceInstantiatorTypeArgumentsReg = R5;
   const Register kInstanceParentFunctionTypeArgumentsReg = R9;
   const Register kInstanceDelayedFunctionTypeArgumentsReg = R10;
 
-  const Register kNullReg = R7;
-
-  __ LoadObject(kNullReg, NullObject());
+  // All of these must be distinct from TypeTestABI::kSubtypeTestCacheResultReg
+  // since it is used for kNullReg as well.
 
   // Loop initialization (moved up here to avoid having all dependent loads
   // after each other).
 
   // We avoid a load-acquire barrier here by relying on the fact that all other
   // loads from the array are data-dependent loads.
-  __ ldr(TypeTestABI::kSubtypeTestCacheReg,
+  __ ldr(kCacheArrayReg,
          FieldAddress(TypeTestABI::kSubtypeTestCacheReg,
                       target::SubtypeTestCache::cache_offset()));
-  __ AddImmediate(TypeTestABI::kSubtypeTestCacheReg,
+  __ AddImmediate(kCacheArrayReg,
                   target::Array::data_offset() - kHeapObjectTag);
 
   Label loop, not_closure;
@@ -2856,16 +2867,18 @@
     __ Bind(&not_closure);
     if (n >= 2) {
       Label has_no_type_arguments;
-      __ LoadClassById(R5, kInstanceCidOrFunction);
+      __ LoadClassById(kScratchReg, kInstanceCidOrFunction);
       __ mov(kInstanceInstantiatorTypeArgumentsReg, kNullReg);
       __ LoadFieldFromOffset(
-          R5, R5,
+          kScratchReg, kScratchReg,
           target::Class::host_type_arguments_field_offset_in_words_offset(),
           kWord);
-      __ CompareImmediate(R5, target::Class::kNoTypeArguments);
+      __ CompareImmediate(kScratchReg, target::Class::kNoTypeArguments);
       __ b(&has_no_type_arguments, EQ);
-      __ add(R5, TypeTestABI::kInstanceReg, Operand(R5, LSL, 3));
-      __ ldr(kInstanceInstantiatorTypeArgumentsReg, FieldAddress(R5, 0));
+      __ add(kScratchReg, TypeTestABI::kInstanceReg,
+             Operand(kScratchReg, LSL, 3));
+      __ ldr(kInstanceInstantiatorTypeArgumentsReg,
+             FieldAddress(kScratchReg, 0));
       __ Bind(&has_no_type_arguments);
 
       if (n >= 6) {
@@ -2876,74 +2889,76 @@
     __ SmiTag(kInstanceCidOrFunction);
   }
 
-  Label found, not_found, next_iteration;
+  Label found, done, next_iteration;
 
   // Loop header
   __ Bind(&loop);
-  __ ldr(R5, Address(TypeTestABI::kSubtypeTestCacheReg,
-                     target::kWordSize *
-                         target::SubtypeTestCache::kInstanceClassIdOrFunction));
-  __ cmp(R5, Operand(kNullReg));
-  __ b(&not_found, EQ);
-  __ cmp(R5, Operand(kInstanceCidOrFunction));
+  __ ldr(kScratchReg,
+         Address(kCacheArrayReg,
+                 target::kWordSize *
+                     target::SubtypeTestCache::kInstanceClassIdOrFunction));
+  __ cmp(kScratchReg, Operand(kNullReg));
+  __ b(&done, EQ);
+  __ cmp(kScratchReg, Operand(kInstanceCidOrFunction));
   if (n == 1) {
     __ b(&found, EQ);
   } else {
     __ b(&next_iteration, NE);
-    __ ldr(R5, Address(TypeTestABI::kSubtypeTestCacheReg,
-                       target::kWordSize *
-                           target::SubtypeTestCache::kInstanceTypeArguments));
-    __ cmp(R5, Operand(kInstanceInstantiatorTypeArgumentsReg));
+    __ ldr(kScratchReg,
+           Address(kCacheArrayReg,
+                   target::kWordSize *
+                       target::SubtypeTestCache::kInstanceTypeArguments));
+    __ cmp(kScratchReg, Operand(kInstanceInstantiatorTypeArgumentsReg));
     if (n == 2) {
       __ b(&found, EQ);
     } else {
       __ b(&next_iteration, NE);
-      __ ldr(R5,
-             Address(TypeTestABI::kSubtypeTestCacheReg,
+      __ ldr(kScratchReg,
+             Address(kCacheArrayReg,
                      target::kWordSize *
                          target::SubtypeTestCache::kInstantiatorTypeArguments));
-      __ cmp(R5, Operand(TypeTestABI::kInstantiatorTypeArgumentsReg));
+      __ cmp(kScratchReg, Operand(TypeTestABI::kInstantiatorTypeArgumentsReg));
       __ b(&next_iteration, NE);
-      __ ldr(R5, Address(TypeTestABI::kSubtypeTestCacheReg,
-                         target::kWordSize *
-                             target::SubtypeTestCache::kFunctionTypeArguments));
-      __ cmp(R5, Operand(TypeTestABI::kFunctionTypeArgumentsReg));
+      __ ldr(kScratchReg,
+             Address(kCacheArrayReg,
+                     target::kWordSize *
+                         target::SubtypeTestCache::kFunctionTypeArguments));
+      __ cmp(kScratchReg, Operand(TypeTestABI::kFunctionTypeArgumentsReg));
       if (n == 4) {
         __ b(&found, EQ);
       } else {
         ASSERT(n == 6);
         __ b(&next_iteration, NE);
 
-        __ ldr(R5, Address(TypeTestABI::kSubtypeTestCacheReg,
-                           target::kWordSize *
-                               target::SubtypeTestCache::
-                                   kInstanceParentFunctionTypeArguments));
-        __ cmp(R5, Operand(kInstanceParentFunctionTypeArgumentsReg));
+        __ ldr(kScratchReg,
+               Address(kCacheArrayReg,
+                       target::kWordSize *
+                           target::SubtypeTestCache::
+                               kInstanceParentFunctionTypeArguments));
+        __ cmp(kScratchReg, Operand(kInstanceParentFunctionTypeArgumentsReg));
         __ b(&next_iteration, NE);
 
-        __ ldr(R5, Address(TypeTestABI::kSubtypeTestCacheReg,
-                           target::kWordSize *
-                               target::SubtypeTestCache::
-                                   kInstanceDelayedFunctionTypeArguments));
-        __ cmp(R5, Operand(kInstanceDelayedFunctionTypeArgumentsReg));
+        __ ldr(kScratchReg,
+               Address(kCacheArrayReg,
+                       target::kWordSize *
+                           target::SubtypeTestCache::
+                               kInstanceDelayedFunctionTypeArguments));
+        __ cmp(kScratchReg, Operand(kInstanceDelayedFunctionTypeArgumentsReg));
         __ b(&found, EQ);
       }
     }
   }
   __ Bind(&next_iteration);
   __ AddImmediate(
-      TypeTestABI::kSubtypeTestCacheReg,
+      kCacheArrayReg,
       target::kWordSize * target::SubtypeTestCache::kTestEntryLength);
   __ b(&loop);
 
   __ Bind(&found);
-  __ ldr(R1,
-         Address(TypeTestABI::kSubtypeTestCacheReg,
+  __ ldr(TypeTestABI::kSubtypeTestCacheResultReg,
+         Address(kCacheArrayReg,
                  target::kWordSize * target::SubtypeTestCache::kTestResult));
-  __ ret();
-
-  __ Bind(&not_found);
-  __ mov(R1, kNullReg);
+  __ Bind(&done);
   __ ret();
 }
 
@@ -2967,20 +2982,22 @@
   GenerateSubtypeNTestCacheStub(assembler, 6);
 }
 
-// Used to test whether a given value is of a given type (different variants,
-// all have the same calling convention).
+// The <X>TypeTestStubs are used to test whether a given value is of a given
+// type. All variants have the same calling convention:
 //
-// Inputs:
-//   - R0 : instance to test against.
-//   - R2 : instantiator type arguments (if needed).
-//   - R1 : function type arguments (if needed).
+// Inputs (from TypeTestABI struct):
+//   - kSubtypeTestCacheReg: RawSubtypeTestCache
+//   - kInstanceReg: instance to test against.
+//   - kInstantiatorTypeArgumentsReg : instantiator type arguments (if needed).
+//   - kFunctionTypeArgumentsReg : function type arguments (if needed).
 //
-//   - R3 : subtype test cache.
+// See GenerateSubtypeNTestCacheStub for registers that may need saving by the
+// caller.
 //
-//   - R8 : type to test against.
-//   - R4 : name of destination variable.
+// Output (from TypeTestABI struct):
+//   - kResultReg: checked instance.
 //
-// Preserves R0/R2.
+// Throws if the check is unsuccessful.
 //
 // Note of warning: The caller will not populate CODE_REG and we have therefore
 // no access to the pool.
@@ -3136,27 +3153,26 @@
   __ CompareObject(TypeTestABI::kSubtypeTestCacheReg, NullObject());
   __ BranchIf(EQUAL, &call_runtime);
 
-  const Register kTmp = R9;
-
   // If this is not a [Type] object, we'll go to the runtime.
   Label is_simple_case, is_complex_case;
-  __ LoadClassId(kTmp, TypeTestABI::kDstTypeReg);
-  __ cmp(kTmp, Operand(kTypeCid));
+  __ LoadClassId(TypeTestABI::kScratchReg, TypeTestABI::kDstTypeReg);
+  __ cmp(TypeTestABI::kScratchReg, Operand(kTypeCid));
   __ BranchIf(NOT_EQUAL, &is_complex_case);
 
   // Check whether this [Type] is instantiated/uninstantiated.
-  __ ldr(kTmp,
+  __ ldr(TypeTestABI::kScratchReg,
          FieldAddress(TypeTestABI::kDstTypeReg,
                       target::Type::type_state_offset(), kByte),
          kByte);
-  __ cmp(kTmp,
+  __ cmp(TypeTestABI::kScratchReg,
          Operand(target::AbstractTypeLayout::kTypeStateFinalizedInstantiated));
   __ BranchIf(NOT_EQUAL, &is_complex_case);
 
   // Check whether this [Type] is a function type.
-  __ ldr(kTmp, FieldAddress(TypeTestABI::kDstTypeReg,
-                            target::Type::signature_offset()));
-  __ CompareObject(kTmp, NullObject());
+  __ ldr(
+      TypeTestABI::kScratchReg,
+      FieldAddress(TypeTestABI::kDstTypeReg, target::Type::signature_offset()));
+  __ CompareObject(TypeTestABI::kScratchReg, NullObject());
   __ BranchIf(NOT_EQUAL, &is_complex_case);
 
   // This [Type] could be a FutureOr. Subtype2TestCache does not support Smi.
@@ -3169,7 +3185,8 @@
     __ PushPair(TypeTestABI::kFunctionTypeArgumentsReg,
                 TypeTestABI::kSubtypeTestCacheReg);
     __ BranchLink(StubCodeSubtype2TestCache());
-    __ CompareObject(R1, CastHandle<Object>(TrueObject()));
+    __ CompareObject(TypeTestABI::kSubtypeTestCacheResultReg,
+                     CastHandle<Object>(TrueObject()));
     __ PopPair(TypeTestABI::kFunctionTypeArgumentsReg,
                TypeTestABI::kSubtypeTestCacheReg);
     __ BranchIf(EQUAL, &done);  // Cache said: yes.
@@ -3181,7 +3198,8 @@
     __ PushPair(TypeTestABI::kFunctionTypeArgumentsReg,
                 TypeTestABI::kSubtypeTestCacheReg);
     __ BranchLink(StubCodeSubtype6TestCache());
-    __ CompareObject(R1, CastHandle<Object>(TrueObject()));
+    __ CompareObject(TypeTestABI::kSubtypeTestCacheResultReg,
+                     CastHandle<Object>(TrueObject()));
     __ PopPair(TypeTestABI::kFunctionTypeArgumentsReg,
                TypeTestABI::kSubtypeTestCacheReg);
     __ BranchIf(EQUAL, &done);  // Cache said: yes.
diff --git a/runtime/vm/compiler/stub_code_compiler_ia32.cc b/runtime/vm/compiler/stub_code_compiler_ia32.cc
index bc4c545..efc4e36 100644
--- a/runtime/vm/compiler/stub_code_compiler_ia32.cc
+++ b/runtime/vm/compiler/stub_code_compiler_ia32.cc
@@ -2186,29 +2186,69 @@
 // TOS + 2: instantiator type arguments (only if n == 4, can be raw_null).
 // TOS + 3: instance.
 // TOS + 4: SubtypeTestCache.
-// Result in ECX: null -> not found, otherwise result (true or false).
+//
+// No registers are preserved by this stub.
+//
+// Result in SubtypeTestCacheReg::kResultReg: null -> not found, otherwise
+// result (true or false).
 static void GenerateSubtypeNTestCacheStub(Assembler* assembler, int n) {
   ASSERT(n == 1 || n == 2 || n == 4 || n == 6);
 
-  static intptr_t kFunctionTypeArgumentsInBytes = 1 * target::kWordSize;
-  static intptr_t kInstantiatorTypeArgumentsInBytes = 2 * target::kWordSize;
-  static intptr_t kInstanceOffsetInBytes = 3 * target::kWordSize;
-  static intptr_t kCacheOffsetInBytes = 4 * target::kWordSize;
+  // We represent the depth of as a depth from the top of the stack at the
+  // start of the stub. That is, depths for input values are non-negative and
+  // depths for values pushed during the stub are negative.
 
-  const Register kInstanceCidOrFunction = ECX;
-  const Register kInstanceInstantiatorTypeArgumentsReg = EBX;
+  // Used to initialize depths for conditionally-pushed values.
+  const intptr_t kNoDepth = kIntptrMin;
+  // Offset of the original top of the stack from the current top of stack.
+  intptr_t original_tos_offset = 0;
+
+  // Inputs use relative depths.
+  static constexpr intptr_t kFunctionTypeArgumentsDepth = 1;
+  static constexpr intptr_t kInstantiatorTypeArgumentsDepth = 2;
+  static constexpr intptr_t kInstanceDepth = 3;
+  static constexpr intptr_t kCacheDepth = 4;
+  // Others use absolute depths. We initialize conditionally pushed values to
+  // kNoInput for extra checking.
+  intptr_t kInstanceParentFunctionTypeArgumentsDepth = kNoDepth;
+  intptr_t kInstanceDelayedFunctionTypeArgumentsDepth = kNoDepth;
+
+  // Other values are stored in non-kInstanceReg registers from TypeTestABI.
+  const Register kCacheArrayReg = TypeTestABI::kInstantiatorTypeArgumentsReg;
+  const Register kScratchReg = TypeTestABI::kSubtypeTestCacheReg;
+  const Register kInstanceCidOrFunction =
+      TypeTestABI::kFunctionTypeArgumentsReg;
+  const Register kInstanceInstantiatorTypeArgumentsReg =
+      TypeTestABI::kDstTypeReg;
+
+  // Loads a value at the given depth from the stack into dst.
+  auto load_from_stack = [&](Register dst, intptr_t depth) {
+    ASSERT(depth != kNoDepth);
+    __ movl(dst,
+            Address(ESP, (original_tos_offset + depth) * target::kWordSize));
+  };
+
+  // Compares a value at the given depth from the stack to the value in src.
+  auto compare_to_stack = [&](Register src, intptr_t depth) {
+    ASSERT(depth != kNoDepth);
+    __ cmpl(src,
+            Address(ESP, (original_tos_offset + depth) * target::kWordSize));
+  };
 
   const auto& raw_null = Immediate(target::ToRawPointer(NullObject()));
 
-  __ movl(TypeTestABI::kInstanceReg, Address(ESP, kInstanceOffsetInBytes));
+  load_from_stack(TypeTestABI::kInstanceReg, kInstanceDepth);
 
   // Loop initialization (moved up here to avoid having all dependent loads
   // after each other)
-  __ movl(EDX, Address(ESP, kCacheOffsetInBytes));
+  load_from_stack(kCacheArrayReg, kCacheDepth);
   // We avoid a load-acquire barrier here by relying on the fact that all other
   // loads from the array are data-dependent loads.
-  __ movl(EDX, FieldAddress(EDX, target::SubtypeTestCache::cache_offset()));
-  __ addl(EDX, Immediate(target::Array::data_offset() - kHeapObjectTag));
+  __ movl(
+      kCacheArrayReg,
+      FieldAddress(kCacheArrayReg, target::SubtypeTestCache::cache_offset()));
+  __ addl(kCacheArrayReg,
+          Immediate(target::Array::data_offset() - kHeapObjectTag));
 
   Label loop, not_closure;
   if (n >= 4) {
@@ -2246,16 +2286,17 @@
     __ Bind(&not_closure);
     if (n >= 2) {
       Label has_no_type_arguments;
-      __ LoadClassById(EDI, kInstanceCidOrFunction);
+      __ LoadClassById(kScratchReg, kInstanceCidOrFunction);
       __ movl(kInstanceInstantiatorTypeArgumentsReg, raw_null);
-      __ movl(EDI,
-              FieldAddress(
-                  EDI, target::Class::
+      __ movl(
+          kScratchReg,
+          FieldAddress(kScratchReg,
+                       target::Class::
                            host_type_arguments_field_offset_in_words_offset()));
-      __ cmpl(EDI, Immediate(target::Class::kNoTypeArguments));
+      __ cmpl(kScratchReg, Immediate(target::Class::kNoTypeArguments));
       __ j(EQUAL, &has_no_type_arguments, Assembler::kNearJump);
       __ movl(kInstanceInstantiatorTypeArgumentsReg,
-              FieldAddress(TypeTestABI::kInstanceReg, EDI, TIMES_4, 0));
+              FieldAddress(TypeTestABI::kInstanceReg, kScratchReg, TIMES_4, 0));
       __ Bind(&has_no_type_arguments);
 
       if (n >= 6) {
@@ -2266,84 +2307,91 @@
     __ SmiTag(kInstanceCidOrFunction);
   }
 
-  const intptr_t kInstanceParentFunctionTypeArgumentsFromSp = 0;
-  const intptr_t kInstanceDelayedFunctionTypeArgumentsFromSp =
-      target::kWordSize;
-  const intptr_t args_offset = n >= 6 ? 2 * target::kWordSize : 0;
+  if (n >= 6) {
+    // Now that instance handling is done, both the delayed and parent function
+    // type arguments stack slots have been set, so any input uses must be
+    // offset by the new values and the new values can now be accessed in
+    // the following code without issue when n >= 6.
+    original_tos_offset = 2;
+    kInstanceDelayedFunctionTypeArgumentsDepth = -1;
+    kInstanceParentFunctionTypeArgumentsDepth = -2;
+  }
 
-  Label found, not_found, next_iteration;
+  Label done, next_iteration;
 
   // Loop header.
   __ Bind(&loop);
-  __ movl(
-      EDI,
-      Address(EDX, target::kWordSize *
-                       target::SubtypeTestCache::kInstanceClassIdOrFunction));
-  __ cmpl(EDI, raw_null);
-  __ j(EQUAL, &not_found, Assembler::kNearJump);
-  __ cmpl(EDI, kInstanceCidOrFunction);
+  __ movl(kScratchReg,
+          Address(kCacheArrayReg,
+                  target::kWordSize *
+                      target::SubtypeTestCache::kInstanceClassIdOrFunction));
+  __ cmpl(kScratchReg, raw_null);
+  __ j(EQUAL, &done, Assembler::kNearJump);
+  __ cmpl(kScratchReg, kInstanceCidOrFunction);
   if (n == 1) {
-    __ j(EQUAL, &found, Assembler::kNearJump);
+    __ j(EQUAL, &done, Assembler::kNearJump);
   } else {
     __ j(NOT_EQUAL, &next_iteration, Assembler::kNearJump);
     __ cmpl(kInstanceInstantiatorTypeArgumentsReg,
-            Address(EDX, target::kWordSize *
-                             target::SubtypeTestCache::kInstanceTypeArguments));
+            Address(kCacheArrayReg,
+                    target::kWordSize *
+                        target::SubtypeTestCache::kInstanceTypeArguments));
     if (n == 2) {
-      __ j(EQUAL, &found, Assembler::kNearJump);
+      __ j(EQUAL, &done, Assembler::kNearJump);
     } else {
       __ j(NOT_EQUAL, &next_iteration, Assembler::kNearJump);
       __ movl(
-          EDI,
-          Address(EDX,
+          kScratchReg,
+          Address(kCacheArrayReg,
                   target::kWordSize *
                       target::SubtypeTestCache::kInstantiatorTypeArguments));
-      __ cmpl(EDI,
-              Address(ESP, args_offset + kInstantiatorTypeArgumentsInBytes));
+      compare_to_stack(kScratchReg, kInstantiatorTypeArgumentsDepth);
       __ j(NOT_EQUAL, &next_iteration, Assembler::kNearJump);
-      __ movl(
-          EDI,
-          Address(EDX, target::kWordSize *
-                           target::SubtypeTestCache::kFunctionTypeArguments));
-      __ cmpl(EDI, Address(ESP, args_offset + kFunctionTypeArgumentsInBytes));
+      __ movl(kScratchReg,
+              Address(kCacheArrayReg,
+                      target::kWordSize *
+                          target::SubtypeTestCache::kFunctionTypeArguments));
+      compare_to_stack(kScratchReg, kFunctionTypeArgumentsDepth);
       if (n == 4) {
-        __ j(EQUAL, &found, Assembler::kNearJump);
+        __ j(EQUAL, &done, Assembler::kNearJump);
       } else {
         ASSERT(n == 6);
         __ j(NOT_EQUAL, &next_iteration, Assembler::kNearJump);
 
-        __ movl(EDI,
-                Address(EDX, target::kWordSize *
-                                 target::SubtypeTestCache::
-                                     kInstanceParentFunctionTypeArguments));
-        __ cmpl(EDI, Address(ESP, kInstanceParentFunctionTypeArgumentsFromSp));
+        __ movl(kScratchReg,
+                Address(kCacheArrayReg,
+                        target::kWordSize *
+                            target::SubtypeTestCache::
+                                kInstanceParentFunctionTypeArguments));
+        compare_to_stack(kScratchReg,
+                         kInstanceParentFunctionTypeArgumentsDepth);
         __ j(NOT_EQUAL, &next_iteration, Assembler::kNearJump);
-        __ movl(EDI,
-                Address(EDX, target::kWordSize *
-                                 target::SubtypeTestCache::
-                                     kInstanceDelayedFunctionTypeArguments));
-        __ cmpl(EDI, Address(ESP, kInstanceDelayedFunctionTypeArgumentsFromSp));
-        __ j(EQUAL, &found, Assembler::kNearJump);
+        __ movl(kScratchReg,
+                Address(kCacheArrayReg,
+                        target::kWordSize *
+                            target::SubtypeTestCache::
+                                kInstanceDelayedFunctionTypeArguments));
+        compare_to_stack(kScratchReg,
+                         kInstanceDelayedFunctionTypeArgumentsDepth);
+        __ j(EQUAL, &done, Assembler::kNearJump);
       }
     }
   }
   __ Bind(&next_iteration);
-  __ addl(EDX, Immediate(target::kWordSize *
-                         target::SubtypeTestCache::kTestEntryLength));
+  __ addl(kCacheArrayReg,
+          Immediate(target::kWordSize *
+                    target::SubtypeTestCache::kTestEntryLength));
   __ jmp(&loop, Assembler::kNearJump);
 
-  __ Bind(&found);
-  __ movl(ECX, Address(EDX, target::kWordSize *
-                                target::SubtypeTestCache::kTestResult));
-  if (n == 6) {
+  __ Bind(&done);
+  // In the not found case, the test result slot is null, so we can
+  // unconditionally load from the cache entry.
+  __ movl(TypeTestABI::kSubtypeTestCacheResultReg,
+          Address(kCacheArrayReg,
+                  target::kWordSize * target::SubtypeTestCache::kTestResult));
+  if (n >= 6) {
     __ Drop(2);
-  }
-  __ ret();
-
-  __ Bind(&not_found);
-  __ movl(ECX, raw_null);
-  if (n == 6) {
-    __ Drop(2);
+    original_tos_offset = 0;  // In case we add any input uses after this point.
   }
   __ ret();
 }
diff --git a/runtime/vm/compiler/stub_code_compiler_x64.cc b/runtime/vm/compiler/stub_code_compiler_x64.cc
index b3c99af..676714f 100644
--- a/runtime/vm/compiler/stub_code_compiler_x64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_x64.cc
@@ -2732,31 +2732,41 @@
 
 // Used to check class and type arguments. Arguments passed in registers:
 //
-// Inputs:
-//   - R9  : RawSubtypeTestCache
-//   - RAX : instance to test against.
-//   - RDX : instantiator type arguments (for n=4).
-//   - RCX : function type arguments (for n=4).
-//
+// Input registers (from TypeTestABI struct):
+//   - kSubtypeTestCacheReg: SubtypeTestCacheLayout
+//   - kInstanceReg: instance to test against (must be preserved).
+//   - kInstantiatorTypeArgumentsReg: instantiator type arguments (for n>=4).
+//   - kFunctionTypeArgumentsReg: function type arguments (for n>=4).
+// Inputs from stack:
 //   - TOS + 0: return address.
 //
-// Preserves R9/RAX/RCX/RDX, RBX.
+// All input registers are preserved.
 //
-// Result in R8: null -> not found, otherwise result (true or false).
+// Result in SubtypeTestCacheReg::kResultReg: null -> not found, otherwise
+// result (true or false).
 static void GenerateSubtypeNTestCacheStub(Assembler* assembler, int n) {
   ASSERT(n == 1 || n == 2 || n == 4 || n == 6);
 
+  // Until we have the result, we use the result register to store the null
+  // value for quick access. This has the side benefit of initializing the
+  // result to null, so it only needs to be changed if found.
+  const Register kNullReg = TypeTestABI::kSubtypeTestCacheResultReg;
+  __ LoadObject(kNullReg, NullObject());
+
+  // All of these must be distinct from TypeTestABI::kSubtypeTestCacheResultReg
+  // since it is used for kNullReg as well.
+  const Register kCacheArrayReg = RDI;
+  const Register kScratchReg = TypeTestABI::kScratchReg;
   const Register kInstanceCidOrFunction = R10;
   const Register kInstanceInstantiatorTypeArgumentsReg = R13;
-  const Register kInstanceParentFunctionTypeArgumentsReg = PP;
-  const Register kInstanceDelayedFunctionTypeArgumentsReg = CODE_REG;
-
-  const Register kNullReg = R8;
-
-  __ LoadObject(kNullReg, NullObject());
+  // Only used for n >= 6, so set conditionally in that case to catch misuse.
+  Register kInstanceParentFunctionTypeArgumentsReg = kNoRegister;
+  Register kInstanceDelayedFunctionTypeArgumentsReg = kNoRegister;
 
   // Free up these 2 registers to be used for 6-value test.
   if (n >= 6) {
+    kInstanceParentFunctionTypeArgumentsReg = PP;
+    kInstanceDelayedFunctionTypeArgumentsReg = CODE_REG;
     __ pushq(kInstanceParentFunctionTypeArgumentsReg);
     __ pushq(kInstanceDelayedFunctionTypeArgumentsReg);
   }
@@ -2766,9 +2776,11 @@
 
   // We avoid a load-acquire barrier here by relying on the fact that all other
   // loads from the array are data-dependent loads.
-  __ movq(RSI, FieldAddress(TypeTestABI::kSubtypeTestCacheReg,
-                            target::SubtypeTestCache::cache_offset()));
-  __ addq(RSI, Immediate(target::Array::data_offset() - kHeapObjectTag));
+  __ movq(kCacheArrayReg,
+          FieldAddress(TypeTestABI::kSubtypeTestCacheReg,
+                       target::SubtypeTestCache::cache_offset()));
+  __ addq(kCacheArrayReg,
+          Immediate(target::Array::data_offset() - kHeapObjectTag));
 
   Label loop, not_closure;
   if (n >= 4) {
@@ -2808,16 +2820,17 @@
     __ Bind(&not_closure);
     if (n >= 2) {
       Label has_no_type_arguments;
-      __ LoadClassById(RDI, kInstanceCidOrFunction);
+      __ LoadClassById(kScratchReg, kInstanceCidOrFunction);
       __ movq(kInstanceInstantiatorTypeArgumentsReg, kNullReg);
-      __ movl(RDI,
-              FieldAddress(
-                  RDI, target::Class::
+      __ movl(
+          kScratchReg,
+          FieldAddress(kScratchReg,
+                       target::Class::
                            host_type_arguments_field_offset_in_words_offset()));
-      __ cmpl(RDI, Immediate(target::Class::kNoTypeArguments));
+      __ cmpl(kScratchReg, Immediate(target::Class::kNoTypeArguments));
       __ j(EQUAL, &has_no_type_arguments, Assembler::kNearJump);
       __ movq(kInstanceInstantiatorTypeArgumentsReg,
-              FieldAddress(TypeTestABI::kInstanceReg, RDI, TIMES_8, 0));
+              FieldAddress(TypeTestABI::kInstanceReg, kScratchReg, TIMES_8, 0));
       __ Bind(&has_no_type_arguments);
 
       if (n >= 6) {
@@ -2832,34 +2845,35 @@
 
   // Loop header.
   __ Bind(&loop);
-  __ movq(
-      RDI,
-      Address(RSI, target::kWordSize *
-                       target::SubtypeTestCache::kInstanceClassIdOrFunction));
-  __ cmpq(RDI, kNullReg);
+  __ movq(kScratchReg,
+          Address(kCacheArrayReg,
+                  target::kWordSize *
+                      target::SubtypeTestCache::kInstanceClassIdOrFunction));
+  __ cmpq(kScratchReg, kNullReg);
   __ j(EQUAL, &not_found, Assembler::kNearJump);
-  __ cmpq(RDI, kInstanceCidOrFunction);
+  __ cmpq(kScratchReg, kInstanceCidOrFunction);
   if (n == 1) {
     __ j(EQUAL, &found, Assembler::kNearJump);
   } else {
     __ j(NOT_EQUAL, &next_iteration, Assembler::kNearJump);
     __ cmpq(kInstanceInstantiatorTypeArgumentsReg,
-            Address(RSI, target::kWordSize *
-                             target::SubtypeTestCache::kInstanceTypeArguments));
+            Address(kCacheArrayReg,
+                    target::kWordSize *
+                        target::SubtypeTestCache::kInstanceTypeArguments));
     if (n == 2) {
       __ j(EQUAL, &found, Assembler::kNearJump);
     } else {
       __ j(NOT_EQUAL, &next_iteration, Assembler::kNearJump);
       __ cmpq(
           TypeTestABI::kInstantiatorTypeArgumentsReg,
-          Address(RSI,
+          Address(kCacheArrayReg,
                   target::kWordSize *
                       target::SubtypeTestCache::kInstantiatorTypeArguments));
       __ j(NOT_EQUAL, &next_iteration, Assembler::kNearJump);
-      __ cmpq(
-          TypeTestABI::kFunctionTypeArgumentsReg,
-          Address(RSI, target::kWordSize *
-                           target::SubtypeTestCache::kFunctionTypeArguments));
+      __ cmpq(TypeTestABI::kFunctionTypeArgumentsReg,
+              Address(kCacheArrayReg,
+                      target::kWordSize *
+                          target::SubtypeTestCache::kFunctionTypeArguments));
 
       if (n == 4) {
         __ j(EQUAL, &found, Assembler::kNearJump);
@@ -2868,32 +2882,31 @@
         __ j(NOT_EQUAL, &next_iteration, Assembler::kNearJump);
 
         __ cmpq(kInstanceParentFunctionTypeArgumentsReg,
-                Address(RSI, target::kWordSize *
-                                 target::SubtypeTestCache::
-                                     kInstanceParentFunctionTypeArguments));
+                Address(kCacheArrayReg,
+                        target::kWordSize *
+                            target::SubtypeTestCache::
+                                kInstanceParentFunctionTypeArguments));
         __ j(NOT_EQUAL, &next_iteration, Assembler::kNearJump);
         __ cmpq(kInstanceDelayedFunctionTypeArgumentsReg,
-                Address(RSI, target::kWordSize *
-                                 target::SubtypeTestCache::
-                                     kInstanceDelayedFunctionTypeArguments));
+                Address(kCacheArrayReg,
+                        target::kWordSize *
+                            target::SubtypeTestCache::
+                                kInstanceDelayedFunctionTypeArguments));
         __ j(EQUAL, &found, Assembler::kNearJump);
       }
     }
   }
 
   __ Bind(&next_iteration);
-  __ addq(RSI, Immediate(target::kWordSize *
-                         target::SubtypeTestCache::kTestEntryLength));
+  __ addq(kCacheArrayReg,
+          Immediate(target::kWordSize *
+                    target::SubtypeTestCache::kTestEntryLength));
   __ jmp(&loop, Assembler::kNearJump);
 
   __ Bind(&found);
-  __ movq(R8, Address(RSI, target::kWordSize *
-                               target::SubtypeTestCache::kTestResult));
-  if (n >= 6) {
-    __ popq(kInstanceDelayedFunctionTypeArgumentsReg);
-    __ popq(kInstanceParentFunctionTypeArgumentsReg);
-  }
-  __ ret();
+  __ movq(TypeTestABI::kSubtypeTestCacheResultReg,
+          Address(kCacheArrayReg,
+                  target::kWordSize * target::SubtypeTestCache::kTestResult));
 
   __ Bind(&not_found);
   if (n >= 6) {
@@ -2923,19 +2936,22 @@
   GenerateSubtypeNTestCacheStub(assembler, 6);
 }
 
-// Used to test whether a given value is of a given type (different variants,
-// all have the same calling convention).
+// The <X>TypeTestStubs are used to test whether a given value is of a given
+// type. All variants have the same calling convention:
 //
-// Inputs:
-//   - R9  : RawSubtypeTestCache
-//   - RAX : instance to test against.
-//   - RDX : instantiator type arguments (if needed).
-//   - RCX : function type arguments (if needed).
+// Inputs (from TypeTestABI struct):
+//   - kSubtypeTestCacheReg: RawSubtypeTestCache
+//   - kInstanceReg: instance to test against.
+//   - kInstantiatorTypeArgumentsReg : instantiator type arguments (if needed).
+//   - kFunctionTypeArgumentsReg : function type arguments (if needed).
 //
-//   - RBX : type to test against.
-//   - R10 : name of destination variable.
+// See GenerateSubtypeNTestCacheStub for registers that may need saving by the
+// caller.
 //
-// Preserves R9/RAX/RCX/RDX, RBX, R10.
+// Output (from TypeTestABI struct):
+//   - kResultReg: checked instance.
+//
+// Throws if the check is unsuccessful.
 //
 // Note of warning: The caller will not populate CODE_REG and we have therefore
 // no access to the pool.
@@ -3083,12 +3099,10 @@
   __ CompareObject(TypeTestABI::kSubtypeTestCacheReg, NullObject());
   __ BranchIf(EQUAL, &call_runtime);
 
-  const Register kTmp = RDI;
-
   // If this is not a [Type] object, we'll go to the runtime.
   Label is_simple_case, is_complex_case;
-  __ LoadClassId(kTmp, TypeTestABI::kDstTypeReg);
-  __ cmpq(kTmp, Immediate(kTypeCid));
+  __ LoadClassId(TypeTestABI::kScratchReg, TypeTestABI::kDstTypeReg);
+  __ cmpq(TypeTestABI::kScratchReg, Immediate(kTypeCid));
   __ BranchIf(NOT_EQUAL, &is_complex_case);
 
   // Check whether this [Type] is instantiated/uninstantiated.
@@ -3098,9 +3112,10 @@
   __ BranchIf(NOT_EQUAL, &is_complex_case);
 
   // Check whether this [Type] is a function type.
-  __ movq(kTmp, FieldAddress(TypeTestABI::kDstTypeReg,
-                             target::Type::signature_offset()));
-  __ CompareObject(kTmp, NullObject());
+  __ movq(
+      TypeTestABI::kScratchReg,
+      FieldAddress(TypeTestABI::kDstTypeReg, target::Type::signature_offset()));
+  __ CompareObject(TypeTestABI::kScratchReg, NullObject());
   __ BranchIf(NOT_EQUAL, &is_complex_case);
 
   // This [Type] could be a FutureOr. Subtype2TestCache does not support Smi.
@@ -3111,7 +3126,8 @@
   __ Bind(&is_simple_case);
   {
     __ Call(StubCodeSubtype2TestCache());
-    __ CompareObject(R8, CastHandle<Object>(TrueObject()));
+    __ CompareObject(TypeTestABI::kSubtypeTestCacheResultReg,
+                     CastHandle<Object>(TrueObject()));
     __ BranchIf(EQUAL, &done);  // Cache said: yes.
     __ Jump(&call_runtime);
   }
@@ -3119,7 +3135,8 @@
   __ Bind(&is_complex_case);
   {
     __ Call(StubCodeSubtype6TestCache());
-    __ CompareObject(R8, CastHandle<Object>(TrueObject()));
+    __ CompareObject(TypeTestABI::kSubtypeTestCacheResultReg,
+                     CastHandle<Object>(TrueObject()));
     __ BranchIf(EQUAL, &done);  // Cache said: yes.
     // Fall through to runtime_call
   }
diff --git a/runtime/vm/constants_arm.h b/runtime/vm/constants_arm.h
index 016d438..23b16b3 100644
--- a/runtime/vm/constants_arm.h
+++ b/runtime/vm/constants_arm.h
@@ -333,13 +333,16 @@
   static const Register kSubtypeTestCacheReg = R3;
   static const Register kScratchReg = R4;
 
+  // For calls to InstanceOfStub.
+  static const Register kInstanceOfResultReg = kInstanceReg;
+  // For calls to SubtypeNTestCacheStub. Must be saved by the caller if the
+  // original value is needed after the call.
+  static const Register kSubtypeTestCacheResultReg = kSubtypeTestCacheReg;
+
   static const intptr_t kAbiRegisters =
       (1 << kInstanceReg) | (1 << kDstTypeReg) |
       (1 << kInstantiatorTypeArgumentsReg) | (1 << kFunctionTypeArgumentsReg) |
       (1 << kSubtypeTestCacheReg) | (1 << kScratchReg);
-
-  // For call to InstanceOfStub.
-  static const Register kResultReg = R0;
 };
 
 // Calling convention when calling AssertSubtypeStub.
diff --git a/runtime/vm/constants_arm64.h b/runtime/vm/constants_arm64.h
index 39225df..21319ca 100644
--- a/runtime/vm/constants_arm64.h
+++ b/runtime/vm/constants_arm64.h
@@ -165,13 +165,17 @@
   static const Register kSubtypeTestCacheReg = R3;
   static const Register kScratchReg = R4;
 
+  // For calls to InstanceOfStub.
+  static const Register kInstanceOfResultReg = kInstanceReg;
+  // For calls to SubtypeNTestCacheStub. Must not overlap with any other
+  // registers above, for it is also used internally as kNullReg in those stubs.
+  static const Register kSubtypeTestCacheResultReg = R7;
+
   static const intptr_t kAbiRegisters =
       (1 << kInstanceReg) | (1 << kDstTypeReg) |
       (1 << kInstantiatorTypeArgumentsReg) | (1 << kFunctionTypeArgumentsReg) |
-      (1 << kSubtypeTestCacheReg) | (1 << kScratchReg);
-
-  // For call to InstanceOfStub.
-  static const Register kResultReg = R0;
+      (1 << kSubtypeTestCacheReg) | (1 << kScratchReg) |
+      (1 << kSubtypeTestCacheResultReg);
 };
 
 // Calling convention when calling AssertSubtypeStub.
diff --git a/runtime/vm/constants_ia32.h b/runtime/vm/constants_ia32.h
index a8d5b46..94db1e2 100644
--- a/runtime/vm/constants_ia32.h
+++ b/runtime/vm/constants_ia32.h
@@ -118,7 +118,10 @@
       EDI;  // On ia32 we don't use CODE_REG.
 
   // For call to InstanceOfStub.
-  static const Register kResultReg = kNoRegister;
+  static const Register kInstanceOfResultReg = kNoRegister;
+  // For call to SubtypeNTestCacheStub.
+  static const Register kSubtypeTestCacheResultReg =
+      TypeTestABI::kSubtypeTestCacheReg;
 };
 
 // Calling convention when calling kSubtypeCheckRuntimeEntry, to match other
diff --git a/runtime/vm/constants_x64.h b/runtime/vm/constants_x64.h
index c1fb6c3..ae421fd 100644
--- a/runtime/vm/constants_x64.h
+++ b/runtime/vm/constants_x64.h
@@ -155,13 +155,17 @@
   static const Register kSubtypeTestCacheReg = R9;
   static const Register kScratchReg = RSI;
 
+  // For calls to InstanceOfStub.
+  static const Register kInstanceOfResultReg = kInstanceReg;
+  // For calls to SubtypeNTestCacheStub. Must not overlap with any other
+  // registers above, for it is also used internally as kNullReg in those stubs.
+  static const Register kSubtypeTestCacheResultReg = R8;
+
   static const intptr_t kAbiRegisters =
       (1 << kInstanceReg) | (1 << kDstTypeReg) |
       (1 << kInstantiatorTypeArgumentsReg) | (1 << kFunctionTypeArgumentsReg) |
-      (1 << kSubtypeTestCacheReg) | (1 << kScratchReg);
-
-  // For call to InstanceOfStub.
-  static const Register kResultReg = RAX;
+      (1 << kSubtypeTestCacheReg) | (1 << kScratchReg) |
+      (1 << kSubtypeTestCacheResultReg);
 };
 
 // Calling convention when calling AssertSubtypeStub.
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 4776fa5..7c1e68c 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -1590,6 +1590,9 @@
   {
     StackZone zone(T);
     HandleScope handle_scope(T);
+#if defined(DEBUG)
+    I->ValidateConstants();
+#endif
     Dart::RunShutdownCallback();
   }
   Dart::ShutdownIsolate();
diff --git a/runtime/vm/flag_list.h b/runtime/vm/flag_list.h
index 5e02239..341b4fc 100644
--- a/runtime/vm/flag_list.h
+++ b/runtime/vm/flag_list.h
@@ -167,8 +167,6 @@
   P(print_snapshot_sizes, bool, false, "Print sizes of generated snapshots.")  \
   P(print_snapshot_sizes_verbose, bool, false,                                 \
     "Print cluster sizes of generated snapshots.")                             \
-  P(print_benchmarking_metrics, bool, false,                                   \
-    "Print additional memory and latency metrics for benchmarking.")           \
   R(print_ssa_liveranges, false, bool, false,                                  \
     "Print live ranges after allocation.")                                     \
   R(print_stacktrace_at_api_error, false, bool, false,                         \
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index ca08cac..914eaa9 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -2068,6 +2068,11 @@
 #endif  // !PRODUCT
   IsolateSpawnState* state = spawn_state();
   if (state != nullptr) {
+    // If the embedder does not make the isolate runnable during the
+    // `create_isolate_group`/`initialize_isolate` embedder callbacks but rather
+    // some time in the future, we'll hit this case.
+    // WARNING: This is currently untested - we might consider changing our APIs
+    // to disallow two different flows.
     ASSERT(this == state->isolate());
     Run();
   }
@@ -2086,15 +2091,6 @@
     Service::HandleEvent(&runnableEvent);
   }
   GetRunnableLatencyMetric()->set_value(UptimeMicros());
-  if (FLAG_print_benchmarking_metrics) {
-    {
-      StartIsolateScope scope(this);
-      heap()->CollectAllGarbage();
-    }
-    int64_t heap_size = (heap()->UsedInWords(Heap::kNew) * kWordSize) +
-                        (heap()->UsedInWords(Heap::kOld) * kWordSize);
-    GetRunnableHeapSizeMetric()->set_value(heap_size);
-  }
 #endif  // !PRODUCT
   return nullptr;
 }
@@ -2404,27 +2400,8 @@
 }
 
 static void ShutdownIsolate(uword parameter) {
-  Isolate* isolate = reinterpret_cast<Isolate*>(parameter);
-  {
-    // Print the error if there is one.  This may execute dart code to
-    // print the exception object, so we need to use a StartIsolateScope.
-    StartIsolateScope start_scope(isolate);
-    Thread* thread = Thread::Current();
-    ASSERT(thread->isolate() == isolate);
-
-    // We must wait for any outstanding spawn calls to complete before
-    // running the shutdown callback.
-    isolate->WaitForOutstandingSpawns();
-
-    StackZone zone(thread);
-    HandleScope handle_scope(thread);
-#if defined(DEBUG)
-    isolate->ValidateConstants();
-#endif  // defined(DEBUG)
-    Dart::RunShutdownCallback();
-  }
-  // Shut the isolate down.
-  Dart::ShutdownIsolate(isolate);
+  Dart_EnterIsolate(reinterpret_cast<Dart_Isolate>(parameter));
+  Dart_ShutdownIsolate();
 }
 
 void Isolate::SetStickyError(ErrorPtr sticky_error) {
@@ -2548,7 +2525,7 @@
         "\tisolate:    %s\n",
         name());
   }
-  if (FLAG_print_metrics || FLAG_print_benchmarking_metrics) {
+  if (FLAG_print_metrics) {
     LogBlock lb;
     OS::PrintErr("Printing metrics for %s\n", name());
 #define ISOLATE_GROUP_METRIC_PRINT(type, variable, name, unit)                 \
diff --git a/runtime/vm/metrics.cc b/runtime/vm/metrics.cc
index ecfda6c..f7e582b 100644
--- a/runtime/vm/metrics.cc
+++ b/runtime/vm/metrics.cc
@@ -206,7 +206,7 @@
 }
 
 void Metric::Cleanup() {
-  if (FLAG_print_metrics || FLAG_print_benchmarking_metrics) {
+  if (FLAG_print_metrics) {
     // Create a zone to allocate temporary strings in.
     StackZone sz(Thread::Current());
     OS::PrintErr("Printing metrics for VM\n");
diff --git a/sdk/lib/_internal/js_runtime/lib/js_array.dart b/sdk/lib/_internal/js_runtime/lib/js_array.dart
index e3a6b03..93bd80c 100644
--- a/sdk/lib/_internal/js_runtime/lib/js_array.dart
+++ b/sdk/lib/_internal/js_runtime/lib/js_array.dart
@@ -26,7 +26,9 @@
     return new JSArray<E>.fixed(length);
   }
 
-  /// Returns a fresh JavaScript Array, marked as fixed-length.
+  /// Returns a fresh JavaScript Array, marked as fixed-length. The holes in the
+  /// array yield `undefined`, making the Dart List appear to be filled with
+  /// `null` values.
   ///
   /// [length] must be a non-negative integer.
   factory JSArray.fixed(int length) {
@@ -36,8 +38,34 @@
     if (length is! int) {
       throw new ArgumentError.value(length, 'length', 'is not an integer');
     }
-    // The JavaScript Array constructor with one argument throws if
-    // the value is not a UInt32. Give a better error message.
+    // The JavaScript Array constructor with one argument throws if the value is
+    // not a UInt32 but the error message does not contain the bad value. Give a
+    // better error message.
+    int maxJSArrayLength = 0xFFFFFFFF;
+    if (length < 0 || length > maxJSArrayLength) {
+      throw new RangeError.range(length, 0, maxJSArrayLength, 'length');
+    }
+    return new JSArray<E>.markFixed(JS('', 'new Array(#)', length));
+  }
+
+  /// Returns a fresh JavaScript Array, marked as fixed-length.  The Array is
+  /// allocated but no elements are assigned.
+  ///
+  /// All elements of the array must be assigned before the array is valid. This
+  /// is essentially the same as `JSArray.fixed` except that global type
+  /// inference starts with bottom for the element type.
+  ///
+  /// [length] must be a non-negative integer.
+  factory JSArray.allocateFixed(int length) {
+    // Explicit type test is necessary to guard against JavaScript conversions
+    // in unchecked mode, and against `new Array(null)` which creates a single
+    // element Array containing `null`.
+    if (length is! int) {
+      throw new ArgumentError.value(length, 'length', 'is not an integer');
+    }
+    // The JavaScript Array constructor with one argument throws if the value is
+    // not a UInt32 but the error message does not contain the bad value. Give a
+    // better error message.
     int maxJSArrayLength = 0xFFFFFFFF;
     if (length < 0 || length > maxJSArrayLength) {
       throw new RangeError.range(length, 0, maxJSArrayLength, 'length');
@@ -48,9 +76,11 @@
   /// Returns a fresh growable JavaScript Array of zero length length.
   factory JSArray.emptyGrowable() => new JSArray<E>.markGrowable(JS('', '[]'));
 
-  /// Returns a fresh growable JavaScript Array with initial length.
+  /// Returns a fresh growable JavaScript Array with initial length. The holes
+  /// in the array yield `undefined`, making the Dart List appear to be filled
+  /// with `null` values.
   ///
-  /// [validatedLength] must be a non-negative integer.
+  /// [length] must be a non-negative integer.
   factory JSArray.growable(int length) {
     // Explicit type test is necessary to guard against JavaScript conversions
     // in unchecked mode.
@@ -60,6 +90,23 @@
     return new JSArray<E>.markGrowable(JS('', 'new Array(#)', length));
   }
 
+  /// Returns a fresh growable JavaScript Array with initial length. The Array
+  /// is allocated but no elements are assigned.
+  ///
+  /// All elements of the array must be assigned before the array is valid. This
+  /// is essentially the same as `JSArray.growable` except that global type
+  /// inference starts with bottom for the element type.
+  ///
+  /// [length] must be a non-negative integer.
+  factory JSArray.allocateGrowable(int length) {
+    // Explicit type test is necessary to guard against JavaScript conversions
+    // in unchecked mode.
+    if ((length is! int) || (length < 0)) {
+      throw new ArgumentError('Length must be a non-negative integer: $length');
+    }
+    return new JSArray<E>.markGrowable(JS('', 'new Array(#)', length));
+  }
+
   /// Constructor for adding type parameters to an existing JavaScript Array.
   /// The compiler specially recognizes this constructor.
   ///
diff --git a/sdk/lib/_internal/js_runtime/lib/regexp_helper.dart b/sdk/lib/_internal/js_runtime/lib/regexp_helper.dart
index 16ab9b0..c6adb12 100644
--- a/sdk/lib/_internal/js_runtime/lib/regexp_helper.dart
+++ b/sdk/lib/_internal/js_runtime/lib/regexp_helper.dart
@@ -209,9 +209,9 @@
   }
 
   String? namedGroup(String name) {
-    var groups = JS('Object|Null', '#.groups', _match);
+    var groups = JS('=Object|Null', '#.groups', _match);
     if (groups != null) {
-      var result = JS('String|Null', '#[#]', groups, name);
+      String? result = JS('String|Null', '#[#]', groups, name);
       if (result != null || JS('bool', '# in #', name, groups)) {
         return result;
       }
@@ -220,7 +220,7 @@
   }
 
   Iterable<String> get groupNames {
-    var groups = JS('Object|Null', '#.groups', _match);
+    var groups = JS('=Object|Null', '#.groups', _match);
     if (groups != null) {
       var keys = new JSArray<String>.markGrowable(
           JS('returns:JSExtendableArray;new:true', 'Object.keys(#)', groups));
diff --git a/sdk/lib/async/broadcast_stream_controller.dart b/sdk/lib/async/broadcast_stream_controller.dart
index c362eea..4db2a34 100644
--- a/sdk/lib/async/broadcast_stream_controller.dart
+++ b/sdk/lib/async/broadcast_stream_controller.dart
@@ -251,8 +251,7 @@
   }
 
   void addError(Object error, [StackTrace? stackTrace]) {
-    // TODO(40614): Remove once non-nullability is sound.
-    ArgumentError.checkNotNull(error, "error");
+    checkNotNullable(error, "error");
     if (!_mayAddEvent) throw _addEventError();
     AsyncError? replacement = Zone.current.errorCallback(error, stackTrace);
     if (replacement != null) {
@@ -486,8 +485,7 @@
   }
 
   void addError(Object error, [StackTrace? stackTrace]) {
-    // TODO(40614): Remove once non-nullability is sound.
-    ArgumentError.checkNotNull(error, "error");
+    checkNotNullable(error, "error");
     stackTrace ??= AsyncError.defaultStackTrace(error);
     if (!isClosed && _isFiring) {
       _addPendingEvent(new _DelayedError(error, stackTrace));
diff --git a/sdk/lib/async/future.dart b/sdk/lib/async/future.dart
index 0e1d703..0e92b2e 100644
--- a/sdk/lib/async/future.dart
+++ b/sdk/lib/async/future.dart
@@ -275,7 +275,7 @@
    */
   factory Future.error(Object error, [StackTrace? stackTrace]) {
     // TODO(40614): Remove once non-nullability is sound.
-    ArgumentError.checkNotNull(error, "error");
+    checkNotNullable(error, "error");
     if (!identical(Zone.current, _rootZone)) {
       AsyncError? replacement = Zone.current.errorCallback(error, stackTrace);
       if (replacement != null) {
diff --git a/sdk/lib/async/future_impl.dart b/sdk/lib/async/future_impl.dart
index 4e24a48..0d38c61 100644
--- a/sdk/lib/async/future_impl.dart
+++ b/sdk/lib/async/future_impl.dart
@@ -18,7 +18,7 @@
 
   void completeError(Object error, [StackTrace? stackTrace]) {
     // TODO(40614): Remove once non-nullability is sound.
-    ArgumentError.checkNotNull(error, "error");
+    checkNotNullable(error, "error");
     if (!future._mayComplete) throw new StateError("Future already completed");
     AsyncError? replacement = Zone.current.errorCallback(error, stackTrace);
     if (replacement != null) {
diff --git a/sdk/lib/async/stream.dart b/sdk/lib/async/stream.dart
index 150a759..60f4aa1 100644
--- a/sdk/lib/async/stream.dart
+++ b/sdk/lib/async/stream.dart
@@ -152,7 +152,7 @@
   @Since("2.5")
   factory Stream.error(Object error, [StackTrace? stackTrace]) {
     // TODO(40614): Remove once non-nullability is sound.
-    ArgumentError.checkNotNull(error, "error");
+    checkNotNullable(error, "error");
     return (_AsyncStreamController<T>(null, null, null, null)
           .._addError(error, stackTrace ?? AsyncError.defaultStackTrace(error))
           .._closeUnchecked())
@@ -2212,20 +2212,22 @@
 }
 
 /**
- * An [Iterator] like interface for the values of a [Stream].
+ * An [Iterator]-like interface for the values of a [Stream].
  *
  * This wraps a [Stream] and a subscription on the stream. It listens
  * on the stream, and completes the future returned by [moveNext] when the
  * next value becomes available.
  *
  * The stream may be paused between calls to [moveNext].
+ *
+ * The [current] value must only be used after a future returned by [moveNext]
+ * has completed with `true`, and only until [moveNext] is called again.
  */
 abstract class StreamIterator<T> {
   /** Create a [StreamIterator] on [stream]. */
-  factory StreamIterator(Stream<T> stream)
+  factory StreamIterator(Stream<T> stream) =>
       // TODO(lrn): use redirecting factory constructor when type
       // arguments are supported.
-      =>
       new _StreamIterator<T>(stream);
 
   /**
@@ -2247,14 +2249,16 @@
   /**
    * The current value of the stream.
    *
-   * Is `null` before the first call to [moveNext] and after a call to
-   * `moveNext` completes with a `false` result or an error.
-   *
-   * When a `moveNext` call completes with `true`, the `current` field holds
+   * When a [moveNext] call completes with `true`, the [current] field holds
    * the most recent event of the stream, and it stays like that until the next
-   * call to `moveNext`.
-   * Between a call to `moveNext` and when its returned future completes,
-   * the value is unspecified.
+   * call to [moveNext]. This value must only be read after a call to [moveNext]
+   * has completed with `true`, and only until the [moveNext] is called again.
+   *
+   * If the StreamIterator has not yet been moved to the first element
+   * ([moveNext] has not been called and completed yet), or if the
+   * StreamIterator has been moved past the last element ([moveNext] has
+   * returned `false`), then [current] is unspecified. A [StreamIterator] may
+   * either throw or return an iterator-specific default value in that case.
    */
   T get current;
 
diff --git a/sdk/lib/async/stream_controller.dart b/sdk/lib/async/stream_controller.dart
index 4c3e4f9..dbaacc1 100644
--- a/sdk/lib/async/stream_controller.dart
+++ b/sdk/lib/async/stream_controller.dart
@@ -628,8 +628,7 @@
    * Send or enqueue an error event.
    */
   void addError(Object error, [StackTrace? stackTrace]) {
-    // TODO(40614): Remove once non-nullability is sound. Use checkNotNullable.
-    ArgumentError.checkNotNull(error, "error");
+    checkNotNullable(error, "error");
     if (!_mayAddEvent) throw _badEventState();
     AsyncError? replacement = Zone.current.errorCallback(error, stackTrace);
     if (replacement != null) {
diff --git a/sdk/lib/async/stream_impl.dart b/sdk/lib/async/stream_impl.dart
index 6ec5d03..bb4a1ba 100644
--- a/sdk/lib/async/stream_impl.dart
+++ b/sdk/lib/async/stream_impl.dart
@@ -1009,7 +1009,7 @@
   bool _hasValue = false;
 
   _StreamIterator(final Stream<T> stream)
-      : _stateData = ArgumentError.checkNotNull(stream, "stream");
+      : _stateData = checkNotNullable(stream, "stream");
 
   T get current {
     if (_hasValue) return _stateData as dynamic;
diff --git a/sdk/lib/async/stream_transformers.dart b/sdk/lib/async/stream_transformers.dart
index ca7934c..aa99f5f 100644
--- a/sdk/lib/async/stream_transformers.dart
+++ b/sdk/lib/async/stream_transformers.dart
@@ -229,8 +229,7 @@
   }
 
   void addError(Object error, [StackTrace? stackTrace]) {
-    // TODO(40614): Remove once non-nullability is sound.
-    ArgumentError.checkNotNull(error, "error");
+    checkNotNullable(error, "error");
     var sink = _sink;
     if (sink == null) {
       throw StateError("Sink is closed");
diff --git a/sdk/lib/async/zone.dart b/sdk/lib/async/zone.dart
index b4e3994..f662d53 100644
--- a/sdk/lib/async/zone.dart
+++ b/sdk/lib/async/zone.dart
@@ -36,16 +36,14 @@
 typedef Zone ForkHandler(Zone self, ZoneDelegate parent, Zone zone,
     ZoneSpecification? specification, Map<Object?, Object?>? zoneValues);
 
-/** Pair of error and stack trace. Returned by [Zone.errorCallback]. */
+/// Pair of error and stack trace. Returned by [Zone.errorCallback].
 class AsyncError implements Error {
   final Object error;
   final StackTrace stackTrace;
 
-  AsyncError(this.error, StackTrace? stackTrace)
-      : stackTrace = stackTrace ?? defaultStackTrace(error) {
-    // TODO(40614): Remove once non-nullability is sound.
-    ArgumentError.checkNotNull(error, "error");
-  }
+  AsyncError(Object error, StackTrace? stackTrace)
+      : error = checkNotNullable(error, "error"),
+        stackTrace = stackTrace ?? defaultStackTrace(error);
 
   /// A default stack trace for an error.
   ///
@@ -795,8 +793,7 @@
   }
 
   AsyncError? errorCallback(Zone zone, Object error, StackTrace? stackTrace) {
-    // TODO(40614): Remove once non-nullability is sound.
-    ArgumentError.checkNotNull(error, "error");
+    checkNotNullable(error, "error");
     var implementation = _delegationTarget._errorCallback;
     _Zone implZone = implementation.zone;
     if (identical(implZone, _rootZone)) return null;
@@ -1130,8 +1127,7 @@
   }
 
   AsyncError? errorCallback(Object error, StackTrace? stackTrace) {
-    // TODO(40614): Remove once non-nullability is sound.
-    ArgumentError.checkNotNull(error, "error");
+    checkNotNullable(error, "error");
     var implementation = this._errorCallback;
     final _Zone implementationZone = implementation.zone;
     if (identical(implementationZone, _rootZone)) return null;
@@ -1532,7 +1528,7 @@
     {Map<Object?, Object?>? zoneValues,
     ZoneSpecification? zoneSpecification,
     @Deprecated("Use runZonedGuarded instead") Function? onError}) {
-  ArgumentError.checkNotNull(body, "body");
+  checkNotNullable(body, "body");
   if (onError != null) {
     // TODO: Remove this when code have been migrated off using [onError].
     if (onError is! void Function(Object, StackTrace)) {
@@ -1592,8 +1588,8 @@
 @Since("2.8")
 R? runZonedGuarded<R>(R body(), void onError(Object error, StackTrace stack),
     {Map<Object?, Object?>? zoneValues, ZoneSpecification? zoneSpecification}) {
-  ArgumentError.checkNotNull(body, "body");
-  ArgumentError.checkNotNull(onError, "onError");
+  checkNotNullable(body, "body");
+  checkNotNullable(onError, "onError");
   _Zone parentZone = Zone._current;
   HandleUncaughtErrorHandler errorHandler = (Zone self, ZoneDelegate parent,
       Zone zone, Object error, StackTrace stackTrace) {
diff --git a/sdk/lib/collection/iterable.dart b/sdk/lib/collection/iterable.dart
index 17f84a2..9e697a5 100644
--- a/sdk/lib/collection/iterable.dart
+++ b/sdk/lib/collection/iterable.dart
@@ -197,7 +197,7 @@
   }
 
   E elementAt(int index) {
-    ArgumentError.checkNotNull(index, "index");
+    checkNotNullable(index, "index");
     RangeError.checkNotNegative(index, "index");
     int elementIndex = 0;
     for (E element in this) {
diff --git a/sdk/lib/collection/list.dart b/sdk/lib/collection/list.dart
index 045bbd5..327a7ea 100644
--- a/sdk/lib/collection/list.dart
+++ b/sdk/lib/collection/list.dart
@@ -523,7 +523,7 @@
   }
 
   void insert(int index, E element) {
-    ArgumentError.checkNotNull(index, "index");
+    checkNotNullable(index, "index");
     var length = this.length;
     RangeError.checkValueInInterval(index, 0, length, "index");
     add(element);
diff --git a/sdk/lib/collection/set.dart b/sdk/lib/collection/set.dart
index 0a6ccbf..5cd75d6 100644
--- a/sdk/lib/collection/set.dart
+++ b/sdk/lib/collection/set.dart
@@ -268,7 +268,7 @@
   }
 
   E elementAt(int index) {
-    ArgumentError.checkNotNull(index, "index");
+    checkNotNullable(index, "index");
     RangeError.checkNotNegative(index, "index");
     int elementIndex = 0;
     for (E element in this) {
diff --git a/sdk/lib/convert/chunked_conversion.dart b/sdk/lib/convert/chunked_conversion.dart
index d2c24f0..19a8561 100644
--- a/sdk/lib/convert/chunked_conversion.dart
+++ b/sdk/lib/convert/chunked_conversion.dart
@@ -73,8 +73,7 @@
   }
 
   void addError(Object error, [StackTrace? stackTrace]) {
-    // TODO(40614): Remove once non-nullability is sound.
-    ArgumentError.checkNotNull(error, "error");
+    checkNotNullable(error, "error");
     _eventSink.addError(error, stackTrace);
   }
 
diff --git a/sdk/lib/convert/convert.dart b/sdk/lib/convert/convert.dart
index 6173782..1ea3d26 100644
--- a/sdk/lib/convert/convert.dart
+++ b/sdk/lib/convert/convert.dart
@@ -55,7 +55,7 @@
 
 import 'dart:async';
 import 'dart:typed_data';
-import 'dart:_internal' show CastConverter, parseHexByte;
+import 'dart:_internal' show CastConverter, checkNotNullable, parseHexByte;
 
 part 'ascii.dart';
 part 'base64.dart';
diff --git a/sdk/lib/core/date_time.dart b/sdk/lib/core/date_time.dart
index aa85fcc..e8c9dcb 100644
--- a/sdk/lib/core/date_time.dart
+++ b/sdk/lib/core/date_time.dart
@@ -394,7 +394,7 @@
           "DateTime is outside valid range: $millisecondsSinceEpoch");
     }
     // For backwards compatibility with legacy mode.
-    ArgumentError.checkNotNull(isUtc, "isUtc");
+    checkNotNullable(isUtc, "isUtc");
   }
 
   /**
diff --git a/sdk/lib/core/iterable.dart b/sdk/lib/core/iterable.dart
index 839792e..7482058 100644
--- a/sdk/lib/core/iterable.dart
+++ b/sdk/lib/core/iterable.dart
@@ -170,7 +170,7 @@
   Iterable<R> cast<R>() => Iterable.castFrom<E, R>(this);
 
   /**
-   * Returns the lazy concatentation of this iterable and [other].
+   * Returns the lazy concatenation of this iterable and [other].
    *
    * The returned iterable will provide the same elements as this iterable,
    * and, after that, the elements of [other], in the same order as in the
diff --git a/tests/co19/co19-co19.status b/tests/co19/co19-co19.status
index 8970fe8..32c17f0 100644
--- a/tests/co19/co19-co19.status
+++ b/tests/co19/co19-co19.status
@@ -43,6 +43,12 @@
 LanguageFeatures/Subtyping/static/tests/left_bottom_A01_t02: Skip # Type aliases are not supported by analyzer
 LanguageFeatures/Subtyping/static/tests/left_bottom_A01_t04: Skip # Type aliases are not supported by analyzer
 LanguageFeatures/Subtyping/static/tests/left_bottom_A01_t06: Skip # Type aliases are not supported by analyzer
+LanguageFeatures/nnbd/Least-greatest-closure/nonfunction_typedef/nonfunction_dynamic_t01: Skip # Type aliases are not fully supported by analyzer yet
+LanguageFeatures/nnbd/Least-greatest-closure/nonfunction_typedef/nonfunction_dynamic_t02: Skip # Type aliases are not fully supported by analyzer yet
+LanguageFeatures/nnbd/Least-greatest-closure/nonfunction_typedef/nonfunction_dynamic_t03: Skip # Type aliases are not fully supported by analyzer yet
+LanguageFeatures/nnbd/Least-greatest-closure/nonfunction_typedef/nonfunction_static_t01: Skip # Type aliases are not fully supported by analyzer yet
+LanguageFeatures/nnbd/Least-greatest-closure/nonfunction_typedef/nonfunction_static_t02: Skip # Type aliases are not fully supported by analyzer yet
+LanguageFeatures/nnbd/Least-greatest-closure/nonfunction_typedef/nonfunction_static_t03: Skip # Type aliases are not fully supported by analyzer yet
 
 [ $compiler == fasta ]
 Language/Classes/Superclasses/Inheritance_and_Overriding/inheritance_t10: Skip # Type aliases are not fully implemented
diff --git a/tests/co19/co19-dart2js.status b/tests/co19/co19-dart2js.status
index 6bb5259..1fe42fe 100644
--- a/tests/co19/co19-dart2js.status
+++ b/tests/co19/co19-dart2js.status
@@ -21,6 +21,23 @@
 Language/Libraries_and_Scripts/Scripts/main_optional_parameters_t03: SkipByDesign # https://github.com/dart-lang/co19/issues/952
 Language/Metadata/before*: Skip # dart:mirrors not supported https://github.com/dart-lang/co19/issues/523.
 Language/Reference/Operator_Precedence/precedence_15_unary_prefix_t08: SkipByDesign # binary '~' produces different results in JavaScript and Dart
+LanguageFeatures/Abstract-external-fields/static_analysis_external_A01_t01: SkipByDesign # External variables are not supported by dart2js
+LanguageFeatures/Abstract-external-fields/static_analysis_external_A01_t02: SkipByDesign # External variables are not supported by dart2js
+LanguageFeatures/Abstract-external-fields/static_analysis_external_A01_t03: SkipByDesign # External variables are not supported by dart2js
+LanguageFeatures/Abstract-external-fields/static_analysis_external_A02_t01: SkipByDesign # External variables are not supported by dart2js
+LanguageFeatures/Abstract-external-fields/static_analysis_external_A02_t04: SkipByDesign # External variables are not supported by dart2js
+LanguageFeatures/Abstract-external-fields/static_analysis_external_A03_t01: SkipByDesign # External variables are not supported by dart2js
+LanguageFeatures/Abstract-external-fields/static_analysis_external_A03_t02: SkipByDesign # External variables are not supported by dart2js
+LanguageFeatures/Abstract-external-fields/static_analysis_external_A03_t03: SkipByDesign # External variables are not supported by dart2js
+LanguageFeatures/Abstract-external-fields/static_analysis_external_A03_t04: SkipByDesign # External variables are not supported by dart2js
+LanguageFeatures/Abstract-external-fields/static_analysis_external_A03_t06: SkipByDesign # External variables are not supported by dart2js
+LanguageFeatures/Abstract-external-fields/static_analysis_external_A04_t01: SkipByDesign # External variables are not supported by dart2js
+LanguageFeatures/Abstract-external-fields/static_analysis_external_A04_t02: SkipByDesign # External variables are not supported by dart2js
+LanguageFeatures/Abstract-external-fields/static_analysis_external_A05_t01: SkipByDesign # External variables are not supported by dart2js
+LanguageFeatures/Abstract-external-fields/static_analysis_external_A05_t02: SkipByDesign # External variables are not supported by dart2js
+LanguageFeatures/Abstract-external-fields/static_analysis_external_A05_t03: SkipByDesign # External variables are not supported by dart2js
+LanguageFeatures/Abstract-external-fields/syntax_A01_t03: SkipByDesign # External variables are not supported by dart2js
+LanguageFeatures/Abstract-external-fields/syntax_A02_t03: SkipByDesign # External variables are not supported by dart2js
 LibTest/core/DateTime/DateTime.fromMicrosecondsSinceEpoch_A01_t01: SkipByDesign # microseconds are not supported in JavaScript
 LibTest/core/DateTime/microsecond_A01_t01: SkipByDesign # microseconds are not supported in JavaScript
 LibTest/core/DateTime/microsecondsSinceEpoch_A01_t01: SkipByDesign # microseconds are not supported in JavaScript
diff --git a/tests/dart2js/list_generate_1_test.dart b/tests/dart2js/list_generate_1_test.dart
new file mode 100644
index 0000000..1b2fb4d
--- /dev/null
+++ b/tests/dart2js/list_generate_1_test.dart
@@ -0,0 +1,69 @@
+// 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.
+
+import 'package:expect/expect.dart';
+
+dynamic function = 'function';
+dynamic growable = 'growable';
+dynamic length = 'length';
+
+// Use top level dynamic variables so this call is not lowered.  This function
+// and the callers (test1, test2) are not inlined to prevent store-forwarding of
+// the top-level variables.
+@pragma('dart2js:noInline')
+List<T> general<T>() => List<T>.generate(length, function, growable: growable);
+
+void main() {
+  function = (int i) => i;
+  growable = true;
+  length = 5;
+
+  test1();
+
+  int k = 0;
+  function = (int u) => seq3(u += 10, k += 100, () => u += k + 100000);
+  growable = false;
+  length = 5;
+
+  test2();
+}
+
+@pragma('dart2js:noInline')
+void test1() {
+  // Simple test.
+  final r1 = List<num>.generate(5, (i) => i, growable: true);
+  final r2 = general<num>();
+
+  Expect.equals('[0, 1, 2, 3, 4]', '$r1');
+  Expect.equals('[0, 1, 2, 3, 4]', '$r2');
+}
+
+// A sequence of two operations in expression form, returning the last value.
+T seq2<T>(dynamic a, T b) => b;
+T seq3<T>(dynamic a, dynamic b, T c) => c;
+
+@pragma('dart2js:noInline')
+void test2() {
+  // Test with a complex environment.
+  int c = 0;
+  final r1 = List<int Function()>.generate(
+    5,
+    (i) => seq3(i += 10, c += 100, () => i += c + 100000),
+    growable: false,
+  );
+  final r2 = general<int Function()>();
+
+  final e12 = r1[2];
+  final e22 = r2[2];
+
+  final s123 = [e12(), e12(), e12()];
+  final s223 = [e22(), e22(), e22()];
+
+  // 'i' is bound to the loop variable (2 for element at [2]).
+  // 'i' is incremented by 10, so the low digits are '12'
+  // 'c' is shared by all closures, and has been incremented to 500 at end of
+  // construction, so each call increments 'i' by 100500.
+  Expect.equals('[100512, 201012, 301512]', '$s123');
+  Expect.equals('[100512, 201012, 301512]', '$s123');
+}
diff --git a/tests/dart2js/list_generate_2_test.dart b/tests/dart2js/list_generate_2_test.dart
new file mode 100644
index 0000000..83487e5
--- /dev/null
+++ b/tests/dart2js/list_generate_2_test.dart
@@ -0,0 +1,21 @@
+// 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.
+
+import 'package:expect/expect.dart';
+
+void main() {
+  test(0, '[]');
+  test(1, '[[[1]]]');
+  test(2, '[[[1]], [[2], [3, 4]]]');
+  test(3, '[[[1]], [[2], [3, 4]], [[5], [6, 7], [8, 9, 10]]]');
+}
+
+void test(int i, String expected) {
+  // Many nested closures with shadowing variables.
+  int c = 0;
+  final r = List.generate(
+      i, (i) => List.generate(i + 1, (i) => List.generate(i + 1, (i) => ++c)));
+
+  Expect.equals(expected, '$r');
+}
diff --git a/tests/language/regress/regress44136_test.dart b/tests/language/regress/regress44136_test.dart
new file mode 100644
index 0000000..99dfa35
--- /dev/null
+++ b/tests/language/regress/regress44136_test.dart
@@ -0,0 +1,12 @@
+// 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.
+
+import "package:expect/expect.dart";
+
+typedef Foo = void Function<X extends dynamic>();
+typedef Bar = void Function<X extends Object?>();
+
+void main() {
+  Expect.notEquals(Foo, Bar);
+}
diff --git a/tests/language_2/regress/regress44136_test.dart b/tests/language_2/regress/regress44136_test.dart
new file mode 100644
index 0000000..8478fc5
--- /dev/null
+++ b/tests/language_2/regress/regress44136_test.dart
@@ -0,0 +1,12 @@
+// 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.
+
+import "package:expect/expect.dart";
+
+typedef Foo = void Function<X extends dynamic>();
+typedef Bar = void Function<X extends Object>();
+
+void main() {
+  Expect.notEquals(Foo, Bar);
+}
diff --git a/tests/lib/async/future_error_test.dart b/tests/lib/async/future_error_test.dart
index 760c392..47f0f15 100644
--- a/tests/lib/async/future_error_test.dart
+++ b/tests/lib/async/future_error_test.dart
@@ -8,7 +8,7 @@
 
 main() {
   // The error cannot be null.
-  Expect.throwsNullCheckError(() {
+  Expect.throwsTypeError(() {
     Future.error(null as dynamic);
   });
 }
diff --git a/tests/lib/async/stream_controller_add_error_test.dart b/tests/lib/async/stream_controller_add_error_test.dart
index f195fef..aea9294 100644
--- a/tests/lib/async/stream_controller_add_error_test.dart
+++ b/tests/lib/async/stream_controller_add_error_test.dart
@@ -9,41 +9,41 @@
 main() {
   // Single-cast async.
   var controller = StreamController();
-  Expect.throwsNullCheckError(() {
+  Expect.throwsTypeError(() {
     controller.addError(null as dynamic);
   });
 
-  Expect.throwsNullCheckError(() {
+  Expect.throwsTypeError(() {
     controller.sink.addError(null as dynamic);
   });
 
   // Single-cast sync.
   controller = StreamController(sync: true);
-  Expect.throwsNullCheckError(() {
+  Expect.throwsTypeError(() {
     controller.addError(null as dynamic);
   });
 
-  Expect.throwsNullCheckError(() {
+  Expect.throwsTypeError(() {
     controller.sink.addError(null as dynamic);
   });
 
   // Broadcast async.
   controller = StreamController.broadcast();
-  Expect.throwsNullCheckError(() {
+  Expect.throwsTypeError(() {
     controller.addError(null as dynamic);
   });
 
-  Expect.throwsNullCheckError(() {
+  Expect.throwsTypeError(() {
     controller.sink.addError(null as dynamic);
   });
 
   // Broadcast sync.
   controller = StreamController.broadcast(sync: true);
-  Expect.throwsNullCheckError(() {
+  Expect.throwsTypeError(() {
     controller.addError(null as dynamic);
   });
 
-  Expect.throwsNullCheckError(() {
+  Expect.throwsTypeError(() {
     controller.sink.addError(null as dynamic);
   });
 }
diff --git a/tests/lib/async/zone_async_error_test.dart b/tests/lib/async/zone_async_error_test.dart
index ca6c87c..1d3bf04 100644
--- a/tests/lib/async/zone_async_error_test.dart
+++ b/tests/lib/async/zone_async_error_test.dart
@@ -8,7 +8,7 @@
 
 main() {
   // The error cannot be null.
-  Expect.throwsNullCheckError(() {
+  Expect.throwsTypeError(() {
     AsyncError(null as dynamic, StackTrace.current);
   });
 }
diff --git a/tests/lib/js/extends_test/extends_subtyping_test.dart b/tests/lib/js/extends_test/extends_subtyping_test.dart
new file mode 100644
index 0000000..76dc51e
--- /dev/null
+++ b/tests/lib/js/extends_test/extends_subtyping_test.dart
@@ -0,0 +1,10 @@
+// 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.
+
+import 'extends_test_util.dart';
+
+void main() {
+  setUpWithoutES6Syntax();
+  testSubtyping();
+}
diff --git a/tests/lib/js/extends_test/extends_test.dart b/tests/lib/js/extends_test/extends_test.dart
index 12f8e3f..a9c8781 100644
--- a/tests/lib/js/extends_test/extends_test.dart
+++ b/tests/lib/js/extends_test/extends_test.dart
@@ -5,53 +5,6 @@
 import 'extends_test_util.dart';
 
 void main() {
-  // Use the old way to define inheritance between JS objects.
-  eval(r"""
-    function inherits(child, parent) {
-      if (child.prototype.__proto__) {
-        child.prototype.__proto__ = parent.prototype;
-      } else {
-        function tmp() {};
-        tmp.prototype = parent.prototype;
-        child.prototype = new tmp();
-        child.prototype.constructor = child;
-      }
-    }
-    function JSClass(a) {
-      this.a = a;
-      this.getA = function() {
-        return this.a;
-      }
-      this.getAOrB = function() {
-        return this.getA();
-      }
-    }
-    function JSExtendJSClass(a, b) {
-      JSClass.call(this, a);
-      this.b = b;
-      this.getB = function() {
-        return this.b;
-      }
-      this.getAOrB = function() {
-        return this.getB();
-      }
-    }
-    inherits(JSExtendJSClass, JSClass);
-    function JSExtendAnonymousClass(a, b) {
-      this.a = a;
-      this.b = b;
-      this.getA = function() {
-        return this.a;
-      }
-      this.getB = function() {
-        return this.b;
-      }
-      this.getAOrB = function() {
-        return this.getB();
-      }
-    }
-    self.anonExtendAnon = new JSExtendAnonymousClass(1, 2);
-    self.anonExtendJS = new JSExtendJSClass(1, 2);
-  """);
+  setUpWithoutES6Syntax();
   testInheritance();
 }
diff --git a/tests/lib/js/extends_test/extends_test_util.dart b/tests/lib/js/extends_test/extends_test_util.dart
index 5185176..e39e1b9 100644
--- a/tests/lib/js/extends_test/extends_test_util.dart
+++ b/tests/lib/js/extends_test/extends_test_util.dart
@@ -60,6 +60,108 @@
 external AnonymousExtendAnonymousClass get anonExtendAnon;
 external AnonymousExtendJSClass get anonExtendJS;
 
+void useJSClass(JSClass js) {}
+void useAnonymousClass(AnonymousClass a) {}
+
+void setUpWithoutES6Syntax() {
+  // Use the old way to define inheritance between JS objects.
+  eval(r"""
+    function inherits(child, parent) {
+      if (child.prototype.__proto__) {
+        child.prototype.__proto__ = parent.prototype;
+      } else {
+        function tmp() {};
+        tmp.prototype = parent.prototype;
+        child.prototype = new tmp();
+        child.prototype.constructor = child;
+      }
+    }
+    function JSClass(a) {
+      this.a = a;
+      this.getA = function() {
+        return this.a;
+      }
+      this.getAOrB = function() {
+        return this.getA();
+      }
+    }
+    function JSExtendJSClass(a, b) {
+      JSClass.call(this, a);
+      this.b = b;
+      this.getB = function() {
+        return this.b;
+      }
+      this.getAOrB = function() {
+        return this.getB();
+      }
+    }
+    inherits(JSExtendJSClass, JSClass);
+    function JSExtendAnonymousClass(a, b) {
+      this.a = a;
+      this.b = b;
+      this.getA = function() {
+        return this.a;
+      }
+      this.getB = function() {
+        return this.b;
+      }
+      this.getAOrB = function() {
+        return this.getB();
+      }
+    }
+    self.anonExtendAnon = new JSExtendAnonymousClass(1, 2);
+    self.anonExtendJS = new JSExtendJSClass(1, 2);
+  """);
+}
+
+void setUpWithES6Syntax() {
+  // Use the ES6 syntax for classes to make inheritance easier.
+  eval(r"""
+    class JSClass {
+      constructor(a) {
+        this.a = a;
+      }
+      getA() {
+        return this.a;
+      }
+      getAOrB() {
+        return this.getA();
+      }
+    }
+    class JSExtendJSClass extends JSClass {
+      constructor(a, b) {
+        super(a);
+        this.b = b;
+      }
+      getB() {
+        return this.b;
+      }
+      getAOrB() {
+        return this.getB();
+      }
+    }
+    self.JSExtendJSClass = JSExtendJSClass;
+    class JSExtendAnonymousClass {
+      constructor(a, b) {
+        this.a = a;
+        this.b = b;
+      }
+      getA() {
+        return this.a;
+      }
+      getB() {
+        return this.b;
+      }
+      getAOrB() {
+        return this.getB();
+      }
+    }
+    self.JSExtendAnonymousClass = JSExtendAnonymousClass;
+    self.anonExtendAnon = new JSExtendAnonymousClass(1, 2);
+    self.anonExtendJS = new JSExtendJSClass(1, 2);
+  """);
+}
+
 void testInheritance() {
   // Note that for the following, there are no meaningful tests for is checks or
   // as casts, since the web compilers should return true and succeed for all JS
@@ -92,3 +194,17 @@
   expect(anonExtendJS.getAOrB(), 2);
   expect((anonExtendJS as JSClass).getAOrB(), 2);
 }
+
+void testSubtyping() {
+  // Test subtyping for inheritance between JS and anonymous classes.
+  expect(useJSClass is void Function(JSExtendJSClass js), true);
+  expect(useAnonymousClass is void Function(AnonymousExtendAnonymousClass a),
+      true);
+  expect(useJSClass is void Function(AnonymousExtendJSClass a), true);
+  expect(useAnonymousClass is void Function(JSExtendAnonymousClass js), true);
+
+  expect(useJSClass is void Function(AnonymousExtendAnonymousClass a), false);
+  expect(useAnonymousClass is void Function(JSExtendJSClass js), false);
+  expect(useJSClass is void Function(JSExtendAnonymousClass js), false);
+  expect(useAnonymousClass is void Function(AnonymousExtendJSClass a), false);
+}
diff --git a/tests/lib/js/extends_test/extends_with_es6_subtyping_test.dart b/tests/lib/js/extends_test/extends_with_es6_subtyping_test.dart
new file mode 100644
index 0000000..e6185da
--- /dev/null
+++ b/tests/lib/js/extends_test/extends_with_es6_subtyping_test.dart
@@ -0,0 +1,10 @@
+// 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.
+
+import 'extends_test_util.dart';
+
+void main() {
+  setUpWithES6Syntax();
+  testSubtyping();
+}
diff --git a/tests/lib/js/extends_test/extends_with_es6_test.dart b/tests/lib/js/extends_test/extends_with_es6_test.dart
index 00eb41f..8daae16 100644
--- a/tests/lib/js/extends_test/extends_with_es6_test.dart
+++ b/tests/lib/js/extends_test/extends_with_es6_test.dart
@@ -5,50 +5,6 @@
 import 'extends_test_util.dart';
 
 void main() {
-  // Use the ES6 syntax for classes to make inheritance easier.
-  eval(r"""
-    class JSClass {
-      constructor(a) {
-        this.a = a;
-      }
-      getA() {
-        return this.a;
-      }
-      getAOrB() {
-        return this.getA();
-      }
-    }
-    class JSExtendJSClass extends JSClass {
-      constructor(a, b) {
-        super(a);
-        this.b = b;
-      }
-      getB() {
-        return this.b;
-      }
-      getAOrB() {
-        return this.getB();
-      }
-    }
-    self.JSExtendJSClass = JSExtendJSClass;
-    class JSExtendAnonymousClass {
-      constructor(a, b) {
-        this.a = a;
-        this.b = b;
-      }
-      getA() {
-        return this.a;
-      }
-      getB() {
-        return this.b;
-      }
-      getAOrB() {
-        return this.getB();
-      }
-    }
-    self.JSExtendAnonymousClass = JSExtendAnonymousClass;
-    self.anonExtendAnon = new JSExtendAnonymousClass(1, 2);
-    self.anonExtendJS = new JSExtendJSClass(1, 2);
-  """);
+  setUpWithES6Syntax();
   testInheritance();
 }
diff --git a/tests/lib/js/is_check_and_as_cast_test.dart b/tests/lib/js/is_check_and_as_cast_test.dart
index ef48137..d88255c 100644
--- a/tests/lib/js/is_check_and_as_cast_test.dart
+++ b/tests/lib/js/is_check_and_as_cast_test.dart
@@ -9,6 +9,7 @@
 library is_check_and_as_cast_test;
 
 import 'package:js/js.dart';
+import 'package:expect/expect.dart' show hasUnsoundNullSafety;
 import 'package:expect/minitest.dart';
 
 @JS()
@@ -57,6 +58,8 @@
 external LiteralA get a;
 external LiteralB get b;
 
+class DartClass {}
+
 void main() {
   eval(r"""
     function Foo(a) {
@@ -119,4 +122,26 @@
   expect(() => (foo as LiteralB), returnsNormally);
   expect(a is Foo, isTrue);
   expect(() => (a as Foo), returnsNormally);
+
+  // You cannot cast between JS interop objects and Dart objects, however.
+  var dartClass = DartClass();
+  expect(dartClass is Foo, isFalse);
+  expect(() => (dartClass as Foo), throws);
+  expect(dartClass is LiteralA, isFalse);
+  expect(() => (dartClass as LiteralA), throws);
+
+  expect(foo is DartClass, isFalse);
+  expect(() => (foo as DartClass), throws);
+  expect(a is DartClass, isFalse);
+  expect(() => (a as DartClass), throws);
+
+  // Test that nullability is still respected with JS types.
+  Foo? nullableFoo = null;
+  expect(nullableFoo is Foo, false);
+  expect(() => (nullableFoo as Foo),
+      hasUnsoundNullSafety ? returnsNormally : throws);
+  LiteralA? nullableA = null;
+  expect(nullableA is LiteralA, false);
+  expect(() => (nullableA as LiteralA),
+      hasUnsoundNullSafety ? returnsNormally : throws);
 }
diff --git a/tests/lib/js/subtyping_test.dart b/tests/lib/js/subtyping_test.dart
new file mode 100644
index 0000000..2551fcd
--- /dev/null
+++ b/tests/lib/js/subtyping_test.dart
@@ -0,0 +1,88 @@
+// 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.
+
+// Tests subtyping relationships between JS and anonymous classes.
+
+@JS()
+library subtyping_test;
+
+import 'package:js/js.dart';
+import 'package:expect/expect.dart' show hasUnsoundNullSafety;
+import 'package:expect/minitest.dart';
+
+@JS()
+class JSClassA {}
+
+@JS()
+class JSClassB {}
+
+@JS()
+@anonymous
+class AnonymousClassA {}
+
+@JS()
+@anonymous
+class AnonymousClassB {}
+
+class DartClass {}
+
+void useJSClassA(JSClassA _) {}
+void useAnonymousClassA(AnonymousClassA _) {}
+void useDartClass(DartClass _) {}
+
+void useNullableJSClassA(JSClassA? _) {}
+void useNullableAnonymousClassA(AnonymousClassA? _) {}
+
+// Avoid static type optimization by running all tests using this.
+@pragma('dart2js:noInline')
+@pragma('dart2js:assumeDynamic')
+confuse(x) => x;
+
+void main() {
+  // Checks subtyping with the same type and nullability subtyping.
+  expect(useJSClassA is void Function(JSClassA), true);
+  expect(useAnonymousClassA is void Function(AnonymousClassA), true);
+  expect(useJSClassA is void Function(JSClassA?), hasUnsoundNullSafety);
+  expect(useAnonymousClassA is void Function(AnonymousClassA?),
+      hasUnsoundNullSafety);
+  expect(useNullableJSClassA is void Function(JSClassA?), true);
+  expect(useNullableAnonymousClassA is void Function(AnonymousClassA?), true);
+  expect(useNullableJSClassA is void Function(JSClassA), true);
+  expect(useNullableAnonymousClassA is void Function(AnonymousClassA), true);
+
+  expect(confuse(useJSClassA) is void Function(JSClassA), true);
+  expect(confuse(useAnonymousClassA) is void Function(AnonymousClassA), true);
+  expect(
+      confuse(useJSClassA) is void Function(JSClassA?), hasUnsoundNullSafety);
+  expect(confuse(useAnonymousClassA) is void Function(AnonymousClassA?),
+      hasUnsoundNullSafety);
+  expect(confuse(useNullableJSClassA) is void Function(JSClassA?), true);
+  expect(confuse(useNullableAnonymousClassA) is void Function(AnonymousClassA?),
+      true);
+  expect(confuse(useNullableJSClassA) is void Function(JSClassA), true);
+  expect(confuse(useNullableAnonymousClassA) is void Function(AnonymousClassA),
+      true);
+
+  // No subtyping between JS and anonymous classes.
+  expect(useJSClassA is void Function(AnonymousClassA), false);
+  expect(useAnonymousClassA is void Function(JSClassA), false);
+
+  expect(confuse(useJSClassA) is void Function(AnonymousClassA), false);
+  expect(confuse(useAnonymousClassA) is void Function(JSClassA), false);
+
+  // No subtyping between separate classes even if they're both JS classes or
+  // anonymous classes.
+  expect(useJSClassA is void Function(JSClassB), false);
+  expect(useAnonymousClassA is void Function(AnonymousClassB), false);
+
+  expect(confuse(useJSClassA) is void Function(JSClassB), false);
+  expect(confuse(useAnonymousClassA) is void Function(AnonymousClassB), false);
+
+  // No subtyping between JS/anonymous classes and Dart classes.
+  expect(useJSClassA is void Function(DartClass), false);
+  expect(useAnonymousClassA is void Function(DartClass), false);
+
+  expect(confuse(useJSClassA) is void Function(DartClass), false);
+  expect(confuse(useAnonymousClassA) is void Function(DartClass), false);
+}
diff --git a/tests/lib_2/async/future_error_test.dart b/tests/lib_2/async/future_error_test.dart
index 3ef0eb3..bb96fa3 100644
--- a/tests/lib_2/async/future_error_test.dart
+++ b/tests/lib_2/async/future_error_test.dart
@@ -7,7 +7,7 @@
 
 main() {
   // The error cannot be null.
-  Expect.throwsArgumentError(() {
+  Expect.throwsTypeError(() {
     Future.error(null);
   });
 }
diff --git a/tests/lib_2/async/future_test.dart b/tests/lib_2/async/future_test.dart
index 6c227cf..2e546bf 100644
--- a/tests/lib_2/async/future_test.dart
+++ b/tests/lib_2/async/future_test.dart
@@ -709,7 +709,7 @@
 
 void testCompleteErrorWithNull() {
   final completer = new Completer<int>();
-  Expect.throwsArgumentError(() {
+  Expect.throwsTypeError(() {
     completer.completeError(null);
   });
 }
diff --git a/tests/lib_2/async/stream_controller_add_error_test.dart b/tests/lib_2/async/stream_controller_add_error_test.dart
index aed39e8..fe24ceb 100644
--- a/tests/lib_2/async/stream_controller_add_error_test.dart
+++ b/tests/lib_2/async/stream_controller_add_error_test.dart
@@ -8,41 +8,41 @@
 main() {
   // Single-cast async.
   var controller = StreamController();
-  Expect.throwsArgumentError(() {
+  Expect.throwsTypeError(() {
     controller.addError(null);
   });
 
-  Expect.throwsArgumentError(() {
+  Expect.throwsTypeError(() {
     controller.sink.addError(null);
   });
 
   // Single-cast sync.
   controller = StreamController(sync: true);
-  Expect.throwsArgumentError(() {
+  Expect.throwsTypeError(() {
     controller.addError(null);
   });
 
-  Expect.throwsArgumentError(() {
+  Expect.throwsTypeError(() {
     controller.sink.addError(null);
   });
 
   // Broadcast async.
   controller = StreamController.broadcast();
-  Expect.throwsArgumentError(() {
+  Expect.throwsTypeError(() {
     controller.addError(null);
   });
 
-  Expect.throwsArgumentError(() {
+  Expect.throwsTypeError(() {
     controller.sink.addError(null);
   });
 
   // Broadcast sync.
   controller = StreamController.broadcast(sync: true);
-  Expect.throwsArgumentError(() {
+  Expect.throwsTypeError(() {
     controller.addError(null);
   });
 
-  Expect.throwsArgumentError(() {
+  Expect.throwsTypeError(() {
     controller.sink.addError(null);
   });
 }
diff --git a/tests/lib_2/async/stream_error_test.dart b/tests/lib_2/async/stream_error_test.dart
index 22b091b..1c0737e 100644
--- a/tests/lib_2/async/stream_error_test.dart
+++ b/tests/lib_2/async/stream_error_test.dart
@@ -73,7 +73,7 @@
   }
 
   // A null error argument is a synchronous error.
-  Expect.throwsArgumentError(() {
+  Expect.throwsTypeError(() {
     Stream.error(null);
   });
 
diff --git a/tests/lib_2/async/zone_async_error_test.dart b/tests/lib_2/async/zone_async_error_test.dart
index b1f67ce..ad3709d 100644
--- a/tests/lib_2/async/zone_async_error_test.dart
+++ b/tests/lib_2/async/zone_async_error_test.dart
@@ -7,7 +7,7 @@
 
 main() {
   // The error cannot be null.
-  Expect.throwsArgumentError(() {
+  Expect.throwsTypeError(() {
     AsyncError(null, StackTrace.current);
   });
 }
diff --git a/tools/VERSION b/tools/VERSION
index d10bed3..31011dd 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 31
+PRERELEASE 32
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/lib/src/firestore.dart b/tools/bots/lib/src/firestore.dart
index 44344e2..166173c 100644
--- a/tools/bots/lib/src/firestore.dart
+++ b/tools/bots/lib/src/firestore.dart
@@ -70,7 +70,7 @@
     var response = await _client.get(url, headers: _headers);
     if (response.statusCode == HttpStatus.ok) {
       var document = jsonDecode(response.body);
-      if (!document is Map) {
+      if (document is! Map) {
         throw _error(response, message: 'Expected a Map');
       }
       return document;
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index dfe661f..8d4be38 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -45,7 +45,6 @@
       "tests/corelib_2/",
       "tests/dart2js_2/",
       "tests/dartdevc_2/",
-      "tests/kernel/",
       "tests/language/",
       "tests/language_2/",
       "tests/lib_2/",
@@ -86,7 +85,6 @@
       "tests/corelib/",
       "tests/dart2js/",
       "tests/dartdevc/",
-      "tests/kernel/",
       "tests/language/",
       "tests/language_2/",
       "tests/lib/",
@@ -134,7 +132,6 @@
       "tests/corelib_2/",
       "tests/dart2js_2/",
       "tests/dartdevc_2/",
-      "tests/kernel/",
       "tests/language/",
       "tests/language_2/",
       "tests/lib_2/",
@@ -178,7 +175,6 @@
       "tests/corelib/",
       "tests/dart2js/",
       "tests/dartdevc/",
-      "tests/kernel/",
       "tests/language/",
       "tests/language_2/",
       "tests/lib/",
@@ -225,7 +221,6 @@
       "tests/dart2js_2/",
       "tests/dartdevc/",
       "tests/dartdevc_2/",
-      "tests/kernel/",
       "tests/language/",
       "tests/language_2/",
       "tests/lib/",
@@ -336,7 +331,6 @@
       "tests/dart2js_2/",
       "tests/dartdevc",
       "tests/dartdevc_2",
-      "tests/kernel/",
       "tests/language/",
       "tests/language_2/",
       "tests/lib/",
diff --git a/tools/generate_idefiles.py b/tools/generate_idefiles.py
index ebaae9c..5727e49 100755
--- a/tools/generate_idefiles.py
+++ b/tools/generate_idefiles.py
@@ -111,7 +111,6 @@
     - tests/dartdevc_2/**
     - tests/ffi/**
     - tests/ffi_2/**
-    - tests/kernel/**
     - tests/language/**
     - tests/language_2/**
     - tests/lib/**