Version 3.0.0-144.0.dev

Merge ae5dc9e2a929e082fa7a0db65bf52f3bfd3b2414 into dev
diff --git a/DEPS b/DEPS
index ef4ceb9..f292dcc 100644
--- a/DEPS
+++ b/DEPS
@@ -125,7 +125,7 @@
   # For more details, see https://github.com/dart-lang/sdk/issues/30164.
   "dart_style_rev": "d0c6b1da52c9f80679a7aaa05e5935ef5c6bb52f", # manually rev'd
 
-  "dartdoc_rev": "a99abd4ba40758dffdb112b725b7a96c16345684",
+  "dartdoc_rev": "ed56883d7b3c31aea3faa27232c3bffc2c97aaa4",
   "devtools_rev": "dd3fade2bd2ef74fc6102e56333a48f0efc594d3",
   "ffi_rev": "2a56c2a9a87122c057168874bde384debbb806b0",
   "file_rev": "b768f79dcd104a5feabafab47101c4355b71cd8f",
@@ -134,7 +134,7 @@
   "html_rev": "3dd00b0ca99e222697e6b6dc653774dc877da420",
   "http_multi_server_rev": "cce50802b66d33f703f82b3189988aa8e51976ac",
   "http_parser_rev": "6f73e4a399df013ded8f4c81f151d122b36d361b",
-  "http_rev": "c955c7e33cc7b02e10f8fded071272d541518cdd",
+  "http_rev": "57c53b05e9f42546149f51348bc063bc7279283c",
   "icu_rev": "81d656878ec611cb0b42d52c82e9dae93920d9ba",
   "intl_rev": "6140b600b3fd42d86e289f7d92c18488fe7e4bb9",
   "jinja2_rev": "2222b31554f03e62600cd7e383376a7c187967a1",
@@ -151,7 +151,7 @@
   "path_rev": "9768908ef81cf92fc77cb259c2cf01ca8725de88",
   "ply_rev": "604b32590ffad5cbb82e4afef1d305512d06ae93",
   "pool_rev": "ad4e2a7fde6a2937b2f7f59af271b437376d8e3d",
-  "protobuf_rev": "dd04535271a25fd6f11ccaf09bca18d968538420",
+  "protobuf_rev": "cc0f287fb6a8680e431eb9210225f1d0ac33c047",
   "pub_rev": "0cbaf7a2fb8c8ca543c6f222bf25d5f5c63abbf3", # manually rev'd
   "pub_semver_rev": "3946e33446365aa6af84abc0b1f47ad3a6f3b490",
   "root_certificates_rev": "692f6d6488af68e0121317a9c2c9eb393eb0ee50",
@@ -168,7 +168,7 @@
   "test_descriptor_rev": "b73c691c4e38e23bf8383148d3d68fa65e4a57e9",
   "test_process_rev": "62ea2ba786d9e0e7cccedaf305d3972ee41fbc6a",
   "test_reflective_loader_rev": "cf58259b91c4d3cccc90abd4c0b44273e6421646",
-  "test_rev": "43fd92843c5d3cb43dede2b971c69923d9f7308c",
+  "test_rev": "19582a8d45670a53fa4fca8d06a3746d1616d46e",
   "typed_data_rev": "9c209b9c99ca7528a45c23d5d09579788b3ea81b",
   "usage_rev": "2773c7d334db9a902582feb8c5f38899c83d7b54",
   "vector_math_rev": "1eee95b15e5d35cf519514cc253037b19705fb7a",
@@ -177,7 +177,7 @@
   "web_socket_channel_rev": "28d7b82986cf931e6d977d973a7849cfc7c0bab9",
   "WebCore_rev": "bcb10901266c884e7b3740abc597ab95373ab55c",
   "webdev_rev": "f978b90cdfc97967b8caec1b5e2a4919ce9fa3d2",
-  "webdriver_rev": "b8c7bb5f57075e7c4215209c14d3e0a8767bf887",
+  "webdriver_rev": "00c887daa14de516c0dd02f1021225d478f503c1",
   "webkit_inspection_protocol_rev": "15244ffbab9221f1603eb04eaae74ae7c7ca3944",
   "yaml_edit_rev": "299f74594ff9fda412c1da5c0b5d5231d0c6fc42",
   "yaml_rev": "b2fce6cb8f07f091967b849d5434ed495cbb305f",
diff --git a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
index 5365fb9..f1dfc39 100644
--- a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
@@ -157,6 +157,22 @@
   /// See [assert_begin] for more information.
   void assert_end();
 
+  /// Call this method when the temporary variable holding the result of a
+  /// pattern match is assigned to a user-accessible variable.  (Depending on
+  /// the client's model, this might happen right after a variable pattern is
+  /// matched, or later, after one or more logical-or patterns have been
+  /// handled).
+  ///
+  /// [promotionKey] is the promotion key used by flow analysis to represent the
+  /// temporary variable holding the result of the pattern match, and [variable]
+  /// is the user-accessible variable that the value is being assigned to.
+  ///
+  /// Returns the promotion key used by flow analysis to represent [variable].
+  /// This may be used in future calls to [assignMatchedPatternVariable] to
+  /// handle nested logical-ors, or logical-ors nested within switch cases that
+  /// share a body.
+  int assignMatchedPatternVariable(Variable variable, int promotionKey);
+
   /// Call this method when visiting a boolean literal expression.
   void booleanLiteral(Expression expression, bool value);
 
@@ -194,16 +210,34 @@
   /// to [skipDuplicateCheck].
   ///
   /// TODO(paulberry): try to remove all uses of skipDuplicateCheck
-  void declare(Variable variable, bool initialized, Type staticType,
-      {bool skipDuplicateCheck = false});
+  void declare(Variable variable, Type staticType,
+      {required bool initialized, bool skipDuplicateCheck = false});
 
   /// Call this method after visiting a variable pattern in a non-assignment
   /// context (or a wildcard pattern).
   ///
   /// [matchedType] should be the static type of the value being matched.
   /// [staticType] should be the static type of the variable pattern itself.
-  void declaredVariablePattern(
-      {required Type matchedType, required Type staticType});
+  /// [initializerExpression] should be the initializer expression being matched
+  /// (or `null` if there is no expression being matched to this variable).
+  /// [isFinal] indicates whether the variable is final, and [isImplicitlyTyped]
+  /// indicates whether the variable has an explicit type annotation.
+  ///
+  /// Although pattern variables in Dart cannot be late, the client is allowed
+  /// to model a traditional (non-patterned) variable declaration statement
+  /// using the same flow analysis machinery as it uses for pattern variable
+  /// declaration statements; when it does so, it may use [isLate] to indicate
+  /// whether the variable in question is a `late` variable.
+  ///
+  /// Returns the promotion key used by flow analysis to track the temporary
+  /// variable that holds the matched value.
+  int declaredVariablePattern(
+      {required Type matchedType,
+      required Type staticType,
+      Expression? initializerExpression,
+      bool isFinal = false,
+      bool isLate = false,
+      required bool isImplicitlyTyped});
 
   /// Call this method before visiting the body of a "do-while" statement.
   /// [doStatement] should be the same node that was passed to
@@ -992,6 +1026,13 @@
   }
 
   @override
+  int assignMatchedPatternVariable(Variable variable, int promotionKey) {
+    return _wrap('assignMatchedPatternVariable($variable, $promotionKey)',
+        () => _wrapped.assignMatchedPatternVariable(variable, promotionKey),
+        isQuery: true, isPure: false);
+  }
+
+  @override
   void booleanLiteral(Expression expression, bool value) {
     _wrap('booleanLiteral($expression, $value)',
         () => _wrapped.booleanLiteral(expression, value));
@@ -1029,23 +1070,37 @@
   }
 
   @override
-  void declare(Variable variable, bool initialized, Type staticType,
-      {bool skipDuplicateCheck = false}) {
+  void declare(Variable variable, Type staticType,
+      {required bool initialized, bool skipDuplicateCheck = false}) {
     _wrap(
-        'declare($variable, $initialized, $staticType, '
-        'skipDuplicateCheck: $skipDuplicateCheck)',
-        () => _wrapped.declare(variable, initialized, staticType,
-            skipDuplicateCheck: skipDuplicateCheck));
+        'declare($variable, $staticType, '
+        'initialized: $initialized, skipDuplicateCheck: $skipDuplicateCheck)',
+        () => _wrapped.declare(variable, staticType,
+            initialized: initialized, skipDuplicateCheck: skipDuplicateCheck));
   }
 
   @override
-  void declaredVariablePattern(
-      {required Type matchedType, required Type staticType}) {
-    _wrap(
+  int declaredVariablePattern(
+      {required Type matchedType,
+      required Type staticType,
+      Expression? initializerExpression,
+      bool isFinal = false,
+      bool isLate = false,
+      required bool isImplicitlyTyped}) {
+    return _wrap(
         'declaredVariablePattern(matchedType: $matchedType, '
-        'staticType: $staticType)',
+        'staticType: $staticType, '
+        'initializerExpression: $initializerExpression, isFinal: $isFinal, '
+        'isLate: $isLate, isImplicitlyTyped: $isImplicitlyTyped)',
         () => _wrapped.declaredVariablePattern(
-            matchedType: matchedType, staticType: staticType));
+            matchedType: matchedType,
+            staticType: staticType,
+            initializerExpression: initializerExpression,
+            isFinal: isFinal,
+            isLate: isLate,
+            isImplicitlyTyped: isImplicitlyTyped),
+        isQuery: true,
+        isPure: false);
   }
 
   @override
@@ -3532,6 +3587,14 @@
   }
 
   @override
+  int assignMatchedPatternVariable(Variable variable, int promotionKey) {
+    int mergedKey = promotionKeyStore.keyForVariable(variable);
+    _current =
+        _current._updateVariableInfo(mergedKey, _current.infoFor(promotionKey));
+    return mergedKey;
+  }
+
+  @override
   void booleanLiteral(Expression expression, bool value) {
     FlowModel<Type> unreachable = _current.setUnreachable();
     _storeExpressionInfo(
@@ -3588,8 +3651,8 @@
   }
 
   @override
-  void declare(Variable variable, bool initialized, Type staticType,
-      {bool skipDuplicateCheck = false}) {
+  void declare(Variable variable, Type staticType,
+      {required bool initialized, bool skipDuplicateCheck = false}) {
     assert(
         operations.isSameType(staticType, operations.variableType(variable)));
     assert(_debugDeclaredVariables.add(variable) || skipDuplicateCheck,
@@ -3599,8 +3662,13 @@
   }
 
   @override
-  void declaredVariablePattern(
-      {required Type matchedType, required Type staticType}) {
+  int declaredVariablePattern(
+      {required Type matchedType,
+      required Type staticType,
+      Expression? initializerExpression,
+      bool isFinal = false,
+      bool isLate = false,
+      required bool isImplicitlyTyped}) {
     _PatternContext<Type> context = _stack.last as _PatternContext<Type>;
     ReferenceWithType<Type> matchedValueReference =
         context._matchedValueReference;
@@ -3629,6 +3697,16 @@
     if (!coversMatchedType) {
       _unmatched = _join(_unmatched!, ifFalse);
     }
+    // Choose a fresh promotion key to represent the temporary variable that
+    // stores the matched value, and mark it as initialized.
+    int promotionKey = promotionKeyStore.makeTemporaryKey();
+    _current = _current.declare(promotionKey, true);
+    _initialize(promotionKey, matchedType, initializerExpression,
+        isFinal: isFinal,
+        isLate: isLate,
+        isImplicitlyTyped: isImplicitlyTyped,
+        unpromotedType: staticType);
+    return promotionKey;
   }
 
   @override
@@ -3951,30 +4029,11 @@
       required bool isImplicitlyTyped}) {
     Type unpromotedType = operations.variableType(variable);
     int variableKey = promotionKeyStore.keyForVariable(variable);
-    ExpressionInfo<Type>? expressionInfo;
-    if (isLate) {
-      // Don't get expression info for late variables, since we don't know when
-      // they'll be initialized.
-    } else if (isImplicitlyTyped && !respectImplicitlyTypedVarInitializers) {
-      // If the language version is too old, SSA analysis has to ignore
-      // initializer expressions for implicitly typed variables, in order to
-      // preserve the buggy behavior of
-      // https://github.com/dart-lang/language/issues/1785.
-    } else if (initializerExpression != null) {
-      expressionInfo = _getExpressionInfo(initializerExpression);
-    }
-    SsaNode<Type> newSsaNode = new SsaNode<Type>(
-        expressionInfo is _TrivialExpressionInfo ? null : expressionInfo);
-    _current = _current.write(
-        this, null, variableKey, matchedType, newSsaNode, operations,
-        promoteToTypeOfInterest: !isImplicitlyTyped && !isFinal,
+    _initialize(variableKey, matchedType, initializerExpression,
+        isFinal: isFinal,
+        isLate: isLate,
+        isImplicitlyTyped: isImplicitlyTyped,
         unpromotedType: unpromotedType);
-    if (isImplicitlyTyped && operations.isTypeParameterType(matchedType)) {
-      _current = _current
-          .tryPromoteForTypeCheck(this,
-              _variableReference(variableKey, unpromotedType), matchedType)
-          .ifTrue;
-    }
   }
 
   @override
@@ -4687,6 +4746,38 @@
     return promotedType;
   }
 
+  void _initialize(
+      int promotionKey, Type matchedType, Expression? initializerExpression,
+      {required bool isFinal,
+      required bool isLate,
+      required bool isImplicitlyTyped,
+      required Type unpromotedType}) {
+    ExpressionInfo<Type>? expressionInfo;
+    if (isLate) {
+      // Don't get expression info for late variables, since we don't know when
+      // they'll be initialized.
+    } else if (isImplicitlyTyped && !respectImplicitlyTypedVarInitializers) {
+      // If the language version is too old, SSA analysis has to ignore
+      // initializer expressions for implicitly typed variables, in order to
+      // preserve the buggy behavior of
+      // https://github.com/dart-lang/language/issues/1785.
+    } else if (initializerExpression != null) {
+      expressionInfo = _getExpressionInfo(initializerExpression);
+    }
+    SsaNode<Type> newSsaNode = new SsaNode<Type>(
+        expressionInfo is _TrivialExpressionInfo ? null : expressionInfo);
+    _current = _current.write(
+        this, null, promotionKey, matchedType, newSsaNode, operations,
+        promoteToTypeOfInterest: !isImplicitlyTyped && !isFinal,
+        unpromotedType: unpromotedType);
+    if (isImplicitlyTyped && operations.isTypeParameterType(matchedType)) {
+      _current = _current
+          .tryPromoteForTypeCheck(this,
+              _variableReference(promotionKey, unpromotedType), matchedType)
+          .ifTrue;
+    }
+  }
+
   FlowModel<Type> _join(FlowModel<Type>? first, FlowModel<Type>? second) =>
       FlowModel.join(operations, first, second, _current._emptyVariableMap);
 
@@ -4971,6 +5062,9 @@
   void assert_end() {}
 
   @override
+  int assignMatchedPatternVariable(Variable variable, int promotionKey) => 0;
+
+  @override
   void booleanLiteral(Expression expression, bool value) {}
 
   @override
@@ -4994,12 +5088,18 @@
   void constantPattern_end(Expression expression) {}
 
   @override
-  void declare(Variable variable, bool initialized, Type staticType,
-      {bool skipDuplicateCheck = false}) {}
+  void declare(Variable variable, Type staticType,
+      {required bool initialized, bool skipDuplicateCheck = false}) {}
 
   @override
-  void declaredVariablePattern(
-      {required Type matchedType, required Type staticType}) {}
+  int declaredVariablePattern(
+          {required Type matchedType,
+          required Type staticType,
+          Expression? initializerExpression,
+          bool isFinal = false,
+          bool isLate = false,
+          required bool isImplicitlyTyped}) =>
+      0;
 
   @override
   void doStatement_bodyBegin(Statement doStatement) {}
diff --git a/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer.dart b/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer.dart
index 6801af9..95769fe 100644
--- a/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer.dart
@@ -407,16 +407,18 @@
           matchedType: matchedType,
           requiredType: staticType);
     }
-    flow.declaredVariablePattern(
-        matchedType: matchedType, staticType: staticType);
     bool isImplicitlyTyped = declaredType == null;
-    setVariableType(variable, staticType);
-    flow.declare(variable, false, staticType);
     // TODO(paulberry): are we handling _isFinal correctly?
