Version 2.14.0-237.0.dev

Merge commit '9b4ff5e5a6eeced3a8ad7ea57af906d2f4a570bc' into 'dev'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4b4cef9..7d5d4ba3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,9 +12,14 @@
 
 #### `dart:async`
 
-* The uncaught error handlers of `Zone`s are now run in the parent zone
-  of the zone where they were declared. This prevents a throwing handler
-  from causing an infinite loop by repeatedly triggering itself.
+*   The uncaught error handlers of `Zone`s are now run in the parent zone
+    of the zone where they were declared. This prevents a throwing handler
+    from causing an infinite loop by repeatedly triggering itself.
+
+*   Added `ignore()` as extension member on futures.
+
+*   Added `void unawaited(Future)` top-level function to deal with
+    the `unawaited_futures` lint.
 
 #### `dart:core`
 
diff --git a/pkg/analysis_server/lib/src/computer/computer_signature.dart b/pkg/analysis_server/lib/src/computer/computer_signature.dart
index 04fdea1..0e852c2 100644
--- a/pkg/analysis_server/lib/src/computer/computer_signature.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_signature.dart
@@ -28,7 +28,6 @@
 
   bool get offsetIsValid => _node != null;
 
-  // Return the closest argument list surrounding the [_node].
   /// Returns the computed signature information, maybe `null`.
   AnalysisGetSignatureResult? compute() {
     var argumentList = _findArgumentList();
@@ -80,6 +79,7 @@
         defaultValue: param.defaultValueCode);
   }
 
+  /// Return the closest argument list surrounding the [_node].
   ArgumentList? _findArgumentList() {
     var node = _node;
     while (node != null && node is! ArgumentList) {
diff --git a/pkg/analysis_server/lib/src/computer/computer_type_arguments_signature.dart b/pkg/analysis_server/lib/src/computer/computer_type_arguments_signature.dart
new file mode 100644
index 0000000..58aafe2
--- /dev/null
+++ b/pkg/analysis_server/lib/src/computer/computer_type_arguments_signature.dart
@@ -0,0 +1,116 @@
+// 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/lsp_protocol/protocol_generated.dart' as lsp;
+import 'package:analysis_server/src/computer/computer_hover.dart';
+import 'package:analysis_server/src/lsp/dartdoc.dart';
+import 'package:analysis_server/src/lsp/mapping.dart';
+import 'package:analysis_server/src/utilities/extensions/ast.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/src/dart/ast/element_locator.dart';
+import 'package:analyzer/src/dart/ast/utilities.dart';
+import 'package:analyzer/src/dartdoc/dartdoc_directive_info.dart';
+
+/// A computer for the signature help information about the type parameters for
+/// the [TypeArgumentList] surrounding the specified offset of a Dart
+/// [CompilationUnit].
+class DartTypeArgumentsSignatureComputer {
+  final DartdocDirectiveInfo _dartdocInfo;
+  final AstNode? _node;
+  final Set<lsp.MarkupKind>? preferredFormats;
+  late TypeArgumentList _argumentList;
+  final bool _isNonNullableByDefault;
+  DartTypeArgumentsSignatureComputer(
+    this._dartdocInfo,
+    CompilationUnit _unit,
+    int _offset,
+    this.preferredFormats,
+  )   : _node = NodeLocator(_offset).searchWithin(_unit),
+        _isNonNullableByDefault = _unit.isNonNullableByDefault;
+
+  /// The [TypeArgumentList] node located by [compute].
+  TypeArgumentList get argumentList => _argumentList;
+
+  bool get offsetIsValid => _node != null;
+
+  /// Returns the computed signature information, maybe `null`.
+  lsp.SignatureHelp? compute() {
+    var argumentList = _findTypeArgumentList();
+    if (argumentList == null) {
+      return null;
+    }
+    final parent = argumentList.parent;
+    Element? element;
+    if (parent is TypeName) {
+      element = ElementLocator.locate(parent.name);
+    } else if (parent is MethodInvocation) {
+      element = ElementLocator.locate(parent.methodName);
+    }
+    if (element is! TypeParameterizedElement ||
+        element.typeParameters.isEmpty) {
+      return null;
+    }
+
+    _argumentList = argumentList;
+
+    final label =
+        element.getDisplayString(withNullability: _isNonNullableByDefault);
+    final documentation =
+        DartUnitHoverComputer.computeDocumentation(_dartdocInfo, element)?.full;
+
+    return _toSignatureHelp(
+      label,
+      cleanDartdoc(documentation),
+      element.typeParameters,
+    );
+  }
+
+  /// Return the closest type argument list surrounding the [_node].
+  TypeArgumentList? _findTypeArgumentList() {
+    var node = _node;
+    while (node != null && node is! TypeArgumentList) {
+      // Certain nodes don't make sense to search above for an argument list
+      // (for example when inside a function expression).
+      if (node is FunctionExpression) {
+        return null;
+      }
+      node = node.parent;
+    }
+    return node as TypeArgumentList?;
+  }
+
+  /// Builds an [lsp.SignatureHelp] for the given type parameters.
+  lsp.SignatureHelp? _toSignatureHelp(
+    String label,
+    String? documentation,
+    List<TypeParameterElement> typeParameters,
+  ) {
+    final parameters = typeParameters
+        .map((param) => lsp.ParameterInformation(
+              label: param.getDisplayString(
+                  withNullability: _isNonNullableByDefault),
+            ))
+        .toList();
+
+    final signatures = [
+      lsp.SignatureInformation(
+        label: label,
+        documentation: documentation != null
+            ? asStringOrMarkupContent(preferredFormats, documentation)
+            : null,
+        parameters: parameters,
+      ),
+    ];
+
+    return lsp.SignatureHelp(
+      signatures: signatures,
+      activeSignature: 0,
+      // As with [toSignatureHelp], we don't currently use this, but need to set
+      // it to something that doesn't match a parameter to avoid one being
+      // selected.
+      activeParameter: -1,
+    );
+  }
+}
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
index cf29def..ccf8bb2 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
@@ -474,9 +474,9 @@
       node = node.parent;
     }
     return (node is ast.InvocationExpression &&
-            node.argumentList.length != 0) ||
+            !node.argumentList.beginToken.isSynthetic) ||
         (node is ast.InstanceCreationExpression &&
-            node.argumentList.length != 0);
+            !node.argumentList.beginToken.isSynthetic);
   }
 
   Iterable<CompletionItem> _pluginResultsToItems(
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_signature_help.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_signature_help.dart
index b57381e..34f4193 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_signature_help.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_signature_help.dart
@@ -5,9 +5,12 @@
 import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
 import 'package:analysis_server/lsp_protocol/protocol_special.dart';
 import 'package:analysis_server/src/computer/computer_signature.dart';
+import 'package:analysis_server/src/computer/computer_type_arguments_signature.dart';
 import 'package:analysis_server/src/lsp/handlers/handlers.dart';
 import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
 import 'package:analysis_server/src/lsp/mapping.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/src/dartdoc/dartdoc_directive_info.dart';
 
 class SignatureHelpHandler
     extends MessageHandler<SignatureHelpParams, SignatureHelp?> {
@@ -52,10 +55,24 @@
     final offset = await unit.mapResult((unit) => toOffset(unit.lineInfo, pos));
 
     return offset.mapResult((offset) {
-      final computer = DartUnitSignatureComputer(
-          server.getDartdocDirectiveInfoFor(unit.result),
-          unit.result.unit!,
-          offset);
+      final formats = clientCapabilities.signatureHelpDocumentationFormats;
+      final dartDocInfo = server.getDartdocDirectiveInfoFor(unit.result);
+
+      // First check if we're in a type args list and if so build some
+      // signature help for that.
+      final typeArgsSignature = _tryGetTypeArgsSignatureHelp(
+        dartDocInfo,
+        unit.result.unit!,
+        offset,
+        autoTriggered,
+        formats,
+      );
+      if (typeArgsSignature != null) {
+        return success(typeArgsSignature);
+      }
+
+      final computer =
+          DartUnitSignatureComputer(dartDocInfo, unit.result.unit!, offset);
       if (!computer.offsetIsValid) {
         return success(null); // No error, just no valid hover.
       }
@@ -72,8 +89,39 @@
         return success(null);
       }
 
-      final formats = clientCapabilities.signatureHelpDocumentationFormats;
       return success(toSignatureHelp(formats, signature));
     });
   }
+
+  /// Tries to create signature information for a surrounding [TypeArgumentList].
+  ///
+  /// Returns `null` if [offset] is in an invalid location, not inside a type
+  /// argument list or was auto-triggered in a location that was not the start
+  /// of a type argument list.
+  SignatureHelp? _tryGetTypeArgsSignatureHelp(
+    DartdocDirectiveInfo dartDocInfo,
+    CompilationUnit unit,
+    int offset,
+    bool autoTriggered,
+    Set<MarkupKind>? formats,
+  ) {
+    final typeArgsComputer =
+        DartTypeArgumentsSignatureComputer(dartDocInfo, unit, offset, formats);
+    if (!typeArgsComputer.offsetIsValid) {
+      return null;
+    }
+
+    final typeSignature = typeArgsComputer.compute();
+    if (typeSignature == null) {
+      return null;
+    }
+
+    // If auto-triggered from typing a `<`, only show if that `<` was at
+    // the start of the arg list (to avoid triggering on other `<`s).
+    if (autoTriggered && offset != typeArgsComputer.argumentList.offset + 1) {
+      return null;
+    }
+
+    return typeSignature;
+  }
 }
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_type_annotation.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_type_annotation.dart
index 8a89c1f..95ddbc7 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/add_type_annotation.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/add_type_annotation.dart
@@ -8,7 +8,10 @@
 import 'package:analysis_server/src/services/correction/fix.dart';
 import 'package:analysis_server/src/utilities/extensions/ast.dart';
 import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/visitor.dart';
+import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/dart/element/type_system.dart';
 import 'package:analyzer_plugin/src/utilities/change_builder/change_builder_core.dart';
 import 'package:analyzer_plugin/utilities/assist/assist.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
@@ -58,6 +61,31 @@
     }
   }
 
+  Future<void> _applyChange(
+      ChangeBuilder builder, Token? keyword, int offset, DartType type) async {
+    Future<bool> tryToApplyChange(ChangeBuilder builder) async {
+      var validChange = true;
+      await builder.addDartFileEdit(file, (builder) {
+        if (keyword != null && keyword.keyword == Keyword.VAR) {
+          builder.addReplacement(range.token(keyword), (builder) {
+            validChange = builder.writeType(type);
+          });
+        } else {
+          builder.addInsertion(offset, (builder) {
+            validChange = builder.writeType(type);
+            builder.write(' ');
+          });
+        }
+      });
+      return validChange;
+    }
+
+    _configureTargetLocation(node);
+    if (await tryToApplyChange(_temporaryBuilder(builder))) {
+      await tryToApplyChange(builder);
+    }
+  }
+
   /// Configure the [utils] using the given [target].
   void _configureTargetLocation(Object target) {
     utils.targetClassElement = null;
@@ -80,29 +108,8 @@
     if (type is! InterfaceType && type is! FunctionType) {
       return;
     }
-    _configureTargetLocation(node);
-
-    Future<bool> applyChange(ChangeBuilder builder) async {
-      var validChange = true;
-      await builder.addDartFileEdit(file, (builder) {
-        var keyword = declaredIdentifier.keyword;
-        if (keyword != null && keyword.keyword == Keyword.VAR) {
-          builder.addReplacement(range.token(keyword), (builder) {
-            validChange = builder.writeType(type);
-          });
-        } else {
-          builder.addInsertion(declaredIdentifier.identifier.offset, (builder) {
-            validChange = builder.writeType(type);
-            builder.write(' ');
-          });
-        }
-      });
-      return validChange;
-    }
-
-    if (await applyChange(_temporaryBuilder(builder))) {
-      await applyChange(builder);
-    }
+    await _applyChange(builder, declaredIdentifier.keyword,
+        declaredIdentifier.identifier.offset, type);
   }
 
   Future<void> _forSimpleFormalParameter(ChangeBuilder builder,
@@ -120,24 +127,7 @@
     if (type is! InterfaceType) {
       return;
     }
-    _configureTargetLocation(node);
-
-    Future<bool> applyChange(ChangeBuilder builder) async {
-      var validChange = true;
-      await builder.addDartFileEdit(file, (builder) {
-        builder.addInsertion(name.offset, (builder) {
-          validChange = builder.writeType(type);
-          if (validChange) {
-            builder.write(' ');
-          }
-        });
-      });
-      return validChange;
-    }
-
-    if (await applyChange(_temporaryBuilder(builder))) {
-      await applyChange(builder);
-    }
+    await _applyChange(builder, null, name.offset, type);
   }
 
   Future<void> _forVariableDeclaration(
@@ -157,44 +147,82 @@
       return;
     }
     // Ensure that there is an initializer to get the type from.
-    var initializer = variable.initializer;
-    if (initializer == null) {
+    var type = _typeForVariable(variable);
+    if (type == null) {
       return;
     }
-    var type = initializer.staticType;
-    // prepare type source
     if ((type is! InterfaceType || type.isDartCoreNull) &&
         type is! FunctionType) {
       return;
     }
-    _configureTargetLocation(node);
-
-    Future<bool> applyChange(ChangeBuilder builder) async {
-      var validChange = true;
-      await builder.addDartFileEdit(file, (builder) {
-        var keyword = declarationList.keyword;
-        if (keyword != null && keyword.keyword == Keyword.VAR) {
-          builder.addReplacement(range.token(keyword), (builder) {
-            validChange = builder.writeType(type);
-          });
-        } else {
-          builder.addInsertion(variable.offset, (builder) {
-            validChange = builder.writeType(type);
-            builder.write(' ');
-          });
-        }
-      });
-      return validChange;
-    }
-
-    if (await applyChange(_temporaryBuilder(builder))) {
-      await applyChange(builder);
-    }
+    await _applyChange(builder, declarationList.keyword, variable.offset, type);
   }
 
   ChangeBuilder _temporaryBuilder(ChangeBuilder builder) =>
       ChangeBuilder(workspace: (builder as ChangeBuilderImpl).workspace);
 
+  DartType? _typeForVariable(VariableDeclaration variable) {
+    var initializer = variable.initializer;
+    if (initializer != null) {
+      return initializer.staticType;
+    }
+    // The parents should be a [VariableDeclarationList],
+    // [VariableDeclarationStatement], and [Block], in that order.
+    var statement = variable.parent?.parent;
+    var block = statement?.parent;
+    if (statement is! VariableDeclarationStatement || block is! Block) {
+      return null;
+    }
+    var element = variable.declaredElement;
+    if (element is! LocalVariableElement) {
+      return null;
+    }
+    var statements = block.statements;
+    var index = statements.indexOf(statement);
+    var visitor = _AssignedTypeCollector(typeSystem, element);
+    for (var i = index + 1; i < statements.length; i++) {
+      statements[i].accept(visitor);
+    }
+    return visitor.bestType;
+  }
+
   /// Return an instance of this class. Used as a tear-off in `FixProcessor`.
   static AddTypeAnnotation newInstance() => AddTypeAnnotation();
 }
+
+class _AssignedTypeCollector extends RecursiveAstVisitor<void> {
+  /// The type system used to compute the best type.
+  final TypeSystem typeSystem;
+
+  final LocalVariableElement variable;
+
+  /// The types that are assigned to the variable.
+  final Set<DartType> assignedTypes = {};
+
+  _AssignedTypeCollector(this.typeSystem, this.variable);
+
+  DartType? get bestType {
+    if (assignedTypes.isEmpty) {
+      return null;
+    }
+    var types = assignedTypes.toList();
+    var bestType = types[0];
+    for (var i = 1; i < assignedTypes.length; i++) {
+      bestType = typeSystem.leastUpperBound(bestType, types[i]);
+    }
+    return bestType;
+  }
+
+  @override
+  void visitAssignmentExpression(AssignmentExpression node) {
+    var leftHandSide = node.leftHandSide;
+    if (leftHandSide is SimpleIdentifier &&
+        leftHandSide.staticElement == variable) {
+      var type = node.rightHandSide.staticType;
+      if (type != null) {
+        assignedTypes.add(type);
+      }
+    }
+    return super.visitAssignmentExpression(node);
+  }
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index f2a7e4b..2c2cf47 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -640,11 +640,11 @@
   static const REMOVE_THIS_EXPRESSION = FixKind(
       'dart.fix.remove.thisExpression',
       DartFixKindPriority.DEFAULT,
-      'Remove this expression');
+      "Remove 'this' expression");
   static const REMOVE_THIS_EXPRESSION_MULTI = FixKind(
       'dart.fix.remove.thisExpression.multi',
       DartFixKindPriority.IN_FILE,
-      'Remove unnecessary this expressions everywhere in file');
+      "Remove unnecessary 'this' expressions everywhere in file");
   static const REMOVE_TYPE_ANNOTATION = FixKind(
       'dart.fix.remove.typeAnnotation',
       DartFixKindPriority.DEFAULT,
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index 61a67e1..de4a8fc 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -501,6 +501,9 @@
     LintNames.prefer_spread_collections: [
       ConvertAddAllToSpread.newInstance,
     ],
+    LintNames.prefer_typing_uninitialized_variables: [
+      AddTypeAnnotation.newInstance,
+    ],
     LintNames.slash_for_doc_comments: [
       ConvertDocumentationIntoLine.newInstance,
     ],
diff --git a/pkg/analysis_server/lib/src/services/correction/util.dart b/pkg/analysis_server/lib/src/services/correction/util.dart
index 0409ea4..31d0876 100644
--- a/pkg/analysis_server/lib/src/services/correction/util.dart
+++ b/pkg/analysis_server/lib/src/services/correction/util.dart
@@ -17,6 +17,7 @@
 import 'package:analyzer/dart/ast/precedence.dart';
 import 'package:analyzer/dart/ast/visitor.dart';
 import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/nullability_suffix.dart';
 import 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer/error/listener.dart';
 import 'package:analyzer/src/dart/ast/extensions.dart';
@@ -865,6 +866,7 @@
       return _getTypeCodeElementArguments(
         librariesToImport: librariesToImport,
         element: aliasElement,
+        isNullable: type.nullabilitySuffix == NullabilitySuffix.question,
         typeArguments: aliasArguments,
       );
     }
@@ -895,6 +897,7 @@
       return _getTypeCodeElementArguments(
         librariesToImport: librariesToImport,
         element: type.element,
+        isNullable: type.nullabilitySuffix == NullabilitySuffix.question,
         typeArguments: type.typeArguments,
       );
     }
@@ -1161,6 +1164,7 @@
   String? _getTypeCodeElementArguments({
     required Set<Source> librariesToImport,
     required Element element,
+    required bool isNullable,
     required List<DartType> typeArguments,
   }) {
     var sb = StringBuffer();
@@ -1188,6 +1192,9 @@
     // append simple name
     var name = element.displayName;
     sb.write(name);
+    if (isNullable) {
+      sb.write('?');
+    }
 
     // append type arguments
     if (typeArguments.isNotEmpty) {
diff --git a/pkg/analysis_server/lib/src/services/linter/lint_names.dart b/pkg/analysis_server/lib/src/services/linter/lint_names.dart
index 20e6839..e091101 100644
--- a/pkg/analysis_server/lib/src/services/linter/lint_names.dart
+++ b/pkg/analysis_server/lib/src/services/linter/lint_names.dart
@@ -88,6 +88,8 @@
   static const String prefer_relative_imports = 'prefer_relative_imports';
   static const String prefer_single_quotes = 'prefer_single_quotes';
   static const String prefer_spread_collections = 'prefer_spread_collections';
+  static const String prefer_typing_uninitialized_variables =
+      'prefer_typing_uninitialized_variables';
   static const String slash_for_doc_comments = 'slash_for_doc_comments';
   static const String sort_child_properties_last = 'sort_child_properties_last';
   static const String sort_constructors_first = 'sort_constructors_first';
diff --git a/pkg/analysis_server/test/edit/refactoring_test.dart b/pkg/analysis_server/test/edit/refactoring_test.dart
index d14a01d..9c02d35 100644
--- a/pkg/analysis_server/test/edit/refactoring_test.dart
+++ b/pkg/analysis_server/test/edit/refactoring_test.dart
@@ -763,6 +763,43 @@
 ''');
   }
 
+  Future<void> test_statements_nullableReturnType() {
+    addTestFile('''
+void foo(int b) {
+// start
+  int? x;
+  if (b < 2) {
+    x = 42;
+  }
+  if (b >= 2) {
+    x = 43;
+  }
+// end
+  print(x!);
+}
+''');
+    _setOffsetLengthForStartEnd();
+    return assertSuccessfulRefactoring(_computeChange, '''
+void foo(int b) {
+// start
+  int? x = res(b);
+// end
+  print(x!);
+}
+
+int? res(int b) {
+  int? x;
+  if (b < 2) {
+    x = 42;
+  }
+  if (b >= 2) {
+    x = 43;
+  }
+  return x;
+}
+''');
+  }
+
   Future<Response> _computeChange() async {
     await _prepareOptions();
     // send request with the options
diff --git a/pkg/analysis_server/test/lsp/completion_dart_test.dart b/pkg/analysis_server/test/lsp/completion_dart_test.dart
index 072fc7b..fee07a1 100644
--- a/pkg/analysis_server/test/lsp/completion_dart_test.dart
+++ b/pkg/analysis_server/test/lsp/completion_dart_test.dart
@@ -268,6 +268,20 @@
         insertText: 'print',
       );
 
+  Future<void> test_completeFunctionCalls_existingPartialArgList() =>
+      checkCompleteFunctionCallInsertText(
+        '''
+        class Aaaaa {
+          Aaaaa(int a);
+        }
+        void main(int aaa) {
+          var a = new [[Aaa^]](
+        }
+        ''',
+        'Aaaaa(…)',
+        insertText: 'Aaaaa',
+      );
+
   Future<void> test_completeFunctionCalls_expression() =>
       checkCompleteFunctionCallInsertText(
         '''
diff --git a/pkg/analysis_server/test/lsp/signature_help_test.dart b/pkg/analysis_server/test/lsp/signature_help_test.dart
index dacaefe..edaeefd 100644
--- a/pkg/analysis_server/test/lsp/signature_help_test.dart
+++ b/pkg/analysis_server/test/lsp/signature_help_test.dart
@@ -465,6 +465,86 @@
         ));
   }
 
