Version 2.17.0-6.0.dev
Merge commit '226d3c69f4de8ecd784a03c611d33d9a8e15badf' into 'dev'
diff --git a/pkg/front_end/lib/src/fasta/builder/class_builder.dart b/pkg/front_end/lib/src/fasta/builder/class_builder.dart
index c6b977b..f5454a5 100644
--- a/pkg/front_end/lib/src/fasta/builder/class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/class_builder.dart
@@ -10,8 +10,6 @@
Constructor,
DartType,
DynamicType,
- FunctionNode,
- FunctionType,
FutureOrType,
InterfaceType,
Member,
@@ -19,47 +17,27 @@
NullType,
Nullability,
Supertype,
- TreeNode,
- TypeParameter,
getAsTypeArguments;
-import 'package:kernel/class_hierarchy.dart'
- show ClassHierarchy, ClassHierarchyMembers;
-import 'package:kernel/core_types.dart' show CoreTypes;
+import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
import 'package:kernel/src/legacy_erasure.dart';
import 'package:kernel/text/text_serialization_verifier.dart';
-import 'package:kernel/type_algebra.dart' show Substitution, substitute;
-import 'package:kernel/type_environment.dart'
- show SubtypeCheckMode, TypeEnvironment;
import '../fasta_codes.dart';
-import '../kernel/kernel_helper.dart';
-import '../kernel/redirecting_factory_body.dart' show RedirectingFactoryBody;
-import '../loader.dart';
import '../modifier.dart';
-import '../names.dart' show noSuchMethodName;
import '../problems.dart' show internalProblem, unhandled;
import '../scope.dart';
-import '../source/source_factory_builder.dart';
-import '../source/source_library_builder.dart' show SourceLibraryBuilder;
-import '../source/source_loader.dart';
-import '../source/source_member_builder.dart';
import '../type_inference/type_schema.dart' show UnknownType;
-import '../util/helpers.dart' show DelayedActionPerformer;
import 'builder.dart';
-import 'constructor_reference_builder.dart';
import 'declaration_builder.dart';
-import 'function_builder.dart';
import 'library_builder.dart';
import 'member_builder.dart';
import 'metadata_builder.dart';
import 'named_type_builder.dart';
-import 'never_type_declaration_builder.dart';
import 'nullability_builder.dart';
import 'type_alias_builder.dart';
import 'type_builder.dart';
import 'type_declaration_builder.dart';
import 'type_variable_builder.dart';
-import 'void_type_declaration_builder.dart';
abstract class ClassBuilder implements DeclarationBuilder {
/// The type variables declared on a class, extension or mixin declaration.
@@ -100,12 +78,6 @@
abstract TypeBuilder? mixedInTypeBuilder;
- void buildOutlineExpressions(
- SourceLibraryBuilder library,
- ClassHierarchy classHierarchy,
- List<DelayedActionPerformer> delayedActionPerformers,
- List<SynthesizedFunctionNode> synthesizedFunctionNodes);
-
/// Registers a constructor redirection for this class and returns true if
/// this redirection gives rise to a cycle that has not been reported before.
bool checkConstructorCyclic(String source, String target);
@@ -147,50 +119,6 @@
Supertype buildMixedInType(
LibraryBuilder library, List<TypeBuilder>? arguments);
- void checkSupertypes(CoreTypes coreTypes);
-
- void handleSeenCovariant(
- ClassHierarchyMembers memberHierarchy,
- Member interfaceMember,
- bool isSetter,
- callback(Member interfaceMember, bool isSetter));
-
- bool hasUserDefinedNoSuchMethod(
- Class klass, ClassHierarchy hierarchy, Class objectClass);
-
- void checkMixinApplication(ClassHierarchy hierarchy, CoreTypes coreTypes);
-
- // Computes the function type of a given redirection target. Returns [null] if
- // the type of the target could not be computed.
- FunctionType? computeRedirecteeType(
- RedirectingFactoryBuilder factory, TypeEnvironment typeEnvironment);
-
- String computeRedirecteeName(ConstructorReferenceBuilder redirectionTarget);
-
- void checkRedirectingFactory(
- RedirectingFactoryBuilder factory, TypeEnvironment typeEnvironment);
-
- void checkRedirectingFactories(TypeEnvironment typeEnvironment);
-
- /// Returns a map which maps the type variables of [superclass] to their
- /// respective values as defined by the superclass clause of this class (and
- /// its superclasses).
- ///
- /// It's assumed that [superclass] is a superclass of this class.
- ///
- /// For example, given:
- ///
- /// class Box<T> {}
- /// class BeatBox extends Box<Beat> {}
- /// class Beat {}
- ///
- /// We have:
- ///
- /// [[BeatBox]].getSubstitutionMap([[Box]]) -> {[[Box::T]]: Beat]]}.
- ///
- /// It's an error if [superclass] isn't a superclass.
- Map<TypeParameter, DartType> getSubstitutionMap(Class superclass);
-
/// Looks up the member by [name] on the class built by this class builder.
///
/// If [isSetter] is `false`, only fields, methods, and getters with that name
@@ -306,31 +234,6 @@
}
}
- @override
- void buildOutlineExpressions(
- SourceLibraryBuilder library,
- ClassHierarchy classHierarchy,
- List<DelayedActionPerformer> delayedActionPerformers,
- List<SynthesizedFunctionNode> synthesizedFunctionNodes) {
- void build(String ignore, Builder declaration) {
- SourceMemberBuilder member = declaration as SourceMemberBuilder;
- member.buildOutlineExpressions(library, classHierarchy,
- delayedActionPerformers, synthesizedFunctionNodes);
- }
-
- MetadataBuilder.buildAnnotations(isPatch ? origin.cls : cls, metadata,
- library, this, null, fileUri, library.scope);
- if (typeVariables != null) {
- for (int i = 0; i < typeVariables!.length; i++) {
- typeVariables![i].buildOutlineExpressions(library, this, null,
- classHierarchy, delayedActionPerformers, scope.parent!);
- }
- }
-
- constructors.forEach(build);
- scope.forEach(build);
- }
-
/// Registers a constructor redirection for this class and returns true if
/// this redirection gives rise to a cycle that has not been reported before.
@override
@@ -490,44 +393,6 @@
}
@override
- int get typeVariablesCount => typeVariables?.length ?? 0;
-
- @override
- List<DartType> buildTypeArguments(
- LibraryBuilder library, List<TypeBuilder>? arguments) {
- if (arguments == null && typeVariables == null) {
- return <DartType>[];
- }
-
- if (arguments == null && typeVariables != null) {
- List<DartType> result = new List<DartType>.generate(typeVariables!.length,
- (int i) => typeVariables![i].defaultType!.build(library),
- growable: true);
- if (library is SourceLibraryBuilder) {
- library.inferredTypes.addAll(result);
- }
- return result;
- }
-
- if (arguments != null && arguments.length != typeVariablesCount) {
- // That should be caught and reported as a compile-time error earlier.
- return unhandled(
- templateTypeArgumentMismatch
- .withArguments(typeVariablesCount)
- .problemMessage,
- "buildTypeArguments",
- -1,
- null);
- }
-
- assert(arguments!.length == typeVariablesCount);
- List<DartType> result = new List<DartType>.generate(
- arguments!.length, (int i) => arguments[i].build(library),
- growable: true);
- return result;
- }
-
- @override
DartType buildType(LibraryBuilder library,
NullabilityBuilder nullabilityBuilder, List<TypeBuilder>? arguments) {
return buildTypeWithBuiltArguments(
@@ -565,145 +430,6 @@
}
@override
- void checkSupertypes(CoreTypes coreTypes) {
- // This method determines whether the class (that's being built) its super
- // class appears both in 'extends' and 'implements' clauses and whether any
- // interface appears multiple times in the 'implements' clause.
- // Moreover, it checks that `FutureOr` and `void` are not among the
- // supertypes.
-
- void fail(NamedTypeBuilder target, Message message,
- TypeAliasBuilder? aliasBuilder) {
- int nameOffset = target.nameOffset;
- int nameLength = target.nameLength;
- // TODO(eernst): nameOffset not fully implemented; use backup.
- if (nameOffset == -1) {
- nameOffset = this.charOffset;
- nameLength = noLength;
- }
- if (aliasBuilder != null) {
- addProblem(message, nameOffset, nameLength, context: [
- messageTypedefCause.withLocation(
- aliasBuilder.fileUri, aliasBuilder.charOffset, noLength),
- ]);
- } else {
- addProblem(message, nameOffset, nameLength);
- }
- }
-
- // Extract and check superclass (if it exists).
- ClassBuilder? superClass;
- TypeBuilder? superClassType = supertypeBuilder;
- if (superClassType is NamedTypeBuilder) {
- TypeDeclarationBuilder? decl = superClassType.declaration;
- TypeAliasBuilder? aliasBuilder; // Non-null if a type alias is use.
- if (decl is TypeAliasBuilder) {
- aliasBuilder = decl;
- decl = aliasBuilder.unaliasDeclaration(superClassType.arguments,
- isUsedAsClass: true,
- usedAsClassCharOffset: superClassType.charOffset,
- usedAsClassFileUri: superClassType.fileUri);
- }
- // TODO(eernst): Should gather 'restricted supertype' checks in one place,
- // e.g., dynamic/int/String/Null and more are checked elsewhere.
- if (decl is VoidTypeDeclarationBuilder) {
- fail(superClassType, messageExtendsVoid, aliasBuilder);
- } else if (decl is NeverTypeDeclarationBuilder) {
- fail(superClassType, messageExtendsNever, aliasBuilder);
- } else if (decl is ClassBuilder) {
- superClass = decl;
- }
- }
- if (interfaceBuilders == null) return;
-
- // Validate interfaces.
- Map<ClassBuilder, int>? problems;
- Map<ClassBuilder, int>? problemsOffsets;
- Set<ClassBuilder> implemented = new Set<ClassBuilder>();
- for (TypeBuilder type in interfaceBuilders!) {
- if (type is NamedTypeBuilder) {
- int? charOffset = type.charOffset;
- TypeDeclarationBuilder? typeDeclaration = type.declaration;
- TypeDeclarationBuilder? decl;
- TypeAliasBuilder? aliasBuilder; // Non-null if a type alias is used.
- if (typeDeclaration is TypeAliasBuilder) {
- aliasBuilder = typeDeclaration;
- decl = aliasBuilder.unaliasDeclaration(type.arguments,
- isUsedAsClass: true,
- usedAsClassCharOffset: type.charOffset,
- usedAsClassFileUri: type.fileUri);
- } else {
- decl = typeDeclaration;
- }
- if (decl is ClassBuilder) {
- ClassBuilder interface = decl;
- if (superClass == interface) {
- addProblem(
- templateImplementsSuperClass.withArguments(interface.name),
- this.charOffset,
- noLength);
- } else if (interface.cls.name == "FutureOr" &&
- interface.cls.enclosingLibrary.importUri.scheme == "dart" &&
- interface.cls.enclosingLibrary.importUri.path == "async") {
- addProblem(messageImplementsFutureOr, this.charOffset, noLength);
- } else if (implemented.contains(interface)) {
- // Aggregate repetitions.
- problems ??= <ClassBuilder, int>{};
- problems[interface] ??= 0;
- problems[interface] = problems[interface]! + 1;
- problemsOffsets ??= <ClassBuilder, int>{};
- problemsOffsets[interface] ??= charOffset ?? TreeNode.noOffset;
- } else {
- implemented.add(interface);
- }
- }
- if (decl != superClass) {
- // TODO(eernst): Have all 'restricted supertype' checks in one place.
- if (decl is VoidTypeDeclarationBuilder) {
- fail(type, messageImplementsVoid, aliasBuilder);
- } else if (decl is NeverTypeDeclarationBuilder) {
- fail(type, messageImplementsNever, aliasBuilder);
- }
- }
- }
- }
- if (problems != null) {
- problems.forEach((ClassBuilder interface, int repetitions) {
- addProblem(
- templateImplementsRepeated.withArguments(
- interface.name, repetitions),
- problemsOffsets![interface]!,
- noLength);
- });
- }
- }
-
- @override
- void handleSeenCovariant(
- ClassHierarchyMembers memberHierarchy,
- Member interfaceMember,
- bool isSetter,
- callback(Member interfaceMember, bool isSetter)) {
- // When a parameter is covariant we have to check that we also
- // override the same member in all parents.
- for (Supertype supertype in interfaceMember.enclosingClass!.supers) {
- Member? member = memberHierarchy.getInterfaceMember(
- supertype.classNode, interfaceMember.name,
- setter: isSetter);
- if (member != null) {
- callback(member, isSetter);
- }
- }
- }
-
- @override
- bool hasUserDefinedNoSuchMethod(
- Class klass, ClassHierarchy hierarchy, Class objectClass) {
- Member? noSuchMethod = hierarchy.getDispatchTarget(klass, noSuchMethodName);
- return noSuchMethod != null && noSuchMethod.enclosingClass != objectClass;
- }
-
- @override
String get fullNameForErrors {
return isMixinApplication && !isNamedMixinApplication
? "${supertypeBuilder!.fullNameForErrors} with "
@@ -712,292 +438,6 @@
}
@override
- void checkMixinApplication(ClassHierarchy hierarchy, CoreTypes coreTypes) {
- TypeEnvironment typeEnvironment = new TypeEnvironment(coreTypes, hierarchy);
- // A mixin declaration can only be applied to a class that implements all
- // the declaration's superclass constraints.
- InterfaceType supertype = cls.supertype!.asInterfaceType;
- Substitution substitution = Substitution.fromSupertype(cls.mixedInType!);
- for (Supertype constraint in cls.mixedInClass!.superclassConstraints()) {
- InterfaceType requiredInterface =
- substitution.substituteSupertype(constraint).asInterfaceType;
- InterfaceType? implementedInterface = hierarchy.getTypeAsInstanceOf(
- supertype, requiredInterface.classNode, library.library);
- if (implementedInterface == null ||
- !typeEnvironment.areMutualSubtypes(
- implementedInterface,
- requiredInterface,
- library.isNonNullableByDefault
- ? SubtypeCheckMode.withNullabilities
- : SubtypeCheckMode.ignoringNullabilities)) {
- library.addProblem(
- templateMixinApplicationIncompatibleSupertype.withArguments(
- supertype,
- requiredInterface,
- cls.mixedInType!.asInterfaceType,
- library.isNonNullableByDefault),
- cls.fileOffset,
- noLength,
- cls.fileUri);
- }
- }
- }
-
- @override
- FunctionType? computeRedirecteeType(
- RedirectingFactoryBuilder factory, TypeEnvironment typeEnvironment) {
- ConstructorReferenceBuilder redirectionTarget = factory.redirectionTarget;
- Builder? targetBuilder = redirectionTarget.target;
- FunctionNode targetNode;
- if (targetBuilder == null) return null;
- if (targetBuilder is FunctionBuilder) {
- targetNode = targetBuilder.function;
- } else if (targetBuilder is AmbiguousBuilder) {
- // Multiple definitions with the same name: An error has already been
- // issued.
- // TODO(http://dartbug.com/35294): Unfortunate error; see also
- // https://dart-review.googlesource.com/c/sdk/+/85390/.
- return null;
- } else {
- unhandled("${redirectionTarget.target}", "computeRedirecteeType",
- charOffset, fileUri);
- }
-
- List<DartType>? typeArguments = factory.getTypeArguments();
- FunctionType targetFunctionType =
- targetNode.computeFunctionType(library.nonNullable);
- if (typeArguments != null &&
- targetFunctionType.typeParameters.length != typeArguments.length) {
- addProblemForRedirectingFactory(
- factory,
- templateTypeArgumentMismatch
- .withArguments(targetFunctionType.typeParameters.length),
- redirectionTarget.charOffset,
- noLength);
- return null;
- }
-
- // Compute the substitution of the target class type parameters if
- // [redirectionTarget] has any type arguments.
- Substitution? substitution;
- bool hasProblem = false;
- if (typeArguments != null && typeArguments.length > 0) {
- substitution = Substitution.fromPairs(
- targetFunctionType.typeParameters, typeArguments);
- for (int i = 0; i < targetFunctionType.typeParameters.length; i++) {
- TypeParameter typeParameter = targetFunctionType.typeParameters[i];
- DartType typeParameterBound =
- substitution.substituteType(typeParameter.bound);
- DartType typeArgument = typeArguments[i];
- // Check whether the [typeArgument] respects the bounds of
- // [typeParameter].
- Loader loader = library.loader;
- if (!typeEnvironment.isSubtypeOf(typeArgument, typeParameterBound,
- SubtypeCheckMode.ignoringNullabilities)) {
- addProblemForRedirectingFactory(
- factory,
- templateRedirectingFactoryIncompatibleTypeArgument.withArguments(
- typeArgument,
- typeParameterBound,
- library.isNonNullableByDefault),
- redirectionTarget.charOffset,
- noLength);
- hasProblem = true;
- } else if (library.isNonNullableByDefault && loader is SourceLoader) {
- if (!typeEnvironment.isSubtypeOf(typeArgument, typeParameterBound,
- SubtypeCheckMode.withNullabilities)) {
- addProblemForRedirectingFactory(
- factory,
- templateRedirectingFactoryIncompatibleTypeArgument
- .withArguments(typeArgument, typeParameterBound,
- library.isNonNullableByDefault),
- redirectionTarget.charOffset,
- noLength);
- hasProblem = true;
- }
- }
- }
- } else if (typeArguments == null &&
- targetFunctionType.typeParameters.length > 0) {
- // TODO(hillerstrom): In this case, we need to perform type inference on
- // the redirectee to obtain actual type arguments which would allow the
- // following program to type check:
- //
- // class A<T> {
- // factory A() = B;
- // }
- // class B<T> implements A<T> {
- // B();
- // }
- //
- return null;
- }
-
- // Substitute if necessary.
- targetFunctionType = substitution == null
- ? targetFunctionType
- : (substitution.substituteType(targetFunctionType.withoutTypeParameters)
- as FunctionType);
-
- return hasProblem ? null : targetFunctionType;
- }
-
- @override
- String computeRedirecteeName(ConstructorReferenceBuilder redirectionTarget) {
- String targetName = redirectionTarget.fullNameForErrors;
- if (targetName == "") {
- return redirectionTarget.target!.parent!.fullNameForErrors;
- } else {
- return targetName;
- }
- }
-
- bool _isCyclicRedirectingFactory(RedirectingFactoryBuilder factory) {
- // We use the [tortoise and hare algorithm]
- // (https://en.wikipedia.org/wiki/Cycle_detection#Tortoise_and_hare) to
- // handle cycles.
- Builder? tortoise = factory;
- Builder? hare = factory.redirectionTarget.target;
- if (hare == factory) {
- return true;
- }
- while (tortoise != hare) {
- // Hare moves 2 steps forward.
- if (hare is! RedirectingFactoryBuilder) {
- return false;
- }
- hare = hare.redirectionTarget.target;
- if (hare == factory) {
- return true;
- }
- if (hare is! RedirectingFactoryBuilder) {
- return false;
- }
- hare = hare.redirectionTarget.target;
- if (hare == factory) {
- return true;
- }
- // Tortoise moves one step forward. No need to test type of tortoise
- // as it follows hare which already checked types.
- tortoise =
- (tortoise as RedirectingFactoryBuilder).redirectionTarget.target;
- }
- // Cycle found, but original factory doesn't belong to a cycle.
- return false;
- }
-
- void addProblemForRedirectingFactory(RedirectingFactoryBuilder factory,
- Message message, int charOffset, int length) {
- addProblem(message, charOffset, length);
- String text = library.loader.target.context
- .format(
- message.withLocation(fileUri, charOffset, length), Severity.error)
- .plain;
- factory.body = new RedirectingFactoryBody.error(text);
- }
-
- @override
- void checkRedirectingFactory(
- RedirectingFactoryBuilder factory, TypeEnvironment typeEnvironment) {
- // Check that factory declaration is not cyclic.
- if (_isCyclicRedirectingFactory(factory)) {
- addProblemForRedirectingFactory(
- factory,
- templateCyclicRedirectingFactoryConstructors
- .withArguments("${factory.member.enclosingClass!.name}"
- "${factory.name == '' ? '' : '.${factory.name}'}"),
- factory.charOffset,
- noLength);
- return;
- }
-
- // The factory type cannot contain any type parameters other than those of
- // its enclosing class, because constructors cannot specify type parameters
- // of their own.
- FunctionType factoryType = factory.function
- .computeThisFunctionType(library.nonNullable)
- .withoutTypeParameters;
- FunctionType? redirecteeType =
- computeRedirecteeType(factory, typeEnvironment);
-
- // TODO(hillerstrom): It would be preferable to know whether a failure
- // happened during [_computeRedirecteeType].
- if (redirecteeType == null) {
- return;
- }
-
- // Check whether [redirecteeType] <: [factoryType].
- Loader loader = library.loader;
- if (!typeEnvironment.isSubtypeOf(
- redirecteeType, factoryType, SubtypeCheckMode.ignoringNullabilities)) {
- addProblemForRedirectingFactory(
- factory,
- templateIncompatibleRedirecteeFunctionType.withArguments(
- redirecteeType, factoryType, library.isNonNullableByDefault),
- factory.redirectionTarget.charOffset,
- noLength);
- } else if (library.isNonNullableByDefault && loader is SourceLoader) {
- if (!typeEnvironment.isSubtypeOf(
- redirecteeType, factoryType, SubtypeCheckMode.withNullabilities)) {
- addProblemForRedirectingFactory(
- factory,
- templateIncompatibleRedirecteeFunctionType.withArguments(
- redirecteeType, factoryType, library.isNonNullableByDefault),
- factory.redirectionTarget.charOffset,
- noLength);
- }
- }
- }
-
- @override
- void checkRedirectingFactories(TypeEnvironment typeEnvironment) {
- Map<String, MemberBuilder> constructors = this.constructors.local;
- for (Builder? constructor in constructors.values) {
- do {
- if (constructor is RedirectingFactoryBuilder) {
- checkRedirectingFactory(constructor, typeEnvironment);
- }
- constructor = constructor!.next;
- } while (constructor != null);
- }
- }
-
- @override
- Map<TypeParameter, DartType> getSubstitutionMap(Class superclass) {
- Supertype? supertype = cls.supertype;
- Map<TypeParameter, DartType> substitutionMap = <TypeParameter, DartType>{};
- List<DartType> arguments;
- List<TypeParameter> variables;
- Class? classNode;
-
- while (classNode != superclass) {
- classNode = supertype!.classNode;
- arguments = supertype.typeArguments;
- variables = classNode.typeParameters;
- supertype = classNode.supertype;
- if (variables.isNotEmpty) {
- Map<TypeParameter, DartType> directSubstitutionMap =
- <TypeParameter, DartType>{};
- for (int i = 0; i < variables.length; i++) {
- DartType argument =
- i < arguments.length ? arguments[i] : const DynamicType();
- // ignore: unnecessary_null_comparison
- if (substitutionMap != null) {
- // TODO(ahe): Investigate if requiring the caller to use
- // `substituteDeep` from `package:kernel/type_algebra.dart` instead
- // of `substitute` is faster. If so, we can simply this code.
- argument = substitute(argument, substitutionMap);
- }
- directSubstitutionMap[variables[i]] = argument;
- }
- substitutionMap = directSubstitutionMap;
- }
- }
-
- return substitutionMap;
- }
-
- @override
Member? lookupInstanceMember(ClassHierarchy hierarchy, Name name,
{bool isSetter: false, bool isSuper: false}) {
Class? instanceClass = cls;
diff --git a/pkg/front_end/lib/src/fasta/source/source_class_builder.dart b/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
index e645c59..3faf679 100644
--- a/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
@@ -7,6 +7,7 @@
import 'package:kernel/ast.dart';
import 'package:kernel/class_hierarchy.dart'
show ClassHierarchy, ClassHierarchyMembers;
+import 'package:kernel/core_types.dart';
import 'package:kernel/reference_from_index.dart' show IndexedClass;
import 'package:kernel/src/bounds_checks.dart';
import 'package:kernel/src/legacy_erasure.dart';
@@ -16,6 +17,7 @@
FreshTypeParameters,
Substitution,
getFreshTypeParameters,
+ substitute,
updateBoundNullabilities;
import 'package:kernel/type_environment.dart';
@@ -28,17 +30,20 @@
import '../builder/member_builder.dart';
import '../builder/metadata_builder.dart';
import '../builder/named_type_builder.dart';
+import '../builder/never_type_declaration_builder.dart';
import '../builder/nullability_builder.dart';
import '../builder/type_alias_builder.dart';
import '../builder/type_builder.dart';
import '../builder/type_declaration_builder.dart';
import '../builder/type_variable_builder.dart';
+import '../builder/void_type_declaration_builder.dart';
import '../dill/dill_member_builder.dart';
import '../fasta_codes.dart';
import '../kernel/combined_member_signature.dart';
import '../kernel/kernel_helper.dart';
import '../kernel/kernel_target.dart' show KernelTarget;
-import '../kernel/redirecting_factory_body.dart' show redirectingName;
+import '../kernel/redirecting_factory_body.dart'
+ show RedirectingFactoryBody, redirectingName;
import '../kernel/type_algorithms.dart' show computeTypeVariableBuilderVariance;
import '../kernel/utils.dart' show compareProcedures;
import '../names.dart' show equalsName, noSuchMethodName;
@@ -188,7 +193,7 @@
scope.forEach(buildBuilders);
constructors.forEach(buildBuilders);
if (supertypeBuilder != null) {
- supertypeBuilder = checkSupertype(supertypeBuilder!);
+ supertypeBuilder = _checkSupertype(supertypeBuilder!);
}
Supertype? supertype =
supertypeBuilder?.buildSupertype(library, charOffset, fileUri);
@@ -222,7 +227,7 @@
actualCls.supertype = supertype;
if (mixedInTypeBuilder != null) {
- mixedInTypeBuilder = checkSupertype(mixedInTypeBuilder!);
+ mixedInTypeBuilder = _checkSupertype(mixedInTypeBuilder!);
}
Supertype? mixedInType =
mixedInTypeBuilder?.buildMixedInType(library, charOffset, fileUri);
@@ -245,7 +250,7 @@
cls.isMacro = isMacro;
if (interfaceBuilders != null) {
for (int i = 0; i < interfaceBuilders!.length; ++i) {
- interfaceBuilders![i] = checkSupertype(interfaceBuilders![i]);
+ interfaceBuilders![i] = _checkSupertype(interfaceBuilders![i]);
Supertype? supertype =
interfaceBuilders![i].buildSupertype(library, charOffset, fileUri);
if (supertype != null) {
@@ -314,6 +319,30 @@
return false;
}
+ void buildOutlineExpressions(
+ SourceLibraryBuilder library,
+ ClassHierarchy classHierarchy,
+ List<DelayedActionPerformer> delayedActionPerformers,
+ List<SynthesizedFunctionNode> synthesizedFunctionNodes) {
+ void build(String ignore, Builder declaration) {
+ SourceMemberBuilder member = declaration as SourceMemberBuilder;
+ member.buildOutlineExpressions(library, classHierarchy,
+ delayedActionPerformers, synthesizedFunctionNodes);
+ }
+
+ MetadataBuilder.buildAnnotations(isPatch ? origin.cls : cls, metadata,
+ library, this, null, fileUri, library.scope);
+ if (typeVariables != null) {
+ for (int i = 0; i < typeVariables!.length; i++) {
+ typeVariables![i].buildOutlineExpressions(library, this, null,
+ classHierarchy, delayedActionPerformers, scope.parent!);
+ }
+ }
+
+ constructors.forEach(build);
+ scope.forEach(build);
+ }
+
@override
void forEachConstructor(void Function(String, MemberBuilder) f,
{bool includeInjectedConstructors: false}) {
@@ -383,6 +412,95 @@
}
@override
+ int get typeVariablesCount => typeVariables?.length ?? 0;
+
+ @override
+ List<DartType> buildTypeArguments(
+ LibraryBuilder library, List<TypeBuilder>? arguments) {
+ if (arguments == null && typeVariables == null) {
+ return <DartType>[];
+ }
+
+ if (arguments == null && typeVariables != null) {
+ List<DartType> result = new List<DartType>.generate(typeVariables!.length,
+ (int i) => typeVariables![i].defaultType!.build(library),
+ growable: true);
+ if (library is SourceLibraryBuilder) {
+ library.inferredTypes.addAll(result);
+ }
+ return result;
+ }
+
+ if (arguments != null && arguments.length != typeVariablesCount) {
+ // That should be caught and reported as a compile-time error earlier.
+ return unhandled(
+ templateTypeArgumentMismatch
+ .withArguments(typeVariablesCount)
+ .problemMessage,
+ "buildTypeArguments",
+ -1,
+ null);
+ }
+
+ assert(arguments!.length == typeVariablesCount);
+ List<DartType> result = new List<DartType>.generate(
+ arguments!.length, (int i) => arguments[i].build(library),
+ growable: true);
+ return result;
+ }
+
+ /// Returns a map which maps the type variables of [superclass] to their
+ /// respective values as defined by the superclass clause of this class (and
+ /// its superclasses).
+ ///
+ /// It's assumed that [superclass] is a superclass of this class.
+ ///
+ /// For example, given:
+ ///
+ /// class Box<T> {}
+ /// class BeatBox extends Box<Beat> {}
+ /// class Beat {}
+ ///
+ /// We have:
+ ///
+ /// [[BeatBox]].getSubstitutionMap([[Box]]) -> {[[Box::T]]: Beat]]}.
+ ///
+ /// It's an error if [superclass] isn't a superclass.
+ Map<TypeParameter, DartType> getSubstitutionMap(Class superclass) {
+ Supertype? supertype = cls.supertype;
+ Map<TypeParameter, DartType> substitutionMap = <TypeParameter, DartType>{};
+ List<DartType> arguments;
+ List<TypeParameter> variables;
+ Class? classNode;
+
+ while (classNode != superclass) {
+ classNode = supertype!.classNode;
+ arguments = supertype.typeArguments;
+ variables = classNode.typeParameters;
+ supertype = classNode.supertype;
+ if (variables.isNotEmpty) {
+ Map<TypeParameter, DartType> directSubstitutionMap =
+ <TypeParameter, DartType>{};
+ for (int i = 0; i < variables.length; i++) {
+ DartType argument =
+ i < arguments.length ? arguments[i] : const DynamicType();
+ // ignore: unnecessary_null_comparison
+ if (substitutionMap != null) {
+ // TODO(ahe): Investigate if requiring the caller to use
+ // `substituteDeep` from `package:kernel/type_algebra.dart` instead
+ // of `substitute` is faster. If so, we can simply this code.
+ argument = substitute(argument, substitutionMap);
+ }
+ directSubstitutionMap[variables[i]] = argument;
+ }
+ substitutionMap = directSubstitutionMap;
+ }
+ }
+
+ return substitutionMap;
+ }
+
+ @override
void applyPatch(Builder patch) {
if (patch is SourceClassBuilder) {
patch.actualOrigin = this;
@@ -431,7 +549,357 @@
}
}
- TypeBuilder checkSupertype(TypeBuilder supertype) {
+ void checkSupertypes(CoreTypes coreTypes) {
+ // This method determines whether the class (that's being built) its super
+ // class appears both in 'extends' and 'implements' clauses and whether any
+ // interface appears multiple times in the 'implements' clause.
+ // Moreover, it checks that `FutureOr` and `void` are not among the
+ // supertypes.
+
+ void fail(NamedTypeBuilder target, Message message,
+ TypeAliasBuilder? aliasBuilder) {
+ int nameOffset = target.nameOffset;
+ int nameLength = target.nameLength;
+ // TODO(eernst): nameOffset not fully implemented; use backup.
+ if (nameOffset == -1) {
+ nameOffset = this.charOffset;
+ nameLength = noLength;
+ }
+ if (aliasBuilder != null) {
+ addProblem(message, nameOffset, nameLength, context: [
+ messageTypedefCause.withLocation(
+ aliasBuilder.fileUri, aliasBuilder.charOffset, noLength),
+ ]);
+ } else {
+ addProblem(message, nameOffset, nameLength);
+ }
+ }
+
+ // Extract and check superclass (if it exists).
+ ClassBuilder? superClass;
+ TypeBuilder? superClassType = supertypeBuilder;
+ if (superClassType is NamedTypeBuilder) {
+ TypeDeclarationBuilder? decl = superClassType.declaration;
+ TypeAliasBuilder? aliasBuilder; // Non-null if a type alias is use.
+ if (decl is TypeAliasBuilder) {
+ aliasBuilder = decl;
+ decl = aliasBuilder.unaliasDeclaration(superClassType.arguments,
+ isUsedAsClass: true,
+ usedAsClassCharOffset: superClassType.charOffset,
+ usedAsClassFileUri: superClassType.fileUri);
+ }
+ // TODO(eernst): Should gather 'restricted supertype' checks in one place,
+ // e.g., dynamic/int/String/Null and more are checked elsewhere.
+ if (decl is VoidTypeDeclarationBuilder) {
+ fail(superClassType, messageExtendsVoid, aliasBuilder);
+ } else if (decl is NeverTypeDeclarationBuilder) {
+ fail(superClassType, messageExtendsNever, aliasBuilder);
+ } else if (decl is ClassBuilder) {
+ superClass = decl;
+ }
+ }
+ if (interfaceBuilders == null) return;
+
+ // Validate interfaces.
+ Map<ClassBuilder, int>? problems;
+ Map<ClassBuilder, int>? problemsOffsets;
+ Set<ClassBuilder> implemented = new Set<ClassBuilder>();
+ for (TypeBuilder type in interfaceBuilders!) {
+ if (type is NamedTypeBuilder) {
+ int? charOffset = type.charOffset;
+ TypeDeclarationBuilder? typeDeclaration = type.declaration;
+ TypeDeclarationBuilder? decl;
+ TypeAliasBuilder? aliasBuilder; // Non-null if a type alias is used.
+ if (typeDeclaration is TypeAliasBuilder) {
+ aliasBuilder = typeDeclaration;
+ decl = aliasBuilder.unaliasDeclaration(type.arguments,
+ isUsedAsClass: true,
+ usedAsClassCharOffset: type.charOffset,
+ usedAsClassFileUri: type.fileUri);
+ } else {
+ decl = typeDeclaration;
+ }
+ if (decl is ClassBuilder) {
+ ClassBuilder interface = decl;
+ if (superClass == interface) {
+ addProblem(
+ templateImplementsSuperClass.withArguments(interface.name),
+ this.charOffset,
+ noLength);
+ } else if (interface.cls.name == "FutureOr" &&
+ interface.cls.enclosingLibrary.importUri.scheme == "dart" &&
+ interface.cls.enclosingLibrary.importUri.path == "async") {
+ addProblem(messageImplementsFutureOr, this.charOffset, noLength);
+ } else if (implemented.contains(interface)) {
+ // Aggregate repetitions.
+ problems ??= <ClassBuilder, int>{};
+ problems[interface] ??= 0;
+ problems[interface] = problems[interface]! + 1;
+ problemsOffsets ??= <ClassBuilder, int>{};
+ problemsOffsets[interface] ??= charOffset ?? TreeNode.noOffset;
+ } else {
+ implemented.add(interface);
+ }
+ }
+ if (decl != superClass) {
+ // TODO(eernst): Have all 'restricted supertype' checks in one place.
+ if (decl is VoidTypeDeclarationBuilder) {
+ fail(type, messageImplementsVoid, aliasBuilder);
+ } else if (decl is NeverTypeDeclarationBuilder) {
+ fail(type, messageImplementsNever, aliasBuilder);
+ }
+ }
+ }
+ }
+ if (problems != null) {
+ problems.forEach((ClassBuilder interface, int repetitions) {
+ addProblem(
+ templateImplementsRepeated.withArguments(
+ interface.name, repetitions),
+ problemsOffsets![interface]!,
+ noLength);
+ });
+ }
+ }
+
+ void checkMixinApplication(ClassHierarchy hierarchy, CoreTypes coreTypes) {
+ TypeEnvironment typeEnvironment = new TypeEnvironment(coreTypes, hierarchy);
+ // A mixin declaration can only be applied to a class that implements all
+ // the declaration's superclass constraints.
+ InterfaceType supertype = cls.supertype!.asInterfaceType;
+ Substitution substitution = Substitution.fromSupertype(cls.mixedInType!);
+ for (Supertype constraint in cls.mixedInClass!.superclassConstraints()) {
+ InterfaceType requiredInterface =
+ substitution.substituteSupertype(constraint).asInterfaceType;
+ InterfaceType? implementedInterface = hierarchy.getTypeAsInstanceOf(
+ supertype, requiredInterface.classNode, library.library);
+ if (implementedInterface == null ||
+ !typeEnvironment.areMutualSubtypes(
+ implementedInterface,
+ requiredInterface,
+ library.isNonNullableByDefault
+ ? SubtypeCheckMode.withNullabilities
+ : SubtypeCheckMode.ignoringNullabilities)) {
+ library.addProblem(
+ templateMixinApplicationIncompatibleSupertype.withArguments(
+ supertype,
+ requiredInterface,
+ cls.mixedInType!.asInterfaceType,
+ library.isNonNullableByDefault),
+ cls.fileOffset,
+ noLength,
+ cls.fileUri);
+ }
+ }
+ }
+
+ // Computes the function type of a given redirection target. Returns [null] if
+ // the type of the target could not be computed.
+ FunctionType? _computeRedirecteeType(
+ RedirectingFactoryBuilder factory, TypeEnvironment typeEnvironment) {
+ ConstructorReferenceBuilder redirectionTarget = factory.redirectionTarget;
+ Builder? targetBuilder = redirectionTarget.target;
+ FunctionNode targetNode;
+ if (targetBuilder == null) return null;
+ if (targetBuilder is FunctionBuilder) {
+ targetNode = targetBuilder.function;
+ } else if (targetBuilder is AmbiguousBuilder) {
+ // Multiple definitions with the same name: An error has already been
+ // issued.
+ // TODO(http://dartbug.com/35294): Unfortunate error; see also
+ // https://dart-review.googlesource.com/c/sdk/+/85390/.
+ return null;
+ } else {
+ unhandled("${redirectionTarget.target}", "computeRedirecteeType",
+ charOffset, fileUri);
+ }
+
+ List<DartType>? typeArguments = factory.getTypeArguments();
+ FunctionType targetFunctionType =
+ targetNode.computeFunctionType(library.nonNullable);
+ if (typeArguments != null &&
+ targetFunctionType.typeParameters.length != typeArguments.length) {
+ _addProblemForRedirectingFactory(
+ factory,
+ templateTypeArgumentMismatch
+ .withArguments(targetFunctionType.typeParameters.length),
+ redirectionTarget.charOffset,
+ noLength);
+ return null;
+ }
+
+ // Compute the substitution of the target class type parameters if
+ // [redirectionTarget] has any type arguments.
+ Substitution? substitution;
+ bool hasProblem = false;
+ if (typeArguments != null && typeArguments.length > 0) {
+ substitution = Substitution.fromPairs(
+ targetFunctionType.typeParameters, typeArguments);
+ for (int i = 0; i < targetFunctionType.typeParameters.length; i++) {
+ TypeParameter typeParameter = targetFunctionType.typeParameters[i];
+ DartType typeParameterBound =
+ substitution.substituteType(typeParameter.bound);
+ DartType typeArgument = typeArguments[i];
+ // Check whether the [typeArgument] respects the bounds of
+ // [typeParameter].
+ if (!typeEnvironment.isSubtypeOf(typeArgument, typeParameterBound,
+ SubtypeCheckMode.ignoringNullabilities)) {
+ _addProblemForRedirectingFactory(
+ factory,
+ templateRedirectingFactoryIncompatibleTypeArgument.withArguments(
+ typeArgument,
+ typeParameterBound,
+ library.isNonNullableByDefault),
+ redirectionTarget.charOffset,
+ noLength);
+ hasProblem = true;
+ } else if (library.isNonNullableByDefault) {
+ if (!typeEnvironment.isSubtypeOf(typeArgument, typeParameterBound,
+ SubtypeCheckMode.withNullabilities)) {
+ _addProblemForRedirectingFactory(
+ factory,
+ templateRedirectingFactoryIncompatibleTypeArgument
+ .withArguments(typeArgument, typeParameterBound,
+ library.isNonNullableByDefault),
+ redirectionTarget.charOffset,
+ noLength);
+ hasProblem = true;
+ }
+ }
+ }
+ } else if (typeArguments == null &&
+ targetFunctionType.typeParameters.length > 0) {
+ // TODO(hillerstrom): In this case, we need to perform type inference on
+ // the redirectee to obtain actual type arguments which would allow the
+ // following program to type check:
+ //
+ // class A<T> {
+ // factory A() = B;
+ // }
+ // class B<T> implements A<T> {
+ // B();
+ // }
+ //
+ return null;
+ }
+
+ // Substitute if necessary.
+ targetFunctionType = substitution == null
+ ? targetFunctionType
+ : (substitution.substituteType(targetFunctionType.withoutTypeParameters)
+ as FunctionType);
+
+ return hasProblem ? null : targetFunctionType;
+ }
+
+ bool _isCyclicRedirectingFactory(RedirectingFactoryBuilder factory) {
+ // We use the [tortoise and hare algorithm]
+ // (https://en.wikipedia.org/wiki/Cycle_detection#Tortoise_and_hare) to
+ // handle cycles.
+ Builder? tortoise = factory;
+ Builder? hare = factory.redirectionTarget.target;
+ if (hare == factory) {
+ return true;
+ }
+ while (tortoise != hare) {
+ // Hare moves 2 steps forward.
+ if (hare is! RedirectingFactoryBuilder) {
+ return false;
+ }
+ hare = hare.redirectionTarget.target;
+ if (hare == factory) {
+ return true;
+ }
+ if (hare is! RedirectingFactoryBuilder) {
+ return false;
+ }
+ hare = hare.redirectionTarget.target;
+ if (hare == factory) {
+ return true;
+ }
+ // Tortoise moves one step forward. No need to test type of tortoise
+ // as it follows hare which already checked types.
+ tortoise =
+ (tortoise as RedirectingFactoryBuilder).redirectionTarget.target;
+ }
+ // Cycle found, but original factory doesn't belong to a cycle.
+ return false;
+ }
+
+ void _addProblemForRedirectingFactory(RedirectingFactoryBuilder factory,
+ Message message, int charOffset, int length) {
+ addProblem(message, charOffset, length);
+ String text = library.loader.target.context
+ .format(
+ message.withLocation(fileUri, charOffset, length), Severity.error)
+ .plain;
+ factory.body = new RedirectingFactoryBody.error(text);
+ }
+
+ void _checkRedirectingFactory(
+ RedirectingFactoryBuilder factory, TypeEnvironment typeEnvironment) {
+ // Check that factory declaration is not cyclic.
+ if (_isCyclicRedirectingFactory(factory)) {
+ _addProblemForRedirectingFactory(
+ factory,
+ templateCyclicRedirectingFactoryConstructors
+ .withArguments("${factory.member.enclosingClass!.name}"
+ "${factory.name == '' ? '' : '.${factory.name}'}"),
+ factory.charOffset,
+ noLength);
+ return;
+ }
+
+ // The factory type cannot contain any type parameters other than those of
+ // its enclosing class, because constructors cannot specify type parameters
+ // of their own.
+ FunctionType factoryType = factory.function
+ .computeThisFunctionType(library.nonNullable)
+ .withoutTypeParameters;
+ FunctionType? redirecteeType =
+ _computeRedirecteeType(factory, typeEnvironment);
+
+ // TODO(hillerstrom): It would be preferable to know whether a failure
+ // happened during [_computeRedirecteeType].
+ if (redirecteeType == null) {
+ return;
+ }
+
+ // Check whether [redirecteeType] <: [factoryType].
+ if (!typeEnvironment.isSubtypeOf(
+ redirecteeType, factoryType, SubtypeCheckMode.ignoringNullabilities)) {
+ _addProblemForRedirectingFactory(
+ factory,
+ templateIncompatibleRedirecteeFunctionType.withArguments(
+ redirecteeType, factoryType, library.isNonNullableByDefault),
+ factory.redirectionTarget.charOffset,
+ noLength);
+ } else if (library.isNonNullableByDefault) {
+ if (!typeEnvironment.isSubtypeOf(
+ redirecteeType, factoryType, SubtypeCheckMode.withNullabilities)) {
+ _addProblemForRedirectingFactory(
+ factory,
+ templateIncompatibleRedirecteeFunctionType.withArguments(
+ redirecteeType, factoryType, library.isNonNullableByDefault),
+ factory.redirectionTarget.charOffset,
+ noLength);
+ }
+ }
+ }
+
+ void checkRedirectingFactories(TypeEnvironment typeEnvironment) {
+ Map<String, MemberBuilder> constructors = this.constructors.local;
+ for (Builder? constructor in constructors.values) {
+ do {
+ if (constructor is RedirectingFactoryBuilder) {
+ _checkRedirectingFactory(constructor, typeEnvironment);
+ }
+ constructor = constructor!.next;
+ } while (constructor != null);
+ }
+ }
+
+ TypeBuilder _checkSupertype(TypeBuilder supertype) {
if (typeVariables == null) return supertype;
Message? message;
for (int i = 0; i < typeVariables!.length; ++i) {
@@ -802,6 +1270,12 @@
return charOffset.compareTo(other.charOffset);
}
+ bool _hasUserDefinedNoSuchMethod(
+ Class klass, ClassHierarchy hierarchy, Class objectClass) {
+ Member? noSuchMethod = hierarchy.getDispatchTarget(klass, noSuchMethodName);
+ return noSuchMethod != null && noSuchMethod.enclosingClass != objectClass;
+ }
+
bool _addMissingNoSuchMethodForwarders(
KernelTarget target, Set<Member> existingForwarders,
{required bool forSetters}) {
@@ -820,7 +1294,7 @@
Procedure noSuchMethod = ClassHierarchy.findMemberByName(
hierarchy.getInterfaceMembers(cls), noSuchMethodName) as Procedure;
bool clsHasUserDefinedNoSuchMethod =
- hasUserDefinedNoSuchMethod(cls, hierarchy, target.objectClass);
+ _hasUserDefinedNoSuchMethod(cls, hierarchy, target.objectClass);
bool changed = false;
@@ -890,7 +1364,7 @@
nearestConcreteSuperclass = nearestConcreteSuperclass.superclass;
}
if (nearestConcreteSuperclass != null) {
- bool superHasUserDefinedNoSuchMethod = hasUserDefinedNoSuchMethod(
+ bool superHasUserDefinedNoSuchMethod = _hasUserDefinedNoSuchMethod(
nearestConcreteSuperclass, hierarchy, target.objectClass);
{
List<Member> concrete =
@@ -1085,14 +1559,14 @@
} else if (targetBuilder is DillMemberBuilder) {
targetNode = targetBuilder.member;
} else if (targetBuilder is AmbiguousBuilder) {
- addProblemForRedirectingFactory(
+ _addProblemForRedirectingFactory(
declaration,
templateDuplicatedDeclarationUse
.withArguments(redirectionTarget.fullNameForErrors),
redirectionTarget.charOffset,
noLength);
} else {
- addProblemForRedirectingFactory(
+ _addProblemForRedirectingFactory(
declaration,
templateRedirectionTargetNotFound
.withArguments(redirectionTarget.fullNameForErrors),
@@ -1102,7 +1576,7 @@
if (targetNode != null &&
targetNode is Constructor &&
targetNode.enclosingClass.isAbstract) {
- addProblemForRedirectingFactory(
+ _addProblemForRedirectingFactory(
declaration,
templateAbstractRedirectedClassInstantiation
.withArguments(redirectionTarget.fullNameForErrors),
@@ -1127,6 +1601,23 @@
return count;
}
+ void _handleSeenCovariant(
+ ClassHierarchyMembers memberHierarchy,
+ Member interfaceMember,
+ bool isSetter,
+ callback(Member interfaceMember, bool isSetter)) {
+ // When a parameter is covariant we have to check that we also
+ // override the same member in all parents.
+ for (Supertype supertype in interfaceMember.enclosingClass!.supers) {
+ Member? member = memberHierarchy.getInterfaceMember(
+ supertype.classNode, interfaceMember.name,
+ setter: isSetter);
+ if (member != null) {
+ callback(member, isSetter);
+ }
+ }
+ }
+
void checkOverride(
Types types,
ClassHierarchyMembers memberHierarchy,
@@ -1161,7 +1652,7 @@
isInterfaceCheck,
declaredNeedsLegacyErasure);
if (seenCovariant) {
- handleSeenCovariant(
+ _handleSeenCovariant(
memberHierarchy, interfaceMember, isSetter, callback);
}
} else if (declaredMember.kind == ProcedureKind.Getter) {
@@ -1181,7 +1672,7 @@
isInterfaceCheck,
declaredNeedsLegacyErasure);
if (seenCovariant) {
- handleSeenCovariant(
+ _handleSeenCovariant(
memberHierarchy, interfaceMember, isSetter, callback);
}
} else {
@@ -1222,7 +1713,7 @@
isInterfaceCheck,
declaredNeedsLegacyErasure);
if (seenCovariant) {
- handleSeenCovariant(
+ _handleSeenCovariant(
memberHierarchy, interfaceMember, isSetter, callback);
}
}
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index 915ff8d..28267ee 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -2963,7 +2963,7 @@
Iterator<Builder> iterator = this.iterator;
while (iterator.moveNext()) {
Builder declaration = iterator.current;
- if (declaration is ClassBuilder) {
+ if (declaration is SourceClassBuilder) {
declaration.buildOutlineExpressions(this, classHierarchy,
delayedActionPerformers, synthesizedFunctionNodes);
} else if (declaration is ExtensionBuilder) {
diff --git a/pkg/front_end/test/patching/patching_test.dart b/pkg/front_end/test/patching/patching_test.dart
index a3b4829..9828fd4 100644
--- a/pkg/front_end/test/patching/patching_test.dart
+++ b/pkg/front_end/test/patching/patching_test.dart
@@ -10,8 +10,8 @@
import 'package:front_end/src/api_prototype/compiler_options.dart';
import 'package:front_end/src/api_prototype/experimental_flags.dart';
import 'package:front_end/src/fasta/builder/builder.dart';
-import 'package:front_end/src/fasta/builder/class_builder.dart';
import 'package:front_end/src/fasta/builder/member_builder.dart';
+import 'package:front_end/src/fasta/source/source_class_builder.dart';
import 'package:front_end/src/fasta/source/source_member_builder.dart';
import 'package:front_end/src/testing/id_testing_helper.dart';
import 'package:front_end/src/testing/id_testing_utils.dart';
@@ -128,7 +128,8 @@
@override
Features computeClassValue(Id id, Class cls) {
- ClassBuilder clsBuilder = lookupClassBuilder(compilerResult, cls)!;
+ SourceClassBuilder clsBuilder =
+ lookupClassBuilder(compilerResult, cls) as SourceClassBuilder;
Features features = new Features();
if (cls.isAbstract) {
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.cc b/runtime/vm/compiler/backend/flow_graph_compiler.cc
index a6d5418..aa1d703 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.cc
@@ -3572,60 +3572,6 @@
}
}
-void FlowGraphCompiler::EmitMoveConst(const compiler::ffi::NativeLocation& dst,
- Location src,
- Representation src_type,
- TemporaryRegisterAllocator* temp) {
- ASSERT(src.IsConstant());
- const auto& dst_type = dst.payload_type();
- if (dst.IsExpressibleAsLocation() &&
- dst_type.IsExpressibleAsRepresentation() &&
- dst_type.AsRepresentationOverApprox(zone_) == src_type) {
- // We can directly emit the const in the right place and representation.
- const Location dst_loc = dst.AsLocation();
- EmitMove(dst_loc, src, temp);
- } else {
- // We need an intermediate location.
- Location intermediate;
- if (dst_type.IsInt()) {
- if (TMP == kNoRegister) {
- Register scratch = temp->AllocateTemporary();
- Location::RegisterLocation(scratch);
- } else {
- intermediate = Location::RegisterLocation(TMP);
- }
- } else {
- ASSERT(dst_type.IsFloat());
- intermediate = Location::FpuRegisterLocation(FpuTMP);
- }
-
- if (src.IsPairLocation()) {
- for (intptr_t i : {0, 1}) {
- const Representation src_type_split =
- compiler::ffi::NativeType::FromUnboxedRepresentation(zone_,
- src_type)
- .Split(zone_, i)
- .AsRepresentation();
- const auto& intermediate_native =
- compiler::ffi::NativeLocation::FromLocation(zone_, intermediate,
- src_type_split);
- EmitMove(intermediate, src.AsPairLocation()->At(i), temp);
- EmitNativeMove(dst.Split(zone_, 2, i), intermediate_native, temp);
- }
- } else {
- const auto& intermediate_native =
- compiler::ffi::NativeLocation::FromLocation(zone_, intermediate,
- src_type);
- EmitMove(intermediate, src, temp);
- EmitNativeMove(dst, intermediate_native, temp);
- }
-
- if (dst_type.IsInt() && TMP == kNoRegister) {
- temp->ReleaseTemporary();
- }
- }
- return;
-}
// The assignment to loading units here must match that in
// AssignLoadingUnitsCodeVisitor, which runs after compilation is done.
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.h b/runtime/vm/compiler/backend/flow_graph_compiler.h
index 975e431..f53e11f 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.h
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.h
@@ -613,12 +613,6 @@
const compiler::ffi::NativeLocation& src,
TemporaryRegisterAllocator* temp);
- // Emits a Dart const to a native location.
- void EmitMoveConst(const compiler::ffi::NativeLocation& dst,
- Location src,
- Representation src_type,
- TemporaryRegisterAllocator* temp);
-
bool CheckAssertAssignableTypeTestingABILocations(
const LocationSummary& locs);
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 878bcec..02daa82 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -6504,7 +6504,10 @@
ConstantTemporaryAllocator temp_alloc(temp);
if (origin.IsConstant()) {
- compiler->EmitMoveConst(def_target, origin, origin_rep, &temp_alloc);
+ // Can't occur because we currently don't inline FFI trampolines (see
+ // http://dartbug.com/45055), which means all incomming arguments
+ // originate from parameters and thus are non-constant.
+ UNREACHABLE();
} else {
compiler->EmitMoveToNative(def_target, origin, origin_rep, &temp_alloc);
}
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index cb4dcdf..4ed246d 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -8173,9 +8173,11 @@
// being inlined (for now).
if (ForceOptimize()) {
if (IsFfiTrampoline()) {
- // The CallSiteInliner::InlineCall asserts in PrepareGraphs that
- // GraphEntryInstr::SuccessorCount() == 1, but FFI trampoline has two
- // entries (a normal and a catch entry).
+ // We currently don't support inlining FFI trampolines. Some of them
+ // are naturally non-inlinable because they contain a try/catch block,
+ // but this condition is broader than strictly necessary.
+ // The work necessary for inlining FFI trampolines is tracked by
+ // http://dartbug.com/45055.
return false;
}
return CompilerState::Current().is_aot();
diff --git a/sdk/lib/core/bigint.dart b/sdk/lib/core/bigint.dart
index 63f9198..e5a871c 100644
--- a/sdk/lib/core/bigint.dart
+++ b/sdk/lib/core/bigint.dart
@@ -8,6 +8,39 @@
///
/// Big integers are signed and can have an arbitrary number of
/// significant digits, only limited by memory.
+///
+/// To create a big integer from the provided number, use [BigInt.from].
+/// ```dart
+/// var bigInteger = BigInt.from(-1); // -1
+/// bigInteger = BigInt.from(0.9999); // 0
+/// bigInteger = BigInt.from(-10.99); // -10
+/// bigInteger = BigInt.from(0x7FFFFFFFFFFFFFFF); // 9223372036854775807
+/// bigInteger = BigInt.from(1e+30); // 1000000000000000019884624838656
+/// ```
+/// To parse a large integer value from a string, use [parse] or [tryParse].
+/// ```dart
+/// var value = BigInt.parse('0x1ffffffffffffffff'); // 36893488147419103231
+/// value = BigInt.parse('12345678901234567890'); // 12345678901234567890
+/// ```
+/// To check whether a big integer can be represented as an [int] without losing
+/// precision, use [isValidInt].
+/// ```dart continued
+/// print(bigNumber.isValidInt); // false
+/// ```
+/// To convert a big integer into an [int], use [toInt].
+/// To convert a big integer into an [double], use [toDouble].
+/// ```dart
+/// var bigValue = BigInt.from(10).pow(3);
+/// print(bigValue.isValidInt); // true
+/// print(bigValue.toInt()); // 1000
+/// print(bigValue.toDouble()); // 1000.0
+/// ```
+/// **See also:**
+/// * [int]: An integer number.
+/// * [double]: A double-precision floating point number.
+/// * [num]: The super class for [int] and [double].
+/// * [Numbers](https://dart.dev/guides/language/numbers) in
+/// [A tour of the Dart language](https://dart.dev/guides/language/language-tour).
abstract class BigInt implements Comparable<BigInt> {
/// A big integer with the numerical value 0.
external static BigInt get zero;
@@ -39,6 +72,31 @@
///
/// Throws a [FormatException] if the [source] is not a valid integer literal,
/// optionally prefixed by a sign.
+ /// Examples:
+ /// ```dart
+ /// print(BigInt.parse('-12345678901234567890')); // -12345678901234567890
+ /// print(BigInt.parse('0xFF')); // 255
+ /// print(BigInt.parse('0xffffffffffffffff')); // 18446744073709551615
+ ///
+ /// // From binary (base 2) value.
+ /// print(BigInt.parse('1100', radix: 2)); // 12
+ /// print(BigInt.parse('00011111', radix: 2)); // 31
+ /// print(BigInt.parse('011111100101', radix: 2)); // 2021
+ /// // From octal (base 8) value.
+ /// print(BigInt.parse('14', radix: 8)); // 12
+ /// print(BigInt.parse('37', radix: 8)); // 31
+ /// print(BigInt.parse('3745', radix: 8)); // 2021
+ /// // From hexadecimal (base 16) value.
+ /// print(BigInt.parse('c', radix: 16)); // 12
+ /// print(BigInt.parse('1f', radix: 16)); // 31
+ /// print(BigInt.parse('7e5', radix: 16)); // 2021
+ /// // From base 35 value.
+ /// print(BigInt.parse('y1', radix: 35)); // 1191 == 34 * 35 + 1
+ /// print(BigInt.parse('z1', radix: 35)); // Throws.
+ /// // From base 36 value.
+ /// print(BigInt.parse('y1', radix: 36)); // 1225 == 34 * 36 + 1
+ /// print(BigInt.parse('z1', radix: 36)); // 1261 == 35 * 36 + 1
+ /// ```
external static BigInt parse(String source, {int? radix});
/// Parses [source] as a, possibly signed, integer literal and returns its
@@ -46,9 +104,42 @@
///
/// As [parse] except that this method returns `null` if the input is not
/// valid
+ ///
+ /// Examples:
+ /// ```dart
+ /// print(BigInt.tryParse('-12345678901234567890')); // -12345678901234567890
+ /// print(BigInt.tryParse('0xFF')); // 255
+ /// print(BigInt.tryParse('0xffffffffffffffff')); // 18446744073709551615
+ ///
+ /// // From binary (base 2) value.
+ /// print(BigInt.tryParse('1100', radix: 2)); // 12
+ /// print(BigInt.tryParse('00011111', radix: 2)); // 31
+ /// print(BigInt.tryParse('011111100101', radix: 2)); // 2021
+ /// // From octal (base 8) value.
+ /// print(BigInt.tryParse('14', radix: 8)); // 12
+ /// print(BigInt.tryParse('37', radix: 8)); // 31
+ /// print(BigInt.tryParse('3745', radix: 8)); // 2021
+ /// // From hexadecimal (base 16) value.
+ /// print(BigInt.tryParse('c', radix: 16)); // 12
+ /// print(BigInt.tryParse('1f', radix: 16)); // 31
+ /// print(BigInt.tryParse('7e5', radix: 16)); // 2021
+ /// // From base 35 value.
+ /// print(BigInt.tryParse('y1', radix: 35)); // 1191 == 34 * 35 + 1
+ /// print(BigInt.tryParse('z1', radix: 35)); // null
+ /// // From base 36 value.
+ /// print(BigInt.tryParse('y1', radix: 36)); // 1225 == 34 * 36 + 1
+ /// print(BigInt.tryParse('z1', radix: 36)); // 1261 == 35 * 36 + 1
+ /// ```
external static BigInt? tryParse(String source, {int? radix});
- /// Allocates a big integer from the provided [value] number.
+ /// Creates a big integer from the provided [value] number.
+ ///
+ /// Examples:
+ /// ```dart
+ /// var bigInteger = BigInt.from(1); // 1
+ /// bigInteger = BigInt.from(0.9999); // 0
+ /// bigInteger = BigInt.from(-10.99); // -10
+ /// ```
external factory BigInt.from(num value);
/// Returns the absolute value of this integer.
@@ -83,7 +174,14 @@
/// this operation first performs [toDouble] on both this big integer
/// and [other], then does [double.operator/] on those values and
/// returns the result.
- /// The initial [toDouble] conversion may lose precision.
+ ///
+ /// **Note:** The initial [toDouble] conversion may lose precision.
+ ///
+ /// Example:
+ /// ```dart
+ /// print(BigInt.from(1) / BigInt.from(2)); // 0.5
+ /// print(BigInt.from(1.99999) / BigInt.from(2)); // 0.5
+ /// ```
double operator /(BigInt other);
/// Truncating integer division operator.
@@ -114,6 +212,14 @@
/// The sign of the returned value `r` is always positive.
///
/// See [remainder] for the remainder of the truncating division.
+ ///
+ /// Example:
+ /// ```dart
+ /// print(BigInt.from(5) % BigInt.from(3)); // 2
+ /// print(BigInt.from(-5) % BigInt.from(3)); // 1
+ /// print(BigInt.from(5) % BigInt.from(-3)); // 2
+ /// print(BigInt.from(-5) % BigInt.from(-3)); // 1
+ /// ```
BigInt operator %(BigInt other);
/// Returns the remainder of the truncating division of `this` by [other].
@@ -121,6 +227,14 @@
/// The result `r` of this operation satisfies:
/// `this == (this ~/ other) * other + r`.
/// As a consequence the remainder `r` has the same sign as the divider `this`.
+ ///
+ /// Example:
+ /// ```dart
+ /// print(BigInt.from(5).remainder(BigInt.from(3))); // 2
+ /// print(BigInt.from(-5).remainder(BigInt.from(3))); // -2
+ /// print(BigInt.from(5).remainder(BigInt.from(-3))); // 2
+ /// print(BigInt.from(-5).remainder(BigInt.from(-3))); // -2
+ /// ```
BigInt remainder(BigInt other);
/// Shift the bits of this integer to the left by [shiftAmount].
@@ -198,12 +312,19 @@
///
/// Returns a negative number if `this` is less than `other`, zero if they are
/// equal, and a positive number if `this` is greater than `other`.
+ ///
+ /// Example:
+ /// ```dart
+ /// print(BigInt.from(1).compareTo(BigInt.from(2))); // => -1
+ /// print(BigInt.from(2).compareTo(BigInt.from(1))); // => 1
+ /// print(BigInt.from(1).compareTo(BigInt.from(1))); // => 0
+ /// ```
int compareTo(BigInt other);
/// Returns the minimum number of bits required to store this big integer.
///
/// The number of bits excludes the sign bit, which gives the natural length
- /// for non-negative (unsigned) values. Negative values are complemented to
+ /// for non-negative (unsigned) values. Negative values are complemented to
/// return the bit position of the first bit that differs from the sign bit.
///
/// To find the number of bits needed to store the value as a signed value,
@@ -246,6 +367,20 @@
///
/// The result is always equal to the mathematical result of this to the power
/// [exponent], only limited by the available memory.
+ ///
+ /// Example:
+ /// ```dart
+ /// var value = BigInt.from(1000);
+ /// print(value.pow(0)); // 1
+ /// print(value.pow(1)); // 1000
+ /// print(value.pow(2)); // 1000000
+ /// print(value.pow(3)); // 1000000000
+ /// print(value.pow(4)); // 1000000000000
+ /// print(value.pow(5)); // 1000000000000000
+ /// print(value.pow(6)); // 1000000000000000000
+ /// print(value.pow(7)); // 1000000000000000000000
+ /// print(value.pow(8)); // 1000000000000000000000000
+ /// ```
BigInt pow(int exponent);
/// Returns this integer to the power of [exponent] modulo [modulus].
@@ -269,15 +404,24 @@
/// integer dividing both `this` and `other`.
///
/// The greatest common divisor is independent of the order,
- /// so `x.gcd(y)` is always the same as `y.gcd(x)`.
+ /// so `x.gcd(y)` is always the same as `y.gcd(x)`.
///
/// For any integer `x`, `x.gcd(x)` is `x.abs()`.
///
/// If both `this` and `other` is zero, the result is also zero.
+ ///
+ /// Example:
+ /// ```dart
+ /// print(BigInt.from(4).gcd(BigInt.from(2))); // 2
+ /// print(BigInt.from(8).gcd(BigInt.from(4))); // 4
+ /// print(BigInt.from(10).gcd(BigInt.from(12))); // 2
+ /// print(BigInt.from(10).gcd(BigInt.from(10))); // 10
+ /// print(BigInt.from(-2).gcd(BigInt.from(-3))); // 1
+ /// ```
BigInt gcd(BigInt other);
/// Returns the least significant [width] bits of this big integer as a
- /// non-negative number (i.e. unsigned representation). The returned value has
+ /// non-negative number (i.e. unsigned representation). The returned value has
/// zeros in all bit positions higher than [width].
///
/// ```dart
@@ -294,7 +438,7 @@
/// `q` will count from `0` up to `255` and then wrap around to `0`.
///
/// If the input fits in [width] bits without truncation, the result is the
- /// same as the input. The minimum width needed to avoid truncation of `x` is
+ /// same as the input. The minimum width needed to avoid truncation of `x` is
/// given by `x.bitLength`, i.e.
///
/// ```dart
@@ -303,8 +447,8 @@
BigInt toUnsigned(int width);
/// Returns the least significant [width] bits of this integer, extending the
- /// highest retained bit to the sign. This is the same as truncating the value
- /// to fit in [width] bits using an signed 2-s complement representation. The
+ /// highest retained bit to the sign. This is the same as truncating the value
+ /// to fit in [width] bits using an signed 2-s complement representation. The
/// returned value has the same bit value in all positions higher than [width].
///
/// ```dart
@@ -328,7 +472,7 @@
/// `127`.
///
/// If the input value fits in [width] bits without truncation, the result is
- /// the same as the input. The minimum width needed to avoid truncation of `x`
+ /// the same as the input. The minimum width needed to avoid truncation of `x`
/// is `x.bitLength + 1`, i.e.
///
/// ```dart
@@ -339,9 +483,18 @@
/// Whether this big integer can be represented as an `int` without losing
/// precision.
///
- /// Warning: this function may give a different result on
+ /// **Warning:** this function may give a different result on
/// dart2js, dev compiler, and the VM, due to the differences in
/// integer precision.
+ ///
+ /// Example:
+ /// ```dart
+ /// var bigNumber = BigInt.parse('100000000000000000000000');
+ /// print(bigNumber.isValidInt); // false
+ ///
+ /// var value = BigInt.parse('0xFF'); // 255
+ /// print(value.isValidInt); // true
+ /// ```
bool get isValidInt;
/// Returns this [BigInt] as an [int].
@@ -349,9 +502,15 @@
/// If the number does not fit, clamps to the max (or min)
/// integer.
///
- /// Warning: the clamping behaves differently on dart2js, dev
- /// compiler, and the VM, due to the differences in integer
- /// precision.
+ /// **Warning:** the clamping behaves differently between the web and
+ /// native platforms due to the differences in integer precision.
+ ///
+ /// Example:
+ /// ```dart
+ /// var bigNumber = BigInt.parse('100000000000000000000000');
+ /// print(bigNumber.isValidInt); // false
+ /// print(bigNumber.toInt()); // 9223372036854775807
+ /// ```
int toInt();
/// Returns this [BigInt] as a [double].
@@ -359,6 +518,12 @@
/// If the number is not representable as a [double], an
/// approximation is returned. For numerically large integers, the
/// approximation may be infinite.
+ ///
+ /// Example:
+ /// ```dart
+ /// var bigNumber = BigInt.parse('100000000000000000000000');
+ /// print(bigNumber.toDouble()); // 1e+23
+ /// ```
double toDouble();
/// Returns a String-representation of this integer.
@@ -366,6 +531,12 @@
/// The returned string is parsable by [parse].
/// For any `BigInt` `i`, it is guaranteed that
/// `i == BigInt.parse(i.toString())`.
+ ///
+ /// Example:
+ /// ```dart
+ /// var bigNumber = BigInt.parse('100000000000000000000000');
+ /// print(bigNumber.toString()); // "100000000000000000000000"
+ /// ```
String toString();
/// Converts [this] to a string representation in the given [radix].
@@ -374,5 +545,24 @@
/// '9', with 'a' being 10 an 'z' being 35.
///
/// The [radix] argument must be an integer in the range 2 to 36.
+ ///
+ /// Example:
+ /// ```dart
+ /// // Binary (base 2).
+ /// print(BigInt.from(12).toRadixString(2)); // 1100
+ /// print(BigInt.from(31).toRadixString(2)); // 11111
+ /// print(BigInt.from(2021).toRadixString(2)); // 11111100101
+ /// print(BigInt.from(-12).toRadixString(2)); // -1100
+ /// // Octal (base 8).
+ /// print(BigInt.from(12).toRadixString(8)); // 14
+ /// print(BigInt.from(31).toRadixString(8)); // 37
+ /// print(BigInt.from(2021).toRadixString(8)); // 3745
+ /// // Hexadecimal (base 16).
+ /// print(BigInt.from(12).toRadixString(16)); // c
+ /// print(BigInt.from(31).toRadixString(16)); // 1f
+ /// print(BigInt.from(2021).toRadixString(16)); // 7e5
+ /// // Base 36.
+ /// print(BigInt.from(35 * 36 + 1).toRadixString(36)); // z1
+ /// ```
String toRadixString(int radix);
}
diff --git a/sdk/lib/core/identical.dart b/sdk/lib/core/identical.dart
index a8bdedf..c13ab27 100644
--- a/sdk/lib/core/identical.dart
+++ b/sdk/lib/core/identical.dart
@@ -5,6 +5,18 @@
part of dart.core;
/// Check whether two references are to the same object.
+///
+/// Example:
+/// ```dart
+/// var o = new Object();
+/// var isIdentical = identical(o, new Object()); // false, different objects.
+/// isIdentical = identical(o, o); // true, same object
+/// isIdentical = identical(const Object(), const Object()); // true, const canonicalizes
+/// isIdentical = identical([1], [1]); // false
+/// isIdentical = identical(const [1], const [1]); // true
+/// isIdentical = identical(const [1], const [2]); // false
+/// isIdentical = identical(2, 1 + 1); // true, integers canonicalizes
+/// ```
external bool identical(Object? a, Object? b);
/// The identity hash code of [object].
@@ -14,5 +26,14 @@
///
/// This hash code is compatible with [identical],
/// which just means that it's guaranteed to be stable over time.
+/// ```dart import:dart:collection
+/// var identitySet = HashSet(equals: identical, hashCode: identityHashCode);
+/// var dt1 = DateTime.now();
+/// var dt2 = DateTime.fromMicrosecondsSinceEpoch(dt1.microsecondsSinceEpoch);
+/// assert(dt1 == dt2);
+/// identitySet.add(dt1);
+/// print(identitySet.contains(dt1)); // true
+/// print(identitySet.contains(dt2)); // false
+/// ```
@pragma("vm:entry-point")
external int identityHashCode(Object? object);
diff --git a/sdk/lib/core/map.dart b/sdk/lib/core/map.dart
index f6c6caf..ae091be 100644
--- a/sdk/lib/core/map.dart
+++ b/sdk/lib/core/map.dart
@@ -451,11 +451,32 @@
}
/// A key/value pair representing an entry in a [Map].
+///
+/// The [Map] interface contains various methods that can
+/// inspect or modify the map based on entry objects.
+/// ```dart
+/// final map = {'1': 'A', '2': 'B'};
+/// map.addEntries([
+/// MapEntry('3', 'C'),
+/// MapEntry('4', 'D'),
+/// ]);
+/// print(map); // {1: A, 2: B, 3: C, 4: D}
+/// ```
class MapEntry<K, V> {
/// The key of the entry.
+ /// ```dart
+ /// final map = {'theKey': 'theValue'};
+ /// var entry = map.entries.first;
+ /// print(entry.key); // theKey
+ /// ```
final K key;
/// The value associated to [key] in the map.
+ /// ```dart
+ /// final map = {'theKey': 'theValue'};
+ /// var entry = map.entries.first;
+ /// print(entry.value); // theValue
+ /// ```
final V value;
/// Creates an entry with [key] and [value].
diff --git a/tools/VERSION b/tools/VERSION
index 0b7b793..dbe4c2a 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 17
PATCH 0
-PRERELEASE 5
+PRERELEASE 6
PRERELEASE_PATCH 0
\ No newline at end of file