-    flow.initialize(variable, matchedType, context.getInitializer(node),
+    int promotionKey = flow.declaredVariablePattern(
+        matchedType: matchedType,
+        staticType: staticType,
+        initializerExpression: context.getInitializer(node),
         isFinal: context.isFinal || isVariableFinal(variable),
         isLate: context.isLate,
         isImplicitlyTyped: isImplicitlyTyped);
+    setVariableType(variable, staticType);
+    flow.declare(variable, staticType, initialized: false);
+    flow.assignMatchedPatternVariable(variable, promotionKey);
     return staticType;
   }
 
@@ -532,10 +534,7 @@
       location: JoinedPatternVariableLocation.singlePattern,
     );
 
-    handle_ifCaseStatement_afterPattern(
-      node: node,
-      variables: variables.values,
-    );
+    handle_ifCaseStatement_afterPattern(node: node);
     // Stack: (Expression, Pattern)
     if (guard != null) {
       _checkGuardType(guard, analyzeExpression(guard, boolType));
@@ -1507,7 +1506,7 @@
       {required bool isFinal, required bool isLate}) {
     Type inferredType = declaredType ?? dynamicType;
     setVariableType(variable, inferredType);
-    flow.declare(variable, false, inferredType);
+    flow.declare(variable, inferredType, initialized: false);
     return inferredType;
   }
 
@@ -1537,6 +1536,7 @@
       flow.declaredVariablePattern(
         matchedType: matchedType,
         staticType: declaredType,
+        isImplicitlyTyped: false,
       );
     }
   }
@@ -1656,16 +1656,7 @@
   Type getVariableType(Variable variable);
 
   /// Called after visiting the pattern in `if-case` statement.
-  /// [variables] are variables declared in the pattern.
-  ///
-  /// It is expected that the client will push a new scope with [variables]
-  /// available.  This scope should be used to analyze the guard, and the
-  /// `then` branch. The scope is not used for the `else` branch, so on
-  /// [handle_ifStatement_thenEnd] the client should pop it.
-  void handle_ifCaseStatement_afterPattern({
-    required Statement node,
-    required Iterable<Variable> variables,
-  }) {}
+  void handle_ifCaseStatement_afterPattern({required Statement node}) {}
 
   /// Called after visiting the expression of an `if` element.
   void handle_ifElement_conditionEnd(Node node) {}
@@ -1936,7 +1927,7 @@
           isFinal: resultIsFinal,
           type: resultType,
         );
-        flow.declare(variable, true, resultType);
+        flow.declare(variable, resultType, initialized: true);
       }
     }
   }
diff --git a/pkg/_fe_analyzer_shared/test/mini_ast.dart b/pkg/_fe_analyzer_shared/test/mini_ast.dart
index 53969f7..29f18ef 100644
--- a/pkg/_fe_analyzer_shared/test/mini_ast.dart
+++ b/pkg/_fe_analyzer_shared/test/mini_ast.dart
@@ -914,6 +914,7 @@
     'List<int> <: Object': true,
     'Never <: Object': true,
     'Never <: Object?': true,
+    'Null <: double': false,
     'Null <: double?': true,
     'Null <: int': false,
     'Null <: Object': false,
@@ -3763,11 +3764,8 @@
   }
 
   @override
-  void handle_ifCaseStatement_afterPattern({
-    required covariant _IfCase node,
-    required Iterable<Var> variables,
-  }) {
-    _irVariables(node, variables);
+  void handle_ifCaseStatement_afterPattern({required covariant _IfCase node}) {
+    _irVariables(node, node._candidateVariables.values);
   }
 
   void handleAssignedVariablePattern(covariant _VariablePattern node) {
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/remove_non_null_assertion.dart b/pkg/analysis_server/lib/src/services/correction/dart/remove_non_null_assertion.dart
index bb1ee6c..91b3083 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/remove_non_null_assertion.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/remove_non_null_assertion.dart
@@ -35,5 +35,13 @@
         builder.addDeletion(range.startStart(bangToken, bangToken.next!));
       });
     }
+
+    if (expression is NullAssertPattern) {
+      var bangToken = expression.operator;
+
+      await builder.addDartFileEdit(file, (builder) {
+        builder.addDeletion(range.startStart(bangToken, bangToken.next!));
+      });
+    }
   }
 }
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/remove_question_mark.dart b/pkg/analysis_server/lib/src/services/correction/dart/remove_question_mark.dart
index e8ee546..c51a340 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/remove_question_mark.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/remove_question_mark.dart
@@ -42,5 +42,11 @@
         builder.addDeletion(range.token(questionMark));
       });
     }
+    if (targetNode is NullCheckPattern) {
+      var questionMark = targetNode.operator;
+      await builder.addDartFileEdit(file, (builder) {
+        builder.addDeletion(range.token(questionMark));
+      });
+    }
   }
 }
diff --git a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
index 85b03ac..d59e50c 100644
--- a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
+++ b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
@@ -1370,14 +1370,6 @@
   status: hasFix
 HintCode.DIVISION_OPTIMIZATION:
   status: hasFix
-HintCode.EQUAL_ELEMENTS_IN_SET:
-  status: noFix
-  notes: |-
-    Fix depends on user's intent, which can't be known.
-HintCode.EQUAL_KEYS_IN_MAP:
-  status: noFix
-  notes: |-
-    Fix depends on user's intent, which can't be known.
 HintCode.FILE_IMPORT_INSIDE_LIB_REFERENCES_FILE_OUTSIDE:
   status: noFix
   notes: |-
@@ -2724,9 +2716,9 @@
 StaticWarningCode.UNNECESSARY_NON_NULL_ASSERTION:
   status: hasFix
 StaticWarningCode.UNNECESSARY_NULL_ASSERT_PATTERN:
-  status: needsFix
+  status: hasFix
 StaticWarningCode.UNNECESSARY_NULL_CHECK_PATTERN:
-  status: needsFix
+  status: hasFix
 WarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE_TO_ERROR_HANDLER:
   status: noFix
   notes: |-
@@ -2761,6 +2753,14 @@
   status: hasFix
 WarningCode.DUPLICATE_SHOWN_NAME:
   status: hasFix
+WarningCode.EQUAL_ELEMENTS_IN_SET:
+  status: noFix
+  notes: |-
+    Fix depends on user's intent, which can't be known.
+WarningCode.EQUAL_KEYS_IN_MAP:
+  status: noFix
+  notes: |-
+    Fix depends on user's intent, which can't be known.
 WarningCode.SDK_VERSION_ASYNC_EXPORTED_FROM_CORE:
   status: hasFix
 WarningCode.SDK_VERSION_AS_EXPRESSION_IN_CONST_CONTEXT:
@@ -2787,4 +2787,4 @@
   status: hasFix
 WarningCode.SDK_VERSION_UI_AS_CODE_IN_CONST_CONTEXT:
   status: noFix
-  notes: Deprecated
\ No newline at end of file
+  notes: Deprecated
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 c109bae..cfd72ad 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -1539,6 +1539,12 @@
     StaticWarningCode.UNNECESSARY_NON_NULL_ASSERTION: [
       RemoveNonNullAssertion.new,
     ],
+    StaticWarningCode.UNNECESSARY_NULL_CHECK_PATTERN: [
+      RemoveQuestionMark.new,
+    ],
+    StaticWarningCode.UNNECESSARY_NULL_ASSERT_PATTERN: [
+      RemoveNonNullAssertion.new,
+    ],
 
     WarningCode.BODY_MIGHT_COMPLETE_NORMALLY_NULLABLE: [
       AddReturnNull.new,
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_non_null_assertion_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_non_null_assertion_test.dart
index 42ce051..8b1ec4f 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_non_null_assertion_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_non_null_assertion_test.dart
@@ -49,14 +49,13 @@
 ''');
   }
 
-  @FailingTest(
-      reason: "TODO(keertip): Add support for patterns to FixProcessor")
   Future<void> test_nonNullableCasePattern() async {
     await resolveTestCode('''
 void f() {
   List<String> row = ['h', 'e', 'l'];
   switch (row) {
-    case ['user', var name!]: break;
+    case ['user', var name!]:
+    print(name);
   }
 }
 ''');
@@ -64,25 +63,28 @@
 void f() {
   List<String> row = ['h', 'e', 'l'];
   switch (row) {
-    case ['user', var name]: break;
+    case ['user', var name]:
+    print(name);
   }
 }
 ''');
   }
 
-  @FailingTest(
-      reason: "TODO(keertip): Add support for patterns to FixProcessor")
   Future<void> test_nonNullablePattern() async {
     await resolveTestCode('''
 void f() {
   (int, int?) p = (1, 2);
   var (x!, y!) = p;
+  print(x);
+  print(y);
 }
 ''');
     await assertHasFix('''
 void f() {
   (int, int?) p = (1, 2);
   var (x, y!) = p;
+  print(x);
+  print(y);
 }
 ''');
   }
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_question_mark_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_question_mark_test.dart
index f40ece4..f5cf00f 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_question_mark_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_question_mark_test.dart
@@ -47,15 +47,14 @@
   @override
   FixKind get kind => DartFixKind.REMOVE_QUESTION_MARK;
 
-  @FailingTest(
-      reason: "TODO(keertip): Add support for patterns to FixProcessor")
   Future<void> test_casePattern() async {
     await resolveTestCode('''
 void f() {
   String? maybeString = 'hello';
   switch (maybeString) {
     case
-      var s?: break;
+      var s?:
+      print(s);
   }
 }
 ''');
@@ -64,7 +63,8 @@
   String? maybeString = 'hello';
   switch (maybeString) {
     case
-      var s: break;
+      var s:
+      print(s);
   }
 }
 ''');
diff --git a/pkg/analyzer/doc/implementation/diagnostics.md b/pkg/analyzer/doc/implementation/diagnostics.md
index 42f9db5..a7bc2be 100644
--- a/pkg/analyzer/doc/implementation/diagnostics.md
+++ b/pkg/analyzer/doc/implementation/diagnostics.md
@@ -15,21 +15,29 @@
 
 For most diagnostics, a single message (code) is sufficient. But sometimes it's
 useful to tailor the message based on the context in which the problem occurs or
-because the message can be made more clear. For example, it's an error to
-declare two or more constructors with the same name. That's true whether the
-name is explicit or implicit (the default constructor). In order for the message
-to match the user's model of the language, we define two messages for this one
-problem: one that refers to the constructors by their name, and one that refers
-to the constructors as "unnamed". The way we define two messages is by defining
-two codes.
+because the message can be made more clear.
+
+For example, it's an error to declare two or more constructors with the same name.
+That's true whether the name is explicit or implicit (the default constructor).
+In order for the message to match the user's model of the language, we define
+two messages for this one problem: one that refers to the constructors by their
+name, and one that refers to the constructors as "unnamed". The way we define
+two messages is by defining two codes.
 
 Each code has a unique name (the key used in the map in `messages.yaml`) and can
 optionally define a shared name that links all the codes for a single diagnostic
 together. It is the shared name that is displayed to users. If a shared name
 isn't explicitly provided, it will default to being the same as the unique name.
 
-After every edit to the `messages.yaml` file, you will need to run the utility
-`analyzer/tool/messages/generate.dart` to update the generated files.
+### Updating generated files
+
+After every edit to the `messages.yaml` file, you will need to run the following
+to update the generated files:
+```bash
+dart run analyzer/tool/messages/generate.dart
+```
+
+### Add code to `error_fix_status.yaml`
 
 You also need to manually add the name of the code to the list of codes in the
 file `analysis_server/lib/src/services/correction/error_fix_status.yaml`. The
@@ -39,6 +47,13 @@
 ```
 nested under the name of the code.
 
+At some point, we'll evaluate the diagnostics that are marked as `needsEvaluation`.
+If we can think of a reasonable and useful fix, then we'll change it to `needsFix`
+with a comment indicating what fix we thought of. If we can't think of one, then
+we'll change it to `noFix`. The status is changed to `hasFix` when there's at least
+one fix associated with the diagnostic, which is something we can (and do) verify
+in a test.
+
 ## Write tests
 
 We recommend writing the tests for a diagnostic before writing the code to
@@ -48,6 +63,7 @@
 
 The tests for each diagnostic code (or set of codes that have the same shared
 name) are in a separate file in the directory `analyzer/test/src/diagnostics`.
+
 Looking at the implementation of tests in a few of the other files can help you
 see the basic pattern, but all the tests essentially work by setting up the code
 to be analyzed, then assert that either the expected diagnostic has been
@@ -58,11 +74,14 @@
 ## Report the diagnostic
 
 The last step is to write the code to report the diagnostic. Where that code
-lives depends on the kind of diagnostic you're adding. If you're adding a
-diagnostic that's defined by the language specification (with a severity of
-'error'), then the best place to implement it will usually be in one of the
-`<node class>Resolver` classes. If you're adding a warning, then the class
-`BestPracticesVerifier` is usually the best place for it.
+lives depends on the kind of diagnostic you're adding.
+
+If you're adding a diagnostic that's defined by the language specification
+(with a severity of 'error'), then the best place to implement it will usually
+be in one of the `<node class>Resolver` classes.
+
+If you're adding a warning, then the class `BestPracticesVerifier` is usually
+the best place for it.
 
 ## Document the diagnostic
 
diff --git a/pkg/analyzer/doc/tutorial/tutorial.md b/pkg/analyzer/doc/tutorial/tutorial.md
index 39cdac3..787fe0f 100644
--- a/pkg/analyzer/doc/tutorial/tutorial.md
+++ b/pkg/analyzer/doc/tutorial/tutorial.md
@@ -10,19 +10,19 @@
 [Introduction][introduction] -
 What capabilities does the analyzer package support?
 
-[Performing Analysis][analysis]
+[Performing Analysis][analysis] -
 How to set up the objects used to analyze code.
 
-[The Token Model][tokens]
+[The Token Model][tokens] -
 How are tokens represented?
 
-[The AST][ast]
+[The AST][ast] -
 What is an AST?
 
-[The Element Model][element]
+[The Element Model][element] -
 What is the element model?
 
-[The Type Model][type]
+[The Type Model][type] -
 What is the type model?
 
 [analysis]: analysis.md
diff --git a/pkg/analyzer/lib/src/dart/error/hint_codes.g.dart b/pkg/analyzer/lib/src/dart/error/hint_codes.g.dart
index 304b416..cb928d1 100644
--- a/pkg/analyzer/lib/src/dart/error/hint_codes.g.dart
+++ b/pkg/analyzer/lib/src/dart/error/hint_codes.g.dart
@@ -173,22 +173,6 @@
     hasPublishedDocs: true,
   );
 
-  ///  No parameters.
-  static const HintCode EQUAL_ELEMENTS_IN_SET = HintCode(
-    'EQUAL_ELEMENTS_IN_SET',
-    "Two elements in a set literal shouldn't be equal.",
-    correctionMessage: "Change or remove the duplicate element.",
-    hasPublishedDocs: true,
-  );
-
-  ///  No parameters.
-  static const HintCode EQUAL_KEYS_IN_MAP = HintCode(
-    'EQUAL_KEYS_IN_MAP',
-    "Two keys in a map literal shouldn't be equal.",
-    correctionMessage: "Change or remove the duplicate key.",
-    hasPublishedDocs: true,
-  );
-
   ///  It is a bad practice for a source file in a package "lib" directory
   ///  hierarchy to traverse outside that directory hierarchy. For example, a
   ///  source file in the "lib" directory should not contain a directive such as
@@ -1121,16 +1105,16 @@
   ///  No parameters.
   static const HintCode UNNECESSARY_NAN_COMPARISON_FALSE = HintCode(
     'UNNECESSARY_NAN_COMPARISON',
-    "A double can't equal NaN, so the condition is always 'false'.",
-    correctionMessage: "Try using 'isNan', or removing the condition.",
+    "A double can't equal 'double.nan', so the condition is always 'false'.",
+    correctionMessage: "Try using 'double.isNan', or removing the condition.",
     uniqueName: 'UNNECESSARY_NAN_COMPARISON_FALSE',
   );
 
   ///  No parameters.
   static const HintCode UNNECESSARY_NAN_COMPARISON_TRUE = HintCode(
     'UNNECESSARY_NAN_COMPARISON',
-    "A double can't equal NaN, so the condition is always 'true'.",
-    correctionMessage: "Try using 'isNan', or removing the condition.",
+    "A double can't equal 'double.nan', so the condition is always 'true'.",
+    correctionMessage: "Try using 'double.isNan', or removing the condition.",
     uniqueName: 'UNNECESSARY_NAN_COMPARISON_TRUE',
   );
 
diff --git a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
index 6e0c57d..5774424 100644
--- a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
@@ -161,8 +161,8 @@
         var declaredElement = parameter.declaredElement!;
         // TODO(paulberry): `skipDuplicateCheck` is currently needed to work
         // around a failure in duplicate_definition_test.dart; fix this.
-        flow!.declare(declaredElement, true, declaredElement.type,
-            skipDuplicateCheck: true);
+        flow!.declare(declaredElement, declaredElement.type,
+            initialized: true, skipDuplicateCheck: true);
       }
     }
   }
@@ -288,8 +288,8 @@
       for (var i = 0; i < variables.length; ++i) {
         var variable = variables[i];
         var declaredElement = variable.declaredElement as PromotableElement;
-        flow!.declare(declaredElement, variable.initializer != null,
-            declaredElement.type);
+        flow!.declare(declaredElement, declaredElement.type,
+            initialized: variable.initializer != null);
       }
     }
   }
diff --git a/pkg/analyzer/lib/src/dart/resolver/for_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/for_resolver.dart
index dbd691c..c767c4d 100644
--- a/pkg/analyzer/lib/src/dart/resolver/for_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/for_resolver.dart
@@ -169,7 +169,7 @@
     if (loopVariable != null) {
       var declaredElement = loopVariable.declaredElement!;
       _resolver.flowAnalysis.flow
-          ?.declare(declaredElement, true, declaredElement.type);
+          ?.declare(declaredElement, declaredElement.type, initialized: true);
     }
 
     _resolver.flowAnalysis.flow?.forEach_bodyBegin(node);
diff --git a/pkg/analyzer/lib/src/error/best_practices_verifier.dart b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
index b9c4165..7e468b3 100644
--- a/pkg/analyzer/lib/src/error/best_practices_verifier.dart
+++ b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
@@ -990,8 +990,8 @@
         var value = constEvaluation.value;
         if (value != null && !alreadySeen.add(value)) {
           var errorCode = node.isSet
-              ? HintCode.EQUAL_ELEMENTS_IN_SET
-              : HintCode.EQUAL_KEYS_IN_MAP;
+              ? WarningCode.EQUAL_ELEMENTS_IN_SET
+              : WarningCode.EQUAL_KEYS_IN_MAP;
           _errorReporter.reportErrorForNode(errorCode, expression);
         }
       }
diff --git a/pkg/analyzer/lib/src/error/codes.g.dart b/pkg/analyzer/lib/src/error/codes.g.dart
index 229bd94..fff53af 100644
--- a/pkg/analyzer/lib/src/error/codes.g.dart
+++ b/pkg/analyzer/lib/src/error/codes.g.dart
@@ -5693,6 +5693,22 @@
     hasPublishedDocs: true,
   );
 
+  ///  No parameters.
+  static const WarningCode EQUAL_ELEMENTS_IN_SET = WarningCode(
+    'EQUAL_ELEMENTS_IN_SET',
+    "Two elements in a set literal shouldn't be equal.",
+    correctionMessage: "Change or remove the duplicate element.",
+    hasPublishedDocs: true,
+  );
+
+  ///  No parameters.
+  static const WarningCode EQUAL_KEYS_IN_MAP = WarningCode(
+    'EQUAL_KEYS_IN_MAP',
+    "Two keys in a map literal shouldn't be equal.",
+    correctionMessage: "Change or remove the duplicate key.",
+    hasPublishedDocs: true,
+  );
+
   ///  Parameters:
   ///  0: the name of the class
   static const WarningCode SDK_VERSION_ASYNC_EXPORTED_FROM_CORE = WarningCode(
diff --git a/pkg/analyzer/lib/src/error/error_code_values.g.dart b/pkg/analyzer/lib/src/error/error_code_values.g.dart
index 3fd2206..1368609 100644
--- a/pkg/analyzer/lib/src/error/error_code_values.g.dart
+++ b/pkg/analyzer/lib/src/error/error_code_values.g.dart
@@ -579,8 +579,6 @@
   HintCode.DEPRECATED_MEMBER_USE_FROM_SAME_PACKAGE_WITH_MESSAGE,
   HintCode.DEPRECATED_MEMBER_USE_WITH_MESSAGE,
   HintCode.DIVISION_OPTIMIZATION,
-  HintCode.EQUAL_ELEMENTS_IN_SET,
-  HintCode.EQUAL_KEYS_IN_MAP,
   HintCode.FILE_IMPORT_INSIDE_LIB_REFERENCES_FILE_OUTSIDE,
   HintCode.FILE_IMPORT_OUTSIDE_LIB_REFERENCES_FILE_INSIDE,
   HintCode.IMPORT_DEFERRED_LIBRARY_WITH_LOAD_FUNCTION,
@@ -972,6 +970,8 @@
   WarningCode.DUPLICATE_IGNORE,
   WarningCode.DUPLICATE_IMPORT,
   WarningCode.DUPLICATE_SHOWN_NAME,
+  WarningCode.EQUAL_ELEMENTS_IN_SET,
+  WarningCode.EQUAL_KEYS_IN_MAP,
   WarningCode.SDK_VERSION_ASYNC_EXPORTED_FROM_CORE,
   WarningCode.SDK_VERSION_AS_EXPRESSION_IN_CONST_CONTEXT,
   WarningCode.SDK_VERSION_BOOL_OPERATOR_IN_CONST_CONTEXT,
diff --git a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
index edbed2e..fef4f8a 100644
--- a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
+++ b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
@@ -333,6 +333,7 @@
   static const double minPositive = 5e-324;
   static const double maxFinite = 1.7976931348623157e+308;
 
+  bool get isNaN;
   double get sign;
   double operator %(num other);
   double operator *(num other);
@@ -363,6 +364,17 @@
   int compareTo(Duration other) => 0;
 }
 
+abstract class Enum {
+  int get index; // Enum
+  String get _name;
+}
+
+abstract class _Enum implements Enum {
+  final int index;
+  final String _name;
+  const _Enum(this.index, this._name);
+}
+
 class Error {
   Error();
   static String safeToString(Object? object) => '';
@@ -567,17 +579,6 @@
   static int hashAllUnordered(Iterable<Object?> objects) => 0;
 }
 
-abstract class Enum {
-  int get index; // Enum
-  String get _name;
-}
-
-abstract class _Enum implements Enum {
-  final int index;
-  final String _name;
-  const _Enum(this.index, this._name);
-}
-
 abstract class Pattern {
   Iterable<Match> allMatches(String string, [int start = 0]);
 }
@@ -610,6 +611,10 @@
       throw '';
 }
 
+abstract class Sink {
+  void close();
+}
+
 class StackTrace {}
 
 abstract class String implements Comparable<String>, Pattern {
@@ -1231,6 +1236,10 @@
     Encoding? stderrEncoding,
   });
 }
+
+abstract class Socket {
+  void destroy() {}
+}
 ''',
     )
   ],
diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml
index 4f53728..1c14b18 100644
--- a/pkg/analyzer/messages.yaml
+++ b/pkg/analyzer/messages.yaml
@@ -18116,97 +18116,6 @@
       ```dart
       int divide(num x, num y) => x ~/ y;
       ```
-  EQUAL_ELEMENTS_IN_SET:
-    problemMessage: "Two elements in a set literal shouldn't be equal."
-    correctionMessage: Change or remove the duplicate element.
-    hasPublishedDocs: true
-    comment: No parameters.
-    documentation: |-
-      #### Description
-
-      The analyzer produces this diagnostic when an element in a non-constant set
-      is the same as a previous element in the same set. If two elements are the
-      same, then the second value is ignored, which makes having both elements
-      pointless and likely signals a bug.
-
-      #### Example
-
-      The following code produces this diagnostic because the element `1` appears
-      twice:
-
-      ```dart
-      const a = 1;
-      const b = 1;
-      var s = <int>{a, [!b!]};
-      ```
-
-      #### Common fixes
-
-      If both elements should be included in the set, then change one of the
-      elements:
-
-      ```dart
-      const a = 1;
-      const b = 2;
-      var s = <int>{a, b};
-      ```
-
-      If only one of the elements is needed, then remove the one that isn't
-      needed:
-
-      ```dart
-      const a = 1;
-      var s = <int>{a};
-      ```
-
-      Note that literal sets preserve the order of their elements, so the choice
-      of which element to remove might affect the order in which elements are
-      returned by an iterator.
-  EQUAL_KEYS_IN_MAP:
-    problemMessage: "Two keys in a map literal shouldn't be equal."
-    correctionMessage: Change or remove the duplicate key.
-    hasPublishedDocs: true
-    comment: No parameters.
-    documentation: |-
-      #### Description
-
-      The analyzer produces this diagnostic when a key in a non-constant map is
-      the same as a previous key in the same map. If two keys are the same, then
-      the second value overwrites the first value, which makes having both pairs
-      pointless and likely signals a bug.
-
-      #### Example
-
-      The following code produces this diagnostic because the keys `a` and `b`
-      have the same value:
-
-      ```dart
-      const a = 1;
-      const b = 1;
-      var m = <int, String>{a: 'a', [!b!]: 'b'};
-      ```
-
-      #### Common fixes
-
-      If both entries should be included in the map, then change one of the keys:
-
-      ```dart
-      const a = 1;
-      const b = 2;
-      var m = <int, String>{a: 'a', b: 'b'};
-      ```
-
-      If only one of the entries is needed, then remove the one that isn't
-      needed:
-
-      ```dart
-      const a = 1;
-      var m = <int, String>{a: 'a'};
-      ```
-
-      Note that literal maps preserve the order of their entries, so the choice
-      of which entry to remove might affect the order in which the keys and
-      values are returned by an iterator.
   FILE_IMPORT_INSIDE_LIB_REFERENCES_FILE_OUTSIDE:
     problemMessage: "A file in the 'lib' directory shouldn't import a file outside the 'lib' directory."
     correctionMessage: "Try removing the import, or moving the imported file inside the 'lib' directory."
@@ -20576,14 +20485,45 @@
       the missing references to those names.
   UNNECESSARY_NAN_COMPARISON_FALSE:
     sharedName: UNNECESSARY_NAN_COMPARISON
-    problemMessage: "A double can't equal NaN, so the condition is always 'false'."
-    correctionMessage: Try using 'isNan', or removing the condition.
+    problemMessage: A double can't equal 'double.nan', so the condition is always 'false'.
+    correctionMessage: Try using 'double.isNan', or removing the condition.
     hasPublishedDocs: false
     comment: No parameters.
+    documentation: |-
+      #### Description
+
+      The analyzer produces this diagnostic when a value is compared to
+      `double.nan` using either `==` or `!=`.
+
+      Dart follows the [IEEE 754] floating-point standard for the semantics of
+      floating point operations, which states that, for any floating point value
+      `x` (including NaN, positive infinity, and negative infinity),
+      - `NaN == x` is always false
+      - `NaN != x` is always true
+
+      As a result, comparing any value to NaN is pointless because the result is
+      already known (based on the comparison operator being used).
+
+      #### Example
+
+      The following code produces this diagnostic because `d` is being compared
+      to `double.nan`:
+
+      ```dart
+      bool isNaN(double d) => d [!== double.nan!];
+      ```
+
+      #### Common fixes
+
+      Use the getter `double.isNaN` instead:
+
+      ```dart
+      bool isNaN(double d) => d.isNaN;
+      ```
   UNNECESSARY_NAN_COMPARISON_TRUE:
     sharedName: UNNECESSARY_NAN_COMPARISON
-    problemMessage: "A double can't equal NaN, so the condition is always 'true'."
-    correctionMessage: Try using 'isNan', or removing the condition.
+    problemMessage: A double can't equal 'double.nan', so the condition is always 'true'.
+    correctionMessage: Try using 'double.isNan', or removing the condition.
     hasPublishedDocs: false
     comment: No parameters.
   UNNECESSARY_NO_SUCH_METHOD:
