/
type_graph_nodes.dart
1922 lines (1629 loc) · 60.8 KB
/
type_graph_nodes.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright (c) 2013, 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.
library compiler.src.inferrer.type_graph_nodes;
import 'dart:collection' show IterableBase;
import '../common.dart';
import '../common/names.dart' show Identifiers;
import '../compiler.dart' show Compiler;
import '../constants/values.dart';
import '../elements/elements.dart'
show ConstructorElement, LocalElement, MemberElement;
import '../elements/entities.dart';
import '../elements/types.dart';
import '../tree/tree.dart' as ast show Node, Send;
import '../types/masks.dart'
show
CommonMasks,
ContainerTypeMask,
DictionaryTypeMask,
MapTypeMask,
TypeMask,
ValueTypeMask;
import '../universe/selector.dart' show Selector;
import '../util/util.dart' show ImmutableEmptySet, Setlet;
import '../world.dart' show ClosedWorld;
import 'debug.dart' as debug;
import 'locals_handler.dart' show ArgumentsTypes;
import 'inferrer_engine.dart';
import 'type_system.dart';
/**
* Common class for all nodes in the graph. The current nodes are:
*
* - Concrete types
* - Elements
* - Call sites
* - Narrowing instructions
* - Phi instructions
* - Containers (for lists)
* - Type of the element in a container
*
* A node has a set of assignments and users. Assignments are used to
* compute the type of the node ([TypeInformation.computeType]). Users are
* added to the inferrer's work queue when the type of the node
* changes.
*/
abstract class TypeInformation {
Set<TypeInformation> users;
var /* List|ParameterAssignments */ _assignments;
/// The type the inferrer has found for this [TypeInformation].
/// Initially empty.
TypeMask type = const TypeMask.nonNullEmpty();
/// The graph node of the member this [TypeInformation] node belongs to.
final MemberTypeInformation context;
/// The element this [TypeInformation] node belongs to.
MemberEntity get contextMember => context == null ? null : context.member;
Iterable<TypeInformation> get assignments => _assignments;
/// We abandon inference in certain cases (complex cyclic flow, native
/// behaviours, etc.). In some case, we might resume inference in the
/// closure tracer, which is handled by checking whether [assignments] has
/// been set to [STOP_TRACKING_ASSIGNMENTS_MARKER].
bool abandonInferencing = false;
bool get mightResume =>
!identical(assignments, STOP_TRACKING_ASSIGNMENTS_MARKER);
/// Number of times this [TypeInformation] has changed type.
int refineCount = 0;
/// Whether this [TypeInformation] is currently in the inferrer's
/// work queue.
bool inQueue = false;
/// Used to disable enqueueing of type informations where we know that their
/// type will not change for other reasons than being stable. For example,
/// if inference is disabled for a type and it is hardwired to dynamic, this
/// is set to true to spare recomputing dynamic again and again. Changing this
/// to false should never change inference outcome, just make is slower.
bool doNotEnqueue = false;
/// Whether this [TypeInformation] has a stable [type] that will not
/// change.
bool isStable = false;
// TypeInformations are unique. Store an arbitrary identity hash code.
static int _staticHashCode = 0;
final int hashCode = _staticHashCode = (_staticHashCode + 1).toUnsigned(30);
bool get isConcrete => false;
TypeInformation(this.context)
: _assignments = <TypeInformation>[],
users = new Setlet<TypeInformation>();
TypeInformation.noAssignments(this.context)
: _assignments = const <TypeInformation>[],
users = new Setlet<TypeInformation>();
TypeInformation.untracked()
: _assignments = const <TypeInformation>[],
users = const ImmutableEmptySet(),
context = null;
TypeInformation.withAssignments(this.context, this._assignments)
: users = new Setlet<TypeInformation>();
void addUser(TypeInformation user) {
assert(!user.isConcrete);
users.add(user);
}
void addUsersOf(TypeInformation other) {
users.addAll(other.users);
}
void removeUser(TypeInformation user) {
assert(!user.isConcrete);
users.remove(user);
}
// The below is not a compile time constant to make it differentiable
// from other empty lists of [TypeInformation].
static final STOP_TRACKING_ASSIGNMENTS_MARKER = new List<TypeInformation>(0);
bool areAssignmentsTracked() {
return assignments != STOP_TRACKING_ASSIGNMENTS_MARKER;
}
void addAssignment(TypeInformation assignment) {
// Cheap one-level cycle detection.
if (assignment == this) return;
if (areAssignmentsTracked()) {
_assignments.add(assignment);
}
// Even if we abandon inferencing on this [TypeInformation] we
// need to collect the users, so that phases that track where
// elements flow in still work.
assignment.addUser(this);
}
void removeAssignment(TypeInformation assignment) {
if (!abandonInferencing || mightResume) {
_assignments.remove(assignment);
}
// We can have multiple assignments of the same [TypeInformation].
if (!assignments.contains(assignment)) {
assignment.removeUser(this);
}
}
TypeMask refine(InferrerEngine inferrer) {
return abandonInferencing ? safeType(inferrer) : computeType(inferrer);
}
/**
* Computes a new type for this [TypeInformation] node depending on its
* potentially updated inputs.
*/
TypeMask computeType(InferrerEngine inferrer);
/**
* Returns an approximation for this [TypeInformation] node that is always
* safe to use. Used when abandoning inference on a node.
*/
TypeMask safeType(InferrerEngine inferrer) {
return inferrer.types.dynamicType.type;
}
void giveUp(InferrerEngine inferrer, {bool clearAssignments: true}) {
abandonInferencing = true;
// Do not remove [this] as a user of nodes in [assignments],
// because our tracing analysis could be interested in tracing
// this node.
if (clearAssignments) _assignments = STOP_TRACKING_ASSIGNMENTS_MARKER;
// Do not remove users because our tracing analysis could be
// interested in tracing the users of this node.
}
void clear() {
_assignments = STOP_TRACKING_ASSIGNMENTS_MARKER;
users = const ImmutableEmptySet();
}
/// Reset the analysis of this node by making its type empty.
bool reset(InferrerEngine inferrer) {
if (abandonInferencing) return false;
type = const TypeMask.nonNullEmpty();
refineCount = 0;
return true;
}
accept(TypeInformationVisitor visitor);
/// The [Element] where this [TypeInformation] was created. May be `null`
/// for some [TypeInformation] nodes, where we do not need to store
/// the information.
MemberEntity get owner => (context != null) ? context.member : null;
/// Returns whether the type cannot change after it has been
/// inferred.
bool hasStableType(InferrerEngine inferrer) {
return !mightResume && assignments.every((e) => e.isStable);
}
void removeAndClearReferences(InferrerEngine inferrer) {
assignments.forEach((info) {
info.removeUser(this);
});
}
void stabilize(InferrerEngine inferrer) {
removeAndClearReferences(inferrer);
// Do not remove users because the tracing analysis could be interested
// in tracing the users of this node.
_assignments = STOP_TRACKING_ASSIGNMENTS_MARKER;
abandonInferencing = true;
isStable = true;
}
void maybeResume() {
if (!mightResume) return;
abandonInferencing = false;
doNotEnqueue = false;
}
/// Destroys information not needed after type inference.
void cleanup() {
users = null;
_assignments = null;
}
}
abstract class ApplyableTypeInformation implements TypeInformation {
bool mightBePassedToFunctionApply = false;
}
/**
* Marker node used only during tree construction but not during actual type
* refinement.
*
* Currently, this is used to give a type to an optional parameter even before
* the corresponding default expression has been analyzed. See
* [getDefaultTypeOfParameter] and [setDefaultTypeOfParameter] for details.
*/
class PlaceholderTypeInformation extends TypeInformation {
PlaceholderTypeInformation(MemberTypeInformation context) : super(context);
void accept(TypeInformationVisitor visitor) {
throw new UnsupportedError("Cannot visit placeholder");
}
TypeMask computeType(InferrerEngine inferrer) {
throw new UnsupportedError("Cannot refine placeholder");
}
toString() => "Placeholder [$hashCode]";
}
/**
* Parameters of instance functions behave differently than other
* elements because the inferrer may remove assignments. This happens
* when the receiver of a dynamic call site can be refined
* to a type where we know more about which instance method is being
* called.
*/
class ParameterAssignments extends IterableBase<TypeInformation> {
final Map<TypeInformation, int> assignments = new Map<TypeInformation, int>();
void remove(TypeInformation info) {
int existing = assignments[info];
if (existing == null) return;
if (existing == 1) {
assignments.remove(info);
} else {
assignments[info] = existing - 1;
}
}
void add(TypeInformation info) {
int existing = assignments[info];
if (existing == null) {
assignments[info] = 1;
} else {
assignments[info] = existing + 1;
}
}
void replace(TypeInformation old, TypeInformation replacement) {
int existing = assignments[old];
if (existing != null) {
int other = assignments[replacement];
if (other != null) existing += other;
assignments[replacement] = existing;
assignments.remove(old);
}
}
Iterator<TypeInformation> get iterator => assignments.keys.iterator;
Iterable<TypeInformation> where(Function f) => assignments.keys.where(f);
bool contains(Object info) => assignments.containsKey(info);
String toString() => assignments.keys.toList().toString();
}
/**
* A node representing a resolved element of the program. The kind of
* elements that need an [ElementTypeInformation] are:
*
* - Functions (including getters and setters)
* - Constructors (factory or generative)
* - Fields
* - Parameters
* - Local variables mutated in closures
*
* The [ElementTypeInformation] of a function and a constructor is its
* return type.
*
* Note that a few elements of these kinds must be treated specially,
* and they are dealt in [ElementTypeInformation.handleSpecialCases]:
*
* - Parameters of closures, [noSuchMethod] and [call] instance
* methods: we currently do not infer types for those.
*
* - Fields and parameters being assigned by synthesized calls done by
* the backend: we do not know what types the backend will use.
*
* - Native functions and fields: because native methods contain no Dart
* code, and native fields do not have Dart assignments, we just
* trust their type annotation.
*
*/
abstract class ElementTypeInformation extends TypeInformation {
/// Marker to disable inference for closures in [handleSpecialCases].
bool disableInferenceForClosures = true;
ElementTypeInformation._internal(MemberTypeInformation context)
: super(context);
ElementTypeInformation._withAssignments(
MemberTypeInformation context, ParameterAssignments assignments)
: super.withAssignments(context, assignments);
String getInferredSignature(TypeSystem types);
String get debugName;
}
/**
* A node representing members in the broadest sense:
*
* - Functions
* - Constructors
* - Fields (also synthetic ones due to closures)
* - Local functions (closures)
*
* These should never be created directly but instead are constructed by
* the [ElementTypeInformation] factory.
*/
abstract class MemberTypeInformation extends ElementTypeInformation
with ApplyableTypeInformation {
final MemberEntity _member;
/**
* If [element] is a function, [closurizedCount] is the number of
* times it is closurized. The value gets updated while inferring.
*/
int closurizedCount = 0;
// Strict `bool` value is computed in cleanup(). Also used as a flag to see if
// cleanup has been called.
bool _isCalledOnce = null;
/**
* This map contains the callers of [element]. It stores all unique call sites
* to enable counting the global number of call sites of [element].
*
* A call site is either an AST [ast.Node], an [Element] (see uses of
* [synthesizeForwardingCall] in [SimpleTypeInferrerVisitor]).
*
* The global information is summarized in [cleanup], after which [_callers]
* is set to `null`.
*/
Map<MemberEntity, Setlet<Spannable>> _callers;
MemberTypeInformation._internal(this._member) : super._internal(null) {
assert(_checkMember(_member));
}
bool _checkMember(MemberEntity member) {
return !(member is MemberElement && !member.isDeclaration);
}
MemberEntity get member => _member;
String get debugName => '$member';
void addCall(MemberEntity caller, Spannable node) {
_callers ??= <MemberEntity, Setlet<Spannable>>{};
_callers.putIfAbsent(caller, () => new Setlet()).add(node);
}
void removeCall(MemberEntity caller, node) {
if (_callers == null) return;
Setlet calls = _callers[caller];
if (calls == null) return;
calls.remove(node);
if (calls.isEmpty) {
_callers.remove(caller);
}
}
Iterable<MemberEntity> get callers {
// TODO(sra): This is called only from an unused API and a test. If it
// becomes used, [cleanup] will need to copy `_caller.keys`.
// `simple_inferrer_callers_test.dart` ensures that cleanup has not
// happened.
return _callers.keys;
}
bool isCalledOnce() {
// If this assert fires it means that this MemberTypeInformation for the
// element was not part of type inference. This happens for
// ConstructorBodyElements, so guard the call with a test for
// ConstructorBodyElement. For other elements, investigate why the element
// was not present for type inference.
assert(_isCalledOnce != null);
return _isCalledOnce ?? false;
}
bool _computeIsCalledOnce() {
if (_callers == null) return false;
int count = 0;
for (var set in _callers.values) {
count += set.length;
if (count > 1) return false;
}
return count == 1;
}
bool get isClosurized => closurizedCount > 0;
// Closurized methods never become stable to ensure that the information in
// [users] is accurate. The inference stops tracking users for stable types.
// Note that we only override the getter, the setter will still modify the
// state of the [isStable] field inherited from [TypeInformation].
bool get isStable => super.isStable && !isClosurized;
TypeMask handleSpecialCases(InferrerEngine inferrer);
TypeMask _handleFunctionCase(
FunctionEntity function, InferrerEngine inferrer) {
if (inferrer.closedWorld.nativeData.isNativeMember(function)) {
// Use the type annotation as the type for native elements. We
// also give up on inferring to make sure this element never
// goes in the work queue.
giveUp(inferrer);
return inferrer
.typeOfNativeBehavior(
inferrer.closedWorld.nativeData.getNativeMethodBehavior(function))
.type;
}
return null;
}
TypeMask potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer) {
Compiler compiler = inferrer.compiler;
if (!compiler.options.trustTypeAnnotations &&
!compiler.options.enableTypeAssertions &&
!inferrer.optimizerHints.trustTypeAnnotations(_member)) {
return mask;
}
return _potentiallyNarrowType(mask, inferrer);
}
TypeMask _potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer);
TypeMask computeType(InferrerEngine inferrer) {
TypeMask special = handleSpecialCases(inferrer);
if (special != null) return potentiallyNarrowType(special, inferrer);
return potentiallyNarrowType(
inferrer.types.computeTypeMask(assignments), inferrer);
}
TypeMask safeType(InferrerEngine inferrer) {
return potentiallyNarrowType(super.safeType(inferrer), inferrer);
}
String toString() => 'Member $_member $type';
accept(TypeInformationVisitor visitor) {
return visitor.visitMemberTypeInformation(this);
}
void cleanup() {
// This node is on multiple lists so cleanup() can be called twice.
if (_isCalledOnce != null) return;
_isCalledOnce = _computeIsCalledOnce();
_callers = null;
super.cleanup();
}
@override
String getInferredSignature(TypeSystem types) {
return types.getInferredSignatureOfMethod(_member);
}
}
class FieldTypeInformation extends MemberTypeInformation {
FieldEntity get _field => _member;
final DartType _type;
FieldTypeInformation(FieldEntity element, this._type)
: super._internal(element);
TypeMask handleSpecialCases(InferrerEngine inferrer) {
if (!inferrer.backend.canFieldBeUsedForGlobalOptimizations(
_field, inferrer.closedWorld) ||
inferrer.optimizerHints.assumeDynamic(_field)) {
// Do not infer types for fields that have a corresponding annotation or
// are assigned by synthesized calls
giveUp(inferrer);
return safeType(inferrer);
}
if (inferrer.closedWorld.nativeData.isNativeMember(_field)) {
// Use the type annotation as the type for native elements. We
// also give up on inferring to make sure this element never
// goes in the work queue.
giveUp(inferrer);
return inferrer
.typeOfNativeBehavior(inferrer.closedWorld.nativeData
.getNativeFieldLoadBehavior(_field))
.type;
}
return null;
}
TypeMask _potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer) {
return _narrowType(inferrer.closedWorld, mask, _type);
}
bool hasStableType(InferrerEngine inferrer) {
// The number of assignments of non-final fields is
// not stable. Therefore such a field cannot be stable.
if (!_field.isAssignable) {
return false;
}
return super.hasStableType(inferrer);
}
}
class GetterTypeInformation extends MemberTypeInformation {
FunctionEntity get _getter => _member;
final FunctionType _type;
GetterTypeInformation(FunctionEntity element, this._type)
: super._internal(element);
TypeMask handleSpecialCases(InferrerEngine inferrer) {
return _handleFunctionCase(_getter, inferrer);
}
TypeMask _potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer) {
return _narrowType(inferrer.closedWorld, mask, _type.returnType);
}
}
class SetterTypeInformation extends MemberTypeInformation {
FunctionEntity get _setter => _member;
SetterTypeInformation(FunctionEntity element) : super._internal(element);
TypeMask handleSpecialCases(InferrerEngine inferrer) {
return _handleFunctionCase(_setter, inferrer);
}
TypeMask _potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer) {
return mask;
}
}
class MethodTypeInformation extends MemberTypeInformation {
FunctionEntity get _method => _member;
final FunctionType _type;
MethodTypeInformation(FunctionEntity element, this._type)
: super._internal(element);
TypeMask handleSpecialCases(InferrerEngine inferrer) {
return _handleFunctionCase(_method, inferrer);
}
TypeMask _potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer) {
return _narrowType(inferrer.closedWorld, mask, _type.returnType);
}
bool hasStableType(InferrerEngine inferrer) => false;
}
class FactoryConstructorTypeInformation extends MemberTypeInformation {
ConstructorEntity get _constructor => _member;
final FunctionType _type;
FactoryConstructorTypeInformation(ConstructorEntity element, this._type)
: super._internal(element);
TypeMask handleSpecialCases(InferrerEngine inferrer) {
CommonMasks commonMasks = inferrer.commonMasks;
if (_constructor.isFromEnvironmentConstructor) {
if (_constructor.enclosingClass == inferrer.commonElements.intClass) {
giveUp(inferrer);
return commonMasks.intType.nullable();
} else if (_constructor.enclosingClass ==
inferrer.commonElements.boolClass) {
giveUp(inferrer);
return commonMasks.boolType.nullable();
} else if (_constructor.enclosingClass ==
inferrer.commonElements.stringClass) {
giveUp(inferrer);
return commonMasks.stringType.nullable();
}
}
return _handleFunctionCase(_constructor, inferrer);
}
TypeMask _potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer) {
return _narrowType(inferrer.closedWorld, mask, _type.returnType);
}
bool hasStableType(InferrerEngine inferrer) {
return super.hasStableType(inferrer);
}
}
class GenerativeConstructorTypeInformation extends MemberTypeInformation {
ConstructorEntity get _constructor => _member;
GenerativeConstructorTypeInformation(ConstructorEntity element)
: super._internal(element);
TypeMask handleSpecialCases(InferrerEngine inferrer) {
return _handleFunctionCase(_constructor, inferrer);
}
TypeMask _potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer) {
return mask;
}
bool hasStableType(InferrerEngine inferrer) {
return super.hasStableType(inferrer);
}
}
/**
* A node representing parameters:
*
* - Parameters
* - Initializing formals
*
* These should never be created directly but instead are constructed by
* the [ElementTypeInformation] factory.
*/
class ParameterTypeInformation extends ElementTypeInformation {
final Local _parameter;
final DartType _type;
final FunctionEntity _method;
final bool _isInstanceMemberParameter;
final bool _isClosureParameter;
final bool _isInitializingFormal;
bool _isTearOffClosureParameter = false;
ParameterTypeInformation.localFunction(
MemberTypeInformation context, this._parameter, this._type, this._method)
: _isInstanceMemberParameter = false,
_isClosureParameter = true,
_isInitializingFormal = false,
super._internal(context) {
assert(_checkParameter(_parameter));
}
ParameterTypeInformation.static(
MemberTypeInformation context, this._parameter, this._type, this._method,
{bool isInitializingFormal: false})
: _isInstanceMemberParameter = false,
_isClosureParameter = false,
_isInitializingFormal = isInitializingFormal,
super._internal(context) {
assert(_checkParameter(_parameter));
}
ParameterTypeInformation.instanceMember(
MemberTypeInformation context,
this._parameter,
this._type,
this._method,
ParameterAssignments assignments)
: _isInstanceMemberParameter = true,
_isClosureParameter = false,
_isInitializingFormal = false,
super._withAssignments(context, assignments) {
assert(_checkParameter(_parameter));
}
bool _checkParameter(Local parameter) {
return !(parameter is LocalElement && !parameter.isImplementation);
}
FunctionEntity get method => _method;
Local get parameter => _parameter;
String get debugName => '$parameter';
void tagAsTearOffClosureParameter(InferrerEngine inferrer) {
assert(!_isInitializingFormal);
_isTearOffClosureParameter = true;
// We have to add a flow-edge for the default value (if it exists), as we
// might not see all call-sites and thus miss the use of it.
TypeInformation defaultType =
inferrer.getDefaultTypeOfParameter(_parameter);
if (defaultType != null) defaultType.addUser(this);
}
// TODO(herhut): Cleanup into one conditional.
TypeMask handleSpecialCases(InferrerEngine inferrer) {
if (!inferrer.backend.canFunctionParametersBeUsedForGlobalOptimizations(
_method, inferrer.closedWorld) ||
inferrer.optimizerHints.assumeDynamic(_method)) {
// Do not infer types for parameters that have a corresponding annotation
// or that are assigned by synthesized calls.
giveUp(inferrer);
return safeType(inferrer);
}
// The below do not apply to parameters of constructors, so skip
// initializing formals.
if (_isInitializingFormal) return null;
if ((_isTearOffClosureParameter || _isClosureParameter) &&
disableInferenceForClosures) {
// Do not infer types for parameters of closures. We do not
// clear the assignments in case the closure is successfully
// traced.
giveUp(inferrer, clearAssignments: false);
return safeType(inferrer);
}
if (_isInstanceMemberParameter &&
(_method.name == Identifiers.noSuchMethod_ ||
(_method.name == Identifiers.call &&
disableInferenceForClosures))) {
// Do not infer types for parameters of [noSuchMethod] and
// [call] instance methods.
giveUp(inferrer);
return safeType(inferrer);
}
if (inferrer.closedWorldRefiner
.getCurrentlyKnownMightBePassedToApply(_method)) {
giveUp(inferrer);
return safeType(inferrer);
}
if (_method == inferrer.mainElement) {
// The implicit call to main is not seen by the inferrer,
// therefore we explicitly set the type of its parameters as
// dynamic.
// TODO(14566): synthesize a call instead to get the exact
// types.
giveUp(inferrer);
return safeType(inferrer);
}
return null;
}
TypeMask potentiallyNarrowType(TypeMask mask, InferrerEngine inferrer) {
Compiler compiler = inferrer.compiler;
if (!compiler.options.trustTypeAnnotations &&
!inferrer.optimizerHints.trustTypeAnnotations(_method)) {
return mask;
}
// When type assertions are enabled (aka checked mode), we have to always
// ignore type annotations to ensure that the checks are actually inserted
// into the function body and retained until runtime.
assert(!compiler.options.enableTypeAssertions);
return _narrowType(inferrer.closedWorld, mask, _type);
}
TypeMask computeType(InferrerEngine inferrer) {
TypeMask special = handleSpecialCases(inferrer);
if (special != null) return special;
return potentiallyNarrowType(
inferrer.types.computeTypeMask(assignments), inferrer);
}
TypeMask safeType(InferrerEngine inferrer) {
return potentiallyNarrowType(super.safeType(inferrer), inferrer);
}
bool hasStableType(InferrerEngine inferrer) {
// The number of assignments of parameters of instance methods is
// not stable. Therefore such a parameter cannot be stable.
if (_isInstanceMemberParameter) {
return false;
}
return super.hasStableType(inferrer);
}
accept(TypeInformationVisitor visitor) {
return visitor.visitParameterTypeInformation(this);
}
String toString() => 'Parameter $_parameter $type';
@override
String getInferredSignature(TypeSystem types) {
throw new UnsupportedError('ParameterTypeInformation.getInferredSignature');
}
}
/**
* A [CallSiteTypeInformation] is a call found in the AST, or a
* synthesized call for implicit calls in Dart (such as forwarding
* factories). The [call] field is a [ast.Node] for the former, and an
* [Element] for the latter.
*
* In the inferrer graph, [CallSiteTypeInformation] nodes do not have
* any assignment. They rely on the [caller] field for static calls,
* and [selector] and [receiver] fields for dynamic calls.
*/
abstract class CallSiteTypeInformation extends TypeInformation
with ApplyableTypeInformation {
final Spannable call;
final MemberEntity caller;
final Selector selector;
final TypeMask mask;
final ArgumentsTypes arguments;
final bool inLoop;
CallSiteTypeInformation(MemberTypeInformation context, this.call, this.caller,
this.selector, this.mask, this.arguments, this.inLoop)
: super.noAssignments(context) {
assert(_checkCaller(caller));
// [call] is either an AST node or a constructor element in case of a
// a forwarding constructor call.
assert(call is ast.Node || call is ConstructorElement);
}
bool _checkCaller(MemberEntity caller) {
return !(caller is MemberElement && !caller.isDeclaration);
}
String toString() => 'Call site $call $type';
/// Add [this] to the graph being computed by [engine].
void addToGraph(InferrerEngine engine);
/// Return an iterable over the targets of this call.
Iterable<MemberEntity> get callees;
}
class StaticCallSiteTypeInformation extends CallSiteTypeInformation {
final MemberEntity calledElement;
StaticCallSiteTypeInformation(
MemberTypeInformation context,
Spannable call,
MemberEntity enclosing,
this.calledElement,
Selector selector,
TypeMask mask,
ArgumentsTypes arguments,
bool inLoop)
: super(context, call, enclosing, selector, mask, arguments, inLoop) {
assert(_checkCalledElement(calledElement));
}
bool _checkCalledElement(MemberEntity calledElement) {
return !(calledElement is MemberElement && !calledElement.isDeclaration);
}
MemberTypeInformation _getCalledTypeInfo(InferrerEngine inferrer) {
return inferrer.types.getInferredTypeOfMember(calledElement);
}
void addToGraph(InferrerEngine inferrer) {
MemberTypeInformation callee = _getCalledTypeInfo(inferrer);
callee.addCall(caller, call);
callee.addUser(this);
if (arguments != null) {
arguments.forEach((info) => info.addUser(this));
}
inferrer.updateParameterAssignments(
this, calledElement, arguments, selector, mask,
remove: false, addToQueue: false);
}
bool get isSynthesized {
// Some calls do not have a corresponding selector, for example
// forwarding factory constructors, or synthesized super
// constructor calls. We synthesize these calls but do
// not create a selector for them.
return selector == null;
}
TypeInformation _getCalledTypeInfoWithSelector(InferrerEngine inferrer) {
return inferrer.typeOfMemberWithSelector(calledElement, selector);
}
TypeMask computeType(InferrerEngine inferrer) {
if (isSynthesized) {
assert(arguments != null);
return _getCalledTypeInfo(inferrer).type;
} else {
return _getCalledTypeInfoWithSelector(inferrer).type;
}
}
Iterable<MemberEntity> get callees => [calledElement];
accept(TypeInformationVisitor visitor) {
return visitor.visitStaticCallSiteTypeInformation(this);
}
bool hasStableType(InferrerEngine inferrer) {
bool isStable = _getCalledTypeInfo(inferrer).isStable;
return isStable &&
(arguments == null || arguments.every((info) => info.isStable)) &&
super.hasStableType(inferrer);
}
void removeAndClearReferences(InferrerEngine inferrer) {
ElementTypeInformation callee = _getCalledTypeInfo(inferrer);
callee.removeUser(this);
if (arguments != null) {
arguments.forEach((info) => info.removeUser(this));
}
super.removeAndClearReferences(inferrer);
}
}
class DynamicCallSiteTypeInformation extends CallSiteTypeInformation {
final TypeInformation receiver;
/// Cached targets of this call.
Iterable<MemberEntity> targets;
DynamicCallSiteTypeInformation(
MemberTypeInformation context,
Spannable call,
MemberEntity enclosing,
Selector selector,
TypeMask mask,
this.receiver,
ArgumentsTypes arguments,
bool inLoop)
: super(context, call, enclosing, selector, mask, arguments, inLoop);
void addToGraph(InferrerEngine inferrer) {
assert(receiver != null);
TypeMask typeMask = computeTypedSelector(inferrer);
targets = inferrer.closedWorld.locateMembers(selector, typeMask);
receiver.addUser(this);
if (arguments != null) {
arguments.forEach((info) => info.addUser(this));
}
for (MemberEntity element in targets) {
MemberTypeInformation callee =
inferrer.types.getInferredTypeOfMember(element);
callee.addCall(caller, call);
callee.addUser(this);
inferrer.updateParameterAssignments(
this, element, arguments, selector, typeMask,
remove: false, addToQueue: false);
}
}
Iterable<MemberEntity> get callees => targets;
TypeMask computeTypedSelector(InferrerEngine inferrer) {
TypeMask receiverType = receiver.type;
if (mask != receiverType) {
return receiverType == inferrer.commonMasks.dynamicType
? null
: receiverType;
} else {
return mask;
}
}
bool targetsIncludeComplexNoSuchMethod(InferrerEngine inferrer) {
return targets.any((MemberEntity e) {