blob: 9d91421a3b90e387d5215e05ef4f73df12719bc0 [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.
library fasta.type_variable_builder;
import 'package:kernel/ast.dart'
show DartType, Nullability, TypeParameter, TypeParameterType;
import 'package:kernel/core_types.dart';
import '../fasta_codes.dart'
show
templateInternalProblemUnfinishedTypeVariable,
templateTypeArgumentsOnTypeVariable;
import '../source/source_library_builder.dart';
import '../util/helpers.dart';
import 'builder.dart';
import 'class_builder.dart';
import 'declaration_builder.dart';
import 'library_builder.dart';
import 'member_builder.dart';
import 'metadata_builder.dart';
import 'named_type_builder.dart';
import 'nullability_builder.dart';
import 'type_builder.dart';
import 'type_declaration_builder.dart';
class TypeVariableBuilder extends TypeDeclarationBuilderImpl {
/// Sentinel value used to indicate that the variable has no name. This is
/// used for error recovery.
static const String noNameSentinel = 'no name sentinel';
TypeBuilder? bound;
TypeBuilder? defaultType;
final TypeParameter actualParameter;
TypeVariableBuilder? actualOrigin;
final bool isExtensionTypeParameter;
@override
final Uri? fileUri;
TypeVariableBuilder(
String name, Builder? compilationUnit, int charOffset, this.fileUri,
{this.bound,
this.isExtensionTypeParameter: false,
int? variableVariance,
List<MetadataBuilder>? metadata})
: actualParameter =
new TypeParameter(name == noNameSentinel ? null : name, null)
..fileOffset = charOffset
..variance = variableVariance,
super(metadata, 0, name, compilationUnit, charOffset);
TypeVariableBuilder.fromKernel(
TypeParameter parameter, LibraryBuilder compilationUnit)
: actualParameter = parameter,
// TODO(johnniwinther): Do we need to support synthesized type
// parameters from kernel?
this.isExtensionTypeParameter = false,
fileUri = compilationUnit.fileUri,
super(null, 0, parameter.name ?? '', compilationUnit,
parameter.fileOffset);
bool get isTypeVariable => true;
String get debugName => "TypeVariableBuilder";
StringBuffer printOn(StringBuffer buffer) {
buffer.write(name);
if (bound != null) {
buffer.write(" extends ");
bound!.printOn(buffer);
}
return buffer;
}
String toString() => "${printOn(new StringBuffer())}";
TypeVariableBuilder get origin => actualOrigin ?? this;
/// The [TypeParameter] built by this builder.
TypeParameter get parameter => origin.actualParameter;
int get variance => parameter.variance;
void set variance(int value) {
parameter.variance = value;
}
DartType buildType(LibraryBuilder library,
NullabilityBuilder nullabilityBuilder, List<TypeBuilder>? arguments,
{bool? nonInstanceContext}) {
if (arguments != null) {
int charOffset = -1; // TODO(ahe): Provide these.
Uri? fileUri = null; // TODO(ahe): Provide these.
library.addProblem(
templateTypeArgumentsOnTypeVariable.withArguments(name),
charOffset,
name.length,
fileUri);
}
// If the bound is not set yet, the actual value is not important yet as it
// will be set later.
bool needsPostUpdate = false;
Nullability nullability;
if (nullabilityBuilder.isOmitted) {
if (!identical(parameter.bound, TypeParameter.unsetBoundSentinel)) {
nullability = library.isNonNullableByDefault
? TypeParameterType.computeNullabilityFromBound(parameter)
: Nullability.legacy;
} else {
nullability = Nullability.legacy;
needsPostUpdate = true;
}
} else {
nullability = nullabilityBuilder.build(library);
}
TypeParameterType type =
buildTypesWithBuiltArguments(library, nullability, null);
if (needsPostUpdate) {
if (library is SourceLibraryBuilder) {
library.registerPendingNullability(fileUri!, charOffset, type);
} else {
library.addProblem(
templateInternalProblemUnfinishedTypeVariable.withArguments(
name, library.importUri),
charOffset,
name.length,
fileUri);
}
}
return type;
}
TypeParameterType buildTypesWithBuiltArguments(LibraryBuilder library,
Nullability nullability, List<DartType>? arguments) {
if (arguments != null) {
int charOffset = -1; // TODO(ahe): Provide these.
Uri? fileUri = null; // TODO(ahe): Provide these.
library.addProblem(
templateTypeArgumentsOnTypeVariable.withArguments(name),
charOffset,
name.length,
fileUri);
}
return new TypeParameterType(parameter, nullability);
}
TypeBuilder asTypeBuilder() {
return new NamedTypeBuilder(
name, const NullabilityBuilder.omitted(), null, fileUri, charOffset)
..bind(this);
}
void finish(
LibraryBuilder library, ClassBuilder object, TypeBuilder dynamicType) {
if (isPatch) return;
DartType objectType =
object.buildType(library, library.nullableBuilder, null);
if (identical(parameter.bound, TypeParameter.unsetBoundSentinel)) {
parameter.bound = bound?.build(library) ?? objectType;
}
// If defaultType is not set, initialize it to dynamic, unless the bound is
// explicitly specified as Object, in which case defaultType should also be
// Object. This makes sure instantiation of generic function types with an
// explicit Object bound results in Object as the instantiated type.
if (identical(
parameter.defaultType, TypeParameter.unsetDefaultTypeSentinel)) {
parameter.defaultType = defaultType?.build(library) ??
(bound != null && parameter.bound == objectType
? objectType
: dynamicType.build(library));
}
}
void applyPatch(covariant TypeVariableBuilder patch) {
patch.actualOrigin = this;
}
TypeVariableBuilder clone(
List<TypeBuilder> newTypes,
SourceLibraryBuilder contextLibrary,
TypeParameterScopeBuilder contextDeclaration) {
// TODO(dmitryas): Figure out if using [charOffset] here is a good idea.
// An alternative is to use the offset of the node the cloned type variable
// is declared on.
return new TypeVariableBuilder(name, parent!, charOffset, fileUri,
bound: bound?.clone(newTypes, contextLibrary, contextDeclaration),
variableVariance: variance);
}
void buildOutlineExpressions(
SourceLibraryBuilder libraryBuilder,
DeclarationBuilder? classOrExtensionBuilder,
MemberBuilder? memberBuilder,
CoreTypes coreTypes,
List<DelayedActionPerformer> delayedActionPerformers) {
MetadataBuilder.buildAnnotations(parameter, metadata, libraryBuilder,
classOrExtensionBuilder, memberBuilder, fileUri!);
}
@override
bool operator ==(Object other) {
return other is TypeVariableBuilder && parameter == other.parameter;
}
@override
int get hashCode => parameter.hashCode;
static List<TypeParameter>? typeParametersFromBuilders(
List<TypeVariableBuilder>? builders) {
if (builders == null) return null;
return new List<TypeParameter>.generate(
builders.length, (int i) => builders[i].parameter,
growable: true);
}
}