@@ -22838,6 +22778,97 @@
 
       var x = min(2, min(0, 1));
       ```
+  EQUAL_ELEMENTS_IN_SET:
+    problemMessage: "Two elements in a set literal shouldn't be equal."
+    correctionMessage: Change or remove the duplicate element.
+    hasPublishedDocs: true
+    comment: No parameters.
+    documentation: |-
+      #### Description
+
+      The analyzer produces this diagnostic when an element in a non-constant set
+      is the same as a previous element in the same set. If two elements are the
+      same, then the second value is ignored, which makes having both elements
+      pointless and likely signals a bug.
+
+      #### Example
+
+      The following code produces this diagnostic because the element `1` appears
+      twice:
+
+      ```dart
+      const a = 1;
+      const b = 1;
+      var s = <int>{a, [!b!]};
+      ```
+
+      #### Common fixes
+
+      If both elements should be included in the set, then change one of the
+      elements:
+
+      ```dart
+      const a = 1;
+      const b = 2;
+      var s = <int>{a, b};
+      ```
+
+      If only one of the elements is needed, then remove the one that isn't
+      needed:
+
+      ```dart
+      const a = 1;
+      var s = <int>{a};
+      ```
+
+      Note that literal sets preserve the order of their elements, so the choice
+      of which element to remove might affect the order in which elements are
+      returned by an iterator.
+  EQUAL_KEYS_IN_MAP:
+    problemMessage: "Two keys in a map literal shouldn't be equal."
+    correctionMessage: Change or remove the duplicate key.
+    hasPublishedDocs: true
+    comment: No parameters.
+    documentation: |-
+      #### Description
+
+      The analyzer produces this diagnostic when a key in a non-constant map is
+      the same as a previous key in the same map. If two keys are the same, then
+      the second value overwrites the first value, which makes having both pairs
+      pointless and likely signals a bug.
+
+      #### Example
+
+      The following code produces this diagnostic because the keys `a` and `b`
+      have the same value:
+
+      ```dart
+      const a = 1;
+      const b = 1;
+      var m = <int, String>{a: 'a', [!b!]: 'b'};
+      ```
+
+      #### Common fixes
+
+      If both entries should be included in the map, then change one of the keys:
+
+      ```dart
+      const a = 1;
+      const b = 2;
+      var m = <int, String>{a: 'a', b: 'b'};
+      ```
+
+      If only one of the entries is needed, then remove the one that isn't
+      needed:
+
+      ```dart
+      const a = 1;
+      var m = <int, String>{a: 'a'};
+      ```
+
+      Note that literal maps preserve the order of their entries, so the choice
+      of which entry to remove might affect the order in which the keys and
+      values are returned by an iterator.
   SDK_VERSION_ASYNC_EXPORTED_FROM_CORE:
     problemMessage: "The class '{0}' wasn't exported from 'dart:core' until version 2.1, but this code is required to be able to run on earlier versions."
     correctionMessage: "Try either importing 'dart:async' or updating the SDK constraints."
diff --git a/pkg/analyzer/test/src/diagnostics/equal_elements_in_const_set_test.dart b/pkg/analyzer/test/src/diagnostics/equal_elements_in_const_set_test.dart
index 909d3c1..08e9322 100644
--- a/pkg/analyzer/test/src/diagnostics/equal_elements_in_const_set_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/equal_elements_in_const_set_test.dart
@@ -143,7 +143,7 @@
     await assertErrorsInCode('''
 var c = {1, 2, 1};
 ''', [
-      error(HintCode.EQUAL_ELEMENTS_IN_SET, 15, 1),
+      error(WarningCode.EQUAL_ELEMENTS_IN_SET, 15, 1),
     ]);
   }
 }
diff --git a/pkg/analyzer/test/src/diagnostics/equal_elements_in_set_test.dart b/pkg/analyzer/test/src/diagnostics/equal_elements_in_set_test.dart
index cf24d77..d49d76d 100644
--- a/pkg/analyzer/test/src/diagnostics/equal_elements_in_set_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/equal_elements_in_set_test.dart
@@ -2,7 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import 'package:analyzer/src/dart/error/hint_codes.dart';
+import 'package:analyzer/src/error/codes.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
 import '../dart/resolution/context_collection_resolution.dart';
@@ -21,7 +21,7 @@
 const b = 1;
 var s = {a, b};
 ''', [
-      error(HintCode.EQUAL_ELEMENTS_IN_SET, 38, 1),
+      error(WarningCode.EQUAL_ELEMENTS_IN_SET, 38, 1),
     ]);
   }
 
@@ -30,7 +30,7 @@
 const one = 1;
 var s = {1, one};
 ''', [
-      error(HintCode.EQUAL_ELEMENTS_IN_SET, 27, 3),
+      error(WarningCode.EQUAL_ELEMENTS_IN_SET, 27, 3),
     ]);
   }
 
@@ -38,7 +38,7 @@
     await assertErrorsInCode('''
 var s = {1, 1};
 ''', [
-      error(HintCode.EQUAL_ELEMENTS_IN_SET, 12, 1),
+      error(WarningCode.EQUAL_ELEMENTS_IN_SET, 12, 1),
     ]);
   }
 }
diff --git a/pkg/analyzer/test/src/diagnostics/equal_keys_in_const_map_test.dart b/pkg/analyzer/test/src/diagnostics/equal_keys_in_const_map_test.dart
index 56a7275..5194573 100644
--- a/pkg/analyzer/test/src/diagnostics/equal_keys_in_const_map_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/equal_keys_in_const_map_test.dart
@@ -143,7 +143,7 @@
     await assertErrorsInCode('''
 var c = {1: null, 2: null, 1: null};
 ''', [
-      error(HintCode.EQUAL_KEYS_IN_MAP, 27, 1),
+      error(WarningCode.EQUAL_KEYS_IN_MAP, 27, 1),
     ]);
   }
 }
diff --git a/pkg/analyzer/test/src/diagnostics/equal_keys_in_map_test.dart b/pkg/analyzer/test/src/diagnostics/equal_keys_in_map_test.dart
index 1763922..47df0dd 100644
--- a/pkg/analyzer/test/src/diagnostics/equal_keys_in_map_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/equal_keys_in_map_test.dart
@@ -2,7 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import 'package:analyzer/src/dart/error/hint_codes.dart';
+import 'package:analyzer/src/error/codes.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
 import '../dart/resolution/context_collection_resolution.dart';
@@ -21,7 +21,7 @@
 const b = 1;
 var s = {a: 2, b: 3};
 ''', [
-      error(HintCode.EQUAL_KEYS_IN_MAP, 41, 1),
+      error(WarningCode.EQUAL_KEYS_IN_MAP, 41, 1),
     ]);
   }
 
@@ -30,7 +30,7 @@
 const one = 1;
 var s = {1: 2, one: 3};
 ''', [
-      error(HintCode.EQUAL_KEYS_IN_MAP, 30, 3),
+      error(WarningCode.EQUAL_KEYS_IN_MAP, 30, 3),
     ]);
   }
 
@@ -38,7 +38,7 @@
     await assertErrorsInCode('''
 var s = {1: 2, 1: 3};
 ''', [
-      error(HintCode.EQUAL_KEYS_IN_MAP, 15, 1),
+      error(WarningCode.EQUAL_KEYS_IN_MAP, 15, 1),
     ]);
   }
 }
diff --git a/pkg/analyzer/tool/diagnostics/diagnostics.md b/pkg/analyzer/tool/diagnostics/diagnostics.md
index 7b5b2db..4a7ea1f 100644
--- a/pkg/analyzer/tool/diagnostics/diagnostics.md
+++ b/pkg/analyzer/tool/diagnostics/diagnostics.md
@@ -284,6 +284,7 @@
 that might work in unexpected ways.
 
 [ffi]: https://dart.dev/guides/libraries/c-interop
+[IEEE 754]: https://en.wikipedia.org/wiki/IEEE_754
 [meta-doNotStore]: https://pub.dev/documentation/meta/latest/meta/doNotStore-constant.html
 [meta-factory]: https://pub.dev/documentation/meta/latest/meta/factory-constant.html
 [meta-immutable]: https://pub.dev/documentation/meta/latest/meta/immutable-constant.html
@@ -19357,6 +19358,43 @@
 aren't yet, and if those names aren't imported by other imports, then add
 the missing references to those names.
 
+### unnecessary_nan_comparison
+
+_A double can't equal 'double.nan', so the condition is always 'false'._
+
+_A double can't equal 'double.nan', so the condition is always 'true'._
+
+#### Description
+
+The analyzer produces this diagnostic when a value is compared to
+`double.nan` using either `==` or `!=`.
+
+Dart follows the [IEEE 754] floating-point standard for the semantics of
+floating point operations, which states that, for any floating point value
+`x` (including NaN, positive infinity, and negative infinity),
+- `NaN == x` is always false
+- `NaN != x` is always true
+
+As a result, comparing any value to NaN is pointless because the result is
+already known (based on the comparison operator being used).
+
+#### Example
+
+The following code produces this diagnostic because `d` is being compared
+to `double.nan`:
+
+{% prettify dart tag=pre+code %}
+bool isNaN(double d) => d [!== double.nan!];
+{% endprettify %}
+
+#### Common fixes
+
+Use the getter `double.isNaN` instead:
+
+{% prettify dart tag=pre+code %}
+bool isNaN(double d) => d.isNaN;
+{% endprettify %}
+
 ### unnecessary_non_null_assertion
 
 _The '!' will have no effect because the receiver can't be null._
diff --git a/pkg/analyzer/tool/diagnostics/generate.dart b/pkg/analyzer/tool/diagnostics/generate.dart
index 7c29e74..5f294ef 100644
--- a/pkg/analyzer/tool/diagnostics/generate.dart
+++ b/pkg/analyzer/tool/diagnostics/generate.dart
@@ -192,6 +192,7 @@
 that might work in unexpected ways.
 
 [ffi]: https://dart.dev/guides/libraries/c-interop
+[IEEE 754]: https://en.wikipedia.org/wiki/IEEE_754
 [meta-doNotStore]: https://pub.dev/documentation/meta/latest/meta/doNotStore-constant.html
 [meta-factory]: https://pub.dev/documentation/meta/latest/meta/factory-constant.html
 [meta-immutable]: https://pub.dev/documentation/meta/latest/meta/immutable-constant.html
diff --git a/pkg/dart2wasm/lib/js_runtime_generator.dart b/pkg/dart2wasm/lib/js_runtime_generator.dart
index 0c36340..de32e68 100644
--- a/pkg/dart2wasm/lib/js_runtime_generator.dart
+++ b/pkg/dart2wasm/lib/js_runtime_generator.dart
@@ -47,6 +47,7 @@
     String callArguments;
     String functionParameters;
     String object;
+    bool argumentsNeedParens = parameters.isEmpty || parameters.length > 1;
     if (isConstructor) {
       object = '';
       callArguments = parameters.join(',');
@@ -70,7 +71,7 @@
         for (int i = 0; i < parameters.length; i++) {
           keyValuePairs.add('${keys[i]}: ${parameters[i]}');
         }
-        bodyString = '{${keyValuePairs.join(',')}}';
+        bodyString = '({${keyValuePairs.join(',')}})';
         break;
       case _MethodType.constructor:
         bodyString = 'new $jsString($callArguments)';
@@ -85,9 +86,11 @@
         bodyString = '$object.$jsString = $callArguments';
         break;
     }
-    return """function($functionParameters) {
-      return $bodyString;
-    }""";
+    if (argumentsNeedParens) {
+      return '($functionParameters) => $bodyString';
+    } else {
+      return '$functionParameters => $bodyString';
+    }
   }
 }
 
@@ -446,7 +449,7 @@
     }
 
     // Create Dart procedure stub for JS method.
-    final jsMethodName = '${config.tag}${_jsTrampolineN++}';
+    final jsMethodName = '_${_jsTrampolineN++}';
     final dartProcedureName = '|$jsMethodName';
     final dartProcedure = Procedure(
         Name(dartProcedureName, _library),
diff --git a/pkg/front_end/lib/src/fasta/builder/class_builder.dart b/pkg/front_end/lib/src/fasta/builder/class_builder.dart
index 68a0f12..4c94bc8 100644
--- a/pkg/front_end/lib/src/fasta/builder/class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/class_builder.dart
@@ -93,6 +93,13 @@
 
   bool get isSealed;
 
+  bool get isBase;
+
+  bool get isInterface;
+
+  @override
+  bool get isFinal;
+
   bool get isAugmentation;
 
   bool get declaresConstConstructor;
diff --git a/pkg/front_end/lib/src/fasta/dill/dill_class_builder.dart b/pkg/front_end/lib/src/fasta/dill/dill_class_builder.dart
index 8fa0735..488c494 100644
--- a/pkg/front_end/lib/src/fasta/dill/dill_class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/dill/dill_class_builder.dart
@@ -67,6 +67,15 @@
   bool get isSealed => cls.isSealed;
 
   @override
+  bool get isBase => cls.isBase;
+
+  @override
+  bool get isInterface => cls.isInterface;
+
+  @override
+  bool get isFinal => cls.isFinal;
+
+  @override
   bool get isAugmentation => false;
 
   @override
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index bc5611c..4a394cf 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -1260,13 +1260,14 @@
     final SourceFunctionBuilder builder = member as SourceFunctionBuilder;
     if (thisVariable != null) {
       typeInferrer.flowAnalysis
-          .declare(thisVariable!, true, thisVariable!.type);
+          .declare(thisVariable!, thisVariable!.type, initialized: true);
     }
     if (formals?.parameters != null) {
       for (int i = 0; i < formals!.parameters!.length; i++) {
         FormalParameterBuilder parameter = formals.parameters![i];
         VariableDeclaration variable = parameter.variable!;
-        typeInferrer.flowAnalysis.declare(variable, true, variable.type);
+        typeInferrer.flowAnalysis
+            .declare(variable, variable.type, initialized: true);
       }
       for (int i = 0; i < formals.parameters!.length; i++) {
         FormalParameterBuilder parameter = formals.parameters![i];
@@ -1814,7 +1815,8 @@
     if (formals != null) {
       for (int i = 0; i < formals.length; i++) {
         VariableDeclaration variable = formals[i].variable!;
-        typeInferrer.flowAnalysis.declare(variable, true, variable.type);
+        typeInferrer.flowAnalysis
+            .declare(variable, variable.type, initialized: true);
       }
     }
     InferredFunctionBody inferredFunctionBody = typeInferrer.inferFunctionBody(
@@ -1948,8 +1950,8 @@
         // around a failure in
         // co19/Language/Expressions/Postfix_Expressions/conditional_increment_t02;
         // fix this.
-        typeInferrer.flowAnalysis
-            .declare(variable, true, variable.type, skipDuplicateCheck: true);
+        typeInferrer.flowAnalysis.declare(variable, variable.type,
+            initialized: true, skipDuplicateCheck: true);
       }
     }
 
diff --git a/pkg/front_end/lib/src/fasta/source/outline_builder.dart b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
index 81e9173..500906f 100644
--- a/pkg/front_end/lib/src/fasta/source/outline_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
@@ -1297,12 +1297,8 @@
     TypeBuilder? supertype = nullIfParserRecovery(pop()) as TypeBuilder?;
     Token? mixinToken = pop(NullValues.Token) as Token?;
     Token? augmentToken = pop(NullValues.Token) as Token?;
-    // TODO(kallentu): AST work for class modifiers.
-    // ignore: unused_local_variable
     Token? finalToken = pop(NullValues.Token) as Token?;
-    // ignore: unused_local_variable
     Token? interfaceToken = pop(NullValues.Token) as Token?;
-    // ignore: unused_local_variable
     Token? baseToken = pop(NullValues.Token) as Token?;
     Token? sealedToken = pop(NullValues.Token) as Token?;
     // TODO(johnniwinther): Create builder for inline.
@@ -1403,6 +1399,9 @@
             supertypeOffset,
             isMacro: macroToken != null,
             isSealed: sealedToken != null,
+            isBase: baseToken != null,
+            isInterface: interfaceToken != null,
+            isFinal: finalToken != null,
             isAugmentation: augmentToken != null,
             isMixinClass: mixinToken != null);
       }
@@ -1441,12 +1440,8 @@
         nullIfParserRecovery(pop()) as List<TypeBuilder>?;
     List<TypeVariableBuilder>? typeVariables =
         pop(NullValues.TypeVariables) as List<TypeVariableBuilder>?;
-    // TODO(kallentu): Add AST support for mixin modifiers
-    // ignore: unused_local_variable
     Token? finalToken = pop(NullValues.Token) as Token?;
-    // ignore: unused_local_variable
     Token? interfaceToken = pop(NullValues.Token) as Token?;
-    // ignore: unused_local_variable
     Token? baseToken = pop(NullValues.Token) as Token?;
     Token? sealedToken = pop(NullValues.Token) as Token?;
     Token? augmentToken = pop(NullValues.Token) as Token?;
@@ -1505,6 +1500,9 @@
           endToken.charOffset,
           -1,
           isSealed: sealedToken != null,
+          isBase: baseToken != null,
+          isInterface: interfaceToken != null,
+          isFinal: finalToken != null,
           isAugmentation: augmentToken != null);
     }
     libraryBuilder.setCurrentClassName(null);
@@ -2293,12 +2291,8 @@
     Object? supertype = pop();
     Token? mixinToken = pop(NullValues.Token) as Token?;
     Token? augmentToken = pop(NullValues.Token) as Token?;
-    // TODO(kallentu): AST work for class modifiers.
-    // ignore: unused_local_variable
     Token? finalToken = pop(NullValues.Token) as Token?;
-    // ignore: unused_local_variable
     Token? interfaceToken = pop(NullValues.Token) as Token?;
-    // ignore: unused_local_variable
     Token? baseToken = pop(NullValues.Token) as Token?;
     Token? sealedToken = pop(NullValues.Token) as Token?;
     // TODO(johnniwinther): Report error on 'inline' here; it can't be used on
@@ -2381,6 +2375,9 @@
           charEndOffset,
           isMacro: macroToken != null,
           isSealed: sealedToken != null,
+          isBase: baseToken != null,
+          isInterface: interfaceToken != null,
+          isFinal: finalToken != null,
           isAugmentation: augmentToken != null,
           isMixinClass: mixinToken != null);
     }
diff --git a/pkg/front_end/lib/src/fasta/source/source_class_builder.dart b/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
index 8d5f761..f21d258 100644
--- a/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
@@ -111,6 +111,15 @@
   final bool isSealed;
 
   @override
+  final bool isBase;
+
+  @override
+  final bool isInterface;
+
+  @override
+  final bool isFinal;
+
+  @override
   final bool isAugmentation;
 
   @override
@@ -158,6 +167,9 @@
       this.isMixinDeclaration = false,
       this.isMacro = false,
       this.isSealed = false,
+      this.isBase = false,
+      this.isInterface = false,
+      this.isFinal = false,
       this.isAugmentation = false,
       this.isMixinClass = false})
       : actualCls = initializeClass(cls, typeVariables, name, parent,
@@ -278,6 +290,9 @@
     cls.isMacro = isMacro;
     cls.isMixinClass = isMixinClass;
     cls.isSealed = isSealed;
+    cls.isBase = isBase;
+    cls.isInterface = isInterface;
+    cls.isFinal = isFinal;
     if (interfaceBuilders != null) {
       for (int i = 0; i < interfaceBuilders!.length; ++i) {
         interfaceBuilders![i] = _checkSupertype(interfaceBuilders![i]);
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index 88d73dff..fe81b23 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -1795,6 +1795,9 @@
       int supertypeOffset,
       {required bool isMacro,
       required bool isSealed,
+      required bool isBase,
+      required bool isInterface,
+      required bool isFinal,
       required bool isAugmentation,
       required bool isMixinClass}) {
     _addClass(
@@ -1812,6 +1815,9 @@
         supertypeOffset,
         isMacro: isMacro,
         isSealed: isSealed,
+        isBase: isBase,
+        isInterface: isInterface,
+        isFinal: isFinal,
         isAugmentation: isAugmentation,
         isMixinClass: isMixinClass);
   }
@@ -1828,6 +1834,9 @@
       int endOffset,
       int supertypeOffset,
       {required bool isSealed,
+      required bool isBase,
+      required bool isInterface,
+      required bool isFinal,
       required bool isAugmentation}) {
     TypeBuilder? supertype;
     MixinApplicationBuilder? mixinApplication;
@@ -1855,6 +1864,9 @@
         supertypeOffset,
         isMacro: false,
         isSealed: isSealed,
+        isBase: isBase,
+        isInterface: isInterface,
+        isFinal: isFinal,
         isAugmentation: isAugmentation,
         isMixinClass: false);
   }
@@ -1874,6 +1886,9 @@
       int supertypeOffset,
       {required bool isMacro,
       required bool isSealed,
+      required bool isBase,
+      required bool isInterface,
+      required bool isFinal,
       required bool isAugmentation,
       required bool isMixinClass}) {
     // Nested declaration began in `OutlineBuilder.beginClassDeclaration`.
@@ -1914,6 +1929,9 @@
             typeVariables: typeVariables,
             isMacro: false,
             isSealed: false,
+            isBase: false,
+            isInterface: false,
+            isFinal: false,
             // TODO(johnniwinther): How can we support class with mixins?
             isAugmentation: false,
             isMixinClass: false),
@@ -1932,6 +1950,9 @@
         isMixinDeclaration: isMixinDeclaration,
         isMacro: isMacro,
         isSealed: isSealed,
+        isBase: isBase,
+        isInterface: isInterface,
+        isFinal: isFinal,
         isAugmentation: isAugmentation,
         isMixinClass: isMixinClass);
 
@@ -2286,6 +2307,9 @@
       List<TypeBuilder>? interfaces,
       required bool isMacro,
       required bool isSealed,
+      required bool isBase,
+      required bool isInterface,
+      required bool isFinal,
       required bool isAugmentation,
       required bool isMixinClass}) {
     if (name == null) {
@@ -2523,6 +2547,9 @@
             mixedInTypeBuilder: isMixinDeclaration ? null : mixin,
             isMacro: isNamedMixinApplication && isMacro,
             isSealed: isNamedMixinApplication && isSealed,
+            isBase: isNamedMixinApplication && isBase,
+            isInterface: isNamedMixinApplication && isInterface,
+            isFinal: isNamedMixinApplication && isFinal,
             isAugmentation: isNamedMixinApplication && isAugmentation,
             isMixinClass: isNamedMixinApplication && isMixinClass);
         // TODO(ahe, kmillikin): Should always be true?
@@ -2584,6 +2611,9 @@
       int charEndOffset,
       {required bool isMacro,
       required bool isSealed,
+      required bool isBase,
+      required bool isInterface,
+      required bool isFinal,
       required bool isAugmentation,
       required bool isMixinClass}) {
     // Nested declaration began in `OutlineBuilder.beginNamedMixinApplication`.
@@ -2598,6 +2628,9 @@
         interfaces: interfaces,
         isMacro: isMacro,
         isSealed: isSealed,
+        isBase: isBase,
+        isInterface: isInterface,
+        isFinal: isFinal,
         isAugmentation: isAugmentation,
         isMixinClass: isMixinClass)!;
     checkTypeVariables(typeVariables, supertype.declaration);
@@ -3100,6 +3133,9 @@
             typeVariables: typeVariables,
             isMacro: false,
             isSealed: false,
+            isBase: false,
+            isInterface: false,
+            isFinal: false,
             isAugmentation: false,
             isMixinClass: false),
         interfaceBuilders,
diff --git a/pkg/front_end/lib/src/fasta/type_inference/inference_visitor.dart b/pkg/front_end/lib/src/fasta/type_inference/inference_visitor.dart
index b512d52..752ec87 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/inference_visitor.dart
@@ -1346,7 +1346,7 @@
 
     // This is matched by the call to [forEach_end] in
     // [inferElement], [inferMapEntry] or [inferForInStatement].
-    flowAnalysis.declare(variable, true, variable.type);
+    flowAnalysis.declare(variable, variable.type, initialized: true);
     flowAnalysis.forEach_bodyBegin(node);
 
     VariableDeclaration tempVariable =
@@ -1618,7 +1618,7 @@
           inferredType.returnType;
     }
     variable.type = inferredType;
-    flowAnalysis.declare(variable, true, variable.type);
+    flowAnalysis.declare(variable, variable.type, initialized: true);
     flowAnalysis.functionExpression_end();
     return const StatementInferenceResult();
   }
@@ -8346,7 +8346,8 @@
       }
       node.type = inferredType;
     }
-    flowAnalysis.declare(node, node.hasDeclaredInitializer, node.type);
+    flowAnalysis.declare(node, node.type,
+        initialized: node.hasDeclaredInitializer);
     if (initializerResult != null) {
       DartType initializerType = initializerResult.inferredType;
       // TODO(paulberry): `initializerType` is sometimes `null` during top
diff --git a/pkg/front_end/lib/src/fasta/type_inference/inference_visitor_base.dart b/pkg/front_end/lib/src/fasta/type_inference/inference_visitor_base.dart
index 210c779..784af9d 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/inference_visitor_base.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/inference_visitor_base.dart
@@ -2075,7 +2075,7 @@
         function.positionalParameters;
     for (int i = 0; i < positionalParameters.length; i++) {
       VariableDeclaration parameter = positionalParameters[i];
-      flowAnalysis.declare(parameter, true, parameter.type);
+      flowAnalysis.declare(parameter, parameter.type, initialized: true);
       inferMetadata(visitor, parameter, parameter.annotations);
       if (parameter.initializer != null) {
         ExpressionInferenceResult initializerResult =
@@ -2085,7 +2085,7 @@
       }
     }
     for (VariableDeclaration parameter in function.namedParameters) {
-      flowAnalysis.declare(parameter, true, parameter.type);
+      flowAnalysis.declare(parameter, parameter.type, initialized: true);
       inferMetadata(visitor, parameter, parameter.annotations);
       ExpressionInferenceResult initializerResult =
           visitor.inferExpression(parameter.initializer!, parameter.type);
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index 4ffab32..39383a1 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -217,14 +217,14 @@
     List<Expression> positionalArguments = <Expression>[];
     for (VariableDeclaration parameter
         in redirectingFactoryFunction.positionalParameters) {
-      flowAnalysis.declare(parameter, true, parameter.type);
+      flowAnalysis.declare(parameter, parameter.type, initialized: true);
       positionalArguments
           .add(new VariableGetImpl(parameter, forNullGuardedAccess: false));
     }
     List<NamedExpression> namedArguments = <NamedExpression>[];
     for (VariableDeclaration parameter
         in redirectingFactoryFunction.namedParameters) {
-      flowAnalysis.declare(parameter, true, parameter.type);
+      flowAnalysis.declare(parameter, parameter.type, initialized: true);
       namedArguments.add(new NamedExpression(parameter.name!,
           new VariableGetImpl(parameter, forNullGuardedAccess: false)));
     }
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index f9f296f..3e37e00 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -840,6 +840,7 @@
 ly
 lz
 m
+machinery
 macos
 macro
 macros
@@ -999,6 +1000,7 @@
 patchup
 path
 pattern's
+patterned
 patterns
 paulberry
 pay
diff --git a/pkg/front_end/testcases/README.md b/pkg/front_end/testcases/README.md
index 143a0df..208db5a 100644
--- a/pkg/front_end/testcases/README.md
+++ b/pkg/front_end/testcases/README.md
@@ -11,17 +11,26 @@
 
 The source of truth for these configurations is the file [pkg/front_end/testing.json](../testing.json).
 
-## Dart 1.0 Compilation
-
-* Status file: [legacy.status](legacy.status)
-* Standalone test: [pkg/front_end/test/fasta/legacy_test.dart](../test/fasta/legacy_test.dart)
-* Expectation prefix: `.direct.expect`
-* How to update expectations:
-
+## Updating all expectations
+To update test expectations for all tests at once, run:
+```bash
+dart pkg/front_end/tool/update_expectations.dart
 ```
-./pkg/front_end/tool/fasta testing -DupdateExpectations=true compile/test1 compile/test2 ...
+Note that this takes a long time and should only be used when many tests need updating.
+
+## Updating expectations for a single test
+To update the expectations for a specific test, provide the folder and test name as an argument.
+
+For example, if you want to update the test expectations for a test, such as `pkg/front_end/testcases/general/abstract_instantiation.dart`, then run:
+```bash
+dart pkg/front_end/tool/update_expectations.dart general/abstract_instantiation
 ```
 
+## Updating expectations for all tests in a folder
+If you want to update the test expectations for a specific folder of tests such as the `pkg/front_end/testcases/general/` folder, then run:
+```bash
+dart pkg/front_end/tool/update_expectations.dart general/...
+```
 
 ## Dart 1.0 Outlines
 
diff --git a/pkg/front_end/testcases/class_modifiers/base/base_class_declaration.dart.strong.expect b/pkg/front_end/testcases/class_modifiers/base/base_class_declaration.dart.strong.expect
index 2a76063..2290cce 100644
--- a/pkg/front_end/testcases/class_modifiers/base/base_class_declaration.dart.strong.expect
+++ b/pkg/front_end/testcases/class_modifiers/base/base_class_declaration.dart.strong.expect
@@ -2,19 +2,19 @@
 import self as self;
 import "dart:core" as core;
 
-class A extends core::Object {
+base class A extends core::Object {
   synthetic constructor •() → self::A
     : super core::Object::•()
     ;
 }
-abstract class B extends core::Object {
+abstract base class B extends core::Object {
   synthetic constructor •() → self::B
     : super core::Object::•()
     ;
 }
-abstract class M extends core::Object /*isMixinDeclaration*/  {
+abstract base class M extends core::Object /*isMixinDeclaration*/  {
 }
-class C = core::Object with self::M /*hasConstConstructor*/  {
+base class C = core::Object with self::M /*hasConstConstructor*/  {
   const synthetic constructor •() → self::C
     : super core::Object::•()
     ;
diff --git a/pkg/front_end/testcases/class_modifiers/base/base_class_declaration.dart.strong.transformed.expect b/pkg/front_end/testcases/class_modifiers/base/base_class_declaration.dart.strong.transformed.expect
index 3e3e810..19dc5b7 100644
--- a/pkg/front_end/testcases/class_modifiers/base/base_class_declaration.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/class_modifiers/base/base_class_declaration.dart.strong.transformed.expect
@@ -2,19 +2,19 @@
 import self as self;
 import "dart:core" as core;
 
-class A extends core::Object {
+base class A extends core::Object {
   synthetic constructor •() → self::A
     : super core::Object::•()
     ;
 }
-abstract class B extends core::Object {
+abstract base class B extends core::Object {
   synthetic constructor •() → self::B
     : super core::Object::•()
     ;
 }
-abstract class M extends core::Object /*isMixinDeclaration*/  {
+abstract base class M extends core::Object /*isMixinDeclaration*/  {
 }
-class C extends core::Object implements self::M /*isEliminatedMixin,hasConstConstructor*/  {
+base class C extends core::Object implements self::M /*isEliminatedMixin,hasConstConstructor*/  {
   const synthetic constructor •() → self::C
     : super core::Object::•()
     ;
diff --git a/pkg/front_end/testcases/class_modifiers/base/base_class_declaration.dart.weak.expect b/pkg/front_end/testcases/class_modifiers/base/base_class_declaration.dart.weak.expect
index 2a76063..2290cce 100644
--- a/pkg/front_end/testcases/class_modifiers/base/base_class_declaration.dart.weak.expect
+++ b/pkg/front_end/testcases/class_modifiers/base/base_class_declaration.dart.weak.expect
@@ -2,19 +2,19 @@
 import self as self;
 import "dart:core" as core;
 
-class A extends core::Object {
+base class A extends core::Object {
   synthetic constructor •() → self::A
     : super core::Object::•()
     ;
 }
-abstract class B extends core::Object {
+abstract base class B extends core::Object {
   synthetic constructor •() → self::B
     : super core::Object::•()
     ;
 }
-abstract class M extends core::Object /*isMixinDeclaration*/  {
+abstract base class M extends core::Object /*isMixinDeclaration*/  {
 }
-class C = core::Object with self::M /*hasConstConstructor*/  {
+base class C = core::Object with self::M /*hasConstConstructor*/  {
   const synthetic constructor •() → self::C
     : super core::Object::•()
     ;
diff --git a/pkg/front_end/testcases/class_modifiers/base/base_class_declaration.dart.weak.modular.expect b/pkg/front_end/testcases/class_modifiers/base/base_class_declaration.dart.weak.modular.expect
index 2a76063..2290cce 100644
--- a/pkg/front_end/testcases/class_modifiers/base/base_class_declaration.dart.weak.modular.expect
+++ b/pkg/front_end/testcases/class_modifiers/base/base_class_declaration.dart.weak.modular.expect
@@ -2,19 +2,19 @@
 import self as self;
 import "dart:core" as core;
 
-class A extends core::Object {
+base class A extends core::Object {
   synthetic constructor •() → self::A
     : super core::Object::•()
     ;
 }
-abstract class B extends core::Object {
+abstract base class B extends core::Object {
   synthetic constructor •() → self::B
     : super core::Object::•()
     ;
 }
-abstract class M extends core::Object /*isMixinDeclaration*/  {
+abstract base class M extends core::Object /*isMixinDeclaration*/  {
 }
-class C = core::Object with self::M /*hasConstConstructor*/  {
+base class C = core::Object with self::M /*hasConstConstructor*/  {
   const synthetic constructor •() → self::C
     : super core::Object::•()
     ;
diff --git a/pkg/front_end/testcases/class_modifiers/base/base_class_declaration.dart.weak.outline.expect b/pkg/front_end/testcases/class_modifiers/base/base_class_declaration.dart.weak.outline.expect
index 76f8cc4..5cbc257 100644
--- a/pkg/front_end/testcases/class_modifiers/base/base_class_declaration.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/class_modifiers/base/base_class_declaration.dart.weak.outline.expect
@@ -2,17 +2,17 @@
 import self as self;
 import "dart:core" as core;
 
-class A extends core::Object {
+base class A extends core::Object {
   synthetic constructor •() → self::A
     ;
 }
-abstract class B extends core::Object {
+abstract base class B extends core::Object {
   synthetic constructor •() → self::B
     ;
 }
-abstract class M extends core::Object /*isMixinDeclaration*/  {
+abstract base class M extends core::Object /*isMixinDeclaration*/  {
 }
-class C = core::Object with self::M /*hasConstConstructor*/  {
+base class C = core::Object with self::M /*hasConstConstructor*/  {
   const synthetic constructor •() → self::C
     : super core::Object::•()
     ;
diff --git a/pkg/front_end/testcases/class_modifiers/base/base_class_declaration.dart.weak.transformed.expect b/pkg/front_end/testcases/class_modifiers/base/base_class_declaration.dart.weak.transformed.expect
index 3e3e810..19dc5b7 100644
--- a/pkg/front_end/testcases/class_modifiers/base/base_class_declaration.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/class_modifiers/base/base_class_declaration.dart.weak.transformed.expect
@@ -2,19 +2,19 @@
 import self as self;
 import "dart:core" as core;
 
-class A extends core::Object {
+base class A extends core::Object {
   synthetic constructor •() → self::A
     : super core::Object::•()
     ;
 }
-abstract class B extends core::Object {
+abstract base class B extends core::Object {
   synthetic constructor •() → self::B
     : super core::Object::•()
     ;
 }
-abstract class M extends core::Object /*isMixinDeclaration*/  {
+abstract base class M extends core::Object /*isMixinDeclaration*/  {
 }
-class C extends core::Object implements self::M /*isEliminatedMixin,hasConstConstructor*/  {
+base class C extends core::Object implements self::M /*isEliminatedMixin,hasConstConstructor*/  {
   const synthetic constructor •() → self::C
     : super core::Object::•()
     ;
diff --git a/pkg/front_end/testcases/class_modifiers/final/final_class_declaration.dart.strong.expect b/pkg/front_end/testcases/class_modifiers/final/final_class_declaration.dart.strong.expect
index 2a76063..89853a8 100644
--- a/pkg/front_end/testcases/class_modifiers/final/final_class_declaration.dart.strong.expect
+++ b/pkg/front_end/testcases/class_modifiers/final/final_class_declaration.dart.strong.expect
@@ -2,19 +2,19 @@
 import self as self;
 import "dart:core" as core;
 
-class A extends core::Object {
+final class A extends core::Object {
   synthetic constructor •() → self::A
     : super core::Object::•()
     ;
 }
-abstract class B extends core::Object {
+abstract final class B extends core::Object {
   synthetic constructor •() → self::B
     : super core::Object::•()
     ;
 }
-abstract class M extends core::Object /*isMixinDeclaration*/  {
+abstract final class M extends core::Object /*isMixinDeclaration*/  {
 }
-class C = core::Object with self::M /*hasConstConstructor*/  {
+final class C = core::Object with self::M /*hasConstConstructor*/  {
   const synthetic constructor •() → self::C
     : super core::Object::•()
     ;
diff --git a/pkg/front_end/testcases/class_modifiers/final/final_class_declaration.dart.strong.transformed.expect b/pkg/front_end/testcases/class_modifiers/final/final_class_declaration.dart.strong.transformed.expect
index 3e3e810..c8ef7b9 100644
--- a/pkg/front_end/testcases/class_modifiers/final/final_class_declaration.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/class_modifiers/final/final_class_declaration.dart.strong.transformed.expect
@@ -2,19 +2,19 @@
 import self as self;
 import "dart:core" as core;
 
-class A extends core::Object {
+final class A extends core::Object {
   synthetic constructor •() → self::A
     : super core::Object::•()
     ;
 }
-abstract class B extends core::Object {
+abstract final class B extends core::Object {
   synthetic constructor •() → self::B
     : super core::Object::•()
     ;
 }
-abstract class M extends core::Object /*isMixinDeclaration*/  {
+abstract final class M extends core::Object /*isMixinDeclaration*/  {
 }
-class C extends core::Object implements self::M /*isEliminatedMixin,hasConstConstructor*/  {
+final class C extends core::Object implements self::M /*isEliminatedMixin,hasConstConstructor*/  {
   const synthetic constructor •() → self::C
     : super core::Object::•()
     ;
diff --git a/pkg/front_end/testcases/class_modifiers/final/final_class_declaration.dart.weak.expect b/pkg/front_end/testcases/class_modifiers/final/final_class_declaration.dart.weak.expect
index 2a76063..89853a8 100644
--- a/pkg/front_end/testcases/class_modifiers/final/final_class_declaration.dart.weak.expect
+++ b/pkg/front_end/testcases/class_modifiers/final/final_class_declaration.dart.weak.expect
@@ -2,19 +2,19 @@
 import self as self;
 import "dart:core" as core;
 
-class A extends core::Object {
+final class A extends core::Object {
   synthetic constructor •() → self::A
     : super core::Object::•()
     ;
 }
-abstract class B extends core::Object {
+abstract final class B extends core::Object {
   synthetic constructor •() → self::B
     : super core::Object::•()
     ;
 }
-abstract class M extends core::Object /*isMixinDeclaration*/  {
+abstract final class M extends core::Object /*isMixinDeclaration*/  {
 }
-class C = core::Object with self::M /*hasConstConstructor*/  {
+final class C = core::Object with self::M /*hasConstConstructor*/  {
   const synthetic constructor •() → self::C
     : super core::Object::•()
     ;
diff --git a/pkg/front_end/testcases/class_modifiers/final/final_class_declaration.dart.weak.modular.expect b/pkg/front_end/testcases/class_modifiers/final/final_class_declaration.dart.weak.modular.expect
index 2a76063..89853a8 100644
--- a/pkg/front_end/testcases/class_modifiers/final/final_class_declaration.dart.weak.modular.expect
+++ b/pkg/front_end/testcases/class_modifiers/final/final_class_declaration.dart.weak.modular.expect
@@ -2,19 +2,19 @@
 import self as self;
 import "dart:core" as core;
 
-class A extends core::Object {
+final class A extends core::Object {
   synthetic constructor •() → self::A
     : super core::Object::•()
     ;
 }
-abstract class B extends core::Object {
+abstract final class B extends core::Object {
   synthetic constructor •() → self::B
     : super core::Object::•()
     ;
 }
-abstract class M extends core::Object /*isMixinDeclaration*/  {
+abstract final class M extends core::Object /*isMixinDeclaration*/  {
 }
-class C = core::Object with self::M /*hasConstConstructor*/  {
+final class C = core::Object with self::M /*hasConstConstructor*/  {
   const synthetic constructor •() → self::C
     : super core::Object::•()
     ;
diff --git a/pkg/front_end/testcases/class_modifiers/final/final_class_declaration.dart.weak.outline.expect b/pkg/front_end/testcases/class_modifiers/final/final_class_declaration.dart.weak.outline.expect
index 76f8cc4..18cc74a 100644
--- a/pkg/front_end/testcases/class_modifiers/final/final_class_declaration.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/class_modifiers/final/final_class_declaration.dart.weak.outline.expect
@@ -2,17 +2,17 @@
 import self as self;
 import "dart:core" as core;
 
-class A extends core::Object {
+final class A extends core::Object {
   synthetic constructor •() → self::A
     ;
 }
-abstract class B extends core::Object {
+abstract final class B extends core::Object {
   synthetic constructor •() → self::B
     ;
 }
-abstract class M extends core::Object /*isMixinDeclaration*/  {
+abstract final class M extends core::Object /*isMixinDeclaration*/  {
 }
-class C = core::Object with self::M /*hasConstConstructor*/  {
+final class C = core::Object with self::M /*hasConstConstructor*/  {
   const synthetic constructor •() → self::C
     : super core::Object::•()
     ;
diff --git a/pkg/front_end/testcases/class_modifiers/final/final_class_declaration.dart.weak.transformed.expect b/pkg/front_end/testcases/class_modifiers/final/final_class_declaration.dart.weak.transformed.expect
index 3e3e810..c8ef7b9 100644
--- a/pkg/front_end/testcases/class_modifiers/final/final_class_declaration.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/class_modifiers/final/final_class_declaration.dart.weak.transformed.expect
@@ -2,19 +2,19 @@
 import self as self;
 import "dart:core" as core;
 
-class A extends core::Object {
+final class A extends core::Object {
   synthetic constructor •() → self::A
     : super core::Object::•()
     ;
 }
-abstract class B extends core::Object {
+abstract final class B extends core::Object {
   synthetic constructor •() → self::B
     : super core::Object::•()
     ;
 }
-abstract class M extends core::Object /*isMixinDeclaration*/  {
+abstract final class M extends core::Object /*isMixinDeclaration*/  {
 }
-class C extends core::Object implements self::M /*isEliminatedMixin,hasConstConstructor*/  {
+final class C extends core::Object implements self::M /*isEliminatedMixin,hasConstConstructor*/  {
   const synthetic constructor •() → self::C
     : super core::Object::•()
     ;
diff --git a/pkg/front_end/testcases/class_modifiers/interface/interface_class_declaration.dart.strong.expect b/pkg/front_end/testcases/class_modifiers/interface/interface_class_declaration.dart.strong.expect
index 2a76063..dfd3537 100644
--- a/pkg/front_end/testcases/class_modifiers/interface/interface_class_declaration.dart.strong.expect
+++ b/pkg/front_end/testcases/class_modifiers/interface/interface_class_declaration.dart.strong.expect
@@ -2,19 +2,19 @@
 import self as self;
 import "dart:core" as core;
 
-class A extends core::Object {
+interface class A extends core::Object {
   synthetic constructor •() → self::A
     : super core::Object::•()
     ;
 }
-abstract class B extends core::Object {
+abstract interface class B extends core::Object {
   synthetic constructor •() → self::B
     : super core::Object::•()
     ;
 }
-abstract class M extends core::Object /*isMixinDeclaration*/  {
+abstract interface class M extends core::Object /*isMixinDeclaration*/  {
 }
-class C = core::Object with self::M /*hasConstConstructor*/  {
+interface class C = core::Object with self::M /*hasConstConstructor*/  {
   const synthetic constructor •() → self::C
     : super core::Object::•()
     ;
diff --git a/pkg/front_end/testcases/class_modifiers/interface/interface_class_declaration.dart.strong.transformed.expect b/pkg/front_end/testcases/class_modifiers/interface/interface_class_declaration.dart.strong.transformed.expect
index 3e3e810..e9cf346 100644
--- a/pkg/front_end/testcases/class_modifiers/interface/interface_class_declaration.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/class_modifiers/interface/interface_class_declaration.dart.strong.transformed.expect
@@ -2,19 +2,19 @@
 import self as self;
 import "dart:core" as core;
 
-class A extends core::Object {
+interface class A extends core::Object {
   synthetic constructor •() → self::A
     : super core::Object::•()
     ;
 }
-abstract class B extends core::Object {
+abstract interface class B extends core::Object {
   synthetic constructor •() → self::B
     : super core::Object::•()
     ;
 }
-abstract class M extends core::Object /*isMixinDeclaration*/  {
+abstract interface class M extends core::Object /*isMixinDeclaration*/  {
 }
-class C extends core::Object implements self::M /*isEliminatedMixin,hasConstConstructor*/  {
+interface class C extends core::Object implements self::M /*isEliminatedMixin,hasConstConstructor*/  {
   const synthetic constructor •() → self::C
     : super core::Object::•()
     ;
diff --git a/pkg/front_end/testcases/class_modifiers/interface/interface_class_declaration.dart.weak.expect b/pkg/front_end/testcases/class_modifiers/interface/interface_class_declaration.dart.weak.expect
index 2a76063..dfd3537 100644
--- a/pkg/front_end/testcases/class_modifiers/interface/interface_class_declaration.dart.weak.expect
+++ b/pkg/front_end/testcases/class_modifiers/interface/interface_class_declaration.dart.weak.expect
@@ -2,19 +2,19 @@
 import self as self;
 import "dart:core" as core;
 
-class A extends core::Object {
+interface class A extends core::Object {
   synthetic constructor •() → self::A
     : super core::Object::•()
     ;
 }
-abstract class B extends core::Object {
+abstract interface class B extends core::Object {
   synthetic constructor •() → self::B
     : super core::Object::•()
     ;
 }
-abstract class M extends core::Object /*isMixinDeclaration*/  {
+abstract interface class M extends core::Object /*isMixinDeclaration*/  {
 }
-class C = core::Object with self::M /*hasConstConstructor*/  {
+interface class C = core::Object with self::M /*hasConstConstructor*/  {
   const synthetic constructor •() → self::C
     : super core::Object::•()
     ;
diff --git a/pkg/front_end/testcases/class_modifiers/interface/interface_class_declaration.dart.weak.modular.expect b/pkg/front_end/testcases/class_modifiers/interface/interface_class_declaration.dart.weak.modular.expect
index 2a76063..dfd3537 100644
--- a/pkg/front_end/testcases/class_modifiers/interface/interface_class_declaration.dart.weak.modular.expect
+++ b/pkg/front_end/testcases/class_modifiers/interface/interface_class_declaration.dart.weak.modular.expect
@@ -2,19 +2,19 @@
 import self as self;
 import "dart:core" as core;
 
-class A extends core::Object {
+interface class A extends core::Object {
   synthetic constructor •() → self::A
     : super core::Object::•()
     ;
 }
-abstract class B extends core::Object {
+abstract interface class B extends core::Object {
   synthetic constructor •() → self::B
     : super core::Object::•()
     ;
 }
-abstract class M extends core::Object /*isMixinDeclaration*/  {
+abstract interface class M extends core::Object /*isMixinDeclaration*/  {
 }
-class C = core::Object with self::M /*hasConstConstructor*/  {
+interface class C = core::Object with self::M /*hasConstConstructor*/  {
   const synthetic constructor •() → self::C
     : super core::Object::•()
     ;
diff --git a/pkg/front_end/testcases/class_modifiers/interface/interface_class_declaration.dart.weak.outline.expect b/pkg/front_end/testcases/class_modifiers/interface/interface_class_declaration.dart.weak.outline.expect
index 76f8cc4..25d4bba 100644
--- a/pkg/front_end/testcases/class_modifiers/interface/interface_class_declaration.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/class_modifiers/interface/interface_class_declaration.dart.weak.outline.expect
@@ -2,17 +2,17 @@
 import self as self;
 import "dart:core" as core;
 
-class A extends core::Object {
+interface class A extends core::Object {
   synthetic constructor •() → self::A
     ;
 }
-abstract class B extends core::Object {
+abstract interface class B extends core::Object {
   synthetic constructor •() → self::B
     ;
 }
-abstract class M extends core::Object /*isMixinDeclaration*/  {
+abstract interface class M extends core::Object /*isMixinDeclaration*/  {
 }
-class C = core::Object with self::M /*hasConstConstructor*/  {
+interface class C = core::Object with self::M /*hasConstConstructor*/  {
   const synthetic constructor •() → self::C
     : super core::Object::•()
     ;
diff --git a/pkg/front_end/testcases/class_modifiers/interface/interface_class_declaration.dart.weak.transformed.expect b/pkg/front_end/testcases/class_modifiers/interface/interface_class_declaration.dart.weak.transformed.expect
index 3e3e810..e9cf346 100644
--- a/pkg/front_end/testcases/class_modifiers/interface/interface_class_declaration.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/class_modifiers/interface/interface_class_declaration.dart.weak.transformed.expect
@@ -2,19 +2,19 @@
 import self as self;
 import "dart:core" as core;
 
-class A extends core::Object {
+interface class A extends core::Object {
   synthetic constructor •() → self::A
     : super core::Object::•()
     ;
 }
-abstract class B extends core::Object {
+abstract interface class B extends core::Object {
   synthetic constructor •() → self::B
     : super core::Object::•()
     ;
 }
-abstract class M extends core::Object /*isMixinDeclaration*/  {
+abstract interface class M extends core::Object /*isMixinDeclaration*/  {
 }
-class C extends core::Object implements self::M /*isEliminatedMixin,hasConstConstructor*/  {
+interface class C extends core::Object implements self::M /*isEliminatedMixin,hasConstConstructor*/  {
   const synthetic constructor •() → self::C
     : super core::Object::•()
     ;
diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md
index 5a67d53..1195cce 100644
--- a/pkg/kernel/binary.md
+++ b/pkg/kernel/binary.md
@@ -147,7 +147,7 @@
 
 type ComponentFile {
   UInt32 magic = 0x90ABCDEF;
-  UInt32 formatVersion = 92;
+  UInt32 formatVersion = 93;
   Byte[10] shortSdkHash;
   List<String> problemsAsJson; // Described in problems.md.
   Library[] libraries;
@@ -320,7 +320,7 @@
   FileOffset fileEndOffset;
   UInt flags (isAbstract, isEnum, isAnonymousMixin, isEliminatedMixin,
               isMixinDeclaration, hasConstConstructor, isMacro, isSealed,
-              isMixinClass);
+              isMixinClass, isBase, isInterface, isFinal);
   StringReference name;
   List<Expression> annotations;
   List<TypeParameter> typeParameters;
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index e2bc666..222cb56 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -1027,6 +1027,9 @@
   static const int FlagMacro = 1 << 6;
   static const int FlagSealed = 1 << 7;
   static const int FlagMixinClass = 1 << 8;
+  static const int FlagBase = 1 << 9;
+  static const int FlagInterface = 1 << 10;
+  static const int FlagFinal = 1 << 11;
 
   int flags = 0;
 
@@ -1050,13 +1053,34 @@
     flags = value ? (flags | FlagMacro) : (flags & ~FlagMacro);
   }
 
-  /// Whether this class is a macro class.
+  /// Whether this class is a sealed class.
   bool get isSealed => flags & FlagSealed != 0;
 
   void set isSealed(bool value) {
     flags = value ? (flags | FlagSealed) : (flags & ~FlagSealed);
   }
 
+  /// Whether this class is a base class.
+  bool get isBase => flags & FlagBase != 0;
+
+  void set isBase(bool value) {
+    flags = value ? (flags | FlagBase) : (flags & ~FlagBase);
+  }
+
+  /// Whether this class is an interface class.
+  bool get isInterface => flags & FlagInterface != 0;
+
+  void set isInterface(bool value) {
+    flags = value ? (flags | FlagInterface) : (flags & ~FlagInterface);
+  }
+
+  /// Whether this class is a final class.
+  bool get isFinal => flags & FlagFinal != 0;
+
+  void set isFinal(bool value) {
+    flags = value ? (flags | FlagFinal) : (flags & ~FlagFinal);
+  }
+
   /// Whether this class is a synthetic implementation created for each
   /// mixed-in class. For example the following code:
   /// class Z extends A with B, C, D {}
diff --git a/pkg/kernel/lib/binary/tag.dart b/pkg/kernel/lib/binary/tag.dart
index a41a737..1ff25a8 100644
--- a/pkg/kernel/lib/binary/tag.dart
+++ b/pkg/kernel/lib/binary/tag.dart
@@ -195,7 +195,7 @@
   /// Internal version of kernel binary format.
   /// Bump it when making incompatible changes in kernel binaries.
   /// Keep in sync with runtime/vm/kernel_binary.h, pkg/kernel/binary.md.
-  static const int BinaryFormatVersion = 92;
+  static const int BinaryFormatVersion = 93;
 }
 
 abstract class ConstantTag {
diff --git a/pkg/kernel/lib/text/ast_to_text.dart b/pkg/kernel/lib/text/ast_to_text.dart
index ca6cd58..51827a2 100644
--- a/pkg/kernel/lib/text/ast_to_text.dart
+++ b/pkg/kernel/lib/text/ast_to_text.dart
@@ -1307,6 +1307,9 @@
     writeModifier(node.isAbstract, 'abstract');
     writeModifier(node.isMacro, 'macro');
     writeModifier(node.isSealed, 'sealed');
+    writeModifier(node.isBase, 'base');
+    writeModifier(node.isInterface, 'interface');
+    writeModifier(node.isFinal, 'final');
     writeModifier(node.isMixinClass, 'mixin');
     writeWord('class');
     writeWord(getClassName(node));
diff --git a/pkg/nnbd_migration/lib/src/edge_builder.dart b/pkg/nnbd_migration/lib/src/edge_builder.dart
index b514060..6ea69fd 100644
--- a/pkg/nnbd_migration/lib/src/edge_builder.dart
+++ b/pkg/nnbd_migration/lib/src/edge_builder.dart
@@ -2063,8 +2063,9 @@
       }
       try {
         if (declaredElement is PromotableElement) {
-          _flowAnalysis!.declare(declaredElement, initializer != null,
-              _variables.decoratedElementType(declaredElement));
+          _flowAnalysis!.declare(
+              declaredElement, _variables.decoratedElementType(declaredElement),
+              initialized: initializer != null);
         }
         if (initializer == null) {
           // For top level variables and static fields, we have to generate an
@@ -2124,9 +2125,9 @@
         var declaredElement = parameter.declaredElement!;
         // TODO(paulberry): `skipDuplicateCheck` is currently needed to work
         // around a failure in api_test.dart; fix this.
-        _flowAnalysis!.declare(declaredElement, true,
-            _variables.decoratedElementType(declaredElement),
-            skipDuplicateCheck: true);
+        _flowAnalysis!.declare(
+            declaredElement, _variables.decoratedElementType(declaredElement),
+            initialized: true, skipDuplicateCheck: true);
       }
     }
   }
@@ -2209,8 +2210,9 @@
     if (parameters != null) {
       for (var parameter in parameters.parameters) {
         var declaredElement = parameter.declaredElement!;
-        _flowAnalysis!.declare(declaredElement, true,
-            _variables.decoratedElementType(declaredElement));
+        _flowAnalysis!.declare(
+            declaredElement, _variables.decoratedElementType(declaredElement),
+            initialized: true);
       }
     }
   }
@@ -2949,8 +2951,9 @@
       DecoratedType? lhsType;
       if (parts is ForEachPartsWithDeclaration) {
         var variableElement = parts.loopVariable.declaredElement!;
-        _flowAnalysis!.declare(variableElement, true,
-            _variables.decoratedElementType(variableElement));
+        _flowAnalysis!.declare(
+            variableElement, _variables.decoratedElementType(variableElement),
+            initialized: true);
         lhsElement = variableElement;
         _dispatch(parts.loopVariable.type);
         lhsType = _variables.decoratedElementType(lhsElement);
diff --git a/runtime/lib/string.cc b/runtime/lib/string.cc
index e411e07..ad611b7 100644
--- a/runtime/lib/string.cc
+++ b/runtime/lib/string.cc
@@ -245,6 +245,12 @@
   return result.ptr();
 }
 
+DEFINE_NATIVE_ENTRY(StringBase_intern, 0, 1) {
+  const String& receiver =
+      String::CheckedHandle(zone, arguments->NativeArgAt(0));
+  return Symbols::New(thread, receiver);
+}
+
 DEFINE_NATIVE_ENTRY(OneByteString_substringUnchecked, 0, 3) {
   const String& receiver =
       String::CheckedHandle(zone, arguments->NativeArgAt(0));
diff --git a/runtime/observatory/lib/src/allocation_profile/allocation_profile.dart b/runtime/observatory/lib/src/allocation_profile/allocation_profile.dart
index 0ec7fa1..beb8731 100644
--- a/runtime/observatory/lib/src/allocation_profile/allocation_profile.dart
+++ b/runtime/observatory/lib/src/allocation_profile/allocation_profile.dart
@@ -26,7 +26,7 @@
     totalSpace.add(newSpace);
   }
 
-  static DateTime? _intString2DateTime(String milliseconds) {
+  static DateTime? _intString2DateTime(String? milliseconds) {
     if ((milliseconds == null) || milliseconds == '') {
       return null;
     }
diff --git a/runtime/observatory/lib/src/elements/inbound_references.dart b/runtime/observatory/lib/src/elements/inbound_references.dart
index db22f02..e49a020 100644
--- a/runtime/observatory/lib/src/elements/inbound_references.dart
+++ b/runtime/observatory/lib/src/elements/inbound_references.dart
@@ -89,12 +89,16 @@
   Element _createItem(M.InboundReference reference) {
     final content = <Element>[];
 
-    if (reference.parentField != null) {
+    if (reference.parentField is M.Object) {
       content.addAll([
         new SpanElement()..text = 'referenced by ',
         anyRef(_isolate, reference.parentField, _objects, queue: _r.queue),
         new SpanElement()..text = ' of '
       ]);
+    } else if (reference.parentField is String ||
+        reference.parentField is int) {
+      content.add(new SpanElement()
+        ..text = 'referenced by [ ${reference.parentField} ] of ');
     } else if (reference.parentListIndex != null) {
       content.add(new SpanElement()
         ..text = 'referenced by [ ${reference.parentListIndex} ] of ');
diff --git a/runtime/observatory/lib/src/models/objects/inbound_references.dart b/runtime/observatory/lib/src/models/objects/inbound_references.dart
index ea18744..53dbdbb 100644
--- a/runtime/observatory/lib/src/models/objects/inbound_references.dart
+++ b/runtime/observatory/lib/src/models/objects/inbound_references.dart
@@ -12,11 +12,11 @@
   ObjectRef get source;
 
   /// [optional]
-  ObjectRef get parentField;
+  dynamic get parentField;
 
   /// [optional]
-  int get parentListIndex;
+  int? get parentListIndex;
 
   /// [optional]
-  int get parentWordOffset;
+  int? get parentWordOffset;
 }
diff --git a/runtime/observatory/lib/src/models/objects/retaining_path.dart b/runtime/observatory/lib/src/models/objects/retaining_path.dart
index 360d81d..03e15bc 100644
--- a/runtime/observatory/lib/src/models/objects/retaining_path.dart
+++ b/runtime/observatory/lib/src/models/objects/retaining_path.dart
@@ -14,11 +14,11 @@
   ObjectRef get source;
 
   /// [optional]
-  String get parentField;
+  dynamic get parentField;
 
   /// [optional]
-  int get parentListIndex;
+  int? get parentListIndex;
 
   /// [optional]
-  int get parentWordOffset;
+  int? get parentWordOffset;
 }
diff --git a/runtime/observatory/lib/src/service/object.dart b/runtime/observatory/lib/src/service/object.dart
index 78746d1..817836f 100644
--- a/runtime/observatory/lib/src/service/object.dart
+++ b/runtime/observatory/lib/src/service/object.dart
@@ -1166,9 +1166,9 @@
 
 class InboundReference implements M.InboundReference {
   final ServiceObject /*HeapObject*/ source;
-  final HeapObject parentField;
-  final int parentListIndex;
-  final int parentWordOffset;
+  final dynamic parentField;
+  final int? parentListIndex;
+  final int? parentWordOffset;
 
   InboundReference(Map map)
       : source = map['source'],
@@ -1190,9 +1190,9 @@
 
 class RetainingPathItem implements M.RetainingPathItem {
   final ServiceObject /*HeapObject*/ source;
-  final String parentField;
-  final int parentListIndex;
-  final int parentWordOffset;
+  final dynamic parentField;
+  final int? parentListIndex;
+  final int? parentWordOffset;
 
   RetainingPathItem(Map map)
       : source = map['value'],
diff --git a/runtime/tests/vm/dart/string_intern_test.dart b/runtime/tests/vm/dart/string_intern_test.dart
new file mode 100644
index 0000000..ae2b55c
--- /dev/null
+++ b/runtime/tests/vm/dart/string_intern_test.dart
@@ -0,0 +1,24 @@
+// Copyright (c) 2022, 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:_internal" show intern;
+import "package:expect/expect.dart";
+
+@pragma("vm:never-inline")
+String genString(int i) => "abc-${i}-xyz";
+
+main() {
+  int random = Object().hashCode;
+  var a = genString(random);
+  var b = genString(random);
+  Expect.notIdentical(a, b);
+
+  var internedA = intern(a);
+  Expect.equals(a, internedA);
+  // Likely, but not guarenteed: Expect.identical(a, internedA);
+  var internedB = intern(b);
+  Expect.equals(b, internedB);
+  // Likely, but not guarenteed: Expect.identical(a, internedB);
+  Expect.identical(internedA, internedB);
+}
diff --git a/runtime/tests/vm/dart/weak_canonical_string_table_test.dart b/runtime/tests/vm/dart/weak_canonical_string_table_test.dart
new file mode 100644
index 0000000..346d765
--- /dev/null
+++ b/runtime/tests/vm/dart/weak_canonical_string_table_test.dart
@@ -0,0 +1,17 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// VMOptions=--old_gen_heap_size=20
+
+import "dart:_internal" show intern;
+
+@pragma("vm:never-inline")
+use(x) => x;
+
+main() {
+  const MB = 1 << 20;
+  for (var i = 0; i < 20 * MB; i++) {
+    use(intern((i.toString()))); // Should not hit OutOfMemory
+  }
+}
diff --git a/runtime/vm/app_snapshot.cc b/runtime/vm/app_snapshot.cc
index 56ba0e3..36362bb 100644
--- a/runtime/vm/app_snapshot.cc
+++ b/runtime/vm/app_snapshot.cc
@@ -1534,7 +1534,8 @@
       ASSERT(d_->kind() != Snapshot::kFullAOT);
       field->untag()->guarded_list_length_ = static_cast<SmiPtr>(d.ReadRef());
       if (kind == Snapshot::kFullJIT) {
-        field->untag()->dependent_code_ = static_cast<ArrayPtr>(d.ReadRef());
+        field->untag()->dependent_code_ =
+            static_cast<WeakArrayPtr>(d.ReadRef());
       }
       field->untag()->token_pos_ = d.ReadTokenPosition();
       field->untag()->end_token_pos_ = d.ReadTokenPosition();
diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h
index 418422c..026767f 100644
--- a/runtime/vm/bootstrap_natives.h
+++ b/runtime/vm/bootstrap_natives.h
@@ -125,6 +125,7 @@
   V(StringBase_createFromCodePoints, 3)                                        \
   V(StringBase_substringUnchecked, 3)                                          \
   V(StringBase_joinReplaceAllResult, 4)                                        \
+  V(StringBase_intern, 1)                                                      \
   V(StringBuffer_createStringFromUint16Array, 3)                               \
   V(OneByteString_substringUnchecked, 3)                                       \
   V(OneByteString_allocateFromOneByteList, 3)                                  \
diff --git a/runtime/vm/compiler/backend/flow_graph.cc b/runtime/vm/compiler/backend/flow_graph.cc
index 7021d94..d340683 100644
--- a/runtime/vm/compiler/backend/flow_graph.cc
+++ b/runtime/vm/compiler/backend/flow_graph.cc
@@ -2514,9 +2514,10 @@
     if (auto join = block->AsJoinEntry()) {
       for (PhiIterator it(join); !it.Done(); it.Advance()) {
         PhiInstr* current = it.Current();
-        if (current->HasUnmatchedInputRepresentations()) {
+        if (current->HasUnmatchedInputRepresentations() &&
+            (current->SpeculativeModeOfInputs() == Instruction::kGuardInputs)) {
           // Can't canonicalize this instruction until all conversions for its
-          // inputs are inserted.
+          // speculative inputs are inserted.
           continue;
         }
 
@@ -2533,9 +2534,10 @@
     }
     for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
       Instruction* current = it.Current();
-      if (current->HasUnmatchedInputRepresentations()) {
+      if (current->HasUnmatchedInputRepresentations() &&
+          (current->SpeculativeModeOfInputs() == Instruction::kGuardInputs)) {
         // Can't canonicalize this instruction until all conversions for its
-        // inputs are inserted.
+        // speculative inputs are inserted.
         continue;
       }
 
@@ -2544,14 +2546,18 @@
       if (replacement != current) {
         // For non-definitions Canonicalize should return either NULL or
         // this.
-        ASSERT((replacement == nullptr) || current->IsDefinition());
-        if ((replacement != nullptr) && !unmatched_representations_allowed() &&
-            (replacement->representation() != current->representation()) &&
-            current->AsDefinition()->HasUses()) {
-          // Can't canonicalize this instruction as unmatched
-          // representations are not allowed at this point, but
-          // replacement has a different representation.
-          continue;
+        if (replacement != nullptr) {
+          ASSERT(current->IsDefinition());
+          if (!unmatched_representations_allowed()) {
+            RELEASE_ASSERT(!replacement->HasUnmatchedInputRepresentations());
+            if ((replacement->representation() != current->representation()) &&
+                current->AsDefinition()->HasUses()) {
+              // Can't canonicalize this instruction as unmatched
+              // representations are not allowed at this point, but
+              // replacement has a different representation.
+              continue;
+            }
+          }
         }
         ReplaceCurrentInstruction(&it, current, replacement);
         changed = true;
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 10b6951..6941847 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -3048,6 +3048,12 @@
     return value()->definition();
   }
 
+  // Fold away Box<rep>(v) if v has a target representation already.
+  Definition* value_defn = value()->definition();
+  if (value_defn->representation() == representation()) {
+    return value_defn;
+  }
+
   // Fold away Box<rep>(Unbox<rep>(v)) if value is known to be of the
   // right class.
   UnboxInstr* unbox_defn = value()->definition()->AsUnbox();
@@ -3071,6 +3077,12 @@
     return value()->definition();
   }
 
+  // Fold away Box<rep>(v) if v has a target representation already.
+  Definition* value_defn = value()->definition();
+  if (value_defn->representation() == representation()) {
+    return value_defn;
+  }
+
   return this;
 }
 
@@ -3116,6 +3128,12 @@
 Definition* UnboxInstr::Canonicalize(FlowGraph* flow_graph) {
   if (!HasUses() && !CanDeoptimize()) return NULL;
 
+  // Fold away Unbox<rep>(v) if v has a target representation already.
+  Definition* value_defn = value()->definition();
+  if (value_defn->representation() == representation()) {
+    return value_defn;
+  }
+
   // Fold away Unbox<rep>(Box<rep>(v)).
   BoxInstr* box_defn = value()->definition()->AsBox();
   if ((box_defn != NULL) &&
@@ -3141,6 +3159,12 @@
 Definition* UnboxIntegerInstr::Canonicalize(FlowGraph* flow_graph) {
   if (!HasUses() && !CanDeoptimize()) return NULL;
 
+  // Fold away Unbox<rep>(v) if v has a target representation already.
+  Definition* value_defn = value()->definition();
+  if (value_defn->representation() == representation()) {
+    return value_defn;
+  }
+
   // Do not attempt to fold this instruction if we have not matched
   // input/output representations yet.
   if (HasUnmatchedInputRepresentations()) {
diff --git a/runtime/vm/compiler/backend/il_test.cc b/runtime/vm/compiler/backend/il_test.cc
index 4593ea2..ddba4b6 100644
--- a/runtime/vm/compiler/backend/il_test.cc
+++ b/runtime/vm/compiler/backend/il_test.cc
@@ -1307,11 +1307,10 @@
 
   H.flow_graph()->Canonicalize();
 
-  EXPECT(add->InputAt(0)->definition() == unbox);
-
   if (allow_representation_change) {
-    EXPECT(unbox->value()->definition() == param);
+    EXPECT(add->InputAt(0)->definition() == param);
   } else {
+    EXPECT(add->InputAt(0)->definition() == unbox);
     EXPECT(unbox->value()->definition() == load);
   }
 }
diff --git a/runtime/vm/compiler/compiler_pass.cc b/runtime/vm/compiler/compiler_pass.cc
index 261a599..cd2f4ee 100644
--- a/runtime/vm/compiler/compiler_pass.cc
+++ b/runtime/vm/compiler/compiler_pass.cc
@@ -382,9 +382,9 @@
   INVOKE_PASS(AllocationSinking_Sink);
   INVOKE_PASS(EliminateDeadPhis);
   INVOKE_PASS(DCE);
+  INVOKE_PASS(Canonicalize);
   INVOKE_PASS(TypePropagation);
   INVOKE_PASS(SelectRepresentations_Final);
-  INVOKE_PASS(Canonicalize);
   INVOKE_PASS(UseTableDispatch);
   INVOKE_PASS(EliminateStackOverflowChecks);
   INVOKE_PASS(Canonicalize);
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.h b/runtime/vm/compiler/frontend/kernel_translation_helper.h
index 45fb844..f052c28 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.h
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.h
@@ -755,8 +755,11 @@
     kFlagMixinDeclaration = 1 << 4,
     kHasConstConstructor = 1 << 5,
     kIsMacro = 1 << 6,
-    kisSealed = 1 << 7,
+    kIsSealed = 1 << 7,
     kIsMixinClass = 1 << 8,
+    kIsBase = 1 << 9,
+    kIsInterface = 1 << 10,
+    kIsFinal = 1 << 11,
   };
 
   explicit ClassHelper(KernelReaderHelper* helper)
diff --git a/runtime/vm/heap/weak_code.cc b/runtime/vm/heap/weak_code.cc
index 5e59703..bea6f56 100644
--- a/runtime/vm/heap/weak_code.cc
+++ b/runtime/vm/heap/weak_code.cc
@@ -7,6 +7,7 @@
 #include "platform/assert.h"
 
 #include "vm/code_patcher.h"
+#include "vm/hash_table.h"
 #include "vm/object.h"
 #include "vm/runtime_entry.h"
 #include "vm/stack_frame.h"
@@ -14,65 +15,47 @@
 
 namespace dart {
 
+class CodeTraits {
+ public:
+  static const char* Name() { return "CodeTraits"; }
+  static bool ReportStats() { return false; }
+  static bool IsMatch(const Object& a, const Object& b) {
+    return a.ptr() == b.ptr();
+  }
+  static uword Hash(const Object& key) {
+    ASSERT(key.IsCode());
+    return Code::Cast(key).PayloadStart();
+  }
+};
+
+typedef UnorderedHashSet<CodeTraits, WeakArrayStorageTraits> WeakCodeSet;
+
 bool WeakCodeReferences::HasCodes() const {
   return !array_.IsNull() && (array_.Length() > 0);
 }
 
 void WeakCodeReferences::Register(const Code& value) {
-  if (!array_.IsNull()) {
-    // Try to find and reuse cleared WeakProperty to avoid allocating new one.
-    WeakProperty& weak_property = WeakProperty::Handle();
-    for (intptr_t i = 0; i < array_.Length(); i++) {
-      weak_property ^= array_.At(i);
-      if (weak_property.key() == Code::null()) {
-        // Empty property found. Reuse it.
-        weak_property.set_key(value);
-        return;
-      }
-    }
-  }
-
-  const WeakProperty& weak_property =
-      WeakProperty::Handle(WeakProperty::New(Heap::kOld));
-  weak_property.set_key(value);
-
-  intptr_t length = array_.IsNull() ? 0 : array_.Length();
-  const Array& new_array =
-      Array::Handle(Array::Grow(array_, length + 1, Heap::kOld));
-  new_array.SetAt(length, weak_property);
-  UpdateArrayTo(new_array);
-}
-
-bool WeakCodeReferences::IsOptimizedCode(const Array& dependent_code,
-                                         const Code& code) {
-  if (!code.is_optimized()) {
-    return false;
-  }
-  WeakProperty& weak_property = WeakProperty::Handle();
-  for (intptr_t i = 0; i < dependent_code.Length(); i++) {
-    weak_property ^= dependent_code.At(i);
-    if (code.ptr() == weak_property.key()) {
-      return true;
-    }
-  }
-  return false;
+  WeakCodeSet set(array_.IsNull() ? HashTables::New<WeakCodeSet>(4, Heap::kOld)
+                                  : array_.ptr());
+  set.Insert(value);
+  UpdateArrayTo(set.Release());
 }
 
 void WeakCodeReferences::DisableCode(bool are_mutators_stopped) {
-  Thread* thread = Thread::Current();
-  const Array& code_objects = Array::Handle(thread->zone(), array_.ptr());
 #if defined(DART_PRECOMPILED_RUNTIME)
-  ASSERT(code_objects.IsNull());
+  ASSERT(array_.IsNull());
   return;
 #else
   // Ensure mutators see empty code_objects only after code was deoptimized.
   DEBUG_ASSERT(
       IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
 
-  if (code_objects.IsNull()) {
+  if (array_.IsNull()) {
     return;
   }
 
+  WeakCodeSet set(array_.ptr());
+
   auto isolate_group = IsolateGroup::Current();
   auto disable_code_fun = [&]() {
     Code& code = Code::Handle();
@@ -84,7 +67,8 @@
           StackFrame* frame = iterator.NextFrame();
           while (frame != nullptr) {
             code = frame->LookupDartCode();
-            if (IsOptimizedCode(code_objects, code)) {
+
+            if (set.ContainsKey(code)) {
               ReportDeoptimization(code);
               DeoptimizeAt(mutator_thread, code, frame);
             }
@@ -94,12 +78,11 @@
         /*at_safepoint=*/true);
 
     // Switch functions that use dependent code to unoptimized code.
-    WeakProperty& weak_property = WeakProperty::Handle();
     Object& owner = Object::Handle();
     Function& function = Function::Handle();
-    for (intptr_t i = 0; i < code_objects.Length(); i++) {
-      weak_property ^= code_objects.At(i);
-      code ^= weak_property.key();
+    WeakCodeSet::Iterator it(&set);
+    while (it.MoveNext()) {
+      code ^= set.GetKey(it.Current());
       if (code.IsNull()) {
         // Code was garbage collected already.
         continue;
@@ -138,7 +121,7 @@
       }
     }
 
-    UpdateArrayTo(Object::null_array());
+    UpdateArrayTo(WeakArray::Handle());
   };
 
   // Deoptimize stacks and disable code (with mutators stopped if they are not
@@ -149,6 +132,8 @@
     isolate_group->RunWithStoppedMutators(disable_code_fun);
   }
 
+  set.Release();
+
 #endif  // defined(DART_PRECOMPILED_RUNTIME)
 }
 
diff --git a/runtime/vm/heap/weak_code.h b/runtime/vm/heap/weak_code.h
index e87ac5c..c042d84 100644
--- a/runtime/vm/heap/weak_code.h
+++ b/runtime/vm/heap/weak_code.h
@@ -10,30 +10,28 @@
 
 namespace dart {
 
-class Array;
+class WeakArray;
 class Code;
 
 // Helper class to handle an array of code weak properties. Implements
 // registration and disabling of stored code objects.
 class WeakCodeReferences : public ValueObject {
  public:
-  explicit WeakCodeReferences(const Array& value) : array_(value) {}
+  explicit WeakCodeReferences(const WeakArray& value) : array_(value) {}
   virtual ~WeakCodeReferences() {}
 
   void Register(const Code& value);
 
-  virtual void UpdateArrayTo(const Array& array) = 0;
+  virtual void UpdateArrayTo(const WeakArray& array) = 0;
   virtual void ReportDeoptimization(const Code& code) = 0;
   virtual void ReportSwitchingCode(const Code& code) = 0;
 
-  static bool IsOptimizedCode(const Array& dependent_code, const Code& code);
-
   void DisableCode(bool are_mutators_stopped);
 
   bool HasCodes() const;
 
  private:
-  const Array& array_;  // Array of Code objects.
+  const WeakArray& array_;  // Array of Code objects.
   DISALLOW_COPY_AND_ASSIGN(WeakCodeReferences);
 };
 
diff --git a/runtime/vm/kernel_binary.h b/runtime/vm/kernel_binary.h
index df3a933..ee44e82 100644
--- a/runtime/vm/kernel_binary.h
+++ b/runtime/vm/kernel_binary.h
@@ -18,7 +18,7 @@
 // package:kernel/binary.md.
 
 static const uint32_t kMagicProgramFile = 0x90ABCDEFu;
-static const uint32_t kSupportedKernelFormatVersion = 92;
+static const uint32_t kSupportedKernelFormatVersion = 93;
 
 // Keep in sync with package:kernel/lib/binary/tag.dart
 #define KERNEL_TAG_LIST(V)                                                     \
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index cccab21..0ec1cd4 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -4273,9 +4273,10 @@
 class CHACodeArray : public WeakCodeReferences {
  public:
   explicit CHACodeArray(const Class& cls)
-      : WeakCodeReferences(Array::Handle(cls.dependent_code())), cls_(cls) {}
+      : WeakCodeReferences(WeakArray::Handle(cls.dependent_code())),
+        cls_(cls) {}
 
-  virtual void UpdateArrayTo(const Array& value) {
+  virtual void UpdateArrayTo(const WeakArray& value) {
     // TODO(fschneider): Fails for classes in the VM isolate.
     cls_.set_dependent_code(value);
   }
@@ -4333,13 +4334,13 @@
   DisableCHAOptimizedCode(Class::Handle());
 }
 
-ArrayPtr Class::dependent_code() const {
+WeakArrayPtr Class::dependent_code() const {
   DEBUG_ASSERT(
       IsolateGroup::Current()->program_lock()->IsCurrentThreadReader());
   return untag()->dependent_code();
 }
 
-void Class::set_dependent_code(const Array& array) const {
+void Class::set_dependent_code(const WeakArray& array) const {
   DEBUG_ASSERT(
       IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
   untag()->set_dependent_code(array.ptr());
@@ -11623,13 +11624,13 @@
   return AccessorClosure(true);
 }
 
-ArrayPtr Field::dependent_code() const {
+WeakArrayPtr Field::dependent_code() const {
   DEBUG_ASSERT(
       IsolateGroup::Current()->program_lock()->IsCurrentThreadReader());
   return untag()->dependent_code();
 }
 
-void Field::set_dependent_code(const Array& array) const {
+void Field::set_dependent_code(const WeakArray& array) const {
   ASSERT(IsOriginal());
   DEBUG_ASSERT(
       IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
@@ -11639,10 +11640,10 @@
 class FieldDependentArray : public WeakCodeReferences {
  public:
   explicit FieldDependentArray(const Field& field)
-      : WeakCodeReferences(Array::Handle(field.dependent_code())),
+      : WeakCodeReferences(WeakArray::Handle(field.dependent_code())),
         field_(field) {}
 
-  virtual void UpdateArrayTo(const Array& value) {
+  virtual void UpdateArrayTo(const WeakArray& value) {
     field_.set_dependent_code(value);
   }
 
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 1047ce7..5931314 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -1781,8 +1781,8 @@
   // Return the list of code objects that were compiled using CHA of this class.
   // These code objects will be invalidated if new subclasses of this class
   // are finalized.
-  ArrayPtr dependent_code() const;
-  void set_dependent_code(const Array& array) const;
+  WeakArrayPtr dependent_code() const;
+  void set_dependent_code(const WeakArray& array) const;
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
   bool TraceAllocation(IsolateGroup* isolate_group) const;
@@ -4446,8 +4446,8 @@
   // assumptions about guarded class id and nullability of this field.
   // These code objects must be deoptimized when field's properties change.
   // Code objects are held weakly via an indirection through WeakProperty.
-  ArrayPtr dependent_code() const;
-  void set_dependent_code(const Array& array) const;
+  WeakArrayPtr dependent_code() const;
+  void set_dependent_code(const WeakArray& array) const;
 
   // Add the given code object to the list of dependent ones.
   void RegisterDependentCode(const Code& code) const;
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 267b369..46b6ec0 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -1009,7 +1009,7 @@
   // Stub code for allocation of instances.
   COMPRESSED_POINTER_FIELD(CodePtr, allocation_stub)
   // CHA optimized codes.
-  COMPRESSED_POINTER_FIELD(ArrayPtr, dependent_code)
+  COMPRESSED_POINTER_FIELD(WeakArrayPtr, dependent_code)
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
 #if defined(DART_PRECOMPILED_RUNTIME)
@@ -1447,7 +1447,7 @@
   // - for static fields: index into field_table.
   COMPRESSED_POINTER_FIELD(SmiPtr, host_offset_or_field_id)
   COMPRESSED_POINTER_FIELD(SmiPtr, guarded_list_length)
-  COMPRESSED_POINTER_FIELD(ArrayPtr, dependent_code)
+  COMPRESSED_POINTER_FIELD(WeakArrayPtr, dependent_code)
   CompressedObjectPtr* to_snapshot(Snapshot::Kind kind) {
     switch (kind) {
       case Snapshot::kFull:
diff --git a/sdk/lib/_internal/vm/lib/internal_patch.dart b/sdk/lib/_internal/vm/lib/internal_patch.dart
index 301a1f5..cd59773 100644
--- a/sdk/lib/_internal/vm/lib/internal_patch.dart
+++ b/sdk/lib/_internal/vm/lib/internal_patch.dart
@@ -431,3 +431,6 @@
   @FfiNative<Void Function(Handle, IntPtr)>('FinalizerEntry_SetExternalSize')
   external void setExternalSize(int externalSize);
 }
+
+@pragma("vm:external-name", "StringBase_intern")
+external String intern(String str);
diff --git a/tests/language/patterns/shared_case_variable_test.dart b/tests/language/patterns/shared_case_variable_test.dart
index 601319d..c726ee9 100644
--- a/tests/language/patterns/shared_case_variable_test.dart
+++ b/tests/language/patterns/shared_case_variable_test.dart
@@ -34,7 +34,7 @@
 
 /// OK if variables in different cases have different finality if not used in
 /// the body.
-Object differentFinalityNotUsedInBody(int caseKey, Object value) {
+Object? differentFinalityNotUsedInBody(int caseKey, Object value) {
   switch ((caseKey, value)) {
     case (0, int x) when _guard(x, 'int'):
     case (1, final int x) when _guard(x, 'final int'):
@@ -48,7 +48,7 @@
 
 /// OK if variables in different cases have different types if not used in the
 /// body.
-Object differentTypeNotUsedInBody(int caseKey, Object value) {
+Object? differentTypeNotUsedInBody(int caseKey, Object value) {
   switch ((caseKey, value)) {
     case (0, int x) when _guard(x, 'int'):
     case (1, bool x) when _guard(x, 'bool'):
@@ -61,7 +61,7 @@
 
 /// OK if some variables only exist in some cases if they aren't used in the
 /// body.
-Object differentVariableNotUsedInBody(int caseKey, Object value) {
+Object? differentVariableNotUsedInBody(int caseKey, Object value) {
   switch ((value, value)) {
     case (0, var unique) when _guard(unique, 'unique'):
     case (1, var inTwo) when _guard(inTwo, 'inTwo'):
@@ -74,7 +74,7 @@
 
 /// Variables can be shared if not all are annotated as long as the inferred
 /// type matches.
-Object sharedAnnotatedAndUnannotated(int caseKey, Object value) {
+Object? sharedAnnotatedAndUnannotated(int caseKey, Object value) {
   switch ((caseKey, value)) {
     case (0, var a) when _guard(a, 'var'):
     case (1, Object a) when _guard(a, 'Object'):
@@ -88,7 +88,7 @@
 
 /// Variables can be shared if not all are annotated as long as the type
 /// inferred using promotion matches.
-Object sharedAnnotatedAndPromotionInferred(int caseKey, Object value) {
+Object? sharedAnnotatedAndPromotionInferred(int caseKey, Object value) {
   // Promote value to int.
   if (value is int) {
     switch ((caseKey, value)) {
@@ -105,7 +105,7 @@
 
 /// Variables can be shared even when they occur in different contexts in their
 /// respective patterns.
-Object sharedDifferentContext(Object value) {
+Object? sharedDifferentContext(Object value) {
   // OK, since inferred type is same as annotated.
   switch (value) {
     case [int a, bool b] when _guard(a, 'list'):
@@ -122,8 +122,8 @@
 String _lastMatch = 'none';
 
 bool _guard(Object guardVariable, String label) {
-  if (result) _lastMatch = '$guardVariable: $label';
-  return result;
+  _lastMatch = '$guardVariable: $label';
+  return true;
 }
 
 String _matchedCase() {
diff --git a/tools/VERSION b/tools/VERSION
index e2239ca..20d31e6 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 3
 MINOR 0
 PATCH 0
-PRERELEASE 143
+PRERELEASE 144
 PRERELEASE_PATCH 0