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

Side by Side Diff: pkg/compiler/lib/src/inferrer/inferrer_engine.dart

Issue 2616103002: Flatten inferrer implementation (Closed)
Patch Set: Updated cf. comment. Created 3 years, 11 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
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library type_graph_inferrer;
6
7 import 'dart:collection' show Queue;
8
9 import '../common.dart'; 5 import '../common.dart';
10 import '../common/names.dart' show Identifiers; 6 import '../common/names.dart';
11 import '../compiler.dart' show Compiler; 7 import '../compiler.dart';
12 import '../constants/expressions.dart' show ConstantExpression; 8 import '../constants/expressions.dart';
13 import '../constants/values.dart'; 9 import '../constants/values.dart';
14 import '../elements/resolution_types.dart' show ResolutionDartType; 10 import '../core_types.dart';
15 import '../elements/elements.dart'; 11 import '../elements/elements.dart';
16 import '../js_backend/js_backend.dart' show Annotations, JavaScriptBackend; 12 import '../js_backend/js_backend.dart';
17 import '../resolution/tree_elements.dart' show TreeElementMapping; 13 import '../native/behavior.dart' as native;
18 import '../tree/dartstring.dart' show DartString; 14 import '../resolution/tree_elements.dart';
19 import '../tree/tree.dart' as ast show Node, LiteralBool, TryStatement; 15 import '../tree/nodes.dart' as ast;
20 import '../types/constants.dart' show computeTypeMask; 16 import '../types/constants.dart';
21 import '../types/masks.dart' 17 import '../types/types.dart';
22 show CommonMasks, ContainerTypeMask, MapTypeMask, TypeMask; 18 import '../universe/call_structure.dart';
23 import '../types/types.dart' show TypesInferrer; 19 import '../universe/selector.dart';
24 import '../universe/call_structure.dart' show CallStructure; 20 import '../universe/side_effects.dart';
25 import '../universe/selector.dart' show Selector; 21 import '../util/util.dart';
26 import '../universe/side_effects.dart' show SideEffects; 22 import '../world.dart';
27 import '../util/util.dart' show Setlet;
28 import '../world.dart' show ClosedWorld, ClosedWorldRefiner;
29 import 'closure_tracer.dart'; 23 import 'closure_tracer.dart';
30 import 'debug.dart' as debug; 24 import 'debug.dart' as debug;
31 import 'inferrer_visitor.dart' show ArgumentsTypes, TypeSystem; 25 import 'inferrer_visitor.dart';
32 import 'list_tracer.dart'; 26 import 'list_tracer.dart';
33 import 'map_tracer.dart'; 27 import 'map_tracer.dart';
34 import 'simple_types_inferrer.dart'; 28 import 'simple_types_inferrer.dart';
35 import 'type_graph_dump.dart'; 29 import 'type_graph_dump.dart';
30 import 'type_graph_inferrer.dart';
36 import 'type_graph_nodes.dart'; 31 import 'type_graph_nodes.dart';
37 32 import 'type_system.dart';
38 class TypeInformationSystem extends TypeSystem<TypeInformation> {
39 final ClosedWorld closedWorld;
40
41 /// [ElementTypeInformation]s for elements.
42 final Map<Element, TypeInformation> typeInformations =
43 new Map<Element, TypeInformation>();
44
45 /// [ListTypeInformation] for allocated lists.
46 final Map<ast.Node, TypeInformation> allocatedLists =
47 new Map<ast.Node, TypeInformation>();
48
49 /// [MapTypeInformation] for allocated Maps.
50 final Map<ast.Node, TypeInformation> allocatedMaps =
51 new Map<ast.Node, TypeInformation>();
52
53 /// Closures found during the analysis.
54 final Set<TypeInformation> allocatedClosures = new Set<TypeInformation>();
55
56 /// Cache of [ConcreteTypeInformation].
57 final Map<TypeMask, TypeInformation> concreteTypes =
58 new Map<TypeMask, TypeInformation>();
59
60 /// List of [TypeInformation]s allocated inside method bodies (calls,
61 /// narrowing, phis, and containers).
62 final List<TypeInformation> allocatedTypes = <TypeInformation>[];
63
64 Iterable<TypeInformation> get allTypes => [
65 typeInformations.values,
66 allocatedLists.values,
67 allocatedMaps.values,
68 allocatedClosures,
69 concreteTypes.values,
70 allocatedTypes
71 ].expand((x) => x);
72
73 TypeInformationSystem(this.closedWorld) {
74 nonNullEmptyType = getConcreteTypeFor(commonMasks.emptyType);
75 }
76
77 CommonMasks get commonMasks => closedWorld.commonMasks;
78
79 /// Used to group [TypeInformation] nodes by the element that triggered their
80 /// creation.
81 MemberTypeInformation _currentMember = null;
82 MemberTypeInformation get currentMember => _currentMember;
83
84 void withMember(MemberElement element, action) {
85 assert(invariant(element, _currentMember == null,
86 message: "Already constructing graph for $_currentMember."));
87 _currentMember = getInferredTypeOf(element);
88 action();
89 _currentMember = null;
90 }
91
92 TypeInformation nullTypeCache;
93 TypeInformation get nullType {
94 if (nullTypeCache != null) return nullTypeCache;
95 return nullTypeCache = getConcreteTypeFor(commonMasks.nullType);
96 }
97
98 TypeInformation intTypeCache;
99 TypeInformation get intType {
100 if (intTypeCache != null) return intTypeCache;
101 return intTypeCache = getConcreteTypeFor(commonMasks.intType);
102 }
103
104 TypeInformation uint32TypeCache;
105 TypeInformation get uint32Type {
106 if (uint32TypeCache != null) return uint32TypeCache;
107 return uint32TypeCache = getConcreteTypeFor(commonMasks.uint32Type);
108 }
109
110 TypeInformation uint31TypeCache;
111 TypeInformation get uint31Type {
112 if (uint31TypeCache != null) return uint31TypeCache;
113 return uint31TypeCache = getConcreteTypeFor(commonMasks.uint31Type);
114 }
115
116 TypeInformation positiveIntTypeCache;
117 TypeInformation get positiveIntType {
118 if (positiveIntTypeCache != null) return positiveIntTypeCache;
119 return positiveIntTypeCache =
120 getConcreteTypeFor(commonMasks.positiveIntType);
121 }
122
123 TypeInformation doubleTypeCache;
124 TypeInformation get doubleType {
125 if (doubleTypeCache != null) return doubleTypeCache;
126 return doubleTypeCache = getConcreteTypeFor(commonMasks.doubleType);
127 }
128
129 TypeInformation numTypeCache;
130 TypeInformation get numType {
131 if (numTypeCache != null) return numTypeCache;
132 return numTypeCache = getConcreteTypeFor(commonMasks.numType);
133 }
134
135 TypeInformation boolTypeCache;
136 TypeInformation get boolType {
137 if (boolTypeCache != null) return boolTypeCache;
138 return boolTypeCache = getConcreteTypeFor(commonMasks.boolType);
139 }
140
141 TypeInformation functionTypeCache;
142 TypeInformation get functionType {
143 if (functionTypeCache != null) return functionTypeCache;
144 return functionTypeCache = getConcreteTypeFor(commonMasks.functionType);
145 }
146
147 TypeInformation listTypeCache;
148 TypeInformation get listType {
149 if (listTypeCache != null) return listTypeCache;
150 return listTypeCache = getConcreteTypeFor(commonMasks.listType);
151 }
152
153 TypeInformation constListTypeCache;
154 TypeInformation get constListType {
155 if (constListTypeCache != null) return constListTypeCache;
156 return constListTypeCache = getConcreteTypeFor(commonMasks.constListType);
157 }
158
159 TypeInformation fixedListTypeCache;
160 TypeInformation get fixedListType {
161 if (fixedListTypeCache != null) return fixedListTypeCache;
162 return fixedListTypeCache = getConcreteTypeFor(commonMasks.fixedListType);
163 }
164
165 TypeInformation growableListTypeCache;
166 TypeInformation get growableListType {
167 if (growableListTypeCache != null) return growableListTypeCache;
168 return growableListTypeCache =
169 getConcreteTypeFor(commonMasks.growableListType);
170 }
171
172 TypeInformation mapTypeCache;
173 TypeInformation get mapType {
174 if (mapTypeCache != null) return mapTypeCache;
175 return mapTypeCache = getConcreteTypeFor(commonMasks.mapType);
176 }
177
178 TypeInformation constMapTypeCache;
179 TypeInformation get constMapType {
180 if (constMapTypeCache != null) return constMapTypeCache;
181 return constMapTypeCache = getConcreteTypeFor(commonMasks.constMapType);
182 }
183
184 TypeInformation stringTypeCache;
185 TypeInformation get stringType {
186 if (stringTypeCache != null) return stringTypeCache;
187 return stringTypeCache = getConcreteTypeFor(commonMasks.stringType);
188 }
189
190 TypeInformation typeTypeCache;
191 TypeInformation get typeType {
192 if (typeTypeCache != null) return typeTypeCache;
193 return typeTypeCache = getConcreteTypeFor(commonMasks.typeType);
194 }
195
196 TypeInformation dynamicTypeCache;
197 TypeInformation get dynamicType {
198 if (dynamicTypeCache != null) return dynamicTypeCache;
199 return dynamicTypeCache = getConcreteTypeFor(commonMasks.dynamicType);
200 }
201
202 TypeInformation asyncFutureTypeCache;
203 TypeInformation get asyncFutureType {
204 if (asyncFutureTypeCache != null) return asyncFutureTypeCache;
205 return asyncFutureTypeCache =
206 getConcreteTypeFor(commonMasks.asyncFutureType);
207 }
208
209 TypeInformation syncStarIterableTypeCache;
210 TypeInformation get syncStarIterableType {
211 if (syncStarIterableTypeCache != null) return syncStarIterableTypeCache;
212 return syncStarIterableTypeCache =
213 getConcreteTypeFor(commonMasks.syncStarIterableType);
214 }
215
216 TypeInformation asyncStarStreamTypeCache;
217 TypeInformation get asyncStarStreamType {
218 if (asyncStarStreamTypeCache != null) return asyncStarStreamTypeCache;
219 return asyncStarStreamTypeCache =
220 getConcreteTypeFor(commonMasks.asyncStarStreamType);
221 }
222
223 TypeInformation nonNullEmptyType;
224
225 TypeInformation stringLiteralType(DartString value) {
226 return new StringLiteralTypeInformation(value, commonMasks.stringType);
227 }
228
229 TypeInformation boolLiteralType(ast.LiteralBool value) {
230 return new BoolLiteralTypeInformation(value, commonMasks.boolType);
231 }
232
233 TypeInformation computeLUB(
234 TypeInformation firstType, TypeInformation secondType) {
235 if (firstType == null) return secondType;
236 if (firstType == secondType) return firstType;
237 if (firstType == nonNullEmptyType) return secondType;
238 if (secondType == nonNullEmptyType) return firstType;
239 if (firstType == dynamicType || secondType == dynamicType) {
240 return dynamicType;
241 }
242 return getConcreteTypeFor(
243 firstType.type.union(secondType.type, closedWorld));
244 }
245
246 bool selectorNeedsUpdate(TypeInformation info, TypeMask mask) {
247 return info.type != mask;
248 }
249
250 TypeInformation refineReceiver(Selector selector, TypeMask mask,
251 TypeInformation receiver, bool isConditional) {
252 if (receiver.type.isExact) return receiver;
253 TypeMask otherType = closedWorld.allFunctions.receiverType(selector, mask);
254 // Conditional sends (a?.b) can still narrow the possible types of `a`,
255 // however, we still need to consider that `a` may be null.
256 if (isConditional) {
257 // Note: we don't check that receiver.type.isNullable here because this is
258 // called during the graph construction.
259 otherType = otherType.nullable();
260 }
261 // If this is refining to nullable subtype of `Object` just return
262 // the receiver. We know the narrowing is useless.
263 if (otherType.isNullable && otherType.containsAll(closedWorld)) {
264 return receiver;
265 }
266 assert(TypeMask.assertIsNormalized(otherType, closedWorld));
267 TypeInformation newType = new NarrowTypeInformation(receiver, otherType);
268 allocatedTypes.add(newType);
269 return newType;
270 }
271
272 TypeInformation narrowType(
273 TypeInformation type, ResolutionDartType annotation,
274 {bool isNullable: true}) {
275 if (annotation.treatAsDynamic) return type;
276 if (annotation.isVoid) return nullType;
277 if (annotation.element == closedWorld.commonElements.objectClass &&
278 isNullable) {
279 return type;
280 }
281 TypeMask otherType;
282 if (annotation.isTypedef || annotation.isFunctionType) {
283 otherType = functionType.type;
284 } else if (annotation.isTypeVariable) {
285 // TODO(ngeoffray): Narrow to bound.
286 return type;
287 } else {
288 assert(annotation.isInterfaceType);
289 otherType = annotation.element == closedWorld.commonElements.objectClass
290 ? dynamicType.type.nonNullable()
291 : new TypeMask.nonNullSubtype(annotation.element, closedWorld);
292 }
293 if (isNullable) otherType = otherType.nullable();
294 if (type.type.isExact) {
295 return type;
296 } else {
297 assert(TypeMask.assertIsNormalized(otherType, closedWorld));
298 TypeInformation newType = new NarrowTypeInformation(type, otherType);
299 allocatedTypes.add(newType);
300 return newType;
301 }
302 }
303
304 TypeInformation narrowNotNull(TypeInformation type) {
305 if (type.type.isExact && !type.type.isNullable) {
306 return type;
307 }
308 TypeInformation newType =
309 new NarrowTypeInformation(type, dynamicType.type.nonNullable());
310 allocatedTypes.add(newType);
311 return newType;
312 }
313
314 ElementTypeInformation getInferredTypeOf(Element element) {
315 element = element.implementation;
316 return typeInformations.putIfAbsent(element, () {
317 return new ElementTypeInformation(element, this);
318 });
319 }
320
321 ConcreteTypeInformation getConcreteTypeFor(TypeMask mask) {
322 assert(mask != null);
323 return concreteTypes.putIfAbsent(mask, () {
324 return new ConcreteTypeInformation(mask);
325 });
326 }
327
328 String getInferredSignatureOf(FunctionElement function) {
329 ElementTypeInformation info = getInferredTypeOf(function);
330 FunctionElement impl = function.implementation;
331 FunctionSignature signature = impl.functionSignature;
332 var res = "";
333 signature.forEachParameter((Element parameter) {
334 TypeInformation type = getInferredTypeOf(parameter);
335 res += "${res.isEmpty ? '(' : ', '}${type.type} ${parameter.name}";
336 });
337 res += ") -> ${info.type}";
338 return res;
339 }
340
341 TypeInformation nonNullSubtype(ClassElement type) {
342 return getConcreteTypeFor(
343 new TypeMask.nonNullSubtype(type.declaration, closedWorld));
344 }
345
346 TypeInformation nonNullSubclass(ClassElement type) {
347 return getConcreteTypeFor(
348 new TypeMask.nonNullSubclass(type.declaration, closedWorld));
349 }
350
351 TypeInformation nonNullExact(ClassElement type) {
352 return getConcreteTypeFor(
353 new TypeMask.nonNullExact(type.declaration, closedWorld));
354 }
355
356 TypeInformation nonNullEmpty() {
357 return nonNullEmptyType;
358 }
359
360 bool isNull(TypeInformation type) {
361 return type == nullType;
362 }
363
364 TypeInformation allocateList(
365 TypeInformation type, ast.Node node, Element enclosing,
366 [TypeInformation elementType, int length]) {
367 ClassElement typedDataClass = closedWorld.commonElements.typedDataClass;
368 bool isTypedArray = typedDataClass != null &&
369 closedWorld.isInstantiated(typedDataClass) &&
370 type.type.satisfies(typedDataClass, closedWorld);
371 bool isConst = (type.type == commonMasks.constListType);
372 bool isFixed =
373 (type.type == commonMasks.fixedListType) || isConst || isTypedArray;
374 bool isElementInferred = isConst || isTypedArray;
375
376 int inferredLength = isFixed ? length : null;
377 TypeMask elementTypeMask =
378 isElementInferred ? elementType.type : dynamicType.type;
379 ContainerTypeMask mask = new ContainerTypeMask(
380 type.type, node, enclosing, elementTypeMask, inferredLength);
381 ElementInContainerTypeInformation element =
382 new ElementInContainerTypeInformation(currentMember, elementType);
383 element.inferred = isElementInferred;
384
385 allocatedTypes.add(element);
386 return allocatedLists[node] =
387 new ListTypeInformation(currentMember, mask, element, length);
388 }
389
390 TypeInformation allocateClosure(ast.Node node, Element element) {
391 TypeInformation result =
392 new ClosureTypeInformation(currentMember, node, element);
393 allocatedClosures.add(result);
394 return result;
395 }
396
397 TypeInformation allocateMap(
398 ConcreteTypeInformation type, ast.Node node, Element element,
399 [List<TypeInformation> keyTypes, List<TypeInformation> valueTypes]) {
400 assert(keyTypes.length == valueTypes.length);
401 bool isFixed = (type.type == commonMasks.constMapType);
402
403 TypeMask keyType, valueType;
404 if (isFixed) {
405 keyType = keyTypes.fold(nonNullEmptyType.type,
406 (type, info) => type.union(info.type, closedWorld));
407 valueType = valueTypes.fold(nonNullEmptyType.type,
408 (type, info) => type.union(info.type, closedWorld));
409 } else {
410 keyType = valueType = dynamicType.type;
411 }
412 MapTypeMask mask =
413 new MapTypeMask(type.type, node, element, keyType, valueType);
414
415 TypeInformation keyTypeInfo =
416 new KeyInMapTypeInformation(currentMember, null);
417 TypeInformation valueTypeInfo =
418 new ValueInMapTypeInformation(currentMember, null);
419 allocatedTypes.add(keyTypeInfo);
420 allocatedTypes.add(valueTypeInfo);
421
422 MapTypeInformation map =
423 new MapTypeInformation(currentMember, mask, keyTypeInfo, valueTypeInfo);
424
425 for (int i = 0; i < keyTypes.length; ++i) {
426 TypeInformation newType =
427 map.addEntryAssignment(keyTypes[i], valueTypes[i], true);
428 if (newType != null) allocatedTypes.add(newType);
429 }
430
431 // Shortcut: If we already have a first approximation of the key/value type,
432 // start propagating it early.
433 if (isFixed) map.markAsInferred();
434
435 allocatedMaps[node] = map;
436 return map;
437 }
438
439 TypeMask newTypedSelector(TypeInformation info, TypeMask mask) {
440 // Only type the selector if [info] is concrete, because the other
441 // kinds of [TypeInformation] have the empty type at this point of
442 // analysis.
443 return info.isConcrete ? info.type : mask;
444 }
445
446 TypeInformation allocateDiamondPhi(
447 TypeInformation firstInput, TypeInformation secondInput) {
448 PhiElementTypeInformation result =
449 new PhiElementTypeInformation(currentMember, null, false, null);
450 result.addAssignment(firstInput);
451 result.addAssignment(secondInput);
452 allocatedTypes.add(result);
453 return result;
454 }
455
456 PhiElementTypeInformation _addPhi(
457 ast.Node node, Local variable, inputType, bool isLoop) {
458 PhiElementTypeInformation result =
459 new PhiElementTypeInformation(currentMember, node, isLoop, variable);
460 allocatedTypes.add(result);
461 result.addAssignment(inputType);
462 return result;
463 }
464
465 PhiElementTypeInformation allocatePhi(
466 ast.Node node, Local variable, inputType) {
467 // Check if [inputType] is a phi for a local updated in
468 // the try/catch block [node]. If it is, no need to allocate a new
469 // phi.
470 if (inputType is PhiElementTypeInformation &&
471 inputType.branchNode == node &&
472 inputType.branchNode is ast.TryStatement) {
473 return inputType;
474 }
475 return _addPhi(node, variable, inputType, false);
476 }
477
478 PhiElementTypeInformation allocateLoopPhi(
479 ast.Node node, Local variable, inputType) {
480 return _addPhi(node, variable, inputType, true);
481 }
482
483 TypeInformation simplifyPhi(
484 ast.Node node, Local variable, PhiElementTypeInformation phiType) {
485 assert(phiType.branchNode == node);
486 if (phiType.assignments.length == 1) return phiType.assignments.first;
487 return phiType;
488 }
489
490 PhiElementTypeInformation addPhiInput(Local variable,
491 PhiElementTypeInformation phiType, TypeInformation newType) {
492 phiType.addAssignment(newType);
493 return phiType;
494 }
495
496 TypeMask computeTypeMask(Iterable<TypeInformation> assignments) {
497 return joinTypeMasks(assignments.map((e) => e.type));
498 }
499
500 TypeMask joinTypeMasks(Iterable<TypeMask> masks) {
501 var dynamicType = commonMasks.dynamicType;
502 // Optimization: we are iterating over masks twice, but because `masks` is a
503 // mapped iterable, we save the intermediate results to avoid computing them
504 // again.
505 var list = [];
506 for (TypeMask mask in masks) {
507 // Don't do any work on computing unions if we know that after all that
508 // work the result will be `dynamic`.
509 // TODO(sigmund): change to `mask == dynamicType` so we can continue to
510 // track the non-nullable bit.
511 if (mask.containsAll(closedWorld)) return dynamicType;
512 list.add(mask);
513 }
514
515 TypeMask newType = null;
516 for (TypeMask mask in list) {
517 newType = newType == null ? mask : newType.union(mask, closedWorld);
518 // Likewise - stop early if we already reach dynamic.
519 if (newType.containsAll(closedWorld)) return dynamicType;
520 }
521
522 return newType ?? const TypeMask.nonNullEmpty();
523 }
524 }
525
526 /**
527 * A work queue for the inferrer. It filters out nodes that are tagged as
528 * [TypeInformation.doNotEnqueue], as well as ensures through
529 * [TypeInformation.inQueue] that a node is in the queue only once at
530 * a time.
531 */
532 class WorkQueue {
533 final Queue<TypeInformation> queue = new Queue<TypeInformation>();
534
535 void add(TypeInformation element) {
536 if (element.doNotEnqueue) return;
537 if (element.inQueue) return;
538 queue.addLast(element);
539 element.inQueue = true;
540 }
541
542 void addAll(Iterable<TypeInformation> all) {
543 all.forEach(add);
544 }
545
546 TypeInformation remove() {
547 TypeInformation element = queue.removeFirst();
548 element.inQueue = false;
549 return element;
550 }
551
552 bool get isEmpty => queue.isEmpty;
553
554 int get length => queue.length;
555 }
556 33
557 /** 34 /**
558 * An inferencing engine that computes a call graph of 35 * An inferencing engine that computes a call graph of
559 * [TypeInformation] nodes by visiting the AST of the application, and 36 * [TypeInformation] nodes by visiting the AST of the application, and
560 * then does the inferencing on the graph. 37 * then does the inferencing on the graph.
561 *
562 */ 38 */
563 class TypeGraphInferrerEngine 39 class InferrerEngine {
564 extends InferrerEngine<TypeInformation, TypeInformationSystem> {
565 final Map<Element, TypeInformation> defaultTypeOfParameter = 40 final Map<Element, TypeInformation> defaultTypeOfParameter =
566 new Map<Element, TypeInformation>(); 41 new Map<Element, TypeInformation>();
567 final List<CallSiteTypeInformation> allocatedCalls = 42 final List<CallSiteTypeInformation> allocatedCalls =
568 <CallSiteTypeInformation>[]; 43 <CallSiteTypeInformation>[];
569 final WorkQueue workQueue = new WorkQueue(); 44 final WorkQueue workQueue = new WorkQueue();
570 final Element mainElement; 45 final Element mainElement;
571 final Set<Element> analyzedElements = new Set<Element>(); 46 final Set<Element> analyzedElements = new Set<Element>();
572 47
573 /// The maximum number of times we allow a node in the graph to 48 /// The maximum number of times we allow a node in the graph to
574 /// change types. If a node reaches that limit, we give up 49 /// change types. If a node reaches that limit, we give up
575 /// inferencing on it and give it the dynamic type. 50 /// inferencing on it and give it the dynamic type.
576 final int MAX_CHANGE_COUNT = 6; 51 final int MAX_CHANGE_COUNT = 6;
577 52
578 int overallRefineCount = 0; 53 int overallRefineCount = 0;
579 int addedInGraph = 0; 54 int addedInGraph = 0;
580 55
581 TypeGraphInferrerEngine(Compiler compiler, ClosedWorld closedWorld, 56 final Compiler compiler;
582 ClosedWorldRefiner closedWorldRefiner, this.mainElement) 57
583 : super(compiler, closedWorld, closedWorldRefiner, 58 /// The [ClosedWorld] on which inference reasoning is based.
584 new TypeInformationSystem(closedWorld)); 59 final ClosedWorld closedWorld;
60
61 final ClosedWorldRefiner closedWorldRefiner;
62 final TypeSystem types;
63 final Map<ast.Node, TypeInformation> concreteTypes =
64 new Map<ast.Node, TypeInformation>();
65 final Set<Element> generativeConstructorsExposingThis = new Set<Element>();
66
67 /// Data computed internally within elements, like the type-mask of a send a
68 /// list allocation, or a for-in loop.
69 final Map<Element, GlobalTypeInferenceElementData> inTreeData =
70 new Map<Element, GlobalTypeInferenceElementData>();
71
72 InferrerEngine(this.compiler, ClosedWorld closedWorld,
73 this.closedWorldRefiner, this.mainElement)
74 : this.types = new TypeSystem(closedWorld),
75 this.closedWorld = closedWorld;
76
77 CommonElements get commonElements => closedWorld.commonElements;
78
79 /**
80 * Applies [f] to all elements in the universe that match
81 * [selector] and [mask]. If [f] returns false, aborts the iteration.
82 */
83 void forEachElementMatching(
84 Selector selector, TypeMask mask, bool f(Element element)) {
85 Iterable<Element> elements =
86 closedWorld.allFunctions.filter(selector, mask);
87 for (Element e in elements) {
88 if (!f(e.implementation)) return;
89 }
90 }
91
92 // TODO(johnniwinther): Make this private again.
93 GlobalTypeInferenceElementData dataOf(AstElement element) => inTreeData
94 .putIfAbsent(element, () => new GlobalTypeInferenceElementData());
95
96 /**
97 * Update [sideEffects] with the side effects of [callee] being
98 * called with [selector].
99 */
100 void updateSideEffects(
101 SideEffects sideEffects, Selector selector, Element callee) {
102 if (callee.isField) {
103 if (callee.isInstanceMember) {
104 if (selector.isSetter) {
105 sideEffects.setChangesInstanceProperty();
106 } else if (selector.isGetter) {
107 sideEffects.setDependsOnInstancePropertyStore();
108 } else {
109 sideEffects.setAllSideEffects();
110 sideEffects.setDependsOnSomething();
111 }
112 } else {
113 if (selector.isSetter) {
114 sideEffects.setChangesStaticProperty();
115 } else if (selector.isGetter) {
116 sideEffects.setDependsOnStaticPropertyStore();
117 } else {
118 sideEffects.setAllSideEffects();
119 sideEffects.setDependsOnSomething();
120 }
121 }
122 } else if (callee.isGetter && !selector.isGetter) {
123 sideEffects.setAllSideEffects();
124 sideEffects.setDependsOnSomething();
125 } else {
126 sideEffects.add(closedWorldRefiner.getCurrentlyKnownSideEffects(callee));
127 }
128 }
129
130 /**
131 * Returns the type for [nativeBehavior]. See documentation on
132 * [native.NativeBehavior].
133 */
134 TypeInformation typeOfNativeBehavior(native.NativeBehavior nativeBehavior) {
135 if (nativeBehavior == null) return types.dynamicType;
136 List typesReturned = nativeBehavior.typesReturned;
137 if (typesReturned.isEmpty) return types.dynamicType;
138 TypeInformation returnType;
139 for (var type in typesReturned) {
140 TypeInformation mappedType;
141 if (type == native.SpecialType.JsObject) {
142 mappedType = types.nonNullExact(commonElements.objectClass);
143 } else if (type == commonElements.stringType) {
144 mappedType = types.stringType;
145 } else if (type == commonElements.intType) {
146 mappedType = types.intType;
147 } else if (type == commonElements.numType ||
148 type == commonElements.doubleType) {
149 // Note: the backend double class is specifically for non-integer
150 // doubles, and a native behavior returning 'double' does not guarantee
151 // a non-integer return type, so we return the number type for those.
152 mappedType = types.numType;
153 } else if (type == commonElements.boolType) {
154 mappedType = types.boolType;
155 } else if (type == commonElements.nullType) {
156 mappedType = types.nullType;
157 } else if (type.isVoid) {
158 mappedType = types.nullType;
159 } else if (type.isDynamic) {
160 return types.dynamicType;
161 } else {
162 mappedType = types.nonNullSubtype(type.element);
163 }
164 returnType = types.computeLUB(returnType, mappedType);
165 if (returnType == types.dynamicType) {
166 break;
167 }
168 }
169 return returnType;
170 }
171
172 // TODO(johnniwinther): Pass the [ResolvedAst] instead of [owner].
173 void updateSelectorInTree(
174 AstElement owner, Spannable node, Selector selector, TypeMask mask) {
175 ast.Node astNode = node;
176 GlobalTypeInferenceElementData data = dataOf(owner);
177 if (astNode.asSendSet() != null) {
178 if (selector.isSetter || selector.isIndexSet) {
179 data.setTypeMask(node, mask);
180 } else if (selector.isGetter || selector.isIndex) {
181 data.setGetterTypeMaskInComplexSendSet(node, mask);
182 } else {
183 assert(selector.isOperator);
184 data.setOperatorTypeMaskInComplexSendSet(node, mask);
185 }
186 } else if (astNode.asSend() != null) {
187 data.setTypeMask(node, mask);
188 } else {
189 assert(astNode.asForIn() != null);
190 if (selector == Selectors.iterator) {
191 data.setIteratorTypeMask(node, mask);
192 } else if (selector == Selectors.current) {
193 data.setCurrentTypeMask(node, mask);
194 } else {
195 assert(selector == Selectors.moveNext);
196 data.setMoveNextTypeMask(node, mask);
197 }
198 }
199 }
200
201 bool isNativeElement(Element element) {
202 return compiler.backend.isNative(element);
203 }
204
205 bool checkIfExposesThis(Element element) {
206 element = element.implementation;
207 return generativeConstructorsExposingThis.contains(element);
208 }
209
210 void recordExposesThis(Element element, bool exposesThis) {
211 element = element.implementation;
212 if (exposesThis) {
213 generativeConstructorsExposingThis.add(element);
214 }
215 }
585 216
586 JavaScriptBackend get backend => compiler.backend; 217 JavaScriptBackend get backend => compiler.backend;
587 Annotations get annotations => backend.annotations; 218 Annotations get annotations => backend.annotations;
588 DiagnosticReporter get reporter => compiler.reporter; 219 DiagnosticReporter get reporter => compiler.reporter;
589 CommonMasks get commonMasks => closedWorld.commonMasks; 220 CommonMasks get commonMasks => closedWorld.commonMasks;
590 221
591 /** 222 /**
592 * A set of selector names that [List] implements, that we know return 223 * A set of selector names that [List] implements, that we know return
593 * their element type. 224 * their element type.
594 */ 225 */
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
720 var info = types.getInferredTypeOf(parameter); 351 var info = types.getInferredTypeOf(parameter);
721 info.maybeResume(); 352 info.maybeResume();
722 workQueue.add(info); 353 workQueue.add(info);
723 }); 354 });
724 if (tracer.tracedType.mightBePassedToFunctionApply) { 355 if (tracer.tracedType.mightBePassedToFunctionApply) {
725 closedWorldRefiner.registerMightBePassedToApply(e); 356 closedWorldRefiner.registerMightBePassedToApply(e);
726 } 357 }
727 if (debug.VERBOSE) { 358 if (debug.VERBOSE) {
728 print("traced closure $e as " 359 print("traced closure $e as "
729 "${closedWorldRefiner 360 "${closedWorldRefiner
730 .getCurrentlyKnownMightBePassedToApply(e)}"); 361 .getCurrentlyKnownMightBePassedToApply(e)}");
731 } 362 }
732 }); 363 });
733 } 364 }
734 365
735 if (info is ClosureTypeInformation) { 366 if (info is ClosureTypeInformation) {
736 Iterable<FunctionElement> elements = [info.element]; 367 Iterable<FunctionElement> elements = [info.element];
737 trace(elements, new ClosureTracerVisitor(elements, info, this)); 368 trace(elements, new ClosureTracerVisitor(elements, info, this));
738 } else if (info is CallSiteTypeInformation) { 369 } else if (info is CallSiteTypeInformation) {
739 if (info is StaticCallSiteTypeInformation && 370 if (info is StaticCallSiteTypeInformation &&
740 info.selector != null && 371 info.selector != null &&
(...skipping 338 matching lines...) Expand 10 before | Expand all | Expand 10 after
1079 * should be present and a default type for each parameter should 710 * should be present and a default type for each parameter should
1080 * exist. 711 * exist.
1081 */ 712 */
1082 TypeInformation getDefaultTypeOfParameter(Element parameter) { 713 TypeInformation getDefaultTypeOfParameter(Element parameter) {
1083 return defaultTypeOfParameter.putIfAbsent(parameter, () { 714 return defaultTypeOfParameter.putIfAbsent(parameter, () {
1084 return new PlaceholderTypeInformation(types.currentMember); 715 return new PlaceholderTypeInformation(types.currentMember);
1085 }); 716 });
1086 } 717 }
1087 718
1088 /** 719 /**
1089 * Helper to inspect the [TypeGraphInferrer]'s state. To be removed by 720 * This helper breaks abstractions but is currently required to work around
1090 * TODO(johnniwinther) once synthetic parameters get their own default 721 * the wrong modeling of default values of optional parameters of
1091 * values. 722 * synthetic constructors.
723 *
724 * TODO(johnniwinther): Remove once default values of synthetic parameters
725 * are fixed.
1092 */ 726 */
1093 bool hasAlreadyComputedTypeOfParameterDefault(Element parameter) { 727 bool hasAlreadyComputedTypeOfParameterDefault(Element parameter) {
1094 TypeInformation seen = defaultTypeOfParameter[parameter]; 728 TypeInformation seen = defaultTypeOfParameter[parameter];
1095 return (seen != null && seen is! PlaceholderTypeInformation); 729 return (seen != null && seen is! PlaceholderTypeInformation);
1096 } 730 }
1097 731
732 /**
733 * Returns the type of [element].
734 */
1098 TypeInformation typeOfElement(Element element) { 735 TypeInformation typeOfElement(Element element) {
1099 if (element is FunctionElement) return types.functionType; 736 if (element is FunctionElement) return types.functionType;
1100 return types.getInferredTypeOf(element); 737 return types.getInferredTypeOf(element);
1101 } 738 }
1102 739
740 /**
741 * Returns the return type of [element].
742 */
1103 TypeInformation returnTypeOfElement(Element element) { 743 TypeInformation returnTypeOfElement(Element element) {
1104 if (element is! FunctionElement) return types.dynamicType; 744 if (element is! FunctionElement) return types.dynamicType;
1105 return types.getInferredTypeOf(element); 745 return types.getInferredTypeOf(element);
1106 } 746 }
1107 747
748 /**
749 * Records that [node] sets final field [element] to be of type [type].
750 *
751 * [nodeHolder] is the element holder of [node].
752 */
1108 void recordTypeOfFinalField( 753 void recordTypeOfFinalField(
1109 Spannable node, Element analyzed, Element element, TypeInformation type) { 754 Spannable node, Element analyzed, Element element, TypeInformation type) {
1110 types.getInferredTypeOf(element).addAssignment(type); 755 types.getInferredTypeOf(element).addAssignment(type);
1111 } 756 }
1112 757
758 /**
759 * Records that [node] sets non-final field [element] to be of type
760 * [type].
761 */
1113 void recordTypeOfNonFinalField( 762 void recordTypeOfNonFinalField(
1114 Spannable node, Element element, TypeInformation type) { 763 Spannable node, Element element, TypeInformation type) {
1115 types.getInferredTypeOf(element).addAssignment(type); 764 types.getInferredTypeOf(element).addAssignment(type);
1116 } 765 }
1117 766
767 /**
768 * Records that [element] is of type [type].
769 */
1118 void recordType(Element element, TypeInformation type) { 770 void recordType(Element element, TypeInformation type) {
1119 types.getInferredTypeOf(element).addAssignment(type); 771 types.getInferredTypeOf(element).addAssignment(type);
1120 } 772 }
1121 773
774 /**
775 * Records that the return type [element] is of type [type].
776 */
1122 void recordReturnType(Element element, TypeInformation type) { 777 void recordReturnType(Element element, TypeInformation type) {
1123 TypeInformation info = types.getInferredTypeOf(element); 778 TypeInformation info = types.getInferredTypeOf(element);
1124 if (element.name == '==') { 779 if (element.name == '==') {
1125 // Even if x.== doesn't return a bool, 'x == null' evaluates to 'false'. 780 // Even if x.== doesn't return a bool, 'x == null' evaluates to 'false'.
1126 info.addAssignment(types.boolType); 781 info.addAssignment(types.boolType);
1127 } 782 }
1128 // TODO(ngeoffray): Clean up. We do these checks because 783 // TODO(ngeoffray): Clean up. We do these checks because
1129 // [SimpleTypesInferrer] deals with two different inferrers. 784 // [SimpleTypesInferrer] deals with two different inferrers.
1130 if (type == null) return; 785 if (type == null) return;
1131 if (info.assignments.isEmpty) info.addAssignment(type); 786 if (info.assignments.isEmpty) info.addAssignment(type);
1132 } 787 }
1133 788
789 /**
790 * Notifies to the inferrer that [analyzedElement] can have return
791 * type [newType]. [currentType] is the type the [InferrerVisitor]
792 * currently found.
793 *
794 * Returns the new type for [analyzedElement].
795 */
1134 TypeInformation addReturnTypeFor( 796 TypeInformation addReturnTypeFor(
1135 Element element, TypeInformation unused, TypeInformation newType) { 797 Element element, TypeInformation unused, TypeInformation newType) {
1136 TypeInformation type = types.getInferredTypeOf(element); 798 TypeInformation type = types.getInferredTypeOf(element);
1137 // TODO(ngeoffray): Clean up. We do this check because 799 // TODO(ngeoffray): Clean up. We do this check because
1138 // [SimpleTypesInferrer] deals with two different inferrers. 800 // [SimpleTypesInferrer] deals with two different inferrers.
1139 if (element.isGenerativeConstructor) return type; 801 if (element.isGenerativeConstructor) return type;
1140 type.addAssignment(newType); 802 type.addAssignment(newType);
1141 return type; 803 return type;
1142 } 804 }
1143 805
806 /**
807 * Registers that [caller] calls [callee] at location [node], with
808 * [selector], and [arguments]. Note that [selector] is null for
809 * forwarding constructors.
810 *
811 * [sideEffects] will be updated to incorporate [callee]'s side
812 * effects.
813 *
814 * [inLoop] tells whether the call happens in a loop.
815 */
1144 TypeInformation registerCalledElement( 816 TypeInformation registerCalledElement(
1145 Spannable node, 817 Spannable node,
1146 Selector selector, 818 Selector selector,
1147 TypeMask mask, 819 TypeMask mask,
1148 Element caller, 820 Element caller,
1149 Element callee, 821 Element callee,
1150 ArgumentsTypes arguments, 822 ArgumentsTypes arguments,
1151 SideEffects sideEffects, 823 SideEffects sideEffects,
1152 bool inLoop) { 824 bool inLoop) {
1153 CallSiteTypeInformation info = new StaticCallSiteTypeInformation( 825 CallSiteTypeInformation info = new StaticCallSiteTypeInformation(
(...skipping 12 matching lines...) Expand all
1166 if (cls.callType != null) { 838 if (cls.callType != null) {
1167 types.allocatedClosures.add(info); 839 types.allocatedClosures.add(info);
1168 } 840 }
1169 } 841 }
1170 info.addToGraph(this); 842 info.addToGraph(this);
1171 allocatedCalls.add(info); 843 allocatedCalls.add(info);
1172 updateSideEffects(sideEffects, selector, callee); 844 updateSideEffects(sideEffects, selector, callee);
1173 return info; 845 return info;
1174 } 846 }
1175 847
848 /**
849 * Registers that [caller] calls [selector] with [receiverType] as
850 * receiver, and [arguments].
851 *
852 * [sideEffects] will be updated to incorporate the potential
853 * callees' side effects.
854 *
855 * [inLoop] tells whether the call happens in a loop.
856 */
1176 TypeInformation registerCalledSelector( 857 TypeInformation registerCalledSelector(
1177 ast.Node node, 858 ast.Node node,
1178 Selector selector, 859 Selector selector,
1179 TypeMask mask, 860 TypeMask mask,
1180 TypeInformation receiverType, 861 TypeInformation receiverType,
1181 Element caller, 862 Element caller,
1182 ArgumentsTypes arguments, 863 ArgumentsTypes arguments,
1183 SideEffects sideEffects, 864 SideEffects sideEffects,
1184 bool inLoop) { 865 bool inLoop) {
1185 if (selector.isClosureCall) { 866 if (selector.isClosureCall) {
(...skipping 13 matching lines...) Expand all
1199 mask, 880 mask,
1200 receiverType, 881 receiverType,
1201 arguments, 882 arguments,
1202 inLoop); 883 inLoop);
1203 884
1204 info.addToGraph(this); 885 info.addToGraph(this);
1205 allocatedCalls.add(info); 886 allocatedCalls.add(info);
1206 return info; 887 return info;
1207 } 888 }
1208 889
890 /**
891 * Registers a call to await with an expression of type [argumentType] as
892 * argument.
893 */
1209 TypeInformation registerAwait(ast.Node node, TypeInformation argument) { 894 TypeInformation registerAwait(ast.Node node, TypeInformation argument) {
1210 AwaitTypeInformation info = 895 AwaitTypeInformation info =
1211 new AwaitTypeInformation(types.currentMember, node); 896 new AwaitTypeInformation(types.currentMember, node);
1212 info.addAssignment(argument); 897 info.addAssignment(argument);
1213 types.allocatedTypes.add(info); 898 types.allocatedTypes.add(info);
1214 return info; 899 return info;
1215 } 900 }
1216 901
902 /**
903 * Registers that [caller] calls [closure] with [arguments].
904 *
905 * [sideEffects] will be updated to incorporate the potential
906 * callees' side effects.
907 *
908 * [inLoop] tells whether the call happens in a loop.
909 */
1217 TypeInformation registerCalledClosure( 910 TypeInformation registerCalledClosure(
1218 ast.Node node, 911 ast.Node node,
1219 Selector selector, 912 Selector selector,
1220 TypeMask mask, 913 TypeMask mask,
1221 TypeInformation closure, 914 TypeInformation closure,
1222 Element caller, 915 Element caller,
1223 ArgumentsTypes arguments, 916 ArgumentsTypes arguments,
1224 SideEffects sideEffects, 917 SideEffects sideEffects,
1225 bool inLoop) { 918 bool inLoop) {
1226 sideEffects.setDependsOnSomething(); 919 sideEffects.setDependsOnSomething();
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
1339 return returnTypeOfElement(element); 1032 return returnTypeOfElement(element);
1340 } 1033 }
1341 } else if (element.isGetter || element.isField) { 1034 } else if (element.isGetter || element.isField) {
1342 assert(selector.isCall || selector.isSetter); 1035 assert(selector.isCall || selector.isSetter);
1343 return types.dynamicType; 1036 return types.dynamicType;
1344 } else { 1037 } else {
1345 return returnTypeOfElement(element); 1038 return returnTypeOfElement(element);
1346 } 1039 }
1347 } 1040 }
1348 1041
1042 /**
1043 * Records that the captured variable [local] is read.
1044 */
1349 void recordCapturedLocalRead(Local local) {} 1045 void recordCapturedLocalRead(Local local) {}
1350 1046
1047 /**
1048 * Records that the variable [local] is being updated.
1049 */
1351 void recordLocalUpdate(Local local, TypeInformation type) {} 1050 void recordLocalUpdate(Local local, TypeInformation type) {}
1352 } 1051 }
1353
1354 class TypeGraphInferrer implements TypesInferrer {
1355 TypeGraphInferrerEngine inferrer;
1356 final Compiler compiler;
1357 final ClosedWorld closedWorld;
1358 final ClosedWorldRefiner closedWorldRefiner;
1359
1360 TypeGraphInferrer(this.compiler, this.closedWorld, this.closedWorldRefiner);
1361
1362 String get name => 'Graph inferrer';
1363
1364 CommonMasks get commonMasks => closedWorld.commonMasks;
1365
1366 TypeMask get _dynamicType => commonMasks.dynamicType;
1367
1368 void analyzeMain(Element main) {
1369 inferrer = new TypeGraphInferrerEngine(
1370 compiler, closedWorld, closedWorldRefiner, main);
1371 inferrer.runOverAllElements();
1372 }
1373
1374 TypeMask getReturnTypeOfElement(Element element) {
1375 if (compiler.disableTypeInference) return _dynamicType;
1376 // Currently, closure calls return dynamic.
1377 if (element is! FunctionElement) return _dynamicType;
1378 return inferrer.types.getInferredTypeOf(element).type;
1379 }
1380
1381 TypeMask getTypeOfElement(Element element) {
1382 if (compiler.disableTypeInference) return _dynamicType;
1383 // The inferrer stores the return type for a function, so we have to
1384 // be careful to not return it here.
1385 if (element is FunctionElement) return commonMasks.functionType;
1386 return inferrer.types.getInferredTypeOf(element).type;
1387 }
1388
1389 TypeMask getTypeForNewList(Element owner, ast.Node node) {
1390 if (compiler.disableTypeInference) return _dynamicType;
1391 return inferrer.types.allocatedLists[node].type;
1392 }
1393
1394 bool isFixedArrayCheckedForGrowable(ast.Node node) {
1395 if (compiler.disableTypeInference) return true;
1396 ListTypeInformation info = inferrer.types.allocatedLists[node];
1397 return info.checksGrowable;
1398 }
1399
1400 TypeMask getTypeOfSelector(Selector selector, TypeMask mask) {
1401 if (compiler.disableTypeInference) return _dynamicType;
1402 // Bailout for closure calls. We're not tracking types of
1403 // closures.
1404 if (selector.isClosureCall) return _dynamicType;
1405 if (selector.isSetter || selector.isIndexSet) {
1406 return _dynamicType;
1407 }
1408 if (inferrer.returnsListElementType(selector, mask)) {
1409 ContainerTypeMask containerTypeMask = mask;
1410 TypeMask elementType = containerTypeMask.elementType;
1411 return elementType == null ? _dynamicType : elementType;
1412 }
1413 if (inferrer.returnsMapValueType(selector, mask)) {
1414 MapTypeMask mapTypeMask = mask;
1415 TypeMask valueType = mapTypeMask.valueType;
1416 return valueType == null ? _dynamicType : valueType;
1417 }
1418
1419 TypeMask result = const TypeMask.nonNullEmpty();
1420 Iterable<Element> elements =
1421 inferrer.closedWorld.allFunctions.filter(selector, mask);
1422 for (Element element in elements) {
1423 TypeMask type =
1424 inferrer.typeOfElementWithSelector(element, selector).type;
1425 result = result.union(type, inferrer.closedWorld);
1426 }
1427 return result;
1428 }
1429
1430 Iterable<Element> getCallersOf(Element element) {
1431 if (compiler.disableTypeInference) {
1432 throw new UnsupportedError(
1433 "Cannot query the type inferrer when type inference is disabled.");
1434 }
1435 return inferrer.getCallersOf(element);
1436 }
1437
1438 bool isCalledOnce(Element element) {
1439 if (compiler.disableTypeInference) return false;
1440 MemberTypeInformation info = inferrer.types.getInferredTypeOf(element);
1441 return info.isCalledOnce();
1442 }
1443
1444 void clear() {
1445 inferrer.clear();
1446 }
1447 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/inferrer/closure_tracer.dart ('k') | pkg/compiler/lib/src/inferrer/inferrer_visitor.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698