blob: 23ed13955d30d979ffbb537fee3b30cd4ab0354e [file] [log] [blame]
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE.md file.
import 'package:front_end/src/base/instrumentation.dart';
import 'package:front_end/src/dependency_walker.dart' as dependencyWalker;
import 'package:front_end/src/fasta/kernel/kernel_shadow_ast.dart';
import 'package:front_end/src/fasta/type_inference/type_inference_listener.dart';
import 'package:front_end/src/fasta/type_inference/type_inferrer.dart';
import 'package:front_end/src/fasta/type_inference/type_schema_environment.dart';
import 'package:kernel/ast.dart' show DartType, DynamicType;
import 'package:kernel/class_hierarchy.dart';
import 'package:kernel/core_types.dart';
/// Data structure for tracking dependencies between fields that require type
/// inference.
///
/// TODO(paulberry): see if it's possible to make this class more lightweight
/// by changing the API so that the walker is passed to computeDependencies().
/// (This should allow us to drop the _typeInferenceEngine field).
class FieldNode extends dependencyWalker.Node<FieldNode> {
final TypeInferenceEngineImpl _typeInferenceEngine;
final KernelField _field;
final dependencies = <FieldNode>[];
FieldNode(this._typeInferenceEngine, this._field);
@override
bool get isEvaluated => _typeInferenceEngine.isFieldInferred(_field);
@override
List<FieldNode> computeDependencies() {
return dependencies;
}
}
/// Keeps track of the global state for the type inference that occurs outside
/// of method bodies and initalizers.
///
/// This class describes the interface for use by clients of type inference
/// (e.g. DietListener). Derived classes should derive from
/// [TypeInferenceEngineImpl].
abstract class TypeInferenceEngine {
ClassHierarchy get classHierarchy;
CoreTypes get coreTypes;
/// Creates a type inferrer for use inside of a method body declared in a file
/// with the given [uri].
TypeInferrer createLocalTypeInferrer(Uri uri, TypeInferenceListener listener);
/// Creates a [TypeInferrer] object which is ready to perform type inference
/// on the given [field].
TypeInferrer createTopLevelTypeInferrer(
KernelField field, TypeInferenceListener listener);
/// Performs the second phase of top level initializer inference, which is to
/// visit all fields and top level variables that were passed to [recordField]
/// in topologically-sorted order and assign their types.
void finishTopLevel();
/// Gets the list of top level type inference dependencies of the given
/// [field].
List<FieldNode> getFieldDependencies(KernelField field);
/// Gets ready to do top level type inference for the program having the given
/// [hierarchy], using the given [coreTypes].
void prepareTopLevel(CoreTypes coreTypes, ClassHierarchy hierarchy);
/// Records that the given [field] will need top level type inference.
void recordField(KernelField field);
}
/// Derived class containing generic implementations of
/// [TypeInferenceEngineImpl].
///
/// This class contains as much of the implementation of type inference as
/// possible without knowing the identity of the type parameter. It defers to
/// abstract methods for everything else.
abstract class TypeInferenceEngineImpl extends TypeInferenceEngine {
final Instrumentation instrumentation;
final bool strongMode;
final fieldNodes = <FieldNode>[];
@override
CoreTypes coreTypes;
@override
ClassHierarchy classHierarchy;
TypeSchemaEnvironment typeSchemaEnvironment;
TypeInferenceEngineImpl(this.instrumentation, this.strongMode);
/// Clears the initializer of [field].
void clearFieldInitializer(KernelField field);
/// Creates a [FieldNode] to track dependencies of the given [field].
FieldNode createFieldNode(KernelField field);
/// Queries whether the given [field] has an initializer.
bool fieldHasInitializer(KernelField field);
@override
void finishTopLevel() {
for (var fieldNode in fieldNodes) {
if (fieldNode.isEvaluated) continue;
new _FieldWalker().walk(fieldNode);
}
}
/// Gets the declared type of the given [field], or `null` if the type is
/// implicit.
DartType getFieldDeclaredType(KernelField field);
/// Gets the character offset of the declaration of [field] within its
/// compilation unit.
int getFieldOffset(KernelField field);
/// Retrieve the [TypeInferrer] for the given [field], which was created by
/// a previous call to [createTopLevelTypeInferrer].
TypeInferrerImpl getFieldTypeInferrer(KernelField field);
/// Gets the URI of the compilation unit the [field] is declared in.
/// TODO(paulberry): can we remove this?
String getFieldUri(KernelField field);
/// Performs type inference on the given [field].
void inferField(KernelField field, bool updateType) {
if (fieldHasInitializer(field)) {
var typeInferrer = getFieldTypeInferrer(field);
var type = getFieldDeclaredType(field);
var inferredType = typeInferrer.inferDeclarationType(
typeInferrer.inferFieldInitializer(field, type, type == null));
if (type == null && strongMode && updateType) {
instrumentation?.record(
Uri.parse(typeInferrer.uri),
getFieldOffset(field),
'topType',
new InstrumentationValueForType(inferredType));
setFieldInferredType(field, inferredType);
}
// TODO(paulberry): if type != null, then check that the type of the
// initializer is assignable to it.
// TODO(paulberry): the following is a hack so that outlines don't contain
// initializers. But it means that we rebuild the initializers when doing
// a full compile. There should be a better way.
clearFieldInitializer(field);
}
}
/// Makes a note that the given [field] is part of a circularity, so its type
/// can't be inferred.
void inferFieldCircular(KernelField field) {
// TODO(paulberry): report the appropriate error.
if (getFieldDeclaredType(field) == null) {
var uri = getFieldTypeInferrer(field).uri;
var inferredType = const DynamicType();
instrumentation?.record(Uri.parse(uri), getFieldOffset(field), 'topType',
new InstrumentationValueForType(inferredType));
setFieldInferredType(field, inferredType);
}
}
/// Determines if top level type inference has been completed for [field].
bool isFieldInferred(KernelField field);
@override
void prepareTopLevel(CoreTypes coreTypes, ClassHierarchy hierarchy) {
this.coreTypes = coreTypes;
this.classHierarchy = hierarchy;
this.typeSchemaEnvironment =
new TypeSchemaEnvironment(coreTypes, hierarchy);
}
@override
void recordField(KernelField field) {
fieldNodes.add(createFieldNode(field));
}
/// Stores [inferredType] as the inferred type of [field].
void setFieldInferredType(KernelField field, DartType inferredType);
}
/// Subtype of [dependencyWalker.DependencyWalker] which is specialized to
/// perform top level type inference.
class _FieldWalker extends dependencyWalker.DependencyWalker<FieldNode> {
_FieldWalker();
@override
void evaluate(FieldNode f) {
f._typeInferenceEngine.inferField(f._field, true);
}
@override
void evaluateScc(List<FieldNode> scc) {
// Mark every field as part of a circularity.
for (var f in scc) {
f._typeInferenceEngine.inferFieldCircular(f._field);
}
// Perform type inference on the initializers of every field, but don't
// update the inferred types.
for (var f in scc) {
f._typeInferenceEngine.inferField(f._field, false);
}
}
}