Version 2.12.0-251.0.dev

Merge commit '6854871069eaa5fe56f24bada4325f9aef23888b' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index ff74a21..eb075b1 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -632,7 +632,7 @@
       "name": "sync_http",
       "rootUri": "../third_party/pkg/sync_http",
       "packageUri": "lib/",
-      "languageVersion": "2.0"
+      "languageVersion": "2.12"
     },
     {
       "name": "telemetry",
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 01e66a5..86c7101 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,11 +20,19 @@
 
 ### Core libraries
 
+#### `dart:async`
+
+* Adds extension method `onError` on `Future` to allow better typing
+  of error handling.
+
 #### `dart:collection`
 
 * Added `UnmodifiableSetView` class, which allows users to guarantee that
   methods that could change underlying `Set` instance can not be invoked.
 
+* `LinkedList` made it explicit that elements are compared by identity,
+  and updated `contains` to take advantage of this.
+
 #### `dart:core`
 
 * Added `unmodifiable` constructor to class `Set`, which allows users to create
@@ -41,11 +49,6 @@
   constructors, a name which can be associated with the port and displayed in
   tooling.
 
-#### `dart:collection`
-
-* `LinkedList` made it explicit that elements are compared by identity,
-  and updated `contains` to take advantage of this.
-
 #### `dart:html`
 
 * `EventStreamSubscription.cancel` has been updated to retain its synchronous
@@ -85,7 +88,7 @@
 
 #### Linter
 
-Updated the Linter to `0.1.128`, which includes:
+Updated the Linter to `0.1.129`, which includes:
 
 * New lint: `avoid_dynamic_calls`.
 * (Internal): `avoid_type_to_string` updated to use `addArgumentList` registry API.
diff --git a/DEPS b/DEPS
index f591082..f867cb4 100644
--- a/DEPS
+++ b/DEPS
@@ -118,7 +118,7 @@
   "intl_tag": "0.17.0-nullsafety",
   "jinja2_rev": "2222b31554f03e62600cd7e383376a7c187967a1",
   "json_rpc_2_rev": "b8dfe403fd8528fd14399dee3a6527b55802dd4d",
-  "linter_tag": "0.1.128",
+  "linter_tag": "0.1.129",
   "logging_rev": "e2f633b543ef89c54688554b15ca3d7e425b86a2",
   "markupsafe_rev": "8f45f5cfa0009d2a70589bcda0349b8cb2b72783",
   "markdown_rev": "6f89681d59541ddb1cf3a58efbdaa2304ffc3f51",
@@ -152,7 +152,7 @@
   "stagehand_rev": "e64ac90cac508981011299c4ceb819149e71f1bd",
   "stream_channel_tag": "d7251e61253ec389ee6e045ee1042311bced8f1d",
   "string_scanner_rev": "1b63e6e5db5933d7be0a45da6e1129fe00262734",
-  "sync_http_rev": "a85d7ec764ea485cbbc49f3f3e7f1b43f87a1c74",
+  "sync_http_rev": "b59c134f2e34d12acac110d4f17f83e5a7db4330",
   "test_descriptor_tag": "1.1.1",
   "test_process_tag": "1.0.3",
   "term_glyph_rev": "6a0f9b6fb645ba75e7a00a4e20072678327a0347",
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/and.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/and.dart
new file mode 100644
index 0000000..f0fc09b
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/and.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.
+
+// This test checks that binary `&&` expressions are annotated with the
+// variables written on their right hand sides.  This is needed for legacy type
+// promotion, to ensure that an assignment on the RHS defeats promotion.
+
+/*member: rhs:declared={a, b, c}, read={a, c}, assigned={b}*/
+rhs(bool a, bool b, bool c) {
+  a /*read={c}, assigned={b}*/ && (b = c);
+}
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/assignment.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/assignment.dart
index 08d699d..ddd0931 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/assignment.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/assignment.dart
@@ -2,17 +2,17 @@
 // 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.
 
-/*member: ordinary:declared={x, y}, assigned={x}*/
+/*member: ordinary:declared={x, y}, read={y}, assigned={x}*/
 ordinary(int x, int y) {
   x = y;
 }
 
-/*member: nullAware:declared={x, y}, assigned={x}*/
+/*member: nullAware:declared={x, y}, read={x, y}, assigned={x}*/
 nullAware(int? x, int y) {
   x ??= y;
 }
 
-/*member: compound:declared={x, y}, assigned={x}*/
+/*member: compound:declared={x, y}, read={x, y}, assigned={x}*/
 compound(int x, int y) {
   x += y;
 }
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/conditional.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/conditional.dart
new file mode 100644
index 0000000..4671eff
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/conditional.dart
@@ -0,0 +1,13 @@
+// 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.
+
+// This test checks that conditional (`?:`) expressions are annotated with the
+// variables written in their "then" branches.  This is needed for legacy type
+// promotion, to ensure that an assignment in the "then" branch defeats
+// promotion.
+
+/*member: then:declared={a, b, c, d, e}, read={a, c, e}, assigned={b, d}*/
+then(bool a, bool b, bool c, bool d, bool e) {
+  a /*read={c}, assigned={b}*/ ? b = c : d = e;
+}
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/for_element.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/for_element.dart
index cb3d20b..11b6beb 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/for_element.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/for_element.dart
@@ -7,9 +7,9 @@
   [/*assigned={b, c, d}*/ for (a = 0; (b = 0) != 0; c = 0) (d = 0)];
 }
 
-/*member: cStyle_unparenthesized:declared={a, b, c, d, e}, assigned={a, b, d, e}*/
+/*member: cStyle_unparenthesized:declared={a, b, c, d, e}, read={c}, assigned={a, b, d, e}*/
 cStyle_unparenthesized(int a, bool b, bool c, int d, int e) {
-  [/*assigned={b, d, e}*/ for (a = 0; b = c; d = 0) e = 0];
+  [/*read={c}, assigned={b, d, e}*/ for (a = 0; b = c; d = 0) e = 0];
 }
 
 /*member: cStyleWithDeclaration:declared={a, b, c, d, e}, assigned={a, b, c, d}*/
@@ -17,9 +17,9 @@
   [/*assigned={b, c, d}*/ for (int e = (a = 0); (b = 0) != 0; c = 0) (d = 0)];
 }
 
-/*member: cStyleWithDeclaration_unparenthesized:declared={a, b, c, d, e, f}, assigned={a, b, d, e}*/
+/*member: cStyleWithDeclaration_unparenthesized:declared={a, b, c, d, e, f}, read={c}, assigned={a, b, d, e}*/
 cStyleWithDeclaration_unparenthesized(int a, bool b, bool c, int d, int e) {
-  [/*assigned={b, d, e}*/ for (int f = a = 0; b = c; d = 0) e = 0];
+  [/*read={c}, assigned={b, d, e}*/ for (int f = a = 0; b = c; d = 0) e = 0];
 }
 
 /*member: forEach:declared={a, b, c}, assigned={a, b, c}*/
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/if.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/if.dart
new file mode 100644
index 0000000..7a426c9
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/if.dart
@@ -0,0 +1,61 @@
+// 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.
+
+// This test checks that `if` statements and collection elements are annotated
+// with the variables written in their "then" branches.  This is needed for
+// legacy type promotion, to ensure that an assignment in the "then" branch
+// defeats promotion.
+
+/*member: ifThenStatement:declared={a, b, c}, read={a, c}, assigned={b}*/
+ifThenStatement(bool a, bool b, bool c) {
+  /*read={c}, assigned={b}*/ if (a) b = c;
+}
+
+/*member: ifThenElseStatement:declared={a, b, c, d, e}, read={a, c, e}, assigned={b, d}*/
+ifThenElseStatement(bool a, bool b, bool c, bool d, bool e) {
+  /*read={c}, assigned={b}*/ if (a)
+    b = c;
+  else
+    d = e;
+}
+
+/*member: ifThenListElement:declared={a, b, c}, read={a, c}, assigned={b}*/
+ifThenListElement(bool a, bool b, bool c) {
+  [/*read={c}, assigned={b}*/ if (a) b = c];
+}
+
+/*member: ifThenElseListElement:declared={a, b, c, d, e}, read={a, c, e}, assigned={b, d}*/
+ifThenElseListElement(bool a, bool b, bool c, bool d, bool e) {
+  [/*read={c}, assigned={b}*/ if (a) b = c else d = e];
+}
+
+/*member: ifThenSetElement:declared={a, b, c}, read={a, c}, assigned={b}*/
+ifThenSetElement(bool a, bool b, bool c) {
+  ({/*read={c}, assigned={b}*/ if (a) b = c});
+}
+
+/*member: ifThenElseSetElement:declared={a, b, c, d, e}, read={a, c, e}, assigned={b, d}*/
+ifThenElseSetElement(bool a, bool b, bool c, bool d, bool e) {
+  ({/*read={c}, assigned={b}*/ if (a) b = c else d = e});
+}
+
+/*member: ifThenMapKey:declared={a, b, c}, read={a, c}, assigned={b}*/
+ifThenMapKey(bool a, bool b, bool c) {
+  ({/*read={c}, assigned={b}*/ if (a) b = c: null});
+}
+
+/*member: ifThenElseMapKey:declared={a, b, c, d, e}, read={a, c, e}, assigned={b, d}*/
+ifThenElseMapKey(bool a, bool b, bool c, bool d, bool e) {
+  ({/*read={c}, assigned={b}*/ if (a) b = c: null else d = e: null});
+}
+
+/*member: ifThenMapValue:declared={a, b, c}, read={a, c}, assigned={b}*/
+ifThenMapValue(bool a, bool b, bool c) {
+  ({/*read={c}, assigned={b}*/ if (a) null: b = c});
+}
+
+/*member: ifThenElseMapValue:declared={a, b, c, d, e}, read={a, c, e}, assigned={b, d}*/
+ifThenElseMapValue(bool a, bool b, bool c, bool d, bool e) {
+  ({/*read={c}, assigned={b}*/ if (a) null: b = c else null: d = e});
+}
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/postfix.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/postfix.dart
index 8f91e74..df8d011 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/postfix.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/postfix.dart
@@ -2,17 +2,17 @@
 // 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.
 
-/*member: increment:declared={x}, assigned={x}*/
+/*member: increment:declared={x}, read={x}, assigned={x}*/
 increment(int x) {
   x++;
 }
 
-/*member: decrement:declared={x}, assigned={x}*/
+/*member: decrement:declared={x}, read={x}, assigned={x}*/
 decrement(int x) {
   x--;
 }
 
-/*member: nonNullAssert:declared={x}*/
+/*member: nonNullAssert:declared={x}, read={x}*/
 nonNullAssert(int? x) {
   x!;
 }
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/prefix.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/prefix.dart
index 445015c..131b478 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/prefix.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/assigned_variables/data/prefix.dart
@@ -2,27 +2,27 @@
 // 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.
 
-/*member: increment:declared={x}, assigned={x}*/
+/*member: increment:declared={x}, read={x}, assigned={x}*/
 increment(int x) {
   ++x;
 }
 
-/*member: decrement:declared={x}, assigned={x}*/
+/*member: decrement:declared={x}, read={x}, assigned={x}*/
 decrement(int x) {
   --x;
 }
 
-/*member: not:declared={b}*/
+/*member: not:declared={b}, read={b}*/
 not(bool b) {
   !b;
 }
 
-/*member: binaryNot:declared={x}*/
+/*member: binaryNot:declared={x}, read={x}*/
 binaryNot(int x) {
   ~x;
 }
 
-/*member: unaryMinus:declared={x}*/
+/*member: unaryMinus:declared={x}, read={x}*/
 unaryMinus(int x) {
   -x;
 }
diff --git a/pkg/analysis_server/lib/src/edit/edit_domain.dart b/pkg/analysis_server/lib/src/edit/edit_domain.dart
index 6b15978..fe63631 100644
--- a/pkg/analysis_server/lib/src/edit/edit_domain.dart
+++ b/pkg/analysis_server/lib/src/edit/edit_domain.dart
@@ -105,8 +105,8 @@
       }
 
       var workspace = DartChangeWorkspace(server.currentSessions);
-      var processor =
-          BulkFixProcessor(server.instrumentationService, workspace);
+      var processor = BulkFixProcessor(server.instrumentationService, workspace,
+          useConfigFiles: params.inTestMode);
 
       String sdkPath;
       var sdk = server.findSdk();
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 ae7dbd0..a0dbd4c 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
@@ -67,12 +67,16 @@
 import 'package:analysis_server/src/services/correction/dart/use_is_not_empty.dart';
 import 'package:analysis_server/src/services/correction/dart/use_rethrow.dart';
 import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/transform_override_set.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/transform_override_set_parser.dart';
 import 'package:analysis_server/src/services/correction/fix_internal.dart';
 import 'package:analysis_server/src/services/linter/lint_names.dart';
 import 'package:analyzer/dart/analysis/analysis_context.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/error/error.dart';
+import 'package:analyzer/error/listener.dart';
 import 'package:analyzer/exception/exception.dart';
+import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/instrumentation/service.dart';
 import 'package:analyzer/source/error_processor.dart';
 import 'package:analyzer/src/error/codes.dart';
@@ -391,6 +395,10 @@
   /// will be produced.
   final DartChangeWorkspace workspace;
 
+  /// A flag indicating whether configuration files should be used to override
+  /// the transforms.
+  final bool useConfigFiles;
+
   /// The change builder used to build the changes required to fix the
   /// diagnostics.
   ChangeBuilder builder;
@@ -400,8 +408,10 @@
 
   /// Initialize a newly created processor to create fixes for diagnostics in
   /// libraries in the [workspace].
-  BulkFixProcessor(this.instrumentationService, this.workspace)
-      : builder = ChangeBuilder(workspace: workspace);
+  BulkFixProcessor(this.instrumentationService, this.workspace,
+      {bool useConfigFiles})
+      : useConfigFiles = useConfigFiles ?? false,
+        builder = ChangeBuilder(workspace: workspace);
 
   List<BulkFix> get fixDetails {
     var details = <BulkFix>[];
@@ -455,11 +465,12 @@
       (name) => [],
     );
 
+    var overrideSet = _readOverrideSet(unit);
     for (var error in errors) {
       final processor = ErrorProcessor.getProcessor(analysisOptions, error);
       // Only fix errors not filtered out in analysis options.
       if (processor == null || processor.severity != null) {
-        await _fixSingleError(fixContext, unit, error);
+        await _fixSingleError(fixContext, unit, error, overrideSet);
       }
     }
 
@@ -533,11 +544,12 @@
         null,
         (name) => [],
       );
+      var overrideSet = _readOverrideSet(unitResult);
       for (var error in unitResult.errors) {
         var processor = ErrorProcessor.getProcessor(analysisOptions, error);
         // Only fix errors not filtered out in analysis options.
         if (processor == null || processor.severity != null) {
-          await _fixSingleError(fixContext, unitResult, error);
+          await _fixSingleError(fixContext, unitResult, error, overrideSet);
         }
       }
     }
@@ -546,12 +558,16 @@
   /// Use the change [builder] and the [fixContext] to create a fix for the
   /// given [diagnostic] in the compilation unit associated with the analysis
   /// [result].
-  Future<void> _fixSingleError(DartFixContext fixContext,
-      ResolvedUnitResult result, AnalysisError diagnostic) async {
+  Future<void> _fixSingleError(
+      DartFixContext fixContext,
+      ResolvedUnitResult result,
+      AnalysisError diagnostic,
+      TransformOverrideSet overrideSet) async {
     var context = CorrectionProducerContext(
       applyingBulkFixes: true,
       dartFixContext: fixContext,
       diagnostic: diagnostic,
+      overrideSet: overrideSet,
       resolvedResult: result,
       selectionOffset: diagnostic.offset,
       selectionLength: diagnostic.length,
@@ -626,6 +642,28 @@
           s);
     }
   }
+
+  /// Return the override set corresponding to the given [result], or `null` if
+  /// there is no corresponding configuration file or the file content isn't a
+  /// valid override set.
+  TransformOverrideSet _readOverrideSet(ResolvedUnitResult result) {
+    if (useConfigFiles) {
+      var provider = result.session.resourceProvider;
+      var context = provider.pathContext;
+      var dartFileName = result.path;
+      var configFileName = '${context.withoutExtension(dartFileName)}.config';
+      var configFile = provider.getFile(configFileName);
+      try {
+        var content = configFile.readAsStringSync();
+        var parser = TransformOverrideSetParser(ErrorReporter(
+            AnalysisErrorListener.NULL_LISTENER, configFile.createSource()));
+        return parser.parse(content);
+      } on FileSystemException {
+        // Fall through to return null.
+      }
+    }
+    return null;
+  }
 }
 
 /// Maps changes to library paths.
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/abstract_producer.dart b/pkg/analysis_server/lib/src/services/correction/dart/abstract_producer.dart
index 93409b4..559d342 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/abstract_producer.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/abstract_producer.dart
@@ -7,6 +7,7 @@
 import 'package:_fe_analyzer_shared/src/scanner/token.dart';
 import 'package:analysis_server/plugin/edit/fix/fix_dart.dart';
 import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/transform_override_set.dart';
 import 'package:analysis_server/src/services/correction/util.dart';
 import 'package:analysis_server/src/utilities/flutter.dart';
 import 'package:analyzer/dart/analysis/results.dart';
@@ -232,6 +233,8 @@
 
   final Diagnostic diagnostic;
 
+  final TransformOverrideSet overrideSet;
+
   AstNode _node;
 
   CorrectionProducerContext({
@@ -240,6 +243,7 @@
     this.applyingBulkFixes = false,
     this.dartFixContext,
     this.diagnostic,
+    this.overrideSet,
     this.selectionOffset = -1,
     this.selectionLength = 0,
   })  : file = resolvedResult.path,
@@ -319,6 +323,7 @@
   /// Initialize a newly created producer.
   _AbstractCorrectionProducer();
 
+  /// Return `true` if the fixes are being built for the bulk-fix request.
   bool get applyingBulkFixes => _context.applyingBulkFixes;
 
   /// The most deeply nested node that completely covers the highlight region of
@@ -357,6 +362,10 @@
 
   AstNode get node => _context.node;
 
+  /// Return the set of overrides to be applied to the transform set when
+  /// running tests, or `null` if there are no overrides to apply.
+  TransformOverrideSet get overrideSet => _context.overrideSet;
+
   ResolvedUnitResult get resolvedResult => _context.resolvedResult;
 
   /// Return the resource provider used to access the file system.
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart b/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart
index 524cdaa..e4c9bd3 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart
@@ -52,7 +52,13 @@
     if (transformSetsForTests != null) {
       return transformSetsForTests;
     }
-    return TransformSetManager.instance.forLibrary(library);
+    var transformSets = TransformSetManager.instance.forLibrary(library);
+    var overrideSet = this.overrideSet;
+    if (overrideSet != null) {
+      transformSets =
+          transformSets.map((set) => set.applyOverrides(overrideSet)).toList();
+    }
+    return transformSets;
   }
 
   /// Return an instance of this class. Used as a tear-off in `FixProcessor`.
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform.dart
index 20d7ff4..0a60dd7 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform.dart
@@ -5,6 +5,7 @@
 import 'package:analysis_server/src/services/correction/fix/data_driven/changes_selector.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/element_descriptor.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/element_matcher.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/transform_override.dart';
 import 'package:meta/meta.dart';
 
 /// A description of a set of changes to a single element of the API.
@@ -44,4 +45,19 @@
     }
     return matcher.matches(element);
   }
