Version 2.18.0-43.0.dev

Merge commit 'e63cea6634a46b59f8c14f94b4f253f4f0d0cfba' into 'dev'
diff --git a/pkg/front_end/lib/src/fasta/incremental_compiler.dart b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
index ac929fb..b646c44 100644
--- a/pkg/front_end/lib/src/fasta/incremental_compiler.dart
+++ b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
@@ -288,9 +288,11 @@
     if (_resetTicker) {
       _ticker.reset();
     }
-    entryPoints ??= context.options.inputs;
+    List<Uri>? entryPointsSavedForLaterOverwrite = entryPoints;
     return context
         .runInContext<IncrementalCompilerResult>((CompilerContext c) async {
+      List<Uri> entryPoints =
+          entryPointsSavedForLaterOverwrite ?? context.options.inputs;
       if (_computeDeltaRunOnce && _initializedForExpressionCompilationOnly) {
         throw new StateError("Initialized for expression compilation: "
             "cannot do another general compile.");
@@ -311,23 +313,7 @@
       Set<Uri?> invalidatedUris = this._invalidatedUris.toSet();
       _invalidateNotKeptUserBuilders(invalidatedUris);
       ReusageResult? reusedResult = _computeReusedLibraries(
-          lastGoodKernelTarget,
-          _userBuilders,
-          invalidatedUris,
-          uriTranslator,
-          entryPoints!);
-
-      // Use the reused libraries to re-write entry-points.
-      if (reusedResult.arePartsUsedAsEntryPoints()) {
-        for (int i = 0; i < entryPoints!.length; i++) {
-          Uri entryPoint = entryPoints![i];
-          Uri? redirect =
-              reusedResult.getLibraryUriForPartUsedAsEntryPoint(entryPoint);
-          if (redirect != null) {
-            entryPoints![i] = redirect;
-          }
-        }
-      }
+          lastGoodKernelTarget, _userBuilders, invalidatedUris, uriTranslator);
 
       // Experimental invalidation initialization (e.g. figure out if we can).
       _benchmarker
@@ -338,6 +324,10 @@
           experimentalInvalidation?.missingSources.length ?? 0);
 
       _benchmarker
+          ?.enterPhase(BenchmarkPhases.incremental_rewriteEntryPointsIfPart);
+      _rewriteEntryPointsIfPart(entryPoints, reusedResult);
+
+      _benchmarker
           ?.enterPhase(BenchmarkPhases.incremental_invalidatePrecompiledMacros);
       await _invalidatePrecompiledMacros(
           c.options, reusedResult.notReusedLibraries);
@@ -375,11 +365,11 @@
       while (true) {
         _benchmarker?.enterPhase(BenchmarkPhases.incremental_setupInLoop);
         currentKernelTarget = _setupNewKernelTarget(c, uriTranslator, hierarchy,
-            reusedLibraries, experimentalInvalidation, entryPoints!.first);
+            reusedLibraries, experimentalInvalidation, entryPoints.first);
         Map<LibraryBuilder, List<LibraryBuilder>>? rebuildBodiesMap =
             _experimentalInvalidationCreateRebuildBodiesBuilders(
                 currentKernelTarget, experimentalInvalidation, uriTranslator);
-        entryPoints = currentKernelTarget.setEntryPoints(entryPoints!);
+        entryPoints = currentKernelTarget.setEntryPoints(entryPoints);
 
         // TODO(johnniwinther,jensj): Ensure that the internal state of the
         // incremental compiler is consistent across 1 or more macro
@@ -477,7 +467,7 @@
               currentKernelTarget,
               data.component != null || fullComponent,
               compiledLibraries,
-              entryPoints!,
+              entryPoints,
               reusedLibraries,
               hierarchy,
               uriTranslator,
@@ -540,6 +530,21 @@
     });
   }
 
+  void _rewriteEntryPointsIfPart(
+      List<Uri> entryPoints, ReusageResult reusedResult) {
+    for (int i = 0; i < entryPoints.length; i++) {
+      Uri entryPoint = entryPoints[i];
+      LibraryBuilder? parent = reusedResult.partUriToParent[entryPoint];
+      if (parent == null) continue;
+      // TODO(jensj): .contains on a list is O(n).
+      // It will only be done for each entry point that's a part though, i.e.
+      // most likely very rarely.
+      if (reusedResult.reusedLibraries.contains(parent)) {
+        entryPoints[i] = parent.importUri;
+      }
+    }
+  }
+
   /// Convert every SourceLibraryBuilder to a DillLibraryBuilder.
   /// As we always do this, this will only be the new ones.
   ///
@@ -1156,6 +1161,9 @@
   /// Figure out if we can (and was asked to) do experimental invalidation.
   /// Note that this returns (future or) [null] if we're not doing experimental
   /// invalidation.
+  ///
+  /// Note that - when doing experimental invalidation - [reusedResult] is
+  /// updated.
   Future<ExperimentalInvalidation?> _initializeExperimentalInvalidation(
       ReusageResult reusedResult, CompilerContext c) async {
     Set<LibraryBuilder>? rebuildBodies;
@@ -1229,9 +1237,6 @@
       if (before != now) {
         return null;
       }
-      // TODO(jensj): We should only do this when we're sure we're going to
-      // do it!
-      CompilerContext.current.uriToSource.remove(builder.fileUri);
       missingSources ??= new Set<Uri>();
       missingSources.add(builder.fileUri);
       LibraryBuilder? partOfLibrary = builder.partOfLibrary;
@@ -1263,7 +1268,6 @@
               // to rebuild only the body of.
               // TODO(jensj): We can probably add this to the rebuildBodies
               // list and just rebuild that library too.
-              // print("Usage of mixin in ${lib.importUri}");
               return null;
             }
           }
@@ -1315,8 +1319,13 @@
     reusedResult.notReusedLibraries.clear();
     reusedResult.notReusedLibraries.addAll(rebuildBodies!);
 
+    // Now we know we're going to do it --- remove old sources.
+    for (Uri fileUri in missingSources!) {
+      CompilerContext.current.uriToSource.remove(fileUri);
+    }
+
     return new ExperimentalInvalidation(
-        rebuildBodies, originalNotReusedLibraries, missingSources!);
+        rebuildBodies, originalNotReusedLibraries, missingSources);
   }
 
   /// Get UriTranslator, and figure out if the packages file was (potentially)
@@ -2020,8 +2029,7 @@
       IncrementalKernelTarget? lastGoodKernelTarget,
       Map<Uri, LibraryBuilder>? _userBuilders,
       Set<Uri?> invalidatedUris,
-      UriTranslator uriTranslator,
-      List<Uri> entryPoints) {
+      UriTranslator uriTranslator) {
     Set<Uri> seenUris = new Set<Uri>();
     List<LibraryBuilder> reusedLibraries = <LibraryBuilder>[];
     for (int i = 0; i < _platformBuilders!.length; i++) {
@@ -2179,26 +2187,8 @@
       if (!seenUris.add(builder.importUri)) continue;
       reusedLibraries.add(builder);
     }
-
-    ReusageResult result = new ReusageResult(
-        notReusedLibraries,
-        directlyInvalidated,
-        invalidatedBecauseOfPackageUpdate,
-        reusedLibraries);
-
-    for (Uri entryPoint in entryPoints) {
-      LibraryBuilder? parent = partUriToParent[entryPoint];
-      if (parent == null) continue;
-      // TODO(jensj): .contains on a list is O(n).
-      // It will only be done for each entry point that's a part though, i.e.
-      // most likely very rarely.
-      if (reusedLibraries.contains(parent)) {
-        result.registerLibraryUriForPartUsedAsEntryPoint(
-            entryPoint, parent.importUri);
-      }
-    }
-
-    return result;
+    return new ReusageResult(notReusedLibraries, directlyInvalidated,
+        invalidatedBecauseOfPackageUpdate, reusedLibraries, partUriToParent);
   }
 
   @override
