Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(377)

Unified Diff: lib/src/checker/resolver.dart

Issue 1028793002: Transitive inference using SCC (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: cl comments Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « lib/runtime/dart/isolate.js ('k') | lib/src/options.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/src/checker/resolver.dart
diff --git a/lib/src/checker/resolver.dart b/lib/src/checker/resolver.dart
index 5df1c3387b9d1106b406216ce7ba380667bdd483..d4bcd799bbdafce9aad95e1f6c67b5091e2ef485 100644
--- a/lib/src/checker/resolver.dart
+++ b/lib/src/checker/resolver.dart
@@ -18,6 +18,8 @@ import 'package:analyzer/src/generated/source.dart' show DartUriResolver;
import 'package:analyzer/src/generated/source.dart' show Source;
import 'package:analyzer/src/generated/source_io.dart';
import 'package:analyzer/src/generated/static_type_analyzer.dart';
+import 'package:analyzer/src/generated/utilities_collection.dart'
+ show DirectedGraph;
import 'package:logging/logging.dart' as logger;
import 'package:dev_compiler/src/options.dart';
@@ -89,7 +91,7 @@ class TypeResolver {
class AnalyzerError extends Message {
factory AnalyzerError.from(analyzer.AnalysisError error) {
- var severity = error.errorCode.errorSeverity;
+ var severity = error.errorCode.type.severity;
var isError = severity == analyzer.ErrorSeverity.ERROR;
var level = isError ? logger.Level.SEVERE : logger.Level.WARNING;
int begin = error.offset;
@@ -125,9 +127,12 @@ class LibraryResolverWithInference extends LibraryResolver {
// Run resolution in two stages, skipping method bodies first, so we can run
// type-inference before we fully analyze methods.
- _resolveReferencesAndTypes(true);
- _runInference();
- _resolveReferencesAndTypes(false);
+ var visitors = _createVisitors();
+ _resolveEverything(visitors);
+ _runInference(visitors);
+
+ visitors.values.forEach((v) => v.skipMethodBodies = false);
+ _resolveEverything(visitors);
}
// Note: this was split from _resolveReferencesAndTypesInLibrary so we do it
@@ -143,19 +148,30 @@ class LibraryResolverWithInference extends LibraryResolver {
// Note: this was split from _resolveReferencesAndTypesInLibrary so we can do
// resolution in pieces.
- void _resolveReferencesAndTypes(bool skipMethods) {
+ Map<Source, RestrictedResolverVisitor> _createVisitors() {
+ var visitors = <Source, RestrictedResolverVisitor>{};
+ for (Library library in resolvedLibraries) {
+ for (Source source in library.compilationUnitSources) {
+ var visitor = new RestrictedResolverVisitor(
+ library, source, typeProvider, _options);
+ visitors[source] = visitor;
+ }
+ }
+ return visitors;
+ }
+
+ /// Runs the resolver on the entire library cycle.
+ void _resolveEverything(Map<Source, RestrictedResolverVisitor> visitors) {
for (Library library in resolvedLibraries) {
for (Source source in library.compilationUnitSources) {
- library.getAST(source).accept(new RestrictedResolverVisitor(
- library, source, typeProvider, _options, skipMethods));
+ library.getAST(source).accept(visitors[source]);
}
}
}
- _runInference() {
- var consts = [];
- var statics = [];
- var classes = [];
+ _runInference(Map<Source, RestrictedResolverVisitor> visitors) {
+ var globalsAndStatics = <VariableDeclaration>[];
+ var classes = <ClassDeclaration>[];
// Extract top-level members that are const, statics, or classes.
for (Library library in resolvedLibraries) {
@@ -163,30 +179,61 @@ class LibraryResolverWithInference extends LibraryResolver {
CompilationUnit ast = library.getAST(source);
for (var declaration in ast.declarations) {
if (declaration is TopLevelVariableDeclaration) {
- if (declaration.variables.isConst) {
- consts.addAll(declaration.variables.variables);
- } else {
- statics.addAll(declaration.variables.variables);
- }
+ globalsAndStatics.addAll(declaration.variables.variables);
} else if (declaration is ClassDeclaration) {
classes.add(declaration);
for (var member in declaration.members) {
- if (member is! FieldDeclaration) continue;
- if (member.fields.isConst) {
- consts.addAll(member.fields.variables);
- } else if (member.isStatic) {
- statics.addAll(member.fields.variables);
+ if (member is FieldDeclaration &&
+ (member.fields.isConst || member.isStatic)) {
+ globalsAndStatics.addAll(member.fields.variables);
}
}
}
}
}
}
+ _inferGlobalsAndStatics(globalsAndStatics, visitors);
+ _inferInstanceFields(classes, visitors);
+ }
- // TODO(sigmund): consider propagating const types after this layer of
- // inference, so their types can be used to initialize other members below.
- _inferVariableFromInitializer(consts);
- _inferVariableFromInitializer(statics);
+ _inferGlobalsAndStatics(List<VariableDeclaration> globalsAndStatics,
+ Map<Source, RestrictedResolverVisitor> visitors) {
+ var elementToDeclaration = {};
+ for (var c in globalsAndStatics) {
+ elementToDeclaration[c.element] = c;
+ }
+ var constGraph = new DirectedGraph<VariableDeclaration>();
+ globalsAndStatics.forEach(constGraph.addNode);
+ for (var c in globalsAndStatics) {
+ for (var e in _VarExtractor.extract(c.initializer)) {
+ // Note: declaration is null for variables that come from other strongly
+ // connected components.
+ var declaration = elementToDeclaration[e];
+ if (declaration != null) constGraph.addEdge(c, declaration);
+ }
+ }
+
+ for (var component in constGraph.computeTopologicalSort()) {
+ if (_options.inferTransitively) {
+ component.forEach((v) => _reanalyzeVar(visitors, v));
+ }
+ _inferVariableFromInitializer(component);
+ }
+ }
+
+ _inferInstanceFields(List<ClassDeclaration> classes,
+ Map<Source, RestrictedResolverVisitor> visitors) {
+ // First propagate what was inferred from globals to all instance fields.
+ if (_options.inferTransitively) {
+ // TODO(sigmund): also do a fine-grain propagation between fields. We want
+ // infer-by-override to take precedence, so we would have to include
+ // classes in the dependency graph and ensure that fields depend on their
+ // class, and classes depend on superclasses.
+ classes
+ .expand((c) => c.members.where(_isInstanceField))
+ .expand((f) => f.fields.variables)
+ .forEach((v) => _reanalyzeVar(visitors, v));
+ }
// Track types in this strongly connected component, ensure we visit
// supertypes before subtypes.
@@ -203,9 +250,6 @@ class LibraryResolverWithInference extends LibraryResolver {
}
seen.add(type);
- _isInstanceField(f) =>
- f is FieldDeclaration && !f.isStatic && !f.fields.isConst;
-
if (_options.inferFromOverrides) {
// Infer field types from overrides first, otherwise from initializers.
var pending = new Set<VariableDeclaration>();
@@ -227,6 +271,16 @@ class LibraryResolverWithInference extends LibraryResolver {
classes.forEach(visit);
}
+ void _reanalyzeVar(Map<Source, RestrictedResolverVisitor> visitors,
+ VariableDeclaration variable) {
+ if (variable.initializer == null) return;
+ var visitor = visitors[(variable.root as CompilationUnit).element.source];
+ visitor.reanalyzeInitializer(variable);
+ }
+
+ static bool _isInstanceField(f) =>
+ f is FieldDeclaration && !f.isStatic && !f.fields.isConst;
+
/// Attempts to infer the type on [field] from overridden fields or getters if
/// a type was not specified. If no type could be inferred, but it contains an
/// initializer, we add it to [pending] so we can try to infer it using the
@@ -309,6 +363,7 @@ class LibraryResolverWithInference extends LibraryResolver {
}
bool _canInferFrom(Expression expression) {
+ if (_options.inferTransitively) return true;
if (expression is Literal) return true;
if (expression is InstanceCreationExpression) return true;
if (expression is FunctionExpression) return true;
@@ -317,11 +372,11 @@ class LibraryResolverWithInference extends LibraryResolver {
return _canInferFrom(expression.target);
}
if (expression is SimpleIdentifier || expression is PropertyAccess) {
- return _options.inferTransitively;
+ return false;
}
if (expression is PrefixedIdentifier) {
if (expression.staticElement is PropertyAccessorElement) {
- return _options.inferTransitively;
+ return false;
}
return _canInferFrom(expression.identifier);
}
@@ -345,6 +400,22 @@ class LibraryResolverWithInference extends LibraryResolver {
}
}
+/// Extracts the [VariableElement]s used in an initializer expression.
+class _VarExtractor extends RecursiveAstVisitor {
+ final elements = <VariableElement>[];
+ visitSimpleIdentifier(SimpleIdentifier node) {
+ var e = node.staticElement;
+ if (e is PropertyAccessorElement) elements.add(e.variable);
+ }
+
+ static List<VariableElement> extract(Expression initializer) {
+ if (initializer == null) return const [];
+ var extractor = new _VarExtractor();
+ initializer.accept(extractor);
+ return extractor.elements;
+ }
+}
+
/// Overrides the default [ResolverVisitor] to support type inference in
/// [LibraryResolverWithInference] above.
///
@@ -356,10 +427,26 @@ class RestrictedResolverVisitor extends ResolverVisitor {
final TypeProvider _typeProvider;
/// Whether to skip resolution within method bodies.
- final bool skipMethodBodies;
+ bool skipMethodBodies = true;
+
+ /// State of the resolver at the point a field or variable was declared.
+ final _stateAtDeclaration = <AstNode, _ResolverState>{};
+
+ /// Internal tracking of whether a node was skipped while visiting, for
+ /// example, if it contained a function expression with a function body.
+ bool _nodeWasSkipped = false;
+
+ /// Internal state, whether we are revisiting an initializer, so we minimize
+ /// the work being done elsewhere.
+ bool _revisiting = false;
+
+ /// Initializers that have been visited, reanalyzed, and for which no node was
+ /// internally skipped. These initializers are fully resolved and don't need
+ /// to be re-resolved on a sunsequent pass.
+ final _visitedInitializers = new Set<VariableDeclaration>();
RestrictedResolverVisitor(Library library, Source source,
- TypeProvider typeProvider, ResolverOptions options, this.skipMethodBodies)
+ TypeProvider typeProvider, ResolverOptions options)
: _typeProvider = typeProvider,
super.con1(library, source, typeProvider,
typeAnalyzerFactory: RestrictedStaticTypeAnalyzer.constructor);
@@ -380,18 +467,62 @@ class RestrictedResolverVisitor extends ResolverVisitor {
return super.visitCatchClause(node);
}
+ reanalyzeInitializer(VariableDeclaration variable) {
+ try {
+ _revisiting = true;
+ _nodeWasSkipped = false;
+ var node = variable.parent.parent;
+ var oldState;
+ var state = _stateAtDeclaration[node];
+ if (state != null) {
+ oldState = new _ResolverState(this);
+ state.restore(this);
+ if (node is FieldDeclaration) {
+ var cls = node.parent;
+ enclosingClass = cls.element;
+ }
+ }
+ visitNode(variable.initializer);
+ if (!_nodeWasSkipped) _visitedInitializers.add(variable);
+ if (oldState != null) oldState.restore(this);
+ } finally {
+ _revisiting = false;
+ }
+ }
+
+ @override
+ Object visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
+ _stateAtDeclaration[node] = new _ResolverState(this);
+ return super.visitTopLevelVariableDeclaration(node);
+ }
+
+ @override
+ Object visitFieldDeclaration(FieldDeclaration node) {
+ _stateAtDeclaration[node] = new _ResolverState(this);
+ return super.visitFieldDeclaration(node);
+ }
+
+ Object visitVariableDeclaration(VariableDeclaration node) {
+ var state = new _ResolverState(this);
+ try {
+ if (_revisiting) {
+ _stateAtDeclaration[node].restore(this);
+ } else {
+ _stateAtDeclaration[node] = state;
+ }
+ return super.visitVariableDeclaration(node);
+ } finally {
+ state.restore(this);
+ }
+ }
+
@override
Object visitNode(AstNode node) {
- if (skipMethodBodies &&
- (node is FunctionBody ||
- node is FunctionExpression ||
- node is FunctionExpressionInvocation ||
- node is SuperConstructorInvocation ||
- node is RedirectingConstructorInvocation ||
- node is Annotation ||
- node is Comment)) {
+ if (skipMethodBodies && node is FunctionBody) {
+ _nodeWasSkipped = true;
return null;
}
+ if (_visitedInitializers.contains(node)) return null;
assert(node is! Statement || !skipMethodBodies);
return super.visitNode(node);
}
@@ -430,6 +561,25 @@ class RestrictedResolverVisitor extends ResolverVisitor {
}
}
+/// Internal state of the resolver, stored so we can reanalyze portions of the
+/// AST quickly, without recomputing everything from the top.
+class _ResolverState {
+ final TypePromotionManager_TypePromoteScope promotionScope;
+ final TypeOverrideManager_TypeOverrideScope overrideScope;
+ final Scope nameScope;
+
+ _ResolverState(ResolverVisitor visitor)
+ : promotionScope = visitor.promoteManager.currentScope,
+ overrideScope = visitor.overrideManager.currentScope,
+ nameScope = visitor.nameScope;
+
+ void restore(ResolverVisitor visitor) {
+ visitor.promoteManager.currentScope = promotionScope;
+ visitor.overrideManager.currentScope = overrideScope;
+ visitor.nameScope_J2DAccessor = nameScope;
+ }
+}
+
/// Overrides the default [StaticTypeAnalyzer] to adjust rules that are stricter
/// in the restricted type system and to infer types for untyped local
/// variables.
« no previous file with comments | « lib/runtime/dart/isolate.js ('k') | lib/src/options.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698