+
+  /// Return a new transform with the [override] applied, or this transform if
+  /// there are no overrides.
+  Transform applyOverride(TransformOverride override) {
+    var overriddenBulkApply = override.bulkApply;
+    if (overriddenBulkApply != null && overriddenBulkApply != bulkApply) {
+      return Transform(
+          title: title,
+          date: date,
+          bulkApply: overriddenBulkApply,
+          element: element,
+          changesSelector: changesSelector);
+    }
+    return this;
+  }
 }
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_override.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_override.dart
new file mode 100644
index 0000000..abb7246
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_override.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2021, 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:meta/meta.dart';
+
+/// A description of a set of changes to a single transform.
+class TransformOverride {
+  /// The title of the transform being overridden.
+  final String title;
+
+  /// The overridden value of the `bulkApply` property of the transform, or
+  /// `null` if the property should not be overridden.
+  final bool bulkApply;
+
+  /// Initialize a newly created transform override to override the transform
+  /// with the given [title]. The remaining parameters correspond to properties
+  /// of the transform. They should have non-null values when the property is to
+  /// be overridden, and a value of `null` when the property should be
+  /// unchanged.
+  TransformOverride({@required this.title, this.bulkApply});
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_override_set.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_override_set.dart
new file mode 100644
index 0000000..597efde
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_override_set.dart
@@ -0,0 +1,26 @@
+// Copyright (c) 2021, 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/transform_override.dart';
+
+/// A description of a set of transform overrides.
+class TransformOverrideSet {
+  /// A map from transform titles to the override for that transform.
+  final Map<String, TransformOverride> overrideMap = {};
+
+  /// Initialize a newly created transform override set to include all of the
+  /// [overrides].
+  TransformOverrideSet(List<TransformOverride> overrides) {
+    for (var override in overrides) {
+      overrideMap[override.title] = override;
+    }
+  }
+
+  /// Return the overrides in this set.
+  List<TransformOverride> get overrides => overrideMap.values.toList();
+
+  /// Return the override for the transform with the given [title] or `null` if
+  /// there is no such override in this set.
+  TransformOverride overrideForTransform(String title) => overrideMap[title];
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_override_set_parser.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_override_set_parser.dart
new file mode 100644
index 0000000..fea46f7
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_override_set_parser.dart
@@ -0,0 +1,217 @@
+// Copyright (c) 2021, 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/transform_override.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/transform_override_set.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/transform_set_parser.dart';
+import 'package:analysis_server/src/utilities/extensions/yaml.dart';
+import 'package:analyzer/error/listener.dart';
+import 'package:yaml/src/yaml_exception.dart';
+import 'package:yaml/src/yaml_node.dart';
+import 'package:yaml/yaml.dart';
+
+/// A parser used to parse the content of a configuration file.
+class TransformOverrideSetParser {
+  // TODO(brianwilkerson) Create a class or mixin that would allow this class
+  //  and `TransformSetParser` to share code.
+  static const String _bulkApplyKey = 'bulkApply';
+
+  /// The error reporter to which diagnostics will be reported.
+  final ErrorReporter errorReporter;
+
+  /// Initialize a newly created parser to report errors to the [errorReporter].
+  TransformOverrideSetParser(this.errorReporter);
+
+  /// Return the result of parsing the file [content] into a transform override,
+  /// or `null` if the content does not represent a valid transform override.
+  TransformOverrideSet parse(String content) {
+    assert(content != null);
+    var map = _parseYaml(content);
+    if (map == null) {
+      // The error has already been reported.
+      return null;
+    }
+    return _translateTransformOverrideSet(map);
+  }
+
+  /// Return a textual description of the type of value represented by the
+  /// [node].
+  String _nodeType(YamlNode node) {
+    if (node is YamlScalar) {
+      return node.value.runtimeType.toString();
+    } else if (node is YamlList) {
+      return 'List';
+    } else if (node is YamlMap) {
+      return 'Map';
+    }
+    // We shouldn't get here.
+    return node.runtimeType.toString();
+  }
+
+  /// Return the result of parsing the file [content] into a YAML node.
+  YamlNode _parseYaml(String content) {
+    try {
+      return loadYamlNode(content);
+    } on YamlException catch (e) {
+      var span = e.span;
+      errorReporter.reportErrorForOffset(TransformSetErrorCode.yamlSyntaxError,
+          span.start.offset, span.length, [e.message]);
+    }
+    return null;
+  }
+
+  /// Report a diagnostic with the given [code] associated with the given
+  /// [node]. A list of [arguments] should be provided if the diagnostic message
+  /// has parameters.
+  void _reportError(TransformSetErrorCode code, YamlNode node,
+      [List<String> arguments]) {
+    var span = node.span;
+    errorReporter.reportErrorForOffset(
+        code, span.start.offset, span.length, arguments);
+  }
+
+  /// Report that the value represented by the [node] does not have the
+  /// [expectedType], using the [context] to get the key to use in the message.
+  Null _reportInvalidValue(
+      YamlNode node, ErrorContext context, String expectedType) {
+    _reportError(TransformSetErrorCode.invalidValue, node,
+        [context.key, expectedType, _nodeType(node)]);
+    return null;
+  }
+
+  /// Report that a required key is missing, using the [context] to locate the
+  /// node associated with the diagnostic and the key to use in the message.
+  Null _reportMissingKey(ErrorContext context) {
+    _reportError(
+        TransformSetErrorCode.missingKey, context.parentNode, [context.key]);
+    return null;
+  }
+
+  /// Report any keys in the [map] whose values are not in [validKeys].
+  void _reportUnsupportedKeys(YamlMap map, Set<String> validKeys) {
+    for (var keyNode in map.nodes.keys) {
+      var key = _translateKey(keyNode);
+      if (key != null && !validKeys.contains(key)) {
+        _reportError(TransformSetErrorCode.unsupportedKey, keyNode, [key]);
+      }
+    }
+  }
+
+  /// Translate the [node] into a bool. Return the resulting bool, or `null`
+  /// if the [node] doesn't represent a valid bool. If the [node] isn't valid,
+  /// use the [context] to report the error. If the [node] doesn't exist and
+  /// [required] is `true`, then report an error.
+  bool _translateBool(YamlNode node, ErrorContext context,
+      {bool required = true}) {
+    if (node is YamlScalar) {
+      var value = node.value;
+      if (value is bool) {
+        return value;
+      }
+      return _reportInvalidValue(node, context, 'boolean');
+    } else if (node == null) {
+      if (required) {
+        return _reportMissingKey(context);
+      }
+      return null;
+    } else {
+      return _reportInvalidValue(node, context, 'boolean');
+    }
+  }
+
+  /// Translate the given [node] as a key.
+  String _translateKey(YamlNode node) {
+    String type;
+    if (node is YamlScalar) {
+      if (node.value is String) {
+        return node.value as String;
+      }
+      type = node.value.runtimeType.toString();
+    } else if (node is YamlList) {
+      type = 'List';
+    } else if (node is YamlMap) {
+      type = 'Map';
+    } else {
+      type = node.runtimeType.toString();
+    }
+    _reportError(TransformSetErrorCode.invalidKey, node, [type]);
+    return null;
+  }
+
+  /// Translate the [node] into a string. Return the resulting string, or `null`
+  /// if the [node] doesn't represent a valid string. If the [node] isn't valid,
+  /// use the [context] to report the error. If the [node] doesn't exist and
+  /// [required] is `true`, then report an error.
+  String _translateString(YamlNode node, ErrorContext context,
+      {bool required = true}) {
+    if (node is YamlScalar) {
+      var value = node.value;
+      if (value is String) {
+        return value;
+      }
+      return _reportInvalidValue(node, context, 'String');
+    } else if (node == null) {
+      if (required) {
+        return _reportMissingKey(context);
+      }
+      return null;
+    } else {
+      return _reportInvalidValue(node, context, 'String');
+    }
+  }
+
+  /// Translate the [node] into a transform override. Return the resulting
+  /// transform override, or `null` if the [node] does not represent a valid
+  /// transform override.
+  TransformOverride _translateTransformOverride(
+      YamlNode node, ErrorContext context, String title) {
+    assert(node != null);
+    if (node is YamlMap) {
+      _reportUnsupportedKeys(node, const {_bulkApplyKey});
+      var bulkApplyNode = node.valueAt(_bulkApplyKey);
+      if (bulkApplyNode != null) {
+        var bulkApplyValue = _translateBool(
+            bulkApplyNode, ErrorContext(key: _bulkApplyKey, parentNode: node),
+            required: true);
+        if (bulkApplyValue != null) {
+          return TransformOverride(title: title, bulkApply: bulkApplyValue);
+        }
+      }
+      return null;
+    } else {
+      return _reportInvalidValue(node, context, 'Map');
+    }
+  }
+
+  /// Translate the [node] into a transform override. Return the resulting
+  /// transform override, or `null` if the [node] does not represent a valid
+  /// transform override.
+  TransformOverrideSet _translateTransformOverrideSet(YamlNode node) {
+    assert(node != null);
+    if (node is YamlMap) {
+      var overrides = <TransformOverride>[];
+      for (var entry in node.nodes.entries) {
+        var keyNode = entry.key as YamlNode;
+        var errorContext = ErrorContext(key: 'file', parentNode: node);
+        var key = _translateString(keyNode, errorContext);
+        if (key != null) {
+          var valueNode = entry.value;
+          var override =
+              _translateTransformOverride(valueNode, errorContext, key);
+          if (override != null) {
+            overrides.add(override);
+          }
+        }
+      }
+      return TransformOverrideSet(overrides);
+    } else {
+      // TODO(brianwilkerson) Consider having a different error code for the
+      //  top-level node (instead of using 'file' as the "key").
+      _reportError(TransformSetErrorCode.invalidValue, node,
+          ['file', 'Map', _nodeType(node)]);
+      return null;
+    }
+  }
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set.dart
index bbf0976..de1b7dd 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set.dart
@@ -4,6 +4,7 @@
 
 import 'package:analysis_server/src/services/correction/fix/data_driven/element_matcher.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/transform.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/transform_override_set.dart';
 import 'package:meta/meta.dart';
 
 /// A set of transforms used to aid in the construction of fixes for issues
@@ -18,6 +19,21 @@
     _transforms.add(transform);
   }
 
+  /// Return the result of applying the overrides in the [overrideSet] to the
+  /// transforms in this set.
+  TransformSet applyOverrides(TransformOverrideSet overrideSet) {
+    var overriddenSet = TransformSet();
+    for (var transform in _transforms) {
+      var override = overrideSet.overrideForTransform(transform.title);
+      if (override != null) {
+        overriddenSet.addTransform(transform.applyOverride(override));
+      } else {
+        overriddenSet.addTransform(transform);
+      }
+    }
+    return overriddenSet;
+  }
+
   /// Return a list of the transforms that match the [matcher]. The flag
   /// [applyingBulkFixes] indicates whether the transforms are being applied in
   /// the context of a bulk fix.
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 a239042..59ae8b8 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
@@ -30,6 +30,9 @@
   /// Return the lint code being tested.
   String get lintCode => null;
 
+  /// Return `true` if this test uses config files.
+  bool get useConfigFiles => false;
+
   /// The workspace in which fixes contributor operates.
   ChangeWorkspace get workspace {
     return DartChangeWorkspace([session]);
@@ -58,7 +61,8 @@
     var tracker = DeclarationsTracker(MemoryByteStore(), resourceProvider);
     var analysisContext = contextFor(testFile);
     tracker.addContext(analysisContext);
-    var processor = BulkFixProcessor(TestInstrumentationService(), workspace);
+    var processor = BulkFixProcessor(TestInstrumentationService(), workspace,
+        useConfigFiles: useConfigFiles);
     await processor.fixErrors([analysisContext]);
     return processor;
   }
diff --git a/pkg/analysis_server/test/src/services/correction/fix/bulk/data_driven_test.dart b/pkg/analysis_server/test/src/services/correction/fix/bulk/data_driven_test.dart
index f39851f..2a480a0 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/bulk/data_driven_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/bulk/data_driven_test.dart
@@ -29,6 +29,7 @@
     defineReflectiveTests(UndefinedIdentifierTest);
     defineReflectiveTests(UndefinedMethodTest);
     defineReflectiveTests(UndefinedSetterTest);
+    defineReflectiveTests(WithConfigFileTest);
     defineReflectiveTests(WrongNumberOfTypeArgumentsConstructorTest);
     defineReflectiveTests(WrongNumberOfTypeArgumentsExtensionTest);
     defineReflectiveTests(WrongNumberOfTypeArgumentsMethodTest);
@@ -1051,6 +1052,67 @@
 }
 
 @reflectiveTest
+class WithConfigFileTest extends _DataDrivenTest {
+  @override
+  bool get useConfigFiles => true;
+
+  Future<void> test_bulkApply_withConfig() async {
+    setPackageContent('''
+class New {}
+''');
+    addPackageDataFile('''
+version: 1
+transforms:
+- title: 'Rename to New'
+  date: 2021-21-01
+  bulkApply: false
+  element:
+    uris: ['$importUri']
+    class: 'Old'
+  changes:
+    - kind: 'rename'
+      newName: 'New'
+''');
+    addSource('/home/test/lib/test.config', '''
+'Rename to New':
+  bulkApply: true
+''');
+    await resolveTestCode('''
+import '$importUri';
+void f(Old p) {}
+''');
+    await assertHasFix('''
+import '$importUri';
+void f(New p) {}
+''');
+  }
+
+  Future<void> test_bulkApply_withoutConfig() async {
+    setPackageContent('''
+class New {}
+''');
+    addPackageDataFile('''
+version: 1
+transforms:
+- title: 'Rename to New'
+  date: 2021-21-01
+  bulkApply: false
+  element:
+    uris: ['$importUri']
+    class: 'Old'
+  changes:
+    - kind: 'rename'
+      newName: 'New'
+''');
+    await resolveTestCode('''
+import '$importUri';
+void f(Old p) {}
+''');
+    await assertNoFix();
+  }
+}
+
+@reflectiveTest
 class WrongNumberOfTypeArgumentsConstructorTest extends _DataDrivenTest {
   Future<void> test_addTypeParameter() async {
     setPackageContent('''
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_all.dart
index 22ee1a1..e449a9e 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_all.dart
@@ -14,6 +14,8 @@
 import 'modify_parameters_test.dart' as modify_parameters;
 import 'rename_parameter_test.dart' as rename_parameter;
 import 'rename_test.dart' as rename;
+import 'transform_override_set_parser_test.dart'
+    as transform_override_set_parser;
 import 'transform_set_manager_test.dart' as transform_set_manager;
 import 'transform_set_parser_test.dart' as transform_set_parser;
 
@@ -29,6 +31,7 @@
     modify_parameters.main();
     rename_parameter.main();
     rename.main();
+    transform_override_set_parser.main();
     transform_set_manager.main();
     transform_set_parser.main();
   });
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_override_set_parser_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_override_set_parser_test.dart
new file mode 100644
index 0000000..5aa8721
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_override_set_parser_test.dart
@@ -0,0 +1,92 @@
+// Copyright (c) 2021, 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:_fe_analyzer_shared/src/base/errors.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/transform_override_set.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/transform_override_set_parser.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_error_code.dart';
+import 'package:analyzer/error/listener.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../../../../../mocks.dart';
+import '../../../../../utils/test_support.dart';
+
+void main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(TransformSetParserTest);
+  });
+}
+
+/// Utilities shared between tests of the [TransformOverrideSetParser].
+abstract class AbstractTransformOverrideSetParserTest {
+  /// The listener to which errors will be reported.
+  GatheringErrorListener errorListener;
+
+  /// The result of parsing the test file's content.
+  TransformOverrideSet result;
+
+  void assertErrors(String code, List<ExpectedError> expectedErrors) {
+    parse(code);
+    errorListener.assertErrors(expectedErrors);
+  }
+
+  void assertNoErrors(String content) {
+    parse(content);
+    errorListener.assertNoErrors();
+  }
+
+  void assertOverride(String title, {bool bulkApply}) {
+    var override = result.overrideForTransform(title);
+    expect(override, isNotNull);
+    if (bulkApply != null) {
+      expect(override.bulkApply, bulkApply);
+    }
+  }
+
+  ExpectedError error(ErrorCode code, int offset, int length,
+          {String text,
+          Pattern messageContains,
+          List<ExpectedContextMessage> contextMessages =
+              const <ExpectedContextMessage>[]}) =>
+      ExpectedError(code, offset, length,
+          message: text,
+          messageContains: messageContains,
+          expectedContextMessages: contextMessages);
+
+  void parse(String content) {
+    errorListener = GatheringErrorListener();
+    var errorReporter = ErrorReporter(errorListener, MockSource('data.yaml'));
+    var parser = TransformOverrideSetParser(errorReporter);
+    result = parser.parse(content);
+  }
+}
+
+@reflectiveTest
+class TransformSetParserTest extends AbstractTransformOverrideSetParserTest {
+  void test_emptyFile() {
+    assertErrors('''
+''', [error(TransformSetErrorCode.invalidValue, 0, 0)]);
+    expect(result, isNull);
+  }
+
+  void test_oneTransform() {
+    assertNoErrors('''
+"transform one":
+  bulkApply: true
+''');
+    assertOverride('transform one', bulkApply: true);
+  }
+
+  void test_twoTransforms() {
+    assertNoErrors('''
+"transform one":
+  bulkApply: false
+"transform two":
+  bulkApply: true
+''');
+    assertOverride('transform one', bulkApply: false);
+    assertOverride('transform two', bulkApply: true);
+  }
+}
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 6a9b1ed..9792278 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -86,7 +86,7 @@
 /// TODO(scheglov) Clean up the list of implicitly analyzed files.
 class AnalysisDriver implements AnalysisDriverGeneric {
   /// The version of data format, should be incremented on every format change.
-  static const int DATA_VERSION = 120;
+  static const int DATA_VERSION = 122;
 
   /// The length of the list returned by [_computeDeclaredVariablesSignature].
   static const int _declaredVariablesSignatureLength = 4;
diff --git a/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart b/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart
index f778aef..43bd5eb 100644
--- a/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart
+++ b/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart
@@ -365,10 +365,10 @@
         return lower;
       }
       if (!identical(UnknownInferredType.instance, upper)) {
-        return toKnownType ? _typeSystem.greatestClosure(upper) : upper;
+        return toKnownType ? _typeSystem.greatestClosureOfSchema(upper) : upper;
       }
       if (!identical(UnknownInferredType.instance, lower)) {
-        return toKnownType ? _typeSystem.leastClosure(lower) : lower;
+        return toKnownType ? _typeSystem.leastClosureOfSchema(lower) : lower;
       }
       return upper;
     } else {
@@ -379,10 +379,10 @@
         return upper;
       }
       if (!identical(UnknownInferredType.instance, lower)) {
-        return toKnownType ? _typeSystem.leastClosure(lower) : lower;
+        return toKnownType ? _typeSystem.leastClosureOfSchema(lower) : lower;
       }
       if (!identical(UnknownInferredType.instance, upper)) {
-        return toKnownType ? _typeSystem.greatestClosure(upper) : upper;
+        return toKnownType ? _typeSystem.greatestClosureOfSchema(upper) : upper;
       }
       return lower;
     }
diff --git a/pkg/analyzer/lib/src/dart/element/least_greatest_closure.dart b/pkg/analyzer/lib/src/dart/element/least_greatest_closure.dart
new file mode 100644
index 0000000..c800909
--- /dev/null
+++ b/pkg/analyzer/lib/src/dart/element/least_greatest_closure.dart
@@ -0,0 +1,97 @@
+// Copyright (c) 2021, 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/element/element.dart';
+import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/src/dart/element/replacement_visitor.dart';
+import 'package:analyzer/src/dart/element/type.dart';
+import 'package:analyzer/src/dart/element/type_algebra.dart';
+import 'package:analyzer/src/dart/element/type_system.dart';
+import 'package:meta/meta.dart';
+
+class LeastGreatestClosureHelper extends ReplacementVisitor {
+  final TypeSystemImpl typeSystem;
+  final DartType topType;
+  final DartType topFunctionType;
+  final DartType bottomType;
+  final Set<TypeParameterElement> eliminationTargets;
+
+  bool _isLeastClosure;
+  bool _isCovariant = true;
+
+  LeastGreatestClosureHelper({
+    @required this.typeSystem,
+    @required this.topType,
+    @required this.topFunctionType,
+    @required this.bottomType,
+    @required this.eliminationTargets,
+  });
+
+  DartType get _functionReplacement {
+    return _isLeastClosure && _isCovariant ||
+            (!_isLeastClosure && !_isCovariant)
+        ? bottomType
+        : topFunctionType;
+  }
+
+  DartType get _typeParameterReplacement {
+    return _isLeastClosure && _isCovariant ||
+            (!_isLeastClosure && !_isCovariant)
+        ? bottomType
+        : topType;
+  }
+
+  @override
+  void changeVariance() {
+    _isCovariant = !_isCovariant;
+  }
+
+  /// Returns a supertype of [type] for all values of [eliminationTargets].
+  DartType eliminateToGreatest(DartType type) {
+    _isCovariant = true;
+    _isLeastClosure = false;
+    return type.accept(this) ?? type;
+  }
+
+  /// Returns a subtype of [type] for all values of [eliminationTargets].
+  DartType eliminateToLeast(DartType type) {
+    _isCovariant = true;
+    _isLeastClosure = true;
+    return type.accept(this) ?? type;
+  }
+
+  @override
+  DartType visitFunctionType(FunctionType node) {
+    // - if `S` is
+    //   `T Function<X0 extends B0, ...., Xk extends Bk>(T0 x0, ...., Tn xn,
+    //       [Tn+1 xn+1, ..., Tm xm])`
+    //   or `T Function<X0 extends B0, ...., Xk extends Bk>(T0 x0, ...., Tn xn,
+    //       {Tn+1 xn+1, ..., Tm xm})`
+    //   and `L` contains any free type variables from any of the `Bi`:
+    //  - The least closure of `S` with respect to `L` is `Never`
+    //  - The greatest closure of `S` with respect to `L` is `Function`
+    for (var typeParameter in node.typeFormals) {
+      var bound = typeParameter.bound as TypeImpl;
+      if (bound.referencesAny(eliminationTargets)) {
+        return _functionReplacement;
+      }
+    }
+
+    return super.visitFunctionType(node);
+  }
+
+  @override
+  DartType visitTypeParameterType(TypeParameterType node) {
+    if (eliminationTargets.contains(node.element)) {
+      var replacement = _typeParameterReplacement as TypeImpl;
+      return replacement.withNullability(
+        uniteNullabilities(
+          replacement.nullabilitySuffix,
+          node.nullabilitySuffix,
+        ),
+      );
+    }
+    return super.visitTypeParameterType(node);
+  }
+}
diff --git a/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart b/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart
index d355eab..7a031c7 100644
--- a/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart
+++ b/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart
@@ -635,7 +635,7 @@
 
     // UP(X1 extends B1, T2)
     // UP(X1 & B1, T2)
-    if (T1 is TypeParameterType) {
+    if (T1 is TypeParameterTypeImpl) {
       // T2 if X1 <: T2
       if (_typeSystem.isSubtypeOf2(T1, T2)) {
         return T2;
@@ -644,14 +644,16 @@
       if (_typeSystem.isSubtypeOf2(T2, T1)) {
         return T1;
       }
-      // otherwise UP(B1[Object/X1], T2)
-      var T1_toObject = _typeParameterResolveToObjectBounds(T1);
-      return getLeastUpperBound(T1_toObject, T2);
+      // otherwise UP(B1a, T2)
+      //   where B1a is the greatest closure of B1 with respect to X1
+      var bound = _typeParameterBound(T1);
+      var closure = _typeSystem.greatestClosure(bound, [T1.element]);
+      return getLeastUpperBound(closure, T2);
     }
 
     // UP(T1, X2 extends B2)
     // UP(T1, X2 & B2)
-    if (T2 is TypeParameterType) {
+    if (T2 is TypeParameterTypeImpl) {
       // X2 if T1 <: X2
       if (_typeSystem.isSubtypeOf2(T1, T2)) {
         // TODO(scheglov) How to get here?
@@ -661,9 +663,11 @@
       if (_typeSystem.isSubtypeOf2(T2, T1)) {
         return T1;
       }
-      // otherwise UP(T1, B2[Object/X2])
-      var T2_toObject = _typeParameterResolveToObjectBounds(T2);
-      return getLeastUpperBound(T1, T2_toObject);
+      // otherwise UP(T1, B2a)
+      //   where B2a is the greatest closure of B2 with respect to X2
+      var bound = _typeParameterBound(T2);
+      var closure = _typeSystem.greatestClosure(bound, [T2.element]);
+      return getLeastUpperBound(T1, closure);
     }
 
     // UP(T Function<...>(...), Function) = Function
@@ -880,16 +884,14 @@
     return _typeSystem.getGreatestLowerBound(a.type, b.type);
   }
 
-  /// TODO(scheglov) Use greatest closure.
-  /// See https://github.com/dart-lang/language/pull/1195
-  DartType _typeParameterResolveToObjectBounds(DartType type) {
-    var element = type.element;
-
-    var objectType = _typeSystem.isNonNullableByDefault
+  /// Return the promoted or declared bound of the type parameter.
+  DartType _typeParameterBound(TypeParameterTypeImpl type) {
+    var bound = type.promotedBound ?? type.element.bound;
+    if (bound != null) {
+      return bound;
+    }
+    return _typeSystem.isNonNullableByDefault
         ? _typeSystem.objectQuestion
         : _typeSystem.objectStar;
-
-    type = type.resolveToBound(objectType);
-    return Substitution.fromMap({element: objectType}).substituteType(type);
   }
 }
diff --git a/pkg/analyzer/lib/src/dart/element/type_algebra.dart b/pkg/analyzer/lib/src/dart/element/type_algebra.dart
index fdfffde..a06c562 100644
--- a/pkg/analyzer/lib/src/dart/element/type_algebra.dart
+++ b/pkg/analyzer/lib/src/dart/element/type_algebra.dart
@@ -104,6 +104,25 @@
   return Substitution.fromMap(substitution).substituteType(type);
 }
 
+///  1. Substituting T=X! into T! yields X!
+///  2. Substituting T=X* into T! yields X*
+///  3. Substituting T=X? into T! yields X?
+///  4. Substituting T=X! into T* yields X*
+///  5. Substituting T=X* into T* yields X*
+///  6. Substituting T=X? into T* yields X?
+///  7. Substituting T=X! into T? yields X?
+///  8. Substituting T=X* into T? yields X?
+///  9. Substituting T=X? into T? yields X?
+NullabilitySuffix uniteNullabilities(NullabilitySuffix a, NullabilitySuffix b) {
+  if (a == NullabilitySuffix.question || b == NullabilitySuffix.question) {
+    return NullabilitySuffix.question;
+  }
+  if (a == NullabilitySuffix.star || b == NullabilitySuffix.star) {
+    return NullabilitySuffix.star;
+  }
+  return NullabilitySuffix.none;
+}
+
 class FreshTypeParameters {
   final List<TypeParameterElement> freshTypeParameters;
   final Substitution substitution;
@@ -533,7 +552,7 @@
 
     var parameterSuffix = type.nullabilitySuffix;
     var argumentSuffix = argument.nullabilitySuffix;
-    var nullability = _computeNullability(parameterSuffix, argumentSuffix);
+    var nullability = uniteNullabilities(parameterSuffix, argumentSuffix);
     return (argument as TypeImpl).withNullability(nullability);
   }
 
@@ -547,30 +566,6 @@
     if (types == null) return null;
     return types.map((e) => e.accept(this)).toList();
   }
-
-  ///  1. Substituting T=X! into T! yields X!
-  ///  2. Substituting T=X* into T! yields X*
-  ///  3. Substituting T=X? into T! yields X?
-  ///  4. Substituting T=X! into T* yields X*
-  ///  5. Substituting T=X* into T* yields X*
-  ///  6. Substituting T=X? into T* yields X?
-  ///  7. Substituting T=X! into T? yields X?
-  ///  8. Substituting T=X* into T? yields X?
-  ///  9. Substituting T=X? into T? yields X?
-  static NullabilitySuffix _computeNullability(
-    NullabilitySuffix parameterSuffix,
-    NullabilitySuffix argumentSuffix,
-  ) {
-    if (parameterSuffix == NullabilitySuffix.question ||
-        argumentSuffix == NullabilitySuffix.question) {
-      return NullabilitySuffix.question;
-    }
-    if (parameterSuffix == NullabilitySuffix.star ||
-        argumentSuffix == NullabilitySuffix.star) {
-      return NullabilitySuffix.star;
-    }
-    return NullabilitySuffix.none;
-  }
 }
 
 class _UpperLowerBoundsSubstitution extends Substitution {
diff --git a/pkg/analyzer/lib/src/dart/element/type_system.dart b/pkg/analyzer/lib/src/dart/element/type_system.dart
index 3acda34..9b2ab91 100644
--- a/pkg/analyzer/lib/src/dart/element/type_system.dart
+++ b/pkg/analyzer/lib/src/dart/element/type_system.dart
@@ -16,6 +16,7 @@
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/dart/element/generic_inferrer.dart';
 import 'package:analyzer/src/dart/element/greatest_lower_bound.dart';
+import 'package:analyzer/src/dart/element/least_greatest_closure.dart';
 import 'package:analyzer/src/dart/element/least_upper_bound.dart';
 import 'package:analyzer/src/dart/element/normalize.dart';
 import 'package:analyzer/src/dart/element/nullability_eliminator.dart';
@@ -348,6 +349,36 @@
     return _leastUpperBoundHelper.getLeastUpperBound(T1, T2);
   }
 
+  /// Returns the greatest closure of [type] with respect to [typeParameters].
+  ///
+  /// https://github.com/dart-lang/language
+  /// See `resources/type-system/inference.md`
+  DartType greatestClosure(
+    DartType type,
+    List<TypeParameterElement> typeParameters,
+  ) {
+    var typeParameterSet = Set<TypeParameterElement>.identity();
+    typeParameterSet.addAll(typeParameters);
+
+    if (isNonNullableByDefault) {
+      return LeastGreatestClosureHelper(
+        typeSystem: this,
+        topType: objectQuestion,
+        topFunctionType: typeProvider.functionType,
+        bottomType: NeverTypeImpl.instance,
+        eliminationTargets: typeParameterSet,
+      ).eliminateToGreatest(type);
+    } else {
+      return LeastGreatestClosureHelper(
+        typeSystem: this,
+        topType: DynamicTypeImpl.instance,
+        topFunctionType: typeProvider.functionType,
+        bottomType: typeProvider.nullType,
+        eliminationTargets: typeParameterSet,
+      ).eliminateToGreatest(type);
+    }
+  }
+
   /// Returns the greatest closure of the given type [schema] with respect to
   /// `_`.
   ///
@@ -362,7 +393,7 @@
   ///
   /// Note that the greatest closure of a type schema is always a supertype of
   /// any type which matches the schema.
-  DartType greatestClosure(DartType schema) {
+  DartType greatestClosureOfSchema(DartType schema) {
     if (isNonNullableByDefault) {
       return TypeSchemaEliminationVisitor.run(
         topType: objectQuestion,
@@ -1170,6 +1201,36 @@
     return false;
   }
 
+  /// Returns the least closure of [type] with respect to [typeParameters].
+  ///
+  /// https://github.com/dart-lang/language
+  /// See `resources/type-system/inference.md`
+  DartType leastClosure(
+    DartType type,
+    List<TypeParameterElement> typeParameters,
+  ) {
+    var typeParameterSet = Set<TypeParameterElement>.identity();
+    typeParameterSet.addAll(typeParameters);
+
+    if (isNonNullableByDefault) {
+      return LeastGreatestClosureHelper(
+        typeSystem: this,
+        topType: objectQuestion,
+        topFunctionType: typeProvider.functionType,
+        bottomType: NeverTypeImpl.instance,
+        eliminationTargets: typeParameterSet,
+      ).eliminateToLeast(type);
+    } else {
+      return LeastGreatestClosureHelper(
+        typeSystem: this,
+        topType: DynamicTypeImpl.instance,
+        topFunctionType: typeProvider.functionType,
+        bottomType: typeProvider.nullType,
+        eliminationTargets: typeParameterSet,
+      ).eliminateToLeast(type);
+    }
+  }
+
   /// Returns the least closure of the given type [schema] with respect to `_`.
   ///
   /// The least closure of a type schema `P` with respect to `_` is defined as
@@ -1183,7 +1244,7 @@
   ///
   /// Note that the least closure of a type schema is always a subtype of any
   /// type which matches the schema.
-  DartType leastClosure(DartType schema) {
+  DartType leastClosureOfSchema(DartType schema) {
     if (isNonNullableByDefault) {
       return TypeSchemaEliminationVisitor.run(
         topType: objectQuestion,
diff --git a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
index 6f59ded..1e46e61 100644
--- a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
@@ -397,6 +397,18 @@
   }
 
   @override
+  void visitBinaryExpression(BinaryExpression node) {
+    if (node.operator.type == TokenType.AMPERSAND_AMPERSAND) {
+      node.leftOperand.accept(this);
+      assignedVariables.beginNode();
+      node.rightOperand.accept(this);
+      assignedVariables.endNode(node);
+    } else {
+      super.visitBinaryExpression(node);
+    }
+  }
+
+  @override
   void visitCatchClause(CatchClause node) {
     for (var identifier in [
       node.exceptionParameter,
@@ -411,6 +423,15 @@
   }
 
   @override
+  void visitConditionalExpression(ConditionalExpression node) {
+    node.condition.accept(this);
+    assignedVariables.beginNode();
+    node.thenExpression.accept(this);
+    assignedVariables.endNode(node);
+    node.elseExpression.accept(this);
+  }
+
+  @override
   void visitConstructorDeclaration(ConstructorDeclaration node) {
     throw StateError('Should not visit top level declarations');
   }
@@ -458,6 +479,24 @@
   }
 
   @override
+  void visitIfElement(IfElement node) {
+    node.condition.accept(this);
+    assignedVariables.beginNode();
+    node.thenElement.accept(this);
+    assignedVariables.endNode(node);
+    node.elseElement?.accept(this);
+  }
+
+  @override
+  void visitIfStatement(IfStatement node) {
+    node.condition.accept(this);
+    assignedVariables.beginNode();
+    node.thenStatement.accept(this);
+    assignedVariables.endNode(node);
+    node.elseStatement?.accept(this);
+  }
+
+  @override
   void visitMethodDeclaration(MethodDeclaration node) {
     throw StateError('Should not visit top level declarations');
   }
@@ -491,6 +530,17 @@
   }
 
   @override
+  void visitSimpleIdentifier(SimpleIdentifier node) {
+    var element = node.staticElement;
+    if (element is VariableElement &&
+        node.inGetterContext() &&
+        node.parent is! FormalParameter &&
+        node.parent is! CatchClause) {
+      assignedVariables.read(element);
+    }
+  }
+
+  @override
   void visitSwitchStatement(SwitchStatement node) {
     var expression = node.expression;
     var members = node.members;
diff --git a/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart
index aec4def..56332a4 100644
--- a/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/function_expression_resolver.dart
@@ -98,7 +98,7 @@
         // corresponding parameter in the context type schema with type
         // schema `K`, the parameter is given an inferred type `T` where `T`
         // is derived from `K` as follows.
-        inferredType = _typeSystem.greatestClosure(inferredType);
+        inferredType = _typeSystem.greatestClosureOfSchema(inferredType);
 
         // If the greatest closure of `K` is `S` and `S` is a subtype of
         // `Null`, then `T` is `Object?`. Otherwise, `T` is `S`.
diff --git a/pkg/analyzer/lib/src/summary2/apply_resolution.dart b/pkg/analyzer/lib/src/summary2/apply_resolution.dart
index f1094c6..ec7202d 100644
--- a/pkg/analyzer/lib/src/summary2/apply_resolution.dart
+++ b/pkg/analyzer/lib/src/summary2/apply_resolution.dart
@@ -141,6 +141,15 @@
   }
 
   @override
+  void visitAwaitExpression(AwaitExpression node) {
+    _expectMarker(MarkerTag.AwaitExpression_expression);
+    node.expression.accept(this);
+    _expectMarker(MarkerTag.AwaitExpression_expression2);
+    _expression(node);
+    _expectMarker(MarkerTag.AwaitExpression_end);
+  }
+
+  @override
   void visitBinaryExpression(BinaryExpression node) {
     _expectMarker(MarkerTag.BinaryExpression_leftOperand);
     node.leftOperand.accept(this);
diff --git a/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart b/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart
index 6313f8e..8be47e2 100644
--- a/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart
@@ -46,6 +46,8 @@
         return _readAssertInitializer();
       case Tag.AssignmentExpression:
         return _readAssignmentExpression();
+      case Tag.AwaitExpression:
+        return _readAwaitExpression();
       case Tag.BinaryExpression:
         return _readBinaryExpression();
       case Tag.BooleanLiteral:
@@ -309,6 +311,11 @@
     );
   }
 
+  AwaitExpression _readAwaitExpression() {
+    var expression = readNode() as Expression;
+    return astFactory.awaitExpression(Tokens.AWAIT, expression);
+  }
+
   BinaryExpression _readBinaryExpression() {
     var leftOperand = readNode() as Expression;
     var rightOperand = readNode() as Expression;
diff --git a/pkg/analyzer/lib/src/summary2/ast_binary_tag.dart b/pkg/analyzer/lib/src/summary2/ast_binary_tag.dart
index bcda462..43eee8a 100644
--- a/pkg/analyzer/lib/src/summary2/ast_binary_tag.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_binary_tag.dart
@@ -20,6 +20,9 @@
   AsExpression_type,
   AsExpression_expression2,
   AsExpression_end,
+  AwaitExpression_expression,
+  AwaitExpression_expression2,
+  AwaitExpression_end,
   Expression_staticType,
   AssertInitializer_condition,
   AssertInitializer_message,
@@ -343,6 +346,7 @@
   static const int AsExpression = 84;
   static const int AssertInitializer = 82;
   static const int AssignmentExpression = 96;
+  static const int AwaitExpression = 100;
   static const int BinaryExpression = 52;
   static const int BooleanLiteral = 4;
   static const int CascadeExpression = 95;
diff --git a/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart b/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart
index c805bbd..16e1177 100644
--- a/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart
@@ -146,6 +146,19 @@
   }
 
   @override
+  void visitAwaitExpression(AwaitExpression node) {
+    _writeByte(Tag.AwaitExpression);
+
+    _writeMarker(MarkerTag.AwaitExpression_expression);
+    _writeNode(node.expression);
+
+    _writeMarker(MarkerTag.AsExpression_expression2);
+    _storeExpression(node);
+
+    _writeMarker(MarkerTag.AwaitExpression_end);
+  }
+
+  @override
   void visitBinaryExpression(BinaryExpression node) {
     _writeByte(Tag.BinaryExpression);
 
diff --git a/pkg/analyzer/lib/src/util/ast_data_extractor.dart b/pkg/analyzer/lib/src/util/ast_data_extractor.dart
index 2147db8..a95a277 100644
--- a/pkg/analyzer/lib/src/util/ast_data_extractor.dart
+++ b/pkg/analyzer/lib/src/util/ast_data_extractor.dart
@@ -26,13 +26,13 @@
   void computeForClass(Declaration node, Id id) {
     if (id == null) return;
     T value = computeNodeValue(id, node);
-    registerValue(uri, node.offset, id, value, node);
+    registerValue(uri, _nodeOffset(node), id, value, node);
   }
 
   void computeForCollectionElement(CollectionElement node, NodeId id) {
     if (id == null) return;
     T value = computeNodeValue(id, node);
-    registerValue(uri, node.offset, id, value, node);
+    registerValue(uri, _nodeOffset(node), id, value, node);
   }
 
   void computeForLibrary(LibraryElement library, Id id) {
@@ -44,13 +44,13 @@
   void computeForMember(Declaration node, Id id) {
     if (id == null) return;
     T value = computeNodeValue(id, node);
-    registerValue(uri, node.offset, id, value, node);
+    registerValue(uri, _nodeOffset(node), id, value, node);
   }
 
   void computeForStatement(Statement node, NodeId id) {
     if (id == null) return;
     T value = computeNodeValue(id, node);
-    registerValue(uri, node.offset, id, value, node);
+    registerValue(uri, _nodeOffset(node), id, value, node);
   }
 
   /// Implement this to compute the data corresponding to [node].
@@ -174,7 +174,14 @@
   }
 
   int _nodeOffset(AstNode node) {
-    var offset = node.offset;
+    int offset;
+    if (node is ConditionalExpression) {
+      offset = node.question.offset;
+    } else if (node is BinaryExpression) {
+      offset = node.operator.offset;
+    } else {
+      offset = node.offset;
+    }
     assert(offset != null && offset >= 0,
         "No fileOffset on $node (${node.runtimeType})");
     return offset;
diff --git a/pkg/analyzer/test/generated/invalid_code_test.dart b/pkg/analyzer/test/generated/invalid_code_test.dart
index 4b043ca..0d4c949 100644
--- a/pkg/analyzer/test/generated/invalid_code_test.dart
+++ b/pkg/analyzer/test/generated/invalid_code_test.dart
@@ -20,6 +20,12 @@
 /// and analysis finishes without exceptions.
 @reflectiveTest
 class InvalidCodeTest extends PubPackageResolutionTest {
+  test_const_AwaitExpression() async {
+    await _assertCanBeAnalyzed(r'''
+const a = await b();
+''');
+  }
+
   test_const_ForPartsWithExpression() async {
     await _assertCanBeAnalyzed(r'''
 @A([for (;;) 0])
diff --git a/pkg/analyzer/test/id_tests/assigned_variables_test.dart b/pkg/analyzer/test/id_tests/assigned_variables_test.dart
index f674c7c..00c8a11 100644
--- a/pkg/analyzer/test/id_tests/assigned_variables_test.dart
+++ b/pkg/analyzer/test/id_tests/assigned_variables_test.dart
@@ -69,12 +69,16 @@
     if (node == _currentDeclaration) {
       return _Data(
           _convertVars(_currentAssignedVariables.declaredAtTopLevel),
+          _convertVars(_currentAssignedVariables.readAnywhere),
+          _convertVars(_currentAssignedVariables.readCapturedAnywhere),
           _convertVars(_currentAssignedVariables.writtenAnywhere),
           _convertVars(_currentAssignedVariables.capturedAnywhere));
     }
     if (!_currentAssignedVariables.isTracked(node)) return null;
     return _Data(
         _convertVars(_currentAssignedVariables.declaredInNode(node)),
+        _convertVars(_currentAssignedVariables.readInNode(node)),
+        _convertVars(_currentAssignedVariables.readCapturedInNode(node)),
         _convertVars(_currentAssignedVariables.writtenInNode(node)),
         _convertVars(_currentAssignedVariables.capturedInNode(node)));
   }
@@ -123,6 +127,12 @@
     if (actualData.declared.isNotEmpty) {
       parts.add('declared=${_setToString(actualData.declared)}');
     }
+    if (actualData.read.isNotEmpty) {
+      parts.add('read=${_setToString(actualData.read)}');
+    }
+    if (actualData.readCaptured.isNotEmpty) {
+      parts.add('read=${_setToString(actualData.readCaptured)}');
+    }
     if (actualData.assigned.isNotEmpty) {
       parts.add('assigned=${_setToString(actualData.assigned)}');
     }
@@ -156,9 +166,14 @@
 class _Data {
   final Set<String> declared;
 
+  final Set<String> read;
+
+  final Set<String> readCaptured;
+
   final Set<String> assigned;
 
   final Set<String> captured;
 
-  _Data(this.declared, this.assigned, this.captured);
+  _Data(this.declared, this.read, this.readCaptured, this.assigned,
+      this.captured);
 }
diff --git a/pkg/analyzer/test/src/dart/element/least_greatest_closure_test.dart b/pkg/analyzer/test/src/dart/element/least_greatest_closure_test.dart
new file mode 100644
index 0000000..2d92505
--- /dev/null
+++ b/pkg/analyzer/test/src/dart/element/least_greatest_closure_test.dart
@@ -0,0 +1,275 @@
+// Copyright (c) 2021, 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/element/element.dart';
+import 'package:analyzer/dart/element/type.dart';
+import 'package:meta/meta.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../../../generated/type_system_test.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(GreatestClosureLegacyTest);
+    defineReflectiveTests(GreatestClosureNullSafetyTest);
+  });
+}
+
+@reflectiveTest
+class GreatestClosureLegacyTest extends AbstractTypeSystemTest {
+  TypeParameterElement T;
+  TypeParameterType T_none;
+  TypeParameterType T_question;
+  TypeParameterType T_star;
+
+  @override
+  void setUp() {
+    super.setUp();
+
+    T = typeParameter('T');
+    T_none = typeParameterTypeNone(T);
+    T_question = typeParameterTypeQuestion(T);
+    T_star = typeParameterTypeStar(T);
+  }
+
+  test_contravariant() {
+    _check(
+      functionTypeStar(returnType: voidNone, parameters: [
+        requiredParameter(type: T_star),
+      ]),
+      greatest: 'void Function(Null*)*',
+      least: 'void Function(dynamic)*',
+    );
+
+    _check(
+      functionTypeStar(
+        returnType: functionTypeStar(
+          returnType: voidNone,
+          parameters: [
+            requiredParameter(type: T_star),
+          ],
+        ),
+      ),
+      greatest: 'void Function(Null*)* Function()*',
+      least: 'void Function(dynamic)* Function()*',
+    );
+  }
+
+  test_covariant() {
+    _check(T_star, greatest: 'dynamic', least: 'Null*');
+
+    _check(
+      listStar(T_star),
+      greatest: 'List<dynamic>*',
+      least: 'List<Null*>*',
+    );
+
+    _check(
+      functionTypeStar(returnType: voidNone, parameters: [
+        requiredParameter(
+          type: functionTypeStar(returnType: intStar, parameters: [
+            requiredParameter(type: T_star),
+          ]),
+        ),
+      ]),
+      greatest: 'void Function(int* Function(dynamic)*)*',
+      least: 'void Function(int* Function(Null*)*)*',
+    );
+  }
+
+  test_function() {
+    // void Function<U extends T>()
+    _check(
+      functionTypeStar(
+        typeFormals: [
+          typeParameter('U', bound: T_star),
+        ],
+        returnType: voidNone,
+      ),
+      greatest: 'Function*',
+      least: 'Null*',
+    );
+  }
+
+  test_unrelated() {
+    _check1(intStar, 'int*');
+    _check1(listStar(intStar), 'List<int*>*');
+
+    _check1(objectStar, 'Object*');
+    _check1(neverStar, 'Never*');
+    _check1(nullStar, 'Null*');
+
+    _check1(dynamicNone, 'dynamic');
+
+    _check1(
+      functionTypeStar(returnType: stringStar, parameters: [
+        requiredParameter(type: intStar),
+      ]),
+      'String* Function(int*)*',
+    );
+
+    _check1(
+      typeParameterTypeStar(
+        typeParameter('U'),
+      ),
+      'U*',
+    );
+  }
+
+  void _check(
+    DartType type, {
+    @required String greatest,
+    @required String least,
+  }) {
+    var greatestResult = typeSystem.greatestClosure(type, [T]);
+    expect(
+      greatestResult.getDisplayString(withNullability: true),
+      greatest,
+    );
+
+    var leastResult = typeSystem.leastClosure(type, [T]);
+    expect(
+      leastResult.getDisplayString(withNullability: true),
+      least,
+    );
+  }
+
+  void _check1(DartType type, String expected) {
+    _check(type, greatest: expected, least: expected);
+  }
+}
+
+@reflectiveTest
+class GreatestClosureNullSafetyTest extends AbstractTypeSystemNullSafetyTest {
+  TypeParameterElement T;
+  TypeParameterType T_none;
+  TypeParameterType T_question;
+  TypeParameterType T_star;
+
+  @override
+  void setUp() {
+    super.setUp();
+
+    T = typeParameter('T');
+    T_none = typeParameterTypeNone(T);
+    T_question = typeParameterTypeQuestion(T);
+    T_star = typeParameterTypeStar(T);
+  }
+
+  test_contravariant() {
+    _check(
+      functionTypeNone(returnType: voidNone, parameters: [
+        requiredParameter(type: T_none),
+      ]),
+      greatest: 'void Function(Never)',
+      least: 'void Function(Object?)',
+    );
+
+    _check(
+      functionTypeNone(
+        returnType: functionTypeNone(
+          returnType: voidNone,
+          parameters: [
+            requiredParameter(type: T_none),
+          ],
+        ),
+      ),
+      greatest: 'void Function(Never) Function()',
+      least: 'void Function(Object?) Function()',
+    );
+  }
+
+  test_covariant() {
+    _check(T_none, greatest: 'Object?', least: 'Never');
+    _check(T_question, greatest: 'Object?', least: 'Never?');
+    _check(T_star, greatest: 'Object?', least: 'Never*');
+
+    _check(
+      listNone(T_none),
+      greatest: 'List<Object?>',
+      least: 'List<Never>',
+    );
+
+    _check(
+        functionTypeNone(returnType: voidNone, parameters: [
+          requiredParameter(
+            type: functionTypeNone(returnType: intNone, parameters: [
+              requiredParameter(type: T_none),
+            ]),
+          ),
+        ]),
+        greatest: 'void Function(int Function(Object?))',
+        least: 'void Function(int Function(Never))');
+  }
+
+  test_function() {
+    // void Function<U extends T>()
+    _check(
+      functionTypeNone(
+        typeFormals: [
+          typeParameter('U', bound: T_none),
+        ],
+        returnType: voidNone,
+      ),
+      greatest: 'Function',
+      least: 'Never',
+    );
+  }
+
+  test_unrelated() {
+    _check1(intNone, 'int');
+    _check1(intQuestion, 'int?');
+    _check1(intStar, 'int*');
+
+    _check1(listNone(intNone), 'List<int>');
+    _check1(listQuestion(intNone), 'List<int>?');
+
+    _check1(objectNone, 'Object');
+    _check1(objectQuestion, 'Object?');
+    _check1(objectStar, 'Object*');
+
+    _check1(neverNone, 'Never');
+    _check1(neverQuestion, 'Never?');
+    _check1(neverStar, 'Never*');
+
+    _check1(dynamicNone, 'dynamic');
+
+    _check1(
+      functionTypeNone(returnType: stringNone, parameters: [
+        requiredParameter(type: intNone),
+      ]),
+      'String Function(int)',
+    );
+
+    _check1(
+      typeParameterTypeNone(
+        typeParameter('U'),
+      ),
+      'U',
+    );
+  }
+
+  void _check(
+    DartType type, {
+    @required String greatest,
+    @required String least,
+  }) {
+    var greatestResult = typeSystem.greatestClosure(type, [T]);
+    expect(
+      greatestResult.getDisplayString(withNullability: true),
+      greatest,
+    );
+
+    var leastResult = typeSystem.leastClosure(type, [T]);
+    expect(
+      leastResult.getDisplayString(withNullability: true),
+      least,
+    );
+  }
+
+  void _check1(DartType type, String expected) {
+    _check(type, greatest: expected, least: expected);
+  }
+}
diff --git a/pkg/analyzer/test/src/dart/element/subtype_test.dart b/pkg/analyzer/test/src/dart/element/subtype_test.dart
index 4cb55f1..18b28e1 100644
--- a/pkg/analyzer/test/src/dart/element/subtype_test.dart
+++ b/pkg/analyzer/test/src/dart/element/subtype_test.dart
@@ -5410,6 +5410,21 @@
     );
   }
 