@@ -2312,17 +2302,21 @@
   final Set<LibraryBuilder> directlyInvalidated;
   final bool invalidatedBecauseOfPackageUpdate;
   final List<LibraryBuilder> reusedLibraries;
-  final Map<Uri, Uri> _reusedLibrariesPartsToParentForEntryPoints;
+  final Map<Uri?, LibraryBuilder> partUriToParent;
 
   ReusageResult.reusedLibrariesOnly(this.reusedLibraries)
       : notReusedLibraries = const {},
         directlyInvalidated = const {},
         invalidatedBecauseOfPackageUpdate = false,
-        _reusedLibrariesPartsToParentForEntryPoints = const {};
+        partUriToParent = const {};
 
-  ReusageResult(this.notReusedLibraries, this.directlyInvalidated,
-      this.invalidatedBecauseOfPackageUpdate, this.reusedLibraries)
-      : _reusedLibrariesPartsToParentForEntryPoints = {},
+  ReusageResult(
+      this.notReusedLibraries,
+      this.directlyInvalidated,
+      this.invalidatedBecauseOfPackageUpdate,
+      this.reusedLibraries,
+      this.partUriToParent)
+      :
         // ignore: unnecessary_null_comparison
         assert(notReusedLibraries != null),
         // ignore: unnecessary_null_comparison
@@ -2330,18 +2324,9 @@
         // ignore: unnecessary_null_comparison
         assert(invalidatedBecauseOfPackageUpdate != null),
         // ignore: unnecessary_null_comparison
-        assert(reusedLibraries != null);
-
-  void registerLibraryUriForPartUsedAsEntryPoint(
-      Uri entryPoint, Uri importUri) {
-    _reusedLibrariesPartsToParentForEntryPoints[entryPoint] = importUri;
-  }
-
-  bool arePartsUsedAsEntryPoints() =>
-      _reusedLibrariesPartsToParentForEntryPoints.isNotEmpty;
-
-  Uri? getLibraryUriForPartUsedAsEntryPoint(Uri entryPoint) =>
-      _reusedLibrariesPartsToParentForEntryPoints[entryPoint];
+        assert(reusedLibraries != null),
+        // ignore: unnecessary_null_comparison
+        assert(partUriToParent != null);
 }
 
 class ExperimentalInvalidation {
diff --git a/pkg/front_end/lib/src/fasta/kernel/benchmarker.dart b/pkg/front_end/lib/src/fasta/kernel/benchmarker.dart
index 5966303..8008f36 100644
--- a/pkg/front_end/lib/src/fasta/kernel/benchmarker.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/benchmarker.dart
@@ -200,6 +200,7 @@
   incremental_ensurePlatform,
   incremental_invalidate,
   incremental_experimentalInvalidation,
+  incremental_rewriteEntryPointsIfPart,
   incremental_invalidatePrecompiledMacros,
   incremental_cleanup,
   incremental_loadEnsureLoadedComponents,
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
index c8f0d25..5794d25 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -476,13 +476,6 @@
       benchmarker?.enterPhase(BenchmarkPhases.outline_installDefaultSupertypes);
       installDefaultSupertypes();
 
-      benchmarker
-          ?.enterPhase(BenchmarkPhases.outline_installSyntheticConstructors);
-      installSyntheticConstructors(sourceClassBuilders);
-
-      benchmarker?.enterPhase(BenchmarkPhases.outline_resolveConstructors);
-      loader.resolveConstructors(loader.sourceLibraryBuilders);
-
       benchmarker?.enterPhase(BenchmarkPhases.outline_link);
       component =
           link(new List<Library>.of(loader.libraries), nameRoot: nameRoot);
@@ -514,6 +507,13 @@
       }
 
       benchmarker
+          ?.enterPhase(BenchmarkPhases.outline_installSyntheticConstructors);
+      installSyntheticConstructors(sourceClassBuilders);
+
+      benchmarker?.enterPhase(BenchmarkPhases.outline_resolveConstructors);
+      loader.resolveConstructors(loader.sourceLibraryBuilders);
+
+      benchmarker
           ?.enterPhase(BenchmarkPhases.outline_buildClassHierarchyMembers);
       loader.buildClassHierarchyMembers(sourceClassBuilders);
 
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index 3ec34d7..29a216f 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -2408,91 +2408,73 @@
           argument is Expression || argument is NamedExpression,
           "Expected the argument to be either an Expression "
           "or a NamedExpression, got '${argument.runtimeType}'.");