+  Future<void> test_typeParams_class() async {
+    final content = '''
+    /// My Foo.
+    class Foo<T1, T2 extends String> {}
+
+    class Bar extends Foo<^> {}
+    ''';
+
+    const expectedLabel = 'class Foo<T1, T2 extends String>';
+    const expectedDoc = 'My Foo.';
+
+    await initialize(
+        textDocumentCapabilities: withSignatureHelpContentFormat(
+            emptyTextDocumentClientCapabilities, [MarkupKind.Markdown]));
+    await openFile(mainFileUri, withoutMarkers(content));
+    await testSignature(
+      content,
+      expectedLabel,
+      expectedDoc,
+      [
+        ParameterInformation(label: 'T1'),
+        ParameterInformation(label: 'T2 extends String'),
+      ],
+    );
+  }
+
+  Future<void> test_typeParams_function() async {
+    final content = '''
+    /// My Foo.
+    void foo<T1, T2 extends String>() {
+      foo<^>();
+    }
+    ''';
+
+    const expectedLabel = 'void foo<T1, T2 extends String>()';
+    const expectedDoc = 'My Foo.';
+
+    await initialize(
+        textDocumentCapabilities: withSignatureHelpContentFormat(
+            emptyTextDocumentClientCapabilities, [MarkupKind.Markdown]));
+    await openFile(mainFileUri, withoutMarkers(content));
+    await testSignature(
+      content,
+      expectedLabel,
+      expectedDoc,
+      [
+        ParameterInformation(label: 'T1'),
+        ParameterInformation(label: 'T2 extends String'),
+      ],
+    );
+  }
+
+  Future<void> test_typeParams_method() async {
+    final content = '''
+    class Foo {
+      /// My Foo.
+      void foo<T1, T2 extends String>() {
+        foo<^>();
+      }
+    }
+    ''';
+
+    const expectedLabel = 'void foo<T1, T2 extends String>()';
+    const expectedDoc = 'My Foo.';
+
+    await initialize(
+        textDocumentCapabilities: withSignatureHelpContentFormat(
+            emptyTextDocumentClientCapabilities, [MarkupKind.Markdown]));
+    await openFile(mainFileUri, withoutMarkers(content));
+    await testSignature(
+      content,
+      expectedLabel,
+      expectedDoc,
+      [
+        ParameterInformation(label: 'T1'),
+        ParameterInformation(label: 'T2 extends String'),
+      ],
+    );
+  }
+
   Future<void> test_unopenFile() async {
     final content = '''
     /// Does foo.
diff --git a/pkg/analysis_server/test/services/refactoring/extract_method_test.dart b/pkg/analysis_server/test/services/refactoring/extract_method_test.dart
index fec1997..e530fd5 100644
--- a/pkg/analysis_server/test/services/refactoring/extract_method_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/extract_method_test.dart
@@ -1209,7 +1209,7 @@
     _createRefactoringForStartEndComments();
     // do check
     await refactoring.checkInitialConditions();
-    expect(refactoring.returnType, 'int');
+    expect(refactoring.returnType, 'int?');
   }
 
   Future<void> test_returnType_statements_void() async {
diff --git a/pkg/analysis_server/test/src/services/completion/dart/completion_test.dart b/pkg/analysis_server/test/src/services/completion/dart/completion_test.dart
index 59352d0..14b5877 100644
--- a/pkg/analysis_server/test/src/services/completion/dart/completion_test.dart
+++ b/pkg/analysis_server/test/src/services/completion/dart/completion_test.dart
@@ -13,6 +13,7 @@
     defineReflectiveTests(AsExpressionCompletionTest);
     defineReflectiveTests(AssertStatementCompletionTest);
     defineReflectiveTests(ConstructorCompletionTest);
+    defineReflectiveTests(DeclaredIdentifierCompletionTest);
     defineReflectiveTests(ExpressionFunctionBodyCompletionTest);
     defineReflectiveTests(ExtensionCompletionTest);
     defineReflectiveTests(FormalParameterCompletionTest);
@@ -122,6 +123,33 @@
 }
 
 @reflectiveTest
+class DeclaredIdentifierCompletionTest extends CompletionTestCase {
+  Future<void> test_afterFinal_withIdentifier() async {
+    addTestFile('''
+class C {
+  void m(List<C> cs) {
+    for (final ^ x in cs) {}
+  }
+}
+''');
+    await getSuggestions();
+    assertHasCompletion('C');
+  }
+
+  Future<void> test_afterFinal_withoutIdentifier() async {
+    addTestFile('''
+class C {
+  void m(List<C> cs) {
+    for (final ^) {}
+  }
+}
+''');
+    await getSuggestions();
+    assertHasCompletion('C');
+  }
+}
+
+@reflectiveTest
 class ExpressionFunctionBodyCompletionTest extends CompletionTestCase {
   Future<void> test_voidReturn_localFunction() async {
     addTestFile('''
diff --git a/pkg/analysis_server/test/src/services/correction/assist/add_type_annotation_test.dart b/pkg/analysis_server/test/src/services/correction/assist/add_type_annotation_test.dart
index 06724fb..e0639a0 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/add_type_annotation_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/add_type_annotation_test.dart
@@ -20,6 +20,9 @@
   @override
   AssistKind get kind => DartAssistKind.ADD_TYPE_ANNOTATION;
 
+  @override
+  String? get testPackageLanguageVersion => '2.12';
+
   Future<void> test_classField_final() async {
     await resolveTestCode('''
 class A {
@@ -59,21 +62,21 @@
 
   Future<void> test_declaredIdentifier() async {
     await resolveTestCode('''
-main(List<String> items) {
+void f(List<String> items) {
   for (var item in items) {
   }
 }
 ''');
     // on identifier
     await assertHasAssistAt('item in', '''
-main(List<String> items) {
+void f(List<String> items) {
   for (String item in items) {
   }
 }
 ''');
     // on "for"
     await assertHasAssistAt('for (', '''
-main(List<String> items) {
+void f(List<String> items) {
   for (String item in items) {
   }
 }
@@ -87,7 +90,7 @@
 ''');
     await resolveTestCode('''
 import 'my_lib.dart';
-main() {
+void f() {
   for (var map in getMap()) {
   }
 }
@@ -96,7 +99,7 @@
 import 'dart:collection';
 
 import 'my_lib.dart';
-main() {
+void f() {
   for (HashMap<String, int> map in getMap()) {
   }
 }
@@ -105,13 +108,13 @@
 
   Future<void> test_declaredIdentifier_final() async {
     await resolveTestCode('''
-main(List<String> items) {
+void f(List<String> items) {
   for (final item in items) {
   }
 }
 ''');
     await assertHasAssistAt('item in', '''
-main(List<String> items) {
+void f(List<String> items) {
   for (final String item in items) {
   }
 }
@@ -121,7 +124,7 @@
   Future<void> test_declaredIdentifier_generic() async {
     await resolveTestCode('''
 class A<T> {
-  main(List<List<T>> items) {
+  void f(List<List<T>> items) {
     for (var item in items) {
     }
   }
@@ -129,7 +132,7 @@
 ''');
     await assertHasAssistAt('item in', '''
 class A<T> {
-  main(List<List<T>> items) {
+  void f(List<List<T>> items) {
     for (List<T> item in items) {
     }
   }
@@ -139,7 +142,7 @@
 
   Future<void> test_declaredIdentifier_hasTypeAnnotation() async {
     await resolveTestCode('''
-main(List<String> items) {
+void f(List<String> items) {
   for (String item in items) {
   }
 }
@@ -149,7 +152,7 @@
 
   Future<void> test_declaredIdentifier_inForEachBody() async {
     await resolveTestCode('''
-main(List<String> items) {
+void f(List<String> items) {
   for (var item in items) {
     42;
   }
@@ -161,7 +164,7 @@
   Future<void> test_declaredIdentifier_unknownType() async {
     verifyNoTestUnitErrors = false;
     await resolveTestCode('''
-main() {
+void f() {
   for (var item in unknownList) {
   }
 }
@@ -176,7 +179,7 @@
 ''');
     await resolveTestCode('''
 import 'my_lib.dart';
-main() {
+void f() {
   var v = getMap();
 }
 ''');
@@ -184,7 +187,7 @@
 import 'dart:collection';
 
 import 'my_lib.dart';
-main() {
+void f() {
   HashMap<String, int> v = getMap();
 }
 ''');
@@ -203,7 +206,7 @@
 ''';
     addTestSource(r'''
 part of my_app;
-main() {
+void f() {
   var /*caret*/v = getMap();
 }
 ''');
@@ -215,7 +218,7 @@
 
     await assertHasAssist('''
 part of my_app;
-main() {
+void f() {
   HashMap<String, int> v = getMap();
 }
 ''', additionallyChangedFiles: {
@@ -243,14 +246,14 @@
 ''');
     await resolveTestCode('''
 import 'ccc/lib_b.dart';
-main() {
+void f() {
   var v = newMyClass();
 }
 ''');
     await assertHasAssistAt('v =', '''
 import 'aa/bbb/lib_a.dart';
 import 'ccc/lib_b.dart';
-main() {
+void f() {
   MyClass v = newMyClass();
 }
 ''');
@@ -258,7 +261,7 @@
 
   Future<void> test_local_bottom() async {
     await resolveTestCode('''
-main() {
+void f() {
   var v = throw 42;
 }
 ''');
@@ -267,12 +270,12 @@
 
   Future<void> test_local_Function() async {
     await resolveTestCode('''
-main() {
+void f() {
   var v = () => 1;
 }
 ''');
     await assertHasAssistAt('v =', '''
-main() {
+void f() {
   int Function() v = () => 1;
 }
 ''');
@@ -281,14 +284,14 @@
   Future<void> test_local_generic_literal() async {
     await resolveTestCode('''
 class A {
-  main(List<int> items) {
+  void m(List<int> items) {
     var v = items;
   }
 }
 ''');
     await assertHasAssistAt('v =', '''
 class A {
-  main(List<int> items) {
+  void m(List<int> items) {
     List<int> v = items;
   }
 }
@@ -298,14 +301,14 @@
   Future<void> test_local_generic_local() async {
     await resolveTestCode('''
 class A<T> {
-  main(List<T> items) {
+  void m(List<T> items) {
     var v = items;
   }
 }
 ''');
     await assertHasAssistAt('v =', '''
 class A<T> {
-  main(List<T> items) {
+  void m(List<T> items) {
     List<T> v = items;
   }
 }
@@ -314,7 +317,7 @@
 
   Future<void> test_local_hasTypeAnnotation() async {
     await resolveTestCode('''
-main() {
+void f() {
   int v = 42;
 }
 ''');
@@ -323,12 +326,12 @@
 
   Future<void> test_local_int() async {
     await resolveTestCode('''
-main() {
+void f() {
   var v = 0;
 }
 ''');
     await assertHasAssistAt('v =', '''
-main() {
+void f() {
   int v = 0;
 }
 ''');
@@ -336,12 +339,12 @@
 
   Future<void> test_local_List() async {
     await resolveTestCode('''
-main() {
+void f() {
   var v = <String>[];
 }
 ''');
     await assertHasAssistAt('v =', '''
-main() {
+void f() {
   List<String> v = <String>[];
 }
 ''');
@@ -350,15 +353,15 @@
   Future<void> test_local_localType() async {
     await resolveTestCode('''
 class C {}
-C f() => null;
-main() {
+C f() => C();
+void g() {
   var x = f();
 }
 ''');
     await assertHasAssistAt('x =', '''
 class C {}
-C f() => null;
-main() {
+C f() => C();
+void g() {
   C x = f();
 }
 ''');
@@ -366,26 +369,183 @@
 
   Future<void> test_local_multiple() async {
     await resolveTestCode('''
-main() {
+void f() {
   var a = 1, b = '';
 }
 ''');
     await assertNoAssistAt('var ');
   }
 
-  Future<void> test_local_noValue() async {
+  Future<void> test_local_noInitializer_noAssignments() async {
     verifyNoTestUnitErrors = false;
     await resolveTestCode('''
-main() {
+void f() {
   var v;
 }
 ''');
     await assertNoAssistAt('var ');
   }
 
+  Future<void> test_local_noInitializer_oneAssignment_dynamic() async {
+    verifyNoTestUnitErrors = false;
+    await resolveTestCode('''
+void f(p) {
+  var v;
+  v = p;
+}
+''');
+    await assertNoAssistAt('var ');
+  }
+
+  Future<void> test_local_noInitializer_oneAssignment_functionType() async {
+    verifyNoTestUnitErrors = false;
+    await resolveTestCode('''
+void f(int Function(int) p) {
+  var v;
+  v = p;
+}
+''');
+    // TODO(brianwilkerson) Improve `DartChangeBuilder.writeType` so that
+    //  unnecessary parameter names (`p1`) are not written.
+    await assertHasAssistAt('var ', '''
+void f(int Function(int) p) {
+  int Function(int p1) v;
+  v = p;
+}
+''');
+  }
+
+  Future<void> test_local_noInitializer_oneAssignment_insideClosure() async {
+    verifyNoTestUnitErrors = false;
+    await resolveTestCode('''
+void f() {
+  var v;
+  () {
+    v = '';
+  }();
+}
+''');
+    await assertHasAssistAt('var ', '''
+void f() {
+  String v;
+  () {
+    v = '';
+  }();
+}
+''');
+  }
+
+  Future<void> test_local_noInitializer_oneAssignment_interfaceType() async {
+    verifyNoTestUnitErrors = false;
+    await resolveTestCode('''
+void f() {
+  var v;
+  v = '';
+}
+''');
+    await assertHasAssistAt('var ', '''
+void f() {
+  String v;
+  v = '';
+}
+''');
+  }
+
+  Future<void> test_local_noInitializer_threeAssignments() async {
+    verifyNoTestUnitErrors = false;
+    await resolveTestCode('''
+void f(int a, String b) {
+  var v;
+  v = a;
+  v = null;
+  v = b;
+}
+''');
+    await assertHasAssistAt('var ', '''
+void f(int a, String b) {
+  Object? v;
+  v = a;
+  v = null;
+  v = b;
+}
+''');
+  }
+
+  Future<void> test_local_noInitializer_twoAssignments_differentTypes() async {
+    verifyNoTestUnitErrors = false;
+    await resolveTestCode('''
+void f() {
+  var v;
+  v = 0;
+  v = 3.1;
+}
+''');
+    await assertHasAssistAt('var ', '''
+void f() {
+  num v;
+  v = 0;
+  v = 3.1;
+}
+''');
+  }
+
+  Future<void> test_local_noInitializer_twoAssignments_oneNull() async {
+    verifyNoTestUnitErrors = false;
+    await resolveTestCode('''
+void f() {
+  var v;
+  v = null;
+  v = 0;
+}
+''');
+    await assertHasAssistAt('var ', '''
+void f() {
+  int? v;
+  v = null;
+  v = 0;
+}
+''');
+  }
+
+  Future<void> test_local_noInitializer_twoAssignments_oneNullable() async {
+    verifyNoTestUnitErrors = false;
+    await resolveTestCode('''
+void f(int a, int? b) {
+  var v;
+  v = a;
+  v = b;
+}
+''');
+    await assertHasAssistAt('var ', '''
+void f(int a, int? b) {
+  int? v;
+  v = a;
+  v = b;
+}
+''');
+  }
+
+  Future<void> test_local_noInitializer_twoAssignments_sameTypes() async {
+    verifyNoTestUnitErrors = false;
+    await resolveTestCode('''
+void f() {
+  var v;
+  v = 'a';
+  v = 'b';
+}
+''');
+    await assertHasAssistAt('var ', '''
+void f() {
+  String v;
+  v = 'a';
+  v = 'b';
+}
+''');
+  }
+
   Future<void> test_local_null() async {
     await resolveTestCode('''
-main() {
+void f() {
   var v = null;
 }
 ''');
@@ -394,7 +554,7 @@
 
   Future<void> test_local_onInitializer() async {
     await resolveTestCode('''
-main() {
+void f() {
   var abc = 0;
 }
 ''');
@@ -403,12 +563,12 @@
 
   Future<void> test_local_onName() async {
     await resolveTestCode('''
-main() {
+void f() {
   var abc = 0;
 }
 ''');
     await assertHasAssistAt('bc', '''
-main() {
+void f() {
   int abc = 0;
 }
 ''');
@@ -416,12 +576,12 @@
 
   Future<void> test_local_onVar() async {
     await resolveTestCode('''
-main() {
+void f() {
   var v = 0;
 }
 ''');
     await assertHasAssistAt('var ', '''
-main() {
+void f() {
   int v = 0;
 }
 ''');
@@ -430,7 +590,7 @@
   Future<void> test_local_unknown() async {
     verifyNoTestUnitErrors = false;
     await resolveTestCode('''
-main() {
+void f() {
   var v = unknownVar;
 }
 ''');
@@ -440,13 +600,13 @@
   Future<void> test_parameter() async {
     await resolveTestCode('''
 foo(f(int p)) {}
-main() {
+void f() {
   foo((test) {});
 }
 ''');
     await assertHasAssistAt('test', '''
 foo(f(int p)) {}
-main() {
+void f() {
   foo((int test) {});
 }
 ''');
@@ -455,7 +615,7 @@
   Future<void> test_parameter_hasExplicitType() async {
     await resolveTestCode('''
 foo(f(int p)) {}
-main() {
+void f() {
   foo((num test) {});
 }
 ''');
@@ -465,7 +625,7 @@
   Future<void> test_parameter_noPropagatedType() async {
     await resolveTestCode('''
 foo(f(p)) {}
-main() {
+void f() {
   foo((test) {});
 }
 ''');
@@ -481,7 +641,7 @@
 ''');
     await resolveTestCode('''
 import 'my_lib.dart';
-main() {
+void f() {
   foo((test) {});
 }
  ''');
@@ -498,7 +658,7 @@
     await resolveTestCode('''
 import 'my_lib.dart';
 class A<T> {
-  main() {
+  void m() {
     for (var item in getValues()) {
     }
   }
@@ -518,13 +678,13 @@
 ''');
     await resolveTestCode('''
 import 'my_lib.dart';
-main() {
+void f() {
   var v = getValues();
 }
 ''');
     await assertHasAssistAt('var ', '''
 import 'my_lib.dart';
-main() {
+void f() {
   List v = getValues();
 }
 ''');
@@ -534,14 +694,14 @@
     await resolveTestCode('''
 class _A {}
 _A getValue() => _A();
-main() {
+void f() {
   var v = getValue();
 }
 ''');
     await assertHasAssistAt('var ', '''
 class _A {}
 _A getValue() => _A();
-main() {
+void f() {
   _A v = getValue();
 }
 ''');
@@ -556,7 +716,7 @@
 ''');
     await resolveTestCode('''
 import 'my_lib.dart';
-main() {
+void f() {
   var v = getValue();
 }
 ''');
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_type_annotation_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_type_annotation_test.dart
index dd375be..edf86dc 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_type_annotation_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_type_annotation_test.dart
@@ -11,36 +11,13 @@
 
 void main() {
   defineReflectiveSuite(() {
-    defineReflectiveTests(AddTypeAnnotationLintTest);
     defineReflectiveTests(AddTypeAnnotationTest);
+    defineReflectiveTests(AlwaysSpecifyTypesLintTest);
+    defineReflectiveTests(PreferTypingUninitializedVariablesLintTest);
   });
 }
 
 @reflectiveTest
-class AddTypeAnnotationLintTest extends FixProcessorLintTest {
-  @override
-  FixKind get kind => DartFixKind.ADD_TYPE_ANNOTATION;
-
-  @override
-  String get lintCode => LintNames.always_specify_types;
-
-  // More coverage in the `add_type_annotation_test.dart` assist test.
-
-  Future<void> test_do_block() async {
-    await resolveTestCode('''
-class A {
-  final f = 0;
-}
-''');
-    await assertHasFix('''
-class A {
-  final int f = 0;
-}
-''');
-  }
-}
-
-@reflectiveTest
 class AddTypeAnnotationTest extends FixProcessorTest {
   @override
   FixKind get kind => DartFixKind.ADD_TYPE_ANNOTATION;
@@ -73,3 +50,55 @@
 ''');
   }
 }
+
+@reflectiveTest
+class AlwaysSpecifyTypesLintTest extends FixProcessorLintTest {
+  @override
+  FixKind get kind => DartFixKind.ADD_TYPE_ANNOTATION;
+
+  @override
+  String get lintCode => LintNames.always_specify_types;
+
+  // More coverage in the `add_type_annotation_test.dart` assist test.
+
+  Future<void> test_field() async {
+    await resolveTestCode('''
+class A {
+  final f = 0;
+}
+''');
+    await assertHasFix('''
+class A {
+  final int f = 0;
+}
+''');
+  }
+}
+
+@reflectiveTest
+class PreferTypingUninitializedVariablesLintTest extends FixProcessorLintTest {
+  @override
+  FixKind get kind => DartFixKind.ADD_TYPE_ANNOTATION;
+
+  @override
+  String get lintCode => LintNames.prefer_typing_uninitialized_variables;
+
+  // More coverage in the `add_type_annotation_test.dart` assist test.
+
+  Future<void> test_local() async {
+    await resolveTestCode('''
+void f() {
+  var l;
+  l = 0;
+  print(l);
+}
+''');
+    await assertHasFix('''
+void f() {
+  int l;
+  l = 0;
+  print(l);
+}
+''');
+  }
+}
diff --git a/pkg/analyzer/CHANGELOG.md b/pkg/analyzer/CHANGELOG.md
index 86f31f2..79fe467 100644
--- a/pkg/analyzer/CHANGELOG.md
+++ b/pkg/analyzer/CHANGELOG.md
@@ -6,7 +6,9 @@
 * Deprecated `CompilationUnitElement.types`, use `classes` instead.
 * Added `Element.nonSynthetic`, use it to get the element that caused creation
   of this element, e.g. the field for a synthetic getter.
+* `FieldElement.isFinal` is `true` only when the field is not synthetic.
 * Synthetic getters and setters now use `-1` as `nameOffset`.
+* Fixed bug that `defaultValueCode` is `null` for field formal parameters.
 
 ## 1.7.0
 * Require `meta: ^1.4.0`.
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index 57905b9..bbdc21f 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -1620,6 +1620,11 @@
   /// [nameOffset].
   DefaultFieldFormalParameterElementImpl(String name, int nameOffset)
       : super(name, nameOffset);
+
+  @override
+  String? get defaultValueCode {
+    return constantInitializer?.toSource();
+  }
 }
 
 /// A [ParameterElement] for parameters that have an initializer.
@@ -1632,8 +1637,7 @@
 
   @override
   String? get defaultValueCode {
-    var ast = constantInitializer;
-    return ast != null ? ast.toSource() : null;
+    return constantInitializer?.toSource();
   }
 }
 
diff --git a/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart
index b8c9a8a..96cc743 100644
--- a/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart
@@ -167,9 +167,6 @@
     var invokeType = rawType.instantiate(typeArguments);
     node.typeArgumentTypes = typeArguments;
     node.staticType = invokeType;
-
-    // TODO(srawlins): Verify that type arguments conform to bounds. This will
-    // probably be done later, not in this resolution phase.
   }
 
   void _resolveFunctionReferenceMethod({
diff --git a/pkg/analyzer/lib/src/error/type_arguments_verifier.dart b/pkg/analyzer/lib/src/error/type_arguments_verifier.dart
index a14d9f4..e4a6930 100644
--- a/pkg/analyzer/lib/src/error/type_arguments_verifier.dart
+++ b/pkg/analyzer/lib/src/error/type_arguments_verifier.dart
@@ -33,10 +33,22 @@
       _libraryElement.typeSystem as TypeSystemImpl;
 
   void checkFunctionExpressionInvocation(FunctionExpressionInvocation node) {
-    _checkTypeArguments(node);
+    _checkTypeArguments(
+      node.typeArguments?.arguments,
+      node.function.staticType,
+      node.staticInvokeType,
+    );
     _checkForImplicitDynamicInvoke(node);
   }
 
+  void checkFunctionReference(FunctionReference node) {
+    _checkTypeArguments(
+      node.typeArguments?.arguments,
+      node.function.staticType,
+      node.staticType,
+    );
+  }
+
   void checkListLiteral(ListLiteral node) {
     var typeArguments = node.typeArguments;
     if (typeArguments != null) {
@@ -68,7 +80,11 @@
   }
 
   void checkMethodInvocation(MethodInvocation node) {
-    _checkTypeArguments(node);
+    _checkTypeArguments(
+      node.typeArguments?.arguments,
+      node.function.staticType,
+      node.staticInvokeType,
+    );
     _checkForImplicitDynamicInvoke(node);
   }
 
@@ -324,63 +340,65 @@
     }
   }
 
-  /// Verify that the given [typeArguments] are all within their bounds, as
-  /// defined by the given [element].
-  void _checkTypeArguments(InvocationExpression node) {
-    var typeArgumentList = node.typeArguments?.arguments;
+  /// Verify that each type argument in [typeArgumentList] is within its bounds,
+  /// as defined by [genericType].
+  void _checkTypeArguments(
+    List<TypeAnnotation>? typeArgumentList,
+    DartType? genericType,
+    DartType? instantiatedType,
+  ) {
     if (typeArgumentList == null) {
       return;
     }
 
-    var genericType = node.function.staticType;
-    var instantiatedType = node.staticInvokeType;
-    if (genericType is FunctionType && instantiatedType is FunctionType) {
-      var fnTypeParams = genericType.typeFormals;
-      var typeArgs = typeArgumentList.map((t) => t.typeOrThrow).toList();
+    if (genericType is! FunctionType || instantiatedType is! FunctionType) {
+      return;
+    }
 
-      // If the amount mismatches, clean up the lists to be substitutable. The
-      // mismatch in size is reported elsewhere, but we must successfully
-      // perform substitution to validate bounds on mismatched lists.
-      final providedLength = math.min(typeArgs.length, fnTypeParams.length);
-      fnTypeParams = fnTypeParams.sublist(0, providedLength);
-      typeArgs = typeArgs.sublist(0, providedLength);
+    var fnTypeParams = genericType.typeFormals;
+    var typeArgs = typeArgumentList.map((t) => t.typeOrThrow).toList();
 
-      for (int i = 0; i < providedLength; i++) {
-        // Check the `extends` clause for the type parameter, if any.
-        //
-        // Also substitute to handle cases like this:
-        //
-        //     <TFrom, TTo extends TFrom>
-        //     <TFrom, TTo extends Iterable<TFrom>>
-        //     <T extends Cloneable<T>>
-        //
-        DartType argType = typeArgs[i];
+    // If the amount mismatches, clean up the lists to be substitutable. The
+    // mismatch in size is reported elsewhere, but we must successfully
+    // perform substitution to validate bounds on mismatched lists.
+    var providedLength = math.min(typeArgs.length, fnTypeParams.length);
+    fnTypeParams = fnTypeParams.sublist(0, providedLength);
+    typeArgs = typeArgs.sublist(0, providedLength);
 
-        if (argType is FunctionType && argType.typeFormals.isNotEmpty) {
-          if (!_libraryElement.featureSet.isEnabled(Feature.generic_metadata)) {
-            _errorReporter.reportErrorForNode(
-              CompileTimeErrorCode
-                  .GENERIC_FUNCTION_TYPE_CANNOT_BE_TYPE_ARGUMENT,
-              typeArgumentList[i],
-            );
-            continue;
-          }
-        }
+    for (int i = 0; i < providedLength; i++) {
+      // Check the `extends` clause for the type parameter, if any.
+      //
+      // Also substitute to handle cases like this:
+      //
+      //     <TFrom, TTo extends TFrom>
+      //     <TFrom, TTo extends Iterable<TFrom>>
+      //     <T extends Cloneable<T>>
+      //
+      DartType argType = typeArgs[i];
 
-        var fnTypeParam = fnTypeParams[i];
-        var rawBound = fnTypeParam.bound;
-        if (rawBound == null) {
+      if (argType is FunctionType && argType.typeFormals.isNotEmpty) {
+        if (!_libraryElement.featureSet.isEnabled(Feature.generic_metadata)) {
+          _errorReporter.reportErrorForNode(
+            CompileTimeErrorCode.GENERIC_FUNCTION_TYPE_CANNOT_BE_TYPE_ARGUMENT,
+            typeArgumentList[i],
+          );
           continue;
         }
+      }
 
-        var substitution = Substitution.fromPairs(fnTypeParams, typeArgs);
-        var bound = substitution.substituteType(rawBound);
-        if (!_typeSystem.isSubtypeOf(argType, bound)) {
-          _errorReporter.reportErrorForNode(
-              CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS,
-              typeArgumentList[i],
-              [argType, fnTypeParam.name, bound]);
-        }
+      var fnTypeParam = fnTypeParams[i];
+      var rawBound = fnTypeParam.bound;
+      if (rawBound == null) {
+        continue;
+      }
+
+      var substitution = Substitution.fromPairs(fnTypeParams, typeArgs);
+      var bound = substitution.substituteType(rawBound);
+      if (!_typeSystem.isSubtypeOf(argType, bound)) {
+        _errorReporter.reportErrorForNode(
+            CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS,
+            typeArgumentList[i],
+            [argType, fnTypeParam.name, bound]);
       }
     }
   }
diff --git a/pkg/analyzer/lib/src/error/use_result_verifier.dart b/pkg/analyzer/lib/src/error/use_result_verifier.dart
index 7a32f02..c96ee1d 100644
--- a/pkg/analyzer/lib/src/error/use_result_verifier.dart
+++ b/pkg/analyzer/lib/src/error/use_result_verifier.dart
@@ -64,14 +64,24 @@
 
     var message = _getUseResultMessage(annotation);
     if (message == null || message.isEmpty) {
-      _errorReporter
-          .reportErrorForNode(HintCode.UNUSED_RESULT, node, [displayName]);
-    } else {
       _errorReporter.reportErrorForNode(
-          HintCode.UNUSED_RESULT_WITH_MESSAGE, node, [displayName, message]);
+          HintCode.UNUSED_RESULT, _getNodeToAnnotate(node), [displayName]);
+    } else {
+      _errorReporter.reportErrorForNode(HintCode.UNUSED_RESULT_WITH_MESSAGE,
+          _getNodeToAnnotate(node), [displayName, message]);
     }
   }
 
+  static AstNode _getNodeToAnnotate(AstNode node) {
+    if (node is MethodInvocation) {
+      return node.methodName;
+    }
+    if (node is PropertyAccess) {
+      return node.propertyName;
+    }
+    return node;
+  }
+
   static String? _getUseResultMessage(ElementAnnotation annotation) {
     if (annotation.element is PropertyAccessorElement) {
       return null;
@@ -94,7 +104,9 @@
       return false;
     }
 
-    if (parent is ParenthesizedExpression || parent is ConditionalExpression) {
+    if (parent is ParenthesizedExpression ||
+        parent is ConditionalExpression ||
+        parent is CascadeExpression) {
       return _isUsed(parent);
     }
 
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index cfe297c4..7d6e8ab 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -757,6 +757,11 @@
   }
 
   @override
+  void visitFunctionReference(FunctionReference node) {
+    _typeArgumentsVerifier.checkFunctionReference(node);
+  }
+
+  @override
   void visitFunctionTypeAlias(FunctionTypeAlias node) {
     _checkForBuiltInIdentifierAsName(
         node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME);
diff --git a/pkg/analyzer/lib/src/pubspec/pubspec_validator.dart b/pkg/analyzer/lib/src/pubspec/pubspec_validator.dart
index 5bed787..b0ab45b 100644
--- a/pkg/analyzer/lib/src/pubspec/pubspec_validator.dart
+++ b/pkg/analyzer/lib/src/pubspec/pubspec_validator.dart
@@ -7,6 +7,7 @@
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer/src/pubspec/validators/dependency_validator.dart';
+import 'package:analyzer/src/pubspec/validators/field_validator.dart';
 import 'package:analyzer/src/pubspec/validators/flutter_validator.dart';
 import 'package:analyzer/src/pubspec/validators/name_validator.dart';
 import 'package:source_span/src/span.dart';
@@ -21,6 +22,16 @@
 
   BasePubspecValidator(this.provider, this.source);
 
+  String? asString(dynamic node) {
+    if (node is String) {
+      return node;
+    }
+    if (node is YamlScalar && node.value is String) {
+      return node.value as String;
+    }
+    return null;
+  }
+
   /// Report an error for the given node.
   void reportErrorForNode(
       ErrorReporter reporter, YamlNode node, ErrorCode errorCode,
@@ -70,6 +81,7 @@
   final Source source;
 
   final DependencyValidator _dependencyValidator;
+  final FieldValidator _fieldValidator;
   final FlutterValidator _flutterValidator;
   final NameValidator _nameValidator;
 
@@ -77,6 +89,7 @@
   /// [source].
   PubspecValidator(this.provider, this.source)
       : _dependencyValidator = DependencyValidator(provider, source),
+        _fieldValidator = FieldValidator(provider, source),
         _flutterValidator = FlutterValidator(provider, source),
         _nameValidator = NameValidator(provider, source);
 
@@ -90,6 +103,7 @@
     );
 
     _dependencyValidator.validate(reporter, contents);
+    _fieldValidator.validate(reporter, contents);
     _flutterValidator.validate(reporter, contents);
     _nameValidator.validate(reporter, contents);
 
diff --git a/pkg/analyzer/lib/src/pubspec/pubspec_warning_code.dart b/pkg/analyzer/lib/src/pubspec/pubspec_warning_code.dart
index 0c03a1f..7a2d01d 100644
--- a/pkg/analyzer/lib/src/pubspec/pubspec_warning_code.dart
+++ b/pkg/analyzer/lib/src/pubspec/pubspec_warning_code.dart
@@ -4,10 +4,10 @@
 
 import 'package:analyzer/error/error.dart';
 
-/// The error codes used for warnings in analysis options files. The convention
-/// for this class is for the name of the error code to indicate the problem
-/// that caused the error to be generated and for the error message to explain
-/// what is wrong and, when appropriate, how the problem can be corrected.
+/// The error codes used for warnings in pubspec files. The convention for this
+/// class is for the name of the error code to indicate the problem that caused
+/// the error to be generated and for the error message to explain what is wrong
+/// and, when appropriate, how the problem can be corrected.
 class PubspecWarningCode extends ErrorCode {
   /// A code indicating that a specified asset does not exist.
   ///
@@ -46,6 +46,12 @@
           "The value of the '{0}' field is expected to be a map.",
           correction: "Try converting the value to be a map.");
 
+  /// A code indicating that a field is deprecated.
+  static const PubspecWarningCode DEPRECATED_FIELD = PubspecWarningCode(
+      'DEPRECATED_FIELD',
+      "The '{0}' field is no longer used and may be removed.",
+      correction: "Try removing the field.");
+
   /// A code indicating that the value of the flutter field is not a map.
   static const PubspecWarningCode FLUTTER_FIELD_NOT_MAP = PubspecWarningCode(
       'FLUTTER_FIELD_NOT_MAP',
diff --git a/pkg/analyzer/lib/src/pubspec/validators/dependency_validator.dart b/pkg/analyzer/lib/src/pubspec/validators/dependency_validator.dart
index 49f859c..e091ea27 100644
--- a/pkg/analyzer/lib/src/pubspec/validators/dependency_validator.dart
+++ b/pkg/analyzer/lib/src/pubspec/validators/dependency_validator.dart
@@ -26,7 +26,7 @@
     bool isPublishablePackage = false;
     var version = contents[PubspecField.VERSION_FIELD];
     if (version != null) {
-      var publishTo = _asString(contents[PubspecField.PUBLISH_TO_FIELD]);
+      var publishTo = asString(contents[PubspecField.PUBLISH_TO_FIELD]);
       if (publishTo != 'none') {
         isPublishablePackage = true;
       }
@@ -46,16 +46,6 @@
     }
   }
 
-  String? _asString(dynamic node) {
-    if (node is String) {
-      return node;
-    }
-    if (node is YamlScalar && node.value is String) {
-      return node.value as String;
-    }
-    return null;
-  }
-
   /// Return a map whose keys are the names of declared dependencies and whose
   /// values are the specifications of those dependencies. The map is extracted
   /// from the given [contents] using the given [key].
@@ -84,7 +74,7 @@
   void _validatePathEntries(ErrorReporter reporter, YamlNode dependency,
       bool checkForPathAndGitDeps) {
     if (dependency is YamlMap) {
-      var pathEntry = _asString(dependency[PubspecField.PATH_FIELD]);
+      var pathEntry = asString(dependency[PubspecField.PATH_FIELD]);
       if (pathEntry != null) {
         YamlNode pathKey() => dependency.getKey(PubspecField.PATH_FIELD)!;
         YamlNode pathValue() => dependency.valueAt(PubspecField.PATH_FIELD)!;
diff --git a/pkg/analyzer/lib/src/pubspec/validators/field_validator.dart b/pkg/analyzer/lib/src/pubspec/validators/field_validator.dart
new file mode 100644
index 0000000..1d2ac0e
--- /dev/null
+++ b/pkg/analyzer/lib/src/pubspec/validators/field_validator.dart
@@ -0,0 +1,33 @@
+// 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/error/listener.dart';
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/pubspec/pubspec_validator.dart';
+import 'package:analyzer/src/pubspec/pubspec_warning_code.dart';
+import 'package:yaml/yaml.dart';
+
+class FieldValidator extends BasePubspecValidator {
+  static const deprecatedFields = [
+    'author',
+    'authors',
+    'transformers',
+    'web',
+  ];
+
+  FieldValidator(ResourceProvider provider, Source source)
+      : super(provider, source);
+
+  /// Validate fields.
+  void validate(ErrorReporter reporter, Map<dynamic, YamlNode> contents) {
+    for (var field in contents.keys) {
+      var name = asString(field);
+      if (name != null && deprecatedFields.contains(name)) {
+        reportErrorForNode(
+            reporter, field, PubspecWarningCode.DEPRECATED_FIELD, [name]);
+      }
+    }
+  }
+}
diff --git a/pkg/analyzer/test/src/dart/resolution/prefix_element_test.dart b/pkg/analyzer/test/src/dart/resolution/prefix_element_test.dart
index a87e051..06faef1 100644
--- a/pkg/analyzer/test/src/dart/resolution/prefix_element_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/prefix_element_test.dart
@@ -2,8 +2,6 @@
 // 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/src/dart/element/element.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -63,20 +61,20 @@
     var aImport = findElement.importFind('package:test/a.dart');
     var bImport = findElement.importFind('package:test/b.dart');
 
-    _assertMultiplyDefinedElement(
+    expect(
       scope.lookup('foo').getter,
-      [
+      multiplyDefinedElementMatcher([
         aImport.topGet('foo'),
         bImport.topGet('foo'),
-      ],
+      ]),
     );
 
-    _assertMultiplyDefinedElement(
+    expect(
       scope.lookup('foo').setter,
-      [
+      multiplyDefinedElementMatcher([
         aImport.topSet('foo'),
         bImport.topSet('foo'),
-      ],
+      ]),
     );
   }
 
@@ -250,12 +248,4 @@
       scope.lookup('cos').getter,
     );
   }
-
-  void _assertMultiplyDefinedElement(
-    Element? element,
-    List<Element> expected,
-  ) {
-    element as MultiplyDefinedElementImpl;
-    expect(element.conflictingElements, unorderedEquals(expected));
-  }
 }
diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart
index fefb145..188a40a 100644
--- a/pkg/analyzer/test/src/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/diagnostics/test_all.dart
@@ -684,13 +684,13 @@
 import 'unused_import_test.dart' as unused_import;
 import 'unused_label_test.dart' as unused_label;
 import 'unused_local_variable_test.dart' as unused_local_variable;
+import 'unused_result_test.dart' as unused_result;
 import 'unused_shown_name_test.dart' as unused_shown_name;
 import 'uri_does_not_exist_test.dart' as uri_does_not_exist;
 import 'uri_with_interpolation_test.dart' as uri_with_interpolation;
 import 'use_of_native_extension_test.dart' as use_of_native_extension;
 import 'use_of_nullable_value_test.dart' as use_of_nullable_value_test;
 import 'use_of_void_result_test.dart' as use_of_void_result;
-import 'use_result_test.dart' as use_result;
 import 'variable_type_mismatch_test.dart' as variable_type_mismatch;
 import 'void_with_type_arguments_test.dart' as void_with_type_arguments_test;
 import 'wrong_number_of_parameters_for_operator_test.dart'
@@ -1168,13 +1168,13 @@
     unused_import.main();
     unused_label.main();
     unused_local_variable.main();
+    unused_result.main();
     unused_shown_name.main();
     uri_does_not_exist.main();
     uri_with_interpolation.main();
     use_of_native_extension.main();
     use_of_nullable_value_test.main();
     use_of_void_result.main();
-    use_result.main();
     variable_type_mismatch.main();
     void_with_type_arguments_test.main();
     wrong_number_of_parameters_for_operator.main();
diff --git a/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart b/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart
index 25184c2..72f8042 100644
--- a/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart
@@ -451,6 +451,32 @@
 ''');
   }
 
+  test_functionReference() async {
+    await assertErrorsInCode('''
+void bar(void Function<T extends num>(T a) foo) {
+  foo<String>;
+}
+''', [
+      error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 56, 6),
+    ]);
+  }
+
+  test_functionReference_matching() async {
+    await assertNoErrorsInCode('''
+void bar(void Function<T extends num>(T a) foo) {
+  foo<int>;
+}
+''');
+  }
+
+  test_functionReference_regularBounded() async {
+    await assertNoErrorsInCode('''
+void bar(void Function<T>(T a) foo) {
+  foo<String>;
+}
+''');
+  }
+
   test_genericFunctionTypeArgument_invariant() async {
     await assertErrorsInCode(r'''
 typedef F = T Function<T>(T);
diff --git a/pkg/analyzer/test/src/diagnostics/use_result_test.dart b/pkg/analyzer/test/src/diagnostics/unused_result_test.dart
similarity index 85%
rename from pkg/analyzer/test/src/diagnostics/use_result_test.dart
rename to pkg/analyzer/test/src/diagnostics/unused_result_test.dart
index 154d9c0..0a0a134 100644
--- a/pkg/analyzer/test/src/diagnostics/use_result_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/unused_result_test.dart
@@ -9,12 +9,12 @@
 
 main() {
   defineReflectiveSuite(() {
-    defineReflectiveTests(UseResultTest);
+    defineReflectiveTests(UnusedResultTest);
   });
 }
 
 @reflectiveTest
-class UseResultTest extends PubPackageResolutionTest {
+class UnusedResultTest extends PubPackageResolutionTest {
   @override
   void setUp() {
     super.setUp();
@@ -114,7 +114,7 @@
   A().foo;
 }
 ''', [
-      error(HintCode.UNUSED_RESULT, 100, 7),
+      error(HintCode.UNUSED_RESULT, 104, 3),
     ]);
   }
 
@@ -207,7 +207,7 @@
   A().foo;
 }
 ''', [
-      error(HintCode.UNUSED_RESULT, 91, 7),
+      error(HintCode.UNUSED_RESULT, 95, 3),
     ]);
   }
 
@@ -224,7 +224,7 @@
   b ? A().foo : 0;
 }
 ''', [
-      error(HintCode.UNUSED_RESULT, 98, 7),
+      error(HintCode.UNUSED_RESULT, 102, 3),
     ]);
   }
 
@@ -241,7 +241,7 @@
   b ? (A().foo) : 0;
 }
 ''', [
-      error(HintCode.UNUSED_RESULT, 99, 7),
+      error(HintCode.UNUSED_RESULT, 103, 3),
     ]);
   }
 
@@ -262,7 +262,7 @@
   });
 }
 ''', [
-      error(HintCode.UNUSED_RESULT, 126, 7),
+      error(HintCode.UNUSED_RESULT, 130, 3),
     ]);
   }
 
@@ -346,6 +346,23 @@
 ''');
   }
 
+  test_field_static_result_unassigned() async {
+    await assertErrorsInCode(r'''
+import 'package:meta/meta.dart';
+
+class A {
+  @useResult
+  static int foo = 0;
+}
+
+void main() {
+  A.foo;
+}
+''', [
+      error(HintCode.UNUSED_RESULT, 100, 3),
+    ]);
+  }
+
   test_getter_result_passed() async {
     await assertNoErrorsInCode(r'''
 import 'package:meta/meta.dart';
@@ -419,7 +436,9 @@
 void main() {
   A().foo;
 }
-''', [error(HintCode.UNUSED_RESULT, 96, 7)]);
+''', [
+      error(HintCode.UNUSED_RESULT, 100, 3),
+    ]);
   }
 
   test_method_result_assigned() async {
@@ -472,12 +491,10 @@
   test_method_result_targetedMethod() async {
     await assertNoErrorsInCode(r'''
 import 'package:meta/meta.dart';
-
 class A {
   @useResult
   String foo() => '';
 }
-
 void main() {
   A().foo().toString(); // OK
 }
@@ -512,7 +529,27 @@
   A().foo();
 }
 ''', [
-      error(HintCode.UNUSED_RESULT, 94, 9),
+      error(HintCode.UNUSED_RESULT, 98, 3),
+    ]);
+  }
+
+  test_method_result_unassigned_cascade() async {
+    await assertErrorsInCode(r'''
+import 'package:meta/meta.dart';
+
+class C {
+  @useResult
+  C m1() => throw '';
+  
+  C m2() => throw '';
+  
+  void m3() {
+    m2()..m1();
+  }
+}
+''', [
+      error(HintCode.UNUSED_RESULT, 131, 2,
+          messageContains: "'m1' should be used."),
     ]);
   }
 
@@ -530,6 +567,20 @@
 ''');
   }
 
+  test_topLevelFunction_result_assigned_cascade() async {
+    await assertNoErrorsInCode(r'''
+import 'package:meta/meta.dart';
+
+@useResult
+int foo() => 0;
+
+void main() {
+  var x = foo()..toString(); // OK
+  print(x);
+}
+''');
+  }
+
   test_topLevelFunction_result_passed() async {
     await assertNoErrorsInCode(r'''
 import 'package:meta/meta.dart';
@@ -569,7 +620,6 @@
 ''');
   }
 
-  // todo(pq):implement visitExpressionStatement?
   test_topLevelFunction_result_unassigned() async {
     await assertErrorsInCode(r'''
 import 'package:meta/meta.dart';
@@ -585,7 +635,22 @@
   baz(); // OK
 }
 ''', [
-      error(HintCode.UNUSED_RESULT, 108, 5),
+      error(HintCode.UNUSED_RESULT, 108, 3),
+    ]);
+  }
+
+  test_topLevelFunction_result_unassigned_cascade() async {
+    await assertErrorsInCode(r'''
+import 'package:meta/meta.dart';
+
+@useResult
+int foo() => 0;
+
+void main() {
+  foo()..toString();
+}
+''', [
+      error(HintCode.UNUSED_RESULT, 78, 3),
     ]);
   }
 
diff --git a/pkg/analyzer/test/src/pubspec/diagnostics/deprecated_field_test.dart b/pkg/analyzer/test/src/pubspec/diagnostics/deprecated_field_test.dart
new file mode 100644
index 0000000..63d1a3b
--- /dev/null
+++ b/pkg/analyzer/test/src/pubspec/diagnostics/deprecated_field_test.dart
@@ -0,0 +1,48 @@
+// 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/src/pubspec/pubspec_warning_code.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../pubspec_test_support.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(DeprecatedFieldTest);
+  });
+}
+
+@reflectiveTest
+class DeprecatedFieldTest extends PubspecDiagnosticTest {
+  test_deprecated_author() {
+    assertErrors('''
+name: sample
+author: foo
+''', [PubspecWarningCode.DEPRECATED_FIELD]);
+  }
+
+  test_deprecated_authors() {
+    assertErrors('''
+name: sample
+authors:
+  - foo
+  - bar
+''', [PubspecWarningCode.DEPRECATED_FIELD]);
+  }
+
+  test_deprecated_transformers() {
+    assertErrors('''
+name: sample
+transformers:
+  - foo
+''', [PubspecWarningCode.DEPRECATED_FIELD]);
+  }
+
+  test_deprecated_web() {
+    assertErrors('''
+name: sample
+web: foo
+''', [PubspecWarningCode.DEPRECATED_FIELD]);
+  }
+}
diff --git a/pkg/analyzer/test/src/pubspec/diagnostics/test_all.dart b/pkg/analyzer/test/src/pubspec/diagnostics/test_all.dart
index 7b1b0ed..4a862bc 100644
--- a/pkg/analyzer/test/src/pubspec/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/pubspec/diagnostics/test_all.dart
@@ -10,6 +10,7 @@
 import 'asset_field_not_list_test.dart' as asset_field_not_list;
 import 'asset_not_string_test.dart' as asset_not_string;
 import 'dependencies_field_not_map_test.dart' as dependencies_field_not_map;
+import 'deprecated_field_test.dart' as deprecated_field;
 import 'flutter_field_not_map_test.dart' as flutter_field_not_map;
 import 'invalid_dependency_test.dart' as invalid_dependency;
 import 'missing_name_test.dart' as missing_name;
@@ -26,6 +27,7 @@
     asset_field_not_list.main();
     asset_not_string.main();
     dependencies_field_not_map.main();
+    deprecated_field.main();
     flutter_field_not_map.main();
     invalid_dependency.main();
     missing_name.main();
diff --git a/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart b/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
index 14ea739..4db68d9 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
@@ -543,6 +543,17 @@
   }
 
   @override
+  void visitDeclaredIdentifier(DeclaredIdentifier node) {
+    var identifier = node.identifier;
+    if (identifier == entity &&
+        offset < identifier.offset &&
+        node.type == null) {
+      // Adding a type before the identifier.
+      optype.includeTypeNameSuggestions = true;
+    }
+  }
+
+  @override
   void visitDefaultFormalParameter(DefaultFormalParameter node) {
     if (identical(entity, node.defaultValue)) {
       optype.completionLocation = 'DefaultFormalParameter_defaultValue';
diff --git a/pkg/analyzer_plugin/pubspec.yaml b/pkg/analyzer_plugin/pubspec.yaml
index 06f98e5..1a166c4 100644
--- a/pkg/analyzer_plugin/pubspec.yaml
+++ b/pkg/analyzer_plugin/pubspec.yaml
@@ -1,7 +1,6 @@
 name: analyzer_plugin
 description: A framework and support code for building plugins for the analysis server.
 version: 0.6.0
-author: Dart Team <misc@dartlang.org>
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/analyzer_plugin
 
 environment:
diff --git a/pkg/dart2native/analysis_options.yaml b/pkg/dart2native/analysis_options.yaml
index c84f27c..dd90435 100644
--- a/pkg/dart2native/analysis_options.yaml
+++ b/pkg/dart2native/analysis_options.yaml
@@ -1,13 +1,8 @@
-include: package:pedantic/analysis_options.1.8.0.yaml
-
-analyzer:
-  errors:
-    # Increase the severity of several hints.
-    prefer_single_quotes: warning
-    unused_import: warning
+include: package:lints/recommended.yaml
 
 linter:
   rules:
-    - directives_ordering
-    - prefer_relative_imports
-    - prefer_single_quotes
+    # Enable additional rules.
+    depend_on_referenced_packages: true
+    directives_ordering: true
+    sort_pub_dependencies: true
diff --git a/pkg/dart2native/bin/dart2native.dart b/pkg/dart2native/bin/dart2native.dart
index ed18daf..d5d888a 100644
--- a/pkg/dart2native/bin/dart2native.dart
+++ b/pkg/dart2native/bin/dart2native.dart
@@ -103,7 +103,7 @@
   final String sourceFile = parsedArgs.rest[0];
   if (!FileSystemEntity.isFileSync(sourceFile)) {
     stderr.writeln(
-        '"${sourceFile}" is not a file. See \'--help\' for more information.');
+        '"$sourceFile" is not a file. See \'--help\' for more information.');
     await stderr.flush();
     exit(1);
   }
diff --git a/pkg/dart2native/lib/dart2native.dart b/pkg/dart2native/lib/dart2native.dart
index 3e51b53..3329a8d 100644
--- a/pkg/dart2native/lib/dart2native.dart
+++ b/pkg/dart2native/lib/dart2native.dart
@@ -52,10 +52,10 @@
     genKernel,
     '--platform',
     platformDill,
-    if (enableExperiment.isNotEmpty) '--enable-experiment=${enableExperiment}',
+    if (enableExperiment.isNotEmpty) '--enable-experiment=$enableExperiment',
     '--aot',
     '-Ddart.vm.product=true',
-    ...(defines.map((d) => '-D${d}')),
+    ...(defines.map((d) => '-D$d')),
     if (packages != null) ...['--packages', packages],
     '-o',
     kernelFile,
@@ -73,7 +73,7 @@
     List<String> extraGenSnapshotOptions) {
   return Process.run(genSnapshot, [
     '--snapshot-kind=app-aot-elf',
-    '--elf=${snapshotFile}',
+    '--elf=$snapshotFile',
     if (debugFile != null) '--save-debugging-info=$debugFile',
     if (debugFile != null) '--dwarf-stack-traces',
     if (debugFile != null) '--strip',
diff --git a/pkg/dart2native/lib/generate.dart b/pkg/dart2native/lib/generate.dart
index 3df6176..8c773d6 100644
--- a/pkg/dart2native/lib/generate.dart
+++ b/pkg/dart2native/lib/generate.dart
@@ -3,17 +3,19 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:io';
+
 import 'package:path/path.dart' as path;
+
 import 'dart2native.dart';
 
 final Directory binDir = File(Platform.resolvedExecutable).parent;
 final String executableSuffix = Platform.isWindows ? '.exe' : '';
 final String dartaotruntime =
-    path.join(binDir.path, 'dartaotruntime${executableSuffix}');
+    path.join(binDir.path, 'dartaotruntime$executableSuffix');
 final String genKernel =
     path.join(binDir.path, 'snapshots', 'gen_kernel.dart.snapshot');
 final String genSnapshot =
-    path.join(binDir.path, 'utils', 'gen_snapshot${executableSuffix}');
+    path.join(binDir.path, 'utils', 'gen_snapshot$executableSuffix');
 final String productPlatformDill = path.join(
     binDir.parent.path, 'lib', '_internal', 'vm_platform_strong_product.dill');
 
@@ -42,12 +44,11 @@
       'exe': Kind.exe,
     }[kind];
     final sourceWithoutDart = sourcePath.replaceFirst(RegExp(r'\.dart$'), '');
-    final outputPath = path.canonicalize(path.normalize(outputFile != null
-        ? outputFile
-        : {
-            Kind.aot: '${sourceWithoutDart}.aot',
-            Kind.exe: '${sourceWithoutDart}.exe',
-          }[outputKind]));
+    final outputPath = path.canonicalize(path.normalize(outputFile ??
+        {
+          Kind.aot: '$sourceWithoutDart.aot',
+          Kind.exe: '$sourceWithoutDart.exe',
+        }[outputKind]));
     final debugPath =
         debugFile != null ? path.canonicalize(path.normalize(debugFile)) : null;
 
@@ -113,7 +114,7 @@
       }
     }
 
-    print('Generated: ${outputPath}');
+    print('Generated: $outputPath');
   } finally {
     tempDir.deleteSync(recursive: true);
   }
diff --git a/pkg/dart2native/pubspec.yaml b/pkg/dart2native/pubspec.yaml
index 4c274e7..1e858a5 100644
--- a/pkg/dart2native/pubspec.yaml
+++ b/pkg/dart2native/pubspec.yaml
@@ -11,8 +11,9 @@
 
 dependencies:
    args: ^1.4.0
-   path: any
    front_end:
      path: ../front_end
+   path: any
 
 dev_dependencies:
+  lints: any
diff --git a/pkg/dartdev/lib/dartdev.dart b/pkg/dartdev/lib/dartdev.dart
index 83ba6bd..ed23c1e 100644
--- a/pkg/dartdev/lib/dartdev.dart
+++ b/pkg/dartdev/lib/dartdev.dart
@@ -63,6 +63,9 @@
 }
 
 class DartdevRunner extends CommandRunner<int> {
+  static const String dartdevDescription =
+      'A command-line utility for Dart development';
+
   @override
   final ArgParser argParser = ArgParser(
     usageLineLength: dartdevUsageLineLength,
@@ -71,8 +74,7 @@
 
   final bool verbose;
 
-  static const String dartdevDescription =
-      'A command-line utility for Dart development';
+  Analytics _analytics;
 
   DartdevRunner(List<String> args)
       : verbose = args.contains('-v') || args.contains('--verbose'),
@@ -108,17 +110,15 @@
     addCommand(TestCommand());
   }
 
-  @override
-  String get usageFooter =>
-      'See https://dart.dev/tools/dart-tool for detailed documentation.';
+  @visibleForTesting
+  Analytics get analytics => _analytics;
 
   @override
   String get invocation =>
       'dart ${verbose ? '[vm-options] ' : ''}<command|dart-file> [arguments]';
-
-  @visibleForTesting
-  Analytics get analytics => _analytics;
-  Analytics _analytics;
+  @override
+  String get usageFooter =>
+      'See https://dart.dev/tools/dart-tool for detailed documentation.';
 
   @override
   Future<int> runCommand(ArgResults topLevelResults) async {
diff --git a/pkg/telemetry/pubspec.yaml b/pkg/telemetry/pubspec.yaml
index 950cf63..98d68d6 100644
--- a/pkg/telemetry/pubspec.yaml
+++ b/pkg/telemetry/pubspec.yaml
@@ -2,7 +2,6 @@
 description: A library to facilitate reporting analytics and crash reports.
 # This package is not intended for consumption on pub.dev. DO NOT publish.
 publish_to: none
-author: Dart Team <misc@dartlang.org>
 
 environment:
   sdk: '>=2.12.0 <3.0.0'
diff --git a/pkg/vm_service/example/vm_service_assert.dart b/pkg/vm_service/example/vm_service_assert.dart
index f9f2d6e..b458ad6 100644
--- a/pkg/vm_service/example/vm_service_assert.dart
+++ b/pkg/vm_service/example/vm_service_assert.dart
@@ -159,6 +159,7 @@
   if (obj == "ServiceUnregistered") return obj;
   if (obj == "TimelineEvents") return obj;
   if (obj == "TimelineStreamSubscriptionsUpdate") return obj;
+  if (obj == "UserTagChanged") return obj;
   if (obj == "VMFlagUpdate") return obj;
   if (obj == "VMUpdate") return obj;
   if (obj == "WriteEvent") return obj;
diff --git a/pkg/vm_service/java/version.properties b/pkg/vm_service/java/version.properties
index 88bab86..62cbe79 100644
--- a/pkg/vm_service/java/version.properties
+++ b/pkg/vm_service/java/version.properties
@@ -1 +1 @@
-version=3.47
+version=3.48
diff --git a/pkg/vm_service/lib/src/vm_service.dart b/pkg/vm_service/lib/src/vm_service.dart
index 983d0f7..903bb0c 100644
--- a/pkg/vm_service/lib/src/vm_service.dart
+++ b/pkg/vm_service/lib/src/vm_service.dart
@@ -26,7 +26,7 @@
         HeapSnapshotObjectNoData,
         HeapSnapshotObjectNullData;
 
-const String vmServiceVersion = '3.47.0';
+const String vmServiceVersion = '3.48.0';
 
 /// @optional
 const String optional = 'optional';
@@ -1150,6 +1150,7 @@
   /// Debug | PauseStart, PauseExit, PauseBreakpoint, PauseInterrupted,
   /// PauseException, PausePostRequest, Resume, BreakpointAdded,
   /// BreakpointResolved, BreakpointRemoved, BreakpointUpdated, Inspect, None
+  /// Profiler | UserTagChanged
   /// GC | GC
   /// Extension | Extension
   /// Timeline | TimelineEvents, TimelineStreamsSubscriptionUpdate
@@ -1690,6 +1691,9 @@
   // PauseStart, PauseExit, PauseBreakpoint, PauseInterrupted, PauseException, PausePostRequest, Resume, BreakpointAdded, BreakpointResolved, BreakpointRemoved, BreakpointUpdated, Inspect, None
   Stream<Event> get onDebugEvent => _getEventController('Debug').stream;
 
+  // UserTagChanged
+  Stream<Event> get onProfilerEvent => _getEventController('Profiler').stream;
+
   // GC
   Stream<Event> get onGCEvent => _getEventController('GC').stream;
 
@@ -2384,6 +2388,7 @@
   static const String kVM = 'VM';
   static const String kIsolate = 'Isolate';
   static const String kDebug = 'Debug';
+  static const String kProfiler = 'Profiler';
   static const String kGC = 'GC';
   static const String kExtension = 'Extension';
   static const String kTimeline = 'Timeline';
@@ -2496,6 +2501,9 @@
   /// Notification that a Service has been removed from the Service Protocol
   /// from another client.
   static const String kServiceUnregistered = 'ServiceUnregistered';
+
+  /// Notification that the UserTag for an isolate has been changed.
+  static const String kUserTagChanged = 'UserTagChanged';
 }
 
 /// Adding new values to `InstanceKind` is considered a backwards compatible
@@ -3885,6 +3893,14 @@
   @optional
   bool? last;
 
+  /// The current UserTag label.
+  @optional
+  String? updatedTag;
+
+  /// The previous UserTag label.
+  @optional
+  String? previousTag;
+
   /// Binary data associated with the event.
   ///
   /// This is provided for the event kinds:
@@ -3917,6 +3933,8 @@
     this.flag,
     this.newValue,
     this.last,
+    this.updatedTag,
+    this.previousTag,
     this.data,
   });
 
@@ -3959,6 +3977,8 @@
     flag = json['flag'];
     newValue = json['newValue'];
     last = json['last'];
+    updatedTag = json['updatedTag'];
+    previousTag = json['previousTag'];
     data = json['data'];
   }
 
@@ -3998,6 +4018,8 @@
     _setIfNotNull(json, 'flag', flag);
     _setIfNotNull(json, 'newValue', newValue);
     _setIfNotNull(json, 'last', last);
+    _setIfNotNull(json, 'updatedTag', updatedTag);
+    _setIfNotNull(json, 'previousTag', previousTag);
     _setIfNotNull(json, 'data', data);
     return json;
   }
diff --git a/pkg/vm_service/test/common/test_helper.dart b/pkg/vm_service/test/common/test_helper.dart
index eee241f..45ce97b 100644
--- a/pkg/vm_service/test/common/test_helper.dart
+++ b/pkg/vm_service/test/common/test_helper.dart
@@ -346,7 +346,7 @@
       completer!.complete(vmIsolates.first);
       completer = null;
     }
-    return await (completer!.future as FutureOr<IsolateRef>);
+    return (await completer!.future) as IsolateRef;
   }
 }
 
diff --git a/pkg/vm_service/test/user_tag_changed_test.dart b/pkg/vm_service/test/user_tag_changed_test.dart
new file mode 100644
index 0000000..e2b63fa
--- /dev/null
+++ b/pkg/vm_service/test/user_tag_changed_test.dart
@@ -0,0 +1,62 @@
+// 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 'dart:developer';
+
+import 'package:async/async.dart';
+import 'package:test/test.dart';
+import 'package:vm_service/vm_service.dart';
+
+import 'common/service_test_common.dart';
+import 'common/test_helper.dart';
+
+void testMain() {
+  final tag = UserTag('Foo');
+  final origTag = tag.makeCurrent();
+  origTag.makeCurrent();
+}
+
+late StreamQueue<Event> stream;
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  (VmService service, IsolateRef isolate) async {
+    await service.streamListen(EventStreams.kProfiler);
+    stream = StreamQueue(
+      service.onProfilerEvent.transform(
+        SingleSubscriptionTransformer<Event, Event>(),
+      ),
+    );
+  },
+  resumeIsolate,
+  hasStoppedAtExit,
+  (VmService service, IsolateRef isolate) async {
+    await service.streamCancel(EventStreams.kProfiler);
+    expect(await stream.hasNext, true);
+
+    var event = await stream.next;
+    expect(event.kind, EventKind.kUserTagChanged);
+    expect(event.updatedTag, 'Foo');
+    expect(event.previousTag, 'Default');
+
+    expect(await stream.hasNext, true);
+    event = await stream.next;
+    expect(event.kind, EventKind.kUserTagChanged);
+    expect(event.updatedTag, 'Default');
+    expect(event.previousTag, 'Foo');
+  },
+  resumeIsolate,
+  (VmService service, IsolateRef isolate) async {
+    expect(await stream.hasNext, false);
+  }
+];
+
+main([args = const <String>[]]) async => await runIsolateTests(
+      args,
+      tests,
+      'user_tag_changed_test.dart',
+      pause_on_start: true,
+      pause_on_exit: true,
+      testeeConcurrent: testMain,
+    );
diff --git a/runtime/include/dart_tools_api.h b/runtime/include/dart_tools_api.h
index 3ed741b..3d1b890 100644
--- a/runtime/include/dart_tools_api.h
+++ b/runtime/include/dart_tools_api.h
@@ -496,4 +496,53 @@
 DART_EXPORT int64_t
 Dart_IsolateRunnableHeapSizeMetric(Dart_Isolate isolate);  // Byte
 
+/*
+ * ========
+ * UserTags
+ * ========
+ */
+
+/*
+ * Gets the current isolate's currently set UserTag instance.
+ *
+ * \return The currently set UserTag instance.
+ */
+DART_EXPORT Dart_Handle Dart_GetCurrentUserTag();
+
+/*
+ * Gets the current isolate's default UserTag instance.
+ *
+ * \return The default UserTag with label 'Default'
+ */
+DART_EXPORT Dart_Handle Dart_GetDefaultUserTag();
+
+/*
+ * Creates a new UserTag instance.
+ *
+ * \param label The name of the new UserTag.
+ *
+ * \return The newly created UserTag instance or an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_NewUserTag(const char* label);
+
+/*
+ * Updates the current isolate's UserTag to a new value.
+ *
+ * \param user_tag The UserTag to be set as the current UserTag.
+ *
+ * \return The previously set UserTag instance or an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_SetCurrentUserTag(Dart_Handle user_tag);
+
+/*
+ * Returns the label of a given UserTag instance.
+ *
+ * \param user_tag The UserTag from which the label will be retrieved.
+ *
+ * \return The UserTag's label. NULL if the user_tag is invalid. The caller is
+ *   responsible for freeing the returned label.
+ */
+DART_WARN_UNUSED_RESULT DART_EXPORT char* Dart_GetUserTagLabel(
+    Dart_Handle user_tag);
+
 #endif  // RUNTIME_INCLUDE_DART_TOOLS_API_H_
diff --git a/runtime/lib/profiler.cc b/runtime/lib/profiler.cc
index 956597a..9a9fc88 100644
--- a/runtime/lib/profiler.cc
+++ b/runtime/lib/profiler.cc
@@ -29,12 +29,7 @@
 
 DEFINE_NATIVE_ENTRY(UserTag_makeCurrent, 0, 1) {
   const UserTag& self = UserTag::CheckedHandle(zone, arguments->NativeArgAt(0));
-  if (FLAG_trace_intrinsified_natives) {
-    OS::PrintErr("UserTag_makeCurrent: %s\n", self.ToCString());
-  }
-  const UserTag& old = UserTag::Handle(zone, isolate->current_tag());
-  self.MakeActive();
-  return old.ptr();
+  return self.MakeActive();
 }
 
 DEFINE_NATIVE_ENTRY(UserTag_defaultTag, 0, 0) {
diff --git a/runtime/observatory/tests/service/get_version_rpc_test.dart b/runtime/observatory/tests/service/get_version_rpc_test.dart
index fad888f..75d1743 100644
--- a/runtime/observatory/tests/service/get_version_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_version_rpc_test.dart
@@ -12,7 +12,7 @@
     final result = await vm.invokeRpcNoUpgrade('getVersion', {});
     expect(result['type'], 'Version');
     expect(result['major'], 3);
-    expect(result['minor'], 47);
+    expect(result['minor'], 48);
     expect(result['_privateMajor'], 0);
     expect(result['_privateMinor'], 0);
   },
diff --git a/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart
index c7b238a..443f8bb 100644
--- a/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart
+++ b/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart
@@ -9,10 +9,10 @@
 
 var tests = <VMTest>[
   (VM vm) async {
-    var result = await vm.invokeRpcNoUpgrade('getVersion', {});
+    final result = await vm.invokeRpcNoUpgrade('getVersion', {});
     expect(result['type'], equals('Version'));
     expect(result['major'], equals(3));
-    expect(result['minor'], equals(47));
+    expect(result['minor'], equals(48));
     expect(result['_privateMajor'], equals(0));
     expect(result['_privateMinor'], equals(0));
   },
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index 99f2596..9040385 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -5560,7 +5560,6 @@
         *code ^= d->ReadRef();
         StubCode::EntryAtPut(i, code);
       }
-      StubCode::InitializationDone();
     }
   }
 
diff --git a/runtime/vm/compiler/asm_intrinsifier_arm.cc b/runtime/vm/compiler/asm_intrinsifier_arm.cc
index f41c51d..375c454 100644
--- a/runtime/vm/compiler/asm_intrinsifier_arm.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_arm.cc
@@ -1272,15 +1272,19 @@
 
 // Compares cid1 and cid2 to see if they're syntactically equivalent. If this
 // can be determined by this fast path, it jumps to either equal or not_equal,
-// otherwise it jumps to normal_ir_body. May clobber cid1, cid2, and scratch.
+// if equal but belonging to a generic class, it falls through with the scratch
+// register containing host_type_arguments_field_offset_in_words,
+// otherwise it jumps to normal_ir_body. May clobber scratch.
 static void EquivalentClassIds(Assembler* assembler,
                                Label* normal_ir_body,
                                Label* equal,
                                Label* not_equal,
                                Register cid1,
                                Register cid2,
-                               Register scratch) {
-  Label different_cids, not_integer, not_integer_or_string;
+                               Register scratch,
+                               bool testing_instance_cids) {
+  Label different_cids, equal_cids_but_generic, not_integer,
+      not_integer_or_string;
 
   // Check if left hand side is a closure. Closures are handled in the runtime.
   __ CompareImmediate(cid1, kClosureCid);
@@ -1296,10 +1300,13 @@
   // Check if there are no type arguments. In this case we can return true.
   // Otherwise fall through into the runtime to handle comparison.
   __ LoadClassById(scratch, cid1);
-  __ ldrh(scratch,
-          FieldAddress(scratch, target::Class::num_type_arguments_offset()));
-  __ CompareImmediate(scratch, 0);
-  __ b(normal_ir_body, NE);
+  __ ldr(
+      scratch,
+      FieldAddress(
+          scratch,
+          target::Class::host_type_arguments_field_offset_in_words_offset()));
+  __ CompareImmediate(scratch, target::Class::kNoTypeArguments);
+  __ b(&equal_cids_but_generic, NE);
   __ b(equal);
 
   // Class ids are different. Check if we are comparing two string types (with
@@ -1318,35 +1325,50 @@
 
   __ Bind(&not_integer);
   // Check if both are String types.
-  JumpIfNotString(assembler, cid1, scratch, &not_integer_or_string);
+  JumpIfNotString(assembler, cid1, scratch,
+                  testing_instance_cids ? &not_integer_or_string : not_equal);
 
   // First type is String. Check if the second is a string too.
   JumpIfString(assembler, cid2, scratch, equal);
   // String types are only equivalent to other String types.
   __ b(not_equal);
 
-  __ Bind(&not_integer_or_string);
-  // Check if the first type is a Type. If it is not then types are not
-  // equivalent because they have different class ids and they are not String
-  // or integer or Type.
-  JumpIfNotType(assembler, cid1, scratch, not_equal);
+  if (testing_instance_cids) {
+    __ Bind(&not_integer_or_string);
+    // Check if the first type is a Type. If it is not then types are not
+    // equivalent because they have different class ids and they are not String
+    // or integer or Type.
+    JumpIfNotType(assembler, cid1, scratch, not_equal);
 
-  // First type is a Type. Check if the second is a Type too.
-  JumpIfType(assembler, cid2, scratch, equal);
-  // Type types are only equivalent to other Type types.
-  __ b(not_equal);
+    // First type is a Type. Check if the second is a Type too.
+    JumpIfType(assembler, cid2, scratch, equal);
+    // Type types are only equivalent to other Type types.
+    __ b(not_equal);
+  }
+
+  // The caller must compare the type arguments.
+  __ Bind(&equal_cids_but_generic);
 }
 
 void AsmIntrinsifier::ObjectHaveSameRuntimeType(Assembler* assembler,
                                                 Label* normal_ir_body) {
-  __ ldr(R0, Address(SP, 0 * target::kWordSize));
-  __ LoadClassIdMayBeSmi(R1, R0);
-
-  __ ldr(R0, Address(SP, 1 * target::kWordSize));
-  __ LoadClassIdMayBeSmi(R2, R0);
+  __ ldm(IA, SP, (1 << R1 | 1 << R2));
+  __ LoadClassIdMayBeSmi(R1, R1);
+  __ LoadClassIdMayBeSmi(R2, R2);
 
   Label equal, not_equal;
-  EquivalentClassIds(assembler, normal_ir_body, &equal, &not_equal, R1, R2, R0);
+  EquivalentClassIds(assembler, normal_ir_body, &equal, &not_equal, R1, R2, R0,
+                     /* testing_instance_cids = */ true);
+
+  // Compare type arguments, host_type_arguments_field_offset_in_words in R0.
+  __ ldm(IA, SP, (1 << R1 | 1 << R2));
+  __ AddImmediate(R1, -kHeapObjectTag);
+  __ ldr(R1, Address(R1, R0, LSL, target::kWordSizeLog2));
+  __ AddImmediate(R2, -kHeapObjectTag);
+  __ ldr(R2, Address(R2, R0, LSL, target::kWordSizeLog2));
+  __ cmp(R1, Operand(R2));
+  __ b(normal_ir_body, NE);
+  // Fall through to equal case if type arguments are equal.
 
   __ Bind(&equal);
   __ LoadObject(R0, CastHandle<Object>(TrueObject()));
@@ -1396,8 +1418,16 @@
   __ SmiUntag(R3);
   __ ldr(R4, FieldAddress(R2, target::Type::type_class_id_offset()));
   __ SmiUntag(R4);
+  // We are not testing instance cids, but type class cids of Type instances.
   EquivalentClassIds(assembler, normal_ir_body, &equiv_cids, &not_equal, R3, R4,
-                     R0);
+                     R0, /* testing_instance_cids = */ false);
+
+  // Compare type arguments in Type instances.
+  __ ldr(R3, FieldAddress(R1, target::Type::arguments_offset()));
+  __ ldr(R4, FieldAddress(R2, target::Type::arguments_offset()));
+  __ cmp(R3, Operand(R4));
+  __ b(normal_ir_body, NE);
+  // Fall through to check nullability if type arguments are equal.
 
   // Check nullability.
   __ Bind(&equiv_cids);
@@ -1990,24 +2020,6 @@
   __ Branch(FieldAddress(R0, target::Function::entry_point_offset()));
 }
 
-// On stack: user tag (+0).
-void AsmIntrinsifier::UserTag_makeCurrent(Assembler* assembler,
-                                          Label* normal_ir_body) {
-  // R1: Isolate.
-  __ LoadIsolate(R1);
-  // R0: Current user tag.
-  __ ldr(R0, Address(R1, target::Isolate::current_tag_offset()));
-  // R2: UserTag.
-  __ ldr(R2, Address(SP, +0 * target::kWordSize));
-  // Set target::Isolate::current_tag_.
-  __ str(R2, Address(R1, target::Isolate::current_tag_offset()));
-  // R2: UserTag's tag.
-  __ ldr(R2, FieldAddress(R2, target::UserTag::tag_offset()));
-  // Set target::Isolate::user_tag_.
-  __ str(R2, Address(R1, target::Isolate::user_tag_offset()));
-  __ Ret();
-}
-
 void AsmIntrinsifier::UserTag_defaultTag(Assembler* assembler,
                                          Label* normal_ir_body) {
   __ LoadIsolate(R0);
diff --git a/runtime/vm/compiler/asm_intrinsifier_arm64.cc b/runtime/vm/compiler/asm_intrinsifier_arm64.cc
index 5749365..ce8d67d 100644
--- a/runtime/vm/compiler/asm_intrinsifier_arm64.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_arm64.cc
@@ -1418,15 +1418,19 @@
 
 // Compares cid1 and cid2 to see if they're syntactically equivalent. If this
 // can be determined by this fast path, it jumps to either equal or not_equal,
-// otherwise it jumps to normal_ir_body. May clobber cid1, cid2, and scratch.
+// if equal but belonging to a generic class, it falls through with the scratch
+// register containing host_type_arguments_field_offset_in_words,
+// otherwise it jumps to normal_ir_body. May clobber scratch.
 static void EquivalentClassIds(Assembler* assembler,
                                Label* normal_ir_body,
                                Label* equal,
                                Label* not_equal,
                                Register cid1,
                                Register cid2,
-                               Register scratch) {
-  Label different_cids, not_integer, not_integer_or_string;
+                               Register scratch,
+                               bool testing_instance_cids) {
+  Label different_cids, equal_cids_but_generic, not_integer,
+      not_integer_or_string;
 
   // Check if left hand side is a closure. Closures are handled in the runtime.
   __ CompareImmediate(cid1, kClosureCid);
@@ -1443,10 +1447,12 @@
   // Otherwise fall through into the runtime to handle comparison.
   __ LoadClassById(scratch, cid1);
   __ ldr(scratch,
-         FieldAddress(scratch, target::Class::num_type_arguments_offset(),
-                      kTwoBytes),
-         kTwoBytes);
-  __ cbnz(normal_ir_body, scratch);
+         FieldAddress(
+             scratch,
+             target::Class::host_type_arguments_field_offset_in_words_offset()),
+         kFourBytes);
+  __ CompareImmediate(scratch, target::Class::kNoTypeArguments);
+  __ b(&equal_cids_but_generic, NE);
   __ b(equal);
 
   // Class ids are different. Check if we are comparing two string types (with
@@ -1465,35 +1471,50 @@
 
   __ Bind(&not_integer);
   // Check if both are String types.
-  JumpIfNotString(assembler, cid1, scratch, &not_integer_or_string);
+  JumpIfNotString(assembler, cid1, scratch,
+                  testing_instance_cids ? &not_integer_or_string : not_equal);
 
   // First type is String. Check if the second is a string too.
   JumpIfString(assembler, cid2, scratch, equal);
   // String types are only equivalent to other String types.
   __ b(not_equal);
 
-  __ Bind(&not_integer_or_string);
-  // Check if the first type is a Type. If it is not then types are not
-  // equivalent because they have different class ids and they are not String
-  // or integer or Type.
-  JumpIfNotType(assembler, cid1, scratch, not_equal);
+  if (testing_instance_cids) {
+    __ Bind(&not_integer_or_string);
+    // Check if the first type is a Type. If it is not then types are not
+    // equivalent because they have different class ids and they are not String
+    // or integer or Type.
+    JumpIfNotType(assembler, cid1, scratch, not_equal);
 
-  // First type is a Type. Check if the second is a Type too.
-  JumpIfType(assembler, cid2, scratch, equal);
-  // Type types are only equivalent to other Type types.
-  __ b(not_equal);
+    // First type is a Type. Check if the second is a Type too.
+    JumpIfType(assembler, cid2, scratch, equal);
+    // Type types are only equivalent to other Type types.
+    __ b(not_equal);
+  }
+
+  // The caller must compare the type arguments.
+  __ Bind(&equal_cids_but_generic);
 }
 
 void AsmIntrinsifier::ObjectHaveSameRuntimeType(Assembler* assembler,
                                                 Label* normal_ir_body) {
-  __ ldr(R0, Address(SP, 0 * target::kWordSize));
+  __ ldp(R0, R1, Address(SP, 0 * target::kWordSize, Address::PairOffset));
+  __ LoadClassIdMayBeSmi(R2, R1);
   __ LoadClassIdMayBeSmi(R1, R0);
 
-  __ ldr(R0, Address(SP, 1 * target::kWordSize));
-  __ LoadClassIdMayBeSmi(R2, R0);
-
   Label equal, not_equal;
-  EquivalentClassIds(assembler, normal_ir_body, &equal, &not_equal, R1, R2, R0);
+  EquivalentClassIds(assembler, normal_ir_body, &equal, &not_equal, R1, R2, R0,
+                     /* testing_instance_cids = */ true);
+
+  // Compare type arguments, host_type_arguments_field_offset_in_words in R0.
+  __ ldp(R1, R2, Address(SP, 0 * target::kWordSize, Address::PairOffset));
+  __ AddImmediate(R1, -kHeapObjectTag);
+  __ ldr(R1, Address(R1, R0, UXTX, Address::Scaled));
+  __ AddImmediate(R2, -kHeapObjectTag);
+  __ ldr(R2, Address(R2, R0, UXTX, Address::Scaled));
+  __ CompareObjectRegisters(R1, R2);
+  __ b(normal_ir_body, NE);
+  // Fall through to equal case if type arguments are equal.
 
   __ Bind(&equal);
   __ LoadObject(R0, CastHandle<Object>(TrueObject()));
@@ -1549,8 +1570,16 @@
   __ LoadCompressedSmi(R4,
                        FieldAddress(R2, target::Type::type_class_id_offset()));
   __ SmiUntag(R4);
+  // We are not testing instance cids, but type class cids of Type instances.
   EquivalentClassIds(assembler, normal_ir_body, &equiv_cids, &not_equal, R3, R4,
-                     R0);
+                     R0, /* testing_instance_cids = */ false);
+
+  // Compare type arguments in Type instances.
+  __ LoadCompressed(R3, FieldAddress(R1, target::Type::arguments_offset()));
+  __ LoadCompressed(R4, FieldAddress(R2, target::Type::arguments_offset()));
+  __ CompareObjectRegisters(R3, R4);
+  __ b(normal_ir_body, NE);
+  // Fall through to check nullability if type arguments are equal.
 
   // Check nullability.
   __ Bind(&equiv_cids);
@@ -2195,24 +2224,6 @@
   __ br(R1);
 }
 
-// On stack: user tag (+0).
-void AsmIntrinsifier::UserTag_makeCurrent(Assembler* assembler,
-                                          Label* normal_ir_body) {
-  // R1: Isolate.
-  __ LoadIsolate(R1);
-  // R0: Current user tag.
-  __ ldr(R0, Address(R1, target::Isolate::current_tag_offset()));
-  // R2: UserTag.
-  __ ldr(R2, Address(SP, +0 * target::kWordSize));
-  // Set target::Isolate::current_tag_.
-  __ str(R2, Address(R1, target::Isolate::current_tag_offset()));
-  // R2: UserTag's tag.
-  __ ldr(R2, FieldAddress(R2, target::UserTag::tag_offset()));
-  // Set target::Isolate::user_tag_.
-  __ str(R2, Address(R1, target::Isolate::user_tag_offset()));
-  __ ret();
-}
-
 void AsmIntrinsifier::UserTag_defaultTag(Assembler* assembler,
                                          Label* normal_ir_body) {
   __ LoadIsolate(R0);
diff --git a/runtime/vm/compiler/asm_intrinsifier_ia32.cc b/runtime/vm/compiler/asm_intrinsifier_ia32.cc
index 5909eab..a17f7a8 100644
--- a/runtime/vm/compiler/asm_intrinsifier_ia32.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_ia32.cc
@@ -1358,15 +1358,19 @@
 
 // Compares cid1 and cid2 to see if they're syntactically equivalent. If this
 // can be determined by this fast path, it jumps to either equal or not_equal,
-// otherwise it jumps to normal_ir_body. May clobber cid1, cid2, and scratch.
+// if equal but belonging to a generic class, it falls through with the scratch
+// register containing host_type_arguments_field_offset_in_words,
+// otherwise it jumps to normal_ir_body. May clobber scratch.
 static void EquivalentClassIds(Assembler* assembler,
                                Label* normal_ir_body,
                                Label* equal,
                                Label* not_equal,
                                Register cid1,
                                Register cid2,
-                               Register scratch) {
-  Label different_cids, not_integer, not_integer_or_string;
+                               Register scratch,
+                               bool testing_instance_cids) {
+  Label different_cids, equal_cids_but_generic, not_integer,
+      not_integer_or_string;
 
   // Check if left hand side is a closure. Closures are handled in the runtime.
   __ cmpl(cid1, Immediate(kClosureCid));
@@ -1382,10 +1386,13 @@
   // Check if there are no type arguments. In this case we can return true.
   // Otherwise fall through into the runtime to handle comparison.
   __ LoadClassById(scratch, cid1);
-  __ movzxw(scratch,
-            FieldAddress(scratch, target::Class::num_type_arguments_offset()));
-  __ cmpl(scratch, Immediate(0));
-  __ j(NOT_EQUAL, normal_ir_body);
+  __ movl(
+      scratch,
+      FieldAddress(
+          scratch,
+          target::Class::host_type_arguments_field_offset_in_words_offset()));
+  __ cmpl(scratch, Immediate(target::Class::kNoTypeArguments));
+  __ j(NOT_EQUAL, &equal_cids_but_generic, Assembler::kNearJump);
   __ jmp(equal);
 
   // Class ids are different. Check if we are comparing two string types (with
@@ -1405,23 +1412,29 @@
 
   __ Bind(&not_integer);
   // Check if both are String types.
-  JumpIfNotString(assembler, cid1, &not_integer_or_string);
+  JumpIfNotString(assembler, cid1,
+                  testing_instance_cids ? &not_integer_or_string : not_equal);
 
   // First type is a String. Check if the second is a String too.
   JumpIfString(assembler, cid2, equal);
   // String types are only equivalent to other String types.
   __ jmp(not_equal);
 
-  __ Bind(&not_integer_or_string);
-  // Check if the first type is a Type. If it is not then types are not
-  // equivalent because they have different class ids and they are not String
-  // or integer or Type.
-  JumpIfNotType(assembler, cid1, not_equal);
+  if (testing_instance_cids) {
+    __ Bind(&not_integer_or_string);
+    // Check if the first type is a Type. If it is not then types are not
+    // equivalent because they have different class ids and they are not String
+    // or integer or Type.
+    JumpIfNotType(assembler, cid1, not_equal);
 
-  // First type is a Type. Check if the second is a Type too.
-  JumpIfType(assembler, cid2, equal);
-  // Type types are only equivalent to other Type types.
-  __ jmp(not_equal);
+    // First type is a Type. Check if the second is a Type too.
+    JumpIfType(assembler, cid2, equal);
+    // Type types are only equivalent to other Type types.
+    __ jmp(not_equal);
+  }
+
+  // The caller must compare the type arguments.
+  __ Bind(&equal_cids_but_generic);
 }
 
 void AsmIntrinsifier::ObjectHaveSameRuntimeType(Assembler* assembler,
@@ -1434,7 +1447,16 @@
 
   Label equal, not_equal;
   EquivalentClassIds(assembler, normal_ir_body, &equal, &not_equal, EDI, EBX,
-                     EAX);
+                     EAX, /* testing_instance_cids = */ true);
+
+  // Compare type arguments, host_type_arguments_field_offset_in_words in EAX.
+  __ movl(EDI, Address(ESP, +1 * target::kWordSize));
+  __ movl(EBX, Address(ESP, +2 * target::kWordSize));
+  __ movl(EDI, FieldAddress(EDI, EAX, TIMES_4, 0));
+  __ movl(EBX, FieldAddress(EBX, EAX, TIMES_4, 0));
+  __ cmpl(EDI, EBX);
+  __ j(NOT_EQUAL, normal_ir_body, Assembler::kNearJump);
+  // Fall through to equal case if type arguments are equal.
 
   __ Bind(&equal);
   __ LoadObject(EAX, CastHandle<Object>(TrueObject()));
@@ -1489,8 +1511,16 @@
   __ SmiUntag(ECX);
   __ movl(EDX, FieldAddress(EBX, target::Type::type_class_id_offset()));
   __ SmiUntag(EDX);
+  // We are not testing instance cids, but type class cids of Type instances.
   EquivalentClassIds(assembler, normal_ir_body, &equiv_cids, &not_equal, ECX,
-                     EDX, EAX);
+                     EDX, EAX, /* testing_instance_cids = */ false);
+
+  // Compare type arguments in Type instances.
+  __ movl(ECX, FieldAddress(EDI, target::Type::arguments_offset()));
+  __ movl(EDX, FieldAddress(EBX, target::Type::arguments_offset()));
+  __ cmpl(ECX, EDX);
+  __ j(NOT_EQUAL, normal_ir_body, Assembler::kNearJump);
+  // Fall through to check nullability if type arguments are equal.
 
   // Check nullability.
   __ Bind(&equiv_cids);
@@ -1979,24 +2009,6 @@
   __ jmp(FieldAddress(EAX, target::Function::entry_point_offset()));
 }
 
-// On stack: user tag (+1), return-address (+0).
-void AsmIntrinsifier::UserTag_makeCurrent(Assembler* assembler,
-                                          Label* normal_ir_body) {
-  // EDI: Isolate.
-  __ LoadIsolate(EDI);
-  // EAX: Current user tag.
-  __ movl(EAX, Address(EDI, target::Isolate::current_tag_offset()));
-  // EAX: UserTag.
-  __ movl(EBX, Address(ESP, +1 * target::kWordSize));
-  // Set target::Isolate::current_tag_.
-  __ movl(Address(EDI, target::Isolate::current_tag_offset()), EBX);
-  // EAX: UserTag's tag.
-  __ movl(EBX, FieldAddress(EBX, target::UserTag::tag_offset()));
-  // Set target::Isolate::user_tag_.
-  __ movl(Address(EDI, target::Isolate::user_tag_offset()), EBX);
-  __ ret();
-}
-
 void AsmIntrinsifier::UserTag_defaultTag(Assembler* assembler,
                                          Label* normal_ir_body) {
   __ LoadIsolate(EAX);
diff --git a/runtime/vm/compiler/asm_intrinsifier_x64.cc b/runtime/vm/compiler/asm_intrinsifier_x64.cc
index f691931..3ba88b8 100644
--- a/runtime/vm/compiler/asm_intrinsifier_x64.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_x64.cc
@@ -1262,15 +1262,19 @@
 
 // Compares cid1 and cid2 to see if they're syntactically equivalent. If this
 // can be determined by this fast path, it jumps to either equal or not_equal,
-// otherwise it jumps to normal_ir_body. May clobber cid1, cid2, and scratch.
+// if equal but belonging to a generic class, it falls through with the scratch
+// register containing host_type_arguments_field_offset_in_words,
+// otherwise it jumps to normal_ir_body. May clobber scratch.
 static void EquivalentClassIds(Assembler* assembler,
                                Label* normal_ir_body,
                                Label* equal,
                                Label* not_equal,
                                Register cid1,
                                Register cid2,
-                               Register scratch) {
-  Label different_cids, not_integer, not_integer_or_string;
+                               Register scratch,
+                               bool testing_instance_cids) {
+  Label different_cids, equal_cids_but_generic, not_integer,
+      not_integer_or_string;
 
   // Check if left hand side is a closure. Closures are handled in the runtime.
   __ cmpq(cid1, Immediate(kClosureCid));
@@ -1286,10 +1290,13 @@
   // Check if there are no type arguments. In this case we can return true.
   // Otherwise fall through into the runtime to handle comparison.
   __ LoadClassById(scratch, cid1);
-  __ movzxw(scratch,
-            FieldAddress(scratch, target::Class::num_type_arguments_offset()));
-  __ cmpq(scratch, Immediate(0));
-  __ j(NOT_EQUAL, normal_ir_body);
+  __ movl(
+      scratch,
+      FieldAddress(
+          scratch,
+          target::Class::host_type_arguments_field_offset_in_words_offset()));
+  __ cmpl(scratch, Immediate(target::Class::kNoTypeArguments));
+  __ j(NOT_EQUAL, &equal_cids_but_generic, Assembler::kNearJump);
   __ jmp(equal);
 
   // Class ids are different. Check if we are comparing two string types (with
@@ -1309,23 +1316,29 @@
 
   __ Bind(&not_integer);
   // Check if both are String types.
-  JumpIfNotString(assembler, cid1, &not_integer_or_string);
+  JumpIfNotString(assembler, cid1,
+                  testing_instance_cids ? &not_integer_or_string : not_equal);
 
   // First type is a String. Check if the second is a String too.
   JumpIfString(assembler, cid2, equal);
   // String types are only equivalent to other String types.
   __ jmp(not_equal);
 
-  __ Bind(&not_integer_or_string);
-  // Check if the first type is a Type. If it is not then types are not
-  // equivalent because they have different class ids and they are not String
-  // or integer or Type.
-  JumpIfNotType(assembler, cid1, not_equal);
+  if (testing_instance_cids) {
+    __ Bind(&not_integer_or_string);
+    // Check if the first type is a Type. If it is not then types are not
+    // equivalent because they have different class ids and they are not String
+    // or integer or Type.
+    JumpIfNotType(assembler, cid1, not_equal);
 
-  // First type is a Type. Check if the second is a Type too.
-  JumpIfType(assembler, cid2, equal);
-  // Type types are only equivalent to other Type types.
-  __ jmp(not_equal);
+    // First type is a Type. Check if the second is a Type too.
+    JumpIfType(assembler, cid2, equal);
+    // Type types are only equivalent to other Type types.
+    __ jmp(not_equal);
+  }
+
+  // The caller must compare the type arguments.
+  __ Bind(&equal_cids_but_generic);
 }
 
 void AsmIntrinsifier::ObjectHaveSameRuntimeType(Assembler* assembler,
@@ -1338,7 +1351,16 @@
 
   Label equal, not_equal;
   EquivalentClassIds(assembler, normal_ir_body, &equal, &not_equal, RCX, RDX,
-                     RAX);
+                     RAX, /* testing_instance_cids = */ true);
+
+  // Compare type arguments, host_type_arguments_field_offset_in_words in RAX.
+  __ movq(RCX, Address(RSP, +1 * target::kWordSize));
+  __ movq(RDX, Address(RSP, +2 * target::kWordSize));
+  __ movq(RCX, FieldAddress(RCX, RAX, TIMES_8, 0));
+  __ movq(RDX, FieldAddress(RDX, RAX, TIMES_8, 0));
+  __ cmpq(RCX, RDX);
+  __ j(NOT_EQUAL, normal_ir_body, Assembler::kNearJump);
+  // Fall through to equal case if type arguments are equal.
 
   __ Bind(&equal);
   __ LoadObject(RAX, CastHandle<Object>(TrueObject()));
@@ -1399,8 +1421,16 @@
   __ LoadCompressedSmi(RSI,
                        FieldAddress(RDX, target::Type::type_class_id_offset()));
   __ SmiUntag(RSI);
+  // We are not testing instance cids, but type class cids of Type instances.
   EquivalentClassIds(assembler, normal_ir_body, &equiv_cids, &not_equal, RDI,
-                     RSI, RAX);
+                     RSI, RAX, /* testing_instance_cids = */ false);
+
+  // Compare type arguments in Type instances.
+  __ LoadCompressed(RDI, FieldAddress(RCX, target::Type::arguments_offset()));
+  __ LoadCompressed(RSI, FieldAddress(RDX, target::Type::arguments_offset()));
+  __ cmpq(RDI, RSI);
+  __ j(NOT_EQUAL, normal_ir_body, Assembler::kNearJump);
+  // Fall through to check nullability if type arguments are equal.
 
   // Check nullability.
   __ Bind(&equiv_cids);
@@ -2056,24 +2086,6 @@
   __ jmp(RDI);
 }
 
-// On stack: user tag (+1), return-address (+0).
-void AsmIntrinsifier::UserTag_makeCurrent(Assembler* assembler,
-                                          Label* normal_ir_body) {
-  // RBX: Isolate.
-  __ LoadIsolate(RBX);
-  // RAX: Current user tag.
-  __ movq(RAX, Address(RBX, target::Isolate::current_tag_offset()));
-  // R10: UserTag.
-  __ movq(R10, Address(RSP, +1 * target::kWordSize));
-  // Set Isolate::current_tag_.
-  __ movq(Address(RBX, target::Isolate::current_tag_offset()), R10);
-  // R10: UserTag's tag.
-  __ movq(R10, FieldAddress(R10, target::UserTag::tag_offset()));
-  // Set Isolate::user_tag_.
-  __ movq(Address(RBX, target::Isolate::user_tag_offset()), R10);
-  __ ret();
-}
-
 void AsmIntrinsifier::UserTag_defaultTag(Assembler* assembler,
                                          Label* normal_ir_body) {
   __ LoadIsolate(RAX);
diff --git a/runtime/vm/compiler/recognized_methods_list.h b/runtime/vm/compiler/recognized_methods_list.h
index 4c4af85..3f46559 100644
--- a/runtime/vm/compiler/recognized_methods_list.h
+++ b/runtime/vm/compiler/recognized_methods_list.h
@@ -377,7 +377,6 @@
   GRAPH_MATH_LIB_INTRINSIC_LIST(V)                                             \
 
 #define DEVELOPER_LIB_INTRINSIC_LIST(V)                                        \
-  V(_UserTag, makeCurrent, UserTag_makeCurrent, 0x5bd9b88e)                    \
   V(::, _getDefaultTag, UserTag_defaultTag, 0x6c19c8a5)                        \
   V(::, _getCurrentTag, Profiler_getCurrentTag, 0x70ead08e)                    \
   V(::, _isDartStreamEnabled, Timeline_isDartStreamEnabled, 0xc97aafb3)        \
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 7ba1fc0..db08698 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -7011,4 +7011,53 @@
   OS::PrepareToAbort();
 }
 
+DART_EXPORT Dart_Handle Dart_GetCurrentUserTag() {
+  Thread* thread = Thread::Current();
+  CHECK_ISOLATE(thread->isolate());
+  DARTSCOPE(thread);
+  Isolate* isolate = thread->isolate();
+  return Api::NewHandle(thread, isolate->current_tag());
+}
+
+DART_EXPORT Dart_Handle Dart_GetDefaultUserTag() {
+  Thread* thread = Thread::Current();
+  CHECK_ISOLATE(thread->isolate());
+  DARTSCOPE(thread);
+  Isolate* isolate = thread->isolate();
+  return Api::NewHandle(thread, isolate->default_tag());
+}
+
+DART_EXPORT Dart_Handle Dart_NewUserTag(const char* label) {
+  Thread* thread = Thread::Current();
+  CHECK_ISOLATE(thread->isolate());
+  DARTSCOPE(thread);
+  if (label == nullptr) {
+    return Api::NewError(
+        "Dart_NewUserTag expects argument 'label' to be non-null");
+  }
+  const String& value = String::Handle(String::New(label));
+  return Api::NewHandle(thread, UserTag::New(value));
+}
+
+DART_EXPORT Dart_Handle Dart_SetCurrentUserTag(Dart_Handle user_tag) {
+  Thread* thread = Thread::Current();
+  CHECK_ISOLATE(thread->isolate());
+  DARTSCOPE(thread);
+  const UserTag& tag = Api::UnwrapUserTagHandle(Z, user_tag);
+  if (tag.IsNull()) {
+    RETURN_TYPE_ERROR(Z, user_tag, UserTag);
+  }
+  return Api::NewHandle(thread, tag.MakeActive());
+}
+
+DART_EXPORT char* Dart_GetUserTagLabel(Dart_Handle user_tag) {
+  DARTSCOPE(Thread::Current());
+  const UserTag& tag = Api::UnwrapUserTagHandle(Z, user_tag);
+  if (tag.IsNull()) {
+    return nullptr;
+  }
+  const String& label = String::Handle(Z, tag.label());
+  return Utils::StrDup(label.ToCString());
+}
+
 }  // namespace dart
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index 1f2f190..6f116a0 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -9363,6 +9363,52 @@
       "Invalid argument(s): Couldn't resolve function: 'DoesNotResolve'");
 }
 
+TEST_CASE(DartAPI_UserTags) {
+  Dart_Handle default_tag = Dart_GetDefaultUserTag();
+  EXPECT_VALID(default_tag);
+
+  auto default_label =
+      Utils::CStringUniquePtr(Dart_GetUserTagLabel(default_tag), std::free);
+  EXPECT_STREQ(default_label.get(), "Default");
+
+  Dart_Handle current_tag = Dart_GetCurrentUserTag();
+  EXPECT(Dart_IdentityEquals(default_tag, current_tag));
+
+  auto current_label =
+      Utils::CStringUniquePtr(Dart_GetUserTagLabel(current_tag), std::free);
+  EXPECT_STREQ(default_label.get(), current_label.get());
+
+  Dart_Handle new_tag = Dart_NewUserTag("Foo");
+  EXPECT_VALID(new_tag);
+
+  auto new_tag_label =
+      Utils::CStringUniquePtr(Dart_GetUserTagLabel(new_tag), std::free);
+  EXPECT_STREQ(new_tag_label.get(), "Foo");
+
+  Dart_Handle old_tag = Dart_SetCurrentUserTag(new_tag);
+  EXPECT_VALID(old_tag);
+
+  auto old_label =
+      Utils::CStringUniquePtr(Dart_GetUserTagLabel(old_tag), std::free);
+  EXPECT_STREQ(old_label.get(), default_label.get());
+
+  current_tag = Dart_GetCurrentUserTag();
+  EXPECT(Dart_IdentityEquals(new_tag, current_tag));
+
+  current_label =
+      Utils::CStringUniquePtr(Dart_GetUserTagLabel(current_tag), std::free);
+  EXPECT_STREQ(current_label.get(), new_tag_label.get());
+
+  EXPECT(Dart_GetUserTagLabel(Dart_Null()) == nullptr);
+
+  EXPECT_ERROR(Dart_NewUserTag(nullptr),
+               "Dart_NewUserTag expects argument 'label' to be non-null");
+
+  EXPECT_ERROR(
+      Dart_SetCurrentUserTag(Dart_Null()),
+      "Dart_SetCurrentUserTag expects argument 'user_tag' to be non-null");
+}
+
 #endif  // !PRODUCT
 
 }  // namespace dart
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index b78b400..fbc6aba 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -6183,17 +6183,18 @@
       printer->AddString(TypeParameter::CanonicalNameCString(
           are_class_type_parameters, base, base + i));
     }
-    if (!AllDynamicBounds()) {
+    if (FLAG_show_internal_names || !AllDynamicBounds()) {
       type = BoundAt(i);
       // Do not print default bound or non-nullable Object bound in weak mode.
       if (!type.IsNull() &&
-          (!type.IsObjectType() ||
+          (FLAG_show_internal_names || !type.IsObjectType() ||
            (thread->isolate_group()->null_safety() && type.IsNonNullable()))) {
         printer->AddString(" extends ");
         type.PrintName(name_visibility, printer);
         if (FLAG_show_internal_names && !AllDynamicDefaults()) {
           type = DefaultAt(i);
-          if (!type.IsNull() && !type.IsDynamicType()) {
+          if (!type.IsNull() &&
+              (FLAG_show_internal_names || !type.IsDynamicType())) {
             printer->AddString(" defaults to ");
             type.PrintName(name_visibility, printer);
           }
@@ -6427,18 +6428,19 @@
   if (this->ptr() == other.ptr()) {
     return true;
   }
-  if (IsNull() || other.IsNull()) {
-    return false;
-  }
-  const intptr_t num_types = Length();
-  if (num_types != other.Length()) {
-    return false;
+  if (kind == TypeEquality::kCanonical) {
+    if (IsNull() || other.IsNull()) {
+      return false;
+    }
+    if (Length() != other.Length()) {
+      return false;
+    }
   }
   AbstractType& type = AbstractType::Handle();
   AbstractType& other_type = AbstractType::Handle();
   for (intptr_t i = from_index; i < from_index + len; i++) {
-    type = TypeAt(i);
-    other_type = other.TypeAt(i);
+    type = IsNull() ? Type::DynamicType() : TypeAt(i);
+    other_type = other.IsNull() ? Type::DynamicType() : other.TypeAt(i);
     // Still unfinalized vectors should not be considered equivalent.
     if (type.IsNull() || !type.IsEquivalent(other_type, kind, trail)) {
       return false;
@@ -20252,6 +20254,7 @@
   if (stub.IsNull()) {
     // This only happens during bootstrapping when creating Type objects before
     // we have the instructions.
+    ASSERT(type_class_id() == kDynamicCid || type_class_id() == kVoidCid);
     StoreNonPointer(&untag()->type_test_stub_entry_point_, 0);
     untag()->set_type_test_stub(stub.ptr());
     return;
@@ -25687,10 +25690,25 @@
   return "_MirrorReference";
 }
 
-void UserTag::MakeActive() const {
+UserTagPtr UserTag::MakeActive() const {
   Isolate* isolate = Isolate::Current();
   ASSERT(isolate != NULL);
+  UserTag& old = UserTag::Handle(isolate->current_tag());
   isolate->set_current_tag(*this);
+
+#if !defined(PRODUCT)
+  // Notify VM service clients that the current UserTag has changed.
+  if (Service::profiler_stream.enabled()) {
+    ServiceEvent event(ServiceEvent::kUserTagChanged);
+    String& name = String::Handle(old.label());
+    event.set_previous_tag(name.ToCString());
+    name ^= label();
+    event.set_updated_tag(name.ToCString());
+    Service::HandleEvent(&event);
+  }
+#endif  // !defined(PRODUCT)
+
+  return old.ptr();
 }
 
 UserTagPtr UserTag::New(const String& label, Heap::Space space) {
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 3620f18..70dcdcc 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -7735,8 +7735,9 @@
   bool IsEquivalent(const TypeArguments& other,
                     TypeEquality kind,
                     TrailPtr trail = nullptr) const {
-    return IsSubvectorEquivalent(other, 0, IsNull() ? 0 : Length(), kind,
-                                 trail);
+    // Make a null vector a vector of dynamic as long as the other vector.
+    return IsSubvectorEquivalent(other, 0, IsNull() ? other.Length() : Length(),
+                                 kind, trail);
   }
   bool IsSubvectorEquivalent(const TypeArguments& other,
                              intptr_t from_index,
@@ -11558,7 +11559,7 @@
 
   StringPtr label() const { return untag()->label(); }
 
-  void MakeActive() const;
+  UserTagPtr MakeActive() const;
 
   static intptr_t InstanceSize() {
     return RoundedAllocationSize(sizeof(UntaggedUserTag));
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index 70256cf..a920d42 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -4605,7 +4605,7 @@
         "vmName\":\"\",\"location\":{\"type\":\"SourceLocation\",\"script\":{"
         "\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\",\"uri\":\"dart:"
         "developer-patch\\/profiler.dart\",\"_kind\":\"kernel\"},\"tokenPos\":"
-        "414,\"endTokenPos\":672},\"library\":{\"type\":\"@Library\","
+        "414,\"endTokenPos\":628},\"library\":{\"type\":\"@Library\","
         "\"fixedId\":true,\"id\":\"\",\"name\":\"dart.developer\",\"uri\":"
         "\"dart:developer\"}},"
         // Handle non-zero identity hash.
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index 4ebfbae..804ca50 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -128,16 +128,18 @@
 StreamInfo Service::logging_stream("Logging");
 StreamInfo Service::extension_stream("Extension");
 StreamInfo Service::timeline_stream("Timeline");
+StreamInfo Service::profiler_stream("Profiler");
 
 const uint8_t* Service::dart_library_kernel_ = NULL;
 intptr_t Service::dart_library_kernel_len_ = 0;
 
 static StreamInfo* const streams_[] = {
-    &Service::vm_stream,      &Service::isolate_stream,
-    &Service::debug_stream,   &Service::gc_stream,
-    &Service::echo_stream,    &Service::heapsnapshot_stream,
-    &Service::logging_stream, &Service::extension_stream,
-    &Service::timeline_stream};
+    &Service::vm_stream,       &Service::isolate_stream,
+    &Service::debug_stream,    &Service::gc_stream,
+    &Service::echo_stream,     &Service::heapsnapshot_stream,
+    &Service::logging_stream,  &Service::extension_stream,
+    &Service::timeline_stream, &Service::profiler_stream,
+};
 
 bool Service::ListenStream(const char* stream_id) {
   if (FLAG_trace_service) {
diff --git a/runtime/vm/service.h b/runtime/vm/service.h
index 4b411a3..e178c6a 100644
--- a/runtime/vm/service.h
+++ b/runtime/vm/service.h
@@ -15,7 +15,7 @@
 namespace dart {
 
 #define SERVICE_PROTOCOL_MAJOR_VERSION 3
-#define SERVICE_PROTOCOL_MINOR_VERSION 47
+#define SERVICE_PROTOCOL_MINOR_VERSION 48
 
 class Array;
 class EmbedderServiceHandler;
@@ -171,6 +171,7 @@
   static StreamInfo logging_stream;
   static StreamInfo extension_stream;
   static StreamInfo timeline_stream;
+  static StreamInfo profiler_stream;
 
   static bool ListenStream(const char* stream_id);
   static void CancelStream(const char* stream_id);
diff --git a/runtime/vm/service/service.md b/runtime/vm/service/service.md
index ed0eebb..d8f1e79 100644
--- a/runtime/vm/service/service.md
+++ b/runtime/vm/service/service.md
@@ -1,8 +1,8 @@
-# Dart VM Service Protocol 3.47
+# Dart VM Service Protocol 3.48
 
 > Please post feedback to the [observatory-discuss group][discuss-list]
 
-This document describes of _version 3.47_ of the Dart VM Service Protocol. This
+This document describes of _version 3.48_ of the Dart VM Service Protocol. This
 protocol is used to communicate with a running Dart Virtual Machine.
 
 To use the Service Protocol, start the VM with the *--observe* flag.
@@ -1477,6 +1477,7 @@
 VM | VMUpdate, VMFlagUpdate
 Isolate | IsolateStart, IsolateRunnable, IsolateExit, IsolateUpdate, IsolateReload, ServiceExtensionAdded
 Debug | PauseStart, PauseExit, PauseBreakpoint, PauseInterrupted, PauseException, PausePostRequest, Resume, BreakpointAdded, BreakpointResolved, BreakpointRemoved, BreakpointUpdated, Inspect, None
+Profiler | UserTagChanged
 GC | GC
 Extension | Extension
 Timeline | TimelineEvents, TimelineStreamsSubscriptionUpdate
@@ -2154,6 +2155,12 @@
   // This is provided for the event kinds:
   //   HeapSnapshot
   bool last [optional];
+
+  // The current UserTag label.
+  string updatedTag [optional];
+
+  // The previous UserTag label.
+  string previousTag [optional];
 }
 ```
 
@@ -2264,6 +2271,9 @@
   // Notification that a Service has been removed from the Service Protocol
   // from another client.
   ServiceUnregistered,
+
+  // Notification that the UserTag for an isolate has been changed.
+  UserTagChanged,
 }
 ```
 
@@ -4058,5 +4068,6 @@
 3.45 | Added `setBreakpointState` RPC and `BreakpointUpdated` event kind.
 3.46 | Moved `sourceLocation` property into reference types for `Class`, `Field`, and `Function`.
 3.47 | Added `shows` and `hides` properties to `LibraryDependency`.
+3.48 | Added `Profiler` stream, `UserTagChanged` event kind, and `updatedTag` and `previousTag` properties to `Event`.
 
 [discuss-list]: https://groups.google.com/a/dartlang.org/forum/#!forum/observatory-discuss
diff --git a/runtime/vm/service_event.cc b/runtime/vm/service_event.cc
index 6b97b6d..f8ab174 100644
--- a/runtime/vm/service_event.cc
+++ b/runtime/vm/service_event.cc
@@ -28,22 +28,24 @@
     : isolate_(isolate),
       isolate_group_(isolate_group),
       kind_(event_kind),
-      flag_name_(NULL),
-      flag_new_value_(NULL),
-      embedder_kind_(NULL),
-      embedder_stream_id_(NULL),
-      breakpoint_(NULL),
-      top_frame_(NULL),
-      timeline_event_block_(NULL),
-      extension_rpc_(NULL),
-      exception_(NULL),
-      reload_error_(NULL),
-      spawn_token_(NULL),
-      spawn_error_(NULL),
+      flag_name_(nullptr),
+      flag_new_value_(nullptr),
+      previous_tag_(nullptr),
+      updated_tag_(nullptr),
+      embedder_kind_(nullptr),
+      embedder_stream_id_(nullptr),
+      breakpoint_(nullptr),
+      top_frame_(nullptr),
+      timeline_event_block_(nullptr),
+      extension_rpc_(nullptr),
+      exception_(nullptr),
+      reload_error_(nullptr),
+      spawn_token_(nullptr),
+      spawn_error_(nullptr),
       at_async_jump_(false),
-      inspectee_(NULL),
-      gc_stats_(NULL),
-      bytes_(NULL),
+      inspectee_(nullptr),
+      gc_stats_(nullptr),
+      bytes_(nullptr),
       bytes_length_(0),
       timestamp_(OS::GetCurrentTimeMillis()) {
   // We should never generate events for the vm isolate as it is never reported
@@ -137,6 +139,8 @@
       return "TimelineEvents";
     case kTimelineStreamSubscriptionsUpdate:
       return "TimelineStreamSubscriptionsUpdate";
+    case kUserTagChanged:
+      return "UserTagChanged";
     default:
       UNREACHABLE();
       return "Unknown";
@@ -187,11 +191,14 @@
       return &Service::timeline_stream;
 
     case kEmbedder:
-      return NULL;
+      return nullptr;
+
+    case kUserTagChanged:
+      return &Service::profiler_stream;
 
     default:
       UNREACHABLE();
-      return NULL;
+      return nullptr;
   }
 }
 
@@ -213,6 +220,10 @@
     // For backwards compatibility, "new_value" is also provided.
     jsobj.AddProperty("newValue", flag_new_value());
   }
+  if (kind() == kUserTagChanged) {
+    jsobj.AddProperty("previousTag", previous_tag());
+    jsobj.AddProperty("updatedTag", updated_tag());
+  }
   if (kind() == kIsolateReload) {
     if (reload_error_ == NULL) {
       jsobj.AddProperty("status", "success");
diff --git a/runtime/vm/service_event.h b/runtime/vm/service_event.h
index 80aaa8a..3b8de0e 100644
--- a/runtime/vm/service_event.h
+++ b/runtime/vm/service_event.h
@@ -60,6 +60,8 @@
     // Sent when SetVMTimelineFlags is called.
     kTimelineStreamSubscriptionsUpdate,
 
+    kUserTagChanged,
+
     kIllegal,
   };
 
@@ -111,6 +113,14 @@
   const char* flag_new_value() const { return flag_new_value_; }
   void set_flag_new_value(const char* value) { flag_new_value_ = value; }
 
+  const char* previous_tag() const { return previous_tag_; }
+  void set_previous_tag(const char* previous_tag) {
+    previous_tag_ = previous_tag;
+  }
+
+  const char* updated_tag() const { return updated_tag_; }
+  void set_updated_tag(const char* updated_tag) { updated_tag_ = updated_tag; }
+
   const char* embedder_kind() const { return embedder_kind_; }
 
   const char* KindAsCString() const;
@@ -217,6 +227,8 @@
   EventKind kind_;
   const char* flag_name_;
   const char* flag_new_value_;
+  const char* previous_tag_;
+  const char* updated_tag_;
   const char* embedder_kind_;
   const char* embedder_stream_id_;
   Breakpoint* breakpoint_;
diff --git a/runtime/vm/stub_code.cc b/runtime/vm/stub_code.cc
index aee8934..78a3355 100644
--- a/runtime/vm/stub_code.cc
+++ b/runtime/vm/stub_code.cc
@@ -35,7 +35,6 @@
     VM_STUB_CODE_LIST(STUB_CODE_DECLARE)
 #undef STUB_CODE_DECLARE
 };
-AcqRelAtomic<bool> StubCode::initialized_ = {false};
 
 #if defined(DART_PRECOMPILED_RUNTIME)
 void StubCode::Init() {
@@ -85,8 +84,6 @@
     ASSERT(StubCode::UnknownDartCode().IsFunctionCode());
   }
 #endif  // defined(DART_PRECOMPILER)
-
-  InitializationDone();
 }
 
 #undef STUB_CODE_GENERATE
@@ -114,13 +111,16 @@
 #endif  // defined(DART_PRECOMPILED_RUNTIME)
 
 void StubCode::Cleanup() {
-  initialized_.store(false, std::memory_order_release);
-
   for (size_t i = 0; i < ARRAY_SIZE(entries_); i++) {
     entries_[i].code = nullptr;
   }
 }
 
+bool StubCode::HasBeenInitialized() {
+  // Use AsynchronousGapMarker as canary.
+  return entries_[kAsynchronousGapMarkerIndex].code != nullptr;
+}
+
 bool StubCode::InInvocationStub(uword pc) {
   ASSERT(HasBeenInitialized());
   uword entry = StubCode::InvokeDartCode().EntryPoint();
diff --git a/runtime/vm/stub_code.h b/runtime/vm/stub_code.h
index ae39cae..6dcedc5 100644
--- a/runtime/vm/stub_code.h
+++ b/runtime/vm/stub_code.h
@@ -42,12 +42,7 @@
   static void Cleanup();
 
   // Returns true if stub code has been initialized.
-  static bool HasBeenInitialized() {
-    return initialized_.load(std::memory_order_acquire);
-  }
-  static void InitializationDone() {
-    initialized_.store(true, std::memory_order_release);
-  }
+  static bool HasBeenInitialized();
 
   // Check if specified pc is in the dart invocation stub used for
   // transitioning into dart code.
@@ -125,7 +120,6 @@
 #endif
   };
   static StubCodeEntry entries_[kNumStubEntries];
-  static AcqRelAtomic<bool> initialized_;
 };
 
 }  // namespace dart
diff --git a/runtime/vm/type_testing_stubs.cc b/runtime/vm/type_testing_stubs.cc
index 98842ce..879677c 100644
--- a/runtime/vm/type_testing_stubs.cc
+++ b/runtime/vm/type_testing_stubs.cc
@@ -106,6 +106,9 @@
   // During bootstrapping we have no access to stubs yet, so we'll just return
   // `null` and patch these later in `Object::FinishInit()`.
   if (!StubCode::HasBeenInitialized()) {
+    ASSERT(type.IsType());
+    const classid_t cid = type.type_class_id();
+    ASSERT(cid == kDynamicCid || cid == kVoidCid);
     return Code::null();
   }
 
diff --git a/sdk/lib/_internal/fix_data.yaml b/sdk/lib/_internal/fix_data.yaml
index 1d4cc33..baf52a4 100644
--- a/sdk/lib/_internal/fix_data.yaml
+++ b/sdk/lib/_internal/fix_data.yaml
@@ -3,7 +3,7 @@
 # BSD-style license that can be found in the LICENSE file.
 
 # Please add new fixes to the top of the file, separated by one blank line
-# from other fixes.  Add corresponding golden tests to 
+# from other fixes.  Add corresponding golden tests to
 # `tests/lib/fix_data_tests` for each new fix.
 
 # For documentation about this file format, see
@@ -59,4 +59,433 @@
       inClass: 'FileMode'
     changes:
       - kind: 'rename'
-        newName: 'writeOnlyAppend'
\ No newline at end of file
+        newName: 'writeOnlyAppend'
+
+  - title: "Rename to 'continue_'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'CONTINUE'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'continue_'
+
+  - title: "Rename to 'switchingProtocols'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'SWITCHING_PROTOCOLS'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'switchingProtocols'
+
+  - title: "Rename to 'ok'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'OK'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'ok'
+
+  - title: "Rename to 'created'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'CREATED'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'created'
+
+  - title: "Rename to 'accepted'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'ACCEPTED'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'accepted'
+
+  - title: "Rename to 'nonAuthoritativeInformation'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'NON_AUTHORITATIVE_INFORMATION'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'nonAuthoritativeInformation'
+
+  - title: "Rename to 'noContent'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'NO_CONTENT'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'noContent'
+
+  - title: "Rename to 'resetContent'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'RESET_CONTENT'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'resetContent'
+
+  - title: "Rename to 'partialContent'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'PARTIAL_CONTENT'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'partialContent'
+
+  - title: "Rename to 'multipleChoices'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'MULTIPLE_CHOICES'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'multipleChoices'
+
+  - title: "Rename to 'movedPermanently'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'MOVED_PERMANENTLY'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'movedPermanently'
+
+  - title: "Rename to 'found'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'FOUND'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'found'
+
+  - title: "Rename to 'movedTemporarily'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'MOVED_TEMPORARILY'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'movedTemporarily'
+
+  - title: "Rename to 'seeOther'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'SEE_OTHER'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'seeOther'
+
+  - title: "Rename to 'notModified'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'NOT_MODIFIED'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'notModified'
+
+  - title: "Rename to 'useProxy'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'USE_PROXY'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'useProxy'
+
+  - title: "Rename to 'temporaryRedirect'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'TEMPORARY_REDIRECT'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'temporaryRedirect'
+
+  - title: "Rename to 'badRequest'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'BAD_REQUEST'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'badRequest'
+
+  - title: "Rename to 'unauthorized'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'UNAUTHORIZED'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'unauthorized'
+
+  - title: "Rename to 'paymentRequired'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'PAYMENT_REQUIRED'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'paymentRequired'
+
+  - title: "Rename to 'forbidden'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'FORBIDDEN'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'forbidden'
+
+  - title: "Rename to 'notFound'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'NOT_FOUND'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'notFound'
+
+  - title: "Rename to 'methodNotAllowed'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'METHOD_NOT_ALLOWED'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'methodNotAllowed'
+
+  - title: "Rename to 'notAcceptable'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'NOT_ACCEPTABLE'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'notAcceptable'
+
+  - title: "Rename to 'proxyAuthenticationRequired'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'PROXY_AUTHENTICATION_REQUIRED'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'proxyAuthenticationRequired'
+
+  - title: "Rename to 'requestTimeout'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'REQUEST_TIMEOUT'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'requestTimeout'
+
+  - title: "Rename to 'conflict'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'CONFLICT'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'conflict'
+
+  - title: "Rename to 'gone'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'GONE'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'gone'
+
+  - title: "Rename to 'lengthRequired'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'LENGTH_REQUIRED'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'lengthRequired'
+
+  - title: "Rename to 'preconditionFailed'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'PRECONDITION_FAILED'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'preconditionFailed'
+
+  - title: "Rename to 'requestEntityTooLarge'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'REQUEST_ENTITY_TOO_LARGE'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'requestEntityTooLarge'
+
+  - title: "Rename to 'requestUriTooLong'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'REQUEST_URI_TOO_LONG'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'requestUriTooLong'
+
+  - title: "Rename to 'unsupportedMediaType'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'UNSUPPORTED_MEDIA_TYPE'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'unsupportedMediaType'
+
+  - title: "Rename to 'requestedRangeNotSatisfiable'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'REQUESTED_RANGE_NOT_SATISFIABLE'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'requestedRangeNotSatisfiable'
+  - title: "Rename to 'expectationFailed'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'EXPECTATION_FAILED'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'expectationFailed'
+
+  - title: "Rename to 'upgradeRequired'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'UPGRADE_REQUIRED'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'upgradeRequired'
+
+  - title: "Rename to 'internalServerError'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'INTERNAL_SERVER_ERROR'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'internalServerError'
+
+  - title: "Rename to 'notImplemented'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'NOT_IMPLEMENTED'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'notImplemented'
+
+  - title: "Rename to 'badGateway'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'BAD_GATEWAY'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'badGateway'
+
+  - title: "Rename to 'serviceUnavailable'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'SERVICE_UNAVAILABLE'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'serviceUnavailable'
+
+  - title: "Rename to 'gatewayTimeout'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'GATEWAY_TIMEOUT'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'gatewayTimeout'
+
+  - title: "Rename to 'httpVersionNotSupported'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'HTTP_VERSION_NOT_SUPPORTED'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'httpVersionNotSupported'
+
+  - title: "Rename to 'networkConnectTimeoutError'"
+    date: 2021-06-15
+    element:
+      uris: [ 'dart:io', 'dart:html' ]
+      field: 'NETWORK_CONNECT_TIMEOUT_ERROR'
+      inClass: 'HttpStatus'
+    changes:
+      - kind: 'rename'
+        newName: 'networkConnectTimeoutError'
diff --git a/sdk/lib/_internal/vm/lib/profiler.dart b/sdk/lib/_internal/vm/lib/profiler.dart
index 812916e..722d938 100644
--- a/sdk/lib/_internal/vm/lib/profiler.dart
+++ b/sdk/lib/_internal/vm/lib/profiler.dart
@@ -18,7 +18,6 @@
 class _UserTag implements UserTag {
   factory _UserTag(String label) native "UserTag_new";
   String get label native "UserTag_label";
-  @pragma("vm:recognized", "asm-intrinsic")
   UserTag makeCurrent() native "UserTag_makeCurrent";
 }
 
diff --git a/sdk/lib/async/future.dart b/sdk/lib/async/future.dart
index 4d98a53..edc6264 100644
--- a/sdk/lib/async/future.dart
+++ b/sdk/lib/async/future.dart
@@ -698,6 +698,30 @@
   Future<T> timeout(Duration timeLimit, {FutureOr<T> onTimeout()?});
 }
 
+/// Explicitly ignores a future.
+///
+/// Not all futures need to be awaited.
+/// The Dart linter has an optional ["unawaited futures" lint](https://dart-lang.github.io/linter/lints/unawaited_futures.html)
+/// which enforces that futures (expressions with a static type of [Future])
+/// in asynchronous functions are handled *somehow*.
+/// If a particular future value doesn't need to be awaited,
+/// you can call `unawaited(...)` with it, which will avoid the lint,
+/// simply because the expression no longer has type [Future].
+/// Using `unawaited` has no other effect.
+/// You should use `unawaited` to convey the *intention* of
+/// deliberately not waiting for the future.
+///
+/// If the future completes with an error,
+/// it was likely a mistake to not await it.
+/// That error will still occur and will be considered unhandled
+/// unless the same future is awaited (or otherwise handled) elsewhere too.
+/// Because of that, `unawaited` should only be used for futures that
+/// are *expected* to complete with a value.
+/// You can use [FutureExtension.ignore] if you also don't want to know
+/// about errors from this future.
+@Since("2.15")
+void unawaited(Future<void> future) {}
+
 /// Convenience methods on futures.
 ///
 /// Adds functionality to futures which makes it easier to
@@ -770,6 +794,32 @@
             handleError(error as E, stackTrace),
         test: (Object error) => error is E && (test == null || test(error)));
   }
+
+  /// Completely ignores this future and its result.
+  ///
+  /// Not all futures are important, not even if they contain errors,
+  /// for example if a request was made, but the response is no longer needed.
+  /// Simply ignoring a future can result in uncaught asynchronous errors.
+  /// This method instead handles (and ignores) any values or errors
+  /// coming from this future, making it safe to otherwise ignore
+  /// the future.
+  ///
+  /// Use `ignore` to signal that the result of the future is
+  /// no longer important to the program, not even if it's an error.
+  /// If you merely want to silence the ["unawaited futures" lint](https://dart-lang.github.io/linter/lints/unawaited_futures.html),
+  /// use the [unawaited] function instead.
+  /// That will ensure that an unexpected error is still reported.
+  @Since("2.15")
+  void ignore() {
+    var self = this;
+    if (self is _Future<T>) {
+      self._ignore();
+    } else {
+      self.then<void>(_ignore, onError: _ignore);
+    }
+  }
+
+  static void _ignore(Object? _, [Object? __]) {}
 }
 
 /// Thrown when a scheduled timeout happens while waiting for an async result.
diff --git a/sdk/lib/async/future_impl.dart b/sdk/lib/async/future_impl.dart
index e73300f..c89d345 100644
--- a/sdk/lib/async/future_impl.dart
+++ b/sdk/lib/async/future_impl.dart
@@ -69,9 +69,14 @@
   static const int maskTestError = 4;
   static const int maskWhenComplete = 8;
   static const int stateChain = 0;
+  // Handles values, passes errors on.
   static const int stateThen = maskValue;
+  // Handles values and errors.
   static const int stateThenOnerror = maskValue | maskError;
+  // Handles errors, has errorCallback.
   static const int stateCatchError = maskError;
+  // Ignores both values and errors. Has no callback or errorCallback.
+  // The [result] future is ignored, its always the same as the source.
   static const int stateCatchErrorTest = maskError | maskTestError;
   static const int stateWhenComplete = maskWhenComplete;
   static const int maskType =
@@ -191,21 +196,40 @@
   /// [_FutureListener] listeners.
   static const int _stateIncomplete = 0;
 
+  /// Flag set when an error need not be handled.
+  ///
+  /// Set by the [FutureExtensions.ignore] method to avoid
+  /// having to introduce an unnecessary listener.
+  /// Only relevant until the future is completed.
+  static const int _stateIgnoreError = 1;
+
   /// Pending completion. Set when completed using [_asyncComplete] or
   /// [_asyncCompleteError]. It is an error to try to complete it again.
   /// [_resultOrListeners] holds listeners.
-  static const int _statePendingComplete = 1;
+  static const int _statePendingComplete = 2;
 
-  /// The future has been chained to another future. The result of that
-  /// other future becomes the result of this future as well.
+  /// The future has been chained to another "source" [_Future].
+  ///
+  /// The result of that other future becomes the result of this future
+  /// as well, when the other future completes.
+  /// This future cannot be completed again.
   /// [_resultOrListeners] contains the source future.
-  static const int _stateChained = 2;
+  /// Listeners have been moved to the chained future.
+  static const int _stateChained = 4;
 
   /// The future has been completed with a value result.
-  static const int _stateValue = 4;
+  ///
+  /// [_resultOrListeners] contains the value.
+  static const int _stateValue = 8;
 
   /// The future has been completed with an error result.
-  static const int _stateError = 8;
+  ///
+  /// [_resultOrListeners] contains an [AsyncEror]
+  /// holding the error and stack trace.
+  static const int _stateError = 16;
+
+  /// Mask for the states above except [_stateIgnoreError].
+  static const int _completionStateMask = 30;
 
   /// Whether the future is complete, and as what.
   int _state = _stateIncomplete;
@@ -227,8 +251,8 @@
   /// and it is not chained to another future.
   ///
   /// The future is another future that this future is chained to. This future
-  /// is waiting for the other future to complete, and when it does, this future
-  /// will complete with the same result.
+  /// is waiting for the other future to complete, and when it does,
+  /// this future will complete with the same result.
   /// All listeners are forwarded to the other future.
   @pragma("vm:entry-point")
   var _resultOrListeners;
@@ -253,12 +277,14 @@
   /// Creates a future that is already completed with the value.
   _Future.value(T value) : this.zoneValue(value, Zone._current);
 
-  bool get _mayComplete => _state == _stateIncomplete;
-  bool get _isPendingComplete => _state == _statePendingComplete;
-  bool get _mayAddListener => _state <= _statePendingComplete;
-  bool get _isChained => _state == _stateChained;
-  bool get _isComplete => _state >= _stateValue;
-  bool get _hasError => _state == _stateError;
+  bool get _mayComplete => (_state & _completionStateMask) == _stateIncomplete;
+  bool get _isPendingComplete => (_state & _statePendingComplete) != 0;
+  bool get _mayAddListener =>
+      _state <= (_statePendingComplete | _stateIgnoreError);
+  bool get _isChained => (_state & _stateChained) != 0;
+  bool get _isComplete => (_state & (_stateValue | _stateError)) != 0;
+  bool get _hasError => (_state & _stateError) != 0;
+  bool get _ignoreError => (_state & _stateIgnoreError) != 0;
 
   static List<Function>? _continuationFunctions(_Future<Object> future) {
     List<Function>? result = null;
@@ -283,7 +309,7 @@
 
   void _setChained(_Future source) {
     assert(_mayAddListener);
-    _state = _stateChained;
+    _state = _stateChained | (_state & _stateIgnoreError);
     _resultOrListeners = source;
   }
 
@@ -315,6 +341,10 @@
     return result;
   }
 
+  void _ignore() {
+    _state |= _stateIgnoreError;
+  }
+
   Future<T> catchError(Function onError, {bool test(Object error)?}) {
     _Future<T> result = new _Future<T>();
     if (!identical(result._zone, _rootZone)) {
@@ -337,13 +367,13 @@
   Stream<T> asStream() => new Stream<T>.fromFuture(this);
 
   void _setPendingComplete() {
-    assert(_mayComplete);
-    _state = _statePendingComplete;
+    assert(_mayComplete); // Aka _statIncomplete
+    _state ^= _stateIncomplete ^ _statePendingComplete;
   }
 
   void _clearPendingComplete() {
     assert(_isPendingComplete);
-    _state = _stateIncomplete;
+    _state ^= _statePendingComplete ^ _stateIncomplete;
   }
 
   AsyncError get _error {
@@ -365,7 +395,7 @@
 
   void _setErrorObject(AsyncError error) {
     assert(!_isComplete); // But may have a completion pending.
-    _state = _stateError;
+    _state = _stateError | (_state & _stateIgnoreError);
     _resultOrListeners = error;
   }
 
@@ -379,7 +409,8 @@
   void _cloneResult(_Future source) {
     assert(!_isComplete);
     assert(source._isComplete);
-    _state = source._state;
+    _state =
+        (source._state & _completionStateMask) | (_state & _stateIgnoreError);
     _resultOrListeners = source._resultOrListeners;
   }
 
@@ -615,7 +646,7 @@
       assert(source._isComplete);
       bool hasError = source._hasError;
       if (listeners == null) {
-        if (hasError) {
+        if (hasError && !source._ignoreError) {
           AsyncError asyncError = source._error;
           source._zone
               .handleUncaughtError(asyncError.error, asyncError.stackTrace);
diff --git a/tests/language/regress/regress23746_test.dart b/tests/language/regress/regress23746_test.dart
new file mode 100644
index 0000000..fd9a510
--- /dev/null
+++ b/tests/language/regress/regress23746_test.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:expect/expect.dart";
+
+class A<T, U> {
+  B<T> get b => B<T>();
+}
+
+class B<T> {}
+
+@pragma('vm:never-inline')
+bool test(Object a, Object b) {
+  print(a.runtimeType);
+  print(b.runtimeType);
+  return a.runtimeType == b.runtimeType;
+}
+
+void main() {
+  Expect.isTrue(test(B<int>(), A<int, String>().b));
+}
diff --git a/tests/language/static_type_helper.dart b/tests/language/static_type_helper.dart
index 9484387..4a7809a 100644
--- a/tests/language/static_type_helper.dart
+++ b/tests/language/static_type_helper.dart
@@ -2,7 +2,10 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// Ensures a context type of [T] for the operand.
+/// Helper to create [Type] values.
+Type typeOf<T>() => T;
+
+/// Ensures a context type of [T] for the operand.
 void context<T>(T x) {}
 
 /// Captures the context type of the call and returns the same type.
@@ -34,6 +37,23 @@
   T expectStaticType<R extends Exactly<T>>() {
     return this;
   }
+
+  /// Invokes [callback] with the static type of `this`.
+  ///
+  /// Allows any operation on the type.
+  T captureStaticType(void Function<X>() callback) {
+    callback<T>();
+    return this;
+  }
+}
+
+/// Invokes [callback] with the static type of [value].
+///
+/// Similar to [StaticType.captureStaticType], but works
+/// for types like `void` and `dynamic` which do not allow
+/// extension methods.
+void captureStaticType<T>(T value, void Function<X>(X value) callback) {
+  callback<T>(value);
 }
 
 /// Use with [StaticType.expectStaticType] to expect precisely the type [T].
diff --git a/tests/language_2/regress/regress23746_test.dart b/tests/language_2/regress/regress23746_test.dart
new file mode 100644
index 0000000..fd9a510
--- /dev/null
+++ b/tests/language_2/regress/regress23746_test.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:expect/expect.dart";
+
+class A<T, U> {
+  B<T> get b => B<T>();
+}
+
+class B<T> {}
+
+@pragma('vm:never-inline')
+bool test(Object a, Object b) {
+  print(a.runtimeType);
+  print(b.runtimeType);
+  return a.runtimeType == b.runtimeType;
+}
+
+void main() {
+  Expect.isTrue(test(B<int>(), A<int, String>().b));
+}
diff --git a/tests/language_2/static_type_helper.dart b/tests/language_2/static_type_helper.dart
new file mode 100644
index 0000000..4a7809a
--- /dev/null
+++ b/tests/language_2/static_type_helper.dart
@@ -0,0 +1,90 @@
+// 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.
+
+/// Helper to create [Type] values.
+Type typeOf<T>() => T;
+
+/// Ensures a context type of [T] for the operand.
+void context<T>(T x) {}
+
+/// Captures the context type of the call and returns the same type.
+///
+/// Can be used to check the context type as:
+/// ```dart
+/// int x = contextType(1 /* valid value */)..expectStaticType<Exactly<int>>;
+/// ```
+T contextType<T>(Object result) => result as T;
+
+extension StaticType<T> on T {
+  /// Check the static type.
+  ///
+  /// Use as follows (assuming `e` has static type `num`):
+  /// ```dart
+  ///   e.expectStaticType<Exactly<num>>()  // No context type.
+  ///   e.expectStaticType<SubtypeOf<Object>>()  // No context type.
+  ///   e.expectStaticType<SupertypeOf<int>>()  // No context type.
+  /// ```
+  /// or
+  /// ```dart
+  ///   e..expectStaticType<Exactly<num>>()  // Preserve context type.
+  ///   e..expectStaticType<SubtypeOf<Object>>()  // Preserve context type.
+  ///   e..expectStaticType<SupertypeOf<int>>()  // Preserve context type.
+  /// ```
+  /// This will be a *compile-time error* if the static type is not
+  /// as required by the constraints type (the one passed to [Exactly],
+  /// [SubtypeOf] or [SupertypeOf].)
+  T expectStaticType<R extends Exactly<T>>() {
+    return this;
+  }
+
+  /// Invokes [callback] with the static type of `this`.
+  ///
+  /// Allows any operation on the type.
+  T captureStaticType(void Function<X>() callback) {
+    callback<T>();
+    return this;
+  }
+}
+
+/// Invokes [callback] with the static type of [value].
+///
+/// Similar to [StaticType.captureStaticType], but works
+/// for types like `void` and `dynamic` which do not allow
+/// extension methods.
+void captureStaticType<T>(T value, void Function<X>(X value) callback) {
+  callback<T>(value);
+}
+
+/// Use with [StaticType.expectStaticType] to expect precisely the type [T].
+///
+/// Example use:
+/// ```dart
+/// "abc".expectStaticType<Exactly<String>>();
+/// ```
+typedef Exactly<T> = T Function(T);
+
+/// Use with [StaticType.expectStaticType] to expect a subtype of [T].
+///
+/// Example use:
+/// ```dart
+/// num x = 1;
+/// x.expectStaticType<SubtypeOf<Object>>();
+/// ```
+typedef SubtypeOf<T> = Never Function(T);
+
+/// Use with [StaticType.expectStaticType] to expect a supertype of [T].
+///
+/// Example use:
+/// ```dart
+/// num x = 1;
+/// x.expectStaticType<SupertypeOf<int>>();
+/// ```
+typedef SupertypeOf<T> = T Function(Object?);
+
+/// Checks that an expression is assignable to [T1], [T2] and [Object].
+///
+/// This ensures that the static type of the expression is either dynamic,
+/// Never, or a type assignable to both [T1] and [T2], and if those are
+/// unrelated, it must be an intersection type.
+void checkIntersectionType<T1, T2>(T1 v1, T2 v2, Object v3) {}
diff --git a/tests/lib/async/future_extension_test.dart b/tests/lib/async/future_extension_test.dart
new file mode 100644
index 0000000..cd28986
--- /dev/null
+++ b/tests/lib/async/future_extension_test.dart
@@ -0,0 +1,54 @@
+// 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:async_helper/async_helper.dart';
+import "package:expect/expect.dart";
+import 'dart:async' show Completer, runZonedGuarded;
+import '../../language/static_type_helper.dart';
+
+void main() {
+  testIgnore();
+}
+
+void testIgnore() {
+  var future = Future<int>.value(42);
+  captureStaticType(future.ignore(), <T>(T value) {
+    Expect.equals(typeOf<void>(), T);
+  });
+
+  asyncStart();
+  // Ignored futures can still be listend to.
+  {
+    var c = Completer<int>.sync();
+    var f = c.future;
+    f.ignore();
+    asyncStart();
+    f.catchError((e) {
+      Expect.equals("ERROR1", e);
+      asyncEnd();
+      return 0;
+    });
+    c.completeError("ERROR1");
+  }
+
+  // Ignored futures are not uncaught errors.
+  {
+    asyncStart();
+    bool threw = false;
+    runZonedGuarded(() {
+      var c = Completer<int>.sync();
+      var f = c.future;
+      f.ignore();
+      c.completeError("ERROR2");
+    }, (e, s) {
+      threw = true;
+      Expect.fail("Should not happen: $e");
+    });
+    Future.delayed(Duration.zero, () {
+      if (threw) Expect.fail("Future not ignored.");
+      asyncEnd();
+    });
+  }
+  asyncEnd();
+}
diff --git a/tests/lib/async/unawaited_error_test.dart b/tests/lib/async/unawaited_error_test.dart
new file mode 100644
index 0000000..597a9f8
--- /dev/null
+++ b/tests/lib/async/unawaited_error_test.dart
@@ -0,0 +1,11 @@
+// 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.
+
+void main() {
+  // The `unawaited` function is not exposed by dart:core.
+  unawaited;
+  // [error line 7, column 3, length 9]
+  // [cfe] Getter not found: 'unawaited'.
+  // [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
+}
diff --git a/tests/lib/async/unawaited_test.dart b/tests/lib/async/unawaited_test.dart
new file mode 100644
index 0000000..dc095cc
--- /dev/null
+++ b/tests/lib/async/unawaited_test.dart
@@ -0,0 +1,52 @@
+// 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:async_helper/async_helper.dart';
+import "package:expect/expect.dart";
+import 'dart:async' show Completer, runZonedGuarded, unawaited;
+import 'dart:async' as prefix;
+import '../../language/static_type_helper.dart';
+
+void main() {
+  testUnawaited();
+}
+
+void testUnawaited() {
+  // Exists where expected.
+  prefix.unawaited.expectStaticType<Exactly<void Function(Future<Object?>)>>();
+
+  var future = Future<int>.value(42);
+  captureStaticType(unawaited(future), <T>(value) {
+    Expect.equals(typeOf<void>(), T);
+  });
+
+  asyncStart();
+  // Unawaited futures still throw.
+  {
+    var c = Completer<int>();
+    var f = c.future;
+    unawaited(f);
+    asyncStart();
+    f.catchError((e) {
+      Expect.equals("ERROR1", e);
+      asyncEnd();
+      return 0;
+    });
+    c.completeError("ERROR1");
+  }
+  // Unawaited futures are still uncaught errors.
+  {
+    asyncStart();
+    runZonedGuarded(() {
+      var c = Completer<int>();
+      var f = c.future;
+      unawaited(f);
+      c.completeError("ERROR2");
+    }, (e, s) {
+      Expect.equals("ERROR2", e);
+      asyncEnd();
+    });
+  }
+  asyncEnd();
+}
diff --git a/tests/lib/fix_data_tests/file.dart b/tests/lib/fix_data_tests/file.dart
deleted file mode 100644
index 0e5435d..0000000
--- a/tests/lib/fix_data_tests/file.dart
+++ /dev/null
@@ -1,13 +0,0 @@
-// 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 'dart:io';
-
-void main() {
-  print(FileMode.APPEND);
-  print(FileMode.READ);
-  print(FileMode.WRITE);
-  print(FileMode.WRITE_ONLY);
-  print(FileMode.WRITE_ONLY_APPEND);
-}
diff --git a/tests/lib/fix_data_tests/file.dart.expect b/tests/lib/fix_data_tests/file.dart.expect
deleted file mode 100644
index aafbba6..0000000
--- a/tests/lib/fix_data_tests/file.dart.expect
+++ /dev/null
@@ -1,13 +0,0 @@
-// 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 'dart:io';
-
-void main() {
-  print(FileMode.append);
-  print(FileMode.read);
-  print(FileMode.write);
-  print(FileMode.writeOnly);
-  print(FileMode.writeOnlyAppend);
-}
diff --git a/tests/lib/fix_data_tests/html.dart b/tests/lib/fix_data_tests/html.dart
new file mode 100644
index 0000000..e0f3ac3
--- /dev/null
+++ b/tests/lib/fix_data_tests/html.dart
@@ -0,0 +1,51 @@
+// 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 'dart:html';
+
+void main() {
+  print(HttpStatus.CONTINUE);
+  print(HttpStatus.SWITCHING_PROTOCOLS);
+  print(HttpStatus.OK);
+  print(HttpStatus.CREATED);
+  print(HttpStatus.ACCEPTED);
+  print(HttpStatus.NON_AUTHORITATIVE_INFORMATION);
+  print(HttpStatus.NO_CONTENT);
+  print(HttpStatus.RESET_CONTENT);
+  print(HttpStatus.PARTIAL_CONTENT);
+  print(HttpStatus.MULTIPLE_CHOICES);
+  print(HttpStatus.MOVED_PERMANENTLY);
+  print(HttpStatus.FOUND);
+  print(HttpStatus.MOVED_TEMPORARILY);
+  print(HttpStatus.SEE_OTHER);
+  print(HttpStatus.NOT_MODIFIED);
+  print(HttpStatus.USE_PROXY);
+  print(HttpStatus.TEMPORARY_REDIRECT);
+  print(HttpStatus.BAD_REQUEST);
+  print(HttpStatus.UNAUTHORIZED);
+  print(HttpStatus.PAYMENT_REQUIRED);
+  print(HttpStatus.FORBIDDEN);
+  print(HttpStatus.NOT_FOUND);
+  print(HttpStatus.METHOD_NOT_ALLOWED);
+  print(HttpStatus.NOT_ACCEPTABLE);
+  print(HttpStatus.PROXY_AUTHENTICATION_REQUIRED);
+  print(HttpStatus.REQUEST_TIMEOUT);
+  print(HttpStatus.CONFLICT);
+  print(HttpStatus.GONE);
+  print(HttpStatus.LENGTH_REQUIRED);
+  print(HttpStatus.PRECONDITION_FAILED);
+  print(HttpStatus.REQUEST_ENTITY_TOO_LARGE);
+  print(HttpStatus.REQUEST_URI_TOO_LONG);
+  print(HttpStatus.UNSUPPORTED_MEDIA_TYPE);
+  print(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE);
+  print(HttpStatus.EXPECTATION_FAILED);
+  print(HttpStatus.UPGRADE_REQUIRED);
+  print(HttpStatus.INTERNAL_SERVER_ERROR);
+  print(HttpStatus.NOT_IMPLEMENTED);
+  print(HttpStatus.BAD_GATEWAY);
+  print(HttpStatus.SERVICE_UNAVAILABLE);
+  print(HttpStatus.GATEWAY_TIMEOUT);
+  print(HttpStatus.HTTP_VERSION_NOT_SUPPORTED);
+  print(HttpStatus.NETWORK_CONNECT_TIMEOUT_ERROR);
+}
diff --git a/tests/lib/fix_data_tests/html.dart.expect b/tests/lib/fix_data_tests/html.dart.expect
new file mode 100644
index 0000000..066f0b3
--- /dev/null
+++ b/tests/lib/fix_data_tests/html.dart.expect
@@ -0,0 +1,51 @@
+// 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 'dart:html';
+
+void main() {
+  print(HttpStatus.continue_);
+  print(HttpStatus.switchingProtocols);
+  print(HttpStatus.ok);
+  print(HttpStatus.created);
+  print(HttpStatus.accepted);
+  print(HttpStatus.nonAuthoritativeInformation);
+  print(HttpStatus.noContent);
+  print(HttpStatus.resetContent);
+  print(HttpStatus.partialContent);
+  print(HttpStatus.multipleChoices);
+  print(HttpStatus.movedPermanently);
+  print(HttpStatus.found);
+  print(HttpStatus.movedTemporarily);
+  print(HttpStatus.seeOther);
+  print(HttpStatus.notModified);
+  print(HttpStatus.useProxy);
+  print(HttpStatus.temporaryRedirect);
+  print(HttpStatus.badRequest);
+  print(HttpStatus.unauthorized);
+  print(HttpStatus.paymentRequired);
+  print(HttpStatus.forbidden);
+  print(HttpStatus.notFound);
+  print(HttpStatus.methodNotAllowed);
+  print(HttpStatus.notAcceptable);
+  print(HttpStatus.proxyAuthenticationRequired);
+  print(HttpStatus.requestTimeout);
+  print(HttpStatus.conflict);
+  print(HttpStatus.gone);
+  print(HttpStatus.lengthRequired);
+  print(HttpStatus.preconditionFailed);
+  print(HttpStatus.requestEntityTooLarge);
+  print(HttpStatus.requestUriTooLong);
+  print(HttpStatus.unsupportedMediaType);
+  print(HttpStatus.requestedRangeNotSatisfiable);
+  print(HttpStatus.expectationFailed);
+  print(HttpStatus.upgradeRequired);
+  print(HttpStatus.internalServerError);
+  print(HttpStatus.notImplemented);
+  print(HttpStatus.badGateway);
+  print(HttpStatus.serviceUnavailable);
+  print(HttpStatus.gatewayTimeout);
+  print(HttpStatus.httpVersionNotSupported);
+  print(HttpStatus.networkConnectTimeoutError);
+}
diff --git a/tests/lib/fix_data_tests/io.dart b/tests/lib/fix_data_tests/io.dart
new file mode 100644
index 0000000..c43ec21
--- /dev/null
+++ b/tests/lib/fix_data_tests/io.dart
@@ -0,0 +1,56 @@
+// 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 'dart:io';
+
+void main() {
+  print(FileMode.APPEND);
+  print(FileMode.READ);
+  print(FileMode.WRITE);
+  print(FileMode.WRITE_ONLY);
+  print(FileMode.WRITE_ONLY_APPEND);
+  print(HttpStatus.CONTINUE);
+  print(HttpStatus.SWITCHING_PROTOCOLS);
+  print(HttpStatus.OK);
+  print(HttpStatus.CREATED);
+  print(HttpStatus.ACCEPTED);
+  print(HttpStatus.NON_AUTHORITATIVE_INFORMATION);
+  print(HttpStatus.NO_CONTENT);
+  print(HttpStatus.RESET_CONTENT);
+  print(HttpStatus.PARTIAL_CONTENT);
+  print(HttpStatus.MULTIPLE_CHOICES);
+  print(HttpStatus.MOVED_PERMANENTLY);
+  print(HttpStatus.FOUND);
+  print(HttpStatus.MOVED_TEMPORARILY);
+  print(HttpStatus.SEE_OTHER);
+  print(HttpStatus.NOT_MODIFIED);
+  print(HttpStatus.USE_PROXY);
+  print(HttpStatus.TEMPORARY_REDIRECT);
+  print(HttpStatus.BAD_REQUEST);
+  print(HttpStatus.UNAUTHORIZED);
+  print(HttpStatus.PAYMENT_REQUIRED);
+  print(HttpStatus.FORBIDDEN);
+  print(HttpStatus.NOT_FOUND);
+  print(HttpStatus.METHOD_NOT_ALLOWED);
+  print(HttpStatus.NOT_ACCEPTABLE);
+  print(HttpStatus.PROXY_AUTHENTICATION_REQUIRED);
+  print(HttpStatus.REQUEST_TIMEOUT);
+  print(HttpStatus.CONFLICT);
+  print(HttpStatus.GONE);
+  print(HttpStatus.LENGTH_REQUIRED);
+  print(HttpStatus.PRECONDITION_FAILED);
+  print(HttpStatus.REQUEST_ENTITY_TOO_LARGE);
+  print(HttpStatus.REQUEST_URI_TOO_LONG);
+  print(HttpStatus.UNSUPPORTED_MEDIA_TYPE);
+  print(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE);
+  print(HttpStatus.EXPECTATION_FAILED);
+  print(HttpStatus.UPGRADE_REQUIRED);
+  print(HttpStatus.INTERNAL_SERVER_ERROR);
+  print(HttpStatus.NOT_IMPLEMENTED);
+  print(HttpStatus.BAD_GATEWAY);
+  print(HttpStatus.SERVICE_UNAVAILABLE);
+  print(HttpStatus.GATEWAY_TIMEOUT);
+  print(HttpStatus.HTTP_VERSION_NOT_SUPPORTED);
+  print(HttpStatus.NETWORK_CONNECT_TIMEOUT_ERROR);
+}
diff --git a/tests/lib/fix_data_tests/io.dart.expect b/tests/lib/fix_data_tests/io.dart.expect
new file mode 100644
index 0000000..8c95823
--- /dev/null
+++ b/tests/lib/fix_data_tests/io.dart.expect
@@ -0,0 +1,56 @@
+// 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 'dart:io';
+
+void main() {
+  print(FileMode.append);
+  print(FileMode.read);
+  print(FileMode.write);
+  print(FileMode.writeOnly);
+  print(FileMode.writeOnlyAppend);
+  print(HttpStatus.continue_);
+  print(HttpStatus.switchingProtocols);
+  print(HttpStatus.ok);
+  print(HttpStatus.created);
+  print(HttpStatus.accepted);
+  print(HttpStatus.nonAuthoritativeInformation);
+  print(HttpStatus.noContent);
+  print(HttpStatus.resetContent);
+  print(HttpStatus.partialContent);
+  print(HttpStatus.multipleChoices);
+  print(HttpStatus.movedPermanently);
+  print(HttpStatus.found);
+  print(HttpStatus.movedTemporarily);
+  print(HttpStatus.seeOther);
+  print(HttpStatus.notModified);
+  print(HttpStatus.useProxy);
+  print(HttpStatus.temporaryRedirect);
+  print(HttpStatus.badRequest);
+  print(HttpStatus.unauthorized);
+  print(HttpStatus.paymentRequired);
+  print(HttpStatus.forbidden);
+  print(HttpStatus.notFound);
+  print(HttpStatus.methodNotAllowed);
+  print(HttpStatus.notAcceptable);
+  print(HttpStatus.proxyAuthenticationRequired);
+  print(HttpStatus.requestTimeout);
+  print(HttpStatus.conflict);
+  print(HttpStatus.gone);
+  print(HttpStatus.lengthRequired);
+  print(HttpStatus.preconditionFailed);
+  print(HttpStatus.requestEntityTooLarge);
+  print(HttpStatus.requestUriTooLong);
+  print(HttpStatus.unsupportedMediaType);
+  print(HttpStatus.requestedRangeNotSatisfiable);
+  print(HttpStatus.expectationFailed);
+  print(HttpStatus.upgradeRequired);
+  print(HttpStatus.internalServerError);
+  print(HttpStatus.notImplemented);
+  print(HttpStatus.badGateway);
+  print(HttpStatus.serviceUnavailable);
+  print(HttpStatus.gatewayTimeout);
+  print(HttpStatus.httpVersionNotSupported);
+  print(HttpStatus.networkConnectTimeoutError);
+}
diff --git a/tests/lib_2/async/future_extension_test.dart b/tests/lib_2/async/future_extension_test.dart
new file mode 100644
index 0000000..cd28986
--- /dev/null
+++ b/tests/lib_2/async/future_extension_test.dart
@@ -0,0 +1,54 @@
+// 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:async_helper/async_helper.dart';
+import "package:expect/expect.dart";
+import 'dart:async' show Completer, runZonedGuarded;
+import '../../language/static_type_helper.dart';
+
+void main() {
+  testIgnore();
+}
+
+void testIgnore() {
+  var future = Future<int>.value(42);
+  captureStaticType(future.ignore(), <T>(T value) {
+    Expect.equals(typeOf<void>(), T);
+  });
+
+  asyncStart();
+  // Ignored futures can still be listend to.
+  {
+    var c = Completer<int>.sync();
+    var f = c.future;
+    f.ignore();
+    asyncStart();
+    f.catchError((e) {
+      Expect.equals("ERROR1", e);
+      asyncEnd();
+      return 0;
+    });
+    c.completeError("ERROR1");
+  }
+
+  // Ignored futures are not uncaught errors.
+  {
+    asyncStart();
+    bool threw = false;
+    runZonedGuarded(() {
+      var c = Completer<int>.sync();
+      var f = c.future;
+      f.ignore();
+      c.completeError("ERROR2");
+    }, (e, s) {
+      threw = true;
+      Expect.fail("Should not happen: $e");
+    });
+    Future.delayed(Duration.zero, () {
+      if (threw) Expect.fail("Future not ignored.");
+      asyncEnd();
+    });
+  }
+  asyncEnd();
+}
diff --git a/tests/lib_2/async/unawaited_error_test.dart b/tests/lib_2/async/unawaited_error_test.dart
new file mode 100644
index 0000000..597a9f8
--- /dev/null
+++ b/tests/lib_2/async/unawaited_error_test.dart
@@ -0,0 +1,11 @@
+// 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.
+
+void main() {
+  // The `unawaited` function is not exposed by dart:core.
+  unawaited;
+  // [error line 7, column 3, length 9]
+  // [cfe] Getter not found: 'unawaited'.
+  // [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
+}
diff --git a/tests/lib_2/async/unawaited_test.dart b/tests/lib_2/async/unawaited_test.dart
new file mode 100644
index 0000000..16bd285
--- /dev/null
+++ b/tests/lib_2/async/unawaited_test.dart
@@ -0,0 +1,54 @@
+// 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.
+
+// @dart = 2.9
+
+import 'package:async_helper/async_helper.dart';
+import "package:expect/expect.dart";
+import 'dart:async' show Completer, runZonedGuarded, unawaited;
+import 'dart:async' as prefix;
+import '../../language/static_type_helper.dart';
+
+void main() {
+  testUnawaited();
+}
+
+void testUnawaited() {
+  // Exists where expected.
+  prefix.unawaited.expectStaticType<Exactly<void Function(Future<Object>)>>();
+
+  var future = Future<int>.value(42);
+  captureStaticType(unawaited(future), <T>(value) {
+    Expect.equals(typeOf<void>(), T);
+  });
+
+  asyncStart();
+  // Unawaited futures still throw.
+  {
+    var c = Completer<int>();
+    var f = c.future;
+    unawaited(f);
+    asyncStart();
+    f.catchError((e) {
+      Expect.equals("ERROR1", e);
+      asyncEnd();
+      return 0;
+    });
+    c.completeError("ERROR1");
+  }
+  // Unawaited futures are still uncaught errors.
+  {
+    asyncStart();
+    runZonedGuarded(() {
+      var c = Completer<int>();
+      var f = c.future;
+      unawaited(f);
+      c.completeError("ERROR2");
+    }, (e, s) {
+      Expect.equals("ERROR2", e);
+      asyncEnd();
+    });
+  }
+  asyncEnd();
+}
diff --git a/tools/VERSION b/tools/VERSION
index d15e618..991514c 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 236
+PRERELEASE 237
 PRERELEASE_PATCH 0
\ No newline at end of file