+  @FailingTest(issue: 'https://github.com/dart-lang/language/issues/433')
+  test_typeParameter_44() {
+    var T = typeParameter('T');
+    var T_none = typeParameterTypeNone(T);
+    var FutureOr_T_none = futureOrNone(T_none);
+    T.bound = FutureOr_T_none;
+
+    isSubtype(
+      T_none,
+      FutureOr_T_none,
+      strT0: 'T, T extends FutureOr<T>',
+      strT1: 'FutureOr<T>, T extends FutureOr<T>',
+    );
+  }
+
   void _defineType(String str, DartType type) {
     for (var key in _types.keys) {
       if (key == 'Never' || _typeStr(type) == 'Never') {
diff --git a/pkg/analyzer/test/src/dart/element/test_all.dart b/pkg/analyzer/test/src/dart/element/test_all.dart
index bac0ac6..f86065c 100644
--- a/pkg/analyzer/test/src/dart/element/test_all.dart
+++ b/pkg/analyzer/test/src/dart/element/test_all.dart
@@ -13,6 +13,7 @@
 import 'future_value_type_test.dart' as future_value_type;
 import 'generic_inferrer_test.dart' as generic_inferrer;
 import 'inheritance_manager3_test.dart' as inheritance_manager3;
+import 'least_greatest_closure_test.dart' as least_greatest_closure_test;
 import 'least_upper_bound_helper_test.dart' as least_upper_bound_helper;
 import 'normalize_type_test.dart' as normalize_type;
 import 'nullability_eliminator_test.dart' as nullability_eliminator;
@@ -41,6 +42,7 @@
     future_value_type.main();
     generic_inferrer.main();
     inheritance_manager3.main();
+    least_greatest_closure_test.main();
     least_upper_bound_helper.main();
     normalize_type.main();
     nullability_eliminator.main();
diff --git a/pkg/analyzer/test/src/dart/element/upper_lower_bound_test.dart b/pkg/analyzer/test/src/dart/element/upper_lower_bound_test.dart
index 77f2918..6e4b9fe 100644
--- a/pkg/analyzer/test/src/dart/element/upper_lower_bound_test.dart
+++ b/pkg/analyzer/test/src/dart/element/upper_lower_bound_test.dart
@@ -3092,6 +3092,63 @@
     );
   }
 
+  void test_typeParameter_greatestClosure_functionBounded() {
+    var T = typeParameter('T');
+    var T_none = typeParameterTypeNone(T);
+    T.bound = functionTypeNone(
+      returnType: voidNone,
+      parameters: [
+        requiredParameter(type: T_none),
+      ],
+    );
+
+    _checkLeastUpperBound(
+      T_none,
+      functionTypeNone(
+        returnType: voidNone,
+        parameters: [
+          requiredParameter(type: nullNone),
+        ],
+      ),
+      functionTypeNone(
+        returnType: voidNone,
+        parameters: [
+          requiredParameter(type: neverNone),
+        ],
+      ),
+    );
+  }
+
+  void test_typeParameter_greatestClosure_functionPromoted() {
+    var T = typeParameter('T');
+    var T_none = typeParameterTypeNone(T);
+    var T_none_promoted = typeParameterTypeNone(
+      T,
+      promotedBound: functionTypeNone(
+        returnType: voidNone,
+        parameters: [
+          requiredParameter(type: T_none),
+        ],
+      ),
+    );
+
+    _checkLeastUpperBound(
+      T_none_promoted,
+      functionTypeNone(
+        returnType: voidNone,
+        parameters: [
+          requiredParameter(type: nullNone),
+        ],
+      ),
+      functionTypeNone(
+        returnType: voidNone,
+        parameters: [
+          requiredParameter(type: neverNone),
+        ],
+      ),
+    );
+  }
+
   void test_typeParameter_interface_bounded() {
     var A = class_(name: 'A');
     var A_none = interfaceTypeNone(A);
diff --git a/pkg/analyzer_plugin/lib/plugin/assist_mixin.dart b/pkg/analyzer_plugin/lib/plugin/assist_mixin.dart
index 051d9d0..3a90b0b 100644
--- a/pkg/analyzer_plugin/lib/plugin/assist_mixin.dart
+++ b/pkg/analyzer_plugin/lib/plugin/assist_mixin.dart
@@ -28,8 +28,6 @@
   @override
   Future<EditGetAssistsResult> handleEditGetAssists(
       EditGetAssistsParams parameters) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     var path = parameters.file;
     var request = await getAssistRequest(parameters);
     var generator = AssistGenerator(getAssistContributors(path));
@@ -50,8 +48,6 @@
   @override
   Future<AssistRequest> getAssistRequest(
       EditGetAssistsParams parameters) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     var path = parameters.file;
     var result = await getResolvedUnitResult(path);
     return DartAssistRequestImpl(
diff --git a/pkg/analyzer_plugin/lib/plugin/completion_mixin.dart b/pkg/analyzer_plugin/lib/plugin/completion_mixin.dart
index ed39aba..4bbf59a 100644
--- a/pkg/analyzer_plugin/lib/plugin/completion_mixin.dart
+++ b/pkg/analyzer_plugin/lib/plugin/completion_mixin.dart
@@ -29,8 +29,6 @@
   @override
   Future<CompletionGetSuggestionsResult> handleCompletionGetSuggestions(
       CompletionGetSuggestionsParams parameters) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     var path = parameters.file;
     var request = await getCompletionRequest(parameters);
     var generator = CompletionGenerator(getCompletionContributors(path));
@@ -52,8 +50,6 @@
   @override
   Future<CompletionRequest> getCompletionRequest(
       CompletionGetSuggestionsParams parameters) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     var result = await getResolvedUnitResult(parameters.file);
     return DartCompletionRequestImpl(
         resourceProvider, parameters.offset, result);
diff --git a/pkg/analyzer_plugin/lib/plugin/fix_mixin.dart b/pkg/analyzer_plugin/lib/plugin/fix_mixin.dart
index 1b8d651..1fde901 100644
--- a/pkg/analyzer_plugin/lib/plugin/fix_mixin.dart
+++ b/pkg/analyzer_plugin/lib/plugin/fix_mixin.dart
@@ -21,8 +21,6 @@
 mixin DartFixesMixin implements FixesMixin {
   @override
   Future<FixesRequest> getFixesRequest(EditGetFixesParams parameters) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     var path = parameters.file;
     var offset = parameters.offset;
     var result = await getResolvedUnitResult(path);
@@ -59,8 +57,6 @@
   @override
   Future<EditGetFixesResult> handleEditGetFixes(
       EditGetFixesParams parameters) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     var path = parameters.file;
     var request = await getFixesRequest(parameters);
     var generator = FixGenerator(getFixContributors(path));
diff --git a/pkg/analyzer_plugin/lib/plugin/folding_mixin.dart b/pkg/analyzer_plugin/lib/plugin/folding_mixin.dart
index ecae950..6bc5e1b 100644
--- a/pkg/analyzer_plugin/lib/plugin/folding_mixin.dart
+++ b/pkg/analyzer_plugin/lib/plugin/folding_mixin.dart
@@ -19,8 +19,6 @@
 mixin DartFoldingMixin implements FoldingMixin {
   @override
   Future<FoldingRequest> getFoldingRequest(String path) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     var result = await getResolvedUnitResult(path);
     return DartFoldingRequestImpl(resourceProvider, result);
   }
@@ -44,8 +42,6 @@
 
   @override
   Future<void> sendFoldingNotification(String path) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     try {
       var request = await getFoldingRequest(path);
       var generator = FoldingGenerator(getFoldingContributors(path));
diff --git a/pkg/analyzer_plugin/lib/plugin/highlights_mixin.dart b/pkg/analyzer_plugin/lib/plugin/highlights_mixin.dart
index a74deba..1a119bb 100644
--- a/pkg/analyzer_plugin/lib/plugin/highlights_mixin.dart
+++ b/pkg/analyzer_plugin/lib/plugin/highlights_mixin.dart
@@ -19,8 +19,6 @@
 mixin DartHighlightsMixin implements HighlightsMixin {
   @override
   Future<HighlightsRequest> getHighlightsRequest(String path) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     var result = await getResolvedUnitResult(path);
     return DartHighlightsRequestImpl(resourceProvider, result);
   }
@@ -44,8 +42,6 @@
 
   @override
   Future<void> sendHighlightsNotification(String path) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     try {
       var request = await getHighlightsRequest(path);
       var generator = HighlightsGenerator(getHighlightsContributors(path));
diff --git a/pkg/analyzer_plugin/lib/plugin/kythe_mixin.dart b/pkg/analyzer_plugin/lib/plugin/kythe_mixin.dart
index 353ec8d..d452e0d 100644
--- a/pkg/analyzer_plugin/lib/plugin/kythe_mixin.dart
+++ b/pkg/analyzer_plugin/lib/plugin/kythe_mixin.dart
@@ -21,8 +21,6 @@
   @override
   Future<EntryRequest> getEntryRequest(
       KytheGetKytheEntriesParams parameters) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     var path = parameters.file;
     var result = await getResolvedUnitResult(path);
     return DartEntryRequestImpl(resourceProvider, result);
@@ -48,8 +46,6 @@
   @override
   Future<KytheGetKytheEntriesResult> handleKytheGetKytheEntries(
       KytheGetKytheEntriesParams parameters) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     var path = parameters.file;
     var request = await getEntryRequest(parameters);
     var generator = EntryGenerator(getEntryContributors(path));
diff --git a/pkg/analyzer_plugin/lib/plugin/navigation_mixin.dart b/pkg/analyzer_plugin/lib/plugin/navigation_mixin.dart
index fb215d7..07dbddb 100644
--- a/pkg/analyzer_plugin/lib/plugin/navigation_mixin.dart
+++ b/pkg/analyzer_plugin/lib/plugin/navigation_mixin.dart
@@ -21,8 +21,6 @@
   @override
   Future<NavigationRequest> getNavigationRequest(
       AnalysisGetNavigationParams parameters) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     var path = parameters.file;
     var result = await getResolvedUnitResult(path);
     var offset = parameters.offset;
@@ -55,8 +53,6 @@
   @override
   Future<AnalysisGetNavigationResult> handleAnalysisGetNavigation(
       AnalysisGetNavigationParams parameters) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     var path = parameters.file;
     var request = await getNavigationRequest(parameters);
     var generator = NavigationGenerator(getNavigationContributors(path));
@@ -69,8 +65,6 @@
   /// server.
   @override
   Future<void> sendNavigationNotification(String path) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     try {
       var request =
           await getNavigationRequest(AnalysisGetNavigationParams(path, -1, -1));
diff --git a/pkg/analyzer_plugin/lib/plugin/occurrences_mixin.dart b/pkg/analyzer_plugin/lib/plugin/occurrences_mixin.dart
index 7c2fd1e..72a2c6c 100644
--- a/pkg/analyzer_plugin/lib/plugin/occurrences_mixin.dart
+++ b/pkg/analyzer_plugin/lib/plugin/occurrences_mixin.dart
@@ -19,8 +19,6 @@
 mixin DartOccurrencesMixin implements OccurrencesMixin {
   @override
   Future<OccurrencesRequest> getOccurrencesRequest(String path) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     var result = await getResolvedUnitResult(path);
     return DartOccurrencesRequestImpl(resourceProvider, result);
   }
@@ -44,8 +42,6 @@
 
   @override
   Future<void> sendOccurrencesNotification(String path) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     try {
       var request = await getOccurrencesRequest(path);
       var generator = OccurrencesGenerator(getOccurrencesContributors(path));
diff --git a/pkg/analyzer_plugin/lib/plugin/outline_mixin.dart b/pkg/analyzer_plugin/lib/plugin/outline_mixin.dart
index b249718..760103f 100644
--- a/pkg/analyzer_plugin/lib/plugin/outline_mixin.dart
+++ b/pkg/analyzer_plugin/lib/plugin/outline_mixin.dart
@@ -19,8 +19,6 @@
 mixin DartOutlineMixin implements OutlineMixin {
   @override
   Future<OutlineRequest> getOutlineRequest(String path) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     var result = await getResolvedUnitResult(path);
     return DartOutlineRequestImpl(resourceProvider, result);
   }
@@ -44,8 +42,6 @@
 
   @override
   Future<void> sendOutlineNotification(String path) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     try {
       var request = await getOutlineRequest(path);
       var generator = OutlineGenerator(getOutlineContributors(path));
diff --git a/pkg/analyzer_plugin/lib/plugin/plugin.dart b/pkg/analyzer_plugin/lib/plugin/plugin.dart
index 970d5d9..9759368 100644
--- a/pkg/analyzer_plugin/lib/plugin/plugin.dart
+++ b/pkg/analyzer_plugin/lib/plugin/plugin.dart
@@ -153,8 +153,6 @@
   /// Throw a [RequestFailure] is the file cannot be analyzed or if the driver
   /// associated with the file is not an [AnalysisDriver].
   Future<ResolvedUnitResult> getResolvedUnitResult(String path) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     var driver = driverForPath(path);
     if (driver is! AnalysisDriver) {
       // Return an error from the request.
@@ -176,8 +174,6 @@
   /// Throw a [RequestFailure] if the request could not be handled.
   Future<AnalysisGetNavigationResult> handleAnalysisGetNavigation(
       AnalysisGetNavigationParams params) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     return AnalysisGetNavigationResult(
         <String>[], <NavigationTarget>[], <NavigationRegion>[]);
   }
@@ -187,8 +183,6 @@
   /// Throw a [RequestFailure] if the request could not be handled.
   Future<AnalysisHandleWatchEventsResult> handleAnalysisHandleWatchEvents(
       AnalysisHandleWatchEventsParams parameters) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     for (var event in parameters.events) {
       switch (event.type) {
         case WatchEventType.ADD:
@@ -213,8 +207,6 @@
   /// Throw a [RequestFailure] if the request could not be handled.
   Future<AnalysisSetContextRootsResult> handleAnalysisSetContextRoots(
       AnalysisSetContextRootsParams parameters) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     var contextRoots = parameters.roots;
     var oldRoots = driverMap.keys.toList();
     for (var contextRoot in contextRoots) {
@@ -244,8 +236,6 @@
   /// Throw a [RequestFailure] if the request could not be handled.
   Future<AnalysisSetPriorityFilesResult> handleAnalysisSetPriorityFiles(
       AnalysisSetPriorityFilesParams parameters) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     var files = parameters.files;
     var filesByDriver = <AnalysisDriverGeneric, List<String>>{};
     for (var file in files) {
@@ -269,8 +259,6 @@
   /// Throw a [RequestFailure] if the request could not be handled.
   Future<AnalysisSetSubscriptionsResult> handleAnalysisSetSubscriptions(
       AnalysisSetSubscriptionsParams parameters) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     var subscriptions = parameters.subscriptions;
     var newSubscriptions = subscriptionManager.setSubscriptions(subscriptions);
     sendNotificationsForSubscriptions(newSubscriptions);
@@ -284,8 +272,6 @@
   /// Throw a [RequestFailure] if the request could not be handled.
   Future<AnalysisUpdateContentResult> handleAnalysisUpdateContent(
       AnalysisUpdateContentParams parameters) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     Map<String, Object> files = parameters.files;
     files.forEach((String filePath, Object overlay) {
       if (overlay is AddContentOverlay) {
@@ -319,8 +305,6 @@
   /// Throw a [RequestFailure] if the request could not be handled.
   Future<CompletionGetSuggestionsResult> handleCompletionGetSuggestions(
       CompletionGetSuggestionsParams parameters) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     return CompletionGetSuggestionsResult(
         -1, -1, const <CompletionSuggestion>[]);
   }
@@ -330,8 +314,6 @@
   /// Throw a [RequestFailure] if the request could not be handled.
   Future<EditGetAssistsResult> handleEditGetAssists(
       EditGetAssistsParams parameters) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     return EditGetAssistsResult(const <PrioritizedSourceChange>[]);
   }
 
@@ -342,8 +324,6 @@
   /// Throw a [RequestFailure] if the request could not be handled.
   Future<EditGetAvailableRefactoringsResult> handleEditGetAvailableRefactorings(
       EditGetAvailableRefactoringsParams parameters) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     return EditGetAvailableRefactoringsResult(const <RefactoringKind>[]);
   }
 
@@ -352,8 +332,6 @@
   /// Throw a [RequestFailure] if the request could not be handled.
   Future<EditGetFixesResult> handleEditGetFixes(
       EditGetFixesParams parameters) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     return EditGetFixesResult(const <AnalysisErrorFixes>[]);
   }
 
@@ -362,8 +340,7 @@
   /// Throw a [RequestFailure] if the request could not be handled.
   Future<EditGetRefactoringResult> handleEditGetRefactoring(
       EditGetRefactoringParams parameters) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    return await null;
+    return null;
   }
 
   /// Handle a 'kythe.getKytheEntries' request.
@@ -371,8 +348,7 @@
   /// Throw a [RequestFailure] if the request could not be handled.
   Future<KytheGetKytheEntriesResult> handleKytheGetKytheEntries(
       KytheGetKytheEntriesParams parameters) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    return await null;
+    return null;
   }
 
   /// Handle a 'plugin.shutdown' request. Subclasses can override this method to
@@ -382,8 +358,6 @@
   /// Throw a [RequestFailure] if the request could not be handled.
   Future<PluginShutdownResult> handlePluginShutdown(
       PluginShutdownParams parameters) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     return PluginShutdownResult();
   }
 
@@ -392,8 +366,6 @@
   /// Throw a [RequestFailure] if the request could not be handled.
   Future<PluginVersionCheckResult> handlePluginVersionCheck(
       PluginVersionCheckParams parameters) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     var byteStorePath = parameters.byteStorePath;
     var sdkPath = parameters.sdkPath;
     var versionString = parameters.version;
@@ -511,8 +483,6 @@
   /// Compute the response that should be returned for the given [request], or
   /// `null` if the response has already been sent.
   Future<Response> _getResponse(Request request, int requestTime) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     ResponseResult result;
     switch (request.method) {
       case ANALYSIS_REQUEST_GET_NAVIGATION:
@@ -584,8 +554,6 @@
   /// The method that is called when a [request] is received from the analysis
   /// server.
   Future<void> _onRequest(Request request) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     var requestTime = DateTime.now().millisecondsSinceEpoch;
     var id = request.id;
     Response response;
diff --git a/pkg/analyzer_plugin/lib/src/channel/isolate_channel.dart b/pkg/analyzer_plugin/lib/src/channel/isolate_channel.dart
index 664e448..2b35cc7 100644
--- a/pkg/analyzer_plugin/lib/src/channel/isolate_channel.dart
+++ b/pkg/analyzer_plugin/lib/src/channel/isolate_channel.dart
@@ -181,8 +181,6 @@
   Future<void> listen(void Function(Response response) onResponse,
       void Function(Notification notification) onNotification,
       {void Function(dynamic error) onError, void Function() onDone}) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     if (_isolate != null) {
       throw StateError('Cannot listen to the same channel more than once.');
     }
diff --git a/pkg/analyzer_plugin/lib/utilities/completion/completion_core.dart b/pkg/analyzer_plugin/lib/utilities/completion/completion_core.dart
index 0b192c3..4c225fe 100644
--- a/pkg/analyzer_plugin/lib/utilities/completion/completion_core.dart
+++ b/pkg/analyzer_plugin/lib/utilities/completion/completion_core.dart
@@ -72,8 +72,6 @@
   /// non-fatal 'plugin.error' notification.
   Future<GeneratorResult<CompletionGetSuggestionsResult>>
       generateCompletionResponse(CompletionRequest request) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     var notifications = <Notification>[];
     var collector = CompletionCollectorImpl();
     try {
diff --git a/pkg/analyzer_plugin/lib/utilities/completion/inherited_reference_contributor.dart b/pkg/analyzer_plugin/lib/utilities/completion/inherited_reference_contributor.dart
index d18758d..bac46ee 100644
--- a/pkg/analyzer_plugin/lib/utilities/completion/inherited_reference_contributor.dart
+++ b/pkg/analyzer_plugin/lib/utilities/completion/inherited_reference_contributor.dart
@@ -34,8 +34,6 @@
   @override
   Future<void> computeSuggestions(
       DartCompletionRequest request, CompletionCollector collector) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     var target =
         CompletionTarget.forOffset(request.result.unit, request.offset);
     var optype = OpType.forCompletion(target, request.offset);
@@ -61,8 +59,6 @@
     CompletionTarget target,
     OpType optype,
   }) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     target ??= CompletionTarget.forOffset(request.result.unit, request.offset,
         entryPoint: entryPoint);
     optype ??= OpType.forCompletion(target, request.offset);
diff --git a/pkg/analyzer_plugin/lib/utilities/completion/type_member_contributor.dart b/pkg/analyzer_plugin/lib/utilities/completion/type_member_contributor.dart
index f5630f1..19b3cb0 100644
--- a/pkg/analyzer_plugin/lib/utilities/completion/type_member_contributor.dart
+++ b/pkg/analyzer_plugin/lib/utilities/completion/type_member_contributor.dart
@@ -26,8 +26,6 @@
   @override
   Future<void> computeSuggestions(
       DartCompletionRequest request, CompletionCollector collector) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     var containingLibrary = request.result.libraryElement;
     // Gracefully degrade if the library element is not resolved
     // e.g. detached part file or source change
@@ -46,8 +44,6 @@
   /// Clients should not overload this function.
   Future<void> computeSuggestionsWithEntryPoint(DartCompletionRequest request,
       CompletionCollector collector, AstNode entryPoint) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     var containingLibrary = request.result.libraryElement;
     // Gracefully degrade if the library element is not resolved
     // e.g. detached part file or source change
diff --git a/pkg/compiler/lib/src/constants/values.dart b/pkg/compiler/lib/src/constants/values.dart
index 14b1867..f33c13b 100644
--- a/pkg/compiler/lib/src/constants/values.dart
+++ b/pkg/compiler/lib/src/constants/values.dart
@@ -27,6 +27,7 @@
   INTERCEPTOR,
   JS_NAME,
   DUMMY_INTERCEPTOR,
+  LATE_SENTINEL,
   UNREACHABLE,
   INSTANTIATION,
   DEFERRED_GLOBAL,
@@ -52,6 +53,8 @@
       covariant InterceptorConstantValue constant, covariant A arg);
   R visitDummyInterceptor(
       covariant DummyInterceptorConstantValue constant, covariant A arg);
+  R visitLateSentinel(
+      covariant LateSentinelConstantValue constant, covariant A arg);
   R visitUnreachable(
       covariant UnreachableConstantValue constant, covariant A arg);
   R visitJsName(covariant JsNameConstantValue constant, covariant A arg);
@@ -894,6 +897,34 @@
   String toStructuredText(DartTypes dartTypes) => 'DummyInterceptorConstant()';
 }
 
+/// A constant used to represent the sentinel for uninitialized late fields and
+/// variables.
+class LateSentinelConstantValue extends ConstantValue {
+  factory LateSentinelConstantValue() => const LateSentinelConstantValue._();
+
+  const LateSentinelConstantValue._();
+
+  @override
+  List<ConstantValue> getDependencies() => const <ConstantValue>[];
+
+  @override
+  accept(ConstantValueVisitor visitor, arg) {
+    return visitor.visitLateSentinel(this, arg);
+  }
+
+  @override
+  DartType getType(CommonElements types) => types.dynamicType;
+
+  @override
+  ConstantValueKind get kind => ConstantValueKind.LATE_SENTINEL;
+
+  @override
+  String toDartText(DartTypes dartTypes) => 'late_sentinel()';
+
+  @override
+  String toStructuredText(DartTypes dartTypes) => 'LateSentinelConstant()';
+}
+
 // A constant with an empty type used in [HInstruction]s of an expression
 // in an unreachable context.
 class UnreachableConstantValue extends ConstantValue {
diff --git a/pkg/compiler/lib/src/inferrer/typemasks/constants.dart b/pkg/compiler/lib/src/inferrer/typemasks/constants.dart
index 935095e..7495c23 100644
--- a/pkg/compiler/lib/src/inferrer/typemasks/constants.dart
+++ b/pkg/compiler/lib/src/inferrer/typemasks/constants.dart
@@ -56,6 +56,11 @@
   }
 
   @override
+  TypeMask visitLateSentinel(
+          LateSentinelConstantValue constant, JClosedWorld closedWorld) =>
+      _abstractValueDomain.dynamicType;
+
+  @override
   TypeMask visitUnreachable(
       UnreachableConstantValue constant, JClosedWorld closedWorld) {
     return _abstractValueDomain.emptyType;
diff --git a/pkg/compiler/lib/src/js_backend/constant_emitter.dart b/pkg/compiler/lib/src/js_backend/constant_emitter.dart
index 95166e4..4d0546c 100644
--- a/pkg/compiler/lib/src/js_backend/constant_emitter.dart
+++ b/pkg/compiler/lib/src/js_backend/constant_emitter.dart
@@ -19,6 +19,7 @@
 import '../js_model/type_recipe.dart' show TypeExpressionRecipe;
 import '../options.dart';
 import 'field_analysis.dart' show JFieldAnalysis;
+import 'namer.dart';
 import 'runtime_types_new.dart' show RecipeEncoder;
 import 'runtime_types_resolution.dart';
 
@@ -31,8 +32,9 @@
 class ModularConstantEmitter
     implements ConstantValueVisitor<jsAst.Expression, Null> {
   final CompilerOptions _options;
+  final ModularNamer _namer;
 
-  ModularConstantEmitter(this._options);
+  ModularConstantEmitter(this._options, this._namer);
 
   /// Constructs a literal expression that evaluates to the constant. Uses a
   /// canonical name unless the constant can be emitted multiple times (as for
@@ -159,6 +161,10 @@
   }
 
   @override
+  jsAst.Expression visitLateSentinel(LateSentinelConstantValue constant, [_]) =>
+      js('#', _namer.staticStateHolder);
+
+  @override
   jsAst.Expression visitUnreachable(UnreachableConstantValue constant, [_]) {
     // Unreachable constants should be rare in generated code, so we use
     // `undefined` encoded as `void 1' to make them distinctive.
@@ -226,6 +232,7 @@
   /// can be inlined.
   ConstantEmitter(
       CompilerOptions options,
+      ModularNamer _namer,
       this._commonElements,
       this._elementEnvironment,
       this._rtiNeed,
@@ -234,7 +241,7 @@
       this._emitter,
       this._constantReferenceGenerator,
       this._makeConstantList)
-      : super(options);
+      : super(options, _namer);
 
   @override
   jsAst.Expression visitList(ListConstantValue constant, [_]) {
diff --git a/pkg/compiler/lib/src/js_backend/field_analysis.dart b/pkg/compiler/lib/src/js_backend/field_analysis.dart
index ac07284..8b56213 100644
--- a/pkg/compiler/lib/src/js_backend/field_analysis.dart
+++ b/pkg/compiler/lib/src/js_backend/field_analysis.dart
@@ -58,9 +58,16 @@
 
       FieldEntity fieldElement = _elementMap.getField(field);
       ir.Expression expression = field.initializer;
-      ConstantValue value = _elementMap.getConstantValue(
-          _elementMap.getStaticTypeContext(fieldElement), expression,
-          requireConstant: false, implicitNull: true);
+      ConstantValue value;
+      if (expression is ir.StaticInvocation &&
+          identical(
+              expression.target, _elementMap.coreTypes.createSentinelMethod)) {
+        value = LateSentinelConstantValue();
+      } else {
+        value = _elementMap.getConstantValue(
+            _elementMap.getStaticTypeContext(fieldElement), expression,
+            requireConstant: false, implicitNull: true);
+      }
       if (value != null && value.isConstant) {
         fieldData[fieldElement] = new AllocatorData(value);
       }
@@ -127,9 +134,16 @@
   void registerStaticField(KField field, EvaluationComplexity complexity) {
     ir.Field node = _elementMap.getMemberNode(field);
     ir.Expression expression = node.initializer;
-    ConstantValue value = _elementMap.getConstantValue(
-        _elementMap.getStaticTypeContext(field), expression,
-        requireConstant: node.isConst, implicitNull: true);
+    ConstantValue value;
+    if (expression is ir.StaticInvocation &&
+        identical(
+            expression.target, _elementMap.coreTypes.createSentinelMethod)) {
+      value = LateSentinelConstantValue();
+    } else {
+      value = _elementMap.getConstantValue(
+          _elementMap.getStaticTypeContext(field), expression,
+          requireConstant: node.isConst, implicitNull: true);
+    }
     if (value != null && !value.isConstant) {
       value = null;
     }
@@ -358,7 +372,8 @@
               } else if (value.isNull ||
                   value.isInt ||
                   value.isBool ||
-                  value.isString) {
+                  value.isString ||
+                  value is LateSentinelConstantValue) {
                 // TODO(johnniwinther,sra): Support non-primitive constants in
                 // allocators when it does cause allocators to deoptimized
                 // because of deferred loading.
diff --git a/pkg/compiler/lib/src/js_backend/namer.dart b/pkg/compiler/lib/src/js_backend/namer.dart
index da1e7f9..132100f 100644
--- a/pkg/compiler/lib/src/js_backend/namer.dart
+++ b/pkg/compiler/lib/src/js_backend/namer.dart
@@ -1910,6 +1910,11 @@
   }
 
   @override
+  void visitLateSentinel(LateSentinelConstantValue constant, [_]) {
+    add('late_sentinel');
+  }
+
+  @override
   void visitUnreachable(UnreachableConstantValue constant, [_]) {
     add('unreachable');
   }
@@ -2046,6 +2051,13 @@
   }
 
   @override
+  int visitLateSentinel(LateSentinelConstantValue constant, [_]) =>
+      throw failedAt(
+          NO_LOCATION_SPANNABLE,
+          'LateSentinelConstantValue should never be named and '
+          'never be subconstant');
+
+  @override
   int visitUnreachable(UnreachableConstantValue constant, [_]) {
     throw failedAt(
         NO_LOCATION_SPANNABLE,
diff --git a/pkg/compiler/lib/src/js_emitter/constant_ordering.dart b/pkg/compiler/lib/src/js_emitter/constant_ordering.dart
index 48281f3..9d4c41a 100644
--- a/pkg/compiler/lib/src/js_emitter/constant_ordering.dart
+++ b/pkg/compiler/lib/src/js_emitter/constant_ordering.dart
@@ -161,6 +161,11 @@
   }
 
   @override
+  int visitLateSentinel(
+          LateSentinelConstantValue a, LateSentinelConstantValue b) =>
+      0;
+
+  @override
   int visitUnreachable(UnreachableConstantValue a, UnreachableConstantValue b) {
     // Never emitted.
     return 0;
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart
index acf7e3c..ca28cdb 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart
@@ -118,7 +118,7 @@
 
   ModularEmitterImpl(
       ModularNamer namer, this._registry, CompilerOptions options)
-      : _constantEmitter = new ModularConstantEmitter(options),
+      : _constantEmitter = new ModularConstantEmitter(options, namer),
         super(namer);
 
   @override
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
index 60d8baa..e12027b 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
@@ -36,7 +36,11 @@
 import '../../common.dart';
 import '../../common/tasks.dart';
 import '../../constants/values.dart'
-    show ConstantValue, FunctionConstantValue, NullConstantValue;
+    show
+        ConstantValue,
+        FunctionConstantValue,
+        LateSentinelConstantValue,
+        NullConstantValue;
 import '../../common_elements.dart' show CommonElements, JElementEnvironment;
 import '../../deferred_load.dart' show OutputUnit;
 import '../../dump_info.dart';
@@ -88,7 +92,7 @@
   final Namer _namer;
   final CompilerTask _task;
   final Emitter _emitter;
-  ConstantEmitter _constantEmitter;
+  ConstantEmitter constantEmitter;
   final NativeEmitter _nativeEmitter;
   final bool _shouldGenerateSourceMap;
   final JClosedWorld _closedWorld;
@@ -125,8 +129,9 @@
       RecipeEncoder rtiRecipeEncoder,
       this._shouldGenerateSourceMap)
       : _constantOrdering = new ConstantOrdering(_closedWorld.sorter) {
-    this._constantEmitter = new ConstantEmitter(
+    this.constantEmitter = new ConstantEmitter(
         _options,
+        _namer,
         _closedWorld.commonElements,
         _closedWorld.elementEnvironment,
         _closedWorld.rtiNeed,
@@ -146,6 +151,7 @@
     if (constant.isFunction) return true; // Already emitted.
     if (constant.isPrimitive) return true; // Inlined.
     if (constant.isDummy) return true; // Inlined.
+    if (constant is LateSentinelConstantValue) return true; // Inlined.
     return false;
   }
 
@@ -181,7 +187,7 @@
     // We are only interested in the "isInlined" part, but it does not hurt to
     // test for the other predicates.
     if (isConstantInlinedOrAlreadyEmitted(value)) {
-      return _constantEmitter.generate(value);
+      return constantEmitter.generate(value);
     }
     return js.js('#.#',
         [_namer.globalObjectForConstant(value), _namer.constantName(value)]);
@@ -199,7 +205,7 @@
         _dumpInfoTask,
         _namer,
         _emitter,
-        _constantEmitter,
+        constantEmitter,
         this,
         _nativeEmitter,
         _closedWorld,
diff --git a/pkg/compiler/lib/src/js_model/js_world_builder.dart b/pkg/compiler/lib/src/js_model/js_world_builder.dart
index db779c3..07086db 100644
--- a/pkg/compiler/lib/src/js_model/js_world_builder.dart
+++ b/pkg/compiler/lib/src/js_model/js_world_builder.dart
@@ -891,6 +891,9 @@
           DummyInterceptorConstantValue constant, _) =>
       constant;
   @override
+  ConstantValue visitLateSentinel(LateSentinelConstantValue constant, _) =>
+      constant;
+  @override
   ConstantValue visitUnreachable(UnreachableConstantValue constant, _) =>
       constant;
   @override
diff --git a/pkg/compiler/lib/src/kernel/dart2js_target.dart b/pkg/compiler/lib/src/kernel/dart2js_target.dart
index 547be62..1bf5b01 100644
--- a/pkg/compiler/lib/src/kernel/dart2js_target.dart
+++ b/pkg/compiler/lib/src/kernel/dart2js_target.dart
@@ -74,7 +74,7 @@
   int get enabledLateLowerings => LateLowering.all;
 
   @override
-  bool get supportsLateLoweringSentinel => false;
+  bool get supportsLateLoweringSentinel => true;
 
   @override
   bool get useStaticFieldLowering => false;
diff --git a/pkg/compiler/lib/src/serialization/abstract_sink.dart b/pkg/compiler/lib/src/serialization/abstract_sink.dart
index c127c695b..1bd824c 100644
--- a/pkg/compiler/lib/src/serialization/abstract_sink.dart
+++ b/pkg/compiler/lib/src/serialization/abstract_sink.dart
@@ -538,6 +538,8 @@
         break;
       case ConstantValueKind.DUMMY_INTERCEPTOR:
         break;
+      case ConstantValueKind.LATE_SENTINEL:
+        break;
       case ConstantValueKind.UNREACHABLE:
         break;
       case ConstantValueKind.JS_NAME:
diff --git a/pkg/compiler/lib/src/serialization/abstract_source.dart b/pkg/compiler/lib/src/serialization/abstract_source.dart
index c1e8ea1..f053835 100644
--- a/pkg/compiler/lib/src/serialization/abstract_source.dart
+++ b/pkg/compiler/lib/src/serialization/abstract_source.dart
@@ -546,6 +546,8 @@
         return new DeferredGlobalConstantValue(constant, unit);
       case ConstantValueKind.DUMMY_INTERCEPTOR:
         return DummyInterceptorConstantValue();
+      case ConstantValueKind.LATE_SENTINEL:
+        return LateSentinelConstantValue();
       case ConstantValueKind.UNREACHABLE:
         return UnreachableConstantValue();
       case ConstantValueKind.JS_NAME:
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index 7e0a3af..20e835b 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -4147,6 +4147,10 @@
       _handleForeignTypeRef(invocation);
     } else if (name == 'LEGACY_TYPE_REF') {
       _handleForeignLegacyTypeRef(invocation);
+    } else if (name == 'createJsSentinel') {
+      _handleForeignCreateJsSentinel(invocation);
+    } else if (name == 'isJsSentinel') {
+      _handleForeignIsJsSentinel(invocation);
     } else {
       reporter.internalError(
           _elementMap.getSpannable(targetElement, invocation),
@@ -4863,6 +4867,22 @@
       ..sourceInformation = sourceInformation);
   }
 
+  void _handleForeignCreateJsSentinel(ir.StaticInvocation invocation) {
+    SourceInformation sourceInformation =
+        _sourceInformationBuilder.buildCall(invocation, invocation);
+    stack.add(graph.addConstantLateSentinel(closedWorld,
+        sourceInformation: sourceInformation));
+  }
+
+  void _handleForeignIsJsSentinel(ir.StaticInvocation invocation) {
+    SourceInformation sourceInformation =
+        _sourceInformationBuilder.buildCall(invocation, invocation);
+    HInstruction checkedExpression =
+        _visitPositionalArguments(invocation.arguments).single;
+    push(HIsLateSentinel(checkedExpression, _abstractValueDomain.boolType)
+      ..sourceInformation = sourceInformation);
+  }
+
   void _pushStaticInvocation(MemberEntity target, List<HInstruction> arguments,
       AbstractValue typeMask, List<DartType> typeArguments,
       {SourceInformation sourceInformation, InterfaceType instanceType}) {
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index 098531f..81f1761 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -642,7 +642,8 @@
   List<js.Expression> visitArguments(List<HInstruction> inputs,
       {int start: HInvoke.ARGUMENTS_OFFSET}) {
     assert(inputs.length >= start);
-    List<js.Expression> result = new List<js.Expression>.filled(inputs.length - start, null);
+    List<js.Expression> result =
+        new List<js.Expression>.filled(inputs.length - start, null);
     for (int i = start; i < inputs.length; i++) {
       use(inputs[i]);
       result[i - start] = pop();
@@ -2564,6 +2565,8 @@
         use(input.inputs[0]);
       } else if (input is HIdentity) {
         emitIdentityComparison(input, sourceInformation, inverse: true);
+      } else if (input is HIsLateSentinel) {
+        _emitIsLateSentinel(input, sourceInformation, inverse: true);
       } else if (canGenerateOptimizedComparison(input)) {
         HRelational relational = input;
         constant_system.BinaryOperation operation = relational.operation();
@@ -2710,7 +2713,9 @@
       assert(over != null || under != null);
       js.Expression underOver = under == null
           ? over
-          : over == null ? under : new js.Binary("||", under, over);
+          : over == null
+              ? under
+              : new js.Binary("||", under, over);
       js.Statement thenBody = new js.Block.empty();
       js.Block oldContainer = currentContainer;
       currentContainer = thenBody;
@@ -3272,4 +3277,18 @@
     _registry.registerStaticUse(
         new StaticUse.directInvoke(method, selector.callStructure, null));
   }
+
+  _emitIsLateSentinel(HIsLateSentinel node, SourceInformation sourceInformation,
+      {inverse: false}) {
+    use(node.inputs[0]);
+    js.Expression value = pop();
+    js.Expression sentinel =
+        _emitter.constantReference(LateSentinelConstantValue());
+    push(js.Binary(mapRelationalOperator('===', inverse), value, sentinel)
+        .withSourceInformation(sourceInformation));
+  }
+
+  @override
+  visitIsLateSentinel(HIsLateSentinel node) =>
+      _emitIsLateSentinel(node, node.sourceInformation);
 }
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index 25b48a8..cf71f75 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -66,6 +66,7 @@
   R visitInvokeSuper(HInvokeSuper node);
   R visitInvokeConstructorBody(HInvokeConstructorBody node);
   R visitInvokeGeneratorBody(HInvokeGeneratorBody node);
+  R visitIsLateSentinel(HIsLateSentinel node);
   R visitLateValue(HLateValue node);
   R visitLazyStatic(HLazyStatic node);
   R visitLess(HLess node);
@@ -339,6 +340,11 @@
     return addConstant(UnreachableConstantValue(), closedWorld);
   }
 
+  HConstant addConstantLateSentinel(JClosedWorld closedWorld,
+          {SourceInformation sourceInformation}) =>
+      addConstant(LateSentinelConstantValue(), closedWorld,
+          sourceInformation: sourceInformation);
+
   void finalize(AbstractValueDomain domain) {
     addBlock(exit);
     exit.open();
@@ -577,6 +583,8 @@
   @override
   visitTry(HTry node) => visitControlFlow(node);
   @override
+  visitIsLateSentinel(HIsLateSentinel node) => visitInstruction(node);
+  @override
   visitLateValue(HLateValue node) => visitInstruction(node);
   @override
   visitBoolConversion(HBoolConversion node) => visitCheck(node);
@@ -1093,6 +1101,8 @@
   static const int TYPE_EVAL_TYPECODE = 56;
   static const int TYPE_BIND_TYPECODE = 57;
 
+  static const int IS_LATE_SENTINEL_TYPECODE = 58;
+
   HInstruction(this.inputs, this.instructionType) {
     assert(inputs.every((e) => e != null), "inputs: $inputs");
   }
@@ -4513,3 +4523,25 @@
   @override
   String toString() => 'HTypeBind()';
 }
+
+class HIsLateSentinel extends HInstruction {
+  HIsLateSentinel(HInstruction value, AbstractValue type)
+      : super([value], type) {
+    setUseGvn();
+  }
+
+  @override
+  accept(HVisitor visitor) => visitor.visitIsLateSentinel(this);
+
+  @override
+  int typeCode() => HInstruction.IS_LATE_SENTINEL_TYPECODE;
+
+  @override
+  bool typeEquals(HInstruction other) => other is HIsLateSentinel;
+
+  @override
+  bool dataEquals(HIsLateSentinel other) => true;
+
+  @override
+  String toString() => 'HIsLateSentinel()';
+}
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index b855b90..5f0726f 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -1186,6 +1186,20 @@
     return newInstruction == null ? super.visitIdentity(node) : newInstruction;
   }
 
+  @override
+  HInstruction visitIsLateSentinel(HIsLateSentinel node) {
+    HInstruction value = node.inputs[0];
+    if (value is HConstant) {
+      return _graph.addConstantBool(
+          value.constant is LateSentinelConstantValue, _closedWorld);
+    }
+
+    // TODO(fishythefish): Simplify to `false` when the input cannot evalute to
+    // the sentinel. This can be implemented in the powerset domain.
+
+    return super.visitIsLateSentinel(node);
+  }
+
   void simplifyCondition(
       HBasicBlock block, HInstruction condition, bool value) {
     // `excludePhiOutEdges: true` prevents replacing a partially dominated phi
diff --git a/pkg/compiler/lib/src/ssa/ssa_tracer.dart b/pkg/compiler/lib/src/ssa/ssa_tracer.dart
index ef1ad6a..3eb079b 100644
--- a/pkg/compiler/lib/src/ssa/ssa_tracer.dart
+++ b/pkg/compiler/lib/src/ssa/ssa_tracer.dart
@@ -169,6 +169,10 @@
   }
 
   @override
+  String visitIsLateSentinel(HIsLateSentinel node) =>
+      'IsLateSentinel: ${temporaryId(node.inputs[0])}';
+
+  @override
   String visitLateValue(HLateValue node) {
     return "LateValue: ${temporaryId(node.inputs[0])}";
   }
diff --git a/pkg/compiler/test/helpers/shared_helper.dart b/pkg/compiler/test/helpers/shared_helper.dart
index 65ca216..4557ea7 100644
--- a/pkg/compiler/test/helpers/shared_helper.dart
+++ b/pkg/compiler/test/helpers/shared_helper.dart
@@ -232,37 +232,34 @@
     sb.write(')');
   }
 
+  void _unsupported(ConstantValue constant) => throw UnsupportedError(
+      'Unsupported constant value: ${constant.toStructuredText(_dartTypes)}');
+
   @override
-  void visitInterceptor(InterceptorConstantValue constant, StringBuffer sb) {
-    throw new UnsupportedError(
-        'Unsupported constant value: ${constant.toStructuredText(_dartTypes)}');
-  }
+  void visitInterceptor(InterceptorConstantValue constant, StringBuffer sb) =>
+      _unsupported(constant);
 
   @override
   void visitDummyInterceptor(
-      DummyInterceptorConstantValue constant, StringBuffer sb) {
-    throw new UnsupportedError(
-        'Unsupported constant value: ${constant.toStructuredText(_dartTypes)}');
-  }
+          DummyInterceptorConstantValue constant, StringBuffer sb) =>
+      _unsupported(constant);
 
   @override
-  void visitUnreachable(UnreachableConstantValue constant, StringBuffer sb) {
-    throw new UnsupportedError(
-        'Unsupported constant value: ${constant.toStructuredText(_dartTypes)}');
-  }
+  void visitLateSentinel(LateSentinelConstantValue constant, StringBuffer sb) =>
+      _unsupported(constant);
 
   @override
-  void visitJsName(JsNameConstantValue constant, StringBuffer sb) {
-    throw new UnsupportedError(
-        'Unsupported constant value: ${constant.toStructuredText(_dartTypes)}');
-  }
+  void visitUnreachable(UnreachableConstantValue constant, StringBuffer sb) =>
+      _unsupported(constant);
+
+  @override
+  void visitJsName(JsNameConstantValue constant, StringBuffer sb) =>
+      _unsupported(constant);
 
   @override
   void visitDeferredGlobal(
-      DeferredGlobalConstantValue constant, StringBuffer sb) {
-    throw new UnsupportedError(
-        'Unsupported constant value: ${constant.toStructuredText(_dartTypes)}');
-  }
+          DeferredGlobalConstantValue constant, StringBuffer sb) =>
+      _unsupported(constant);
 
   @override
   void visitNonConstant(NonConstantValue constant, StringBuffer sb) {
diff --git a/pkg/dartdev/test/commands/compile_test.dart b/pkg/dartdev/test/commands/compile_test.dart
index 44a3ab9..8f2e8b8 100644
--- a/pkg/dartdev/test/commands/compile_test.dart
+++ b/pkg/dartdev/test/commands/compile_test.dart
@@ -356,4 +356,145 @@
     expect(File(outFile).existsSync(), true,
         reason: 'File not found: $outFile');
   });
+
+  test('Compile AOT snapshot with sound null safety', () {
+    final p = project(mainSrc: '''void main() {}''');
+    final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
+    final outFile = path.canonicalize(path.join(p.dirPath, 'myaot'));
+
+    var result = p.runSync(
+      [
+        'compile',
+        'aot-snapshot',
+        '-o',
+        outFile,
+        inFile,
+      ],
+    );
+
+    expect(result.stdout, contains(soundNullSafetyMessage));
+    expect(result.stderr, isEmpty);
+    expect(result.exitCode, 0);
+    expect(File(outFile).existsSync(), true,
+        reason: 'File not found: $outFile');
+  });
+
+  test('Compile AOT snapshot with unsound null safety', () {
+    final p = project(mainSrc: '''
+// @dart=2.9
+void main() {}
+''');
+    final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
+    final outFile = path.canonicalize(path.join(p.dirPath, 'myaot'));
+
+    var result = p.runSync(
+      [
+        'compile',
+        'aot-snapshot',
+        '-o',
+        outFile,
+        inFile,
+      ],
+    );
+
+    expect(result.stdout, contains(unsoundNullSafetyMessage));
+    expect(result.stderr, isEmpty);
+    expect(result.exitCode, 0);
+    expect(File(outFile).existsSync(), true,
+        reason: 'File not found: $outFile');
+  });
+
+  test('Compile kernel with sound null safety', () {
+    final p = project(mainSrc: '''void main() {}''');
+    final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
+    final outFile = path.canonicalize(path.join(p.dirPath, 'mydill'));
+
+    var result = p.runSync(
+      [
+        'compile',
+        'kernel',
+        '-o',
+        outFile,
+        inFile,
+      ],
+    );
+
+    expect(result.stdout, contains(soundNullSafetyMessage));
+    expect(result.stderr, isEmpty);
+    expect(result.exitCode, 0);
+    expect(File(outFile).existsSync(), true,
+        reason: 'File not found: $outFile');
+  });
+
+  test('Compile kernel with unsound null safety', () {
+    final p = project(mainSrc: '''
+// @dart=2.9
+void main() {}
+''');
+    final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
+    final outFile = path.canonicalize(path.join(p.dirPath, 'mydill'));
+
+    var result = p.runSync(
+      [
+        'compile',
+        'kernel',
+        '-o',
+        outFile,
+        inFile,
+      ],
+    );
+
+    expect(result.stdout, contains(unsoundNullSafetyMessage));
+    expect(result.stderr, isEmpty);
+    expect(result.exitCode, 0);
+    expect(File(outFile).existsSync(), true,
+        reason: 'File not found: $outFile');
+  });
+
+  test('Compile JIT snapshot with sound null safety', () {
+    final p = project(mainSrc: '''void main() {}''');
+    final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
+    final outFile = path.canonicalize(path.join(p.dirPath, 'myjit'));
+
+    var result = p.runSync(
+      [
+        'compile',
+        'jit-snapshot',
+        '-o',
+        outFile,
+        inFile,
+      ],
+    );
+
+    expect(result.stdout, contains(soundNullSafetyMessage));
+    expect(result.stderr, isEmpty);
+    expect(result.exitCode, 0);
+    expect(File(outFile).existsSync(), true,
+        reason: 'File not found: $outFile');
+  });
+
+  test('Compile JIT snapshot with unsound null safety', () {
+    final p = project(mainSrc: '''
+// @dart=2.9
+void main() {}
+''');
+    final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
+    final outFile = path.canonicalize(path.join(p.dirPath, 'myjit'));
+
+    var result = p.runSync(
+      [
+        'compile',
+        'jit-snapshot',
+        '-o',
+        outFile,
+        inFile,
+      ],
+    );
+
+    expect(result.stdout, contains(unsoundNullSafetyMessage));
+    expect(result.stderr, isEmpty);
+    expect(result.exitCode, 0);
+    expect(File(outFile).existsSync(), true,
+        reason: 'File not found: $outFile');
+  });
 }
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index 349d3f8..db638dc 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -3989,7 +3989,12 @@
         [_visitExpression(node.iterable)]);
 
     var iter = _emitTemporaryId('iter');
-    return js.statement(
+
+    var savedContinueTargets = _currentContinueTargets;
+    var savedBreakTargets = _currentBreakTargets;
+    _currentContinueTargets = <LabeledStatement>[];
+    _currentBreakTargets = <LabeledStatement>[];
+    var awaitForStmt = js.statement(
         '{'
         '  let # = #;'
         '  try {'
@@ -4007,6 +4012,9 @@
           js_ast.Yield(js.call('#.cancel()', iter))
             ..sourceInformation = _nodeStart(node.variable)
         ]);
+    _currentContinueTargets = savedContinueTargets;
+    _currentBreakTargets = savedBreakTargets;
+    return awaitForStmt;
   }
 
   @override
@@ -5134,6 +5142,21 @@
     return js_ast.PropertyAccess(js_ast.This(), jsMethod.name);
   }
 
+  /// If [e] is a [TypeLiteral] or a [TypeLiteralConstant] expression, return
+  /// the underlying [DartType], otherwise returns null.
+  // TODO(sigmund,nshahan): remove all uses of type literals in the runtime
+  // libraries, so that this pattern can be deleted.
+  DartType getTypeLiteralType(Expression e) {
+    if (e is TypeLiteral) return e.type;
+    if (e is ConstantExpression) {
+      var constant = e.constant;
+      if (constant is TypeLiteralConstant) {
+        return constant.type.withDeclaredNullability(Nullability.nonNullable);
+      }
+    }
+    return null;
+  }
+
   @override
   js_ast.Expression visitStaticInvocation(StaticInvocation node) {
     var target = node.target;
@@ -5143,18 +5166,14 @@
     // Optimize some internal SDK calls.
     if (isSdkInternalRuntime(target.enclosingLibrary)) {
       var name = target.name.text;
-      if (node.arguments.positional.isEmpty) {
-        if (name == 'typeRep') {
-          return _emitType(node.arguments.types.single);
-        }
+      if (node.arguments.positional.isEmpty &&
+          node.arguments.types.length == 1) {
+        var type = node.arguments.types.single;
+        if (name == 'typeRep') return _emitType(type);
         if (name == 'legacyTypeRep') {
-          return _emitType(node.arguments.types.single
-              .withDeclaredNullability(Nullability.legacy));
+          return _emitType(type.withDeclaredNullability(Nullability.legacy));
         }
-      } else if (node.arguments.positional.length == 1) {
-        var firstArg = node.arguments.positional[0];
-        if (name == 'getGenericClass' && firstArg is TypeLiteral) {
-          var type = firstArg.type;
+        if (name == 'getGenericClassStatic') {
           if (type is InterfaceType) {
             return _emitTopLevelNameNoInterop(type.classNode, suffix: '\$');
           }
@@ -5162,8 +5181,11 @@
             return _emitFutureOrNameNoInterop(suffix: '\$');
           }
         }
-        if (name == 'unwrapType' && firstArg is TypeLiteral) {
-          return _emitType(firstArg.type);
+      } else if (node.arguments.positional.length == 1) {
+        var firstArg = node.arguments.positional[0];
+        var type = getTypeLiteralType(firstArg);
+        if (name == 'unwrapType' && type != null) {
+          return _emitType(type);
         }
         if (name == 'extensionSymbol' && firstArg is StringLiteral) {
           return getSymbol(getExtensionSymbolInternal(firstArg.value));
@@ -5179,19 +5201,18 @@
       } else if (node.arguments.positional.length == 2) {
         var firstArg = node.arguments.positional[0];
         var secondArg = node.arguments.positional[1];
-        if (name == '_jsInstanceOf' && secondArg is TypeLiteral) {
+        var type = getTypeLiteralType(secondArg);
+        if (name == '_jsInstanceOf' && type != null) {
           return js.call('# instanceof #', [
             _visitExpression(firstArg),
-            _emitType(
-                secondArg.type.withDeclaredNullability(Nullability.nonNullable))
+            _emitType(type.withDeclaredNullability(Nullability.nonNullable))
           ]);
         }
 
-        if (name == '_equalType' && secondArg is TypeLiteral) {
+        if (name == '_equalType' && type != null) {
           return js.call('# === #', [
             _visitExpression(firstArg),
-            _emitType(
-                secondArg.type.withDeclaredNullability(Nullability.nonNullable))
+            _emitType(type.withDeclaredNullability(Nullability.nonNullable))
           ]);
         }
       }
@@ -6018,6 +6039,26 @@
             'tearoffInterop(#)', [_emitStaticTarget(node.procedure)]);
       }
     }
+    if (node is TypeLiteralConstant) {
+      // We bypass the use of constants, since types are already canonicalized
+      // in the DDC output. DDC emits type literals in two contexts:
+      //   * Foreign JS functions: we use the non-nullable version of some types
+      //     directly in the runtime libraries (e.g. dart:_runtime). For
+      //     correctness of those libraries, we need to remove the legacy marker
+      //     that was added by the CFE normalization of type literals.
+      //
+      //   * Regular user code: we need to emit a canonicalized type. We do so
+      //     by calling `wrapType` on the type at runtime. By emitting the
+      //     non-nullable version we save some redundant work at runtime.
+      //     Technically, emitting a legacy type in this case would be correct,
+      //     only more verbose and inefficient.
+      var type = node.type;
+      if (type.nullability == Nullability.legacy) {
+        type = type.withDeclaredNullability(Nullability.nonNullable);
+      }
+      assert(!_isInForeignJS || type.nullability == Nullability.nonNullable);
+      return _emitTypeLiteral(type);
+    }
     if (isSdkInternalRuntime(_currentLibrary) || node is PrimitiveConstant) {
       return super.visitConstant(node);
     }
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index 7cd5823..3bf29c0 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -450,6 +450,11 @@
   }
 
   @override
+  void registerVariableDeclaration(VariableDeclaration variable) {
+    typeInferrer?.assignedVariables?.declare(variable);
+  }
+
+  @override
   void push(Object node) {
     if (node is DartType) {
       unhandled("DartType", "push", -1, uri);
@@ -1692,6 +1697,7 @@
       }
       VariableDeclaration variable =
           forest.createVariableDeclarationForValue(expression);
+      registerVariableDeclaration(variable);
       push(new Cascade(variable, isNullAware: isNullAware)
         ..fileOffset = expression.fileOffset);
       push(_createReadOnlyVariableAccess(variable, token, expression.fileOffset,
@@ -1725,9 +1731,15 @@
 
   @override
   void beginBinaryExpression(Token token) {
-    if (optional("&&", token) || optional("||", token)) {
+    bool isAnd = optional("&&", token);
+    if (isAnd || optional("||", token)) {
       Expression lhs = popForValue();
       typePromoter?.enterLogicalExpression(lhs, token.stringValue);
+      // This is matched by the call to [endNode] in
+      // [doLogicalExpression].
+      if (isAnd) {
+        typeInferrer?.assignedVariables?.beginNode();
+      }
       push(lhs);
     }
   }
@@ -1812,6 +1824,11 @@
         offsetForToken(token), receiver, token.stringValue, argument);
     typePromoter?.exitLogicalExpression(argument, logicalExpression);
     push(logicalExpression);
+    if (optional("&&", token)) {
+      // This is matched by the call to [beginNode] in
+      // [beginBinaryExpression].
+      typeInferrer?.assignedVariables?.endNode(logicalExpression);
+    }
   }
 
   /// Handle `a ?? b`.
@@ -2028,6 +2045,9 @@
   @override
   VariableGet createVariableGet(VariableDeclaration variable, int charOffset,
       {bool forNullGuardedAccess: false}) {
+    if (!(variable as VariableDeclarationImpl).isLocalFunction) {
+      typeInferrer?.assignedVariables?.read(variable);
+    }
     Object fact =
         typePromoter?.getFactForAccess(variable, functionNestingLevel);
     Object scope = typePromoter?.currentScope;
@@ -2395,6 +2415,9 @@
   void beginThenStatement(Token token) {
     Expression condition = popForValue();
     enterThenForTypePromotion(condition);
+    // This is matched by the call to [deferNode] in
+    // [endThenStatement].
+    typeInferrer?.assignedVariables?.beginNode();
     push(condition);
     super.beginThenStatement(token);
   }
@@ -2403,16 +2426,26 @@
   void endThenStatement(Token token) {
     typePromoter?.enterElse();
     super.endThenStatement(token);
+    // This is matched by the call to [beginNode] in
+    // [beginThenStatement] and by the call to [storeInfo] in
+    // [endIfStatement].
+    push(typeInferrer?.assignedVariables?.deferNode());
   }
 
   @override
   void endIfStatement(Token ifToken, Token elseToken) {
     Statement elsePart = popStatementIfNotNull(elseToken);
+    AssignedVariablesNodeInfo<VariableDeclaration> assignedVariablesInfo =
+        pop();
     Statement thenPart = popStatement();
     Expression condition = pop();
     typePromoter?.exitConditional();
-    push(forest.createIfStatement(
-        offsetForToken(ifToken), condition, thenPart, elsePart));
+    Statement node = forest.createIfStatement(
+        offsetForToken(ifToken), condition, thenPart, elsePart);
+    // This is matched by the call to [deferNode] in
+    // [endThenStatement].
+    typeInferrer?.assignedVariables?.storeInfo(node, assignedVariablesInfo);
+    push(node);
   }
 
   @override
@@ -3456,6 +3489,9 @@
   void beginConditionalExpression(Token question) {
     Expression condition = popForValue();
     typePromoter?.enterThen(condition);
+    // This is matched by the call to [deferNode] in
+    // [handleConditionalExpressionColon].
+    typeInferrer?.assignedVariables?.beginNode();
     push(condition);
     super.beginConditionalExpression(question);
   }
@@ -3464,6 +3500,10 @@
   void handleConditionalExpressionColon() {
     Expression then = popForValue();
     typePromoter?.enterElse();
+    // This is matched by the call to [beginNode] in
+    // [beginConditionalExpression] and by the call to [storeInfo] in
+    // [endConditionalExpression].
+    push(typeInferrer?.assignedVariables?.deferNode());
     push(then);
     super.handleConditionalExpressionColon();
   }
@@ -3473,10 +3513,16 @@
     debugEvent("ConditionalExpression");
     Expression elseExpression = popForValue();
     Expression thenExpression = pop();
+    AssignedVariablesNodeInfo<VariableDeclaration> assignedVariablesInfo =
+        pop();
     Expression condition = pop();
     typePromoter?.exitConditional();
-    push(forest.createConditionalExpression(
-        offsetForToken(question), condition, thenExpression, elseExpression));
+    Expression node = forest.createConditionalExpression(
+        offsetForToken(question), condition, thenExpression, elseExpression);
+    push(node);
+    // This is matched by the call to [deferNode] in
+    // [handleConditionalExpressionColon].
+    typeInferrer?.assignedVariables?.storeInfo(node, assignedVariablesInfo);
   }
 
   @override
@@ -4618,6 +4664,10 @@
   void handleThenControlFlow(Token token) {
     Expression condition = popForValue();
     enterThenForTypePromotion(condition);
+    // This is matched by the call to [deferNode] in
+    // [handleElseControlFlow] and by the call to [endNode] in
+    // [endIfControlFlow].
+    typeInferrer?.assignedVariables?.beginNode();
     push(condition);
     super.handleThenControlFlow(token);
   }
@@ -4628,6 +4678,10 @@
     // happens before we go into the else block.
     Object node = pop();
     if (node is! MapEntry) node = toValue(node);
+    // This is matched by the call to [beginNode] in
+    // [handleThenControlFlow] and by the call to [storeInfo] in
+    // [endIfElseControlFlow].
+    push(typeInferrer?.assignedVariables?.deferNode());
     push(node);
     typePromoter?.enterElse();
   }
@@ -4640,15 +4694,20 @@
     Token ifToken = pop();
 
     transformCollections = true;
+    TreeNode node;
     if (entry is MapEntry) {
-      push(forest.createIfMapEntry(
-          offsetForToken(ifToken), toValue(condition), entry));
+      node = forest.createIfMapEntry(
+          offsetForToken(ifToken), toValue(condition), entry);
     } else {
-      push(forest.createIfElement(
-          offsetForToken(ifToken), toValue(condition), toValue(entry)));
+      node = forest.createIfElement(
+          offsetForToken(ifToken), toValue(condition), toValue(entry));
     }
+    push(node);
     typePromoter?.enterElse();
     typePromoter?.exitConditional();
+    // This is matched by the call to [beginNode] in
+    // [handleThenControlFlow].
+    typeInferrer?.assignedVariables?.endNode(node);
   }
 
   @override
@@ -4656,72 +4715,79 @@
     debugEvent("endIfElseControlFlow");
     Object elseEntry = pop(); // else entry
     Object thenEntry = pop(); // then entry
+    AssignedVariablesNodeInfo<VariableDeclaration> assignedVariablesInfo =
+        pop();
     Object condition = pop(); // parenthesized expression
     Token ifToken = pop();
     typePromoter?.exitConditional();
 
     transformCollections = true;
+    TreeNode node;
     if (thenEntry is MapEntry) {
       if (elseEntry is MapEntry) {
-        push(forest.createIfMapEntry(
-            offsetForToken(ifToken), toValue(condition), thenEntry, elseEntry));
+        node = forest.createIfMapEntry(
+            offsetForToken(ifToken), toValue(condition), thenEntry, elseEntry);
       } else if (elseEntry is ControlFlowElement) {
         MapEntry elseMapEntry =
             elseEntry.toMapEntry(typeInferrer?.assignedVariables?.reassignInfo);
         if (elseMapEntry != null) {
-          push(forest.createIfMapEntry(offsetForToken(ifToken),
-              toValue(condition), thenEntry, elseMapEntry));
+          node = forest.createIfMapEntry(offsetForToken(ifToken),
+              toValue(condition), thenEntry, elseMapEntry);
         } else {
           int offset = elseEntry is Expression
               ? elseEntry.fileOffset
               : offsetForToken(ifToken);
-          push(new MapEntry(
+          node = new MapEntry(
               buildProblem(
                   fasta.messageCantDisambiguateAmbiguousInformation, offset, 1),
               new NullLiteral())
-            ..fileOffset = offsetForToken(ifToken));
+            ..fileOffset = offsetForToken(ifToken);
         }
       } else {
         int offset = elseEntry is Expression
             ? elseEntry.fileOffset
             : offsetForToken(ifToken);
-        push(new MapEntry(
+        node = new MapEntry(
             buildProblem(fasta.templateExpectedAfterButGot.withArguments(':'),
                 offset, 1),
             new NullLiteral())
-          ..fileOffset = offsetForToken(ifToken));
+          ..fileOffset = offsetForToken(ifToken);
       }
     } else if (elseEntry is MapEntry) {
       if (thenEntry is ControlFlowElement) {
         MapEntry thenMapEntry =
             thenEntry.toMapEntry(typeInferrer?.assignedVariables?.reassignInfo);
         if (thenMapEntry != null) {
-          push(forest.createIfMapEntry(offsetForToken(ifToken),
-              toValue(condition), thenMapEntry, elseEntry));
+          node = forest.createIfMapEntry(offsetForToken(ifToken),
+              toValue(condition), thenMapEntry, elseEntry);
         } else {
           int offset = thenEntry is Expression
               ? thenEntry.fileOffset
               : offsetForToken(ifToken);
-          push(new MapEntry(
+          node = new MapEntry(
               buildProblem(
                   fasta.messageCantDisambiguateAmbiguousInformation, offset, 1),
               new NullLiteral())
-            ..fileOffset = offsetForToken(ifToken));
+            ..fileOffset = offsetForToken(ifToken);
         }
       } else {
         int offset = thenEntry is Expression
             ? thenEntry.fileOffset
             : offsetForToken(ifToken);
-        push(new MapEntry(
+        node = new MapEntry(
             buildProblem(fasta.templateExpectedAfterButGot.withArguments(':'),
                 offset, 1),
             new NullLiteral())
-          ..fileOffset = offsetForToken(ifToken));
+          ..fileOffset = offsetForToken(ifToken);
       }
     } else {
-      push(forest.createIfElement(offsetForToken(ifToken), toValue(condition),
-          toValue(thenEntry), toValue(elseEntry)));
+      node = forest.createIfElement(offsetForToken(ifToken), toValue(condition),
+          toValue(thenEntry), toValue(elseEntry));
     }
+    push(node);
+    // This is matched by the call to [deferNode] in
+    // [handleElseControlFlow].
+    typeInferrer?.assignedVariables?.storeInfo(node, assignedVariablesInfo);
   }
 
   @override
@@ -6253,6 +6319,7 @@
     if (isNullAware) {
       VariableDeclaration variable =
           forest.createVariableDeclarationForValue(receiver);
+      registerVariableDeclaration(variable);
       return new NullAwareMethodInvocation(
           variable,
           forest.createMethodInvocation(
diff --git a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
index 24a3909..5fe56fe 100644
--- a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
@@ -407,6 +407,7 @@
     }
     VariableDeclaration read =
         _helper.forest.createVariableDeclarationForValue(_createRead());
+    _helper.registerVariableDeclaration(read);
     Expression binary = _helper.forest.createBinary(offset,
         _helper.createVariableGet(read, fileOffset), binaryOperator, value);
     VariableDeclaration write = _helper.forest
@@ -534,9 +535,11 @@
     }
     VariableDeclaration variable =
         _helper.forest.createVariableDeclarationForValue(receiver);
+    _helper.registerVariableDeclaration(variable);
     VariableDeclaration read = _helper.forest.createVariableDeclarationForValue(
         _forest.createPropertyGet(fileOffset,
             _helper.createVariableGet(variable, receiver.fileOffset), name));
+    _helper.registerVariableDeclaration(read);
     Expression binary = _helper.forest.createBinary(offset,
         _helper.createVariableGet(read, fileOffset), binaryOperator, value);
     VariableDeclaration write = _helper.forest
@@ -696,6 +699,7 @@
     VariableDeclaration read = _helper.forest.createVariableDeclarationForValue(
         _forest.createPropertyGet(
             fileOffset, _forest.createThisExpression(fileOffset), name));
+    _helper.registerVariableDeclaration(read);
     Expression binary = _helper.forest.createBinary(offset,
         _helper.createVariableGet(read, fileOffset), binaryOperator, value);
     VariableDeclaration write = _helper.forest
@@ -752,6 +756,7 @@
   Expression buildSimpleRead() {
     VariableDeclaration variable =
         _helper.forest.createVariableDeclarationForValue(receiverExpression);
+    _helper.registerVariableDeclaration(variable);
     PropertyGet read = _forest.createPropertyGet(
         fileOffset,
         _helper.createVariableGet(variable, receiverExpression.fileOffset,
@@ -765,6 +770,7 @@
   Expression buildAssignment(Expression value, {bool voidContext = false}) {
     VariableDeclaration variable =
         _helper.forest.createVariableDeclarationForValue(receiverExpression);
+    _helper.registerVariableDeclaration(variable);
     PropertySet read = _helper.forest.createPropertySet(
         fileOffset,
         _helper.createVariableGet(variable, receiverExpression.fileOffset,
@@ -899,6 +905,7 @@
     }
     VariableDeclaration read =
         _helper.forest.createVariableDeclarationForValue(_createRead());
+    _helper.registerVariableDeclaration(read);
     Expression binary = _helper.forest.createBinary(offset,
         _helper.createVariableGet(read, fileOffset), binaryOperator, value);
     VariableDeclaration write = _helper.forest
@@ -977,6 +984,7 @@
     Expression receiverValue;
     if (isNullAware) {
       variable = _forest.createVariableDeclarationForValue(receiver);
+      _helper.registerVariableDeclaration(variable);
       receiverValue = _helper.createVariableGet(variable, fileOffset,
           forNullGuardedAccess: true);
     } else {
@@ -997,6 +1005,7 @@
     Expression receiverValue;
     if (isNullAware) {
       variable = _forest.createVariableDeclarationForValue(receiver);
+      _helper.registerVariableDeclaration(variable);
       receiverValue = _helper.createVariableGet(variable, fileOffset,
           forNullGuardedAccess: true);
     } else {
@@ -1019,6 +1028,7 @@
     Expression receiverValue;
     if (isNullAware) {
       variable = _forest.createVariableDeclarationForValue(receiver);
+      _helper.registerVariableDeclaration(variable);
       receiverValue = _helper.createVariableGet(variable, fileOffset,
           forNullGuardedAccess: true);
     } else {
@@ -1047,6 +1057,7 @@
     Expression receiverValue;
     if (isNullAware) {
       variable = _forest.createVariableDeclarationForValue(receiver);
+      _helper.registerVariableDeclaration(variable);
       receiverValue = _helper.createVariableGet(variable, fileOffset,
           forNullGuardedAccess: true);
     } else {
@@ -1478,6 +1489,7 @@
     }
     VariableDeclaration read =
         _helper.forest.createVariableDeclarationForValue(_createRead());
+    _helper.registerVariableDeclaration(read);
     Expression binary = _helper.forest.createBinary(offset,
         _helper.createVariableGet(read, fileOffset), binaryOperator, value);
     VariableDeclaration write = _helper.forest
@@ -1988,6 +2000,7 @@
     if (isNullAware) {
       VariableDeclaration variable =
           _helper.forest.createVariableDeclarationForValue(receiver);
+      _helper.registerVariableDeclaration(variable);
       return new NullAwareExtension(
           variable,
           _createRead(_helper.createVariableGet(variable, variable.fileOffset,
@@ -2020,6 +2033,7 @@
     if (isNullAware) {
       VariableDeclaration variable =
           _helper.forest.createVariableDeclarationForValue(receiver);
+      _helper.registerVariableDeclaration(variable);
       return new NullAwareExtension(
           variable,
           _createWrite(
@@ -2054,6 +2068,7 @@
     if (isNullAware) {
       VariableDeclaration variable =
           _helper.forest.createVariableDeclarationForValue(receiver);
+      _helper.registerVariableDeclaration(variable);
       Expression read = _createRead(_helper.createVariableGet(
           variable, receiver.fileOffset,
           forNullGuardedAccess: true));
@@ -2140,9 +2155,11 @@
     } else {
       VariableDeclaration variable =
           _helper.forest.createVariableDeclarationForValue(receiver);
+      _helper.registerVariableDeclaration(variable);
       VariableDeclaration read = _helper.forest
           .createVariableDeclarationForValue(_createRead(
               _helper.createVariableGet(variable, receiver.fileOffset)));
+      _helper.registerVariableDeclaration(read);
       Expression binary = _helper.forest.createBinary(offset,
           _helper.createVariableGet(read, fileOffset), binaryOperator, value);
       VariableDeclaration write = _helper.forest
@@ -2163,6 +2180,7 @@
     if (isNullAware) {
       receiverVariable =
           _helper.forest.createVariableDeclarationForValue(receiver);
+      _helper.registerVariableDeclaration(receiverVariable);
       receiverExpression = _helper.createVariableGet(
           receiverVariable, receiverVariable.fileOffset,
           forNullGuardedAccess: true);
@@ -2337,6 +2355,7 @@
     Expression receiverValue;
     if (isNullAware) {
       variable = _forest.createVariableDeclarationForValue(receiver);
+      _helper.registerVariableDeclaration(variable);
       receiverValue = _helper.createVariableGet(variable, fileOffset,
           forNullGuardedAccess: true);
     } else {
@@ -2402,6 +2421,7 @@
     Expression receiverValue;
     if (isNullAware) {
       variable = _forest.createVariableDeclarationForValue(receiver);
+      _helper.registerVariableDeclaration(variable);
       receiverValue = _helper.createVariableGet(variable, fileOffset,
           forNullGuardedAccess: true);
     } else {
diff --git a/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart b/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart
index eaa2468..9a41855 100644
--- a/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart
@@ -163,4 +163,6 @@
   ///
   /// This is needed for type promotion.
   void registerVariableAssignment(VariableDeclaration variable);
+
+  void registerVariableDeclaration(VariableDeclaration variable);
 }
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 1e80ebb..d0363bb 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -1189,7 +1189,8 @@
         variableGet.promotedType = nonNullableLhsType;
       }
       ConditionalExpression conditional = new ConditionalExpression(
-          equalsNull, rhsResult.expression, variableGet, inferredType);
+          equalsNull, rhsResult.expression, variableGet, inferredType)
+        ..fileOffset = node.fileOffset;
       replacement = new Let(variable, conditional)
         ..fileOffset = node.fileOffset;
     }
@@ -5664,7 +5665,8 @@
       Expression readEqualsNull =
           inferrer.createEqualsNull(node.readOffset, read, readEqualsMember);
       replacement = new ConditionalExpression(readEqualsNull, write,
-          new NullLiteral()..fileOffset = node.writeOffset, inferredType);
+          new NullLiteral()..fileOffset = node.writeOffset, inferredType)
+        ..fileOffset = node.writeOffset;
     } else {
       // Encode `receiver?.name ??= value` as:
       //
@@ -5684,7 +5686,8 @@
         variableGet.promotedType = nonNullableReadType;
       }
       ConditionalExpression condition = new ConditionalExpression(
-          readEqualsNull, write, variableGet, inferredType);
+          readEqualsNull, write, variableGet, inferredType)
+        ..fileOffset = receiverVariable.fileOffset;
       replacement = createLet(readVariable, condition);
     }
 
diff --git a/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart b/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart
index b90df05..220157ef 100644
--- a/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart
@@ -257,8 +257,10 @@
           ? elseStatements.first
           : _createBlock(elseStatements);
     }
-    body.add(_createIf(element.fileOffset,
-        element.condition.accept<TreeNode>(this), thenBody, elseBody));
+    IfStatement ifStatement = _createIf(element.fileOffset,
+        element.condition.accept<TreeNode>(this), thenBody, elseBody);
+    _dataForTesting?.registerAlias(element, ifStatement);
+    body.add(ifStatement);
   }
 
   void _translateForElement(
@@ -510,8 +512,10 @@
       elseStatement =
           elseBody.length == 1 ? elseBody.first : _createBlock(elseBody);
     }
-    body.add(_createIf(entry.fileOffset, entry.condition.accept<TreeNode>(this),
-        thenStatement, elseStatement));
+    IfStatement ifStatement = _createIf(entry.fileOffset,
+        entry.condition.accept<TreeNode>(this), thenStatement, elseStatement);
+    _dataForTesting?.registerAlias(entry, ifStatement);
+    body.add(ifStatement);
   }
 
   void _translateForEntry(
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index ec4dd29..0b50fbb 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -4421,7 +4421,8 @@
         equalsNull,
         new NullLiteral()..fileOffset = _nullAwareFileOffset,
         nullAwareAction,
-        inferredType);
+        inferredType)
+      ..fileOffset = _nullAwareFileOffset;
     return new Let(_nullAwareVariable, condition)
       ..fileOffset = _nullAwareFileOffset;
   }
diff --git a/pkg/front_end/lib/src/testing/id_extractor.dart b/pkg/front_end/lib/src/testing/id_extractor.dart
index 11247a0..93af7c6 100644
--- a/pkg/front_end/lib/src/testing/id_extractor.dart
+++ b/pkg/front_end/lib/src/testing/id_extractor.dart
@@ -617,4 +617,16 @@
         node, computeDefaultNodeId(node, skipNodeWithNoOffset: true));
     return super.visitBlock(node);
   }
+
+  @override
+  visitConditionalExpression(ConditionalExpression node) {
+    computeForNode(node, computeDefaultNodeId(node));
+    return super.visitConditionalExpression(node);
+  }
+
+  @override
+  visitLogicalExpression(LogicalExpression node) {
+    computeForNode(node, computeDefaultNodeId(node));
+    return super.visitLogicalExpression(node);
+  }
 }
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index c06e19b..783baee 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -3853,10 +3853,10 @@
     from the import.
   analyzerCode: CONST_DEFERRED_CLASS
   script: |
-    import "dart:core" deferred as prefix;
+    import "dart:convert" deferred as prefix;
 
     main() {
-      const prefix.Object();
+      const prefix.JsonCodec();
     }
 
 CyclicRedirectingFactoryConstructors:
diff --git a/pkg/front_end/test/extensions/data/ambiguous/lib1.dart b/pkg/front_end/test/extensions/data/ambiguous/lib1.dart
index 486d104..3e96ec7 100644
--- a/pkg/front_end/test/extensions/data/ambiguous/lib1.dart
+++ b/pkg/front_end/test/extensions/data/ambiguous/lib1.dart
@@ -4,7 +4,11 @@
 
 // @dart = 2.9
 
-/*library: scope=[AmbiguousExtension1,AmbiguousExtension2,UnambiguousExtension1]*/
+/*library: scope=[
+  AmbiguousExtension1,
+  AmbiguousExtension2,
+  UnambiguousExtension1,
+  async.dart.FutureExtensions]*/
 
 /*class: AmbiguousExtension1:
  builder-name=AmbiguousExtension1,
diff --git a/pkg/front_end/test/extensions/data/ambiguous/lib2.dart b/pkg/front_end/test/extensions/data/ambiguous/lib2.dart
index 859fc4c..60dab6f 100644
--- a/pkg/front_end/test/extensions/data/ambiguous/lib2.dart
+++ b/pkg/front_end/test/extensions/data/ambiguous/lib2.dart
@@ -4,7 +4,11 @@
 
 // @dart = 2.9
 
-/*library: scope=[AmbiguousExtension1,AmbiguousExtension2,UnambiguousExtension2]*/
+/*library: scope=[
+  AmbiguousExtension1,
+  AmbiguousExtension2,
+  UnambiguousExtension2,
+  async.dart.FutureExtensions]*/
 
 /*class: AmbiguousExtension1:
  builder-name=AmbiguousExtension1,
diff --git a/pkg/front_end/test/extensions/data/ambiguous/main.dart b/pkg/front_end/test/extensions/data/ambiguous/main.dart
index 7fda5a5..76b412c 100644
--- a/pkg/front_end/test/extensions/data/ambiguous/main.dart
+++ b/pkg/front_end/test/extensions/data/ambiguous/main.dart
@@ -5,13 +5,13 @@
 // @dart = 2.9
 
 /*library: scope=[
- lib1.dart.AmbiguousExtension1,
- lib1.dart.AmbiguousExtension2,
- lib1.dart.UnambiguousExtension1,
- lib2.dart.AmbiguousExtension1,
- lib2.dart.AmbiguousExtension2,
- lib2.dart.UnambiguousExtension2]
-*/
+  async.dart.FutureExtensions,
+  lib1.dart.AmbiguousExtension1,
+  lib1.dart.AmbiguousExtension2,
+  lib1.dart.UnambiguousExtension1,
+  lib2.dart.AmbiguousExtension1,
+  lib2.dart.AmbiguousExtension2,
+  lib2.dart.UnambiguousExtension2]*/
 
 import 'lib1.dart';
 import 'lib2.dart';
diff --git a/pkg/front_end/test/extensions/data/as_show/lib.dart b/pkg/front_end/test/extensions/data/as_show/lib.dart
index 713c232..2511ba6 100644
--- a/pkg/front_end/test/extensions/data/as_show/lib.dart
+++ b/pkg/front_end/test/extensions/data/as_show/lib.dart
@@ -4,7 +4,9 @@
 
 // @dart = 2.9
 
-/*library: scope=[Extension1]*/
+/*library: scope=[
+  Extension1,
+  async.dart.FutureExtensions]*/
 
 /*class: Extension1:
  builder-name=Extension1,
diff --git a/pkg/front_end/test/extensions/data/as_show/main.dart b/pkg/front_end/test/extensions/data/as_show/main.dart
index f214d01..c65f8c0 100644
--- a/pkg/front_end/test/extensions/data/as_show/main.dart
+++ b/pkg/front_end/test/extensions/data/as_show/main.dart
@@ -4,7 +4,10 @@
 
 // @dart = 2.9
 
-/*library: scope=[lib.dart.Extension1,origin.dart.Extension2]*/
+/*library: scope=[
+  async.dart.FutureExtensions,
+  lib.dart.Extension1,
+  origin.dart.Extension2]*/
 
 import 'lib.dart' as lib1;
 import 'lib.dart' show Extension1;
diff --git a/pkg/front_end/test/extensions/data/as_show/origin.dart b/pkg/front_end/test/extensions/data/as_show/origin.dart
index 757fd60..abbeee6 100644
--- a/pkg/front_end/test/extensions/data/as_show/origin.dart
+++ b/pkg/front_end/test/extensions/data/as_show/origin.dart
@@ -4,7 +4,9 @@
 
 // @dart = 2.9
 
-/*library: scope=[Extension2]*/
+/*library: scope=[
+  Extension2,
+  async.dart.FutureExtensions]*/
 
 /*class: Extension2:
  builder-name=Extension2,
diff --git a/pkg/front_end/test/extensions/data/explicit_this.dart b/pkg/front_end/test/extensions/data/explicit_this.dart
index b56b7fc..e1fcd49 100644
--- a/pkg/front_end/test/extensions/data/explicit_this.dart
+++ b/pkg/front_end/test/extensions/data/explicit_this.dart
@@ -4,7 +4,9 @@
 
 // @dart = 2.9
 
-/*library: scope=[A2]*/
+/*library: scope=[
+  A2,
+  async.dart.FutureExtensions]*/
 
 class A1 {
   Object field;
diff --git a/pkg/front_end/test/extensions/data/export_twice/lib1.dart b/pkg/front_end/test/extensions/data/export_twice/lib1.dart
index ccfb0b9..14a21cd 100644
--- a/pkg/front_end/test/extensions/data/export_twice/lib1.dart
+++ b/pkg/front_end/test/extensions/data/export_twice/lib1.dart
@@ -4,7 +4,9 @@
 
 // @dart = 2.9
 
-/*library: scope=[E]*/
+/*library: scope=[
+  E,
+  async.dart.FutureExtensions]*/
 
 class A {}
 
diff --git a/pkg/front_end/test/extensions/data/export_twice/lib2.dart b/pkg/front_end/test/extensions/data/export_twice/lib2.dart
index f52e3cd1..e3d0760 100644
--- a/pkg/front_end/test/extensions/data/export_twice/lib2.dart
+++ b/pkg/front_end/test/extensions/data/export_twice/lib2.dart
@@ -2,6 +2,8 @@
 // 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.
 
+/*library: scope=[async.dart.FutureExtensions]*/
+
 // @dart = 2.9
 
 export 'lib1.dart' show E;
diff --git a/pkg/front_end/test/extensions/data/export_twice/main.dart b/pkg/front_end/test/extensions/data/export_twice/main.dart
index 216536b..919a7ff 100644
--- a/pkg/front_end/test/extensions/data/export_twice/main.dart
+++ b/pkg/front_end/test/extensions/data/export_twice/main.dart
@@ -4,7 +4,9 @@
 
 // @dart = 2.9
 
-/*library: scope=[lib1.dart.E]*/
+/*library: scope=[
+  async.dart.FutureExtensions,
+  lib1.dart.E]*/
 
 import 'lib1.dart';
 import 'lib2.dart';
diff --git a/pkg/front_end/test/extensions/data/export_unnamed/lib.dart b/pkg/front_end/test/extensions/data/export_unnamed/lib.dart
index 17d6f89..b6fab4b 100644
--- a/pkg/front_end/test/extensions/data/export_unnamed/lib.dart
+++ b/pkg/front_end/test/extensions/data/export_unnamed/lib.dart
@@ -4,7 +4,10 @@
 
 // @dart = 2.9
 
-/*library: scope=[NamedExtension,_extension#0]*/
+/*library: scope=[
+  NamedExtension,
+  _extension#0,
+  async.dart.FutureExtensions]*/
 
 /*class: _extension#0:
  builder-name=_extension#0,
diff --git a/pkg/front_end/test/extensions/data/export_unnamed/main.dart b/pkg/front_end/test/extensions/data/export_unnamed/main.dart
index 132f4bb..5e37c32 100644
--- a/pkg/front_end/test/extensions/data/export_unnamed/main.dart
+++ b/pkg/front_end/test/extensions/data/export_unnamed/main.dart
@@ -4,7 +4,9 @@
 
 // @dart = 2.9
 
-/*library: scope=[lib.dart.NamedExtension]*/
+/*library: scope=[
+  async.dart.FutureExtensions,
+  lib.dart.NamedExtension]*/
 
 import 'lib.dart';
 
diff --git a/pkg/front_end/test/extensions/data/extension_on_type_variable.dart b/pkg/front_end/test/extensions/data/extension_on_type_variable.dart
index c4a88ce..5f7bb41 100644
--- a/pkg/front_end/test/extensions/data/extension_on_type_variable.dart
+++ b/pkg/front_end/test/extensions/data/extension_on_type_variable.dart
@@ -4,7 +4,9 @@
 
 // @dart = 2.9
 
-/*library: scope=[GeneralGeneric]*/
+/*library: scope=[
+  GeneralGeneric,
+  async.dart.FutureExtensions]*/
 
 /*class: GeneralGeneric:
  builder-name=GeneralGeneric,
diff --git a/pkg/front_end/test/extensions/data/implicit_this.dart b/pkg/front_end/test/extensions/data/implicit_this.dart
index b50862b..733fb79 100644
--- a/pkg/front_end/test/extensions/data/implicit_this.dart
+++ b/pkg/front_end/test/extensions/data/implicit_this.dart
@@ -4,7 +4,9 @@
 
 // @dart = 2.9
 
-/*library: scope=[A2]*/
+/*library: scope=[
+  A2,
+  async.dart.FutureExtensions]*/
 
 class A1 {
   Object field;
diff --git a/pkg/front_end/test/extensions/data/instance_members.dart b/pkg/front_end/test/extensions/data/instance_members.dart
index 3b3f248..6bd7d39 100644
--- a/pkg/front_end/test/extensions/data/instance_members.dart
+++ b/pkg/front_end/test/extensions/data/instance_members.dart
@@ -4,7 +4,10 @@
 
 // @dart = 2.9
 
-/*library: scope=[A2,B2]*/
+/*library: scope=[
+  A2,
+  B2,
+  async.dart.FutureExtensions]*/
 
 class A1 {}
 
diff --git a/pkg/front_end/test/extensions/data/named_declarations.dart b/pkg/front_end/test/extensions/data/named_declarations.dart
index ecde7e2..5f7d266 100644
--- a/pkg/front_end/test/extensions/data/named_declarations.dart
+++ b/pkg/front_end/test/extensions/data/named_declarations.dart
@@ -4,7 +4,12 @@
 
 // @dart = 2.9
 
-/*library: scope=[A2,B2,B3,B4]*/
+/*library: scope=[
+  A2,
+  B2,
+  B3,
+  B4,
+  async.dart.FutureExtensions]*/
 
 class A1 {}
 
diff --git a/pkg/front_end/test/extensions/data/other_kinds.dart b/pkg/front_end/test/extensions/data/other_kinds.dart
index 289e358..d38a105 100644
--- a/pkg/front_end/test/extensions/data/other_kinds.dart
+++ b/pkg/front_end/test/extensions/data/other_kinds.dart
@@ -4,7 +4,9 @@
 
 // @dart = 2.9
 
-/*library: scope=[A2]*/
+/*library: scope=[
+  A2,
+  async.dart.FutureExtensions]*/
 
 class A1 {
   int _instanceField;
diff --git a/pkg/front_end/test/extensions/data/part/main.dart b/pkg/front_end/test/extensions/data/part/main.dart
index b014688..124b206 100644
--- a/pkg/front_end/test/extensions/data/part/main.dart
+++ b/pkg/front_end/test/extensions/data/part/main.dart
@@ -4,7 +4,8 @@
 
 /*library: scope=[
   Extension,
-  _extension#0]*/
+  _extension#0,
+  async.dart.FutureExtensions]*/
 
 part 'part.dart';
 
diff --git a/pkg/front_end/test/extensions/data/patching/main.dart b/pkg/front_end/test/extensions/data/patching/main.dart
index 692cfaa..43fab2c 100644
--- a/pkg/front_end/test/extensions/data/patching/main.dart
+++ b/pkg/front_end/test/extensions/data/patching/main.dart
@@ -4,7 +4,10 @@
 
 // @dart = 2.9
 
-/*library: scope=[origin.dart.Extension,origin.dart.GenericExtension]*/
+/*library: scope=[
+  async.dart.FutureExtensions,
+  origin.dart.Extension,
+  origin.dart.GenericExtension]*/
 
 // ignore: uri_does_not_exist
 import 'dart:test';
diff --git a/pkg/front_end/test/extensions/data/patching/origin.dart b/pkg/front_end/test/extensions/data/patching/origin.dart
index c1c0176..f11eb98 100644
--- a/pkg/front_end/test/extensions/data/patching/origin.dart
+++ b/pkg/front_end/test/extensions/data/patching/origin.dart
@@ -4,7 +4,10 @@
 
 // @dart = 2.9
 
-/*library: scope=[Extension,GenericExtension]*/
+/*library: scope=[
+  Extension,
+  GenericExtension,
+  async.dart.FutureExtensions]*/
 
 /*class: Extension:
  builder-name=Extension,
diff --git a/pkg/front_end/test/extensions/data/prefix/lib1.dart b/pkg/front_end/test/extensions/data/prefix/lib1.dart
index 3646dda..37dcd3b 100644
--- a/pkg/front_end/test/extensions/data/prefix/lib1.dart
+++ b/pkg/front_end/test/extensions/data/prefix/lib1.dart
@@ -4,7 +4,10 @@
 
 // @dart = 2.9
 
-/*library: scope=[HiddenExtension1,ShownExtension1]*/
+/*library: scope=[
+  HiddenExtension1,
+  ShownExtension1,
+  async.dart.FutureExtensions]*/
 
 /*class: ShownExtension1:
  builder-name=ShownExtension1,
diff --git a/pkg/front_end/test/extensions/data/prefix/lib2.dart b/pkg/front_end/test/extensions/data/prefix/lib2.dart
index 8affe92..6c32e13 100644
--- a/pkg/front_end/test/extensions/data/prefix/lib2.dart
+++ b/pkg/front_end/test/extensions/data/prefix/lib2.dart
@@ -4,7 +4,10 @@
 
 // @dart = 2.9
 
-/*library: scope=[HiddenExtension2,ShownExtension2]*/
+/*library: scope=[
+  HiddenExtension2,
+  ShownExtension2,
+  async.dart.FutureExtensions]*/
 
 /*class: HiddenExtension2:
  builder-name=HiddenExtension2,
diff --git a/pkg/front_end/test/extensions/data/prefix/lib3.dart b/pkg/front_end/test/extensions/data/prefix/lib3.dart
index 80105ee..a7245a0 100644
--- a/pkg/front_end/test/extensions/data/prefix/lib3.dart
+++ b/pkg/front_end/test/extensions/data/prefix/lib3.dart
@@ -4,7 +4,9 @@
 
 // @dart = 2.9
 
-/*library: scope=[ShownExtension3]*/
+/*library: scope=[
+  ShownExtension3,
+  async.dart.FutureExtensions]*/
 
 /*class: ShownExtension3:
  builder-name=ShownExtension3,
diff --git a/pkg/front_end/test/extensions/data/prefix/main.dart b/pkg/front_end/test/extensions/data/prefix/main.dart
index e988aa1..db772d9 100644
--- a/pkg/front_end/test/extensions/data/prefix/main.dart
+++ b/pkg/front_end/test/extensions/data/prefix/main.dart
@@ -5,10 +5,10 @@
 // @dart = 2.9
 
 /*library: scope=[
- lib1.dart.ShownExtension1,
- lib2.dart.ShownExtension2,
- lib3.dart.ShownExtension3]
-*/
+  async.dart.FutureExtensions,
+  lib1.dart.ShownExtension1,
+  lib2.dart.ShownExtension2,
+  lib3.dart.ShownExtension3]*/
 
 import 'lib1.dart' as lib1 show ShownExtension1;
 import 'lib2.dart' as lib2 hide HiddenExtension2;
diff --git a/pkg/front_end/test/extensions/data/reexport/lib.dart b/pkg/front_end/test/extensions/data/reexport/lib.dart
index b787d79..c250d6a 100644
--- a/pkg/front_end/test/extensions/data/reexport/lib.dart
+++ b/pkg/front_end/test/extensions/data/reexport/lib.dart
@@ -2,6 +2,8 @@
 // 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.
 
+/*library: scope=[async.dart.FutureExtensions]*/
+
 // @dart = 2.9
 
 export 'lib1.dart';
diff --git a/pkg/front_end/test/extensions/data/reexport/lib1.dart b/pkg/front_end/test/extensions/data/reexport/lib1.dart
index da11a1f..ecf38c5 100644
--- a/pkg/front_end/test/extensions/data/reexport/lib1.dart
+++ b/pkg/front_end/test/extensions/data/reexport/lib1.dart
@@ -4,7 +4,10 @@
 
 // @dart = 2.9
 
-/*library: scope=[ClashingExtension,UniqueExtension1]*/
+/*library: scope=[
+  ClashingExtension,
+  UniqueExtension1,
+  async.dart.FutureExtensions]*/
 
 /*class: ClashingExtension:
  builder-name=ClashingExtension,
diff --git a/pkg/front_end/test/extensions/data/reexport/lib2.dart b/pkg/front_end/test/extensions/data/reexport/lib2.dart
index e30d364..56e8c50 100644
--- a/pkg/front_end/test/extensions/data/reexport/lib2.dart
+++ b/pkg/front_end/test/extensions/data/reexport/lib2.dart
@@ -4,7 +4,10 @@
 
 // @dart = 2.9
 
-/*library: scope=[ClashingExtension,UniqueExtension2]*/
+/*library: scope=[
+  ClashingExtension,
+  UniqueExtension2,
+  async.dart.FutureExtensions]*/
 
 /*class: ClashingExtension:
  builder-name=ClashingExtension,
diff --git a/pkg/front_end/test/extensions/data/reexport/main.dart b/pkg/front_end/test/extensions/data/reexport/main.dart
index 1e06107..7238499 100644
--- a/pkg/front_end/test/extensions/data/reexport/main.dart
+++ b/pkg/front_end/test/extensions/data/reexport/main.dart
@@ -4,7 +4,10 @@
 
 // @dart = 2.9
 
-/*library: scope=[lib1.dart.UniqueExtension1,lib2.dart.UniqueExtension2]*/
+/*library: scope=[
+  async.dart.FutureExtensions,
+  lib1.dart.UniqueExtension1,
+  lib2.dart.UniqueExtension2]*/
 
 import 'lib.dart';
 
diff --git a/pkg/front_end/test/extensions/data/show_hide/lib1.dart b/pkg/front_end/test/extensions/data/show_hide/lib1.dart
index 3646dda..37dcd3b 100644
--- a/pkg/front_end/test/extensions/data/show_hide/lib1.dart
+++ b/pkg/front_end/test/extensions/data/show_hide/lib1.dart
@@ -4,7 +4,10 @@
 
 // @dart = 2.9
 
-/*library: scope=[HiddenExtension1,ShownExtension1]*/
+/*library: scope=[
+  HiddenExtension1,
+  ShownExtension1,
+  async.dart.FutureExtensions]*/
 
 /*class: ShownExtension1:
  builder-name=ShownExtension1,
diff --git a/pkg/front_end/test/extensions/data/show_hide/lib2.dart b/pkg/front_end/test/extensions/data/show_hide/lib2.dart
index 8affe92..6c32e13 100644
--- a/pkg/front_end/test/extensions/data/show_hide/lib2.dart
+++ b/pkg/front_end/test/extensions/data/show_hide/lib2.dart
@@ -4,7 +4,10 @@
 
 // @dart = 2.9
 
-/*library: scope=[HiddenExtension2,ShownExtension2]*/
+/*library: scope=[
+  HiddenExtension2,
+  ShownExtension2,
+  async.dart.FutureExtensions]*/
 
 /*class: HiddenExtension2:
  builder-name=HiddenExtension2,
diff --git a/pkg/front_end/test/extensions/data/show_hide/main.dart b/pkg/front_end/test/extensions/data/show_hide/main.dart
index 622bf60..60ba8f4 100644
--- a/pkg/front_end/test/extensions/data/show_hide/main.dart
+++ b/pkg/front_end/test/extensions/data/show_hide/main.dart
@@ -4,7 +4,10 @@
 
 // @dart = 2.9
 
-/*library: scope=[lib1.dart.ShownExtension1,lib2.dart.ShownExtension2]*/
+/*library: scope=[
+  async.dart.FutureExtensions,
+  lib1.dart.ShownExtension1,
+  lib2.dart.ShownExtension2]*/
 
 import 'lib1.dart' show ShownExtension1;
 import 'lib2.dart' hide HiddenExtension2;
diff --git a/pkg/front_end/test/extensions/data/static_members.dart b/pkg/front_end/test/extensions/data/static_members.dart
index f11b66d..eab1942 100644
--- a/pkg/front_end/test/extensions/data/static_members.dart
+++ b/pkg/front_end/test/extensions/data/static_members.dart
@@ -4,7 +4,10 @@
 
 // @dart = 2.9
 
-/*library: scope=[A2,B2]*/
+/*library: scope=[
+  A2,
+  B2,
+  async.dart.FutureExtensions]*/
 
 class A1 {}
 
diff --git a/pkg/front_end/test/extensions/data/super.dart b/pkg/front_end/test/extensions/data/super.dart
index 0123d92..df22e8f 100644
--- a/pkg/front_end/test/extensions/data/super.dart
+++ b/pkg/front_end/test/extensions/data/super.dart
@@ -4,7 +4,9 @@
 
 // @dart = 2.9
 
-/*library: scope=[A2]*/
+/*library: scope=[
+  A2,
+  async.dart.FutureExtensions]*/
 
 class A1 {
   method1() {}
diff --git a/pkg/front_end/test/extensions/data/type_variables.dart b/pkg/front_end/test/extensions/data/type_variables.dart
index e04f82d..8153930 100644
--- a/pkg/front_end/test/extensions/data/type_variables.dart
+++ b/pkg/front_end/test/extensions/data/type_variables.dart
@@ -4,7 +4,11 @@
 
 // @dart = 2.9
 
-/*library: scope=[A2,A3,A4]*/
+/*library: scope=[
+  A2,
+  A3,
+  A4,
+  async.dart.FutureExtensions]*/
 
 class A1<T> {}
 
diff --git a/pkg/front_end/test/extensions/data/unnamed_declarations.dart b/pkg/front_end/test/extensions/data/unnamed_declarations.dart
index 7577c5f..ffa10b4 100644
--- a/pkg/front_end/test/extensions/data/unnamed_declarations.dart
+++ b/pkg/front_end/test/extensions/data/unnamed_declarations.dart
@@ -4,7 +4,13 @@
 
 // @dart = 2.9
 
-/*library: scope=[_extension#0,_extension#1,_extension#2,_extension#3,_extension#4]*/
+/*library: scope=[
+  _extension#0,
+  _extension#1,
+  _extension#2,
+  _extension#3,
+  _extension#4,
+  async.dart.FutureExtensions]*/
 
 class A1 {}
 
diff --git a/pkg/front_end/test/extensions/data/use_as_type.dart b/pkg/front_end/test/extensions/data/use_as_type.dart
index b928105..5cdc007 100644
--- a/pkg/front_end/test/extensions/data/use_as_type.dart
+++ b/pkg/front_end/test/extensions/data/use_as_type.dart
@@ -4,7 +4,10 @@
 
 // @dart = 2.9
 
-/*library: scope=[A2,B2]*/
+/*library: scope=[
+  A2,
+  B2,
+  async.dart.FutureExtensions]*/
 
 class A1 {}
 
diff --git a/pkg/front_end/test/id_tests/assigned_variables_test.dart b/pkg/front_end/test/id_tests/assigned_variables_test.dart
index b0a7d7a..c74e39b 100644
--- a/pkg/front_end/test/id_tests/assigned_variables_test.dart
+++ b/pkg/front_end/test/id_tests/assigned_variables_test.dart
@@ -73,6 +73,8 @@
   _Data computeMemberValue(Id id, Member member) {
     return new _Data(
         _convertVars(_assignedVariables.declaredAtTopLevel),
+        _convertVars(_assignedVariables.readAnywhere),
+        _convertVars(_assignedVariables.readCapturedAnywhere),
         _convertVars(_assignedVariables.writtenAnywhere),
         _convertVars(_assignedVariables.capturedAnywhere));
   }
@@ -93,6 +95,8 @@
     if (!_assignedVariables.isTracked(alias)) return null;
     return new _Data(
         _convertVars(_assignedVariables.declaredInNode(alias)),
+        _convertVars(_assignedVariables.readInNode(alias)),
+        _convertVars(_assignedVariables.readCapturedInNode(alias)),
         _convertVars(_assignedVariables.writtenInNode(alias)),
         _convertVars(_assignedVariables.capturedInNode(alias)));
   }
@@ -107,6 +111,12 @@
     if (actualData.declared.isNotEmpty) {
       parts.add('declared=${_setToString(actualData.declared)}');
     }
+    if (actualData.read.isNotEmpty) {
+      parts.add('read=${_setToString(actualData.read)}');
+    }
+    if (actualData.readCaptured.isNotEmpty) {
+      parts.add('readCaptured=${_setToString(actualData.readCaptured)}');
+    }
     if (actualData.assigned.isNotEmpty) {
       parts.add('assigned=${_setToString(actualData.assigned)}');
     }
@@ -140,9 +150,14 @@
 class _Data {
   final Set<String> declared;
 
+  final Set<String> read;
+
+  final Set<String> readCaptured;
+
   final Set<String> assigned;
 
   final Set<String> captured;
 
-  _Data(this.declared, this.assigned, this.captured);
+  _Data(this.declared, this.read, this.readCaptured, this.assigned,
+      this.captured);
 }
diff --git a/pkg/front_end/test/static_types/data/if_null.dart b/pkg/front_end/test/static_types/data/if_null.dart
index 2f38469..c4e18b6 100644
--- a/pkg/front_end/test/static_types/data/if_null.dart
+++ b/pkg/front_end/test/static_types/data/if_null.dart
@@ -12,10 +12,11 @@
   String? foo() => /*Null*/ null;
   String bar() => /*String!*/ "bar";
 
-  var s = /*invoke: String?*/ foo() ?? /*invoke: String!*/ bar();
+  var s = /*invoke: String?*/ foo() /*String!*/ ?? /*invoke: String!*/ bar();
   /*String!*/ s;
 
-  String s2 = /*invoke: String?*/ hest1/*<String?>*/() ?? /*String!*/ "fisk";
+  String
+      s2 = /*invoke: String?*/ hest1/*<String?>*/() /*String!*/ ?? /*String!*/ "fisk";
 }
 
 // ------------------------- If-null property set. -----------------------------
@@ -24,22 +25,24 @@
 }
 
 test2(A2 a) {
-  var s =
-      (/*A2!*/ a. /*String?*/ /*update: String!*/ foo ??= /*String!*/ "bar");
+  var s = (/*A2!*/ a
+      . /*String?*/ /*update: String!*/ foo /*String!*/ ??= /*String!*/ "bar");
   /*String!*/ s;
 }
 
 // ------------------------------ If-null set. ---------------------------------
 test3() {
   String? s = /*Null*/ null;
-  var s2 = (/*String?*/ /*update: String!*/ s ??= /*String!*/ "bar");
+  var s2 =
+      (/*String?*/ /*update: String!*/ s /*String!*/ ??= /*String!*/ "bar");
   /*String!*/ s2;
 }
 
 // --------------------------- If-null index set. ------------------------------
 test4() {
   List<String?> list = /*List<String?>!*/ [/*Null*/ null];
-  var s = (/*List<String?>!*/ list /*String?*/ /*update: void*/ [/*int!*/ 0] ??=
+  var s = (/*List<String?>!*/ list /*String?*/ /*update: void*/ [
+          /*int!*/ 0] /*String!*/ ??=
       /*String!*/ "bar");
   /*String!*/ s;
 }
@@ -52,7 +55,7 @@
 
 class B5 extends A5 {
   test5() {
-    var s = (super[/*int!*/ 0] ??= /*String!*/ "bar");
+    var s = (super[/*int!*/ 0] /*String!*/ ??= /*String!*/ "bar");
     /*String!*/ s;
   }
 }
@@ -65,7 +68,7 @@
 
 test6() {
   var s = (E6(/*double!*/ 3.14) /*invoke: String?|void*/ [
-      /*int!*/ 0] ??= /*String!*/ "bar");
+      /*int!*/ 0] /*String!*/ ??= /*String!*/ "bar");
   /*String!*/ s;
 }
 
@@ -76,12 +79,12 @@
 }
 
 test7(A7? a) {
-  var s =
-      (/*A7?*/ a?. /*String!*/ /*update: String!*/ foo ??= /*String!*/ "bar");
+  var s = (/*A7?|String?|String!*/ a
+      ?. /*String!*/ /*update: String!*/ foo ??= /*String!*/ "bar");
   /*String?*/ s;
 
-  var s2 =
-      (/*A7?*/ a?. /*String?*/ /*update: String!*/ bar ??= /*String!*/ "bar");
+  var s2 = (/*A7?|String?|String!*/ a
+      ?. /*String?*/ /*update: String!*/ bar ??= /*String!*/ "bar");
   /*String?*/ s2;
 }
 
diff --git a/pkg/front_end/test/static_types/data/null_aware_for_in.dart b/pkg/front_end/test/static_types/data/null_aware_for_in.dart
index 48f0917..42c998b 100644
--- a/pkg/front_end/test/static_types/data/null_aware_for_in.dart
+++ b/pkg/front_end/test/static_types/data/null_aware_for_in.dart
@@ -17,5 +17,5 @@
       Class c in
       /*cfe.as: Iterable<dynamic>*/
       /*cfe:nnbd.as: Iterable<dynamic>!*/
-      /*dynamic*/ o?. /*dynamic*/ iterable) {}
+      /*dynamic|dynamic*/ o?. /*dynamic*/ iterable) {}
 }
diff --git a/pkg/front_end/test/static_types/data/promoted_access.dart b/pkg/front_end/test/static_types/data/promoted_access.dart
index 3e87292..82085cb 100644
--- a/pkg/front_end/test/static_types/data/promoted_access.dart
+++ b/pkg/front_end/test/static_types/data/promoted_access.dart
@@ -13,9 +13,9 @@
       /*cfe.T & Class<dynamic>*/
       /*cfe:nnbd.T! & Class<dynamic>!*/
       o. /*invoke: dynamic*/ method(/*Null*/ null);
-      /*cfe.T & Class<dynamic>*/ /*cfe:nnbd.T! & Class<dynamic>!*/ o
+      /*cfe.T & Class<dynamic>|dynamic*/ /*cfe:nnbd.T! & Class<dynamic>!|dynamic*/ o
           ?. /*invoke: dynamic*/ method(/*Null*/ null);
-      /*cfe.T & Class<dynamic>*/ /*cfe:nnbd.T! & Class<dynamic>!*/ o
+      /*cfe.T & Class<dynamic>|dynamic*/ /*cfe:nnbd.T! & Class<dynamic>!|dynamic*/ o
           ?. /*dynamic*/ property;
     }
   }
@@ -26,9 +26,9 @@
     /*cfe.T & Class<dynamic>*/
     /*cfe:nnbd.T! & Class<dynamic>!*/
     o. /*invoke: dynamic*/ method(/*Null*/ null);
-    /*cfe.T & Class<dynamic>*/ /*cfe:nnbd.T! & Class<dynamic>!*/ o
+    /*cfe.T & Class<dynamic>|dynamic*/ /*cfe:nnbd.T! & Class<dynamic>!|dynamic*/ o
         ?. /*invoke: dynamic*/ method(/*Null*/ null);
-    /*cfe.T & Class<dynamic>*/ /*cfe:nnbd.T! & Class<dynamic>!*/ o
+    /*cfe.T & Class<dynamic>|dynamic*/ /*cfe:nnbd.T! & Class<dynamic>!|dynamic*/ o
         ?. /*dynamic*/ property;
   }
 }
