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

Unified Diff: lib/src/codegen/nullable_type_inference.dart

Issue 1879373004: Implement modular compilation (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 4 years, 8 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/src/codegen/module_builder.dart ('k') | lib/src/codegen/reify_coercions.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/src/codegen/nullable_type_inference.dart
diff --git a/lib/src/codegen/nullable_type_inference.dart b/lib/src/codegen/nullable_type_inference.dart
deleted file mode 100644
index 562d63d7885ae2b07ca7f55faee12b768adf0691..0000000000000000000000000000000000000000
--- a/lib/src/codegen/nullable_type_inference.dart
+++ /dev/null
@@ -1,323 +0,0 @@
-// Copyright (c) 2015, 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 'dart:collection';
-import 'package:analyzer/dart/ast/ast.dart';
-import 'package:analyzer/dart/ast/token.dart' show TokenType;
-import 'package:analyzer/dart/ast/visitor.dart' show RecursiveAstVisitor;
-import 'package:analyzer/dart/element/element.dart';
-import 'package:analyzer/dart/element/type.dart';
-import '../utils.dart' show getStaticType, isInlineJS;
-
-/// An inference engine for nullable types.
-///
-/// This can answer questions about whether expressions are nullable
-/// (see [isNullable]). Given a set of compilation units in a library, it will
-/// determine if locals can be null using flow-insensitive analysis.
-///
-/// The analysis for null expressions is conservative and incomplete, but it can
-/// optimize some patterns.
-// TODO(vsm): Revisit whether we really need this when we get
-// better non-nullability in the type system.
-abstract class NullableTypeInference {
- bool isPrimitiveType(DartType type);
- bool isObjectProperty(String name);
-
- /// Known non-null local variables.
- HashSet<LocalVariableElement> _notNullLocals;
-
- void inferNullableTypesInLibrary(Iterable<CompilationUnit> units) {
- var visitor = new _NullableLocalInference(this);
- for (var unit in units) unit.accept(visitor);
- _notNullLocals = visitor.computeNotNullLocals();
- }
-
- /// Adds a new variable, typically a compiler generated temporary, and record
- /// whether its type is nullable.
- void addTemporaryVariable(LocalVariableElement local, {bool nullable: true}) {
- if (!nullable) _notNullLocals.add(local);
- }
-
- /// Returns true if [expr] can be null.
- bool isNullable(Expression expr) => _isNullable(expr);
-
- /// Returns true if [expr] can be null, optionally using [localIsNullable]
- /// for locals.
- ///
- /// If [localIsNullable] is not supplied, this will use the known list of
- /// [_notNullLocals].
- bool _isNullable(Expression expr,
- [bool localIsNullable(LocalVariableElement e)]) {
- // TODO(jmesserly): we do recursive calls in a few places. This could
- // leads to O(depth) cost for calling this function. We could store the
- // resulting value if that becomes an issue, so we maintain the invariant
- // that each node is visited once.
- Element element = null;
- if (expr is PropertyAccess) {
- element = expr.propertyName.staticElement;
- } else if (expr is Identifier) {
- element = expr.staticElement;
- }
- if (element != null) {
- // Type literals are not null.
- if (element is ClassElement || element is FunctionTypeAliasElement) {
- return false;
- }
-
- if (element is LocalVariableElement) {
- if (localIsNullable != null) {
- return localIsNullable(element);
- }
- return !_notNullLocals.contains(element);
- }
-
- if (element is FunctionElement || element is MethodElement) {
- // A function or method. This can't be null.
- return false;
- }
-
- // Other types of identifiers are nullable (parameters, fields).
- return true;
- }
-
- if (expr is Literal) return expr is NullLiteral;
- if (expr is IsExpression) return false;
- if (expr is FunctionExpression) return false;
- if (expr is ThisExpression) return false;
- if (expr is SuperExpression) return false;
- if (expr is CascadeExpression) {
- // Cascades normally can't return `null`, because if the target is null,
- // they will throw noSuchMethod.
- // The only properties/methods on `null` are those on Object itself.
- for (var section in expr.cascadeSections) {
- Element e = null;
- if (section is PropertyAccess) {
- e = section.propertyName.staticElement;
- } else if (section is MethodInvocation) {
- e = section.methodName.staticElement;
- } else if (section is IndexExpression) {
- // Object does not have operator []=.
- return false;
- }
- // We encountered a non-Object method/property.
- if (e != null && !isObjectProperty(e.name)) {
- return false;
- }
- }
- return _isNullable(expr.target, localIsNullable);
- }
- if (expr is ConditionalExpression) {
- return _isNullable(expr.thenExpression, localIsNullable) ||
- _isNullable(expr.elseExpression, localIsNullable);
- }
- if (expr is ParenthesizedExpression) {
- return _isNullable(expr.expression, localIsNullable);
- }
- if (expr is InstanceCreationExpression) {
- var e = expr.staticElement;
- if (e == null) return true;
-
- // Follow redirects.
- while (e.redirectedConstructor != null) {
- e = e.redirectedConstructor;
- }
-
- // Generative constructors are not nullable.
- if (!e.isFactory) return false;
-
- // Factory constructors are nullable. However it is a bad pattern and
- // our own SDK will never do this.
- // TODO(jmesserly): we could enforce this for user-defined constructors.
- if (e.library.source.isInSystemLibrary) return false;
-
- return true;
- }
-
- DartType type = null;
- if (expr is BinaryExpression) {
- switch (expr.operator.type) {
- case TokenType.EQ_EQ:
- case TokenType.BANG_EQ:
- case TokenType.AMPERSAND_AMPERSAND:
- case TokenType.BAR_BAR:
- return false;
- case TokenType.QUESTION_QUESTION:
- return _isNullable(expr.leftOperand, localIsNullable) &&
- _isNullable(expr.rightOperand, localIsNullable);
- }
- type = getStaticType(expr.leftOperand);
- } else if (expr is PrefixExpression) {
- if (expr.operator.type == TokenType.BANG) return false;
- type = getStaticType(expr.operand);
- } else if (expr is PostfixExpression) {
- type = getStaticType(expr.operand);
- }
- if (type != null && isPrimitiveType(type)) {
- return false;
- }
- if (expr is MethodInvocation) {
- // TODO(vsm): This logic overlaps with the resolver.
- // Where is the best place to put this?
- var e = expr.methodName.staticElement;
- if (isInlineJS(e)) {
- // Fix types for JS builtin calls.
- //
- // This code was taken from analyzer. It's not super sophisticated:
- // only looks for the type name in dart:core, so we just copy it here.
- //
- // TODO(jmesserly): we'll likely need something that can handle a wider
- // variety of types, especially when we get to JS interop.
- var args = expr.argumentList.arguments;
- var first = args.isNotEmpty ? args.first : null;
- if (first is SimpleStringLiteral) {
- var types = first.stringValue;
- if (!types.split('|').contains('Null')) {
- return false;
- }
- }
- }
-
- if (e != null &&
- e.name == 'identical' &&
- e.library.source.uri.toString() == 'dart:core') {
- return false;
- }
- }
-
- // TODO(ochafik,jmesserly): handle other cases such as: refs to top-level
- // finals that have been assigned non-nullable values.
-
- // Failed to recognize a non-nullable case: assume it can be null.
- return true;
- }
-}
-
-/// A visitor that determines which local variables are non-nullable.
-///
-/// This will consider all assignments to local variables using
-/// flow-insensitive inference. That information is used to determine which
-/// variables are nullable in the given scope.
-// TODO(ochafik): Introduce flow analysis (a variable may be nullable in
-// some places and not in others).
-class _NullableLocalInference extends RecursiveAstVisitor {
- final NullableTypeInference _nullInference;
-
- /// Known local variables.
- final _locals = new HashSet<LocalVariableElement>.identity();
-
- /// Variables that are known to be nullable.
- final _nullableLocals = new HashSet<LocalVariableElement>.identity();
-
- /// Given a variable, tracks all other variables that it is assigned to.
- final _assignments =
- new HashMap<LocalVariableElement, Set<LocalVariableElement>>.identity();
-
- _NullableLocalInference(this._nullInference);
-
- /// After visiting nodes, this can be called to compute the set of not-null
- /// locals.
- ///
- /// This method must only be called once. After it is called, the visitor
- /// should be discarded.
- HashSet<LocalVariableElement> computeNotNullLocals() {
- // Given a set of variables that are nullable, remove them from our list of
- // local variables. The end result of this process is a list of variables
- // known to be not null.
- visitNullableLocal(LocalVariableElement e) {
- _locals.remove(e);
-
- // Visit all other locals that this one is assigned to, and record that
- // they are nullable too.
- _assignments.remove(e)?.forEach(visitNullableLocal);
- }
- _nullableLocals.forEach(visitNullableLocal);
-
- // Any remaining locals are non-null.
- return _locals;
- }
-
- @override
- visitVariableDeclaration(VariableDeclaration node) {
- var element = node.element;
- var initializer = node.initializer;
- if (element is LocalVariableElement) {
- _locals.add(element);
- if (initializer != null) {
- _visitAssignment(node.name, initializer);
- } else {
- _nullableLocals.add(element);
- }
- }
- super.visitVariableDeclaration(node);
- }
-
- @override
- visitCatchClause(CatchClause node) {
- var e = node.exceptionParameter?.staticElement;
- if (e != null) {
- _locals.add(e);
- // TODO(jmesserly): we allow throwing of `null`, for better or worse.
- _nullableLocals.add(e);
- }
-
- e = node.stackTraceParameter?.staticElement;
- if (e != null) _locals.add(e);
-
- super.visitCatchClause(node);
- }
-
- @override
- visitAssignmentExpression(AssignmentExpression node) {
- _visitAssignment(node.leftHandSide, node.rightHandSide);
- super.visitAssignmentExpression(node);
- }
-
- @override
- visitBinaryExpression(BinaryExpression node) {
- var op = node.operator.type;
- if (op.isAssignmentOperator) {
- _visitAssignment(node.leftOperand, node);
- }
- super.visitBinaryExpression(node);
- }
-
- @override
- visitPostfixExpression(PostfixExpression node) {
- var op = node.operator.type;
- if (op.isIncrementOperator) {
- _visitAssignment(node.operand, node);
- }
- super.visitPostfixExpression(node);
- }
-
- @override
- visitPrefixExpression(PrefixExpression node) {
- var op = node.operator.type;
- if (op.isIncrementOperator) {
- _visitAssignment(node.operand, node);
- }
- super.visitPrefixExpression(node);
- }
-
- void _visitAssignment(Expression left, Expression right) {
- if (left is SimpleIdentifier) {
- var element = left.staticElement;
- if (element is LocalVariableElement) {
- bool visitLocal(LocalVariableElement otherLocal) {
- // Record the assignment.
- _assignments
- .putIfAbsent(otherLocal, () => new HashSet.identity())
- .add(element);
- // Optimistically assume this local is not null.
- // We will validate this assumption later.
- return false;
- }
-
- if (_nullInference._isNullable(right, visitLocal)) {
- _nullableLocals.add(element);
- }
- }
- }
- }
-}
« no previous file with comments | « lib/src/codegen/module_builder.dart ('k') | lib/src/codegen/reify_coercions.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698