@@ -58,7 +58,7 @@ object Checking {
58
58
/** A type map which checks that the only cycles in a type are F-bounds
59
59
* and that protects all F-bounded references by LazyRefs.
60
60
*/
61
- class CheckNonCyclicMap (implicit ctx : Context ) extends TypeMap {
61
+ class CheckNonCyclicMap (sym : Symbol , reportErrors : Boolean )( implicit ctx : Context ) extends TypeMap {
62
62
63
63
/** Are cycles allowed within nested refinedInfos of currently checked type? */
64
64
private var nestedCycleOK = false
@@ -72,22 +72,32 @@ object Checking {
72
72
*/
73
73
var where : String = " "
74
74
75
+ /** The last type top-level type checked when a CyclicReference occurs. */
76
+ var lastChecked : Type = NoType
77
+
75
78
/** Check info `tp` for cycles. Throw CyclicReference for illegal cycles,
76
79
* break direct cycle with a LazyRef for legal, F-bounded cycles.
77
80
*/
78
81
def checkInfo (tp : Type ): Type = tp match {
79
82
case tp @ TypeBounds (lo, hi) =>
80
83
if (lo eq hi)
81
84
try tp.derivedTypeAlias(apply(lo))
82
- finally where = " alias"
85
+ finally {
86
+ where = " alias"
87
+ lastChecked = lo
88
+ }
83
89
else {
84
- val lo1 = try apply(lo) finally where = " lower bound"
90
+ val lo1 = try apply(lo) finally {
91
+ where = " lower bound"
92
+ lastChecked = lo
93
+ }
85
94
val saved = nestedCycleOK
86
95
nestedCycleOK = true
87
96
try tp.derivedTypeBounds(lo1, apply(hi))
88
97
finally {
89
98
nestedCycleOK = saved
90
99
where = " upper bound"
100
+ lastChecked = hi
91
101
}
92
102
}
93
103
case _ =>
@@ -103,44 +113,66 @@ object Checking {
103
113
finally cycleOK = saved
104
114
case tp @ TypeRef (pre, name) =>
105
115
try {
106
- // Check info of typeref recursively, marking the referred symbol
116
+ // A prefix is interesting if it might contain (transitively) a reference
117
+ // to symbol `sym` itself. We only check references with interesting
118
+ // prefixes for cycles. This pruning is done in order not to force
119
+ // global symbols when doing the cyclicity check.
120
+ def isInteresting (prefix : Type ): Boolean = prefix.stripTypeVar match {
121
+ case NoPrefix => true
122
+ case ThisType (cls) => sym.owner.isClass && cls.isContainedIn(sym.owner)
123
+ case prefix : NamedType => ! prefix.symbol.isStaticOwner && isInteresting(prefix.prefix)
124
+ case SuperType (thistp, _) => isInteresting(thistp)
125
+ case AndType (tp1, tp2) => isInteresting(tp1) || isInteresting(tp2)
126
+ case OrType (tp1, tp2) => isInteresting(tp1) && isInteresting(tp2)
127
+ case _ => false
128
+ }
129
+ // If prefix is interesting, check info of typeref recursively, marking the referred symbol
107
130
// with NoCompleter. This provokes a CyclicReference when the symbol
108
131
// is hit again. Without this precaution we could stackoverflow here.
109
- val info = tp.info
110
- val symInfo = tp.symbol.info
111
- if (tp.symbol.exists) tp.symbol.info = SymDenotations .NoCompleter
112
- try checkInfo(info)
113
- finally if (tp.symbol.exists) tp.symbol.info = symInfo
132
+ if (isInteresting(pre)) {
133
+ val info = tp.info
134
+ val symInfo = tp.symbol.info
135
+ if (tp.symbol.exists) tp.symbol.info = SymDenotations .NoCompleter
136
+ try checkInfo(info)
137
+ finally if (tp.symbol.exists) tp.symbol.info = symInfo
138
+ }
114
139
tp
115
140
} catch {
116
141
case ex : CyclicReference =>
117
142
ctx.debuglog(i " cycle detected for $tp, $nestedCycleOK, $cycleOK" )
118
- if (cycleOK) LazyRef (() => tp) else throw ex
143
+ if (cycleOK) LazyRef (() => tp)
144
+ else if (reportErrors) throw ex
145
+ else tp
119
146
}
120
147
case _ => mapOver(tp)
121
148
}
122
149
}
123
- }
124
-
125
- trait Checking {
126
-
127
- import tpd ._
128
- import Checking ._
129
150
130
151
/** Check that `info` of symbol `sym` is not cyclic.
131
152
* @pre sym is not yet initialized (i.e. its type is a Completer).
132
153
* @return `info` where every legal F-bounded reference is proctected
133
154
* by a `LazyRef`, or `ErrorType` if a cycle was detected and reported.
134
155
*/
135
- def checkNonCyclic (sym : Symbol , info : TypeBounds )(implicit ctx : Context ): Type = {
136
- val checker = new CheckNonCyclicMap
156
+ def checkNonCyclic (sym : Symbol , info : Type , reportErrors : Boolean )(implicit ctx : Context ): Type = {
157
+ val checker = new CheckNonCyclicMap (sym, reportErrors)(ctx.withMode( Mode . CheckCyclic ))
137
158
try checker.checkInfo(info)
138
159
catch {
139
160
case ex : CyclicReference =>
140
- ctx.error(i " illegal cyclic reference: ${checker.where} $info of $sym refers back to the type itself " , sym.pos)
141
- ErrorType
142
- }
161
+ if (reportErrors) {
162
+ ctx.error(i " illegal cyclic reference: ${checker.where} ${checker.lastChecked} of $sym refers back to the type itself " , sym.pos)
163
+ ErrorType
164
+ }
165
+ else info
166
+ }
143
167
}
168
+ }
169
+
170
+ trait Checking {
171
+
172
+ import tpd ._
173
+
174
+ def checkNonCyclic (sym : Symbol , info : TypeBounds , reportErrors : Boolean )(implicit ctx : Context ): Type =
175
+ Checking .checkNonCyclic(sym, info, reportErrors)
144
176
145
177
/** Check that Java statics and packages can only be used in selections.
146
178
*/
@@ -252,6 +284,7 @@ trait Checking {
252
284
253
285
trait NoChecking extends Checking {
254
286
import tpd ._
287
+ override def checkNonCyclic (sym : Symbol , info : TypeBounds , reportErrors : Boolean )(implicit ctx : Context ): Type = info
255
288
override def checkValue (tree : Tree , proto : Type )(implicit ctx : Context ): tree.type = tree
256
289
override def checkBounds (args : List [tpd.Tree ], poly : PolyType , pos : Position )(implicit ctx : Context ): Unit = ()
257
290
override def checkStable (tp : Type , pos : Position )(implicit ctx : Context ): Unit = ()
0 commit comments