diff --git a/pkg/nnbd_migration/lib/src/edge_builder.dart b/pkg/nnbd_migration/lib/src/edge_builder.dart
index 5f30b51..c074a4a 100644
--- a/pkg/nnbd_migration/lib/src/edge_builder.dart
+++ b/pkg/nnbd_migration/lib/src/edge_builder.dart
@@ -2214,8 +2214,12 @@
       }
       return type;
     } catch (exception, stackTrace) {
-      listener.reportException(source, node, exception, stackTrace);
-      return null;
+      if (listener != null) {
+        listener.reportException(source, node, exception, stackTrace);
+        return null;
+      } else {
+        rethrow;
+      }
     }
   }
 
@@ -3038,16 +3042,16 @@
     }
   }
 
-  DecoratedType _handleTarget(Expression target, String name, Element method) {
+  DecoratedType _handleTarget(Expression target, String name, Element callee) {
     if (isDeclaredOnObject(name)) {
       return _dispatch(target);
-    } else if (method is MethodElement &&
-        method.enclosingElement is ExtensionElement) {
+    } else if ((callee is MethodElement || callee is PropertyAccessorElement) &&
+        callee.enclosingElement is ExtensionElement) {
       // Extension methods can be called on a `null` target, when the `on` type
       // of the extension is nullable.
       return _handleAssignment(target,
           destinationType:
-              _variables.decoratedElementType(method.enclosingElement));
+              _variables.decoratedElementType(callee.enclosingElement));
     } else {
       return _checkExpressionNotNull(target);
     }
diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart
index 7089d06..97bac6d 100644
--- a/pkg/nnbd_migration/test/api_test.dart
+++ b/pkg/nnbd_migration/test/api_test.dart
@@ -2289,7 +2289,6 @@
     await _checkSingleFileChanges(content, expected);
   }
 
