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
|
package dotty.tools.dotc.core
import Contexts._, Types._, Symbols._, Names._
trait TypeOps { this: Context =>
final def asSeenFrom(tp: Type, pre: Type, clazz: Symbol, theMap: AsSeenFromMap): Type = {
def skipPrefixOf(pre: Type, clazz: Symbol) =
(pre eq NoType) || (pre eq NoPrefix) || clazz.isPackageClass
def toPrefix(pre: Type, clazz: Symbol, thisclazz: ClassSymbol): Type =
if (skipPrefixOf(pre, clazz))
tp
else if ((thisclazz isNonBottomSubClass clazz) &&
(pre.widen.typeSymbol isNonBottomSubClass thisclazz))
pre match {
case SuperType(thispre, _) => thispre
case _ => pre
}
else
toPrefix(pre.baseType(clazz).normalizedPrefix, clazz.owner, thisclazz)
tp match {
case tp: NamedType =>
val sym = tp.symbol
if (sym.isStatic) tp
else {
val pre0 = tp.prefix
val pre1 = asSeenFrom(pre0, pre, clazz, theMap)
if (pre1 eq pre0) tp
else {
val tp1 = NamedType(pre1, tp.name)
if (sym.isTypeParameter) {
// short-circuit instantiated type parameters
// by replacing pre.tp with its alias, if it has one.
val tp2 = tp1.info
if (tp2.isAliasTypeBounds) return tp2.bounds.hi
}
tp1
}
}
case ThisType(thisclazz) =>
toPrefix(pre, clazz, thisclazz)
case _: BoundType | NoPrefix =>
tp
case tp: RefinedType1 =>
tp.derivedRefinedType1(
asSeenFrom(tp.parent, pre, clazz, theMap),
tp.name1,
asSeenFrom(tp.info1, pre, clazz, theMap))
case tp: RefinedType2 =>
tp.derivedRefinedType2(
asSeenFrom(tp.parent, pre, clazz, theMap),
tp.name1,
asSeenFrom(tp.info1, pre, clazz, theMap),
tp.name2,
asSeenFrom(tp.info2, pre, clazz, theMap))
case _ =>
(if (theMap != null) theMap else new AsSeenFromMap(pre, clazz))
.mapOver(tp)
}
}
class AsSeenFromMap(pre: Type, clazz: Symbol) extends TypeMap {
def apply(tp: Type) = asSeenFrom(tp, pre, clazz, this)
}
final def isVolatile(tp: Type): Boolean = {
def isAbstractIntersection(tp: Type): Boolean = tp match {
case tp: TypeRef => tp.isAbstractType
case AndType(l, r) => isAbstractIntersection(l) | isAbstractIntersection(l)
case OrType(l, r) => isAbstractIntersection(l) & isAbstractIntersection(r)
case _ => false
}
def containsName(names: Set[Name], tp: RefinedType): Boolean = tp match {
case tp: RefinedType1 => names contains tp.name1
case tp: RefinedType2 => (names contains tp.name1) || (names contains tp.name2)
case _ => tp.names exists (names contains)
}
def test = {
tp match {
case ThisType(_) =>
false
case tp: RefinedType =>
tp.parent.isVolatile ||
isAbstractIntersection(tp.parent) &&
containsName(tp.abstractMemberNames(tp), tp)
case tp: TypeProxy =>
tp.underlying.isVolatile
case AndType(l, r) =>
l.isVolatile || r.isVolatile ||
isAbstractIntersection(l) && r.abstractMemberNames(tp).nonEmpty
case OrType(l, r) =>
l.isVolatile && r.isVolatile
case _ =>
false
}
}
// need to be careful not to fall into an infinite recursion here
// because volatile checking is done before all cycles are detected.
// the case to avoid is an abstract type directly or
// indirectly upper-bounded by itself. See #2918
import ctx.root.{ volatileRecursions, pendingVolatiles }
try {
volatileRecursions += 1
if (volatileRecursions < LogVolatileThreshold)
test
else if (pendingVolatiles(tp))
false // we can return false here, because a cycle will be detected
// here afterwards and an error will result anyway.
else
try {
pendingVolatiles += tp
test
} finally {
pendingVolatiles -= tp
}
} finally {
volatileRecursions -= 1
}
}
final def glb(tp1: Type, tp2: Type): Type =
if (tp1 eq tp2) tp1
else if (tp1.isWrong) tp2
else if (tp2.isWrong) tp1
else tp2 match {
case OrType(tp21, tp22) =>
tp1 & tp21 | tp1 & tp22
case _ =>
tp1 match {
case OrType(tp11, tp12) =>
tp11 & tp2 | tp12 & tp2
case _ =>
val t1 = mergeIfSub(tp1, tp2)
if (t1.exists) t1
else {
val t2 = mergeIfSub(tp2, tp1)
if (t2.exists) t2
else AndType(tp1, tp2)
}
}
}
def lub(tp1: Type, tp2: Type): Type =
if (tp1 eq tp2) tp1
else if (tp1.isWrong) tp1
else if (tp2.isWrong) tp2
else {
val t1 = mergeIfSuper(tp1, tp2)
if (t1.exists) t1
else {
val t2 = mergeIfSuper(tp2, tp1)
if (t2.exists) t2
else OrType(tp1, tp2)
}
}
/** Merge `t1` into `tp2` if t1 is a subtype of some part of tp2.
*/
private def mergeIfSub(tp1: Type, tp2: Type)(implicit ctx: Context): Type =
if (tp1 <:< tp2)
if (tp2 <:< tp1) tp2 else tp1
else tp2 match {
case tp2 @ AndType(tp21, tp22) =>
val lower1 = mergeIfSub(tp1, tp21)
if (lower1 eq tp21) tp2
else if (lower1.exists) lower1 & tp22
else {
val lower2 = mergeIfSub(tp1, tp22)
if (lower2 eq tp22) tp2
else if (lower2.exists) tp21 & lower2
else NoType
}
case _ =>
NoType
}
/** Merge `tp1` into `tp2` if tp1 is a supertype of some part of tp2.
*/
private def mergeIfSuper(tp1: Type, tp2: Type)(implicit ctx: Context): Type =
if (tp2 <:< tp1)
if (tp1 <:< tp2) tp2 else tp1
else tp2 match {
case tp2 @ OrType(tp21, tp22) =>
val higher1 = mergeIfSuper(tp1, tp21)
if (higher1 eq tp21) tp2
else if (higher1.exists) higher1 | tp22
else {
val higher2 = mergeIfSuper(tp1, tp22)
if (higher2 eq tp22) tp2
else if (higher2.exists) tp21 | higher2
else NoType
}
case _ =>
NoType
}
}
|