-
Notifications
You must be signed in to change notification settings - Fork 1k
/
Types.scala
6301 lines (5449 loc) · 253 KB
/
Types.scala
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
package dotty.tools
package dotc
package core
import util.common._
import Symbols._
import Flags._
import Names._
import StdNames._, NameOps._
import NullOpsDecorator._
import NameKinds.SkolemName
import Scopes._
import Constants._
import Contexts._
import Phases._
import Annotations._
import SymDenotations._
import Decorators._
import Denotations._
import Periods._
import CheckRealizable._
import Variances.{Variance, varianceFromInt, varianceToInt, setStructuralVariances, Invariant}
import typer.Nullables
import util.Stats._
import util.SimpleIdentitySet
import ast.tpd._
import ast.TreeTypeMap
import printing.Texts._
import printing.Printer
import Hashable._
import Uniques._
import collection.mutable
import config.Config
import config.Feature
import annotation.{tailrec, constructorOnly}
import language.implicitConversions
import scala.util.hashing.{ MurmurHash3 => hashing }
import config.Printers.{core, typr, matchTypes}
import reporting.{trace, Message}
import java.lang.ref.WeakReference
import scala.annotation.internal.sharable
import scala.annotation.threadUnsafe
import dotty.tools.dotc.transform.SymUtils._
object Types {
@sharable private var nextId = 0
implicit def eqType: CanEqual[Type, Type] = CanEqual.derived
/** Main class representing types.
*
* The principal subclasses and sub-objects are as follows:
*
* ```none
* Type -+- ProxyType --+- NamedType ----+--- TypeRef
* | | \
* | +- SingletonType-+-+- TermRef
* | | |
* | | +--- ThisType
* | | +--- SuperType
* | | +--- ConstantType
* | | +--- TermParamRef
* | | +----RecThis
* | | +--- SkolemType
* | +- TypeParamRef
* | +- RefinedOrRecType -+-- RefinedType
* | | -+-- RecType
* | +- AppliedType
* | +- TypeBounds
* | +- ExprType
* | +- AnnotatedType
* | +- TypeVar
* | +- HKTypeLambda
* | +- MatchType
* |
* +- GroundType -+- AndType
* +- OrType
* +- MethodOrPoly ---+-- PolyType
* | +-- MethodType
* +- ClassInfo
* |
* +- NoType
* +- NoPrefix
* +- ErrorType
* +- WildcardType
* ```
*
* Note: please keep in sync with copy in `docs/docs/internals/type-system.md`.
*/
abstract class Type extends Hashable with printing.Showable {
// ----- Tests -----------------------------------------------------
// // debug only: a unique identifier for a type
// val uniqId = {
// nextId = nextId + 1
// if (nextId == 19555)
// println("foo")
// nextId
// }
/** A cache indicating whether the type was still provisional, last time we checked */
@sharable private var mightBeProvisional = true
/** Is this type still provisional? This is the case if the type contains, or depends on,
* uninstantiated type variables or type symbols that have the Provisional flag set.
* This is an antimonotonic property - once a type is not provisional, it stays so forever.
*/
def isProvisional(using Context): Boolean = mightBeProvisional && testProvisional
private def testProvisional(using Context): Boolean =
class ProAcc extends TypeAccumulator[Boolean]:
override def apply(x: Boolean, t: Type) = x || test(t, this)
def test(t: Type, theAcc: TypeAccumulator[Boolean]): Boolean =
if t.mightBeProvisional then
t.mightBeProvisional = t match
case t: TypeRef =>
!t.currentSymbol.isStatic && {
(t: Type).mightBeProvisional = false // break cycles
t.symbol.is(Provisional)
|| test(t.prefix, theAcc)
|| t.info.match
case info: AliasingBounds => test(info.alias, theAcc)
case TypeBounds(lo, hi) => test(lo, theAcc) || test(hi, theAcc)
case _ => false
}
case t: TermRef =>
!t.currentSymbol.isStatic && test(t.prefix, theAcc)
case t: AppliedType =>
t.fold(false, (x, tp) => x || test(tp, theAcc))
case t: TypeVar =>
!t.inst.exists || test(t.inst, theAcc)
case t: LazyRef =>
!t.completed || test(t.ref, theAcc)
case _ =>
(if theAcc != null then theAcc else ProAcc()).foldOver(false, t)
end if
t.mightBeProvisional
end test
test(this, null)
end testProvisional
/** Is this type different from NoType? */
final def exists: Boolean = this.ne(NoType)
/** This type, if it exists, otherwise `that` type */
inline def orElse(inline that: Type): Type = if (exists) this else that
/** Is this type a value type? */
final def isValueType: Boolean = this.isInstanceOf[ValueType]
/** Is this a value type or a type lambda? */
final def isValueTypeOrLambda: Boolean = isValueType || this.isInstanceOf[TypeLambda]
/** Is this a value type or a wildcard? */
final def isValueTypeOrWildcard: Boolean = isValueType || this.isInstanceOf[WildcardType]
/** Does this type denote a stable reference (i.e. singleton type)?
*
* Like in isStableMember, "stability" means idempotence.
* Rationale: If an expression has a stable type, the expression must be idempotent, so stable types
* must be singleton types of stable expressions. */
final def isStable(using Context): Boolean = stripTypeVar match {
case tp: TermRef => tp.symbol.isStableMember && tp.prefix.isStable || tp.info.isStable
case _: SingletonType | NoPrefix => true
case tp: RefinedOrRecType => tp.parent.isStable
case tp: ExprType => tp.resultType.isStable
case tp: AnnotatedType =>
// NOTE UncheckedStableAnnot was originally meant to be put on fields,
// not on types. Allowing it on types is a Scala 3 extension. See:
// https://www.scala-lang.org/files/archive/spec/2.11/11-annotations.html#scala-compiler-annotations
tp.annot.symbol == defn.UncheckedStableAnnot || tp.parent.isStable
case tp: AndType =>
// TODO: fix And type check when tp contains type parames for explicit-nulls flow-typing
// see: tests/explicit-nulls/pos/flow-stable.scala.disabled
tp.tp1.isStable && (realizability(tp.tp2) eq Realizable) ||
tp.tp2.isStable && (realizability(tp.tp1) eq Realizable)
case _ => false
}
/** Is this type a (possibly refined or applied or aliased) type reference
* to the given type symbol?
* @sym The symbol to compare to. It must be a class symbol or abstract type.
* It makes no sense for it to be an alias type because isRef would always
* return false in that case.
*/
def isRef(sym: Symbol, skipRefined: Boolean = true)(using Context): Boolean = stripped match {
case this1: TypeRef =>
this1.info match { // see comment in Namer#typeDefSig
case TypeAlias(tp) => tp.isRef(sym, skipRefined)
case _ => this1.symbol eq sym
}
case this1: RefinedOrRecType if skipRefined =>
this1.parent.isRef(sym, skipRefined)
case this1: AppliedType =>
val this2 = this1.dealias
if (this2 ne this1) this2.isRef(sym, skipRefined)
else this1.underlying.isRef(sym, skipRefined)
case _ => false
}
/** Is this type a (neither aliased nor applied nor annotated) reference to class `sym`? */
def isDirectRef(sym: Symbol)(using Context): Boolean = stripTypeVar match {
case this1: TypeRef =>
this1.name == sym.name && // avoid forcing info if names differ
(this1.symbol eq sym)
case _ =>
false
}
def isAny(using Context): Boolean = isRef(defn.AnyClass, skipRefined = false)
def isAnyRef(using Context): Boolean = isRef(defn.ObjectClass, skipRefined = false)
def isAnyKind(using Context): Boolean = isRef(defn.AnyKindClass, skipRefined = false)
def isTopType(using Context): Boolean = dealias match
case tp: TypeRef => defn.topClasses.contains(tp.symbol)
case _ => false
/** Is this type exactly Null (no vars, aliases, refinements etc allowed)? */
def isExactlyNull(using Context): Boolean = this match {
case tp: TypeRef =>
tp.name == tpnme.Null && (tp.symbol eq defn.NullClass)
case _ => false
}
/** Is this type exactly Nothing (no vars, aliases, refinements etc allowed)? */
def isExactlyNothing(using Context): Boolean = this match {
case tp: TypeRef =>
tp.name == tpnme.Nothing && (tp.symbol eq defn.NothingClass)
case _ => false
}
/** Is this type exactly Any (no vars, aliases, refinements etc allowed)? */
def isExactlyAny(using Context): Boolean = this match {
case tp: TypeRef =>
tp.name == tpnme.Any && (tp.symbol eq defn.AnyClass)
case _ => false
}
def isBottomType(using Context): Boolean =
if ctx.mode.is(Mode.SafeNulls) && !ctx.phase.erasedTypes then hasClassSymbol(defn.NothingClass)
else isBottomTypeAfterErasure
def isBottomTypeAfterErasure(using Context): Boolean =
val d = defn
hasClassSymbol(d.NothingClass) || hasClassSymbol(d.NullClass)
/** Does this type refer exactly to class symbol `sym`, instead of to a subclass of `sym`?
* Implemented like `isRef`, but follows more types: all type proxies as well as and- and or-types
*/
private[Types] def isTightPrefix(sym: Symbol)(using Context): Boolean = stripTypeVar match {
case tp: NamedType => tp.info.isTightPrefix(sym)
case tp: ClassInfo => tp.cls eq sym
case tp: Types.ThisType => tp.cls eq sym
case tp: TypeProxy => tp.underlying.isTightPrefix(sym)
case tp: AndType => tp.tp1.isTightPrefix(sym) && tp.tp2.isTightPrefix(sym)
case tp: OrType => tp.tp1.isTightPrefix(sym) || tp.tp2.isTightPrefix(sym)
case _ => false
}
/** True if this type is an instance of the given `cls` or an instance of
* a non-bottom subclass of `cls`.
*/
final def derivesFrom(cls: Symbol)(using Context): Boolean = {
def isLowerBottomType(tp: Type) =
tp.isBottomType
&& (tp.hasClassSymbol(defn.NothingClass)
|| cls != defn.NothingClass && !cls.isValueClass)
def loop(tp: Type): Boolean = tp match {
case tp: TypeRef =>
val sym = tp.symbol
if (sym.isClass) sym.derivesFrom(cls) else loop(tp.superType)
case tp: AppliedType =>
tp.superType.derivesFrom(cls)
case tp: MatchType =>
tp.bound.derivesFrom(cls) || tp.reduced.derivesFrom(cls)
case tp: TypeProxy =>
loop(tp.underlying)
case tp: AndType =>
loop(tp.tp1) || loop(tp.tp2)
case tp: OrType =>
// If the type is `T | Null` or `T | Nothing`, the class is != Nothing,
// and `T` derivesFrom the class, then the OrType derivesFrom the class.
// Otherwise, we need to check both sides derivesFrom the class.
if isLowerBottomType(tp.tp1) then
loop(tp.tp2)
else if isLowerBottomType(tp.tp2) then
loop(tp.tp1)
else
loop(tp.tp1) && loop(tp.tp2)
case tp: JavaArrayType =>
cls == defn.ObjectClass
case _ =>
false
}
loop(this)
}
def isFromJavaObject(using Context): Boolean =
isRef(defn.ObjectClass) && (typeSymbol eq defn.FromJavaObjectSymbol)
def containsFromJavaObject(using Context): Boolean = this match
case tp: OrType => tp.tp1.containsFromJavaObject || tp.tp2.containsFromJavaObject
case tp: AndType => tp.tp1.containsFromJavaObject && tp.tp2.containsFromJavaObject
case _ => isFromJavaObject
/** True iff `symd` is a denotation of a class type parameter and the reference
* `<pre> . <symd>` is an actual argument reference, i.e. `pre` is not the
* ThisType of `symd`'s owner, or a reference to `symd`'s owner.'
*/
def isArgPrefixOf(symd: SymDenotation)(using Context): Boolean =
symd.exists && !symd.owner.is(Package) && // Early exit if possible because the next check would force SymbolLoaders
symd.isAllOf(ClassTypeParam) && {
this match {
case tp: ThisType => tp.cls ne symd.owner
case tp: TypeRef => tp.symbol ne symd.owner
case _ => true
}
}
/** Is this type a (possibly aliased) singleton type? */
def isSingleton(using Context): Boolean = dealias.isInstanceOf[SingletonType]
/** Is this type of kind `AnyKind`? */
def hasAnyKind(using Context): Boolean = {
@tailrec def loop(tp: Type): Boolean = tp match {
case tp: TypeRef =>
val sym = tp.symbol
if (sym.isClass) sym == defn.AnyKindClass else loop(tp.translucentSuperType)
case tp: TypeProxy =>
loop(tp.underlying)
case _ =>
false
}
loop(this)
}
/** Is this type guaranteed not to have `null` as a value? */
final def isNotNull(using Context): Boolean = this match {
case tp: ConstantType => tp.value.value != null
case tp: ClassInfo => !tp.cls.isNullableClass && tp.cls != defn.NothingClass
case tp: TypeBounds => tp.lo.isNotNull
case tp: TypeProxy => tp.underlying.isNotNull
case AndType(tp1, tp2) => tp1.isNotNull || tp2.isNotNull
case OrType(tp1, tp2) => tp1.isNotNull && tp2.isNotNull
case _ => false
}
/** Is this type produced as a repair for an error? */
final def isError(using Context): Boolean = stripTypeVar.isInstanceOf[ErrorType]
/** Is some part of the widened version of this type produced as a repair for an error?
*
*/
def isErroneous(using Context): Boolean =
widen.existsPart(_.isError, forceLazy = false)
/** Is this type unusable for implicit search or overloading resolution
* since it has embedded errors that can match anything? This is weaker and more
* ad-hoc than isErroneous. The main differences are that we always consider aliases
* (since these are relevant for inference or resolution) but never consider prefixes
* (since these often do not constrain the search space anyway).
*/
def unusableForInference(using Context): Boolean = widenDealias match
case AppliedType(tycon, args) => tycon.unusableForInference || args.exists(_.unusableForInference)
case RefinedType(parent, _, rinfo) => parent.unusableForInference || rinfo.unusableForInference
case TypeBounds(lo, hi) => lo.unusableForInference || hi.unusableForInference
case tp: AndOrType => tp.tp1.unusableForInference || tp.tp2.unusableForInference
case tp: LambdaType => tp.resultType.unusableForInference || tp.paramInfos.exists(_.unusableForInference)
case WildcardType(optBounds) => optBounds.unusableForInference
case _: ErrorType => true
case _ => false
/** Does the type carry an annotation that is an instance of `cls`? */
@tailrec final def hasAnnotation(cls: ClassSymbol)(using Context): Boolean = stripTypeVar match {
case AnnotatedType(tp, annot) => (annot matches cls) || (tp hasAnnotation cls)
case _ => false
}
/** Does this type have a supertype with an annotation satisfying given predicate `p`? */
def derivesAnnotWith(p: Annotation => Boolean)(using Context): Boolean = this match {
case tp: AnnotatedType => p(tp.annot) || tp.parent.derivesAnnotWith(p)
case tp: TypeProxy => tp.superType.derivesAnnotWith(p)
case AndType(l, r) => l.derivesAnnotWith(p) || r.derivesAnnotWith(p)
case OrType(l, r) => l.derivesAnnotWith(p) && r.derivesAnnotWith(p)
case _ => false
}
/** Does this type occur as a part of type `that`? */
def occursIn(that: Type)(using Context): Boolean =
that.existsPart(this == _)
/** Does this type not refer to TypeParamRefs or uninstantiated TypeVars? */
final def isGround(using Context): Boolean =
(new isGroundAccumulator).apply(true, this)
/** Is this a type of a repeated parameter? */
def isRepeatedParam(using Context): Boolean =
typeSymbol eq defn.RepeatedParamClass
/** Is this the type of a method that has a repeated parameter type as
* last parameter type?
*/
def isVarArgsMethod(using Context): Boolean = stripPoly match {
case mt: MethodType => mt.paramInfos.nonEmpty && mt.paramInfos.last.isRepeatedParam
case _ => false
}
/** Is this the type of a method with a leading empty parameter list?
*/
def isNullaryMethod(using Context): Boolean = stripPoly match {
case MethodType(Nil) => true
case _ => false
}
/** Is this an alias TypeBounds? */
final def isTypeAlias: Boolean = this.isInstanceOf[TypeAlias]
/** Is this a Method or PolyType which has implicit or contextual parameters? */
def isImplicitMethod: Boolean = false
/** Is this a Method or PolyType which has contextual parameters as first value parameter list? */
def isContextualMethod: Boolean = false
/** Is this a MethodType for which the parameters will not be used? */
def isErasedMethod: Boolean = false
/** Is this a match type or a higher-kinded abstraction of one?
*/
def isMatch(using Context): Boolean = stripped match {
case _: MatchType => true
case tp: HKTypeLambda => tp.resType.isMatch
case tp: AppliedType => tp.isMatchAlias
case _ => false
}
/** Is this a higher-kinded type lambda with given parameter variances? */
def isDeclaredVarianceLambda: Boolean = false
/** Does this type contain wildcard types? */
final def containsWildcardTypes(using Context) =
existsPart(_.isInstanceOf[WildcardType], StopAt.Static, forceLazy = false)
// ----- Higher-order combinators -----------------------------------
/** Returns true if there is a part of this type that satisfies predicate `p`.
*/
final def existsPart(p: Type => Boolean, stopAt: StopAt = StopAt.None, forceLazy: Boolean = true)(using Context): Boolean =
new ExistsAccumulator(p, stopAt, forceLazy).apply(false, this)
/** Returns true if all parts of this type satisfy predicate `p`.
*/
final def forallParts(p: Type => Boolean)(using Context): Boolean =
!existsPart(!p(_))
/** Performs operation on all parts of this type */
final def foreachPart(p: Type => Unit, stopAt: StopAt = StopAt.None)(using Context): Unit =
new ForeachAccumulator(p, stopAt).apply((), this)
/** The parts of this type which are type or term refs and which
* satisfy predicate `p`.
*
* @param p The predicate to satisfy
*/
def namedPartsWith(p: NamedType => Boolean)(using Context): List[NamedType] =
new NamedPartsAccumulator(p).apply(Nil, this)
/** Map function `f` over elements of an AndType, rebuilding with function `g` */
def mapReduceAnd[T](f: Type => T)(g: (T, T) => T)(using Context): T = stripTypeVar match {
case AndType(tp1, tp2) => g(tp1.mapReduceAnd(f)(g), tp2.mapReduceAnd(f)(g))
case _ => f(this)
}
/** Map function `f` over elements of an OrType, rebuilding with function `g` */
final def mapReduceOr[T](f: Type => T)(g: (T, T) => T)(using Context): T = stripTypeVar match {
case OrType(tp1, tp2) => g(tp1.mapReduceOr(f)(g), tp2.mapReduceOr(f)(g))
case _ => f(this)
}
// ----- Associated symbols ----------------------------------------------
/** The type symbol associated with the type */
@tailrec final def typeSymbol(using Context): Symbol = this match {
case tp: TypeRef => tp.symbol
case tp: TypeProxy => tp.underlying.typeSymbol
case tp: ClassInfo => tp.cls
case _: JavaArrayType => defn.ArrayClass
case _ => NoSymbol
}
/** The least class or trait of which this type is a subtype or parameterized
* instance, or NoSymbol if none exists (either because this type is not a
* value type, or because superclasses are ambiguous).
*/
final def classSymbol(using Context): Symbol = this match
case tp: TypeRef =>
val sym = tp.symbol
if (sym.isClass) sym else tp.superType.classSymbol
case tp: TypeProxy =>
tp.underlying.classSymbol
case tp: ClassInfo =>
tp.cls
case AndType(l, r) =>
val lsym = l.classSymbol
val rsym = r.classSymbol
if (lsym isSubClass rsym) lsym
else if (rsym isSubClass lsym) rsym
else NoSymbol
case tp: OrType =>
if tp.tp1.hasClassSymbol(defn.NothingClass) then
tp.tp2.classSymbol
else if tp.tp2.hasClassSymbol(defn.NothingClass) then
tp.tp1.classSymbol
else
def tp1Null = tp.tp1.hasClassSymbol(defn.NullClass)
def tp2Null = tp.tp2.hasClassSymbol(defn.NullClass)
if ctx.erasedTypes && (tp1Null || tp2Null) then
val otherSide = if tp1Null then tp.tp2.classSymbol else tp.tp1.classSymbol
if otherSide.isValueClass then defn.AnyClass else otherSide
else
tp.join.classSymbol
case _: JavaArrayType =>
defn.ArrayClass
case _ =>
NoSymbol
/** The least (wrt <:<) set of symbols satisfying the `include` predicate of which this type is a subtype
*/
final def parentSymbols(include: Symbol => Boolean)(using Context): List[Symbol] = this match {
case tp: TypeRef =>
val sym = tp.symbol
if (include(sym)) sym :: Nil else tp.superType.parentSymbols(include)
case tp: TypeProxy =>
tp.underlying.parentSymbols(include)
case tp: ClassInfo =>
tp.cls :: Nil
case AndType(l, r) =>
l.parentSymbols(include) | r.parentSymbols(include)
case OrType(l, r) =>
l.parentSymbols(include) intersect r.parentSymbols(include) // TODO does not conform to spec
case _ =>
Nil
}
/** The least (wrt <:<) set of class symbols of which this type is a subtype
*/
final def classSymbols(using Context): List[ClassSymbol] =
parentSymbols(_.isClass).asInstanceOf
/** Same as `this.classSymbols.contains(cls)` but more efficient */
final def hasClassSymbol(cls: Symbol)(using Context): Boolean = this match
case tp: TypeRef =>
val sym = tp.symbol
sym == cls || !sym.isClass && tp.superType.hasClassSymbol(cls)
case tp: TypeProxy =>
tp.underlying.hasClassSymbol(cls)
case tp: ClassInfo =>
tp.cls == cls
case AndType(l, r) =>
l.hasClassSymbol(cls) || r.hasClassSymbol(cls)
case OrType(l, r) =>
l.hasClassSymbol(cls) && r.hasClassSymbol(cls)
case _ =>
false
/** Same as hasClassSmbol(MatchableClass), except that we also follow the constraint
* bounds of type variables in the constraint.
*/
def isMatchableBound(using Context): Boolean = dealias match
case tp: TypeRef => tp.symbol == defn.MatchableClass
case tp: TypeParamRef =>
ctx.typerState.constraint.entry(tp) match
case bounds: TypeBounds => bounds.hi.isMatchableBound
case _ => false
case tp: TypeProxy => tp.underlying.isMatchableBound
case tp: AndType => tp.tp1.isMatchableBound || tp.tp2.isMatchableBound
case tp: OrType => tp.tp1.isMatchableBound && tp.tp2.isMatchableBound
case _ => false
/** The term symbol associated with the type */
@tailrec final def termSymbol(using Context): Symbol = this match {
case tp: TermRef => tp.symbol
case tp: TypeProxy => tp.underlying.termSymbol
case _ => NoSymbol
}
/** The base classes of this type as determined by ClassDenotation
* in linearization order, with the class itself as first element.
* Inherited by all type proxies. Overridden for And and Or types.
* `Nil` for all other types.
*/
def baseClasses(using Context): List[ClassSymbol] =
record("baseClasses")
try
this match
case tp: TypeProxy =>
tp.underlying.baseClasses
case tp: ClassInfo =>
tp.cls.classDenot.baseClasses
case _ => Nil
catch case ex: Throwable =>
handleRecursive("base classes of", this.show, ex)
// ----- Member access -------------------------------------------------
/** The scope of all declarations of this type.
* Defined by ClassInfo, inherited by type proxies.
* Empty scope for all other types.
*/
@tailrec final def decls(using Context): Scope = this match {
case tp: ClassInfo =>
tp.decls
case tp: TypeProxy =>
tp.underlying.decls
case _ =>
EmptyScope
}
/** A denotation containing the declaration(s) in this type with the given name.
* The result is either a SymDenotation or a MultiDenotation of SymDenotations.
* The info(s) are the original symbol infos, no translation takes place.
*/
final def decl(name: Name)(using Context): Denotation = {
record("decl")
findDecl(name, EmptyFlags)
}
/** A denotation containing the non-private declaration(s) in this type with the given name */
final def nonPrivateDecl(name: Name)(using Context): Denotation =
findDecl(name, Private)
/** A denotation containing the declaration(s) in this type with the given
* name, as seen from prefix type `pre`. Declarations that have a flag
* in `excluded` are omitted.
*/
@tailrec final def findDecl(name: Name, excluded: FlagSet)(using Context): Denotation = this match {
case tp: ClassInfo =>
tp.decls.denotsNamed(name).filterWithFlags(EmptyFlags, excluded).toDenot(NoPrefix)
case tp: TypeProxy =>
tp.underlying.findDecl(name, excluded)
case err: ErrorType =>
newErrorSymbol(classSymbol orElse defn.RootClass, name, err.msg)
case _ =>
NoDenotation
}
/** The member of this type with the given name */
final def member(name: Name)(using Context): Denotation = {
record("member")
memberBasedOnFlags(name, required = EmptyFlags, excluded = EmptyFlags)
}
/** The non-private member of this type with the given name. */
final def nonPrivateMember(name: Name)(using Context): Denotation = {
record("nonPrivateMember")
memberBasedOnFlags(name, required = EmptyFlags, excluded = Flags.Private)
}
/** The member with given `name` and required and/or excluded flags */
final def memberBasedOnFlags(name: Name, required: FlagSet = EmptyFlags, excluded: FlagSet = EmptyFlags)(using Context): Denotation = {
// We need a valid prefix for `asSeenFrom`
val pre = this match {
case tp: ClassInfo => tp.appliedRef
case _ => widenIfUnstable
}
findMember(name, pre, required, excluded)
}
/** Find member of this type with given `name`, all `required`
* flags and no `excluded` flag and produce a denotation that contains
* the type of the member as seen from given prefix `pre`.
*/
final def findMember(name: Name, pre: Type, required: FlagSet = EmptyFlags, excluded: FlagSet = EmptyFlags)(using Context): Denotation = {
@tailrec def go(tp: Type): Denotation = tp match {
case tp: TermRef =>
go (tp.underlying match {
case mt: MethodType
if mt.paramInfos.isEmpty && tp.symbol.is(StableRealizable) => mt.resultType
case tp1 => tp1
})
case tp: TypeRef =>
tp.denot match {
case d: ClassDenotation => d.findMember(name, pre, required, excluded)
case d => go(d.info)
}
case tp: AppliedType =>
tp.tycon match {
case tc: TypeRef =>
if (tc.symbol.isClass) go(tc)
else {
val normed = tp.tryNormalize
go(if (normed.exists) normed else tp.superType)
}
case tc: HKTypeLambda =>
goApplied(tp, tc)
case _ =>
go(tp.superType)
}
case tp: ThisType => // ??? inline
goThis(tp)
case tp: RefinedType =>
if (name eq tp.refinedName) goRefined(tp) else go(tp.parent)
case tp: RecType =>
goRec(tp)
case tp: TypeParamRef =>
goParam(tp)
case tp: SuperType =>
goSuper(tp)
case tp: MatchType =>
val normed = tp.tryNormalize
go(if (normed.exists) normed else tp.underlying)
case tp: TypeProxy =>
go(tp.underlying)
case tp: ClassInfo =>
tp.cls.findMember(name, pre, required, excluded)
case AndType(l, r) =>
goAnd(l, r)
case tp: OrType =>
goOr(tp)
case tp: JavaArrayType =>
defn.ObjectType.findMember(name, pre, required, excluded)
case err: ErrorType =>
newErrorSymbol(pre.classSymbol orElse defn.RootClass, name, err.msg)
case _ =>
NoDenotation
}
def goRec(tp: RecType) =
if (tp.parent == null) NoDenotation
else if (tp eq pre) go(tp.parent)
else {
//println(s"find member $pre . $name in $tp")
// We have to be careful because we might open the same (wrt eq) recursive type
// twice during findMember which risks picking the wrong prefix in the `substRecThis(rt, pre)`
// call below. To avoid this problem we do a defensive copy of the recursive
// type first. But if we do this always we risk being inefficient and we ran into
// stackoverflows when compiling pos/hk.scala under the refinement encoding
// of hk-types. So we only do a copy if the type
// is visited again in a recursive call to `findMember`, as tracked by `tp.opened`.
// Furthermore, if this happens we mark the original recursive type with `openedTwice`
// which means that we always defensively copy the type in the future. This second
// measure is necessary because findMember calls might be cached, so do not
// necessarily appear in nested order.
// Without the defensive copy, Typer.scala fails to compile at the line
//
// untpd.rename(lhsCore, setterName).withType(setterType), WildcardType)
//
// because the subtype check
//
// ThisTree[Untyped]#ThisTree[Typed] <: Tree[Typed]
//
// fails (in fact it thinks the underlying type of the LHS is `Tree[Untyped]`.)
//
// Without the `openedTwice` trick, Typer.scala fails to Ycheck
// at phase resolveSuper.
val rt =
if (tp.opened) { // defensive copy
tp.openedTwice = true
RecType(rt => tp.parent.substRecThis(tp, rt.recThis))
}
else tp
rt.opened = true
try go(rt.parent).mapInfo(_.substRecThis(rt, pre))
finally
if (!rt.openedTwice) rt.opened = false
}
def goRefined(tp: RefinedType) = {
val pdenot = go(tp.parent)
val pinfo = pdenot.info
val rinfo = tp.refinedInfo
if (name.isTypeName && !pinfo.isInstanceOf[ClassInfo]) { // simplified case that runs more efficiently
val jointInfo =
if rinfo.isInstanceOf[TypeAlias] && !ctx.mode.is(Mode.CheckBounds) then
// In normal situations, the only way to "improve" on rinfo is to return an empty type bounds
// So, we do not lose anything essential in "widening" to rinfo.
// We need to compute the precise info only when checking for empty bounds
// which is communicated by the CheckBounds mode.
rinfo
else if ctx.base.pendingMemberSearches.contains(name) then
pinfo safe_& rinfo
else
pinfo recoverable_& rinfo
pdenot.asSingleDenotation.derivedSingleDenotation(pdenot.symbol, jointInfo)
}
else
val isRefinedMethod = rinfo.isInstanceOf[MethodOrPoly]
val joint = pdenot.meet(
new JointRefDenotation(NoSymbol, rinfo, Period.allInRun(ctx.runId), pre, isRefinedMethod),
pre,
safeIntersection = ctx.base.pendingMemberSearches.contains(name))
joint match
case joint: SingleDenotation
if isRefinedMethod && rinfo <:< joint.info =>
// use `rinfo` to keep the right parameter names for named args. See i8516.scala.
joint.derivedSingleDenotation(joint.symbol, rinfo, pre, isRefinedMethod)
case _ =>
joint
}
def goApplied(tp: AppliedType, tycon: HKTypeLambda) =
go(tycon.resType).mapInfo(info =>
tycon.derivedLambdaAbstraction(tycon.paramNames, tycon.paramInfos, info).appliedTo(tp.args))
def goThis(tp: ThisType) =
val underlying = tp.underlying
val d = go(underlying)
if d.exists then
if underlying.isInstanceOf[AndType] then
// The underlying type of `this` is specified in a self type clause.
// In this case we need to exclude all private members from `d` which are
// not defined in the class of the `this` type. We could do this test
// always, but the restriction to test only if `underlying` is an AndType
// is made to save execution time in the common case. See i9844.scala for test cases.
def qualifies(sd: SingleDenotation) =
!sd.symbol.is(Private) || sd.symbol.owner == tp.cls
d match
case d: SingleDenotation => if qualifies(d) then d else NoDenotation
case d => d.filterWithPredicate(qualifies)
else d
else
// There is a special case to handle:
// trait Super { this: Sub => private class Inner {} println(this.Inner) }
// class Sub extends Super
// When resolving Super.this.Inner, the normal logic goes to the self type and
// looks for Inner from there. But this fails because Inner is private.
// We fix the problem by having the following fallback case, which links up the
// member in Super instead of Sub.
// As an example of this in the wild, see
// loadClassWithPrivateInnerAndSubSelf in ShowClassTests
go(tp.cls.typeRef) orElse d
def goParam(tp: TypeParamRef) = {
val next = tp.underlying
ctx.typerState.constraint.entry(tp) match {
case bounds: TypeBounds if bounds ne next =>
go(bounds.hi)
case _ =>
go(next)
}
}
def goSuper(tp: SuperType) = go(tp.underlying) match {
case d: JointRefDenotation =>
typr.println(i"redirecting super.$name from $tp to ${d.symbol.showLocated}")
new UniqueRefDenotation(d.symbol, tp.memberInfo(d.symbol), d.validFor, pre)
case d => d
}
def goAnd(l: Type, r: Type) =
go(l).meet(go(r), pre, safeIntersection = ctx.base.pendingMemberSearches.contains(name))
def goOr(tp: OrType) =
inline def searchAfterJoin =
// we need to keep the invariant that `pre <: tp`. Branch `union-types-narrow-prefix`
// achieved that by narrowing `pre` to each alternative, but it led to merge errors in
// lots of places. The present strategy is instead of widen `tp` using `join` to be a
// supertype of `pre`.
go(tp.join)
if Nullables.unsafeNullsEnabled then tp match
case OrNull(tp1) if tp1 <:< defn.ObjectType =>
// Selecting `name` from a type `T | Null` is like selecting `name` from `T`, if
// unsafeNulls is enabled and T is a subtype of AnyRef.
// This can throw at runtime, but we trade soundness for usability.
tp1.findMember(name, pre.stripNull, required, excluded)
case _ =>
searchAfterJoin
else searchAfterJoin
val recCount = ctx.base.findMemberCount
if (recCount >= Config.LogPendingFindMemberThreshold)
ctx.base.pendingMemberSearches = name :: ctx.base.pendingMemberSearches
ctx.base.findMemberCount = recCount + 1
try go(this)
catch {
case ex: Throwable =>
core.println(s"findMember exception for $this member $name, pre = $pre, recCount = $recCount")
def showPrefixSafely(pre: Type)(using Context): String = pre.stripTypeVar match {
case pre: TermRef => i"${pre.symbol.name}."
case pre: TypeRef => i"${pre.symbol.name}#"
case pre: TypeProxy => showPrefixSafely(pre.underlying)
case _ => if (pre.typeSymbol.exists) i"${pre.typeSymbol.name}#" else "."
}
handleRecursive("find-member", i"${showPrefixSafely(pre)}$name", ex)
}
finally {
if (recCount >= Config.LogPendingFindMemberThreshold)
ctx.base.pendingMemberSearches = ctx.base.pendingMemberSearches.tail
ctx.base.findMemberCount = recCount
}
}
/** The set of names of members of this type that pass the given name filter
* when seen as members of `pre`. More precisely, these are all
* of members `name` such that `keepOnly(pre, name)` is `true`.
* @note: OK to use a Set[Name] here because Name hashcodes are replayable,
* hence the Set will always give the same names in the same order.
*/
final def memberNames(keepOnly: NameFilter, pre: Type = this)(using Context): Set[Name] = this match {
case tp: ClassInfo =>
val names = tp.cls.classDenot.memberNames(keepOnly)
if keepOnly.isStable then names else names.filter(keepOnly(pre, _))
case tp: RefinedType =>
val ns = tp.parent.memberNames(keepOnly, pre)
if (keepOnly(pre, tp.refinedName)) ns + tp.refinedName else ns
case tp: TypeProxy =>
tp.underlying.memberNames(keepOnly, pre)
case tp: AndType =>
tp.tp1.memberNames(keepOnly, pre) | tp.tp2.memberNames(keepOnly, pre)
case tp: OrType =>
tp.tp1.memberNames(keepOnly, pre) & tp.tp2.memberNames(keepOnly, pre)
case _ =>
Set()
}
def memberDenots(keepOnly: NameFilter, f: (Name, mutable.Buffer[SingleDenotation]) => Unit)(using Context): Seq[SingleDenotation] = {
val buf = mutable.ListBuffer[SingleDenotation]()
for (name <- memberNames(keepOnly)) f(name, buf)
buf.toList
}
/** The set of abstract term members of this type. */
final def abstractTermMembers(using Context): Seq[SingleDenotation] = {
record("abstractTermMembers")
memberDenots(abstractTermNameFilter,
(name, buf) => buf ++= nonPrivateMember(name).altsWith(_.is(Deferred)))
}
/**
* Returns the set of methods that are abstract and do not overlap with any of
* [[java.lang.Object]] methods.
*
* Conceptually, a SAM (functional interface) has exactly one abstract method.
* If an interface declares an abstract method overriding one of the public
* methods of [[java.lang.Object]], that also does not count toward the interface's
* abstract method count.
*
* @see https://docs.oracle.com/javase/8/docs/api/java/lang/FunctionalInterface.html
*
* @return the set of methods that are abstract and do not match any of [[java.lang.Object]]
*
*/
final def possibleSamMethods(using Context): Seq[SingleDenotation] = {
record("possibleSamMethods")
atPhaseNoLater(erasurePhase) {
abstractTermMembers.toList.filterConserve(m =>
!m.symbol.matchingMember(defn.ObjectType).exists && !m.symbol.isSuperAccessor)
}.map(_.current)
}
/** The set of abstract type members of this type. */
final def abstractTypeMembers(using Context): Seq[SingleDenotation] = {
record("abstractTypeMembers")
memberDenots(abstractTypeNameFilter,
(name, buf) => buf += nonPrivateMember(name).asSingleDenotation)
}
/** The set of abstract type members of this type. */
final def nonClassTypeMembers(using Context): Seq[SingleDenotation] = {
record("nonClassTypeMembers")
memberDenots(nonClassTypeNameFilter,
(name, buf) => buf += member(name).asSingleDenotation)
}
/** The set of type alias members of this type */
final def typeAliasMembers(using Context): Seq[SingleDenotation] = {
record("typeAliasMembers")
memberDenots(typeAliasNameFilter,
(name, buf) => buf += member(name).asSingleDenotation)
}
/** The set of type members of this type */
final def typeMembers(using Context): Seq[SingleDenotation] = {
record("typeMembers")
memberDenots(typeNameFilter,
(name, buf) => buf += member(name).asSingleDenotation)
}
/** The set of implicit term members of this type
* @param kind A subset of {Implicit, Given} that specifies what kind of implicit should
* be returned
*/
final def implicitMembers(using Context): List[TermRef] = {
record("implicitMembers")
memberDenots(implicitFilter,
(name, buf) => buf ++= member(name).altsWith(_.isOneOf(GivenOrImplicitVal)))
.toList.map(d => TermRef(this, d.symbol.asTerm))
}
/** The set of member classes of this type */
final def memberClasses(using Context): Seq[SingleDenotation] = {
record("memberClasses")
memberDenots(typeNameFilter,
(name, buf) => buf ++= member(name).altsWith(x => x.isClass))