-  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/42529')
   Future<void> test_extension_nullable_target() async {
     var content = '''
 extension E on int {
@@ -2507,7 +2506,6 @@
     await _checkSingleFileChanges(content, expected);
   }
 
-  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/42529')
   Future<void> test_extension_override_nullable_target() async {
     var content = '''
 extension E on int {
diff --git a/pkg/nnbd_migration/test/edge_builder_test.dart b/pkg/nnbd_migration/test/edge_builder_test.dart
index 9558d4e..e50b091 100644
--- a/pkg/nnbd_migration/test/edge_builder_test.dart
+++ b/pkg/nnbd_migration/test/edge_builder_test.dart
@@ -6473,6 +6473,38 @@
     expect(hasNullCheckHint(findNode.prefixed('m.pi/*!*/')), isTrue);
   }
 
+  Future<void> test_prefixedIdentifier_extension_nullTarget_get() async {
+    await analyze('''
+class C {}
+extension on C /*1*/ {
+  int get x => 0;
+}
+void f() {
+  C c = null;
+  c.x;
+}
+''');
+    assertEdge(decoratedTypeAnnotation('C c').node,
+        decoratedTypeAnnotation('C /*1*/').node,
+        hard: true);
+  }
+
+  Future<void> test_prefixedIdentifier_extension_nullTarget_set() async {
+    await analyze('''
+class C {}
+extension on C /*1*/ {
+  set x(int value) {}
+}
+void f() {
+  C c = null;
+  c.x = 0;
+}
+''');
+    assertEdge(decoratedTypeAnnotation('C c').node,
+        decoratedTypeAnnotation('C /*1*/').node,
+        hard: true);
+  }
+
   Future<void> test_prefixedIdentifier_field_type() async {
     await analyze('''
 class C {
@@ -6802,6 +6834,70 @@
         hard: false);
   }
 
+  Future<void> test_propertyAccess_extension_nullTarget_get() async {
+    await analyze('''
+class C {}
+extension on C /*1*/ {
+  int get x => 0;
+}
+void f() {
+  C g() => null;
+  g().x;
+}
+''');
+    assertEdge(decoratedTypeAnnotation('C g()').node,
+        decoratedTypeAnnotation('C /*1*/').node,
+        hard: false);
+  }
+
+  Future<void> test_propertyAccess_extension_nullTarget_get_explicit() async {
+    await analyze('''
+class C {}
+extension E on C /*1*/ {
+  int get x => 0;
+}
+void f() {
+  C g() => null;
+  E(g()).x;
+}
+''');
+    assertEdge(decoratedTypeAnnotation('C g()').node,
+        decoratedTypeAnnotation('C /*1*/').node,
+        hard: false);
+  }
+
+  Future<void> test_propertyAccess_extension_nullTarget_set() async {
+    await analyze('''
+class C {}
+extension on C /*1*/ {
+  set x(int value) {}
+}
+void f() {
+  C g() => null;
+  g().x = 0;
+}
+''');
+    assertEdge(decoratedTypeAnnotation('C g()').node,
+        decoratedTypeAnnotation('C /*1*/').node,
+        hard: false);
+  }
+
+  Future<void> test_propertyAccess_extension_nullTarget_set_explicit() async {
+    await analyze('''
+class C {}
+extension E on C /*1*/ {
+  set x(int value) {}
+}
+void f() {
+  C g() => null;
+  E(g()).x = 0;
+}
+''');
+    assertEdge(decoratedTypeAnnotation('C g()').node,
+        decoratedTypeAnnotation('C /*1*/').node,
+        hard: false);
+  }
+
   Future<void> test_propertyAccess_object_property() async {
     await analyze('''
 int f(int i) => i.hashCode;
diff --git a/pkg/vm/bin/kernel_service.dart b/pkg/vm/bin/kernel_service.dart
index a0fc1de..5f226e7 100644
--- a/pkg/vm/bin/kernel_service.dart
+++ b/pkg/vm/bin/kernel_service.dart
@@ -23,7 +23,9 @@
 import 'dart:async' show Future, ZoneSpecification, runZoned;
 import 'dart:collection' show UnmodifiableMapBase;
 import 'dart:convert' show utf8;
-import 'dart:io' show Directory, File, Platform, stderr hide FileSystemEntity;
+import 'dart:io'
+    show Directory, File, Platform, stderr, stdout
+    hide FileSystemEntity;
 import 'dart:isolate';
 import 'dart:typed_data' show Uint8List;
 
@@ -92,7 +94,8 @@
     int nullSafety,
     List<String> experimentalFlags,
     Uri packagesUri,
-    List<String> errors) {
+    List<String> errors,
+    String invocationModes) {
   final expFlags = <String>[];
   if (experimentalFlags != null) {
     for (String flag in experimentalFlags) {
@@ -116,27 +119,33 @@
         ? NnbdMode.Strong
         : NnbdMode.Weak
     ..onDiagnostic = (DiagnosticMessage message) {
-      bool printMessage;
+      bool printToStdErr = false;
+      bool printToStdOut = false;
       switch (message.severity) {
         case Severity.error:
         case Severity.internalProblem:
           // TODO(sigmund): support emitting code with errors as long as they
           // are handled in the generated code.
-          printMessage = false; // errors are printed by VM
+          printToStdErr = false; // errors are printed by VM
           errors.addAll(message.plainTextFormatted);
           break;
         case Severity.warning:
+          printToStdErr = !suppressWarnings;
+          break;
         case Severity.info:
-          printMessage = !suppressWarnings;
+          printToStdOut = !suppressWarnings;
           break;
         case Severity.context:
         case Severity.ignored:
           throw "Unexpected severity: ${message.severity}";
       }
-      if (printMessage) {
+      if (printToStdErr) {
         printDiagnosticMessage(message, stderr.writeln);
+      } else if (printToStdOut) {
+        printDiagnosticMessage(message, stdout.writeln);
       }
-    };
+    }
+    ..invocationModes = InvocationMode.parseArguments(invocationModes);
 }
 
 abstract class Compiler {
@@ -148,6 +157,7 @@
   final int nullSafety;
   final List<String> experimentalFlags;
   final String packageConfig;
+  final String invocationModes;
 
   // Code coverage and hot reload are only supported by incremental compiler,
   // which is used if vm-service is enabled.
@@ -165,7 +175,8 @@
       this.experimentalFlags: null,
       this.supportCodeCoverage: false,
       this.supportHotReload: false,
-      this.packageConfig: null}) {
+      this.packageConfig: null,
+      this.invocationModes: ''}) {
     Uri packagesUri = null;
     if (packageConfig != null) {
       packagesUri = Uri.parse(packageConfig);
@@ -188,7 +199,8 @@
         nullSafety,
         experimentalFlags,
         packagesUri,
-        errors);
+        errors,
+        invocationModes);
   }
 
   Future<CompilerResult> compile(Uri script) {
@@ -278,7 +290,8 @@
       bool enableAsserts: false,
       int nullSafety: kNullSafetyOptionUnspecified,
       List<String> experimentalFlags: null,
-      String packageConfig: null})
+      String packageConfig: null,
+      String invocationModes: ''})
       : super(isolateId, fileSystem, platformKernelPath,
             suppressWarnings: suppressWarnings,
             enableAsserts: enableAsserts,
@@ -286,7 +299,8 @@
             experimentalFlags: experimentalFlags,
             supportHotReload: true,
             supportCodeCoverage: true,
-            packageConfig: packageConfig);
+            packageConfig: packageConfig,
+            invocationModes: invocationModes);
 
   factory IncrementalCompilerWrapper.forExpressionCompilationOnly(
       Component component,
@@ -296,13 +310,15 @@
       {bool suppressWarnings: false,
       bool enableAsserts: false,
       List<String> experimentalFlags: null,
-      String packageConfig: null}) {
+      String packageConfig: null,
+      String invocationModes: ''}) {
     IncrementalCompilerWrapper result = IncrementalCompilerWrapper(
         isolateId, fileSystem, platformKernelPath,
         suppressWarnings: suppressWarnings,
         enableAsserts: enableAsserts,
         experimentalFlags: experimentalFlags,
-        packageConfig: packageConfig);
+        packageConfig: packageConfig,
+        invocationModes: invocationModes);
     result.generator = new IncrementalCompiler.forExpressionCompilationOnly(
         component,
         result.options,
@@ -331,7 +347,8 @@
         enableAsserts: enableAsserts,
         nullSafety: nullSafety,
         experimentalFlags: experimentalFlags,
-        packageConfig: packageConfig);
+        packageConfig: packageConfig,
+        invocationModes: invocationModes);
 
     generator.resetDeltaState();
     Component fullComponent = await generator.compile();
