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

Side by Side Diff: lib/src/codegen/nullability_inferrer.dart

Issue 1751963002: refactor/simplify nullable inference code (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 4 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 library dev_compiler.src.codegen.nullability_inferrer;
6
7 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator;
8 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator;
9 import 'package:analyzer/src/generated/element.dart';
10 import 'package:analyzer/dart/ast/token.dart' show Token, TokenType;
11
12 import 'assignments_index.dart';
13 import 'js_codegen.dart' show TemporaryVariableElement;
14 import '../utils.dart' show isInlineJS, DirectedGraph;
15
16 typedef bool NullableExpressionPredicate(Expression expr);
17
18 typedef DartType _StaticTypeGetter(Expression e);
19 typedef bool _JSBuiltinTypePredicate(DartType t);
20
21 /// Infers flow-insensitive nullability for local variables.
22 // TODO(ochafik): Use flow-sensitive inference.
23 class NullabilityInferrer {
24 /// Index of assignments of [LocalVariableElement]s defined in this inferer's
25 /// context.
26 final Map<LocalVariableElement, List<Expression>> _assignments;
27 final _StaticTypeGetter getStaticType;
28 final _JSBuiltinTypePredicate isJSBuiltinType;
29
30 NullabilityInferrer(Iterable<AstNode> context,
31 {this.getStaticType, this.isJSBuiltinType})
32 : _assignments = indexLocalAssignments(context);
33
34 /// Tests whether [expr] is nullable, with assumptions on local variable
35 /// nullability provided by [isNullableLocal].
36 ///
37 /// If [assignmentsTarget] and [assignments] are not null, this also builds an
38 /// assignments graph (records assignments from each variable to other
39 /// variables it can be assigned to). For example, given an assignment:
40 ///
41 /// y = x;
42 ///
43 /// This will lead us to conclude that `y` can be any value that `x` could
44 /// hold at that location. For example, if we are not considering control
45 /// flow, this means `y` could contain any value that can ever be assigned to
46 /// `x`.
47 bool _isNullableExpression(Expression expr,
48 [bool isNullableLocal(LocalVariableElement e),
49 LocalVariableElement assignmentsTarget,
50 DirectedGraph<LocalVariableElement> assignments]) {
51 // TODO(vsm): Revisit whether we really need this when we get
52 // better non-nullability in the type system.
53 // TODO(jmesserly): we do recursive calls in a few places. This could
54 // leads to O(depth) cost for calling this function. We could store the
55 // resulting value if that becomes an issue, so we maintain the invariant
56 // that each node is visited once.
57
58 if (expr is SimpleIdentifier) {
59 // Type literals are not null.
60 var e = expr.staticElement;
61 if (e is ClassElement || e is FunctionTypeAliasElement) {
62 return false;
63 }
64
65 if (e is LocalVariableElement && isNullableLocal != null) {
66 assignments?.addEdge(e, assignmentsTarget);
67 return isNullableLocal(e);
68 }
69 return true;
70 }
71 bool recurse(Expression x) => _isNullableExpression(
72 x, isNullableLocal, assignmentsTarget, assignments);
73
74 if (expr is Literal) return expr is NullLiteral;
75 if (expr is IsExpression) return false;
76 if (expr is FunctionExpression) return false;
77 if (expr is ThisExpression) return false;
78 if (expr is SuperExpression) return false;
79 if (expr is CascadeExpression) return recurse(expr.target);
80 if (expr is ConditionalExpression) {
81 return recurse(expr.thenExpression) || recurse(expr.elseExpression);
82 }
83 if (expr is ParenthesizedExpression) {
84 return recurse(expr.expression);
85 }
86
87 DartType type = null;
88 if (expr is BinaryExpression) {
89 switch (expr.operator.type) {
90 case TokenType.EQ_EQ:
91 case TokenType.BANG_EQ:
92 case TokenType.AMPERSAND_AMPERSAND:
93 case TokenType.BAR_BAR:
94 return false;
95 case TokenType.QUESTION_QUESTION:
96 return recurse(expr.leftOperand) && recurse(expr.rightOperand);
97 }
98 type = getStaticType(expr.leftOperand);
99 } else if (expr is PrefixExpression) {
100 if (expr.operator.type == TokenType.BANG) return false;
101 type = getStaticType(expr.operand);
102 } else if (expr is PostfixExpression) {
103 type = getStaticType(expr.operand);
104 }
105 if (type != null && isJSBuiltinType(type)) {
106 return false;
107 }
108 if (expr is MethodInvocation) {
109 // TODO(vsm): This logic overlaps with the resolver.
110 // Where is the best place to put this?
111 var e = expr.methodName.staticElement;
112 if (isInlineJS(e)) {
113 // Fix types for JS builtin calls.
114 //
115 // This code was taken from analyzer. It's not super sophisticated:
116 // only looks for the type name in dart:core, so we just copy it here.
117 //
118 // TODO(jmesserly): we'll likely need something that can handle a wider
119 // variety of types, especially when we get to JS interop.
120 var args = expr.argumentList.arguments;
121 var first = args.isNotEmpty ? args.first : null;
122 if (first is SimpleStringLiteral) {
123 var types = first.stringValue;
124 if (!types.split('|').contains('Null')) {
125 return false;
126 }
127 }
128 }
129 // TODO(ochafik): Handle `identical` invocations.
130 }
131
132 // TODO(ochafik): Handle PrefixedIdentifier, refs to top-level finals
133 // that have been assigned non-nullable values, non-generative constructor
134 // calls, refs to local functions...
135
136 // Failed to recognize a non-nullable case: assume it's trivially nullable.
137 return true;
138 }
139
140 NullableExpressionPredicate buildNullabilityPredicate() {
141 // Collect the transitive closure of every variable that can be assigned
142 // trivially nullable values.
143 var assignmentsGraph = new DirectedGraph<LocalVariableElement>();
144
145 /// Detect vars that are "trivially nullable" (i.e. provably nullable
146 /// if we assume all known variables are non-nullable).
147 var trivialNullables = new Set<LocalVariableElement>();
148 _assignments.forEach((local, expressions) {
149 for (var expr in expressions) {
150 // In the unlikely event of an unknown var, assume it's nullable.
151 var isTriviallyNullable = _isNullableExpression(expr,
152 (e) => e is TemporaryVariableElement, local, assignmentsGraph);
153 if (isTriviallyNullable) trivialNullables.add(local);
154 }
155 });
156 var nullables = assignmentsGraph.getTransitiveClosure(trivialNullables);
157
158 bool isNullableLocal(LocalVariableElement e) {
159 // TODO(jmesserly): we should be able to make this work for temps too.
160 return e is TemporaryVariableElement || nullables.contains(e);
161 }
162 return (Expression expr) => _isNullableExpression(expr, isNullableLocal);
163 }
164 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698