Version 2.13.0-23.0.dev

Merge commit 'ee0c0bcd571efc7ada307b11700862d0a7a0a1c8' into 'dev'
diff --git a/pkg/_js_interop_checks/lib/js_interop_checks.dart b/pkg/_js_interop_checks/lib/js_interop_checks.dart
index 5bec8c4..4f3ba97 100644
--- a/pkg/_js_interop_checks/lib/js_interop_checks.dart
+++ b/pkg/_js_interop_checks/lib/js_interop_checks.dart
@@ -23,7 +23,7 @@
 
 import 'src/js_interop.dart';
 
-class JsInteropChecks extends RecursiveVisitor<void> {
+class JsInteropChecks extends RecursiveVisitor {
   final CoreTypes _coreTypes;
   final DiagnosticReporter<Message, LocatedMessage> _diagnosticsReporter;
   final Map<String, Class> _nativeClasses;
diff --git a/pkg/analysis_server/lib/src/status/diagnostics.dart b/pkg/analysis_server/lib/src/status/diagnostics.dart
index c64abed..0dee453 100644
--- a/pkg/analysis_server/lib/src/status/diagnostics.dart
+++ b/pkg/analysis_server/lib/src/status/diagnostics.dart
@@ -483,7 +483,7 @@
     buf.writeln(writeOption('Analysis options path',
         escape(driver.contextRoot.optionsFilePath ?? 'none')));
     buf.writeln(
-        writeOption('SDK root', escape(driver.analysisContext.sdkRoot.path)));
+        writeOption('SDK root', escape(driver.analysisContext.sdkRoot?.path)));
 
     buf.writeln('<div class="columns">');
 
diff --git a/pkg/analyzer/lib/src/dart/error/hint_codes.dart b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
index f55ecc1..7049407 100644
--- a/pkg/analyzer/lib/src/dart/error/hint_codes.dart
+++ b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
@@ -3008,7 +3008,7 @@
   static const HintCode UNUSED_LOCAL_VARIABLE = HintCode(
       'UNUSED_LOCAL_VARIABLE',
       "The value of the local variable '{0}' isn't used.",
-      correction: "Try removing the variable, or using it.",
+      correction: "Try removing the variable or using it.",
       hasPublishedDocs: true);
 
   /**
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
index 4c74215..6ba7bff 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
@@ -1453,7 +1453,7 @@
       expect(error.length, 2);
       expect(error.errorCode, HintCode.UNUSED_LOCAL_VARIABLE);
       expect(error.message, "The value of the local variable 'vv' isn't used.");
-      expect(error.correction, "Try removing the variable, or using it.");
+      expect(error.correction, "Try removing the variable or using it.");
     }
   }
 
diff --git a/pkg/analyzer/test/src/dart/analysis/index_test.dart b/pkg/analyzer/test/src/dart/analysis/index_test.dart
index 8c190aa..080532a 100644
--- a/pkg/analyzer/test/src/dart/analysis/index_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/index_test.dart
@@ -34,10 +34,7 @@
 }
 
 @reflectiveTest
-class IndexTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin, _IndexMixin, WithNonFunctionTypeAliasesMixin {
-  // TODO(https://github.com/dart-lang/sdk/issues/44666): Use null safety in
-  //  test cases.
+class IndexTest extends PubPackageResolutionTest with _IndexMixin {
   test_fieldFormalParameter_noSuchField() async {
     await _indexTestUnit('''
 class B<T> {
diff --git a/pkg/analyzer/test/src/dart/analysis/search_test.dart b/pkg/analyzer/test/src/dart/analysis/search_test.dart
index 6a0844c..b27723e 100644
--- a/pkg/analyzer/test/src/dart/analysis/search_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/search_test.dart
@@ -60,8 +60,7 @@
 }
 
 @reflectiveTest
-class SearchTest extends PubPackageResolutionTest
-    with WithNonFunctionTypeAliasesMixin {
+class SearchTest extends PubPackageResolutionTest {
   AnalysisDriver get driver => driverFor(testFilePath);
 
   CompilationUnitElement get resultUnitElement => result.unit!.declaredElement!;
diff --git a/pkg/analyzer/test/src/dart/constant/potentially_constant_test.dart b/pkg/analyzer/test/src/dart/constant/potentially_constant_test.dart
index 529c2fb..9805e89f 100644
--- a/pkg/analyzer/test/src/dart/constant/potentially_constant_test.dart
+++ b/pkg/analyzer/test/src/dart/constant/potentially_constant_test.dart
@@ -15,6 +15,7 @@
     defineReflectiveTests(IsPotentiallyConstantTypeExpressionTest);
     defineReflectiveTests(PotentiallyConstantTest);
     defineReflectiveTests(PotentiallyConstantWithNonFunctionTypeAliasesTest);
+    defineReflectiveTests(PotentiallyConstantWithoutNullSafetyTest);
   });
 }
 
@@ -179,10 +180,7 @@
 }
 
 @reflectiveTest
-class PotentiallyConstantTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
-  // TODO(https://github.com/dart-lang/sdk/issues/44666): Use null safety in
-  //  test cases.
+class PotentiallyConstantTest extends PubPackageResolutionTest {
   test_adjacentStrings() async {
     await _assertConst(r'''
 var x = 'a' 'b';
@@ -204,14 +202,14 @@
   }
 
   test_asExpression_typeParameter() async {
-    await _assertNotConst(r'''
+    await _assertConst(r'''
 const a = 0;
 class A<T> {
   m() {
     var x = a as T;
   }
 }
-''', () => _xInitializer(), () => [findNode.typeName('T;')]);
+''', () => _xInitializer());
   }
 
   test_conditional() async {
@@ -300,14 +298,14 @@
   }
 
   test_isExpression_typeParameter() async {
-    await _assertNotConst(r'''
+    await _assertConst(r'''
 const a = 0;
 class A<T> {
   m() {
     var x = a is T;
   }
 }
-''', () => _xInitializer(), () => [findNode.typeName('T;')]);
+''', () => _xInitializer());
   }
 
   test_listLiteral() async {
@@ -927,9 +925,11 @@
   }
 }
 
+/// TODO(https://github.com/dart-lang/sdk/issues/44666): Combine this class
+/// with the one it extends.
 @reflectiveTest
 class PotentiallyConstantWithNonFunctionTypeAliasesTest
-    extends PotentiallyConstantTest with WithNonFunctionTypeAliasesMixin {
+    extends PotentiallyConstantTest {
   @override
   test_asExpression_typeParameter() async {
     await _assertConst(r'''
@@ -993,3 +993,46 @@
 ''', () => _xInitializer());
   }
 }
+
+@reflectiveTest
+class PotentiallyConstantWithoutNullSafetyTest extends PubPackageResolutionTest
+    with WithoutNullSafetyMixin {
+  test_asExpression_typeParameter() async {
+    await _assertNotConst(r'''
+const a = 0;
+class A<T> {
+  m() {
+    var x = a as T;
+  }
+}
+''', () => _xInitializer(), () => [findNode.typeName('T;')]);
+  }
+
+  test_isExpression_typeParameter() async {
+    await _assertNotConst(r'''
+const a = 0;
+class A<T> {
+  m() {
+    var x = a is T;
+  }
+}
+''', () => _xInitializer(), () => [findNode.typeName('T;')]);
+  }
+
+  _assertNotConst(String code, AstNode Function() getNode,
+      List<AstNode> Function() getNotConstList) async {
+    await resolveTestCode(code);
+    var node = getNode();
+    var notConstList = getNotPotentiallyConstants(
+      node,
+      isNonNullableByDefault: typeSystem.isNonNullableByDefault,
+    );
+
+    var expectedNotConst = getNotConstList();
+    expect(notConstList, unorderedEquals(expectedNotConst));
+  }
+
+  Expression _xInitializer() {
+    return findNode.variableDeclaration('x = ').initializer!;
+  }
+}
diff --git a/pkg/analyzer/test/src/dart/resolution/assignment_test.dart b/pkg/analyzer/test/src/dart/resolution/assignment_test.dart
index 5a639d1..3b6e492 100644
--- a/pkg/analyzer/test/src/dart/resolution/assignment_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/assignment_test.dart
@@ -2385,9 +2385,11 @@
   }
 }
 
+/// TODO(https://github.com/dart-lang/sdk/issues/44666): Combine this class
+/// with the one above.
 @reflectiveTest
 class AssignmentDriverResolutionWithNonFunctionTypeAliasesTest
-    extends PubPackageResolutionTest with WithNonFunctionTypeAliasesMixin {
+    extends PubPackageResolutionTest {
   test_prefixedIdentifier_typeAlias_static_compound() async {
     await assertNoErrorsInCode(r'''
 class A {
diff --git a/pkg/analyzer/test/src/dart/resolution/ast_rewrite_test.dart b/pkg/analyzer/test/src/dart/resolution/ast_rewrite_test.dart
index ab1f6c6..2fb3462 100644
--- a/pkg/analyzer/test/src/dart/resolution/ast_rewrite_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/ast_rewrite_test.dart
@@ -391,8 +391,7 @@
 
 @reflectiveTest
 class AstRewriteMethodInvocationWithNonFunctionTypeAliasesTest
-    extends PubPackageResolutionTest
-    with WithNonFunctionTypeAliasesMixin, AstRewriteMethodInvocationTestCases {
+    extends PubPackageResolutionTest with AstRewriteMethodInvocationTestCases {
   test_targetNull_typeAlias_interfaceType() async {
     await assertNoErrorsInCode(r'''
 class A<T, U> {
diff --git a/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart b/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
index ab0d50f..1aba2dd 100644
--- a/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
+++ b/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
@@ -247,7 +247,7 @@
   String get testFilePath => '$testPackageLibPath/test.dart';
 
   /// The language version to use by default for `package:test`.
-  String? get testPackageLanguageVersion => '2.12';
+  String? get testPackageLanguageVersion => null;
 
   String get testPackageLibPath => '$testPackageRootPath/lib';
 
@@ -261,6 +261,11 @@
   @override
   void setUp() {
     super.setUp();
+    writeTestPackageAnalysisOptionsFile(
+      AnalysisOptionsFileConfig(
+        experiments: [EnableString.nonfunction_type_aliases],
+      ),
+    );
     writeTestPackageConfig(
       PackageConfigFileBuilder(),
     );
@@ -345,23 +350,6 @@
   }
 }
 
-mixin WithNonFunctionTypeAliasesMixin on PubPackageResolutionTest {
-  @override
-  String? get testPackageLanguageVersion => null;
-
-  @nonVirtual
-  @override
-  void setUp() {
-    super.setUp();
-
-    writeTestPackageAnalysisOptionsFile(
-      AnalysisOptionsFileConfig(
-        experiments: [EnableString.nonfunction_type_aliases],
-      ),
-    );
-  }
-}
-
 mixin WithNullSafetyMixin on PubPackageResolutionTest {
   // TODO(https://github.com/dart-lang/sdk/issues/44666): This mixin is a no-op
   // on PubPackageResolutionTest; remove its usage and remove it.
diff --git a/pkg/analyzer/test/src/dart/resolution/generic_type_alias_test.dart b/pkg/analyzer/test/src/dart/resolution/generic_type_alias_test.dart
index 0decdba..d048c4b 100644
--- a/pkg/analyzer/test/src/dart/resolution/generic_type_alias_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/generic_type_alias_test.dart
@@ -121,7 +121,6 @@
   F.a;
 }
 ''', [
-      error(ParserErrorCode.INVALID_GENERIC_FUNCTION_TYPE, 13, 1),
       error(ParserErrorCode.EXPECTED_TYPE_NAME, 15, 1),
       error(CompileTimeErrorCode.UNDEFINED_CLASS, 15, 0),
       error(CompileTimeErrorCode.UNDEFINED_GETTER, 33, 1),
diff --git a/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart b/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart
index 8092ff1..e9319da 100644
--- a/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart
@@ -2447,9 +2447,11 @@
   }
 }
 
+/// TODO(https://github.com/dart-lang/sdk/issues/44666): Combine this class
+/// with the one it extends.
 @reflectiveTest
 class MethodInvocationResolutionWithNonFunctionTypeAliasesTest
-    extends PubPackageResolutionTest with WithNonFunctionTypeAliasesMixin {
+    extends PubPackageResolutionTest {
   test_hasReceiver_typeAlias_staticMethod() async {
     await assertNoErrorsInCode(r'''
 class A {
diff --git a/pkg/analyzer/test/src/dart/resolution/prefixed_identifier_test.dart b/pkg/analyzer/test/src/dart/resolution/prefixed_identifier_test.dart
index 24598f7..e1fcbcc 100644
--- a/pkg/analyzer/test/src/dart/resolution/prefixed_identifier_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/prefixed_identifier_test.dart
@@ -290,7 +290,7 @@
 
 @reflectiveTest
 class PrefixedIdentifierResolutionWithNonFunctionTypeAliasesTest
-    extends PubPackageResolutionTest with WithNonFunctionTypeAliasesMixin {
+    extends PubPackageResolutionTest {
   test_hasReceiver_typeAlias_staticGetter() async {
     await assertNoErrorsInCode(r'''
 class A {
diff --git a/pkg/analyzer/test/src/dart/resolution/type_name_test.dart b/pkg/analyzer/test/src/dart/resolution/type_name_test.dart
index baab335..bdd17d5 100644
--- a/pkg/analyzer/test/src/dart/resolution/type_name_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/type_name_test.dart
@@ -293,7 +293,7 @@
 
 @reflectiveTest
 class TypeNameResolutionWithNonFunctionTypeAliasesTest
-    extends PubPackageResolutionTest with WithNonFunctionTypeAliasesMixin {
+    extends PubPackageResolutionTest {
   test_typeAlias_asInstanceCreation_explicitNew_typeArguments_interfaceType_none() async {
     await assertNoErrorsInCode(r'''
 class A<T> {}
diff --git a/pkg/analyzer/test/src/diagnostics/assignment_to_type_test.dart b/pkg/analyzer/test/src/diagnostics/assignment_to_type_test.dart
index a8133c3..cee31f2 100644
--- a/pkg/analyzer/test/src/diagnostics/assignment_to_type_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/assignment_to_type_test.dart
@@ -14,8 +14,7 @@
 }
 
 @reflectiveTest
-class AssignmentToTypeTest extends PubPackageResolutionTest
-    with WithNonFunctionTypeAliasesMixin {
+class AssignmentToTypeTest extends PubPackageResolutionTest {
   test_class() async {
     await assertErrorsInCode('''
 class C {}
diff --git a/pkg/analyzer/test/src/diagnostics/built_in_identifier_as_typedef_name_test.dart b/pkg/analyzer/test/src/diagnostics/built_in_identifier_as_typedef_name_test.dart
index 5e344f1..b18076c 100644
--- a/pkg/analyzer/test/src/diagnostics/built_in_identifier_as_typedef_name_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/built_in_identifier_as_typedef_name_test.dart
@@ -43,4 +43,13 @@
       error(ParserErrorCode.EXPECTED_IDENTIFIER_BUT_GOT_KEYWORD, 8, 2)
     ]);
   }
+
+  test_typedef_interfaceType() async {
+    await assertErrorsInCode(r'''
+typedef as = List<int>;
+''', [
+      error(CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME, 8, 2),
+      error(ParserErrorCode.EXPECTED_IDENTIFIER_BUT_GOT_KEYWORD, 8, 2)
+    ]);
+  }
 }
diff --git a/pkg/analyzer/test/src/diagnostics/case_expression_type_is_not_switch_expression_subtype_test.dart b/pkg/analyzer/test/src/diagnostics/case_expression_type_is_not_switch_expression_subtype_test.dart
index dbe659f..a958823 100644
--- a/pkg/analyzer/test/src/diagnostics/case_expression_type_is_not_switch_expression_subtype_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/case_expression_type_is_not_switch_expression_subtype_test.dart
@@ -9,13 +9,13 @@
 
 main() {
   defineReflectiveSuite(() {
-    defineReflectiveTests(CaseExpressionTypeIsNotSswitchExpressionSubtype);
+    defineReflectiveTests(CaseExpressionTypeIsNotSwitchExpressionSubtype);
   });
 }
 
 @reflectiveTest
-class CaseExpressionTypeIsNotSswitchExpressionSubtype
-    extends PubPackageResolutionTest with WithNullSafetyMixin {
+class CaseExpressionTypeIsNotSwitchExpressionSubtype
+    extends PubPackageResolutionTest {
   CompileTimeErrorCode get _errorCode {
     return CompileTimeErrorCode
         .CASE_EXPRESSION_TYPE_IS_NOT_SWITCH_EXPRESSION_SUBTYPE;
diff --git a/pkg/analyzer/test/src/diagnostics/conflicting_generic_interfaces_test.dart b/pkg/analyzer/test/src/diagnostics/conflicting_generic_interfaces_test.dart
index 478f092..d86c1cf 100644
--- a/pkg/analyzer/test/src/diagnostics/conflicting_generic_interfaces_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/conflicting_generic_interfaces_test.dart
@@ -14,8 +14,7 @@
 }
 
 @reflectiveTest
-class ConflictingGenericInterfacesTest extends PubPackageResolutionTest
-    with WithNonFunctionTypeAliasesMixin {
+class ConflictingGenericInterfacesTest extends PubPackageResolutionTest {
   test_class_extends_implements() async {
     await assertErrorsInCode('''
 class I<T> {}
diff --git a/pkg/analyzer/test/src/diagnostics/const_deferred_class_test.dart b/pkg/analyzer/test/src/diagnostics/const_deferred_class_test.dart
index 7a02553..31e9d07 100644
--- a/pkg/analyzer/test/src/diagnostics/const_deferred_class_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/const_deferred_class_test.dart
@@ -31,6 +31,25 @@
     ]);
   }
 
+  test_nonFunctionTypedef() async {
+    newFile('$testPackageLibPath/lib1.dart', content: '''
+library lib1;
+class A {
+  const A();
+}
+typedef B = A;
+''');
+    await assertErrorsInCode('''
+library root;
+import 'lib1.dart' deferred as a;
+main() {
+  const a.B();
+}
+''', [
+      error(CompileTimeErrorCode.CONST_DEFERRED_CLASS, 65, 3),
+    ]);
+  }
+
   test_unnamed() async {
     newFile('$testPackageLibPath/lib1.dart', content: '''
 library lib1;
diff --git a/pkg/analyzer/test/src/diagnostics/const_initialized_with_non_constant_value_test.dart b/pkg/analyzer/test/src/diagnostics/const_initialized_with_non_constant_value_test.dart
index 6b2096d..4776e89 100644
--- a/pkg/analyzer/test/src/diagnostics/const_initialized_with_non_constant_value_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/const_initialized_with_non_constant_value_test.dart
@@ -107,9 +107,11 @@
   }
 }
 
+/// TODO(https://github.com/dart-lang/sdk/issues/44666): Combine this class
+/// with the one above it.
 @reflectiveTest
 class ConstInitializedWithNonConstantValueWithNonFunctionTypeAliasesTest
-    extends PubPackageResolutionTest with WithNonFunctionTypeAliasesMixin {
+    extends PubPackageResolutionTest {
   test_typeLiteral_interfaceType() async {
     await assertNoErrorsInCode(r'''
 const a = int;
diff --git a/pkg/analyzer/test/src/diagnostics/const_with_undefined_constructor_test.dart b/pkg/analyzer/test/src/diagnostics/const_with_undefined_constructor_test.dart
index eb83413..ee5737d 100644
--- a/pkg/analyzer/test/src/diagnostics/const_with_undefined_constructor_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/const_with_undefined_constructor_test.dart
@@ -28,6 +28,21 @@
     ]);
   }
 
+  test_nonFunctionTypedef() async {
+    await assertErrorsInCode(r'''
+class A {
+  const A.name();
+}
+typedef B = A;
+f() {
+  return const B();
+}
+''', [
+      error(
+          CompileTimeErrorCode.CONST_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT, 66, 1),
+    ]);
+  }
+
   test_unnamed() async {
     await assertErrorsInCode(r'''
 class A {
diff --git a/pkg/analyzer/test/src/diagnostics/could_not_infer_test.dart b/pkg/analyzer/test/src/diagnostics/could_not_infer_test.dart
index 46783f7..e917a35 100644
--- a/pkg/analyzer/test/src/diagnostics/could_not_infer_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/could_not_infer_test.dart
@@ -251,6 +251,8 @@
   }
 }
 
+/// TODO(https://github.com/dart-lang/sdk/issues/44078): Add tests with
+/// non-function typedefs.
 @reflectiveTest
 class CouldNotInferWithNullSafetyTest extends PubPackageResolutionTest
     with WithNullSafetyMixin {
diff --git a/pkg/analyzer/test/src/diagnostics/duplicate_definition_test.dart b/pkg/analyzer/test/src/diagnostics/duplicate_definition_test.dart
index 72c03f2..66df7dcab 100644
--- a/pkg/analyzer/test/src/diagnostics/duplicate_definition_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/duplicate_definition_test.dart
@@ -936,8 +936,7 @@
 }
 
 @reflectiveTest
-class DuplicateDefinitionTest extends PubPackageResolutionTest
-    with WithNonFunctionTypeAliasesMixin {
+class DuplicateDefinitionTest extends PubPackageResolutionTest {
   test_catch() async {
     await assertErrorsInCode(r'''
 main() {
@@ -1130,6 +1129,22 @@
     ]);
   }
 
+  test_typeParameters_genericTypedef_functionType() async {
+    await assertErrorsInCode(r'''
+typedef F<T, T> = void Function();
+''', [
+      error(CompileTimeErrorCode.DUPLICATE_DEFINITION, 13, 1),
+    ]);
+  }
+
+  test_typeParameters_genericTypedef_interfaceType() async {
+    await assertErrorsInCode(r'''
+typedef F<T, T> = Map;
+''', [
+      error(CompileTimeErrorCode.DUPLICATE_DEFINITION, 13, 1),
+    ]);
+  }
+
   test_typeParameters_method() async {
     await assertErrorsInCode(r'''
 class A {
diff --git a/pkg/analyzer/test/src/diagnostics/duplicate_named_argument_test.dart b/pkg/analyzer/test/src/diagnostics/duplicate_named_argument_test.dart
index 3fceecb..cb69a3a 100644
--- a/pkg/analyzer/test/src/diagnostics/duplicate_named_argument_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/duplicate_named_argument_test.dart
@@ -15,7 +15,34 @@
 
 @reflectiveTest
 class DuplicateNamedArgumentTest extends PubPackageResolutionTest {
-  test_duplicate_named_argument() async {
+  test_constructor() async {
+    await assertErrorsInCode(r'''
+class C {
+  C({int? a, int? b});
+}
+main() {
+  C(a: 1, a: 2);
+}
+''', [
+      error(CompileTimeErrorCode.DUPLICATE_NAMED_ARGUMENT, 54, 1),
+    ]);
+  }
+
+  test_constructor_nonFunctionTypedef() async {
+    await assertErrorsInCode(r'''
+class C {
+  C({int? a, int? b});
+}
+typedef D = C;
+main() {
+  D(a: 1, a: 2);
+}
+''', [
+      error(CompileTimeErrorCode.DUPLICATE_NAMED_ARGUMENT, 69, 1),
+    ]);
+  }
+
+  test_function() async {
     await assertErrorsInCode(r'''
 f({a, b}) {}
 main() {
diff --git a/pkg/analyzer/test/src/diagnostics/extends_deferred_class_test.dart b/pkg/analyzer/test/src/diagnostics/extends_deferred_class_test.dart
index 11c16ab..effaba0 100644
--- a/pkg/analyzer/test/src/diagnostics/extends_deferred_class_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/extends_deferred_class_test.dart
@@ -43,4 +43,19 @@
       error(CompileTimeErrorCode.EXTENDS_DEFERRED_CLASS, 64, 3),
     ]);
   }
+
+  test_extends_deferred_interfaceTypeTypedef() async {
+    newFile('$testPackageLibPath/lib1.dart', content: '''
+library lib1;
+class A {}
+class B {}
+''');
+    await assertErrorsInCode('''
+library root;
+import 'lib1.dart' deferred as a;
+class B extends a.B {}
+''', [
+      error(CompileTimeErrorCode.EXTENDS_DEFERRED_CLASS, 64, 3),
+    ]);
+  }
 }
diff --git a/pkg/analyzer/test/src/diagnostics/extends_disallowed_class_test.dart b/pkg/analyzer/test/src/diagnostics/extends_disallowed_class_test.dart
index 7102a8a..aaba875 100644
--- a/pkg/analyzer/test/src/diagnostics/extends_disallowed_class_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/extends_disallowed_class_test.dart
@@ -49,6 +49,16 @@
     ]);
   }
 
+  test_class_FutureOr_typedef() async {
+    await assertErrorsInCode('''
+import 'dart:async';
+typedef F = FutureOr<void>;
+class A extends F {}
+''', [
+      error(CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS, 65, 1),
+    ]);
+  }
+
   test_class_FutureOr_typeVariable() async {
     await assertErrorsInCode('''
 import 'dart:async';
diff --git a/pkg/analyzer/test/src/diagnostics/extra_positional_arguments_test.dart b/pkg/analyzer/test/src/diagnostics/extra_positional_arguments_test.dart
index 3255aff..0181e0d 100644
--- a/pkg/analyzer/test/src/diagnostics/extra_positional_arguments_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/extra_positional_arguments_test.dart
@@ -46,6 +46,21 @@
     ]);
   }
 
+  test_constConstructor_typedef() async {
+    await assertErrorsInCode(r'''
+class A {
+  const A({int x = 0});
+}
+typedef B = A;
+main() {
+  const B(0);
+}
+''', [
+      error(CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS_COULD_BE_NAMED, 70,
+          1),
+    ]);
+  }
+
   test_functionExpressionInvocation() async {
     await assertErrorsInCode('''
 main() {
diff --git a/pkg/analyzer/test/src/diagnostics/instantiate_type_alias_expands_to_type_parameter_test.dart b/pkg/analyzer/test/src/diagnostics/instantiate_type_alias_expands_to_type_parameter_test.dart
index 0cb16f19..9355110 100644
--- a/pkg/analyzer/test/src/diagnostics/instantiate_type_alias_expands_to_type_parameter_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/instantiate_type_alias_expands_to_type_parameter_test.dart
@@ -15,7 +15,7 @@
 
 @reflectiveTest
 class InstantiateTypeAliasExpandsToTypeParameterTest
-    extends PubPackageResolutionTest with WithNonFunctionTypeAliasesMixin {
+    extends PubPackageResolutionTest {
   CompileTimeErrorCode get _errorCode =>
       CompileTimeErrorCode.INSTANTIATE_TYPE_ALIAS_EXPANDS_TO_TYPE_PARAMETER;
 
diff --git a/pkg/analyzer/test/src/diagnostics/nullable_type_in_extends_clause_test.dart b/pkg/analyzer/test/src/diagnostics/nullable_type_in_extends_clause_test.dart
index aab024e..3738aa7 100644
--- a/pkg/analyzer/test/src/diagnostics/nullable_type_in_extends_clause_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/nullable_type_in_extends_clause_test.dart
@@ -14,8 +14,7 @@
 }
 
 @reflectiveTest
-class NullableTypeInExtendsClauseTest extends PubPackageResolutionTest
-    with WithNonFunctionTypeAliasesMixin {
+class NullableTypeInExtendsClauseTest extends PubPackageResolutionTest {
   test_class_nonNullable() async {
     await assertNoErrorsInCode('''
 class A {}
diff --git a/pkg/analyzer/test/src/diagnostics/nullable_type_in_implements_clause_test.dart b/pkg/analyzer/test/src/diagnostics/nullable_type_in_implements_clause_test.dart
index 2238940..10c2fcf 100644
--- a/pkg/analyzer/test/src/diagnostics/nullable_type_in_implements_clause_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/nullable_type_in_implements_clause_test.dart
@@ -14,8 +14,7 @@
 }
 
 @reflectiveTest
-class NullableTypeInImplementsClauseTest extends PubPackageResolutionTest
-    with WithNonFunctionTypeAliasesMixin {
+class NullableTypeInImplementsClauseTest extends PubPackageResolutionTest {
   test_class_nonNullable() async {
     await assertNoErrorsInCode('''
 class A {}
diff --git a/pkg/analyzer/test/src/diagnostics/nullable_type_in_on_clause_test.dart b/pkg/analyzer/test/src/diagnostics/nullable_type_in_on_clause_test.dart
index d987fba..da6d102 100644
--- a/pkg/analyzer/test/src/diagnostics/nullable_type_in_on_clause_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/nullable_type_in_on_clause_test.dart
@@ -13,9 +13,10 @@
   });
 }
 
+/// TODO(https://github.com/dart-lang/sdk/issues/44666): Combine this class
+/// with the one above it.
 @reflectiveTest
-class NullableTypeInOnClauseTest extends PubPackageResolutionTest
-    with WithNonFunctionTypeAliasesMixin {
+class NullableTypeInOnClauseTest extends PubPackageResolutionTest {
   test_nonNullable() async {
     await assertNoErrorsInCode('''
 class A {}
diff --git a/pkg/analyzer/test/src/diagnostics/nullable_type_in_with_clause_test.dart b/pkg/analyzer/test/src/diagnostics/nullable_type_in_with_clause_test.dart
index ea7d118..785fe6f 100644
--- a/pkg/analyzer/test/src/diagnostics/nullable_type_in_with_clause_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/nullable_type_in_with_clause_test.dart
@@ -14,8 +14,7 @@
 }
 
 @reflectiveTest
-class NullableTypeInWithClauseTest extends PubPackageResolutionTest
-    with WithNonFunctionTypeAliasesMixin {
+class NullableTypeInWithClauseTest extends PubPackageResolutionTest {
   test_class_nonNullable() async {
     await assertNoErrorsInCode('''
 class A {}
diff --git a/pkg/analyzer/test/src/diagnostics/redirect_to_type_alias_expands_to_type_parameter_test.dart b/pkg/analyzer/test/src/diagnostics/redirect_to_type_alias_expands_to_type_parameter_test.dart
index dbe1342..e9dba90 100644
--- a/pkg/analyzer/test/src/diagnostics/redirect_to_type_alias_expands_to_type_parameter_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/redirect_to_type_alias_expands_to_type_parameter_test.dart
@@ -15,7 +15,7 @@
 
 @reflectiveTest
 class RedirectTypeAliasExpandsToTypeParameterTest
-    extends PubPackageResolutionTest with WithNonFunctionTypeAliasesMixin {
+    extends PubPackageResolutionTest {
   test_generic_typeParameter_withArgument_named() async {
     await assertErrorsInCode(r'''
 class A implements C {
diff --git a/pkg/analyzer/test/src/diagnostics/switch_expression_not_assignable_test.dart b/pkg/analyzer/test/src/diagnostics/switch_expression_not_assignable_test.dart
index 91b2978..564d92f 100644
--- a/pkg/analyzer/test/src/diagnostics/switch_expression_not_assignable_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/switch_expression_not_assignable_test.dart
@@ -16,8 +16,6 @@
 @reflectiveTest
 class SwitchExpressionNotAssignableTest extends PubPackageResolutionTest
     with WithoutNullSafetyMixin {
-  // TODO(https://github.com/dart-lang/sdk/issues/44666): Use null safety in
-  //  test cases.
   test_simple() async {
     await assertErrorsInCode('''
 f(int p) {
diff --git a/pkg/analyzer/test/src/diagnostics/type_alias_cannot_reference_itself_test.dart b/pkg/analyzer/test/src/diagnostics/type_alias_cannot_reference_itself_test.dart
index 7b85a9e..831cfaa 100644
--- a/pkg/analyzer/test/src/diagnostics/type_alias_cannot_reference_itself_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/type_alias_cannot_reference_itself_test.dart
@@ -162,9 +162,11 @@
   }
 }
 
+/// TODO(https://github.com/dart-lang/sdk/issues/44666): Combine this class
+/// with the one it extends.
 @reflectiveTest
 class TypeAliasCannotReferenceItselfWithNonFunctionTypeAliasesTest
-    extends PubPackageResolutionTest with WithNonFunctionTypeAliasesMixin {
+    extends PubPackageResolutionTest {
   test_nonFunction_aliasedType_cycleOf2() async {
     await assertErrorsInCode('''
 typedef T1 = T2;
diff --git a/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart b/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart
index 0a010bf..b33e321 100644
--- a/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart
@@ -418,12 +418,12 @@
   }
 }
 
+/// TODO(https://github.com/dart-lang/sdk/issues/44666): Combine this class
+/// with the one it extends.
 @reflectiveTest
 class TypeArgumentNotMatchingBoundsWithNonFunctionTypeAliasesTest
     extends PubPackageResolutionTest
-    with
-        WithNonFunctionTypeAliasesMixin,
-        TypeArgumentNotMatchingBoundsTestCases {
+    with TypeArgumentNotMatchingBoundsTestCases {
   test_nonFunctionTypeAlias_interfaceType_parameter() async {
     await assertErrorsInCode(r'''
 class A {}
diff --git a/pkg/analyzer/test/src/diagnostics/undefined_method_test.dart b/pkg/analyzer/test/src/diagnostics/undefined_method_test.dart
index 0114920..34c7247 100644
--- a/pkg/analyzer/test/src/diagnostics/undefined_method_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/undefined_method_test.dart
@@ -14,8 +14,7 @@
 }
 
 @reflectiveTest
-class UndefinedMethodTest extends PubPackageResolutionTest
-    with WithNonFunctionTypeAliasesMixin {
+class UndefinedMethodTest extends PubPackageResolutionTest {
   test_constructor_defined() async {
     await assertNoErrorsInCode(r'''
 class C {
diff --git a/pkg/analyzer/test/src/diagnostics/unused_element_test.dart b/pkg/analyzer/test/src/diagnostics/unused_element_test.dart
index de000a1..de7dc23 100644
--- a/pkg/analyzer/test/src/diagnostics/unused_element_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/unused_element_test.dart
@@ -1578,9 +1578,11 @@
   }
 }
 
+/// TODO(https://github.com/dart-lang/sdk/issues/44666): Combine this class
+/// with the one it extends.
 @reflectiveTest
 class UnusedElementWithNonFunctionTypeAliasesTest
-    extends PubPackageResolutionTest with WithNonFunctionTypeAliasesMixin {
+    extends PubPackageResolutionTest {
   test_typeAlias_interfaceType_isUsed_typeName_isExpression() async {
     await assertNoErrorsInCode(r'''
 typedef _A = List<int>;
diff --git a/pkg/compiler/lib/src/inferrer/builder_kernel.dart b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
index 337c547..1a91ae1 100644
--- a/pkg/compiler/lib/src/inferrer/builder_kernel.dart
+++ b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
@@ -37,7 +37,8 @@
 /// Calling [run] will start the work of visiting the body of the code to
 /// construct a set of inference-nodes that abstractly represent what the code
 /// is doing.
-class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> {
+class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation>
+    with ir.VisitorNullMixin<TypeInformation> {
   final CompilerOptions _options;
   final JsClosedWorld _closedWorld;
   final InferrerEngine _inferrer;
diff --git a/pkg/compiler/lib/src/ir/debug.dart b/pkg/compiler/lib/src/ir/debug.dart
index 01fc1f0..8e2adfa 100644
--- a/pkg/compiler/lib/src/ir/debug.dart
+++ b/pkg/compiler/lib/src/ir/debug.dart
@@ -11,7 +11,8 @@
 
 import '../util/util.dart' show Indentation, Tagging;
 
-class DebugPrinter extends Visitor with Indentation, Tagging<Node> {
+class DebugPrinter extends Visitor<void>
+    with Indentation, Tagging<Node>, VisitorVoidMixin {
   @override
   StringBuffer sb = new StringBuffer();
 
diff --git a/pkg/compiler/lib/src/ir/scope_visitor.dart b/pkg/compiler/lib/src/ir/scope_visitor.dart
index d2f8225..e5ff326 100644
--- a/pkg/compiler/lib/src/ir/scope_visitor.dart
+++ b/pkg/compiler/lib/src/ir/scope_visitor.dart
@@ -14,7 +14,7 @@
 /// a [VariableScopeModel] that can respond to queries about how a particular
 /// variable is being used at any point in the code.
 class ScopeModelBuilder extends ir.Visitor<EvaluationComplexity>
-    with VariableCollectorMixin {
+    with VariableCollectorMixin, ir.VisitorNullMixin<EvaluationComplexity> {
   final Dart2jsConstantEvaluator _constantEvaluator;
   ir.StaticTypeContext _staticTypeContext;
 
diff --git a/pkg/compiler/lib/src/ir/static_type_base.dart b/pkg/compiler/lib/src/ir/static_type_base.dart
index fe3d62b..67c6014 100644
--- a/pkg/compiler/lib/src/ir/static_type_base.dart
+++ b/pkg/compiler/lib/src/ir/static_type_base.dart
@@ -57,7 +57,8 @@
 /// expression kind. For instance method invocations whose static type depend
 /// on the static types of the receiver and type arguments and the signature
 /// of the targeted procedure.
-abstract class StaticTypeBase extends ir.Visitor<ir.DartType> {
+abstract class StaticTypeBase extends ir.Visitor<ir.DartType>
+    with ir.VisitorNullMixin<ir.DartType> {
   final ir.TypeEnvironment _typeEnvironment;
 
   StaticTypeBase(this._typeEnvironment);
diff --git a/pkg/compiler/lib/src/ir/visitors.dart b/pkg/compiler/lib/src/ir/visitors.dart
index 8d3d497..94db2ae 100644
--- a/pkg/compiler/lib/src/ir/visitors.dart
+++ b/pkg/compiler/lib/src/ir/visitors.dart
@@ -35,6 +35,9 @@
     }
     return null;
   }
+
+  @override
+  String defaultExpression(ir.Expression node) => null;
 }
 
 /// Visitor that converts kernel dart types into [DartType].
@@ -182,6 +185,11 @@
   DartType visitNullType(ir.NullType node) {
     return elementMap.commonElements.nullType;
   }
+
+  @override
+  DartType defaultDartType(ir.DartType node) {
+    throw UnsupportedError('Unsupported type $node (${node.runtimeType})');
+  }
 }
 
 class ConstantValuefier extends ir.ComputeOnceConstantVisitor<ConstantValue> {
diff --git a/pkg/compiler/lib/src/js_model/locals.dart b/pkg/compiler/lib/src/js_model/locals.dart
index 1d5cd00..103c5a8 100644
--- a/pkg/compiler/lib/src/js_model/locals.dart
+++ b/pkg/compiler/lib/src/js_model/locals.dart
@@ -292,7 +292,7 @@
   }
 }
 
-class JumpVisitor extends ir.Visitor {
+class JumpVisitor extends ir.Visitor<void> with ir.VisitorVoidMixin {
   int jumpIndex = 0;
   int labelIndex = 0;
   final MemberEntity member;
diff --git a/pkg/compiler/lib/src/serialization/node_indexer.dart b/pkg/compiler/lib/src/serialization/node_indexer.dart
index edc81ae..52b27ac 100644
--- a/pkg/compiler/lib/src/serialization/node_indexer.dart
+++ b/pkg/compiler/lib/src/serialization/node_indexer.dart
@@ -6,7 +6,8 @@
 
 /// Visitor that ascribes an index to all [ir.TreeNode]s that potentially
 /// needed for serialization and deserialization.
-class _TreeNodeIndexerVisitor extends ir.Visitor<void> {
+class _TreeNodeIndexerVisitor extends ir.Visitor<void>
+    with ir.VisitorVoidMixin {
   int _currentIndex = 0;
   final Map<int, ir.TreeNode> _indexToNodeMap;
   final Map<ir.TreeNode, int> _nodeToIndexMap;
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index 4bd37c8..5b4f99f 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -84,7 +84,7 @@
       this.staticTypeProvider);
 }
 
-class KernelSsaGraphBuilder extends ir.Visitor {
+class KernelSsaGraphBuilder extends ir.Visitor<void> with ir.VisitorVoidMixin {
   /// Holds the resulting SSA graph.
   final HGraph graph = new HGraph();
 
@@ -6560,7 +6560,8 @@
   }
 }
 
-class _ErroneousInitializerVisitor extends ir.Visitor<bool> {
+class _ErroneousInitializerVisitor extends ir.Visitor<bool>
+    with ir.VisitorDefaultValueMixin<bool> {
   _ErroneousInitializerVisitor();
 
   // TODO(30809): Use const constructor.
@@ -6580,7 +6581,7 @@
 
   // Expressions: Does the expression always throw?
   @override
-  bool defaultExpression(ir.Expression node) => false;
+  bool get defaultValue => false;
 
   @override
   bool visitThrow(ir.Throw node) => true;
@@ -6839,7 +6840,7 @@
   }
 }
 
-class InlineWeeder extends ir.Visitor {
+class InlineWeeder extends ir.Visitor<void> with ir.VisitorVoidMixin {
   // Invariant: *INSIDE_LOOP* > *OUTSIDE_LOOP*
   static const INLINING_NODES_OUTSIDE_LOOP = 15;
   static const INLINING_NODES_OUTSIDE_LOOP_ARG_FACTOR = 3;
@@ -7397,7 +7398,8 @@
 
 /// Visitor to detect environment-rewriting that prevents inlining
 /// (e.g. closures).
-class InlineWeederBodyClosure extends ir.Visitor<void> {
+class InlineWeederBodyClosure extends ir.Visitor<void>
+    with ir.VisitorVoidMixin {
   bool tooDifficult = false;
 
   InlineWeederBodyClosure();
diff --git a/pkg/compiler/lib/src/ssa/kernel_string_builder.dart b/pkg/compiler/lib/src/ssa/kernel_string_builder.dart
index 0d8a57a..b6f69b6 100644
--- a/pkg/compiler/lib/src/ssa/kernel_string_builder.dart
+++ b/pkg/compiler/lib/src/ssa/kernel_string_builder.dart
@@ -10,7 +10,7 @@
 import 'nodes.dart';
 
 /// Visits and concatenates the expressions in a string concatenation.
-class KernelStringBuilder extends ir.Visitor {
+class KernelStringBuilder extends ir.Visitor<void> with ir.VisitorVoidMixin {
   final KernelSsaGraphBuilder builder;
 
   /// The string value generated so far.
diff --git a/pkg/compiler/lib/src/ssa/loop_handler.dart b/pkg/compiler/lib/src/ssa/loop_handler.dart
index 9138b8b..d729fef 100644
--- a/pkg/compiler/lib/src/ssa/loop_handler.dart
+++ b/pkg/compiler/lib/src/ssa/loop_handler.dart
@@ -329,9 +329,10 @@
   int loopKind(ir.TreeNode node) => node.accept(new _KernelLoopTypeVisitor());
 }
 
-class _KernelLoopTypeVisitor extends ir.Visitor<int> {
+class _KernelLoopTypeVisitor extends ir.Visitor<int>
+    with ir.VisitorDefaultValueMixin<int> {
   @override
-  int defaultNode(ir.Node node) => HLoopBlockInformation.NOT_A_LOOP;
+  int get defaultValue => HLoopBlockInformation.NOT_A_LOOP;
 
   @override
   int visitWhileStatement(ir.WhileStatement node) =>
diff --git a/pkg/compiler/lib/src/ssa/switch_continue_analysis.dart b/pkg/compiler/lib/src/ssa/switch_continue_analysis.dart
index 1733898..776a5d5 100644
--- a/pkg/compiler/lib/src/ssa/switch_continue_analysis.dart
+++ b/pkg/compiler/lib/src/ssa/switch_continue_analysis.dart
@@ -7,7 +7,8 @@
 /// Helper class that traverses a kernel AST subtree to see if it has any
 /// continue statements in the body of any switch cases (having continue
 /// statements results in a more complex generated code).
-class SwitchContinueAnalysis extends ir.Visitor<bool> {
+class SwitchContinueAnalysis extends ir.Visitor<bool>
+    with ir.VisitorDefaultValueMixin<bool> {
   SwitchContinueAnalysis._();
 
   static bool containsContinue(ir.Statement switchCaseBody) {
@@ -127,5 +128,5 @@
   }
 
   @override
-  bool defaultNode(ir.Node node) => false;
+  bool get defaultValue => false;
 }
diff --git a/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart b/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart
index 212601c..58af2ac 100644
--- a/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart
@@ -36,7 +36,9 @@
         TreeNode,
         TypeParameter,
         VariableDeclaration,
-        Visitor;
+        Visitor,
+        VisitorNullMixin,
+        VisitorVoidMixin;
 
 DiagnosticMessage _createInternalError(Uri uri, int line, int col, String msg) {
   return Message(Code<String>('Expression Compiler Internal error'),
@@ -84,7 +86,7 @@
 /// - locals
 /// - formals
 /// - captured variables (for closures)
-class DartScopeBuilder extends Visitor<void> {
+class DartScopeBuilder extends Visitor<void> with VisitorVoidMixin {
   final Component _component;
   final int _line;
   final int _column;
@@ -196,7 +198,7 @@
 /// that do not have .fileEndOffset field.
 ///
 /// For example - [Block]
-class FileEndOffsetCalculator extends Visitor<int> {
+class FileEndOffsetCalculator extends Visitor<int> with VisitorNullMixin<int> {
   static const int noOffset = -1;
 
   final int _startOffset;
@@ -266,7 +268,7 @@
 /// in the JavaScript scope, so we need to redefine them.
 ///
 /// See [_addSymbolDefinitions]
-class PrivateFieldsVisitor extends Visitor<void> {
+class PrivateFieldsVisitor extends Visitor<void> with VisitorVoidMixin {
   final Map<String, Library> privateFields = {};
 
   @override
diff --git a/pkg/dev_compiler/lib/src/kernel/kernel_helpers.dart b/pkg/dev_compiler/lib/src/kernel/kernel_helpers.dart
index 64eb246..dc76a82 100644
--- a/pkg/dev_compiler/lib/src/kernel/kernel_helpers.dart
+++ b/pkg/dev_compiler/lib/src/kernel/kernel_helpers.dart
@@ -332,6 +332,9 @@
     visit(node.body);
     visit(node.finalizer);
   }
+
+  @override
+  void defaultStatement(Statement node) {}
 }
 
 /// Ensures that all of the known DartType implementors are handled.
diff --git a/pkg/dev_compiler/lib/src/kernel/nullable_inference.dart b/pkg/dev_compiler/lib/src/kernel/nullable_inference.dart
index bd952b2..b930366 100644
--- a/pkg/dev_compiler/lib/src/kernel/nullable_inference.dart
+++ b/pkg/dev_compiler/lib/src/kernel/nullable_inference.dart
@@ -364,7 +364,7 @@
 /// variables that have already been determined to be nullable.
 ///
 // TODO(jmesserly): Introduce flow analysis.
-class _NullableVariableInference extends RecursiveVisitor<void> {
+class _NullableVariableInference extends RecursiveVisitor {
   NullableInference _nullInference;
 
   /// Variables that are currently believed to be not-null.
diff --git a/pkg/dev_compiler/lib/src/kernel/target.dart b/pkg/dev_compiler/lib/src/kernel/target.dart
index bdcbc1d..81fadbf 100644
--- a/pkg/dev_compiler/lib/src/kernel/target.dart
+++ b/pkg/dev_compiler/lib/src/kernel/target.dart
@@ -245,7 +245,7 @@
 /// members can be eliminated, and adjusts the flags to remove those checks.
 ///
 /// See [_CovarianceTransformer.transform].
-class _CovarianceTransformer extends RecursiveVisitor<void> {
+class _CovarianceTransformer extends RecursiveVisitor {
   /// The set of private instance members in [_library] that (potentially) need
   /// covariance checks.
   ///
diff --git a/pkg/dev_compiler/test/expression_compiler/scope_offset_test.dart b/pkg/dev_compiler/test/expression_compiler/scope_offset_test.dart
index 5fa3f1f..413ae5e 100644
--- a/pkg/dev_compiler/test/expression_compiler/scope_offset_test.dart
+++ b/pkg/dev_compiler/test/expression_compiler/scope_offset_test.dart
@@ -34,7 +34,7 @@
   });
 }
 
-class ScopeOffsetValidator extends Visitor<void> {
+class ScopeOffsetValidator extends Visitor<void> with VisitorVoidMixin {
   int classCount = 0;
   int memberCount = 0;
   int blockCount = 0;
diff --git a/pkg/dev_compiler/test/nullable_inference_test.dart b/pkg/dev_compiler/test/nullable_inference_test.dart
index e077906..6177a5b 100644
--- a/pkg/dev_compiler/test/nullable_inference_test.dart
+++ b/pkg/dev_compiler/test/nullable_inference_test.dart
@@ -536,7 +536,7 @@
 bool useAnnotations = false;
 NullableInference inference;
 
-class _TestRecursiveVisitor extends RecursiveVisitor<void> {
+class _TestRecursiveVisitor extends RecursiveVisitor {
   final Set<Library> librariesFromDill;
   int _functionNesting = 0;
   TypeEnvironment _typeEnvironment;
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
index 02b7300..9456de1 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -792,7 +792,7 @@
   }
 }
 
-class ConstantEvaluator extends RecursiveVisitor<Constant> {
+class ConstantEvaluator extends RecursiveResultVisitor<Constant> {
   final ConstantsBackend backend;
   final NumberSemantics numberSemantics;
   ConstantIntFolder intFolder;
diff --git a/pkg/front_end/lib/src/testing/id_extractor.dart b/pkg/front_end/lib/src/testing/id_extractor.dart
index 93af7c6..ebee200 100644
--- a/pkg/front_end/lib/src/testing/id_extractor.dart
+++ b/pkg/front_end/lib/src/testing/id_extractor.dart
@@ -33,7 +33,8 @@
 
 /// Abstract visitor for computing data corresponding to a node or element,
 /// and record it with a generic [Id]
-abstract class DataExtractor<T> extends Visitor with DataRegistry<T> {
+abstract class DataExtractor<T> extends Visitor<void>
+    with VisitorVoidMixin, DataRegistry<T> {
   @override
   final Map<Id, ActualData<T>> actualMap;
 
diff --git a/pkg/front_end/test/comments_on_certain_arguments_tool.dart b/pkg/front_end/test/comments_on_certain_arguments_tool.dart
index d83bf84..9109db8 100644
--- a/pkg/front_end/test/comments_on_certain_arguments_tool.dart
+++ b/pkg/front_end/test/comments_on_certain_arguments_tool.dart
@@ -159,7 +159,7 @@
   return options;
 }
 
-class InvocationVisitor extends RecursiveVisitor<void> {
+class InvocationVisitor extends RecursiveVisitor {
   void visitProcedure(Procedure node) {
     if (node.isNoSuchMethodForwarder) return;
     super.visitProcedure(node);
diff --git a/pkg/front_end/test/fasta/assert_locations_test.dart b/pkg/front_end/test/fasta/assert_locations_test.dart
index c94a1e4..3ef159b 100644
--- a/pkg/front_end/test/fasta/assert_locations_test.dart
+++ b/pkg/front_end/test/fasta/assert_locations_test.dart
@@ -102,7 +102,7 @@
 
 /// Visitor that verifies that all [AssertStatement]s in the Kernel AST
 /// have expected spans for their conditions.
-class VerifyingVisitor extends RecursiveVisitor<Null> {
+class VerifyingVisitor extends RecursiveVisitor {
   final Test test;
 
   /// Set of names of verified [Procedure]s.
diff --git a/pkg/front_end/test/fasta/testing/suite.dart b/pkg/front_end/test/fasta/testing/suite.dart
index 2b4078a..62858c6 100644
--- a/pkg/front_end/test/fasta/testing/suite.dart
+++ b/pkg/front_end/test/fasta/testing/suite.dart
@@ -109,14 +109,15 @@
         TreeNode,
         UnevaluatedConstant,
         Version,
-        Visitor;
+        Visitor,
+        VisitorVoidMixin;
 
 import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
 
 import 'package:kernel/core_types.dart' show CoreTypes;
 
 import 'package:kernel/kernel.dart'
-    show RecursiveVisitor, loadComponentFromBytes;
+    show RecursiveResultVisitor, loadComponentFromBytes;
 
 import 'package:kernel/reference_from_index.dart' show ReferenceFromIndex;
 
@@ -862,7 +863,7 @@
   }
 }
 
-class StressConstantEvaluatorVisitor extends RecursiveVisitor<Node>
+class StressConstantEvaluatorVisitor extends RecursiveResultVisitor<Node>
     implements ErrorReporter {
   ConstantEvaluator constantEvaluator;
   ConstantEvaluator constantEvaluatorWithEmptyEnvironment;
@@ -1916,7 +1917,7 @@
 /// Visitor that checks that the component has been transformed properly.
 // TODO(johnniwinther): Add checks for all nodes that are unsupported after
 // transformation.
-class VerifyTransformed extends Visitor<void> {
+class VerifyTransformed extends Visitor<void> with VisitorVoidMixin {
   final Target target;
   List<String> errors = [];
 
diff --git a/pkg/front_end/test/fasta/type_inference/type_schema_test.dart b/pkg/front_end/test/fasta/type_inference/type_schema_test.dart
index 2891915..bc794f0 100644
--- a/pkg/front_end/test/fasta/type_inference/type_schema_test.dart
+++ b/pkg/front_end/test/fasta/type_inference/type_schema_test.dart
@@ -113,7 +113,7 @@
   }
 }
 
-class _OrdinaryVisitor<R> extends Visitor<R> {
+class _OrdinaryVisitor<R> extends Visitor<R> with VisitorNullMixin<R> {
   final _UnaryFunction<DartType, R> _defaultDartType;
 
   _OrdinaryVisitor({_UnaryFunction<DartType, R> defaultDartType})
@@ -129,7 +129,7 @@
   }
 }
 
-class _TypeSchemaVisitor<R> extends Visitor<R> {
+class _TypeSchemaVisitor<R> extends Visitor<R> with VisitorNullMixin<R> {
   final _UnaryFunction<DartType, R> _defaultDartType;
   final _UnaryFunction<UnknownType, R> _visitUnknownType;
 
diff --git a/pkg/front_end/test/static_types/analysis_helper.dart b/pkg/front_end/test/static_types/analysis_helper.dart
index 539d58e..fcd3b9f 100644
--- a/pkg/front_end/test/static_types/analysis_helper.dart
+++ b/pkg/front_end/test/static_types/analysis_helper.dart
@@ -42,7 +42,7 @@
       .run(verbose: verbose, generate: generate);
 }
 
-class StaticTypeVisitorBase extends RecursiveVisitor<void> {
+class StaticTypeVisitorBase extends RecursiveVisitor {
   final TypeEnvironment typeEnvironment;
 
   StaticTypeContext staticTypeContext;
diff --git a/pkg/frontend_server/lib/src/to_string_transformer.dart b/pkg/frontend_server/lib/src/to_string_transformer.dart
index 8969e51..9942dd4 100644
--- a/pkg/frontend_server/lib/src/to_string_transformer.dart
+++ b/pkg/frontend_server/lib/src/to_string_transformer.dart
@@ -11,7 +11,7 @@
 
 /// A [RecursiveVisitor] that replaces [Object.toString] overrides with
 /// `super.toString()`.
-class ToStringVisitor extends RecursiveVisitor<void> {
+class ToStringVisitor extends RecursiveVisitor {
   /// The [packageUris] must not be null.
   ToStringVisitor(this._packageUris) : assert(_packageUris != null);
 
diff --git a/pkg/kernel/lib/binary/ast_to_binary.dart b/pkg/kernel/lib/binary/ast_to_binary.dart
index 25c299b..821b2f9 100644
--- a/pkg/kernel/lib/binary/ast_to_binary.dart
+++ b/pkg/kernel/lib/binary/ast_to_binary.dart
@@ -2711,7 +2711,7 @@
   int operator [](SwitchCase node) => index[node];
 }
 
-class ConstantIndexer extends RecursiveVisitor {
+class ConstantIndexer extends RecursiveResultVisitor {
   final StringIndexer stringIndexer;
 
   final List<Constant> entries = <Constant>[];
diff --git a/pkg/kernel/lib/src/bounds_checks.dart b/pkg/kernel/lib/src/bounds_checks.dart
index 272679f..c8353fa 100644
--- a/pkg/kernel/lib/src/bounds_checks.dart
+++ b/pkg/kernel/lib/src/bounds_checks.dart
@@ -74,35 +74,35 @@
   }
 }
 
-class OccurrenceCollectorVisitor extends DartTypeVisitor {
+class OccurrenceCollectorVisitor extends DartTypeVisitor<void> {
   final Set<TypeParameter> typeParameters;
   Set<TypeParameter> occurred = new Set<TypeParameter>();
 
   OccurrenceCollectorVisitor(this.typeParameters);
 
-  visit(DartType node) => node.accept(this);
+  void visit(DartType node) => node.accept(this);
 
-  visitNamedType(NamedType node) {
+  void visitNamedType(NamedType node) {
     node.type.accept(this);
   }
 
-  visitInvalidType(InvalidType node);
-  visitDynamicType(DynamicType node);
-  visitVoidType(VoidType node);
+  void visitInvalidType(InvalidType node);
+  void visitDynamicType(DynamicType node);
+  void visitVoidType(VoidType node);
 
-  visitInterfaceType(InterfaceType node) {
+  void visitInterfaceType(InterfaceType node) {
     for (DartType argument in node.typeArguments) {
       argument.accept(this);
     }
   }
 
-  visitTypedefType(TypedefType node) {
+  void visitTypedefType(TypedefType node) {
     for (DartType argument in node.typeArguments) {
       argument.accept(this);
     }
   }
 
-  visitFunctionType(FunctionType node) {
+  void visitFunctionType(FunctionType node) {
     for (TypeParameter typeParameter in node.typeParameters) {
       typeParameter.bound.accept(this);
       typeParameter.defaultType?.accept(this);
@@ -116,11 +116,14 @@
     node.returnType.accept(this);
   }
 
-  visitTypeParameterType(TypeParameterType node) {
+  void visitTypeParameterType(TypeParameterType node) {
     if (typeParameters.contains(node.parameter)) {
       occurred.add(node.parameter);
     }
   }
+
+  @override
+  void defaultDartType(DartType node) {}
 }
 
 DartType instantiateToBounds(
diff --git a/pkg/kernel/lib/src/tool/find_referenced_libraries.dart b/pkg/kernel/lib/src/tool/find_referenced_libraries.dart
index 73733ca..9988f13 100644
--- a/pkg/kernel/lib/src/tool/find_referenced_libraries.dart
+++ b/pkg/kernel/lib/src/tool/find_referenced_libraries.dart
@@ -23,10 +23,10 @@
   return false;
 }
 
-class _LibraryCollector extends RecursiveVisitor<Null> {
+class _LibraryCollector extends RecursiveVisitor {
   Set<Library> allSeenLibraries = {};
 
-  Null defaultNode(Node node) {
+  void defaultNode(Node node) {
     if (node is NamedNode) {
       // Named nodes can be linked to.
       seen(node);
@@ -38,7 +38,7 @@
     super.defaultNode(node);
   }
 
-  Null defaultMemberReference(Member node) {
+  void defaultMemberReference(Member node) {
     seen(node);
     super.defaultMemberReference(node);
   }
diff --git a/pkg/kernel/lib/text/ast_to_text.dart b/pkg/kernel/lib/text/ast_to_text.dart
index a7faeea..26699f0 100644
--- a/pkg/kernel/lib/text/ast_to_text.dart
+++ b/pkg/kernel/lib/text/ast_to_text.dart
@@ -29,7 +29,7 @@
   NormalNamer(this.prefix);
 }
 
-class ConstantNamer extends RecursiveVisitor<Null> with Namer<Constant> {
+class ConstantNamer extends RecursiveResultVisitor<Null> with Namer<Constant> {
   final String prefix;
   ConstantNamer(this.prefix);
 
@@ -271,7 +271,7 @@
 }
 
 /// A quick and dirty ambiguous text printer.
-class Printer extends Visitor<Null> {
+class Printer extends Visitor<void> with VisitorVoidMixin {
   final NameSystem syntheticNames;
   final StringSink sink;
   final Annotator annotator;
diff --git a/pkg/kernel/lib/text/text_serializer.dart b/pkg/kernel/lib/text/text_serializer.dart
index f3b00a1..3ababf6 100644
--- a/pkg/kernel/lib/text/text_serializer.dart
+++ b/pkg/kernel/lib/text/text_serializer.dart
@@ -107,6 +107,12 @@
   String visitLoadLibrary(LoadLibrary _) => "load";
   String visitConstantExpression(ConstantExpression _) => "const";
   String visitInstanceCreation(InstanceCreation _) => "object";
+
+  @override
+  String defaultExpression(Expression node) {
+    throw new UnimplementedError(
+        'Unimplemented expression $node (${node.runtimeType})');
+  }
 }
 
 const TextSerializer<InvalidExpression> invalidExpressionSerializer =
@@ -846,6 +852,11 @@
   String visitTypedefType(TypedefType _) => "typedef";
   String visitFutureOrType(FutureOrType _) => "futureor";
   String visitNullType(NullType _) => "null-type";
+
+  @override
+  String defaultDartType(DartType node) {
+    throw UnimplementedError('Unimplemented type $node (${node.runtimeType})');
+  }
 }
 
 const TextSerializer<InvalidType> invalidTypeSerializer =
@@ -1031,6 +1042,12 @@
   String visitContinueSwitchStatement(ContinueSwitchStatement node) =>
       "continue";
   String visitFunctionDeclaration(FunctionDeclaration node) => "local-fun";
+
+  @override
+  String defaultStatement(Statement node) {
+    throw new UnimplementedError(
+        "Unimplemented statement $node (${node.runtimeType})");
+  }
 }
 
 TextSerializer<ExpressionStatement> expressionStatementSerializer = new Wrapped(
@@ -1795,6 +1812,12 @@
   String visitTearOffConstant(TearOffConstant node) => "const-tearoff";
   String visitTypeLiteralConstant(TypeLiteralConstant node) => "const-type";
   String visitUnevaluatedConstant(UnevaluatedConstant node) => "const-expr";
+
+  @override
+  String defaultConstant(Constant node) {
+    throw new UnimplementedError(
+        'Unimplemented constant $node (${node.runtimeType})');
+  }
 }
 
 TextSerializer<BoolConstant> boolConstantSerializer =
diff --git a/pkg/kernel/lib/transformations/scanner.dart b/pkg/kernel/lib/transformations/scanner.dart
index 5aac0a8..ee9b660 100644
--- a/pkg/kernel/lib/transformations/scanner.dart
+++ b/pkg/kernel/lib/transformations/scanner.dart
@@ -218,7 +218,7 @@
 }
 
 abstract class ExpressionScanner<Y extends TreeNode>
-    extends RecursiveVisitor<void> implements Scanner<Expression, Y> {
+    extends RecursiveResultVisitor<void> implements Scanner<Expression, Y> {
   final Scanner<Y, TreeNode> next;
   ScanResult<Expression, Y> _result;
 
@@ -242,7 +242,7 @@
 }
 
 abstract class MethodInvocationScanner<Y extends TreeNode>
-    extends RecursiveVisitor<void> implements Scanner<MethodInvocation, Y> {
+    extends RecursiveVisitor implements Scanner<MethodInvocation, Y> {
   final Scanner<Y, TreeNode> next;
   ScanResult<MethodInvocation, Y> _result;
 
diff --git a/pkg/kernel/lib/verifier.dart b/pkg/kernel/lib/verifier.dart
index cdd2f22..00dc2af 100644
--- a/pkg/kernel/lib/verifier.dart
+++ b/pkg/kernel/lib/verifier.dart
@@ -48,7 +48,7 @@
 /// Checks that a kernel component is well-formed.
 ///
 /// This does not include any kind of type checking.
-class VerifyingVisitor extends RecursiveVisitor<void> {
+class VerifyingVisitor extends RecursiveResultVisitor<void> {
   final Set<Class> classes = new Set<Class>();
   final Set<Typedef> typedefs = new Set<Typedef>();
   Set<TypeParameter> typeParametersInScope = new Set<TypeParameter>();
@@ -883,14 +883,14 @@
       : _staticTypeContext = new StatefulStaticTypeContext.stacked(env);
 
   @override
-  visitLibrary(Library node) {
+  void visitLibrary(Library node) {
     _staticTypeContext.enterLibrary(node);
     super.visitLibrary(node);
     _staticTypeContext.leaveLibrary(node);
   }
 
   @override
-  visitField(Field node) {
+  void visitField(Field node) {
     currentMember = node;
     _staticTypeContext.enterMember(node);
     super.visitField(node);
@@ -899,7 +899,7 @@
   }
 
   @override
-  visitProcedure(Procedure node) {
+  void visitProcedure(Procedure node) {
     currentMember = node;
     _staticTypeContext.enterMember(node);
     super.visitProcedure(node);
@@ -908,7 +908,7 @@
   }
 
   @override
-  visitConstructor(Constructor node) {
+  void visitConstructor(Constructor node) {
     currentMember = node;
     _staticTypeContext.enterMember(node);
     super.visitConstructor(node);
@@ -917,7 +917,7 @@
   }
 
   @override
-  defaultExpression(Expression node) {
+  void defaultExpression(Expression node) {
     try {
       node.getStaticType(_staticTypeContext);
     } catch (_) {
@@ -929,7 +929,7 @@
   }
 }
 
-class CheckParentPointers extends Visitor {
+class CheckParentPointers extends Visitor<void> with VisitorVoidMixin {
   static void check(TreeNode node) {
     node.accept(new CheckParentPointers(node.parent));
   }
diff --git a/pkg/kernel/lib/visitor.dart b/pkg/kernel/lib/visitor.dart
index fefa970..4ef6055 100644
--- a/pkg/kernel/lib/visitor.dart
+++ b/pkg/kernel/lib/visitor.dart
@@ -14,7 +14,7 @@
 abstract class ExpressionVisitor<R> {
   const ExpressionVisitor();
 
-  R defaultExpression(Expression node) => null;
+  R defaultExpression(Expression node);
   R defaultBasicLiteral(BasicLiteral node) => defaultExpression(node);
 
   R visitInvalidExpression(InvalidExpression node) => defaultExpression(node);
@@ -87,7 +87,7 @@
 abstract class StatementVisitor<R> {
   const StatementVisitor();
 
-  R defaultStatement(Statement node) => null;
+  R defaultStatement(Statement node);
 
   R visitExpressionStatement(ExpressionStatement node) =>
       defaultStatement(node);
@@ -118,7 +118,7 @@
 abstract class MemberVisitor<R> {
   const MemberVisitor();
 
-  R defaultMember(Member node) => null;
+  R defaultMember(Member node);
 
   R visitConstructor(Constructor node) => defaultMember(node);
   R visitProcedure(Procedure node) => defaultMember(node);
@@ -131,7 +131,7 @@
 abstract class InitializerVisitor<R> {
   const InitializerVisitor();
 
-  R defaultInitializer(Initializer node) => null;
+  R defaultInitializer(Initializer node);
 
   R visitInvalidInitializer(InvalidInitializer node) =>
       defaultInitializer(node);
@@ -143,7 +143,7 @@
   R visitAssertInitializer(AssertInitializer node) => defaultInitializer(node);
 }
 
-class TreeVisitor<R>
+abstract class TreeVisitor<R>
     implements
         ExpressionVisitor<R>,
         StatementVisitor<R>,
@@ -151,7 +151,7 @@
         InitializerVisitor<R> {
   const TreeVisitor();
 
-  R defaultTreeNode(TreeNode node) => null;
+  R defaultTreeNode(TreeNode node);
 
   // Expressions
   R defaultExpression(Expression node) => defaultTreeNode(node);
@@ -289,10 +289,10 @@
   R visitComponent(Component node) => defaultTreeNode(node);
 }
 
-class DartTypeVisitor<R> {
+abstract class DartTypeVisitor<R> {
   const DartTypeVisitor();
 
-  R defaultDartType(DartType node) => null;
+  R defaultDartType(DartType node);
 
   R visitInvalidType(InvalidType node) => defaultDartType(node);
   R visitDynamicType(DynamicType node) => defaultDartType(node);
@@ -307,8 +307,8 @@
   R visitNullType(NullType node) => defaultDartType(node);
 }
 
-class DartTypeVisitor1<R, T> {
-  R defaultDartType(DartType node, T arg) => null;
+abstract class DartTypeVisitor1<R, T> {
+  R defaultDartType(DartType node, T arg);
 
   R visitInvalidType(InvalidType node, T arg) => defaultDartType(node, arg);
   R visitDynamicType(DynamicType node, T arg) => defaultDartType(node, arg);
@@ -332,10 +332,10 @@
 ///
 /// Use [ComputeOnceConstantVisitor] or [VisitOnceConstantVisitor] to visit
 /// a constant node while ensuring each subnode is only visited once.
-class ConstantVisitor<R> {
+abstract class ConstantVisitor<R> {
   const ConstantVisitor();
 
-  R defaultConstant(Constant node) => null;
+  R defaultConstant(Constant node);
 
   R visitNullConstant(NullConstant node) => defaultConstant(node);
   R visitBoolConstant(BoolConstant node) => defaultConstant(node);
@@ -435,7 +435,7 @@
 /// Visitor-like class used for visiting a [Constant] node while computing a
 /// value for each subnode. The visitor caches the computed values ensuring that
 /// each subnode is only visited once.
-class ComputeOnceConstantVisitor<R> implements _ConstantCallback<R> {
+abstract class ComputeOnceConstantVisitor<R> implements _ConstantCallback<R> {
   _ConstantCallbackVisitor<R> _visitor;
   Map<Constant, R> cache = new LinkedHashMap.identity();
 
@@ -461,7 +461,7 @@
     return value;
   }
 
-  R defaultConstant(Constant node) => null;
+  R defaultConstant(Constant node);
 
   R visitNullConstant(NullConstant node) => defaultConstant(node);
   R visitBoolConstant(BoolConstant node) => defaultConstant(node);
@@ -484,7 +484,7 @@
 ///
 /// The visitor records the visited node to ensure that each subnode is only
 /// visited once.
-class VisitOnceConstantVisitor implements _ConstantCallback<void> {
+abstract class VisitOnceConstantVisitor implements _ConstantCallback<void> {
   _ConstantCallbackVisitor<void> _visitor;
   Set<Constant> cache = new LinkedHashSet.identity();
 
@@ -502,7 +502,7 @@
     }
   }
 
-  void defaultConstant(Constant node) => null;
+  void defaultConstant(Constant node);
 
   void visitNullConstant(NullConstant node) => defaultConstant(node);
   void visitBoolConstant(BoolConstant node) => defaultConstant(node);
@@ -523,10 +523,10 @@
       defaultConstant(node);
 }
 
-class MemberReferenceVisitor<R> {
+abstract class MemberReferenceVisitor<R> {
   const MemberReferenceVisitor();
 
-  R defaultMemberReference(Member node) => null;
+  R defaultMemberReference(Member node);
 
   R visitFieldReference(Field node) => defaultMemberReference(node);
   R visitConstructorReference(Constructor node) => defaultMemberReference(node);
@@ -537,7 +537,7 @@
   }
 }
 
-class Visitor<R> extends TreeVisitor<R>
+abstract class Visitor<R> extends TreeVisitor<R>
     implements
         DartTypeVisitor<R>,
         ConstantVisitor<R>,
@@ -545,7 +545,7 @@
   const Visitor();
 
   /// The catch-all case, except for references.
-  R defaultNode(Node node) => null;
+  R defaultNode(Node node);
   R defaultTreeNode(TreeNode node) => defaultNode(node);
 
   // DartTypes
@@ -581,11 +581,13 @@
   R visitUnevaluatedConstant(UnevaluatedConstant node) => defaultConstant(node);
 
   // Class references
-  R visitClassReference(Class node) => null;
-  R visitTypedefReference(Typedef node) => null;
+  R visitClassReference(Class node);
+
+  R visitTypedefReference(Typedef node);
 
   // Constant references
-  R defaultConstantReference(Constant node) => null;
+  R defaultConstantReference(Constant node);
+
   R visitNullConstantReference(NullConstant node) =>
       defaultConstantReference(node);
   R visitBoolConstantReference(BoolConstant node) =>
@@ -617,7 +619,8 @@
       defaultConstantReference(node);
 
   // Member references
-  R defaultMemberReference(Member node) => null;
+  R defaultMemberReference(Member node);
+
   R visitFieldReference(Field node) => defaultMemberReference(node);
   R visitConstructorReference(Constructor node) => defaultMemberReference(node);
   R visitProcedureReference(Procedure node) => defaultMemberReference(node);
@@ -631,9 +634,117 @@
   R visitNamedType(NamedType node) => defaultNode(node);
 }
 
-class RecursiveVisitor<R> extends Visitor<R> {
+/// Visitor mixin that throws as its base case.
+mixin VisitorThrowingMixin<R> implements Visitor<R> {
+  @override
+  R defaultNode(Node node) {
+    throw new UnimplementedError('Unimplemented ${runtimeType}.defaultNode for '
+        '${node} (${node.runtimeType})');
+  }
+
+  @override
+  R visitClassReference(Class node) {
+    throw new UnimplementedError(
+        'Unimplemented ${runtimeType}.visitClassReference for '
+        '${node} (${node.runtimeType})');
+  }
+
+  @override
+  R visitTypedefReference(Typedef node) {
+    throw new UnimplementedError(
+        'Unimplemented ${runtimeType}.visitTypedefReference for '
+        '${node} (${node.runtimeType})');
+  }
+
+  @override
+  R defaultConstantReference(Constant node) {
+    throw new UnimplementedError(
+        'Unimplemented ${runtimeType}.defaultConstantReference for '
+        '${node} (${node.runtimeType})');
+  }
+
+  @override
+  R defaultMemberReference(Member node) {
+    throw new UnimplementedError(
+        'Unimplemented ${runtimeType}.defaultMemberReference for '
+        '${node} (${node.runtimeType})');
+  }
+}
+
+/// Visitor mixin that returns a value of type [R] or `null` and uses `null` as
+/// its base case.
+mixin VisitorNullMixin<R> implements Visitor<R /*?*/ > {
+  @override
+  R defaultNode(Node node) => null;
+
+  @override
+  R visitClassReference(Class node) => null;
+
+  @override
+  R visitTypedefReference(Typedef node) => null;
+
+  @override
+  R defaultConstantReference(Constant node) => null;
+
+  @override
+  R defaultMemberReference(Member node) => null;
+}
+
+/// Visitor mixin that returns void.
+mixin VisitorVoidMixin implements Visitor<void> {
+  @override
+  void defaultNode(Node node) {}
+
+  @override
+  void visitClassReference(Class node) {}
+
+  @override
+  void visitTypedefReference(Typedef node) {}
+
+  @override
+  void defaultConstantReference(Constant node) {}
+
+  @override
+  void defaultMemberReference(Member node) {}
+}
+
+/// Visitor mixin that returns a [defaultValue] of type [R] as its base case.
+mixin VisitorDefaultValueMixin<R> implements Visitor<R> {
+  R get defaultValue;
+
+  @override
+  R defaultNode(Node node) => defaultValue;
+
+  @override
+  R visitClassReference(Class node) => defaultValue;
+
+  @override
+  R visitTypedefReference(Typedef node) => defaultValue;
+
+  @override
+  R defaultConstantReference(Constant node) => defaultValue;
+
+  @override
+  R defaultMemberReference(Member node) => defaultValue;
+}
+
+/// Recursive visitor that doesn't return anything from its visit methods.
+// TODO(johnniwinther): Remove type parameter when all subclasses have been
+// changed to use [RecursiveVisitor] without type arguments.
+class RecursiveVisitor<T> extends Visitor<void> with VisitorVoidMixin {
   const RecursiveVisitor();
 
+  void defaultNode(Node node) {
+    node.visitChildren(this);
+  }
+}
+
+/// Recursive visitor that returns a result of type [R] or `null` from its
+/// visit methods.
+class RecursiveResultVisitor<R> extends Visitor<R /*?*/ >
+    with VisitorNullMixin<R> {
+  const RecursiveResultVisitor();
+
   R defaultNode(Node node) {
     node.visitChildren(this);
     return null;
@@ -685,7 +796,7 @@
 abstract class ExpressionVisitor1<R, T> {
   const ExpressionVisitor1();
 
-  R defaultExpression(Expression node, T arg) => null;
+  R defaultExpression(Expression node, T arg);
   R defaultBasicLiteral(BasicLiteral node, T arg) =>
       defaultExpression(node, arg);
   R visitInvalidExpression(InvalidExpression node, T arg) =>
@@ -784,7 +895,7 @@
 abstract class StatementVisitor1<R, T> {
   const StatementVisitor1();
 
-  R defaultStatement(Statement node, T arg) => null;
+  R defaultStatement(Statement node, T arg);
 
   R visitExpressionStatement(ExpressionStatement node, T arg) =>
       defaultStatement(node, arg);
@@ -825,7 +936,7 @@
     implements StatementVisitor1<R, T> {
   const BodyVisitor1();
 
-  R defaultStatement(Statement node, T arg) => null;
+  R defaultStatement(Statement node, T arg);
   R visitExpressionStatement(ExpressionStatement node, T arg) =>
       defaultStatement(node, arg);
   R visitBlock(Block node, T arg) => defaultStatement(node, arg);
diff --git a/pkg/kernel/test/metadata_test.dart b/pkg/kernel/test/metadata_test.dart
index 377ad22..d1214b6 100644
--- a/pkg/kernel/test/metadata_test.dart
+++ b/pkg/kernel/test/metadata_test.dart
@@ -96,7 +96,7 @@
 
 /// Visitor calling [handle] function on every node which can have metadata
 /// associated with it and also satisfies the given [predicate].
-class Visitor extends RecursiveVisitor<Null> {
+class Visitor extends RecursiveVisitor {
   final NodePredicate predicate;
   final void Function(TreeNode) handle;
 
diff --git a/pkg/vm/lib/transformations/call_site_annotator.dart b/pkg/vm/lib/transformations/call_site_annotator.dart
index 324401f..8a45c5a 100644
--- a/pkg/vm/lib/transformations/call_site_annotator.dart
+++ b/pkg/vm/lib/transformations/call_site_annotator.dart
@@ -30,7 +30,7 @@
   libraries.forEach(transformer.visitLibrary);
 }
 
-class AnnotateWithStaticTypes extends RecursiveVisitor<Null> {
+class AnnotateWithStaticTypes extends RecursiveVisitor {
   final CallSiteAttributesMetadataRepository _metadata;
   final TypeEnvironment env;
   StaticTypeContext _staticTypeContext;
diff --git a/pkg/vm/lib/transformations/devirtualization.dart b/pkg/vm/lib/transformations/devirtualization.dart
index acbfa39..a81d089 100644
--- a/pkg/vm/lib/transformations/devirtualization.dart
+++ b/pkg/vm/lib/transformations/devirtualization.dart
@@ -27,7 +27,7 @@
 /// Subclasses should implement particular devirtualization strategy in
 /// [getDirectCall] method. Once direct target is determined, the invocation
 /// node is annotated with direct call metadata.
-abstract class Devirtualization extends RecursiveVisitor<Null> {
+abstract class Devirtualization extends RecursiveVisitor {
   /// Toggles tracing (useful for debugging).
   static const _trace = const bool.fromEnvironment('trace.devirtualization');
 
diff --git a/pkg/vm/lib/transformations/mixin_deduplication.dart b/pkg/vm/lib/transformations/mixin_deduplication.dart
index 7f76626..31481a8 100644
--- a/pkg/vm/lib/transformations/mixin_deduplication.dart
+++ b/pkg/vm/lib/transformations/mixin_deduplication.dart
@@ -153,7 +153,7 @@
 
 /// Rewrites references to the deduplicated mixin application
 /// classes. Updates interface targets and types.
-class ReferenceUpdater extends RecursiveVisitor<void> {
+class ReferenceUpdater extends RecursiveVisitor {
   final DeduplicateMixinsTransformer transformer;
   final _visitedConstants = new Set<Constant>.identity();
 
diff --git a/pkg/vm/lib/transformations/no_dynamic_invocations_annotator.dart b/pkg/vm/lib/transformations/no_dynamic_invocations_annotator.dart
index e838c80..9b7e27c 100644
--- a/pkg/vm/lib/transformations/no_dynamic_invocations_annotator.dart
+++ b/pkg/vm/lib/transformations/no_dynamic_invocations_annotator.dart
@@ -149,7 +149,7 @@
   }
 }
 
-class DynamicSelectorsCollector extends RecursiveVisitor<Null> {
+class DynamicSelectorsCollector extends RecursiveVisitor {
   final Set<Selector> dynamicSelectors = new Set<Selector>();
   final Set<Selector> nonThisSelectors = new Set<Selector>();
   final Set<Selector> tearOffSelectors = new Set<Selector>();
diff --git a/pkg/vm/lib/transformations/type_flow/signature_shaking.dart b/pkg/vm/lib/transformations/type_flow/signature_shaking.dart
index 4adf6c6..20a0412 100644
--- a/pkg/vm/lib/transformations/type_flow/signature_shaking.dart
+++ b/pkg/vm/lib/transformations/type_flow/signature_shaking.dart
@@ -198,7 +198,7 @@
   }
 }
 
-class _Collect extends RecursiveVisitor<void> {
+class _Collect extends RecursiveVisitor {
   final SignatureShaker shaker;
 
   /// Parameters of the current function.
@@ -332,7 +332,7 @@
   }
 }
 
-class _Transform extends RecursiveVisitor<void> {
+class _Transform extends RecursiveVisitor {
   final SignatureShaker shaker;
 
   StaticTypeContext typeContext;
diff --git a/pkg/vm/lib/transformations/type_flow/summary_collector.dart b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
index 789d1e1..aed40a03 100644
--- a/pkg/vm/lib/transformations/type_flow/summary_collector.dart
+++ b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
@@ -308,7 +308,7 @@
 
 /// Collects sets of captured variables, as well as variables
 /// modified in loops and try blocks.
-class _VariablesInfoCollector extends RecursiveVisitor<Null> {
+class _VariablesInfoCollector extends RecursiveVisitor {
   /// Maps declared variables to their declaration index.
   final Map<VariableDeclaration, int> varIndex = <VariableDeclaration, int>{};
 
@@ -514,7 +514,7 @@
 enum FieldSummaryType { kFieldGuard, kInitializer }
 
 /// Create a type flow summary for a member from the kernel AST.
-class SummaryCollector extends RecursiveVisitor<TypeExpr> {
+class SummaryCollector extends RecursiveResultVisitor<TypeExpr> {
   final Target target;
   final TypeEnvironment _environment;
   final ClosedWorldClassHierarchy _hierarchy;
diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart
index b8b187c..ba36509 100644
--- a/pkg/vm/lib/transformations/type_flow/transformer.dart
+++ b/pkg/vm/lib/transformations/type_flow/transformer.dart
@@ -144,7 +144,7 @@
 }
 
 /// Annotates kernel AST with metadata using results of type flow analysis.
-class AnnotateKernel extends RecursiveVisitor<Null> {
+class AnnotateKernel extends RecursiveVisitor {
   final TypeFlowAnalysis _typeFlowAnalysis;
   final FieldMorpher fieldMorpher;
   final DirectCallMetadataRepository _directCallMetadataRepository;
@@ -741,7 +741,7 @@
 /// Visits Dart types and collects all classes and typedefs used in types.
 /// This visitor is used during pass 1 of tree shaking. It is a separate
 /// visitor because [Transformer] does not provide a way to traverse types.
-class _TreeShakerTypeVisitor extends RecursiveVisitor<Null> {
+class _TreeShakerTypeVisitor extends RecursiveVisitor {
   final TreeShaker shaker;
 
   _TreeShakerTypeVisitor(this.shaker);
diff --git a/pkg/vm/test/incremental_compiler_test.dart b/pkg/vm/test/incremental_compiler_test.dart
index 179c37b..2fc1928 100644
--- a/pkg/vm/test/incremental_compiler_test.dart
+++ b/pkg/vm/test/incremental_compiler_test.dart
@@ -1618,7 +1618,7 @@
   await sink.close();
 }
 
-class LibraryReferenceCollector extends RecursiveVisitor<void> {
+class LibraryReferenceCollector extends RecursiveVisitor {
   Set<Library> librariesReferenced = {};
 
   void defaultMemberReference(Member node) {
diff --git a/pkg/vm/test/transformations/type_flow/summary_collector_test.dart b/pkg/vm/test/transformations/type_flow/summary_collector_test.dart
index 1e63109..a046634 100644
--- a/pkg/vm/test/transformations/type_flow/summary_collector_test.dart
+++ b/pkg/vm/test/transformations/type_flow/summary_collector_test.dart
@@ -61,7 +61,7 @@
   void recordTearOff(Procedure target) {}
 }
 
-class PrintSummaries extends RecursiveVisitor<Null> {
+class PrintSummaries extends RecursiveVisitor {
   SummaryCollector _summaryCollector;
   final StringBuffer _buf = new StringBuffer();
 
diff --git a/runtime/vm/class_id.h b/runtime/vm/class_id.h
index 5fb3b61..ed8a237 100644
--- a/runtime/vm/class_id.h
+++ b/runtime/vm/class_id.h
@@ -28,6 +28,7 @@
   V(Library)                                                                   \
   V(Namespace)                                                                 \
   V(KernelProgramInfo)                                                         \
+  V(WeakSerializationReference)                                                \
   V(Code)                                                                      \
   V(Instructions)                                                              \
   V(InstructionsSection)                                                       \
@@ -87,8 +88,7 @@
   V(LinkedHashMap)                                                             \
   V(FutureOr)                                                                  \
   V(UserTag)                                                                   \
-  V(TransferableTypedData)                                                     \
-  V(WeakSerializationReference)
+  V(TransferableTypedData)
 
 #define CLASS_LIST_ARRAYS(V)                                                   \
   V(Array)                                                                     \
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index c52c61f..805f9dc 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -1997,113 +1997,44 @@
 class WeakSerializationReferenceSerializationCluster
     : public SerializationCluster {
  public:
-  WeakSerializationReferenceSerializationCluster(Zone* zone, Heap* heap)
+  WeakSerializationReferenceSerializationCluster()
       : SerializationCluster(
             "WeakSerializationReference",
-            compiler::target::WeakSerializationReference::InstanceSize()),
-        heap_(ASSERT_NOTNULL(heap)),
-        objects_(zone, 0),
-        canonical_wsrs_(zone, 0),
-        canonical_wsr_map_(zone) {}
+            compiler::target::WeakSerializationReference::InstanceSize()) {}
   ~WeakSerializationReferenceSerializationCluster() {}
 
   void Trace(Serializer* s, ObjectPtr object) {
     ASSERT(s->kind() == Snapshot::kFullAOT);
-    // Make sure we don't trace again after choosing canonical WSRs.
-    ASSERT(!have_canonicalized_wsrs_);
-
-    auto const ref = WeakSerializationReference::RawCast(object);
-    objects_.Add(ref);
-    // We do _not_ push the target, since this is not a strong reference.
+    WeakSerializationReferencePtr weak =
+        WeakSerializationReference::RawCast(object);
+    objects_.Add(weak);
   }
 
+  intptr_t FinalizeWeak(Serializer* s) { return objects_.length(); }
+
   void WriteAlloc(Serializer* s) {
-    ASSERT(s->kind() == Snapshot::kFullAOT);
-    ASSERT(have_canonicalized_wsrs_);
-
     s->WriteCid(kWeakSerializationReferenceCid);
-    s->WriteUnsigned(WrittenCount());
-
-    // Set up references for those objects that will be written.
-    for (auto const& ref : canonical_wsrs_) {
-      s->AssignRef(ref);
-    }
-
-    // In precompiled mode, set the object ID of each non-canonical WSR to
-    // its canonical counterpart's object ID. This ensures that any reference to
-    // it is serialized as a reference to the canonicalized one.
-    for (auto const& ref : objects_) {
-      ASSERT(IsReachableReference(heap_->GetObjectId(ref)));
-      if (ShouldDrop(ref)) {
-        // For dropped references, reset their ID to be the unreachable
-        // reference value, so RefId retrieves the target ID instead.
-        heap_->SetObjectId(ref, kUnreachableReference);
-        continue;
-      }
-      // Skip if we've already allocated a reference (this is a canonical WSR).
-      if (IsAllocatedReference(heap_->GetObjectId(ref))) continue;
-      auto const target_cid = WeakSerializationReference::TargetClassIdOf(ref);
-      ASSERT(canonical_wsr_map_.HasKey(target_cid));
-      auto const canonical_index = canonical_wsr_map_.Lookup(target_cid) - 1;
-      auto const canonical_wsr = objects_[canonical_index];
-      // Set the object ID of this non-canonical WSR to the same as its
-      // canonical WSR entry, so we'll reference the canonical WSR when
-      // serializing references to this object.
-      auto const canonical_heap_id = heap_->GetObjectId(canonical_wsr);
-      ASSERT(IsAllocatedReference(canonical_heap_id));
-      heap_->SetObjectId(ref, canonical_heap_id);
-    }
   }
 
-  void WriteFill(Serializer* s) {
-    ASSERT(s->kind() == Snapshot::kFullAOT);
-    for (auto const& ref : canonical_wsrs_) {
-      AutoTraceObject(ref);
-
-      // In precompiled mode, we drop the reference to the target and only
-      // keep the class ID.
-      s->WriteCid(WeakSerializationReference::TargetClassIdOf(ref));
-    }
-  }
-
-  // Picks a WSR for each target class ID to be canonical. Should only be run
-  // after all objects have been traced.
-  void CanonicalizeReferences() {
-    ASSERT(!have_canonicalized_wsrs_);
+  void ForwardWeakRefs(Serializer* s) {
+    Heap* heap = s->heap();
     for (intptr_t i = 0; i < objects_.length(); i++) {
-      auto const ref = objects_[i];
-      if (ShouldDrop(ref)) continue;
-      auto const target_cid = WeakSerializationReference::TargetClassIdOf(ref);
-      if (canonical_wsr_map_.HasKey(target_cid)) continue;
-      canonical_wsr_map_.Insert(target_cid, i + 1);
-      canonical_wsrs_.Add(ref);
+      WeakSerializationReferencePtr weak = objects_[i];
+
+      intptr_t id = heap->GetObjectId(weak->untag()->target());
+      if (id == kUnreachableReference) {
+        id = heap->GetObjectId(weak->untag()->replacement());
+        ASSERT(id != kUnreachableReference);
+      }
+      ASSERT(IsAllocatedReference(id));
+      heap->SetObjectId(weak, id);
     }
-    have_canonicalized_wsrs_ = true;
   }
 
-  intptr_t WrittenCount() const {
-    ASSERT(have_canonicalized_wsrs_);
-    return canonical_wsrs_.length();
-  }
-
-  intptr_t DroppedCount() const { return TotalCount() - WrittenCount(); }
-
-  intptr_t TotalCount() const { return objects_.length(); }
+  void WriteFill(Serializer* s) {}
 
  private:
-  // Returns whether a WSR should be dropped due to its target being reachable
-  // via strong references. WSRs only wrap heap objects, so we can just retrieve
-  // the object ID from the heap directly.
-  bool ShouldDrop(WeakSerializationReferencePtr ref) const {
-    auto const target = WeakSerializationReference::TargetOf(ref);
-    return IsReachableReference(heap_->GetObjectId(target));
-  }
-
-  Heap* const heap_;
   GrowableArray<WeakSerializationReferencePtr> objects_;
-  GrowableArray<WeakSerializationReferencePtr> canonical_wsrs_;
-  IntMap<intptr_t> canonical_wsr_map_;
-  bool have_canonicalized_wsrs_ = false;
 };
 #endif
 
@@ -2115,29 +2046,8 @@
       : DeserializationCluster("WeakSerializationReference") {}
   ~WeakSerializationReferenceDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool is_canonical) {
-    start_index_ = d->next_index();
-    PageSpace* old_space = d->heap()->old_space();
-    const intptr_t count = d->ReadUnsigned();
-
-    for (intptr_t i = 0; i < count; i++) {
-      auto ref = AllocateUninitialized(
-          old_space, WeakSerializationReference::InstanceSize());
-      d->AssignRef(ref);
-    }
-
-    stop_index_ = d->next_index();
-  }
-
-  void ReadFill(Deserializer* d, bool is_canonical) {
-    for (intptr_t id = start_index_; id < stop_index_; id++) {
-      auto const ref = static_cast<WeakSerializationReferencePtr>(d->Ref(id));
-      Deserializer::InitializeHeader(
-          ref, kWeakSerializationReferenceCid,
-          WeakSerializationReference::InstanceSize());
-      ref->untag()->cid_ = d->ReadCid();
-    }
-  }
+  void ReadAlloc(Deserializer* d, bool stamp_canonical) {}
+  void ReadFill(Deserializer* d, bool stamp_canonical) {}
 };
 #endif
 
@@ -5008,7 +4918,7 @@
  public:
   VMDeserializationRoots() : symbol_table_(Array::Handle()) {}
 
-  void AddBaseObjects(Deserializer* d) {
+  bool AddBaseObjects(Deserializer* d) {
     // These objects are always allocated by Object::InitOnce, so they are not
     // written into the snapshot.
 
@@ -5057,6 +4967,8 @@
         d->AddBaseObject(StubCode::EntryAt(i).ptr());
       }
     }
+
+    return true;  // primary
   }
 
   void ReadRoots(Deserializer* d) {
@@ -5101,43 +5013,26 @@
                             ObjectStore* object_store)
       : base_objects_(base_objects),
         object_store_(object_store),
-        saved_symbol_table_(Array::Handle()),
-        saved_canonical_types_(Array::Handle()),
-        saved_canonical_function_types_(Array::Handle()),
-        saved_canonical_type_parameters_(Array::Handle()),
-        saved_canonical_type_arguments_(Array::Handle()),
         dispatch_table_entries_(Array::Handle()) {
-    saved_symbol_table_ = object_store->symbol_table();
-    object_store->set_symbol_table(
-        Array::Handle(HashTables::New<CanonicalStringSet>(4)));
-
-    saved_canonical_types_ = object_store->canonical_types();
-    object_store->set_canonical_types(
-        Array::Handle(HashTables::New<CanonicalTypeSet>(4)));
-
-    saved_canonical_function_types_ = object_store->canonical_function_types();
-    object_store->set_canonical_function_types(
-        Array::Handle(HashTables::New<CanonicalFunctionTypeSet>(4)));
-
-    saved_canonical_type_parameters_ =
-        object_store->canonical_type_parameters();
-    object_store->set_canonical_type_parameters(
-        Array::Handle(HashTables::New<CanonicalTypeParameterSet>(4)));
-
-    saved_canonical_type_arguments_ = object_store->canonical_type_arguments();
-    object_store->set_canonical_type_arguments(
-        Array::Handle(HashTables::New<CanonicalTypeArgumentsSet>(4)));
+#if defined(DART_PRECOMPILER)
+    if (FLAG_precompiled_mode) {
+      // Elements of constant tables are treated as weak so literals used only
+      // in deferred libraries do not end up in the main snapshot.
+      Array& table = Array::Handle();
+      table = object_store->symbol_table();
+      HashTables::Weaken(table);
+      table = object_store->canonical_types();
+      HashTables::Weaken(table);
+      table = object_store->canonical_function_types();
+      HashTables::Weaken(table);
+      table = object_store->canonical_type_parameters();
+      HashTables::Weaken(table);
+      table = object_store->canonical_type_arguments();
+      HashTables::Weaken(table);
+    }
+#endif
   }
-  ~ProgramSerializationRoots() {
-    object_store_->set_symbol_table(saved_symbol_table_);
-    object_store_->set_canonical_types(saved_canonical_types_);
-    object_store_->set_canonical_function_types(
-        saved_canonical_function_types_);
-    object_store_->set_canonical_type_parameters(
-        saved_canonical_type_parameters_);
-    object_store_->set_canonical_type_arguments(
-        saved_canonical_type_arguments_);
-  }
+  ~ProgramSerializationRoots() {}
 
   void AddBaseObjects(Serializer* s) {
     if (base_objects_ == nullptr) {
@@ -5192,11 +5087,6 @@
  private:
   ZoneGrowableArray<Object*>* base_objects_;
   ObjectStore* object_store_;
-  Array& saved_symbol_table_;
-  Array& saved_canonical_types_;
-  Array& saved_canonical_function_types_;
-  Array& saved_canonical_type_parameters_;
-  Array& saved_canonical_type_arguments_;
   Array& dispatch_table_entries_;
 };
 #endif  // !DART_PRECOMPILED_RUNTIME
@@ -5206,12 +5096,13 @@
   explicit ProgramDeserializationRoots(ObjectStore* object_store)
       : object_store_(object_store) {}
 
-  void AddBaseObjects(Deserializer* d) {
+  bool AddBaseObjects(Deserializer* d) {
     // N.B.: Skipping index 0 because ref 0 is illegal.
     const Array& base_objects = Object::vm_isolate_snapshot_object_table();
     for (intptr_t i = kFirstReference; i < base_objects.Length(); i++) {
       d->AddBaseObject(base_objects.At(i));
     }
+    return true;  // primary
   }
 
   void ReadRoots(Deserializer* d) {
@@ -5312,12 +5203,13 @@
  public:
   explicit UnitDeserializationRoots(const LoadingUnit& unit) : unit_(unit) {}
 
-  void AddBaseObjects(Deserializer* d) {
+  bool AddBaseObjects(Deserializer* d) {
     const Array& base_objects =
         Array::Handle(LoadingUnit::Handle(unit_.parent()).base_objects());
     for (intptr_t i = kFirstReference; i < base_objects.Length(); i++) {
       d->AddBaseObject(base_objects.At(i));
     }
+    return false;  // primary
   }
 
   void ReadRoots(Deserializer* d) {
@@ -5326,7 +5218,8 @@
     for (intptr_t id = deferred_start_index_; id < deferred_stop_index_; id++) {
       CodePtr code = static_cast<CodePtr>(d->Ref(id));
       d->ReadInstructions(code, false);
-      if (code->untag()->owner_->IsFunction()) {
+      if (code->untag()->owner_->IsHeapObject() &&
+          code->untag()->owner_->IsFunction()) {
         FunctionPtr func = static_cast<FunctionPtr>(code->untag()->owner_);
         uword entry_point = code->untag()->entry_point_;
         ASSERT(entry_point != 0);
@@ -5731,8 +5624,7 @@
     case kWeakSerializationReferenceCid:
 #if defined(DART_PRECOMPILER)
       ASSERT(kind_ == Snapshot::kFullAOT);
-      return new (Z)
-          WeakSerializationReferenceSerializationCluster(zone_, heap_);
+      return new (Z) WeakSerializationReferenceSerializationCluster();
 #endif
     default:
       break;
@@ -5988,16 +5880,10 @@
   }
 
 #if defined(DART_PRECOMPILER)
-  // Before we finalize the count of written objects, pick canonical versions
-  // of WSR objects that will be serialized and then remove any non-serialized
-  // or non-canonical WSR objects from that count.
   if (auto const cluster =
           reinterpret_cast<WeakSerializationReferenceSerializationCluster*>(
               clusters_by_cid_[kWeakSerializationReferenceCid])) {
-    cluster->CanonicalizeReferences();
-    auto const dropped_count = cluster->DroppedCount();
-    ASSERT(dropped_count == 0 || kind() == Snapshot::kFullAOT);
-    num_written_objects_ -= dropped_count;
+    num_written_objects_ -= cluster->FinalizeWeak(this);
   }
 #endif
 
@@ -6038,6 +5924,12 @@
   ASSERT(objects_->length() == num_objects);
 
 #if defined(DART_PRECOMPILER)
+  if (auto cluster =
+          reinterpret_cast<WeakSerializationReferenceSerializationCluster*>(
+              clusters_by_cid_[kWeakSerializationReferenceCid])) {
+    cluster->ForwardWeakRefs(this);
+  }
+
   // When writing snapshot profile, we want to retain some of the program
   // structure information (e.g. information about libraries, classes and
   // functions - even if it was dropped when writing snapshot itself).
@@ -6760,6 +6652,7 @@
     ASSERT_EQUAL(initial_field_table_->NumFieldIds(), initial_field_table_len);
   }
 
+  bool primary;
   {
     // The deserializer initializes objects without using the write barrier,
     // partly for speed since we know all the deserialized objects will be
@@ -6777,7 +6670,7 @@
     NoSafepointScope no_safepoint;
     refs_ = refs.ptr();
 
-    roots->AddBaseObjects(this);
+    primary = roots->AddBaseObjects(this);
 
     if (num_base_objects_ != (next_ref_index_ - kFirstReference)) {
       FATAL2("Snapshot expects %" Pd
@@ -6813,8 +6706,8 @@
     {
       TIMELINE_DURATION(thread(), Isolate, "ReadFill");
       for (intptr_t i = 0; i < num_canonical_clusters_; i++) {
-        TIMELINE_DURATION(thread(), Isolate, canonical_clusters_[i]->name());
-        canonical_clusters_[i]->ReadFill(this, /*is_canonical*/ true);
+        bool stamp_canonical = primary;
+        canonical_clusters_[i]->ReadFill(this, stamp_canonical);
 #if defined(DEBUG)
         int32_t section_marker = Read<int32_t>();
         ASSERT(section_marker == kSectionMarker);
@@ -6858,7 +6751,8 @@
     TIMELINE_DURATION(thread(), Isolate, "PostLoad");
     for (intptr_t i = 0; i < num_canonical_clusters_; i++) {
       TIMELINE_DURATION(thread(), Isolate, canonical_clusters_[i]->name());
-      canonical_clusters_[i]->PostLoad(this, refs, /*is_canonical*/ true);
+      bool canonicalize = !primary;
+      canonical_clusters_[i]->PostLoad(this, refs, canonicalize);
     }
     for (intptr_t i = 0; i < num_clusters_; i++) {
       TIMELINE_DURATION(thread(), Isolate, clusters_[i]->name());
diff --git a/runtime/vm/clustered_snapshot.h b/runtime/vm/clustered_snapshot.h
index d69e4443..fcd5d90 100644
--- a/runtime/vm/clustered_snapshot.h
+++ b/runtime/vm/clustered_snapshot.h
@@ -163,7 +163,11 @@
 class DeserializationRoots {
  public:
   virtual ~DeserializationRoots() {}
-  virtual void AddBaseObjects(Deserializer* deserializer) = 0;
+  // Returns true if these roots are the first snapshot loaded into a heap, and
+  // so can assume any canonical objects don't already exist. Returns false if
+  // some other snapshot may be loaded before these roots, and so written
+  // canonical objects need to run canoncalization during load.
+  virtual bool AddBaseObjects(Deserializer* deserializer) = 0;
   virtual void ReadRoots(Deserializer* deserializer) = 0;
   virtual void PostLoad(Deserializer* deserializer, const Array& refs) = 0;
 };
@@ -412,17 +416,8 @@
       return -id;
     }
     ASSERT(!IsArtificialReference(id));
-    if (IsAllocatedReference(id)) return id;
-    if (object->IsWeakSerializationReference()) {
-      // If a reachable WSR has an object ID of 0, then its target was marked
-      // for serialization due to reachable strong references and the WSR will
-      // be dropped instead. Thus, we change the reference to the WSR to a
-      // direct reference to the serialized target.
-      auto const ref = WeakSerializationReference::RawCast(object);
-      auto const target = WeakSerializationReference::TargetOf(ref);
-      auto const target_id = heap_->GetObjectId(target);
-      ASSERT(IsAllocatedReference(target_id));
-      return target_id;
+    if (IsAllocatedReference(id)) {
+      return id;
     }
     if (object->IsCode() && !Snapshot::IncludesCode(kind_)) {
       return RefId(Object::null());
diff --git a/runtime/vm/compiler/aot/precompiler.cc b/runtime/vm/compiler/aot/precompiler.cc
index 855cdca..50a67e9 100644
--- a/runtime/vm/compiler/aot/precompiler.cc
+++ b/runtime/vm/compiler/aot/precompiler.cc
@@ -1749,7 +1749,8 @@
       // Wrap the owner of the code object in case the code object will be
       // serialized but the function object will not.
       owner = code.owner();
-      owner = WeakSerializationReference::Wrap(Z, owner);
+      owner = WeakSerializationReference::New(
+          owner, Smi::Handle(Smi::New(owner.GetClassId())));
       code.set_owner(owner);
     }
     dropped_function_count_++;
@@ -2287,6 +2288,14 @@
   ClassTable* class_table = IG->class_table();
   intptr_t num_cids = class_table->NumCids();
 
+  for (intptr_t cid = 0; cid < num_cids; cid++) {
+    if (!class_table->IsValidIndex(cid)) continue;
+    if (!class_table->HasValidClassAt(cid)) continue;
+    cls = class_table->At(cid);
+    constants = cls.constants();
+    HashTables::Weaken(constants);
+  }
+
   for (intptr_t cid = kNumPredefinedCids; cid < num_cids; cid++) {
     if (!class_table->IsValidIndex(cid)) continue;
     if (!class_table->HasValidClassAt(cid)) continue;
diff --git a/runtime/vm/compiler/runtime_offsets_extracted.h b/runtime/vm/compiler/runtime_offsets_extracted.h
index c4743e3..35ef1e3 100644
--- a/runtime/vm/compiler/runtime_offsets_extracted.h
+++ b/runtime/vm/compiler/runtime_offsets_extracted.h
@@ -544,7 +544,7 @@
 static constexpr dart::compiler::target::word UserTag_InstanceSize = 12;
 static constexpr dart::compiler::target::word WeakProperty_InstanceSize = 16;
 static constexpr dart::compiler::target::word
-    WeakSerializationReference_InstanceSize = 8;
+    WeakSerializationReference_InstanceSize = 12;
 #endif  // defined(TARGET_ARCH_ARM) && !defined(DART_COMPRESSED_POINTERS)
 
 #if defined(TARGET_ARCH_X64) && !defined(DART_COMPRESSED_POINTERS)
@@ -1082,7 +1082,7 @@
 static constexpr dart::compiler::target::word UserTag_InstanceSize = 24;
 static constexpr dart::compiler::target::word WeakProperty_InstanceSize = 32;
 static constexpr dart::compiler::target::word
-    WeakSerializationReference_InstanceSize = 16;
+    WeakSerializationReference_InstanceSize = 24;
 #endif  // defined(TARGET_ARCH_X64) && !defined(DART_COMPRESSED_POINTERS)
 
 #if defined(TARGET_ARCH_IA32) && !defined(DART_COMPRESSED_POINTERS)
@@ -1609,7 +1609,7 @@
 static constexpr dart::compiler::target::word UserTag_InstanceSize = 12;
 static constexpr dart::compiler::target::word WeakProperty_InstanceSize = 16;
 static constexpr dart::compiler::target::word
-    WeakSerializationReference_InstanceSize = 8;
+    WeakSerializationReference_InstanceSize = 12;
 #endif  // defined(TARGET_ARCH_IA32) && !defined(DART_COMPRESSED_POINTERS)
 
 #if defined(TARGET_ARCH_ARM64) && !defined(DART_COMPRESSED_POINTERS)
@@ -2148,7 +2148,7 @@
 static constexpr dart::compiler::target::word UserTag_InstanceSize = 24;
 static constexpr dart::compiler::target::word WeakProperty_InstanceSize = 32;
 static constexpr dart::compiler::target::word
-    WeakSerializationReference_InstanceSize = 16;
+    WeakSerializationReference_InstanceSize = 24;
 #endif  // defined(TARGET_ARCH_ARM64) && !defined(DART_COMPRESSED_POINTERS)
 
 #if defined(TARGET_ARCH_X64) && defined(DART_COMPRESSED_POINTERS)
@@ -2686,7 +2686,7 @@
 static constexpr dart::compiler::target::word UserTag_InstanceSize = 24;
 static constexpr dart::compiler::target::word WeakProperty_InstanceSize = 32;
 static constexpr dart::compiler::target::word
-    WeakSerializationReference_InstanceSize = 16;
+    WeakSerializationReference_InstanceSize = 24;
 #endif  // defined(TARGET_ARCH_X64) && defined(DART_COMPRESSED_POINTERS)
 
 #if defined(TARGET_ARCH_ARM64) && defined(DART_COMPRESSED_POINTERS)
@@ -3225,7 +3225,7 @@
 static constexpr dart::compiler::target::word UserTag_InstanceSize = 24;
 static constexpr dart::compiler::target::word WeakProperty_InstanceSize = 32;
 static constexpr dart::compiler::target::word
-    WeakSerializationReference_InstanceSize = 16;
+    WeakSerializationReference_InstanceSize = 24;
 #endif  // defined(TARGET_ARCH_ARM64) && defined(DART_COMPRESSED_POINTERS)
 
 #else  // !defined(PRODUCT)
@@ -3751,7 +3751,7 @@
 static constexpr dart::compiler::target::word UserTag_InstanceSize = 12;
 static constexpr dart::compiler::target::word WeakProperty_InstanceSize = 16;
 static constexpr dart::compiler::target::word
-    WeakSerializationReference_InstanceSize = 8;
+    WeakSerializationReference_InstanceSize = 12;
 #endif  // defined(TARGET_ARCH_ARM) && !defined(DART_COMPRESSED_POINTERS)
 
 #if defined(TARGET_ARCH_X64) && !defined(DART_COMPRESSED_POINTERS)
@@ -4283,7 +4283,7 @@
 static constexpr dart::compiler::target::word UserTag_InstanceSize = 24;
 static constexpr dart::compiler::target::word WeakProperty_InstanceSize = 32;
 static constexpr dart::compiler::target::word
-    WeakSerializationReference_InstanceSize = 16;
+    WeakSerializationReference_InstanceSize = 24;
 #endif  // defined(TARGET_ARCH_X64) && !defined(DART_COMPRESSED_POINTERS)
 
 #if defined(TARGET_ARCH_IA32) && !defined(DART_COMPRESSED_POINTERS)
@@ -4804,7 +4804,7 @@
 static constexpr dart::compiler::target::word UserTag_InstanceSize = 12;
 static constexpr dart::compiler::target::word WeakProperty_InstanceSize = 16;
 static constexpr dart::compiler::target::word
-    WeakSerializationReference_InstanceSize = 8;
+    WeakSerializationReference_InstanceSize = 12;
 #endif  // defined(TARGET_ARCH_IA32) && !defined(DART_COMPRESSED_POINTERS)
 
 #if defined(TARGET_ARCH_ARM64) && !defined(DART_COMPRESSED_POINTERS)
@@ -5337,7 +5337,7 @@
 static constexpr dart::compiler::target::word UserTag_InstanceSize = 24;
 static constexpr dart::compiler::target::word WeakProperty_InstanceSize = 32;
 static constexpr dart::compiler::target::word
-    WeakSerializationReference_InstanceSize = 16;
+    WeakSerializationReference_InstanceSize = 24;
 #endif  // defined(TARGET_ARCH_ARM64) && !defined(DART_COMPRESSED_POINTERS)
 
 #if defined(TARGET_ARCH_X64) && defined(DART_COMPRESSED_POINTERS)
@@ -5869,7 +5869,7 @@
 static constexpr dart::compiler::target::word UserTag_InstanceSize = 24;
 static constexpr dart::compiler::target::word WeakProperty_InstanceSize = 32;
 static constexpr dart::compiler::target::word
-    WeakSerializationReference_InstanceSize = 16;
+    WeakSerializationReference_InstanceSize = 24;
 #endif  // defined(TARGET_ARCH_X64) && defined(DART_COMPRESSED_POINTERS)
 
 #if defined(TARGET_ARCH_ARM64) && defined(DART_COMPRESSED_POINTERS)
@@ -6402,7 +6402,7 @@
 static constexpr dart::compiler::target::word UserTag_InstanceSize = 24;
 static constexpr dart::compiler::target::word WeakProperty_InstanceSize = 32;
 static constexpr dart::compiler::target::word
-    WeakSerializationReference_InstanceSize = 16;
+    WeakSerializationReference_InstanceSize = 24;
 #endif  // defined(TARGET_ARCH_ARM64) && defined(DART_COMPRESSED_POINTERS)
 
 #endif  // !defined(PRODUCT)
@@ -6997,7 +6997,7 @@
 static constexpr dart::compiler::target::word AOT_WeakProperty_InstanceSize =
     16;
 static constexpr dart::compiler::target::word
-    AOT_WeakSerializationReference_InstanceSize = 8;
+    AOT_WeakSerializationReference_InstanceSize = 12;
 #endif  // defined(TARGET_ARCH_ARM) && !defined(DART_COMPRESSED_POINTERS)
 
 #if defined(TARGET_ARCH_X64) && !defined(DART_COMPRESSED_POINTERS)
@@ -7592,7 +7592,7 @@
 static constexpr dart::compiler::target::word AOT_WeakProperty_InstanceSize =
     32;
 static constexpr dart::compiler::target::word
-    AOT_WeakSerializationReference_InstanceSize = 16;
+    AOT_WeakSerializationReference_InstanceSize = 24;
 #endif  // defined(TARGET_ARCH_X64) && !defined(DART_COMPRESSED_POINTERS)
 
 #if defined(TARGET_ARCH_IA32) && !defined(DART_COMPRESSED_POINTERS)
@@ -8191,7 +8191,7 @@
 static constexpr dart::compiler::target::word AOT_WeakProperty_InstanceSize =
     32;
 static constexpr dart::compiler::target::word
-    AOT_WeakSerializationReference_InstanceSize = 16;
+    AOT_WeakSerializationReference_InstanceSize = 24;
 #endif  // defined(TARGET_ARCH_ARM64) && !defined(DART_COMPRESSED_POINTERS)
 
 #if defined(TARGET_ARCH_X64) && defined(DART_COMPRESSED_POINTERS)
@@ -8786,7 +8786,7 @@
 static constexpr dart::compiler::target::word AOT_WeakProperty_InstanceSize =
     32;
 static constexpr dart::compiler::target::word
-    AOT_WeakSerializationReference_InstanceSize = 16;
+    AOT_WeakSerializationReference_InstanceSize = 24;
 #endif  // defined(TARGET_ARCH_X64) && defined(DART_COMPRESSED_POINTERS)
 
 #if defined(TARGET_ARCH_ARM64) && defined(DART_COMPRESSED_POINTERS)
@@ -9382,7 +9382,7 @@
 static constexpr dart::compiler::target::word AOT_WeakProperty_InstanceSize =
     32;
 static constexpr dart::compiler::target::word
-    AOT_WeakSerializationReference_InstanceSize = 16;
+    AOT_WeakSerializationReference_InstanceSize = 24;
 #endif  // defined(TARGET_ARCH_ARM64) && defined(DART_COMPRESSED_POINTERS)
 
 #else  // !defined(PRODUCT)
@@ -9968,7 +9968,7 @@
 static constexpr dart::compiler::target::word AOT_WeakProperty_InstanceSize =
     16;
 static constexpr dart::compiler::target::word
-    AOT_WeakSerializationReference_InstanceSize = 8;
+    AOT_WeakSerializationReference_InstanceSize = 12;
 #endif  // defined(TARGET_ARCH_ARM) && !defined(DART_COMPRESSED_POINTERS)
 
 #if defined(TARGET_ARCH_X64) && !defined(DART_COMPRESSED_POINTERS)
@@ -10556,7 +10556,7 @@
 static constexpr dart::compiler::target::word AOT_WeakProperty_InstanceSize =
     32;
 static constexpr dart::compiler::target::word
-    AOT_WeakSerializationReference_InstanceSize = 16;
+    AOT_WeakSerializationReference_InstanceSize = 24;
 #endif  // defined(TARGET_ARCH_X64) && !defined(DART_COMPRESSED_POINTERS)
 
 #if defined(TARGET_ARCH_IA32) && !defined(DART_COMPRESSED_POINTERS)
@@ -11148,7 +11148,7 @@
 static constexpr dart::compiler::target::word AOT_WeakProperty_InstanceSize =
     32;
 static constexpr dart::compiler::target::word
-    AOT_WeakSerializationReference_InstanceSize = 16;
+    AOT_WeakSerializationReference_InstanceSize = 24;
 #endif  // defined(TARGET_ARCH_ARM64) && !defined(DART_COMPRESSED_POINTERS)
 
 #if defined(TARGET_ARCH_X64) && defined(DART_COMPRESSED_POINTERS)
@@ -11736,7 +11736,7 @@
 static constexpr dart::compiler::target::word AOT_WeakProperty_InstanceSize =
     32;
 static constexpr dart::compiler::target::word
-    AOT_WeakSerializationReference_InstanceSize = 16;
+    AOT_WeakSerializationReference_InstanceSize = 24;
 #endif  // defined(TARGET_ARCH_X64) && defined(DART_COMPRESSED_POINTERS)
 
 #if defined(TARGET_ARCH_ARM64) && defined(DART_COMPRESSED_POINTERS)
@@ -12325,7 +12325,7 @@
 static constexpr dart::compiler::target::word AOT_WeakProperty_InstanceSize =
     32;
 static constexpr dart::compiler::target::word
-    AOT_WeakSerializationReference_InstanceSize = 16;
+    AOT_WeakSerializationReference_InstanceSize = 24;
 #endif  // defined(TARGET_ARCH_ARM64) && defined(DART_COMPRESSED_POINTERS)
 
 #endif  // !defined(PRODUCT)
diff --git a/runtime/vm/hash_table.h b/runtime/vm/hash_table.h
index cb6d01d..ef89556 100644
--- a/runtime/vm/hash_table.h
+++ b/runtime/vm/hash_table.h
@@ -152,7 +152,9 @@
   template <typename Key>
   intptr_t FindKey(const Key& key) const {
     const intptr_t num_entries = NumEntries();
-    ASSERT(NumOccupied() < num_entries);
+    // Deleted may undercount due to weak references used during AOT
+    // snapshotting.
+    NOT_IN_PRECOMPILED(ASSERT(NumOccupied() < num_entries));
     // TODO(koda): Add salt.
     NOT_IN_PRODUCT(intptr_t collisions = 0;)
     uword hash = KeyTraits::Hash(key);
@@ -188,7 +190,9 @@
   bool FindKeyOrDeletedOrUnused(const Key& key, intptr_t* entry) const {
     const intptr_t num_entries = NumEntries();
     ASSERT(entry != NULL);
-    ASSERT(NumOccupied() < num_entries);
+    // Deleted may undercount due to weak references used during AOT
+    // snapshotting.
+    NOT_IN_PRECOMPILED(ASSERT(NumOccupied() < num_entries));
     NOT_IN_PRODUCT(intptr_t collisions = 0;)
     uword hash = KeyTraits::Hash(key);
     ASSERT(Utils::IsPowerOfTwo(num_entries));
@@ -236,7 +240,9 @@
     }
     InternalSetKey(entry, key);
     ASSERT(IsOccupied(entry));
-    ASSERT(NumOccupied() < NumEntries());
+    // Deleted may undercount due to weak references used during AOT
+    // snapshotting.
+    NOT_IN_PRECOMPILED(ASSERT(NumOccupied() < NumEntries()));
   }
 
   const Object& UnusedMarker() const { return Object::transition_sentinel(); }
@@ -258,7 +264,8 @@
   }
   ObjectPtr GetPayload(intptr_t entry, intptr_t component) const {
     ASSERT(IsOccupied(entry));
-    return data_->At(PayloadIndex(entry, component));
+    return WeakSerializationReference::Unwrap(
+        data_->At(PayloadIndex(entry, component)));
   }
   void UpdatePayload(intptr_t entry,
                      intptr_t component,
@@ -371,7 +378,7 @@
   }
 
   ObjectPtr InternalGetKey(intptr_t entry) const {
-    return data_->At(KeyIndex(entry));
+    return WeakSerializationReference::Unwrap(data_->At(KeyIndex(entry)));
   }
 
   void InternalSetKey(intptr_t entry, const Object& key) const {
@@ -531,6 +538,22 @@
     }
     return result.ptr();
   }
+
+#if defined(DART_PRECOMPILER)
+  // Replace elements of this set with WeakSerializationReferences.
+  static void Weaken(const Array& table) {
+    if (!table.IsNull()) {
+      Object& element = Object::Handle();
+      for (intptr_t i = 0; i < table.Length(); i++) {
+        element = table.At(i);
+        if (!element.IsSmi()) {
+          element = WeakSerializationReference::New(element, table);
+          table.SetAt(i, element);
+        }
+      }
+    }
+  }
+#endif
 };
 
 template <typename BaseIterTable>
@@ -724,7 +747,7 @@
   void Dump() const {
     Object& entry = Object::Handle();
     for (intptr_t i = 0; i < this->data_->Length(); i++) {
-      entry = this->data_->At(i);
+      entry = WeakSerializationReference::Unwrap(this->data_->At(i));
       if (entry.ptr() == BaseSet::UnusedMarker().ptr() ||
           entry.ptr() == BaseSet::DeletedMarker().ptr() || entry.IsSmi()) {
         // empty, deleted, num_used/num_deleted
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 0626dc1..dcb54c3 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -1325,6 +1325,7 @@
   SET_CLASS_NAME(library, LibraryClass);
   SET_CLASS_NAME(namespace, Namespace);
   SET_CLASS_NAME(kernel_program_info, KernelProgramInfo);
+  SET_CLASS_NAME(weak_serialization_reference, WeakSerializationReference);
   SET_CLASS_NAME(code, Code);
   SET_CLASS_NAME(instructions, Instructions);
   SET_CLASS_NAME(instructions_section, InstructionsSection);
@@ -4767,6 +4768,8 @@
       return Symbols::Namespace().ToCString();
     case kKernelProgramInfoCid:
       return Symbols::KernelProgramInfo().ToCString();
+    case kWeakSerializationReferenceCid:
+      return Symbols::WeakSerializationReference().ToCString();
     case kCodeCid:
       return Symbols::Code().ToCString();
     case kInstructionsCid:
@@ -16027,26 +16030,14 @@
 #endif
 
 const char* WeakSerializationReference::ToCString() const {
-#if defined(DART_PRECOMPILED_RUNTIME)
-  return Symbols::OptimizedOut().ToCString();
-#else
   return Object::Handle(target()).ToCString();
-#endif
 }
 
-#if defined(DART_PRECOMPILER)
-bool WeakSerializationReference::CanWrap(const Object& object) {
-  // Currently we do not wrap the null object (which cannot be dropped from
-  // snapshots), non-heap objects, and WSRs (as there is no point in deeply
-  // nesting them). We also only wrap objects in the precompiler.
-  return FLAG_precompiled_mode && !object.IsNull() &&
-         object.ptr()->IsHeapObject() && !object.IsWeakSerializationReference();
-}
-
-ObjectPtr WeakSerializationReference::Wrap(Zone* zone, const Object& target) {
-  if (!CanWrap(target)) return target.ptr();
+WeakSerializationReferencePtr WeakSerializationReference::New(
+    const Object& target,
+    const Object& replacement) {
   ASSERT(Object::weak_serialization_reference_class() != Class::null());
-  WeakSerializationReference& result = WeakSerializationReference::Handle(zone);
+  WeakSerializationReference& result = WeakSerializationReference::Handle();
   {
     ObjectPtr raw = Object::Allocate(WeakSerializationReference::kClassId,
                                      WeakSerializationReference::InstanceSize(),
@@ -16055,10 +16046,10 @@
 
     result ^= raw;
     result.untag()->set_target(target.ptr());
+    result.untag()->set_replacement(replacement.ptr());
   }
   return result.ptr();
 }
-#endif
 
 #if defined(INCLUDE_IL_PRINTER)
 Code::Comments& Code::Comments::New(intptr_t count) {
@@ -24772,6 +24763,7 @@
   auto const T = Thread::Current();
   auto const zone = T->zone();
   auto& stack_trace = StackTrace::Handle(zone, this->ptr());
+  auto& owner = Object::Handle(zone);
   auto& function = Function::Handle(zone);
   auto& code_object = Object::Handle(zone);
   auto& code = Code::Handle(zone);
@@ -24861,7 +24853,12 @@
       ASSERT(code_object.IsCode());
       code ^= code_object.ptr();
       ASSERT(code.IsFunctionCode());
-      function = code.function();
+      owner = code.owner();
+      if (owner.IsFunction()) {
+        function ^= owner.ptr();
+      } else {
+        function = Function::null();
+      }
       const uword pc = code.PayloadStart() + pc_offset;
 
       // If the function is not to be shown, skip.
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 6449bf2..0739a20 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -5853,87 +5853,32 @@
 //
 // Current uses of WSRs:
 //  * Code::owner_
+//  * Canonical table elements
 class WeakSerializationReference : public Object {
  public:
   ObjectPtr target() const { return TargetOf(ptr()); }
-  static ObjectPtr TargetOf(const WeakSerializationReferencePtr raw) {
-#if defined(DART_PRECOMPILED_RUNTIME)
-    // WSRs in the precompiled runtime only contain some remaining info about
-    // their old target, not a reference to the target itself..
-    return Object::null();
-#else
-    // Outside the precompiled runtime, they should always have a target.
-    ASSERT(raw->untag()->target() != Object::null());
-    return raw->untag()->target();
-#endif
+  static ObjectPtr TargetOf(const WeakSerializationReferencePtr obj) {
+    return obj->untag()->target();
   }
 
-  classid_t TargetClassId() const { return TargetClassIdOf(ptr()); }
-  static classid_t TargetClassIdOf(const WeakSerializationReferencePtr raw) {
-#if defined(DART_PRECOMPILED_RUNTIME)
-    // No new instances of WSRs are created in the precompiled runtime, so
-    // this instance came from deserialization and thus must be the empty WSR.
-    return raw->untag()->cid_;
-#else
-    return TargetOf(raw)->GetClassId();
-#endif
-  }
-
-  static ObjectPtr Unwrap(const Object& obj) { return Unwrap(obj.ptr()); }
-  // Gets the underlying object from a WSR, or the original object if it is
-  // not one. Notably, Unwrap(Wrap(r)) == r for all raw objects r, whether
-  // CanWrap(r) or not. However, this will not hold if a serialization and
-  // deserialization step is put between the two calls.
   static ObjectPtr Unwrap(ObjectPtr obj) {
-    if (!obj->IsWeakSerializationReference()) return obj;
-    return TargetOf(static_cast<WeakSerializationReferencePtr>(obj));
-  }
-
-  // An Unwrap that only unwraps if there's a valid target, otherwise the
-  // WSR is returned. Useful for cases where we want to call Object methods
-  // like ToCString() on whatever non-null object we can get.
-  static ObjectPtr UnwrapIfTarget(const Object& obj) {
-    return UnwrapIfTarget(obj.ptr());
-  }
-  static ObjectPtr UnwrapIfTarget(ObjectPtr raw) {
-#if defined(DART_PRECOMPILED_RUNTIME)
-    // In the precompiled runtime, WSRs never have a target so we always return
-    // the argument.
-    return raw;
-#else
-    if (!raw->IsWeakSerializationReference()) return raw;
-    // Otherwise, they always do.
-    return TargetOf(WeakSerializationReference::RawCast(raw));
+#if defined(DART_PRECOMPILER)
+    if (obj->IsHeapObject() && obj->IsWeakSerializationReference()) {
+      return TargetOf(static_cast<WeakSerializationReferencePtr>(obj));
+    }
 #endif
+    return obj;
   }
-
-  static classid_t UnwrappedClassIdOf(const Object& obj) {
-    return UnwrappedClassIdOf(obj.ptr());
-  }
-  // Gets the class ID of the underlying object from a WSR, or the class ID of
-  // the object if it is not one.
-  //
-  // UnwrappedClassOf(Wrap(r)) == UnwrappedClassOf(r) for all raw objects r,
-  // whether CanWrap(r) or not. Unlike Unwrap, this is still true even if
-  // there is a serialization and deserialization step between the two calls,
-  // since that information is saved in the serialized WSR.
-  static classid_t UnwrappedClassIdOf(ObjectPtr obj) {
-    if (!obj->IsWeakSerializationReference()) return obj->GetClassId();
-    return TargetClassIdOf(WeakSerializationReference::RawCast(obj));
-  }
+  static ObjectPtr Unwrap(const Object& obj) { return Unwrap(obj.ptr()); }
+  static ObjectPtr UnwrapIfTarget(ObjectPtr obj) { return Unwrap(obj); }
+  static ObjectPtr UnwrapIfTarget(const Object& obj) { return Unwrap(obj); }
 
   static intptr_t InstanceSize() {
     return RoundedAllocationSize(sizeof(UntaggedWeakSerializationReference));
   }
 
-#if defined(DART_PRECOMPILER)
-  // Returns true if a new WSR would be created when calling Wrap.
-  static bool CanWrap(const Object& object);
-
-  // This returns ObjectPtr, not WeakSerializationReferencePtr, because
-  // target.ptr() is returned when CanWrap(target) is false.
-  static ObjectPtr Wrap(Zone* zone, const Object& target);
-#endif
+  static WeakSerializationReferencePtr New(const Object& target,
+                                           const Object& replacement);
 
  private:
   FINAL_HEAP_OBJECT_IMPLEMENTATION(WeakSerializationReference, Object);
@@ -6294,17 +6239,21 @@
   // while generating the snapshot.
   FunctionPtr function() const {
     ASSERT(IsFunctionCode());
-    return Function::RawCast(
-        WeakSerializationReference::Unwrap(untag()->owner()));
+    return Function::RawCast(owner());
   }
 
-  ObjectPtr owner() const { return untag()->owner(); }
+  ObjectPtr owner() const {
+    return WeakSerializationReference::Unwrap(untag()->owner());
+  }
   void set_owner(const Object& owner) const;
 
   classid_t OwnerClassId() const { return OwnerClassIdOf(ptr()); }
   static classid_t OwnerClassIdOf(CodePtr raw) {
-    return WeakSerializationReference::UnwrappedClassIdOf(
-        raw->untag()->owner());
+    ObjectPtr owner = WeakSerializationReference::Unwrap(raw->untag()->owner());
+    if (!owner->IsHeapObject()) {
+      return RawSmiValue(static_cast<SmiPtr>(owner));
+    }
+    return owner->GetClassId();
   }
 
   static intptr_t owner_offset() { return OFFSET_OF(UntaggedCode, owner_); }
diff --git a/runtime/vm/raw_object.cc b/runtime/vm/raw_object.cc
index a142516..6a03bff 100644
--- a/runtime/vm/raw_object.cc
+++ b/runtime/vm/raw_object.cc
@@ -536,6 +536,7 @@
 REGULAR_VISITOR(SubtypeTestCache)
 REGULAR_VISITOR(LoadingUnit)
 REGULAR_VISITOR(KernelProgramInfo)
+REGULAR_VISITOR(WeakSerializationReference)
 VARIABLE_VISITOR(TypeArguments, Smi::Value(raw_obj->untag()->length_))
 VARIABLE_VISITOR(LocalVarDescriptors, raw_obj->untag()->num_entries_)
 VARIABLE_VISITOR(ExceptionHandlers, raw_obj->untag()->num_entries_)
@@ -576,11 +577,6 @@
 UNREACHABLE_VISITOR(FutureOr)
 // Smi has no heap representation.
 UNREACHABLE_VISITOR(Smi)
-#if defined(DART_PRECOMPILED_RUNTIME)
-NULL_VISITOR(WeakSerializationReference)
-#else
-REGULAR_VISITOR(WeakSerializationReference)
-#endif
 
 intptr_t UntaggedField::VisitFieldPointers(FieldPtr raw_obj,
                                            ObjectPointerVisitor* visitor) {
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 21f3aaa..8c041a2 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -1508,14 +1508,10 @@
 class UntaggedWeakSerializationReference : public UntaggedObject {
   RAW_HEAP_OBJECT_IMPLEMENTATION(WeakSerializationReference);
 
-#if defined(DART_PRECOMPILED_RUNTIME)
-  VISIT_NOTHING();
-  ClassIdTagType cid_;
-#else
   VISIT_FROM(ObjectPtr, target)
   POINTER_FIELD(ObjectPtr, target)
-  VISIT_TO(ObjectPtr, target)
-#endif
+  POINTER_FIELD(ObjectPtr, replacement)
+  VISIT_TO(ObjectPtr, replacement)
 };
 
 class UntaggedCode : public UntaggedObject {
diff --git a/runtime/vm/raw_object_fields.cc b/runtime/vm/raw_object_fields.cc
index 16b25bd..07f24db 100644
--- a/runtime/vm/raw_object_fields.cc
+++ b/runtime/vm/raw_object_fields.cc
@@ -84,6 +84,8 @@
   F(KernelProgramInfo, libraries_cache_)                                       \
   F(KernelProgramInfo, classes_cache_)                                         \
   F(KernelProgramInfo, retained_kernel_blob_)                                  \
+  F(WeakSerializationReference, target_)                                       \
+  F(WeakSerializationReference, replacement_)                                  \
   F(Code, object_pool_)                                                        \
   F(Code, instructions_)                                                       \
   F(Code, owner_)                                                              \
@@ -192,7 +194,7 @@
   F(TypedDataView, offset_in_bytes_)                                           \
   F(FutureOr, type_arguments_)
 
-#define AOT_CLASSES_AND_FIELDS(F) F(WeakSerializationReference, cid_)
+#define AOT_CLASSES_AND_FIELDS(F)
 
 #define JIT_CLASSES_AND_FIELDS(F)                                              \
   F(Code, active_instructions_)                                                \
@@ -201,7 +203,6 @@
   F(ICData, receivers_static_type_)                                            \
   F(Function, unoptimized_code_)                                               \
   F(Field, type_test_cache_)                                                   \
-  F(WeakSerializationReference, target_)
 
 #define NON_PRODUCT_CLASSES_AND_FIELDS(F)                                      \
   F(ReceivePort, debug_name_)                                                  \
diff --git a/runtime/vm/stack_frame.cc b/runtime/vm/stack_frame.cc
index 96ad60d..51c1722 100644
--- a/runtime/vm/stack_frame.cc
+++ b/runtime/vm/stack_frame.cc
@@ -321,7 +321,10 @@
 FunctionPtr StackFrame::LookupDartFunction() const {
   const Code& code = Code::Handle(LookupDartCode());
   if (!code.IsNull()) {
-    return code.function();
+    const Object& owner = Object::Handle(code.owner());
+    if (owner.IsFunction()) {
+      return Function::Cast(owner).ptr();
+    }
   }
   return Function::null();
 }
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 3784362..39dfa25 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -293,6 +293,7 @@
   V(UnwindError, "UnwindError")                                                \
   V(Value, "value")                                                            \
   V(Values, "values")                                                          \
+  V(WeakSerializationReference, "WeakSerializationReference")                  \
   V(YieldKw, "yield")                                                          \
   V(_AsyncAwaitStart, "start")                                                 \
   V(_AsyncStarStreamController, "_AsyncStarStreamController")                  \
diff --git a/sdk/lib/_internal/vm/lib/async_patch.dart b/sdk/lib/_internal/vm/lib/async_patch.dart
index 8816675..5c5ee05 100644
--- a/sdk/lib/_internal/vm/lib/async_patch.dart
+++ b/sdk/lib/_internal/vm/lib/async_patch.dart
@@ -36,8 +36,13 @@
   // zone is the root zone, we don't wrap the continuation, and a bad
   // `Future` implementation could potentially invoke the callback with the
   // wrong number of arguments.
-  if (Zone.current == Zone.root) return continuation;
-  return Zone.current.registerUnaryCallback<dynamic, dynamic>(continuation);
+  final currentZone = Zone._current;
+  if (identical(currentZone, _rootZone) ||
+      identical(currentZone._registerUnaryCallback,
+          _rootZone._registerUnaryCallback)) {
+    return continuation;
+  }
+  return currentZone.registerUnaryCallback<dynamic, dynamic>(continuation);
 }
 
 // We need to pass the exception and stack trace objects as second and third
@@ -46,8 +51,13 @@
     dynamic Function(dynamic, dynamic, StackTrace) continuation) {
   // See comments of `_asyncThenWrapperHelper`.
   dynamic errorCallback(Object e, StackTrace s) => continuation(null, e, s);
-  if (Zone.current == Zone.root) return errorCallback;
-  return Zone.current
+  final currentZone = Zone._current;
+  if (identical(currentZone, _rootZone) ||
+      identical(currentZone._registerBinaryCallback,
+          _rootZone._registerBinaryCallback)) {
+    return errorCallback;
+  }
+  return currentZone
       .registerBinaryCallback<dynamic, Object, StackTrace>(errorCallback);
 }
 
diff --git a/tools/VERSION b/tools/VERSION
index 672d7fb..f69eca3 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 13
 PATCH 0
-PRERELEASE 22
+PRERELEASE 23
 PRERELEASE_PATCH 0
\ No newline at end of file