@@ -361,13 +378,15 @@
       bool enableAsserts: false,
       int nullSafety: kNullSafetyOptionUnspecified,
       List<String> experimentalFlags: null,
-      String packageConfig: null})
+      String packageConfig: null,
+      String invocationModes: ''})
       : super(isolateId, fileSystem, platformKernelPath,
             suppressWarnings: suppressWarnings,
             enableAsserts: enableAsserts,
             nullSafety: nullSafety,
             experimentalFlags: experimentalFlags,
-            packageConfig: packageConfig);
+            packageConfig: packageConfig,
+            invocationModes: invocationModes);
 
   @override
   Future<CompilerResult> compileInternal(Uri script) async {
@@ -403,7 +422,8 @@
     List<String> experimentalFlags: null,
     String packageConfig: null,
     String multirootFilepaths,
-    String multirootScheme}) async {
+    String multirootScheme,
+    String invocationModes: ''}) async {
   IncrementalCompilerWrapper compiler = lookupIncrementalCompiler(isolateId);
   if (compiler != null) {
     updateSources(compiler, sourceFiles);
@@ -432,7 +452,8 @@
           enableAsserts: enableAsserts,
           nullSafety: nullSafety,
           experimentalFlags: experimentalFlags,
-          packageConfig: packageConfig);
+          packageConfig: packageConfig,
+          invocationModes: invocationModes);
     }
     isolateCompilers[isolateId] = compiler;
   }
