blob: e88fdb836422a9b2c65ac9e68a281ac5b6ca3cd2 [file] [log] [blame]
// Copyright (c) 2016, 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 'package:kernel/ast.dart';
import 'package:kernel/class_hierarchy.dart';
import '../base/problems.dart' show unhandled;
import '../base/scope.dart';
import '../builder/builder.dart';
import '../builder/declaration_builders.dart';
import '../builder/formal_parameter_builder.dart';
import '../builder/invalid_type_builder.dart';
import '../builder/library_builder.dart';
import '../builder/member_builder.dart';
import '../builder/metadata_builder.dart';
import '../builder/name_iterator.dart';
import '../builder/record_type_builder.dart';
import '../builder/type_builder.dart';
import '../codes/cfe_codes.dart'
show templateCyclicTypedef, templateTypeArgumentMismatch;
import '../fragment/fragment.dart';
import '../kernel/body_builder_context.dart';
import '../kernel/constructor_tearoff_lowering.dart';
import '../kernel/expression_generator_helper.dart';
import '../kernel/kernel_helper.dart';
import 'source_library_builder.dart' show SourceLibraryBuilder;
import 'source_loader.dart';
class SourceTypeAliasBuilder extends TypeAliasBuilderImpl {
@override
final SourceLibraryBuilder parent;
@override
final int charOffset;
@override
final String name;
@override
final Uri fileUri;
late TypeBuilder _type;
final Reference _reference;
Typedef? _typedef;
@override
DartType? thisType;
@override
Map<Name, Procedure>? tearOffs;
TypedefFragment _introductory;
SourceTypeAliasBuilder(
{required this.name,
required SourceLibraryBuilder enclosingLibraryBuilder,
required this.fileUri,
required int fileOffset,
required TypedefFragment fragment,
required Reference? reference})
: charOffset = fileOffset,
parent = enclosingLibraryBuilder,
_reference = reference ?? new Reference(),
_introductory = fragment,
_type = fragment.type {
_introductory.builder = this;
}
@override
TypeBuilder get type => _type;
/// The [Typedef] built by this builder.
@override
Typedef get typedef {
assert(
_typedef != null, "Typedef node has not been created yet for $this.");
return _typedef!;
}
@override
Reference get reference => _reference;
@override
SourceLibraryBuilder get libraryBuilder =>
super.libraryBuilder as SourceLibraryBuilder;
@override
List<NominalVariableBuilder>? get typeVariables =>
_introductory.typeVariables;
@override
bool get fromDill => false;
@override
int get typeVariablesCount => typeVariables?.length ?? 0;
bool _hasCheckedForCyclicDependency = false;
void _breakCyclicDependency() {
if (_hasCheckedForCyclicDependency) return;
_typedef = new Typedef(name, null,
typeParameters:
NominalVariableBuilder.typeParametersFromBuilders(typeVariables),
fileUri: fileUri,
reference: _reference)
..fileOffset = charOffset;
if (_checkCyclicTypedefDependency(type, this, {this})) {
typedef.type = new InvalidType();
_type = new InvalidTypeBuilderImpl(fileUri, charOffset);
}
if (typeVariables != null) {
for (TypeVariableBuilder typeVariable in typeVariables!) {
if (_checkCyclicTypedefDependency(typeVariable.bound, this, {this})) {
// The bound is erroneous and should be set to [InvalidType].
typeVariable.parameterBound = new InvalidType();
typeVariable.parameterDefaultType = new InvalidType();
typeVariable.bound = new InvalidTypeBuilderImpl(fileUri, charOffset);
typeVariable.defaultType =
new InvalidTypeBuilderImpl(fileUri, charOffset);
// The typedef itself can't be used without proper bounds of its type
// variables, so we set it to mean [InvalidType] too.
typedef.type = new InvalidType();
_type = new InvalidTypeBuilderImpl(fileUri, charOffset);
}
}
}
_hasCheckedForCyclicDependency = true;
}
Typedef build() {
buildThisType();
return typedef;
}
@override
TypeBuilder? unalias(List<TypeBuilder>? typeArguments,
{Set<TypeAliasBuilder>? usedTypeAliasBuilders,
List<StructuralVariableBuilder>? unboundTypeVariables}) {
_breakCyclicDependency();
return super.unalias(typeArguments,
usedTypeAliasBuilders: usedTypeAliasBuilders,
unboundTypeVariables: unboundTypeVariables);
}
bool _checkCyclicTypedefDependency(
TypeBuilder? typeBuilder,
TypeAliasBuilder rootTypeAliasBuilder,
Set<TypeAliasBuilder> seenTypeAliasBuilders) {
switch (typeBuilder) {
case NamedTypeBuilder(
:TypeDeclarationBuilder? declaration,
typeArguments: List<TypeBuilder>? arguments
):
if (declaration is TypeAliasBuilder) {
bool declarationSeenFirstTime =
!seenTypeAliasBuilders.contains(declaration);
if (declaration == rootTypeAliasBuilder) {
for (TypeAliasBuilder seenTypeAliasBuilder in {
...seenTypeAliasBuilders,
declaration
}) {
seenTypeAliasBuilder.libraryBuilder.addProblem(
templateCyclicTypedef
.withArguments(seenTypeAliasBuilder.name),
seenTypeAliasBuilder.charOffset,
seenTypeAliasBuilder.name.length,
seenTypeAliasBuilder.fileUri);
}
return true;
} else {
if (declarationSeenFirstTime) {
if (_checkCyclicTypedefDependency(
declaration.type,
rootTypeAliasBuilder,
{...seenTypeAliasBuilders, declaration})) {
return true;
}
if (declaration.typeVariables != null) {
for (TypeVariableBuilder typeVariable
in declaration.typeVariables!) {
if (_checkCyclicTypedefDependency(
typeVariable.bound,
rootTypeAliasBuilder,
{...seenTypeAliasBuilders, declaration})) {
return true;
}
}
}
}
}
}
if (arguments != null) {
for (TypeBuilder typeArgument in arguments) {
if (_checkCyclicTypedefDependency(
typeArgument, rootTypeAliasBuilder, seenTypeAliasBuilders)) {
return true;
}
}
} else if (declaration != null && declaration.typeVariablesCount > 0) {
List<TypeVariableBuilder>? typeParameters;
switch (declaration) {
case ClassBuilder():
typeParameters = declaration.typeVariables;
case TypeAliasBuilder():
typeParameters = declaration.typeVariables;
// Coverage-ignore(suite): Not run.
case ExtensionTypeDeclarationBuilder():
typeParameters = declaration.typeParameters;
// Coverage-ignore(suite): Not run.
case BuiltinTypeDeclarationBuilder():
case InvalidTypeDeclarationBuilder():
case OmittedTypeDeclarationBuilder():
case ExtensionBuilder():
case TypeVariableBuilder():
}
if (typeParameters != null) {
for (int i = 0; i < typeParameters.length; i++) {
TypeVariableBuilder typeParameter = typeParameters[i];
if (_checkCyclicTypedefDependency(typeParameter.defaultType!,
rootTypeAliasBuilder, seenTypeAliasBuilders)) {
return true;
}
}
}
}
case FunctionTypeBuilder(
:List<StructuralVariableBuilder>? typeVariables,
:List<ParameterBuilder>? formals,
:TypeBuilder returnType
):
if (_checkCyclicTypedefDependency(
returnType, rootTypeAliasBuilder, seenTypeAliasBuilders)) {
return true;
}
if (formals != null) {
for (ParameterBuilder formal in formals) {
if (_checkCyclicTypedefDependency(
formal.type, rootTypeAliasBuilder, seenTypeAliasBuilders)) {
return true;
}
}
}
if (typeVariables != null) {
for (StructuralVariableBuilder typeVariable in typeVariables) {
TypeBuilder? bound = typeVariable.bound;
if (_checkCyclicTypedefDependency(
bound, rootTypeAliasBuilder, seenTypeAliasBuilders)) {
return true;
}
}
}
case RecordTypeBuilder(
:List<RecordTypeFieldBuilder>? positionalFields,
:List<RecordTypeFieldBuilder>? namedFields
):
if (positionalFields != null) {
for (RecordTypeFieldBuilder field in positionalFields) {
if (_checkCyclicTypedefDependency(
field.type, rootTypeAliasBuilder, seenTypeAliasBuilders)) {
return true;
}
}
}
if (namedFields != null) {
for (RecordTypeFieldBuilder field in namedFields) {
if (_checkCyclicTypedefDependency(
field.type, rootTypeAliasBuilder, seenTypeAliasBuilders)) {
return true;
}
}
}
case OmittedTypeBuilder():
case FixedTypeBuilder():
case InvalidTypeBuilder():
case null:
}
return false;
}
@override
DartType buildThisType() {
_breakCyclicDependency();
if (thisType != null) {
if (identical(thisType, pendingTypeAliasMarker)) {
// Coverage-ignore-block(suite): Not run.
thisType = cyclicTypeAliasMarker;
assert(libraryBuilder.loader.assertProblemReportedElsewhere(
"SourceTypeAliasBuilder.buildThisType",
expectedPhase: CompilationPhaseForProblemReporting.outline));
return const InvalidType();
} else if (identical(thisType, cyclicTypeAliasMarker)) {
return const InvalidType();
}
return thisType!;
}
// It is a compile-time error for an alias (typedef) to refer to itself. We
// detect cycles by detecting recursive calls to this method using an
// instance of InvalidType that isn't identical to `const InvalidType()`.
thisType = pendingTypeAliasMarker;
DartType builtType = type.build(libraryBuilder, TypeUse.typedefAlias);
if (typeVariables != null) {
for (NominalVariableBuilder tv in typeVariables!) {
// Follow bound in order to find all cycles
tv.bound?.build(libraryBuilder, TypeUse.typeParameterBound);
}
}
if (identical(thisType, cyclicTypeAliasMarker)) {
builtType = const InvalidType();
}
return thisType = typedef.type ??= builtType;
}
@override
List<DartType> buildAliasedTypeArguments(LibraryBuilder library,
List<TypeBuilder>? arguments, ClassHierarchyBase? hierarchy) {
if (arguments == null && typeVariables == null) {
return <DartType>[];
}
if (arguments == null && typeVariables != null) {
// TODO(johnniwinther): Use i2b here when needed.
List<DartType> result = new List<DartType>.generate(
typeVariables!.length,
(int i) => typeVariables![i]
.defaultType!
// TODO(johnniwinther): Using [libraryBuilder] here instead of
// [library] preserves the nullability of the original
// declaration. We legacy erase it later, but should we legacy
// erase it now also?
.buildAliased(
libraryBuilder, TypeUse.defaultTypeAsTypeArgument, hierarchy),
growable: true);
return result;
}
if (arguments != null && arguments.length != typeVariablesCount) {
// Coverage-ignore-block(suite): Not run.
assert(libraryBuilder.loader.assertProblemReportedElsewhere(
"SourceTypeAliasBuilder.buildAliasedTypeArguments: "
"the numbers of type parameters and type arguments don't match.",
expectedPhase: CompilationPhaseForProblemReporting.outline));
return unhandled(
templateTypeArgumentMismatch
.withArguments(typeVariablesCount)
.problemMessage,
"buildTypeArguments",
-1,
null);
}
// arguments.length == typeVariables.length
return new List<DartType>.generate(
arguments!.length,
(int i) =>
arguments[i].buildAliased(library, TypeUse.typeArgument, hierarchy),
growable: true);
}
BodyBuilderContext createBodyBuilderContext(
{required bool inOutlineBuildingPhase,
required bool inMetadata,
required bool inConstFields}) {
return new TypedefBodyBuilderContext(this,
inOutlineBuildingPhase: inOutlineBuildingPhase,
inMetadata: inMetadata,
inConstFields: inConstFields);
}
void buildOutlineExpressions(ClassHierarchy classHierarchy,
List<DelayedDefaultValueCloner> delayedDefaultValueCloners) {
MetadataBuilder.buildAnnotations(
typedef,
_introductory.metadata,
createBodyBuilderContext(
inOutlineBuildingPhase: true,
inMetadata: true,
inConstFields: false),
libraryBuilder,
fileUri,
libraryBuilder.scope);
if (typeVariables != null) {
for (int i = 0; i < typeVariables!.length; i++) {
typeVariables![i].buildOutlineExpressions(
libraryBuilder,
createBodyBuilderContext(
inOutlineBuildingPhase: true,
inMetadata: true,
inConstFields: false),
classHierarchy,
computeTypeParameterScope(libraryBuilder.scope));
}
}
_tearOffDependencies?.forEach((Procedure tearOff, Member target) {
delayedDefaultValueCloners.add(new DelayedDefaultValueCloner(
target, tearOff,
libraryBuilder: libraryBuilder));
});
}
LookupScope computeTypeParameterScope(LookupScope parent) {
if (typeVariables == null) return parent;
Map<String, Builder> local = <String, Builder>{};
for (NominalVariableBuilder variable in typeVariables!) {
local[variable.name] = variable;
}
return new TypeParameterScope(parent, local);
}
Map<Procedure, Member>? _tearOffDependencies;
DelayedDefaultValueCloner? buildTypedefTearOffs(
SourceLibraryBuilder libraryBuilder, void Function(Procedure) f) {
TypeDeclarationBuilder? declaration = unaliasDeclaration(null);
DartType? targetType = typedef.type;
DelayedDefaultValueCloner? delayedDefaultValueCloner;
switch (declaration) {
case ClassBuilder():
if (targetType is InterfaceType &&
typedef.typeParameters.isNotEmpty &&
!isProperRenameForTypeDeclaration(
libraryBuilder.loader.typeEnvironment,
typedef,
libraryBuilder.library)) {
tearOffs = {};
_tearOffDependencies = {};
NameIterator<MemberBuilder> iterator =
declaration.fullConstructorNameIterator();
while (iterator.moveNext()) {
String constructorName = iterator.name;
MemberBuilder builder = iterator.current;
Member? target = builder.invokeTarget;
if (target != null) {
if (target is Procedure && target.isRedirectingFactory) {
target = builder.readTarget!;
}
Class targetClass = target.enclosingClass!;
if (target is Constructor && targetClass.isAbstract) {
continue;
}
Name targetName =
new Name(constructorName, declaration.libraryBuilder.library);
Reference? tearOffReference;
if (libraryBuilder.indexedLibrary != null) {
Name tearOffName = new Name(
typedefTearOffName(name, constructorName),
libraryBuilder.indexedLibrary!.library);
tearOffReference = libraryBuilder.indexedLibrary!
.lookupGetterReference(tearOffName);
}
Procedure tearOff = tearOffs![targetName] =
createTypedefTearOffProcedure(
name,
constructorName,
libraryBuilder,
target.fileUri,
target.fileOffset,
tearOffReference);
_tearOffDependencies![tearOff] = target;
delayedDefaultValueCloner = buildTypedefTearOffProcedure(
tearOff: tearOff,
declarationConstructor: target,
// TODO(johnniwinther): Handle augmented constructors.
implementationConstructor: target,
enclosingTypeDeclaration: declaration.cls,
typeParameters: typedef.typeParameters,
typeArguments: targetType.typeArguments,
libraryBuilder: libraryBuilder);
f(tearOff);
}
}
}
case ExtensionTypeDeclarationBuilder():
if (targetType is ExtensionType &&
typedef.typeParameters.isNotEmpty &&
!isProperRenameForTypeDeclaration(
libraryBuilder.loader.typeEnvironment,
typedef,
libraryBuilder.library)) {
tearOffs = {};
_tearOffDependencies = {};
NameIterator<MemberBuilder> iterator =
declaration.fullConstructorNameIterator();
while (iterator.moveNext()) {
String constructorName = iterator.name;
MemberBuilder builder = iterator.current;
Member? target = builder.invokeTarget;
if (target != null) {
if (target is Procedure && target.isRedirectingFactory) {
target = builder.readTarget!;
}
Name targetName =
new Name(constructorName, declaration.libraryBuilder.library);
Reference? tearOffReference;
if (libraryBuilder.indexedLibrary != null) {
Name tearOffName = new Name(
typedefTearOffName(name, constructorName),
libraryBuilder.indexedLibrary!.library);
tearOffReference = libraryBuilder.indexedLibrary!
.lookupGetterReference(tearOffName);
}
Procedure tearOff = tearOffs![targetName] =
createTypedefTearOffProcedure(
name,
constructorName,
libraryBuilder,
target.fileUri,
target.fileOffset,
tearOffReference);
_tearOffDependencies![tearOff] = target;
delayedDefaultValueCloner = buildTypedefTearOffProcedure(
tearOff: tearOff,
declarationConstructor: target,
// TODO(johnniwinther): Handle augmented constructors.
implementationConstructor: target,
enclosingTypeDeclaration:
declaration.extensionTypeDeclaration,
typeParameters: typedef.typeParameters,
typeArguments: targetType.typeArguments,
libraryBuilder: libraryBuilder);
f(tearOff);
}
}
}
case TypeAliasBuilder():
case NominalVariableBuilder():
case StructuralVariableBuilder():
case ExtensionBuilder():
case InvalidTypeDeclarationBuilder():
case BuiltinTypeDeclarationBuilder():
// Coverage-ignore(suite): Not run.
// TODO(johnniwinther): How should we handle this case?
case OmittedTypeDeclarationBuilder():
case null:
}
return delayedDefaultValueCloner;
}
}