blob: 3a22dbbf89d7ae3011531e70dc6c5c170e1504b9 [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.kernel_field_builder;
import 'package:kernel/ast.dart'
show Class, DartType, Expression, Field, InvalidType, Name, NullLiteral;
import '../constant_context.dart' show ConstantContext;
import '../fasta_codes.dart'
show
messageInternalProblemAlreadyInitialized,
templateCantInferTypeDueToCircularity;
import '../problems.dart' show internalProblem;
import '../scanner.dart' show Token;
import '../scope.dart' show Scope;
import '../source/source_loader.dart' show SourceLoader;
import '../type_inference/type_inference_engine.dart'
show IncludesTypeParametersCovariantly;
import '../type_inference/type_inferrer.dart' show TypeInferrerImpl;
import '../type_inference/type_schema.dart' show UnknownType;
import 'kernel_body_builder.dart' show KernelBodyBuilder;
import 'kernel_builder.dart'
show
ClassBuilder,
Declaration,
FieldBuilder,
ImplicitFieldType,
KernelLibraryBuilder,
KernelMetadataBuilder,
KernelTypeBuilder,
LibraryBuilder,
MetadataBuilder;
class KernelFieldBuilder extends FieldBuilder<Expression> {
final Field field;
final List<MetadataBuilder> metadata;
final KernelTypeBuilder type;
Token constInitializerToken;
bool hadTypesInferred = false;
KernelFieldBuilder(this.metadata, this.type, String name, int modifiers,
Declaration compilationUnit, int charOffset, int charEndOffset)
: field = new Field(null, fileUri: compilationUnit?.fileUri)
..fileOffset = charOffset
..fileEndOffset = charEndOffset,
super(name, modifiers, compilationUnit, charOffset);
void set initializer(Expression value) {
if (!hasInitializer && value is! NullLiteral && !isConst && !isFinal) {
internalProblem(
messageInternalProblemAlreadyInitialized, charOffset, fileUri);
}
field.initializer = value..parent = field;
}
bool get isEligibleForInference {
return !library.legacyMode &&
type == null &&
(hasInitializer || isInstanceMember);
}
Field build(KernelLibraryBuilder library) {
field.name ??= new Name(name, library.target);
if (type != null) {
field.type = type.build(library);
if (!isFinal && !isConst) {
IncludesTypeParametersCovariantly needsCheckVisitor;
if (parent is ClassBuilder) {
Class enclosingClass = parent.target;
if (enclosingClass.typeParameters.isNotEmpty) {
needsCheckVisitor = new IncludesTypeParametersCovariantly(
enclosingClass.typeParameters);
}
}
if (needsCheckVisitor != null) {
if (field.type.accept(needsCheckVisitor)) {
field.isGenericCovariantImpl = true;
}
}
}
}
bool isInstanceMember = !isStatic && !isTopLevel;
field
..isCovariant = isCovariant
..isFinal = isFinal
..isConst = isConst
..hasImplicitGetter = isInstanceMember
..hasImplicitSetter = isInstanceMember && !isConst && !isFinal
..isStatic = !isInstanceMember;
return field;
}
@override
void buildOutlineExpressions(LibraryBuilder library) {
ClassBuilder classBuilder = isClassMember ? parent : null;
KernelMetadataBuilder.buildAnnotations(
field, metadata, library, classBuilder, this);
if (constInitializerToken != null) {
Scope scope = classBuilder?.scope ?? library.scope;
KernelBodyBuilder bodyBuilder =
new KernelBodyBuilder.forOutlineExpression(
library, classBuilder, this, scope, fileUri);
bodyBuilder.constantContext =
isConst ? ConstantContext.inferred : ConstantContext.none;
initializer = bodyBuilder.parseFieldInitializer(constInitializerToken)
..parent = field;
constInitializerToken = null;
bodyBuilder.typeInferrer
?.inferFieldInitializer(bodyBuilder, field.type, field.initializer);
if (library.loader is SourceLoader) {
SourceLoader loader = library.loader;
loader.transformPostInference(field, bodyBuilder.transformSetLiterals,
bodyBuilder.transformCollections);
}
bodyBuilder.resolveRedirectingFactoryTargets();
}
}
Field get target => field;
@override
void inferType() {
KernelLibraryBuilder library = this.library;
if (field.type is! ImplicitFieldType) {
// We have already inferred a type.
return;
}
ImplicitFieldType type = field.type;
if (type.member != this) {
// The implicit type was inherited.
KernelFieldBuilder other = type.member;
other.inferCopiedType(field);
return;
}
if (type.isStarted) {
library.addProblem(
templateCantInferTypeDueToCircularity.withArguments(name),
charOffset,
name.length,
fileUri);
field.type = const InvalidType();
return;
}
type.isStarted = true;
TypeInferrerImpl typeInferrer = library.loader.typeInferenceEngine
.createTopLevelTypeInferrer(
fileUri, field.enclosingClass?.thisType, null);
KernelBodyBuilder bodyBuilder =
new KernelBodyBuilder.forField(this, typeInferrer);
bodyBuilder.constantContext =
isConst ? ConstantContext.inferred : ConstantContext.none;
initializer = bodyBuilder.parseFieldInitializer(type.initializerToken);
type.initializerToken = null;
DartType inferredType = typeInferrer.inferDeclarationType(typeInferrer
.inferExpression(field.initializer, const UnknownType(), true,
isVoidAllowed: true));
if (field.type is ImplicitFieldType) {
// `field.type` may have changed if a circularity was detected when
// [inferredType] was computed.
field.type = inferredType;
IncludesTypeParametersCovariantly needsCheckVisitor;
if (parent is ClassBuilder) {
Class enclosingClass = parent.target;
if (enclosingClass.typeParameters.isNotEmpty) {
needsCheckVisitor = new IncludesTypeParametersCovariantly(
enclosingClass.typeParameters);
}
}
if (needsCheckVisitor != null) {
if (field.type.accept(needsCheckVisitor)) {
field.isGenericCovariantImpl = true;
}
}
}
// The following is a hack. The outline should contain the compiled
// initializers, however, as top-level inference is subtly different from
// we need to compile the field initializer again when everything else is
// compiled.
field.initializer = null;
}
void inferCopiedType(Field other) {
inferType();
other.type = field.type;
other.initializer = null;
}
@override
DartType get builtType => field.type;
}