@@ -652,10 +673,7 @@
   return utf8.encode(uris.map(_escapeDependency).join(" "));
 }
 
-Future _processListDependenciesRequest(request) async {
-  final SendPort port = request[1];
-  final int isolateId = request[6];
-
+Future _processListDependenciesRequest(SendPort port, int isolateId) async {
   final List<Uri> dependencies = isolateDependencies[isolateId] ?? <Uri>[];
 
   CompilationResult result;
@@ -709,32 +727,34 @@
     return;
   }
 
-  if (tag == kListDependenciesTag) {
-    await _processListDependenciesRequest(request);
-    return;
-  }
-
   if (tag == kNotifyIsolateShutdownTag) {
     await _processIsolateShutdownNotification(request);
     return;
   }
 
   final SendPort port = request[1];
+  final int isolateId = request[7];
+
+  if (tag == kListDependenciesTag) {
+    await _processListDependenciesRequest(port, isolateId);
+    return;
+  }
+
   final String inputFileUri = request[2];
   final Uri script =
       inputFileUri != null ? Uri.base.resolve(inputFileUri) : null;
   final bool incremental = request[4];
-  final int nullSafety = request[5];
-  final int isolateId = request[6];
-  final List sourceFiles = request[7];
-  final bool suppressWarnings = request[8];
-  final bool enableAsserts = request[9];
+  final bool snapshot = request[5];
+  final int nullSafety = request[6];
+  final List sourceFiles = request[8];
+  final bool suppressWarnings = request[9];
+  final bool enableAsserts = request[10];
   final List<String> experimentalFlags =
-      request[10] != null ? request[10].cast<String>() : null;
-  final String packageConfig = request[11];
-  final String multirootFilepaths = request[12];
-  final String multirootScheme = request[13];
-  final String workingDirectory = request[14];
+      request[11] != null ? request[11].cast<String>() : null;
+  final String packageConfig = request[12];
+  final String multirootFilepaths = request[13];
+  final String multirootScheme = request[14];
+  final String workingDirectory = request[15];
 
   Uri platformKernelPath = null;
   List<int> platformKernel = null;
@@ -748,6 +768,8 @@
         computePlatformBinariesLocation().resolve('vm_platform_strong.dill');
   }
 
+  final String invocationModes = snapshot ? 'compile' : '';
+
   Compiler compiler;
 
   // Update the in-memory file system with the provided sources. Currently, only
@@ -792,8 +814,16 @@
       packagesUri = Uri.directory(workingDirectory).resolveUri(packagesUri);
     }
     final List<String> errors = <String>[];
-    var options = setupCompilerOptions(fileSystem, platformKernelPath, false,
-        false, nullSafety, experimentalFlags, packagesUri, errors);
+    var options = setupCompilerOptions(
+        fileSystem,
+        platformKernelPath,
+        false,
+        false,
+        nullSafety,
+        experimentalFlags,
+        packagesUri,
+        errors,
+        invocationModes);
 
     // script should only be null for kUpdateSourcesTag.
     assert(script != null);
@@ -819,7 +849,8 @@
         experimentalFlags: experimentalFlags,
         packageConfig: packageConfig,
         multirootFilepaths: multirootFilepaths,
-        multirootScheme: multirootScheme);
+        multirootScheme: multirootScheme,
+        invocationModes: invocationModes);
   } else {
     FileSystem fileSystem = _buildFileSystem(
         sourceFiles, platformKernel, multirootFilepaths, multirootScheme);
@@ -830,7 +861,8 @@
         enableAsserts: enableAsserts,
         nullSafety: nullSafety,
         experimentalFlags: experimentalFlags,
-        packageConfig: packageConfig);
+        packageConfig: packageConfig,
+        invocationModes: invocationModes);
   }
 
   CompilationResult result;
@@ -974,6 +1006,7 @@
     scriptUri,
     platformKernelPath,
     false /* incremental */,
+    false /* snapshot */,
     kNullSafetyOptionUnspecified /* null safety */,
     1 /* isolateId chosen randomly */,
     [] /* source files */,
diff --git a/pkg/vm_service/CHANGELOG.md b/pkg/vm_service/CHANGELOG.md
index 2013c32..4bd758c 100644
--- a/pkg/vm_service/CHANGELOG.md
+++ b/pkg/vm_service/CHANGELOG.md
@@ -1,5 +1,11 @@
 # Changelog
 
+## 6.0.1-nullsafety.0
+- Fix versioning for pub.
+
+## 6.0.0-nullsafety.4
+- Fixed issue where response parsing could fail for `SourceReportRange.coverage`
+  if no coverage information was provided.
 ## 6.0.0-nullsafety.3
 - Fixed issue where `Response.type` and classes which override `Response.type` were
   returning the name of the `package:vm_service` reference object (e.g., InstanceRef) instead of
diff --git a/pkg/vm_service/lib/src/vm_service.dart b/pkg/vm_service/lib/src/vm_service.dart
index 05cbc00..d7d9d48 100644
--- a/pkg/vm_service/lib/src/vm_service.dart
+++ b/pkg/vm_service/lib/src/vm_service.dart
@@ -6886,7 +6886,7 @@
     compiled = json['compiled'] ?? false;
     error = createServiceObject(json['error'], const ['ErrorRef']) as ErrorRef?;
     coverage =
-        _createSpecificObject(json['coverage']!, SourceReportCoverage.parse);
+        _createSpecificObject(json['coverage'], SourceReportCoverage.parse);
     possibleBreakpoints = json['possibleBreakpoints'] == null
         ? null
         : List<int>.from(json['possibleBreakpoints']);
diff --git a/pkg/vm_service/pubspec.yaml b/pkg/vm_service/pubspec.yaml
index 84a7fe1..fc44e80 100644
--- a/pkg/vm_service/pubspec.yaml
+++ b/pkg/vm_service/pubspec.yaml
@@ -3,7 +3,7 @@
   A library to communicate with a service implementing the Dart VM
   service protocol.
 
-version: 6.0.0-nullsafety.3
+version: 6.0.1-nullsafety.0
 
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/vm_service
 
diff --git a/pkg/vm_service/tool/dart/generate_dart.dart b/pkg/vm_service/tool/dart/generate_dart.dart
index 6f5fca6..b41d7ab 100644
--- a/pkg/vm_service/tool/dart/generate_dart.dart
+++ b/pkg/vm_service/tool/dart/generate_dart.dart
@@ -1580,7 +1580,7 @@
       } else if (name == 'SourceReportRange' && field.name == 'coverage') {
         // Special case `SourceReportRange.coverage`.
         gen.writeln("coverage = _createSpecificObject("
-            "json['coverage']!, SourceReportCoverage.parse);");
+            "json['coverage'], SourceReportCoverage.parse);");
       } else if (name == 'Library' && field.name == 'dependencies') {
         // Special case `Library.dependencies`.
         gen.writeln("dependencies = List<LibraryDependency>.from("
diff --git a/runtime/bin/dfe.cc b/runtime/bin/dfe.cc
index f0aa87a..0a6bc48 100644
--- a/runtime/bin/dfe.cc
+++ b/runtime/bin/dfe.cc
@@ -182,14 +182,15 @@
 
 Dart_KernelCompilationResult DFE::CompileScript(const char* script_uri,
                                                 bool incremental,
-                                                const char* package_config) {
+                                                const char* package_config,
+                                                bool snapshot) {
   // TODO(aam): When Frontend is ready, VM should be passing vm_outline.dill
   // instead of vm_platform.dill to Frontend for compilation.
   PathSanitizer path_sanitizer(script_uri);
   const char* sanitized_uri = path_sanitizer.sanitized_uri();
 
   return Dart_CompileToKernel(sanitized_uri, platform_strong_dill,
-                              platform_strong_dill_size, incremental,
+                              platform_strong_dill_size, incremental, snapshot,
                               package_config);
 }
 
@@ -198,9 +199,10 @@
                                intptr_t* kernel_buffer_size,
                                char** error,
                                int* exit_code,
-                               const char* package_config) {
-  Dart_KernelCompilationResult result =
-      CompileScript(script_uri, use_incremental_compiler(), package_config);
+                               const char* package_config,
+                               bool snapshot) {
+  Dart_KernelCompilationResult result = CompileScript(
+      script_uri, use_incremental_compiler(), package_config, snapshot);
   switch (result.status) {
     case Dart_KernelCompilationStatus_Ok:
       *kernel_buffer = result.kernel;
diff --git a/runtime/bin/dfe.h b/runtime/bin/dfe.h
index eb475bf..0c38270 100644
--- a/runtime/bin/dfe.h
+++ b/runtime/bin/dfe.h
@@ -59,20 +59,28 @@
 
   // Compiles specified script.
   // Returns result from compiling the script.
+  //
+  // `snapshot` is used by the frontend to determine if compilation
+  // related information should be printed to console (e.g., null safety mode).
   Dart_KernelCompilationResult CompileScript(const char* script_uri,
                                              bool incremental,
-                                             const char* package_config);
+                                             const char* package_config,
+                                             bool snapshot);
 
   // Compiles specified script and reads the resulting kernel file.
   // If the compilation is successful, returns a valid in memory kernel
   // representation of the script, NULL otherwise
   // 'error' and 'exit_code' have the error values in case of errors.
+  //
+  // `snapshot` is used by the frontend to determine if compilation
+  // related information should be printed to console (e.g., null safety mode).
   void CompileAndReadScript(const char* script_uri,
                             uint8_t** kernel_buffer,
                             intptr_t* kernel_buffer_size,
                             char** error,
                             int* exit_code,
-                            const char* package_config);
+                            const char* package_config,
+                            bool snapshot);
 
   // Reads the script kernel file if specified 'script_uri' is a kernel file.
   // Returns an in memory kernel representation of the specified script is a
diff --git a/runtime/bin/loader.cc b/runtime/bin/loader.cc
index 9f62982..e855baf 100644
--- a/runtime/bin/loader.cc
+++ b/runtime/bin/loader.cc
@@ -198,7 +198,7 @@
     uint8_t* kernel_buffer = NULL;
     intptr_t kernel_buffer_size = -1;
     dfe.CompileAndReadScript(url_string, &kernel_buffer, &kernel_buffer_size,
-                             &error, &exit_code, NULL);
+                             &error, &exit_code, NULL, false);
     if (exit_code == 0) {
       return Dart_LoadLibraryFromKernel(kernel_buffer, kernel_buffer_size);
     } else if (exit_code == kCompilationErrorExitCode) {
diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc
index fec166f..12e64808 100644
--- a/runtime/bin/main.cc
+++ b/runtime/bin/main.cc
@@ -298,9 +298,14 @@
     }
     uint8_t* application_kernel_buffer = NULL;
     intptr_t application_kernel_buffer_size = 0;
+    // Only pass snapshot = true when generating an AppJIT snapshot to avoid
+    // duplicate null-safety info messages from the frontend when generating
+    // a kernel snapshot (this flag is instead set in
+    // Snapshot::GenerateKernel()).
+    const bool snapshot = Options::gen_snapshot_kind() == kAppJIT;
     dfe.CompileAndReadScript(script_uri, &application_kernel_buffer,
                              &application_kernel_buffer_size, error, exit_code,
-                             resolved_packages_config);
+                             resolved_packages_config, snapshot);
     if (application_kernel_buffer == NULL) {
       Dart_ExitScope();
       Dart_ShutdownIsolate();
diff --git a/runtime/bin/secure_socket_filter.cc b/runtime/bin/secure_socket_filter.cc
index c7a89a5..f515b92 100644
--- a/runtime/bin/secure_socket_filter.cc
+++ b/runtime/bin/secure_socket_filter.cc
@@ -159,10 +159,14 @@
 
 void FUNCTION_NAME(SecureSocket_NewX509CertificateWrapper)(
     Dart_NativeArguments args) {
+// This is to be used only in conjunction with certificate trust evaluator
+// running asynchronously, which is only used on mac/ios at the moment.
+#if !defined(HOST_OS_MACOS)
+  FATAL("This is to be used only on mac/ios platforms");
+#endif
   intptr_t x509_pointer = DartUtils::GetNativeIntptrArgument(args, 0);
   ASSERT(x509_pointer != 0);
   X509* x509 = reinterpret_cast<X509*>(x509_pointer);
-  X509_up_ref(x509);
   Dart_SetReturnValue(args, X509Helper::WrappedX509Certificate(x509));
 }
 
diff --git a/runtime/bin/security_context_macos.cc b/runtime/bin/security_context_macos.cc
index 927e139..c56487d 100644
--- a/runtime/bin/security_context_macos.cc
+++ b/runtime/bin/security_context_macos.cc
@@ -136,6 +136,7 @@
   int current_cert = 0;
   cert_chain.set(CFArrayCreateMutable(NULL, num_certs, NULL));
   X509* ca;
+  // Look for the last certificate in the chain - it's a root certificate.
   while ((ca = sk_X509_shift(unverified)) != NULL) {
     ScopedSecCertificateRef cert(CreateSecCertificateFromX509(ca));
     if (cert == NULL) {
@@ -145,9 +146,12 @@
     ++current_cert;
 
     if (current_cert == num_certs) {
-      root_cert = ca;
+      break;
     }
   }
+  ASSERT(current_cert == num_certs);
+  root_cert = ca;
+  X509_up_ref(ca);
 
   SSL_CTX* ssl_ctx = SSL_get_SSL_CTX(ssl);
   X509_STORE* store = SSL_CTX_get_cert_store(ssl_ctx);
diff --git a/runtime/bin/snapshot_utils.cc b/runtime/bin/snapshot_utils.cc
index 4a19ba2..5168ac5 100644
--- a/runtime/bin/snapshot_utils.cc
+++ b/runtime/bin/snapshot_utils.cc
@@ -471,7 +471,7 @@
     free(kernel_buffer);
   } else {
     Dart_KernelCompilationResult result =
-        dfe.CompileScript(script_name, false, package_config);
+        dfe.CompileScript(script_name, false, package_config, true);
     if (result.status != Dart_KernelCompilationStatus_Ok) {
       ErrorExit(kErrorExitCode, "%s\n", result.error);
     }
diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h
index 5cff8c3..aae9199 100644
--- a/runtime/include/dart_api.h
+++ b/runtime/include/dart_api.h
@@ -3537,6 +3537,10 @@
  *
  * \param platform_kernel_size The length of the platform_kernel buffer.
  *
+ * \param snapshot_compile Set to `true` when the compilation is for a snapshot.
+ * This is used by the frontend to determine if compilation related information
+ * should be printed to console (e.g., null safety mode).
+ *
  * \return Returns the result of the compilation.
  *
  * On a successful compilation the returned [Dart_KernelCompilationResult] has
@@ -3554,6 +3558,7 @@
                      const uint8_t* platform_kernel,
                      const intptr_t platform_kernel_size,
                      bool incremental_compile,
+                     bool snapshot_compile,
                      const char* package_config);
 
 typedef struct {
diff --git a/runtime/tests/vm/dart/sdk_hash_test.dart b/runtime/tests/vm/dart/sdk_hash_test.dart
index 8e31050..0ad4dba 100644
--- a/runtime/tests/vm/dart/sdk_hash_test.dart
+++ b/runtime/tests/vm/dart/sdk_hash_test.dart
@@ -35,7 +35,7 @@
       ]);
       Expect.equals('', result.stderr);
       Expect.equals(0, result.exitCode);