-      if (argument is Expression) {
-        int index = positionalIndex++;
-        DartType formalType = getPositionalParameterType(calleeType, index);
-        DartType inferredFormalType = substitution != null
-            ? substitution.substituteType(formalType)
-            : formalType;
-        DartType inferredType;
+      int index;
+      DartType formalType;
+      Expression argumentExpression;
+      bool isExpression = argument is Expression;
+      if (isExpression) {
+        index = positionalIndex++;
+        formalType = getPositionalParameterType(calleeType, index);
+        argumentExpression = arguments.positional[index];
+      } else {
+        index = namedIndex++;
+        NamedExpression namedArgument = arguments.named[index];
+        formalType = getNamedParameterType(calleeType, namedArgument.name);
+        argumentExpression = namedArgument.value;
+      }
+      DartType inferredFormalType = substitution != null
+          ? substitution.substituteType(formalType)
+          : formalType;
+      if (isExpression) {
         if (isImplicitExtensionMember && index == 0) {
           assert(
               receiverType != null,
               "No receiver type provided for implicit extension member "
               "invocation.");
           continue;
-        } else {
-          if (isSpecialCasedBinaryOperator) {
-            inferredFormalType = typeSchemaEnvironment
-                .getContextTypeOfSpecialCasedBinaryOperator(
-                    typeContext, receiverType!, inferredFormalType,
-                    isNonNullableByDefault: isNonNullableByDefault);
-          } else if (isSpecialCasedTernaryOperator) {
-            inferredFormalType = typeSchemaEnvironment
-                .getContextTypeOfSpecialCasedTernaryOperator(
-                    typeContext, receiverType!, inferredFormalType,
-                    isNonNullableByDefault: isNonNullableByDefault);
-          }
-          ExpressionInferenceResult result = inferExpression(
-              arguments.positional[index],
+        }
+        if (isSpecialCasedBinaryOperator) {
+          inferredFormalType =
+              typeSchemaEnvironment.getContextTypeOfSpecialCasedBinaryOperator(
+                  typeContext, receiverType!, inferredFormalType,
+                  isNonNullableByDefault: isNonNullableByDefault);
+        } else if (isSpecialCasedTernaryOperator) {
+          inferredFormalType =
+              typeSchemaEnvironment.getContextTypeOfSpecialCasedTernaryOperator(
+                  typeContext, receiverType!, inferredFormalType,
+                  isNonNullableByDefault: isNonNullableByDefault);
+        }
+      }
+      ExpressionInferenceResult result = inferExpression(
+          argumentExpression,
+          isNonNullableByDefault
+              ? inferredFormalType
+              : legacyErasure(inferredFormalType),
+          inferenceNeeded ||
+              isSpecialCasedBinaryOperator ||
+              isSpecialCasedTernaryOperator ||
+              typeChecksNeeded);
+      DartType inferredType = identical(result.inferredType, noInferredType) ||
               isNonNullableByDefault
-                  ? inferredFormalType
-                  : legacyErasure(inferredFormalType),
-              inferenceNeeded ||
-                  isSpecialCasedBinaryOperator ||
-                  isSpecialCasedTernaryOperator ||
-                  typeChecksNeeded);
-          inferredType = identical(result.inferredType, noInferredType) ||
-                  isNonNullableByDefault
-              ? result.inferredType
-              : legacyErasure(result.inferredType);
-          if (localHoistedExpressions != null &&
-              evaluationOrderIndex >= hoistingEndIndex) {
-            hoistedExpressions = null;
-          }
-          Expression expression =
-              _hoist(result.expression, inferredType, hoistedExpressions);
-          identicalInfo
-              ?.add(flowAnalysis.equalityOperand_end(expression, inferredType));
-          arguments.positional[index] = expression..parent = arguments;
-        }
-        if (useFormalAndActualTypes) {
-          formalTypes!.add(formalType);
-          actualTypes!.add(inferredType);
-        }
+          ? result.inferredType
+          : legacyErasure(result.inferredType);
+      if (localHoistedExpressions != null &&
+          evaluationOrderIndex >= hoistingEndIndex) {
+        hoistedExpressions = null;
+      }
+      Expression expression =
+          _hoist(result.expression, inferredType, hoistedExpressions);
+      identicalInfo
+          ?.add(flowAnalysis.equalityOperand_end(expression, inferredType));
+      if (isExpression) {
+        arguments.positional[index] = expression..parent = arguments;
       } else {
-        assert(argument is NamedExpression);
-        int index = namedIndex++;
         NamedExpression namedArgument = arguments.named[index];
-        DartType formalType =
-            getNamedParameterType(calleeType, namedArgument.name);
-        DartType inferredFormalType = substitution != null
-            ? substitution.substituteType(formalType)
-            : formalType;
-        ExpressionInferenceResult result = inferExpression(
-            namedArgument.value,
-            isNonNullableByDefault
-                ? inferredFormalType
-                : legacyErasure(inferredFormalType),
-            inferenceNeeded ||
-                isSpecialCasedBinaryOperator ||
-                typeChecksNeeded);
-        DartType inferredType =
-            identical(result.inferredType, noInferredType) ||
-                    isNonNullableByDefault
-                ? result.inferredType
-                : legacyErasure(result.inferredType);
-        if (localHoistedExpressions != null &&
-            evaluationOrderIndex >= hoistingEndIndex) {
-          hoistedExpressions = null;
-        }
-        Expression expression =
-            _hoist(result.expression, inferredType, hoistedExpressions);
         namedArgument.value = expression..parent = namedArgument;
-        if (useFormalAndActualTypes) {
-          formalTypes!.add(formalType);
-          actualTypes!.add(inferredType);
-        }
+      }
+      if (useFormalAndActualTypes) {
+        formalTypes!.add(formalType);
+        actualTypes!.add(inferredType);
       }
     }
     if (identicalInfo != null) {
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart b/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart
index 4d13bfa..7c3f33b 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart
@@ -9,7 +9,12 @@
 import 'package:kernel/core_types.dart' show CoreTypes;
 
 import 'package:kernel/type_algebra.dart'
-    show FreshTypeParameters, Substitution, getFreshTypeParameters, substitute;
+    show
+        FreshTypeParameters,
+        NullabilityAwareFreeTypeVariableEliminator,
+        Substitution,
+        getFreshTypeParameters,
+        substitute;
 
 import 'package:kernel/type_environment.dart';
 
@@ -416,14 +421,17 @@
 
     if (!isEmptyContext(returnContextType)) {
       if (isConst) {
-        returnContextType = new TypeVariableEliminator(
-                clientLibrary.isNonNullableByDefault
-                    ? const NeverType.nonNullable()
-                    : const NullType(),
-                clientLibrary.isNonNullableByDefault
-                    ? objectNullableRawType
-                    : objectLegacyRawType)
-            .substituteType(returnContextType!);
+        if (clientLibrary.isNonNullableByDefault) {
+          returnContextType = new NullabilityAwareFreeTypeVariableEliminator(
+                  bottomType: const NeverType.nonNullable(),
+                  topType: objectNullableRawType,
+                  topFunctionType: functionRawType(Nullability.nonNullable))
+              .eliminateToLeast(returnContextType!);
+        } else {
+          returnContextType =
+              new TypeVariableEliminator(const NullType(), objectLegacyRawType)
+                  .substituteType(returnContextType!);
+        }
       }
       gatherer.tryConstrainUpper(declaredReturnType!, returnContextType!);
     }
diff --git a/pkg/front_end/test/macros/application/data/pkgs/macro/lib/data_class.dart b/pkg/front_end/test/macros/application/data/pkgs/macro/lib/data_class.dart
new file mode 100644
index 0000000..095e5f3
--- /dev/null
+++ b/pkg/front_end/test/macros/application/data/pkgs/macro/lib/data_class.dart
@@ -0,0 +1,116 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'package:_fe_analyzer_shared/src/macros/api.dart';
+import 'dart:collection';
+
+macro
+
+class DataClass implements ClassDeclarationsMacro, ClassDefinitionMacro {
+  const DataClass();
+
+  FutureOr<void> buildDeclarationsForClass(ClassDeclaration clazz,
+      ClassMemberDeclarationBuilder builder) async {
+    Uri dartCore = Uri.parse('dart:core');
+    Identifier objectIdentifier =
+    await builder.resolveIdentifier(dartCore, 'Object');
+    Identifier boolIdentifier =
+    await builder.resolveIdentifier(dartCore, 'bool');
+    Identifier intIdentifier =
+    await builder.resolveIdentifier(dartCore, 'int');
+    Identifier stringIdentifier =
+    await builder.resolveIdentifier(dartCore, 'String');
+
+    List<FieldDeclaration> fields = await builder.fieldsOf(clazz);
+    List<Object> constructorParts = ['const ', clazz.identifier.name, '({'];
+    String comma = '';
+    for (FieldDeclaration field in fields) {
+      constructorParts.addAll([comma, 'required this.', field.identifier]);
+      comma = ', ';
+    }
+    constructorParts.add('});');
+    builder.declareInClass(new DeclarationCode.fromParts(constructorParts));
+
+    builder.declareInClass(new DeclarationCode.fromParts([
+      'external ', intIdentifier, ' get hashCode;']));
+    builder.declareInClass(new DeclarationCode.fromParts([
+      'external ',
+      boolIdentifier,
+      ' operator ==(',
+      objectIdentifier,
+      ' other);'
+    ]));
+    builder.declareInClass(new DeclarationCode.fromParts([
+      'external ', stringIdentifier, ' toString();']));
+  }
+
+  FutureOr<void> buildDefinitionForClass(ClassDeclaration clazz,
+      ClassDefinitionBuilder builder) async {
+    Uri dartCore = Uri.parse('dart:core');
+    Identifier identicalIdentifier =
+    await builder.resolveIdentifier(dartCore, 'identical');
+
+    List<FieldDeclaration> fields = await builder.fieldsOf(clazz);
+    List<MethodDeclaration> methods = await builder.methodsOf(clazz);
+
+    FunctionDefinitionBuilder hashCodeBuilder = await builder.buildMethod(
+        methods
+            .firstWhere((e) => e.identifier.name == 'hashCode')
+            .identifier);
+    FunctionDefinitionBuilder equalsBuilder = await builder.buildMethod(
+        methods
+            .firstWhere((e) => e.identifier.name == '==')
+            .identifier);
+    FunctionDefinitionBuilder toStringBuilder = await builder.buildMethod(
+        methods
+            .firstWhere((e) => e.identifier.name == 'toString')
+            .identifier);
+
+    List<Object> hashCodeParts = ['''{
+    return '''
+    ];
+
+    List<Object> equalsParts = ['''{
+    if (''', identicalIdentifier, '''(this, other)) return true;
+    return other is ${clazz.identifier.name}'''
+    ];
+
+    List<Object> toStringParts = ['''{
+    return "${clazz.identifier.name}('''
+    ];
+
+    bool first = true;
+    for (FieldDeclaration field in fields) {
+      if (!first) {
+        hashCodeParts.add(' ^ ');
+        toStringParts.add(',');
+      }
+      // TODO(johnniwinther): Generate different code for collection typed
+      // fields.
+      hashCodeParts.addAll([field.identifier, '.hashCode']);
+
+      equalsParts.addAll(
+          [' && ', field.identifier, ' == other.', field.identifier]);
+
+      toStringParts.addAll(
+          ['${field.identifier.name}=\${', field.identifier, '}']);
+
+      first = false;
+    }
+
+    hashCodeParts.add(''';
+  }''');
+
+    equalsParts.add(''';
+  }''');
+
+    toStringParts.add(''')";
+  }''');
+
+    hashCodeBuilder.augment(new FunctionBodyCode.fromParts(hashCodeParts));
+    equalsBuilder.augment(new FunctionBodyCode.fromParts(equalsParts));
+    toStringBuilder.augment(new FunctionBodyCode.fromParts(toStringParts));
+  }
+}
diff --git a/pkg/front_end/test/macros/application/data/tests/data_class.dart b/pkg/front_end/test/macros/application/data/tests/data_class.dart
new file mode 100644
index 0000000..1b70609
--- /dev/null
+++ b/pkg/front_end/test/macros/application/data/tests/data_class.dart
@@ -0,0 +1,46 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/*library: 
+Definitions:
+import 'dart:core' as prefix0;
+
+augment class Foo {
+augment prefix0.int hashCode() {
+    return this.bar.hashCode ^ this.baz.hashCode;
+  }
+augment prefix0.bool operator ==(prefix0.Object other, ) {
+    if (prefix0.identical(this, other)) return true;
+    return other is Foo && this.bar == other.bar && this.baz == other.baz;
+  }
+augment prefix0.String toString() {
+    return "Foo(bar=${this.bar},baz=${this.baz})";
+  }
+}
+*/
+
+import 'package:macro/data_class.dart';
+
+@DataClass()
+/*class: Foo:
+augment class Foo {
+const Foo({required this.bar, required this.baz});
+external int get hashCode;
+external bool operator ==(Object other);
+external String toString();
+augment int hashCode() {
+    return bar.hashCode ^ baz.hashCode;
+  }
+augment bool operator ==(Object other, ) {
+    if (identical(this, other)) return true;
+    return other is Foo && bar == other.bar && baz == other.baz;
+  }
+augment String toString() {
+    return "Foo(bar=${bar},baz=${baz})";
+  }
+}*/
+class Foo {
+  final int bar;
+  final String baz;
+}
diff --git a/pkg/front_end/test/macros/application/data/tests/data_class.dart.expect b/pkg/front_end/test/macros/application/data/tests/data_class.dart.expect
new file mode 100644
index 0000000..f763299
--- /dev/null
+++ b/pkg/front_end/test/macros/application/data/tests/data_class.dart.expect
@@ -0,0 +1,31 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "package:macro/data_class.dart" as dat;
+import "dart:core" as core;
+
+import "package:macro/data_class.dart";
+
+@#C1
+class Foo extends core::Object {
+  final field core::int bar;
+  final field core::String baz;
+  const constructor •({required core::int bar = #C2, required core::String baz = #C2}) → self::Foo
+    : self::Foo::bar = bar, self::Foo::baz = baz, super core::Object::•()
+    ;
+  get /* from org-dartlang-augmentation:/a/b/c/main.dart-1 */ hashCode() → core::int {
+    return this.{self::Foo::bar}{core::int}.{core::num::hashCode}{core::int}.{core::int::^}(this.{self::Foo::baz}{core::String}.{core::String::hashCode}{core::int}){(core::int) → core::int};
+  }
+  operator /* from org-dartlang-augmentation:/a/b/c/main.dart-1 */ ==(core::Object other) → core::bool {
+    if(core::identical(this, other))
+      return true;
+    return other is{ForNonNullableByDefault} self::Foo && this.{self::Foo::bar}{core::int} =={core::num::==}{(core::Object) → core::bool} other{self::Foo}.{self::Foo::bar}{core::int} && this.{self::Foo::baz}{core::String} =={core::String::==}{(core::Object) → core::bool} other{self::Foo}.{self::Foo::baz}{core::String};
+  }
+  method /* from org-dartlang-augmentation:/a/b/c/main.dart-1 */ toString() → core::String {
+    return "Foo(bar=${this.{self::Foo::bar}{core::int}},baz=${this.{self::Foo::baz}{core::String}})";
+  }
+}
+
+constants  {
+  #C1 = dat::DataClass {}
+  #C2 = null
+}
diff --git a/pkg/front_end/testcases/incremental/import_package_by_file_uri.yaml b/pkg/front_end/testcases/incremental/import_package_by_file_uri.yaml
new file mode 100644
index 0000000..74104cc
--- /dev/null
+++ b/pkg/front_end/testcases/incremental/import_package_by_file_uri.yaml
@@ -0,0 +1,40 @@
+# Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE.md file.
+
+# Importing a package also via file uri gives two libraries with the same
+# file uri --- that shouldn't crash experimental invalidation though.
+
+type: newworld
+worlds:
+  - entry: package:foo/main.dart
+    experiments: alternative-invalidation-strategy
+    checkEntries: false
+    sources:
+      .dart_tool/package_config.json: |
+        {
+          "configVersion": 2,
+          "packages": [
+            {
+              "name": "foo",
+              "rootUri": "../foo",
+              "languageVersion": "2.12"
+            }
+          ]
+        }
+      foo/main.dart: |
+        import "package:foo/lib.dart";
+      foo/lib.dart: |
+        import "org-dartlang-test:///foo/main.dart";
+    expectedLibraryCount: 3 # main twice --- one for the package and one for the file uri.
+
+  - entry: package:foo/main.dart
+    experiments: alternative-invalidation-strategy
+    checkEntries: false
+    worldType: updated
+    expectInitializeFromDill: false
+    invalidate:
+      - foo/main.dart
+    expectedLibraryCount: 3
+    expectsRebuildBodiesOnly: true
+    checkInvalidatedFiles: false
diff --git a/pkg/front_end/testcases/incremental/import_package_by_file_uri.yaml.world.1.expect b/pkg/front_end/testcases/incremental/import_package_by_file_uri.yaml.world.1.expect
new file mode 100644
index 0000000..81428ed
--- /dev/null
+++ b/pkg/front_end/testcases/incremental/import_package_by_file_uri.yaml.world.1.expect
@@ -0,0 +1,16 @@
+main = <No Member>;
+library from "package:foo/lib.dart" as lib {
+
+  import "org-dartlang-test:///foo/main.dart";
+
+}
+library from "package:foo/main.dart" as main {
+
+  import "package:foo/lib.dart";
+
+}
+library from "org-dartlang-test:///foo/main.dart" as main2 {
+
+  import "package:foo/lib.dart";
+
+}
diff --git a/pkg/front_end/testcases/incremental/import_package_by_file_uri.yaml.world.2.expect b/pkg/front_end/testcases/incremental/import_package_by_file_uri.yaml.world.2.expect
new file mode 100644
index 0000000..81428ed
--- /dev/null
+++ b/pkg/front_end/testcases/incremental/import_package_by_file_uri.yaml.world.2.expect
@@ -0,0 +1,16 @@
+main = <No Member>;
+library from "package:foo/lib.dart" as lib {
+
+  import "org-dartlang-test:///foo/main.dart";
+
+}
+library from "package:foo/main.dart" as main {
+
+  import "package:foo/lib.dart";
+
+}
+library from "org-dartlang-test:///foo/main.dart" as main2 {
+
+  import "package:foo/lib.dart";
+
+}
diff --git a/pkg/front_end/testcases/incremental/part_as_entry_2.yaml b/pkg/front_end/testcases/incremental/part_as_entry_2.yaml
new file mode 100644
index 0000000..0def2e8
--- /dev/null
+++ b/pkg/front_end/testcases/incremental/part_as_entry_2.yaml
@@ -0,0 +1,42 @@
+# Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE.md file.
+
+type: newworld
+worlds:
+  - entry: entry.dart
+    experiments: alternative-invalidation-strategy
+    checkEntries: false
+    sources:
+      entry.dart: |
+        part of "lib.dart";
+      lib.dart: |
+        import 'lib1.dart';
+        export 'lib1.dart';
+        part "entry.dart";
+      lib1.dart: |
+        import 'lib.dart';
+        main() {
+          print("Hello, World!");
+        }
+    expectedLibraryCount: 2
+
+  - entry: entry.dart
+    experiments: alternative-invalidation-strategy
+    checkEntries: false
+    worldType: updated
+    expectInitializeFromDill: false
+    invalidate:
+      - lib.dart
+    expectedLibraryCount: 2
+    expectsRebuildBodiesOnly: true
+
+  - entry: entry.dart
+    experiments: alternative-invalidation-strategy
+    checkEntries: false
+    worldType: updated
+    expectInitializeFromDill: false
+    invalidate:
+      - lib1.dart
+    expectedLibraryCount: 2
+    expectsRebuildBodiesOnly: true
diff --git a/pkg/front_end/testcases/incremental/part_as_entry_2.yaml.world.1.expect b/pkg/front_end/testcases/incremental/part_as_entry_2.yaml.world.1.expect
new file mode 100644
index 0000000..066a54b
--- /dev/null
+++ b/pkg/front_end/testcases/incremental/part_as_entry_2.yaml.world.1.expect
@@ -0,0 +1,17 @@
+main = lib1::main;
+library from "org-dartlang-test:///lib.dart" as lib {
+additionalExports = (lib1::main)
+
+  import "org-dartlang-test:///lib1.dart";
+  export "org-dartlang-test:///lib1.dart";
+
+  part entry.dart;
+}
+library from "org-dartlang-test:///lib1.dart" as lib1 {
+
+  import "org-dartlang-test:///lib.dart";
+
+  static method main() → dynamic {
+    dart.core::print("Hello, World!");
+  }
+}
diff --git a/pkg/front_end/testcases/incremental/part_as_entry_2.yaml.world.2.expect b/pkg/front_end/testcases/incremental/part_as_entry_2.yaml.world.2.expect
new file mode 100644
index 0000000..066a54b
--- /dev/null
+++ b/pkg/front_end/testcases/incremental/part_as_entry_2.yaml.world.2.expect
@@ -0,0 +1,17 @@
+main = lib1::main;
+library from "org-dartlang-test:///lib.dart" as lib {
+additionalExports = (lib1::main)
+
+  import "org-dartlang-test:///lib1.dart";
+  export "org-dartlang-test:///lib1.dart";
+
+  part entry.dart;
+}
+library from "org-dartlang-test:///lib1.dart" as lib1 {
+
+  import "org-dartlang-test:///lib.dart";
+
+  static method main() → dynamic {
+    dart.core::print("Hello, World!");
+  }
+}
diff --git a/pkg/front_end/testcases/incremental/part_as_entry_2.yaml.world.3.expect b/pkg/front_end/testcases/incremental/part_as_entry_2.yaml.world.3.expect
new file mode 100644
index 0000000..066a54b
--- /dev/null
+++ b/pkg/front_end/testcases/incremental/part_as_entry_2.yaml.world.3.expect
@@ -0,0 +1,17 @@
+main = lib1::main;
+library from "org-dartlang-test:///lib.dart" as lib {
+additionalExports = (lib1::main)
+
+  import "org-dartlang-test:///lib1.dart";
+  export "org-dartlang-test:///lib1.dart";
+
+  part entry.dart;
+}
+library from "org-dartlang-test:///lib1.dart" as lib1 {
+
+  import "org-dartlang-test:///lib.dart";
+
+  static method main() → dynamic {
+    dart.core::print("Hello, World!");
+  }
+}
diff --git a/pkg/front_end/testcases/nnbd/issue47795.dart b/pkg/front_end/testcases/nnbd/issue47795.dart
new file mode 100644
index 0000000..c08ad85
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue47795.dart
@@ -0,0 +1,10 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+class C<X> {
+  void m1({List<void Function(X)> xs = const []}) {}
+  void m2({List<void Function<Y extends List<X>>(Y)> xs = const []}) {}
+}
+
+void main() => C().m1();
diff --git a/pkg/front_end/testcases/nnbd/issue47795.dart.strong.expect b/pkg/front_end/testcases/nnbd/issue47795.dart.strong.expect
new file mode 100644
index 0000000..e9d2bf6
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue47795.dart.strong.expect
@@ -0,0 +1,18 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class C<X extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::C<self::C::X%>
+    : super core::Object::•()
+    ;
+  method m1({core::List<(self::C::X%) → void> xs = #C1}) → void {}
+  method m2({covariant-by-class core::List<<Y extends core::List<self::C::X%> = dynamic>(Y) → void> xs = #C2}) → void {}
+}
+static method main() → void
+  return new self::C::•<dynamic>().{self::C::m1}(){({xs: core::List<(dynamic) → void>}) → void};
+
+constants  {
+  #C1 = <(core::Object?) → void>[]
+  #C2 = <Never>[]
+}
diff --git a/pkg/front_end/testcases/nnbd/issue47795.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/issue47795.dart.strong.transformed.expect
new file mode 100644
index 0000000..e9d2bf6
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue47795.dart.strong.transformed.expect
@@ -0,0 +1,18 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class C<X extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::C<self::C::X%>
+    : super core::Object::•()
+    ;
+  method m1({core::List<(self::C::X%) → void> xs = #C1}) → void {}
+  method m2({covariant-by-class core::List<<Y extends core::List<self::C::X%> = dynamic>(Y) → void> xs = #C2}) → void {}
+}
+static method main() → void
+  return new self::C::•<dynamic>().{self::C::m1}(){({xs: core::List<(dynamic) → void>}) → void};
+
+constants  {
+  #C1 = <(core::Object?) → void>[]
+  #C2 = <Never>[]
+}
diff --git a/pkg/front_end/testcases/nnbd/issue47795.dart.textual_outline.expect b/pkg/front_end/testcases/nnbd/issue47795.dart.textual_outline.expect
new file mode 100644
index 0000000..05de177
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue47795.dart.textual_outline.expect
@@ -0,0 +1,6 @@
+class C<X> {
+  void m1({List<void Function(X)> xs = const []}) {}
+  void m2({List<void Function<Y extends List<X>>(Y)> xs = const []}) {}
+}
+
+void main() => C().m1();
diff --git a/pkg/front_end/testcases/nnbd/issue47795.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/nnbd/issue47795.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..05de177
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue47795.dart.textual_outline_modelled.expect
@@ -0,0 +1,6 @@
+class C<X> {
+  void m1({List<void Function(X)> xs = const []}) {}
+  void m2({List<void Function<Y extends List<X>>(Y)> xs = const []}) {}
+}
+
+void main() => C().m1();
diff --git a/pkg/front_end/testcases/nnbd/issue47795.dart.weak.expect b/pkg/front_end/testcases/nnbd/issue47795.dart.weak.expect
new file mode 100644
index 0000000..e7b237c
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue47795.dart.weak.expect
@@ -0,0 +1,18 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class C<X extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::C<self::C::X%>
+    : super core::Object::•()
+    ;
+  method m1({core::List<(self::C::X%) → void> xs = #C1}) → void {}
+  method m2({covariant-by-class core::List<<Y extends core::List<self::C::X%> = dynamic>(Y) → void> xs = #C2}) → void {}
+}
+static method main() → void
+  return new self::C::•<dynamic>().{self::C::m1}(){({xs: core::List<(dynamic) → void>}) → void};
+
+constants  {
+  #C1 = <(core::Object?) →* void>[]
+  #C2 = <Never*>[]
+}
diff --git a/pkg/front_end/testcases/nnbd/issue47795.dart.weak.modular.expect b/pkg/front_end/testcases/nnbd/issue47795.dart.weak.modular.expect
new file mode 100644
index 0000000..e7b237c
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue47795.dart.weak.modular.expect
@@ -0,0 +1,18 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class C<X extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::C<self::C::X%>
+    : super core::Object::•()
+    ;
+  method m1({core::List<(self::C::X%) → void> xs = #C1}) → void {}
+  method m2({covariant-by-class core::List<<Y extends core::List<self::C::X%> = dynamic>(Y) → void> xs = #C2}) → void {}
+}
+static method main() → void
+  return new self::C::•<dynamic>().{self::C::m1}(){({xs: core::List<(dynamic) → void>}) → void};
+
+constants  {
+  #C1 = <(core::Object?) →* void>[]
+  #C2 = <Never*>[]
+}
diff --git a/pkg/front_end/testcases/nnbd/issue47795.dart.weak.outline.expect b/pkg/front_end/testcases/nnbd/issue47795.dart.weak.outline.expect
new file mode 100644
index 0000000..1952228
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue47795.dart.weak.outline.expect
@@ -0,0 +1,20 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class C<X extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::C<self::C::X%>
+    ;
+  method m1({core::List<(self::C::X%) → void> xs = const <(core::Object?) → void>[]}) → void
+    ;
+  method m2({covariant-by-class core::List<<Y extends core::List<self::C::X%> = dynamic>(Y) → void> xs = const <Never>[]}) → void
+    ;
+}
+static method main() → void
+  ;
+
+
+Extra constant evaluation status:
+Evaluated: ListLiteral @ org-dartlang-testcase:///issue47795.dart:6:40 -> ListConstant(const <void Function(Object?)*>[])
+Evaluated: ListLiteral @ org-dartlang-testcase:///issue47795.dart:7:59 -> ListConstant(const <Never*>[])
+Extra constant evaluation: evaluated: 2, effectively constant: 2
diff --git a/pkg/front_end/testcases/nnbd/issue47795.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/issue47795.dart.weak.transformed.expect
new file mode 100644
index 0000000..e7b237c
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue47795.dart.weak.transformed.expect
@@ -0,0 +1,18 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class C<X extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::C<self::C::X%>
+    : super core::Object::•()
+    ;
+  method m1({core::List<(self::C::X%) → void> xs = #C1}) → void {}
+  method m2({covariant-by-class core::List<<Y extends core::List<self::C::X%> = dynamic>(Y) → void> xs = #C2}) → void {}
+}
+static method main() → void
+  return new self::C::•<dynamic>().{self::C::m1}(){({xs: core::List<(dynamic) → void>}) → void};
+
+constants  {
+  #C1 = <(core::Object?) →* void>[]
+  #C2 = <Never*>[]
+}
diff --git a/pkg/kernel/lib/type_algebra.dart b/pkg/kernel/lib/type_algebra.dart
index fe6e356..a7d73a0 100644
--- a/pkg/kernel/lib/type_algebra.dart
+++ b/pkg/kernel/lib/type_algebra.dart
@@ -98,8 +98,10 @@
 ///
 /// Returns `true` if [type] contains a [TypeParameterType] that doesn't refer
 /// to an enclosing generic [FunctionType] within [type].
-bool containsFreeTypeVariables(DartType type) {
-  return new _FreeTypeVariableVisitor().visit(type);
+bool containsFreeTypeVariables(DartType type,
+    {Set<TypeParameter>? boundVariables}) {
+  return new _FreeTypeVariableVisitor(boundVariables: boundVariables)
+      .visit(type);
 }
 
 /// Generates a fresh copy of the given type parameters, with their bounds
@@ -402,6 +404,12 @@
 
   @override
   TypeParameter freshTypeParameter(TypeParameter node) {
+    assert(
+        !substitution.containsKey(node),
+        "Function type variables cannot be substituted while still attached "
+        "to the function. Perform substitution on "
+        "`FunctionType.withoutTypeParameters` instead.");
+
     TypeParameter fresh = new TypeParameter(node.name)..flags = node.flags;
     TypeParameterType typeParameterType = substitution[node] =
         new TypeParameterType.forAlphaRenaming(node, fresh);
@@ -661,13 +669,6 @@
     // any uses, but does not tell if the resulting function type is distinct.
     // Our own use counter will get incremented if something from our
     // environment has been used inside the function.
-    assert(
-        node.typeParameters.every((TypeParameter parameter) =>
-            lookup(parameter, true) == null &&
-            lookup(parameter, false) == null),
-        "Function type variables cannot be substituted while still attached "
-        "to the function. Perform substitution on "
-        "`FunctionType.withoutTypeParameters` instead.");
     _TypeSubstitutor inner =
         node.typeParameters.isEmpty ? this : newInnerEnvironment();
     int before = this.useCounter;
@@ -919,9 +920,10 @@
 }
 
 class _FreeTypeVariableVisitor implements DartTypeVisitor<bool> {
-  final Set<TypeParameter> variables = new Set<TypeParameter>();
+  final Set<TypeParameter> boundVariables;
 
-  _FreeTypeVariableVisitor();
+  _FreeTypeVariableVisitor({Set<TypeParameter>? boundVariables})
+      : this.boundVariables = boundVariables ?? <TypeParameter>{};
 
   bool visit(DartType node) => node.accept(this);
 
@@ -967,22 +969,22 @@
 
   @override
   bool visitFunctionType(FunctionType node) {
-    variables.addAll(node.typeParameters);
+    boundVariables.addAll(node.typeParameters);
     bool result = node.typeParameters.any(handleTypeParameter) ||
         node.positionalParameters.any(visit) ||
         node.namedParameters.any(visitNamedType) ||
         visit(node.returnType);
-    variables.removeAll(node.typeParameters);
+    boundVariables.removeAll(node.typeParameters);
     return result;
   }
 
   @override
   bool visitTypeParameterType(TypeParameterType node) {
-    return !variables.contains(node.parameter);
+    return !boundVariables.contains(node.parameter);
   }
 
   bool handleTypeParameter(TypeParameter node) {
-    assert(variables.contains(node));
+    assert(boundVariables.contains(node));
     if (node.bound.accept(this)) return true;
     // ignore: unnecessary_null_comparison
     if (node.defaultType == null) return false;
@@ -1168,27 +1170,18 @@
   DartType visitVoidType(VoidType node, CoreTypes coreTypes) => node;
 }
 
-/// Eliminates specified free type parameters in a type.
-///
-/// The algorithm for elimination of type variables is described in
-/// https://github.com/dart-lang/language/pull/957
-class NullabilityAwareTypeVariableEliminator extends ReplacementVisitor {
+abstract class NullabilityAwareTypeVariableEliminatorBase
+    extends ReplacementVisitor {
   final DartType bottomType;
   final DartType topType;
   final DartType topFunctionType;
-  final Set<TypeParameter> eliminationTargets;
   late bool _isLeastClosure;
-  final bool Function(DartType type, bool Function(DartType type) recursor)?
-      unhandledTypeHandler;
 
-  NullabilityAwareTypeVariableEliminator(
-      {required this.eliminationTargets,
-      required this.bottomType,
+  NullabilityAwareTypeVariableEliminatorBase(
+      {required this.bottomType,
       required this.topType,
-      required this.topFunctionType,
-      this.unhandledTypeHandler})
-      // ignore: unnecessary_null_comparison
-      : assert(eliminationTargets != null),
+      required this.topFunctionType})
+      :
         // ignore: unnecessary_null_comparison
         assert(bottomType != null),
         // ignore: unnecessary_null_comparison
@@ -1196,13 +1189,17 @@
         // ignore: unnecessary_null_comparison
         assert(topFunctionType != null);
 
-  /// Returns a subtype of [type] for all values of [eliminationTargets].
+  bool containsTypeVariablesToEliminate(DartType type);
+
+  bool isTypeVariableToEliminate(TypeParameter typeParameter);
+
+  /// Returns a subtype of [type] for all variables to be eliminated.
   DartType eliminateToLeast(DartType type) {
     _isLeastClosure = true;
     return type.accept1(this, Variance.covariant) ?? type;
   }
 
-  /// Returns a supertype of [type] for all values of [eliminationTargets].
+  /// Returns a supertype of [type] for all variables to be eliminated.
   DartType eliminateToGreatest(DartType type) {
     _isLeastClosure = false;
     return type.accept1(this, Variance.covariant) ?? type;
@@ -1234,8 +1231,7 @@
     //  - The greatest closure of `S` with respect to `L` is `Function`
     if (node.typeParameters.isNotEmpty) {
       for (TypeParameter typeParameter in node.typeParameters) {
-        if (containsTypeVariable(typeParameter.bound, eliminationTargets,
-            unhandledTypeHandler: unhandledTypeHandler)) {
+        if (containsTypeVariablesToEliminate(typeParameter.bound)) {
           return getFunctionReplacement(variance);
         }
       }
@@ -1245,13 +1241,114 @@
 
   @override
   DartType? visitTypeParameterType(TypeParameterType node, int variance) {
-    if (eliminationTargets.contains(node.parameter)) {
+    if (isTypeVariableToEliminate(node.parameter)) {
       return getTypeParameterReplacement(variance);
     }
     return super.visitTypeParameterType(node, variance);
   }
 }
 
+/// Eliminates specified free type parameters in a type.
+///
+/// Use this class when only a specific subset of unbound variables in a type
+/// should be substituted with one of [bottomType], [topType], or
+/// [topFunctionType].  For example, running a
+/// `NullabilityAwareTypeVariableEliminatorBase({T}, Never, Object?,
+/// Function).eliminateToLeast` on type `T Function<S>(S s, R r)` will return
+/// `Never Function<S>(S s, R r)`.
+///
+/// The algorithm for elimination of type variables is described in
+/// https://github.com/dart-lang/language/pull/957
+class NullabilityAwareTypeVariableEliminator
+    extends NullabilityAwareTypeVariableEliminatorBase {
+  final Set<TypeParameter> eliminationTargets;
+  final bool Function(DartType type, bool Function(DartType type) recursor)?
+      unhandledTypeHandler;
+
+  NullabilityAwareTypeVariableEliminator(
+      {required this.eliminationTargets,
+      required DartType bottomType,
+      required DartType topType,
+      required DartType topFunctionType,
+      this.unhandledTypeHandler})
+      // ignore: unnecessary_null_comparison
+      : assert(eliminationTargets != null),
+        // ignore: unnecessary_null_comparison
+        assert(bottomType != null),
+        // ignore: unnecessary_null_comparison
+        assert(topType != null),
+        // ignore: unnecessary_null_comparison
+        assert(topFunctionType != null),
+        super(
+            bottomType: bottomType,
+            topType: topType,
+            topFunctionType: topFunctionType);
+
+  @override
+  bool containsTypeVariablesToEliminate(DartType type) {
+    return containsTypeVariable(type, eliminationTargets,
+        unhandledTypeHandler: unhandledTypeHandler);
+  }
+
+  @override
+  bool isTypeVariableToEliminate(TypeParameter typeParameter) {
+    return eliminationTargets.contains(typeParameter);
+  }
+}
+
+/// Eliminates all free type parameters in a type.
+///
+/// Use this class when all unbound variables in a type should be substituted
+/// with one of [bottomType], [topType], or [topFunctionType].  For example,
+/// running a `NullabilityAwareFreeTypeVariableEliminator(Never, Object?,
+/// Function).eliminateToLeast` on type `T Function<S>(S s, R r)` will return
+/// `Never Function<S>(S s, Object? r)`.
+///
+/// The algorithm for elimination of type variables is described in
+/// https://github.com/dart-lang/language/pull/957
+class NullabilityAwareFreeTypeVariableEliminator
+    extends NullabilityAwareTypeVariableEliminatorBase {
+  Set<TypeParameter> _boundVariables = <TypeParameter>{};
+
+  NullabilityAwareFreeTypeVariableEliminator(
+      {required DartType bottomType,
+      required DartType topType,
+      required DartType topFunctionType})
+      :
+        // ignore: unnecessary_null_comparison
+        assert(bottomType != null),
+        // ignore: unnecessary_null_comparison
+        assert(topType != null),
+        // ignore: unnecessary_null_comparison
+        assert(topFunctionType != null),
+        super(
+            bottomType: bottomType,
+            topType: topType,
+            topFunctionType: topFunctionType);
+
+  @override
+  DartType? visitFunctionType(FunctionType node, int variance) {
+    if (node.typeParameters.isNotEmpty) {
+      _boundVariables.addAll(node.typeParameters);
+      DartType? result = super.visitFunctionType(node, variance);
+      _boundVariables.removeAll(node.typeParameters);
+      return result;
+    } else {
+      return super.visitFunctionType(node, variance);
+    }
+  }
+
+  @override
+  bool containsTypeVariablesToEliminate(DartType type) {
+    return containsFreeTypeVariables(type, boundVariables: _boundVariables);
+  }
+
+  @override
+  bool isTypeVariableToEliminate(TypeParameter typeParameter) {
+    return !_boundVariables.contains(typeParameter);
+  }
+}
+
 /// Computes [type] as if declared without nullability markers.
 ///
 /// For example, int? and int* are considered applications of the nullable and
diff --git a/runtime/tests/vm/dart/no_local_labels_test.dart b/runtime/tests/vm/dart/no_local_labels_test.dart
new file mode 100644
index 0000000..25fe76f
--- /dev/null
+++ b/runtime/tests/vm/dart/no_local_labels_test.dart
@@ -0,0 +1,78 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This test verifies that assembly snapshot does not contain local labels.
+// (labels starting with 'L').
+
+import 'dart:io';
+
+import 'package:expect/expect.dart';
+import 'package:path/path.dart' as path;
+
+import 'use_flag_test_helper.dart';
+
+main(List<String> args) async {
+  if (!isAOTRuntime) {
+    return; // Running in JIT: AOT binaries not available.
+  }
+
+  if (Platform.isAndroid) {
+    return; // SDK tree not available on the test device.
+  }
+
+  // These are the tools we need to be available to run on a given platform:
+  if (!File(platformDill).existsSync()) {
+    throw "Cannot run test as $platformDill does not exist";
+  }
+  if (!await testExecutable(genSnapshot)) {
+    throw "Cannot run test as $genSnapshot not available";
+  }
+
+  await withTempDir('no-local-labels-test', (String tempDir) async {
+    final script = path.join(tempDir, 'program.dart');
+    final scriptDill = path.join(tempDir, 'program.dill');
+
+    await File(script).writeAsString('''
+class Local {
+  @pragma('vm:never-inline')
+  void foo() {
+  }
+
+  @pragma('vm:never-inline')
+  void bar() {
+  }
+}
+
+void main(List<String> args) {
+  Local()..foo()..bar();
+}
+''');
+
+    // Compile script to Kernel IR.
+    await run(genKernel, <String>[
+      '--aot',
+      '--platform=$platformDill',
+      '-o',
+      scriptDill,
+      script,
+    ]);
+
+    if (Platform.isWindows) {
+      return; // No assembly generation on Windows.
+    }
+
+    final assembly = path.join(tempDir, 'program.S');
+    await run(genSnapshot, <String>[
+      '--snapshot-kind=app-aot-assembly',
+      '--assembly=$assembly',
+      scriptDill,
+    ]);
+
+    final localLabelRe = RegExp(r'^L[a-zA-Z0-9_\.$]*:$', multiLine: true);
+    final match = localLabelRe.firstMatch(await File(assembly).readAsString());
+    if (match != null) {
+      Expect.isTrue(false, 'unexpected local label found ${match[0]!}');
+    }
+  });
+}
diff --git a/runtime/tests/vm/dart_2/no_local_labels_test.dart b/runtime/tests/vm/dart_2/no_local_labels_test.dart
new file mode 100644
index 0000000..7c9c217
--- /dev/null
+++ b/runtime/tests/vm/dart_2/no_local_labels_test.dart
@@ -0,0 +1,80 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This test verifies that assembly snapshot does not contain local labels.
+// (labels starting with 'L').
+
+// @dart=2.9
+
+import 'dart:io';
+
+import 'package:expect/expect.dart';
+import 'package:path/path.dart' as path;
+
+import 'use_flag_test_helper.dart';
+
+main(List<String> args) async {
+  if (!isAOTRuntime) {
+    return; // Running in JIT: AOT binaries not available.
+  }
+
+  if (Platform.isAndroid) {
+    return; // SDK tree not available on the test device.
+  }
+
+  // These are the tools we need to be available to run on a given platform:
+  if (!File(platformDill).existsSync()) {
+    throw "Cannot run test as $platformDill does not exist";
+  }
+  if (!await testExecutable(genSnapshot)) {
+    throw "Cannot run test as $genSnapshot not available";
+  }
+
+  await withTempDir('no-local-labels-test', (String tempDir) async {
+    final script = path.join(tempDir, 'program.dart');
+    final scriptDill = path.join(tempDir, 'program.dill');
+
+    await File(script).writeAsString('''
+class Local {
+  @pragma('vm:never-inline')
+  void foo() {
+  }
+
+  @pragma('vm:never-inline')
+  void bar() {
+  }
+}
+
+void main(List<String> args) {
+  Local()..foo()..bar();
+}
+''');
+
+    // Compile script to Kernel IR.
+    await run(genKernel, <String>[
+      '--aot',
+      '--platform=$platformDill',
+      '-o',
+      scriptDill,
+      script,
+    ]);
+
+    if (Platform.isWindows) {
+      return; // No assembly generation on Windows.
+    }
+
+    final assembly = path.join(tempDir, 'program.S');
+    await run(genSnapshot, <String>[
+      '--snapshot-kind=app-aot-assembly',
+      '--assembly=$assembly',
+      scriptDill,
+    ]);
+
+    final localLabelRe = RegExp(r'^L[a-zA-Z0-9_\.$]*:$', multiLine: true);
+    final match = localLabelRe.firstMatch(await File(assembly).readAsString());
+    if (match != null) {
+      Expect.isTrue(false, 'unexpected local label found ${match[0]}');
+    }
+  });
+}
diff --git a/runtime/vm/image_snapshot.cc b/runtime/vm/image_snapshot.cc
index b4e8514..8115931 100644
--- a/runtime/vm/image_snapshot.cc
+++ b/runtime/vm/image_snapshot.cc
@@ -1108,6 +1108,16 @@
 
 static void AddAssemblerIdentifier(ZoneTextBuffer* printer, const char* label) {
   ASSERT(label[0] != '.');
+  if (label[0] == 'L' && printer->length() == 0) {
+    // Assembler treats labels starting with `L` as local which can cause
+    // some issues down the line e.g. on Mac the linker might fail to encode
+    // compact unwind information because multiple functions end up being
+    // treated as a single function. See https://github.com/flutter/flutter/issues/102281.
+    //
+    // Avoid this by prepending an underscore.
+    printer->AddString("_");
+  }
+
   for (char c = *label; c != '\0'; c = *++label) {
 #define OP(dart_name, asm_name)                                                \
   if (strncmp(label, dart_name, strlen(dart_name)) == 0) {                     \
diff --git a/tools/VERSION b/tools/VERSION
index 5b204e6..c444be5 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 18
 PATCH 0
-PRERELEASE 42
+PRERELEASE 43
 PRERELEASE_PATCH 0
\ No newline at end of file