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