-      Expect.equals('', result.stdout);
+      Expect.equals('$unsoundNullSafetyMessage\n', result.stdout);
     }
 
     {
diff --git a/runtime/tests/vm/dart/snapshot_test_helper.dart b/runtime/tests/vm/dart/snapshot_test_helper.dart
index 1b578b9..28e5c47 100644
--- a/runtime/tests/vm/dart/snapshot_test_helper.dart
+++ b/runtime/tests/vm/dart/snapshot_test_helper.dart
@@ -39,7 +39,10 @@
 
 void expectOutput(String what, Result result) {
   if (result.output != what) {
-    reportError(result, 'Expected test to print \'${what}\' to stdout');
+    reportError(
+        result,
+        'Expected test to print \'${what}\' to stdout. '
+        'Actual: ${result.output}');
   }
 }
 
@@ -127,6 +130,12 @@
     final snapshot1Path = p.join(temp, 'snapshot1');
     final snapshot2Path = p.join(temp, 'snapshot2');
 
+    if (expectedStdout.isEmpty) {
+      expectedStdout = nullSafetyMessage;
+    } else {
+      expectedStdout = '$nullSafetyMessage\n$expectedStdout';
+    }
+
     print("Version ${Platform.version}");
 
     final generate1Result = await runDart('GENERATE SNAPSHOT 1', [
@@ -183,8 +192,15 @@
       testPath,
       '--train'
     ]);
-    expectOutput("OK(Trained)", trainingResult);
+    expectOutput("$nullSafetyMessage\nOK(Trained)", trainingResult);
     final runResult = await runSnapshot!(snapshotPath);
     expectOutput("OK(Run)", runResult);
   });
 }
+
+final String nullSafetyMessage =
+    hasSoundNullSafety ? soundNullSafetyMessage : unsoundNullSafetyMessage;
+
+const String soundNullSafetyMessage = 'Info: Compiling with sound null safety';
+const String unsoundNullSafetyMessage =
+    'Info: Compiling without sound null safety';
diff --git a/runtime/tests/vm/dart_2/sdk_hash_test.dart b/runtime/tests/vm/dart_2/sdk_hash_test.dart
index c73dc03..40f245d 100644
--- a/runtime/tests/vm/dart_2/sdk_hash_test.dart
+++ b/runtime/tests/vm/dart_2/sdk_hash_test.dart
@@ -35,7 +35,7 @@
       ]);
       Expect.equals('', result.stderr);
       Expect.equals(0, result.exitCode);
-      Expect.equals('', result.stdout);
+      Expect.equals('$unsoundNullSafetyMessage\n', result.stdout);
     }
 
     {
diff --git a/runtime/tests/vm/dart_2/snapshot_test_helper.dart b/runtime/tests/vm/dart_2/snapshot_test_helper.dart
index eef877f..ae68005 100644
--- a/runtime/tests/vm/dart_2/snapshot_test_helper.dart
+++ b/runtime/tests/vm/dart_2/snapshot_test_helper.dart
@@ -39,7 +39,10 @@
 
 void expectOutput(String what, Result result) {
   if (result.output != what) {
-    reportError(result, 'Expected test to print \'${what}\' to stdout');
+    reportError(
+        result,
+        'Expected test to print \'${what}\' to stdout. '
+        'Actual: ${result.output}');
   }
 }
 
@@ -127,6 +130,12 @@
     final snapshot1Path = p.join(temp, 'snapshot1');
     final snapshot2Path = p.join(temp, 'snapshot2');
 
+    if (expectedStdout.isEmpty) {
+      expectedStdout = unsoundNullSafetyMessage;
+    } else {
+      expectedStdout = '$unsoundNullSafetyMessage\n$expectedStdout';
+    }
+
     print("Version ${Platform.version}");
 
     final generate1Result = await runDart('GENERATE SNAPSHOT 1', [
@@ -183,8 +192,11 @@
       testPath,
       '--train'
     ]);
-    expectOutput("OK(Trained)", trainingResult);
+    expectOutput("$unsoundNullSafetyMessage\nOK(Trained)", trainingResult);
     final runResult = await runSnapshot(snapshotPath);
     expectOutput("OK(Run)", runResult);
   });
 }
+
+const String unsoundNullSafetyMessage =
+    'Info: Compiling without sound null safety';
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 36e4687..2f53e5d 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -6094,6 +6094,7 @@
                      const uint8_t* platform_kernel,
                      intptr_t platform_kernel_size,
                      bool incremental_compile,
+                     bool snapshot_compile,
                      const char* package_config) {
   API_TIMELINE_DURATION(Thread::Current());
 
@@ -6102,9 +6103,9 @@
   result.status = Dart_KernelCompilationStatus_Unknown;
   result.error = Utils::StrDup("Dart_CompileToKernel is unsupported.");
 #else
-  result = KernelIsolate::CompileToKernel(script_uri, platform_kernel,
-                                          platform_kernel_size, 0, NULL,
-                                          incremental_compile, package_config);
+  result = KernelIsolate::CompileToKernel(
+      script_uri, platform_kernel, platform_kernel_size, 0, NULL,
+      incremental_compile, snapshot_compile, package_config);
   if (result.status == Dart_KernelCompilationStatus_Ok) {
     Dart_KernelCompilationResult accept_result =
         KernelIsolate::AcceptCompilation();
diff --git a/runtime/vm/isolate_reload.cc b/runtime/vm/isolate_reload.cc
index 3d6a284..433af1e 100644
--- a/runtime/vm/isolate_reload.cc
+++ b/runtime/vm/isolate_reload.cc
@@ -1050,9 +1050,9 @@
   {
     const char* root_lib_url = root_lib_url_.ToCString();
     TransitionVMToNative transition(Thread::Current());
-    retval = KernelIsolate::CompileToKernel(root_lib_url, nullptr, 0,
-                                            modified_scripts_count,
-                                            modified_scripts, true, nullptr);
+    retval = KernelIsolate::CompileToKernel(
+        root_lib_url, nullptr, 0, modified_scripts_count, modified_scripts,
+        true, false, nullptr);
   }
   if (retval.status != Dart_KernelCompilationStatus_Ok) {
     if (retval.kernel != nullptr) {
diff --git a/runtime/vm/kernel_isolate.cc b/runtime/vm/kernel_isolate.cc
index 9ab6116..36bfb1b 100644
--- a/runtime/vm/kernel_isolate.cc
+++ b/runtime/vm/kernel_isolate.cc
@@ -708,6 +708,7 @@
       int source_files_count,
       Dart_SourceFile source_files[],
       bool incremental_compile,
+      bool snapshot_compile,
       const char* package_config,
       const char* multiroot_filepaths,
       const char* multiroot_scheme,
@@ -755,6 +756,10 @@
     dart_incremental.type = Dart_CObject_kBool;
     dart_incremental.value.as_bool = incremental_compile;
 
+    Dart_CObject dart_snapshot;
+    dart_snapshot.type = Dart_CObject_kBool;
+    dart_snapshot.value.as_bool = snapshot_compile;
+
     // TODO(aam): Assert that isolate exists once we move CompileAndReadScript
     // compilation logic out of CreateIsolateAndSetupHelper and into
     // IsolateSetupHelper in main.cc.
@@ -857,6 +862,7 @@
                                    &uri,
                                    &dart_platform_kernel,
                                    &dart_incremental,
+                                   &dart_snapshot,
                                    &null_safety,
                                    &isolate_id,
                                    &files,
@@ -1008,6 +1014,7 @@
     int source_file_count,
     Dart_SourceFile source_files[],
     bool incremental_compile,
+    bool snapshot_compile,
     const char* package_config,
     const char* multiroot_filepaths,
     const char* multiroot_scheme) {
@@ -1033,8 +1040,8 @@
   return request.SendAndWaitForResponse(
       kCompileTag, kernel_port, script_uri, platform_kernel,
       platform_kernel_size, source_file_count, source_files,
-      incremental_compile, package_config, multiroot_filepaths,
-      multiroot_scheme, experimental_flags_, NULL);
+      incremental_compile, snapshot_compile, package_config,
+      multiroot_filepaths, multiroot_scheme, experimental_flags_, NULL);
 }
 
 bool KernelIsolate::DetectNullSafety(const char* script_uri,
@@ -1052,7 +1059,7 @@
   KernelCompilationRequest request;
   Dart_KernelCompilationResult result = request.SendAndWaitForResponse(
       kDetectNullabilityTag, kernel_port, script_uri, nullptr, -1, 0, nullptr,
-      false, package_config, nullptr, nullptr, experimental_flags_,
+      false, false, package_config, nullptr, nullptr, experimental_flags_,
       original_working_directory);
   return result.null_safety;
 }
@@ -1068,8 +1075,8 @@
 
   KernelCompilationRequest request;
   return request.SendAndWaitForResponse(kListDependenciesTag, kernel_port, NULL,
-                                        NULL, 0, 0, NULL, false, NULL, NULL,
-                                        NULL, experimental_flags_, NULL);
+                                        NULL, 0, 0, NULL, false, false, NULL,
+                                        NULL, NULL, experimental_flags_, NULL);
 }
 
 Dart_KernelCompilationResult KernelIsolate::AcceptCompilation() {
@@ -1085,7 +1092,7 @@
 
   KernelCompilationRequest request;
   return request.SendAndWaitForResponse(kAcceptTag, kernel_port, NULL, NULL, 0,
-                                        0, NULL, true, NULL, NULL, NULL,
+                                        0, NULL, true, false, NULL, NULL, NULL,
                                         experimental_flags_, NULL);
 }
 
@@ -1131,7 +1138,7 @@
   KernelCompilationRequest request;
   return request.SendAndWaitForResponse(
       kUpdateSourcesTag, kernel_port, NULL, NULL, 0, source_files_count,
-      source_files, true, NULL, NULL, NULL, experimental_flags_, NULL);
+      source_files, true, false, NULL, NULL, NULL, experimental_flags_, NULL);
 }
 
 void KernelIsolate::NotifyAboutIsolateShutdown(const Isolate* isolate) {
diff --git a/runtime/vm/kernel_isolate.h b/runtime/vm/kernel_isolate.h
index 0aa5221..f312aee 100644
--- a/runtime/vm/kernel_isolate.h
+++ b/runtime/vm/kernel_isolate.h
@@ -51,6 +51,7 @@
       int source_files_count = 0,
       Dart_SourceFile source_files[] = NULL,
       bool incremental_compile = true,
+      bool snapshot_compile = false,
       const char* package_config = NULL,
       const char* multiroot_filepaths = NULL,
       const char* multiroot_scheme = NULL);
diff --git a/runtime/vm/unit_test.cc b/runtime/vm/unit_test.cc
index e1370f8..a65d61c 100644
--- a/runtime/vm/unit_test.cc
+++ b/runtime/vm/unit_test.cc
@@ -322,7 +322,8 @@
   Zone* zone = Thread::Current()->zone();
   Dart_KernelCompilationResult result = KernelIsolate::CompileToKernel(
       url, platform_strong_dill, platform_strong_dill_size, sourcefiles_count,
-      sourcefiles, incrementally, NULL, multiroot_filepaths, multiroot_scheme);
+      sourcefiles, incrementally, false, NULL, multiroot_filepaths,
+      multiroot_scheme);
   if (result.status == Dart_KernelCompilationStatus_Ok) {
     if (KernelIsolate::AcceptCompilation().status !=
         Dart_KernelCompilationStatus_Ok) {
diff --git a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/classes.dart b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/classes.dart
index d9af93e..ca02b18 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/classes.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/classes.dart
@@ -155,11 +155,12 @@
 
     // FutureOr<Never> --> Future<Never>
     if (_equalType(typeArg, Never)) {
-      return JS('!', '#(#)', getGenericClass(Future), typeArg);
+      return JS('!', '#(#)', getGenericClassStatic<Future>(), typeArg);
     }
     // FutureOr<Null> --> Future<Null>?
     if (_equalType(typeArg, Null)) {
-      return nullable(JS('!', '#(#)', getGenericClass(Future), typeArg));
+      return nullable(
+          JS('!', '#(#)', getGenericClassStatic<Future>(), typeArg));
     }
     // Otherwise, create the FutureOr<T> type as a normal generic type.
     var genericType = JS('!', '#(#)', genericFutureOrType, typeArg);
@@ -171,7 +172,8 @@
     // Add FutureOr specific is and as methods.
     is_FutureOr(obj) =>
         JS<bool>('!', '#.is(#)', typeArg, obj) ||
-        JS<bool>('!', '#(#).is(#)', getGenericClass(Future), typeArg, obj);
+        JS<bool>(
+            '!', '#(#).is(#)', getGenericClassStatic<Future>(), typeArg, obj);
     JS('!', '#.is = #', genericType, is_FutureOr);
 
     as_FutureOr(obj) {
@@ -183,10 +185,12 @@
       }
 
       if (JS<bool>('!', '#.is(#)', typeArg, obj) ||
-          JS<bool>('!', '#(#).is(#)', getGenericClass(Future), typeArg, obj)) {
+          JS<bool>('!', '#(#).is(#)', getGenericClassStatic<Future>(), typeArg,
+              obj)) {
         return obj;
       }
-      return cast(obj, JS('!', '#(#)', getGenericClass(FutureOr), typeArg));
+      return cast(
+          obj, JS('!', '#(#)', getGenericClassStatic<FutureOr>(), typeArg));
     }
 
     JS('!', '#.as = #', genericType, as_FutureOr);
@@ -252,6 +256,19 @@
 
 getGenericClass(type) => safeGetOwnProperty(type, _originalDeclaration);
 
+/// Extracts the type argument as the accessor for the JS class.
+///
+/// Should be used in place of [getGenericClass] when we know the class we want
+/// statically.
+///
+/// This value is extracted and inlined by the compiler without any runtime
+/// operations. The implementation here is only provided as a theoretical fall
+/// back and shouldn't actually be run.
+///
+/// For example `getGenericClassStatic<FutureOr>` emits `async.FutureOr$`
+/// directly.
+external getGenericClassStatic<T>();
+
 // TODO(markzipan): Make this non-nullable if we can ensure this returns
 // an empty list or if null and the empty list are semantically the same.
 List? getGenericArgs(type) =>
diff --git a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart
index af8e2e1..174b2ff 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart
@@ -665,7 +665,7 @@
   let value = map.get($elementType);
   if (value) return value;
 
-  ${getGenericClass(JSArray)}($elementType).unmodifiable($elements);
+  ${getGenericClassStatic<JSArray>()}($elementType).unmodifiable($elements);
   map.set($elementType, elements);
   return elements;
 })()''');
diff --git a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart
index 7fa6bbe..7874940 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart
@@ -1206,7 +1206,7 @@
     let args = ${getGenericArgs(type)};
     if (args == null) return name;
 
-    if (${getGenericClass(type)} == ${getGenericClass(JSArray)}) name = 'List';
+    if (${getGenericClass(type)} == ${getGenericClassStatic<JSArray>()}) name = 'List';
 
     let result = name;
     result += '<';
@@ -1391,7 +1391,7 @@
 bool _isFutureOr(type) {
   var genericClass = getGenericClass(type);
   return JS<bool>('!', '# && # === #', genericClass, genericClass,
-      getGenericClass(FutureOr));
+      getGenericClassStatic<FutureOr>());
 }
 
 @notNull
@@ -1476,7 +1476,7 @@
 
     // given t1 is Future<A> | A, then:
     // (Future<A> | A) <: t2 iff Future<A> <: t2 and A <: t2.
-    let t1Future = ${getGenericClass(Future)}(t1TypeArg);
+    let t1Future = ${getGenericClassStatic<Future>()}(t1TypeArg);
     // Known to handle the case FutureOr<Null> <: Future<Null>.
     return $_isSubtype(t1Future, $t2, $strictMode) &&
         $_isSubtype(t1TypeArg, $t2, $strictMode);
@@ -1493,7 +1493,7 @@
     // given t2 is Future<A> | A, then:
     // t1 <: (Future<A> | A) iff t1 <: Future<A> or t1 <: A
     let t2TypeArg = ${getGenericArgs(t2)}[0];
-    let t2Future = ${getGenericClass(Future)}(t2TypeArg);
+    let t2Future = ${getGenericClassStatic<Future>()}(t2TypeArg);
     // TODO(nshahan) Need to handle type variables on the left.
     // https://github.com/dart-lang/sdk/issues/38816
     return $_isSubtype($t1, t2Future, $strictMode) || $_isSubtype($t1, t2TypeArg, $strictMode);
@@ -1894,7 +1894,7 @@
       // - And `P` is a subtype match for `Q` with respect to `L` under
       //   constraints `C1`.
       var subtypeFuture =
-          JS<Object>('!', '#(#)', getGenericClass(Future), subtypeArg);
+          JS<Object>('!', '#(#)', getGenericClassStatic<Future>(), subtypeArg);
       return _isSubtypeMatch(subtypeFuture, supertype) &&
           _isSubtypeMatch(subtypeArg!, supertype);
     }
@@ -1909,8 +1909,8 @@
       //   - And `P` is a subtype match for `Q` with respect to `L` under
       //     constraints `C`
       var supertypeArg = getGenericArgs(supertype)![0];
-      var supertypeFuture =
-          JS<Object>('!', '#(#)', getGenericClass(Future), supertypeArg);
+      var supertypeFuture = JS<Object>(
+          '!', '#(#)', getGenericClassStatic<Future>(), supertypeArg);
       return _isSubtypeMatch(subtype, supertypeFuture) ||
           _isSubtypeMatch(subtype, supertypeArg);
     }
diff --git a/sdk/lib/_internal/js_dev_runtime/private/js_array.dart b/sdk/lib/_internal/js_dev_runtime/private/js_array.dart
index 3d5e3d7..a6522bd 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/js_array.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/js_array.dart
@@ -598,7 +598,7 @@
   }
 
   Type get runtimeType =>
-      dart.wrapType(JS('', '#(#)', dart.getGenericClass(List), E));
+      dart.wrapType(JS('', '#(#)', dart.getGenericClassStatic<List>(), E));
 
   Iterable<E> followedBy(Iterable<E> other) =>
       FollowedByIterable<E>.firstEfficient(this, other);
diff --git a/sdk/lib/async/future.dart b/sdk/lib/async/future.dart
index 317c1fe..c8c1403 100644
--- a/sdk/lib/async/future.dart
+++ b/sdk/lib/async/future.dart
@@ -698,6 +698,80 @@
   Future<T> timeout(Duration timeLimit, {FutureOr<T> onTimeout()?});
 }
 
+/// Convenience methods on futures.
+///
+/// Adds functionality to futures which makes it easier to
+/// write well-typed asynchronous code.
+@Since("2.12")
+extension FutureExtensions<T> on Future<T> {
+  /// Handles errors on this future.
+  ///
+  /// Catches errors of type [E] that this future complete with.
+  /// If [test] is supplied, only catches errors of type [E]
+  /// where [test] returns `true`.
+  /// If [E] is [Object], then all errors are potentially caught,
+  /// depending only on a supplied [test].toString()
+  ///
+  /// If the error is caught,
+  /// the returned future completes with the result of calling [handleError]
+  /// with the error and stack trace.
+  /// This result must be a value of the same type that this future
+  /// could otherwise complete with.
+  /// For example, if this future cannot complete with `null`,
+  /// then [handleError] also cannot return `null`.
+  /// Example:
+  /// ```dart
+  /// Future<T> retryOperation<T>(Future<T> operation(), T onFailure()) =>
+  ///     operation().onError<RetryException>((e, s) {
+  ///       if (e.canRetry) {
+  ///         return retryOperation(operation, onFailure);
+  ///       }
+  ///       return onFailure();
+  ///     });
+  /// ```
+  ///
+  /// If [handleError] throws, the returned future completes
+  /// with the thrown error and stack trace,
+  /// except that if it throws the *same* error object again,
+  /// then it is considered a "rethrow"
+  /// and the original stack trace is retained.
+  /// This can be used as an alternative to skipping the
+  /// error in [test].
+  /// Example:
+  /// ```dart
+  /// // Unwraps an an exceptions cause, if it has one.
+  /// someFuture.onError<SomeException>((e, _) {
+  ///   throw e.cause ?? e;
+  /// });
+  /// // vs.
+  /// someFuture.onError<SomeException>((e, _) {
+  ///   throw e.cause!;
+  /// }, test: (e) => e.cause != null);
+  /// ```
+  ///
+  /// If the error is not caught, the returned future
+  /// completes with the same result, value or error,
+  /// as this future.
+  ///
+  /// This method is effectively a more precisely typed version
+  /// of [Future.catchError].
+  /// It makes it easy to catch specific error types,
+  /// and requires a correctly typed error handler function,
+  /// rather than just [Function].
+  /// Because of this, the error handlers must accept
+  /// the stack trace argument.
+  Future<T> onError<E extends Object>(
+      FutureOr<T> handleError(E error, StackTrace stackTrace),
+      {bool test(E error)?}) {
+    // There are various ways to optimize this to avoid the double is E/as E
+    // type check, but for now we are not optimizing the error path.
+    return this.catchError(
+        (Object error, StackTrace stackTrace) =>
+            handleError(error as E, stackTrace),
+        test: (Object error) => error is E && (test == null || test(error)));
+  }
+}
+
 /// Thrown when a scheduled timeout happens while waiting for an async result.
 class TimeoutException implements Exception {
   /// Description of the cause of the timeout.
diff --git a/sdk/lib/core/core.dart b/sdk/lib/core/core.dart
index e0b0124..7e96d5c 100644
--- a/sdk/lib/core/core.dart
+++ b/sdk/lib/core/core.dart
@@ -168,6 +168,8 @@
 
 @Since("2.1")
 export "dart:async" show Future, Stream;
+@Since("2.12")
+export "dart:async" show FutureExtensions;
 
 part "annotations.dart";
 part "bigint.dart";
diff --git a/tests/language/constants_2018/constant_type_literal_test.dart b/tests/language/constants_2018/constant_type_literal_test.dart
index b886d77..924bcab 100644
--- a/tests/language/constants_2018/constant_type_literal_test.dart
+++ b/tests/language/constants_2018/constant_type_literal_test.dart
@@ -8,7 +8,7 @@
 import "dart:core" as core;
 // No reloading support for deferred loading.
 // See https://github.com/dart-lang/sdk/issues/33118.
-import "dart:core" deferred as dcore; //# 01: compile-time error
+import "dart:core" deferred as dcore show int //# 01: compile-time error
 
 // Declares F function type alias, M mixin and C class.
 import "constant_type_literal_types.dart";
diff --git a/tests/language/deferred/import_core_test.dart b/tests/language/deferred/import_core_test.dart
index f3d6644..3c72766 100644
--- a/tests/language/deferred/import_core_test.dart
+++ b/tests/language/deferred/import_core_test.dart
@@ -5,7 +5,7 @@
 // Nothing in the language spec explicitly prohibits a deferred import of
 // 'dart:core'.  Make sure it doesn't lead to any strange behavior.
 
-import "dart:core" deferred as core;
+import "dart:core" deferred as core show Object;
 
 main() {
   core.loadLibrary().then((_) => null);
diff --git a/tests/language_2/constants_2018/constant_type_literal_test.dart b/tests/language_2/constants_2018/constant_type_literal_test.dart
index b886d77..99a26db 100644
--- a/tests/language_2/constants_2018/constant_type_literal_test.dart
+++ b/tests/language_2/constants_2018/constant_type_literal_test.dart
@@ -8,7 +8,7 @@
 import "dart:core" as core;
 // No reloading support for deferred loading.
 // See https://github.com/dart-lang/sdk/issues/33118.
-import "dart:core" deferred as dcore; //# 01: compile-time error
+import "dart:core" deferred as dcore show int; //# 01: compile-time error
 
 // Declares F function type alias, M mixin and C class.
 import "constant_type_literal_types.dart";
diff --git a/tests/language_2/deferred/import_core_test.dart b/tests/language_2/deferred/import_core_test.dart
index f3d6644..3c72766 100644
--- a/tests/language_2/deferred/import_core_test.dart
+++ b/tests/language_2/deferred/import_core_test.dart
@@ -5,7 +5,7 @@
 // Nothing in the language spec explicitly prohibits a deferred import of
 // 'dart:core'.  Make sure it doesn't lead to any strange behavior.
 
-import "dart:core" deferred as core;
+import "dart:core" deferred as core show Object;
 
 main() {
   core.loadLibrary().then((_) => null);
diff --git a/tests/lib/async/catch_errors.dart b/tests/lib/async/catch_errors.dart
index 02c403c..f2a5bc4 100644
--- a/tests/lib/async/catch_errors.dart
+++ b/tests/lib/async/catch_errors.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2013, 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.
+
 library catch_errors;
 
 import 'dart:async';
diff --git a/tests/lib/async/future_onerror_test.dart b/tests/lib/async/future_onerror_test.dart
new file mode 100644
index 0000000..ea5ffab
--- /dev/null
+++ b/tests/lib/async/future_onerror_test.dart
@@ -0,0 +1,115 @@
+// 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:async_helper/async_helper.dart';
+import "package:expect/expect.dart";
+
+main() {
+  var stack = StackTrace.fromString("for testing");
+  var error = Object();
+  var stateError = StateError("test");
+
+  asyncStart();
+
+  {
+    // Match any error.
+    asyncStart();
+    var future = Future.error(error, stack);
+    future.onError((e, s) {
+      Expect.identical(error, e);
+      Expect.identical(stack, s);
+      asyncEnd();
+    });
+  }
+
+  {
+    // With matching test.
+    asyncStart();
+    var future = Future.error(error, stack);
+    future.onError((e, s) {
+      Expect.identical(error, e);
+      Expect.identical(stack, s);
+      asyncEnd();
+    }, test: (o) => true);
+  }
+
+  {
+    // With failing test.
+    asyncStart();
+    var future = Future.error(error, stack);
+    var onErrorFuture = future.onError((e, s) {
+      Expect.fail('unreachable');
+    }, test: (o) => false);
+    onErrorFuture.catchError((e, s) {
+      Expect.identical(error, e);
+      Expect.identical(stack, s);
+      asyncEnd();
+    });
+  }
+
+  {
+    // With matching type.
+    asyncStart();
+    var future = Future.error(stateError, stack);
+    future.onError<StateError>((e, s) {
+      Expect.identical(stateError, e);
+      Expect.identical(stack, s);
+      asyncEnd();
+    });
+  }
+
+  {
+    // With non-matching type.
+    asyncStart();
+    var future = Future.error(stateError, stack);
+    var onErrorFuture = future.onError<ArgumentError>((e, s) {
+      Expect.fail('unreachable');
+    });
+    onErrorFuture.catchError((e, s) {
+      Expect.identical(stateError, e);
+      Expect.identical(stack, s);
+      asyncEnd();
+    });
+  }
+
+  {
+    // With non-matching type and matching test.
+    asyncStart();
+    var future = Future.error(stateError, stack);
+    var onErrorFuture = future.onError<ArgumentError>((e, s) {
+      Expect.fail('unreachable');
+    }, test: (ArgumentError e) => true);
+    onErrorFuture.catchError((e, s) {
+      Expect.identical(stateError, e);
+      Expect.identical(stack, s);
+      asyncEnd();
+    });
+  }
+
+  {
+    // With matching type and matching test.
+    asyncStart();
+    var future = Future.error(stateError, stack);
+    future.onError<StateError>((e, s) {
+      Expect.identical(stateError, e);
+      Expect.identical(stack, s);
+      asyncEnd();
+    }, test: (StateError e) => true);
+  }
+
+  {
+    // With matching type and non-matching test.
+    asyncStart();
+    var future = Future.error(stateError, stack);
+    var onErrorFuture = future.onError<StateError>((e, s) {
+      Expect.fail('unreachable');
+    }, test: (StateError e) => false);
+    onErrorFuture.catchError((e, s) {
+      Expect.identical(stateError, e);
+      Expect.identical(stack, s);
+      asyncEnd();
+    });
+  }
+  asyncEnd();
+}
diff --git a/tools/VERSION b/tools/VERSION
index b96128e..a9ef1ca 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 250
+PRERELEASE 251
 PRERELEASE_PATCH 0
\ No newline at end of file