@@ -58,7 +58,7 @@ object Checking {
5858 /** A type map which checks that the only cycles in a type are F-bounds
5959 * and that protects all F-bounded references by LazyRefs.
6060 */
61- class CheckNonCyclicMap (implicit ctx : Context ) extends TypeMap {
61+ class CheckNonCyclicMap (sym : Symbol , reportErrors : Boolean )( implicit ctx : Context ) extends TypeMap {
6262
6363 /** Are cycles allowed within nested refinedInfos of currently checked type? */
6464 private var nestedCycleOK = false
@@ -72,22 +72,32 @@ object Checking {
7272 */
7373 var where : String = " "
7474
75+ /** The last type top-level type checked when a CyclicReference occurs. */
76+ var lastChecked : Type = NoType
77+
7578 /** Check info `tp` for cycles. Throw CyclicReference for illegal cycles,
7679 * break direct cycle with a LazyRef for legal, F-bounded cycles.
7780 */
7881 def checkInfo (tp : Type ): Type = tp match {
7982 case tp @ TypeBounds (lo, hi) =>
8083 if (lo eq hi)
8184 try tp.derivedTypeAlias(apply(lo))
82- finally where = " alias"
85+ finally {
86+ where = " alias"
87+ lastChecked = lo
88+ }
8389 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+ }
8594 val saved = nestedCycleOK
8695 nestedCycleOK = true
8796 try tp.derivedTypeBounds(lo1, apply(hi))
8897 finally {
8998 nestedCycleOK = saved
9099 where = " upper bound"
100+ lastChecked = hi
91101 }
92102 }
93103 case _ =>
@@ -103,44 +113,66 @@ object Checking {
103113 finally cycleOK = saved
104114 case tp @ TypeRef (pre, name) =>
105115 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
107130 // with NoCompleter. This provokes a CyclicReference when the symbol
108131 // 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+ }
114139 tp
115140 } catch {
116141 case ex : CyclicReference =>
117142 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
119146 }
120147 case _ => mapOver(tp)
121148 }
122149 }
123- }
124-
125- trait Checking {
126-
127- import tpd ._
128- import Checking ._
129150
130151 /** Check that `info` of symbol `sym` is not cyclic.
131152 * @pre sym is not yet initialized (i.e. its type is a Completer).
132153 * @return `info` where every legal F-bounded reference is proctected
133154 * by a `LazyRef`, or `ErrorType` if a cycle was detected and reported.
134155 */
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 ))
137158 try checker.checkInfo(info)
138159 catch {
139160 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+ }
143167 }
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)
144176
145177 /** Check that Java statics and packages can only be used in selections.
146178 */
@@ -252,6 +284,7 @@ trait Checking {
252284
253285trait NoChecking extends Checking {
254286 import tpd ._
287+ override def checkNonCyclic (sym : Symbol , info : TypeBounds , reportErrors : Boolean )(implicit ctx : Context ): Type = info
255288 override def checkValue (tree : Tree , proto : Type )(implicit ctx : Context ): tree.type = tree
256289 override def checkBounds (args : List [tpd.Tree ], poly : PolyType , pos : Position )(implicit ctx : Context ): Unit = ()
257290 override def checkStable (tp : Type , pos : Position )(implicit ctx : Context ): Unit = ()
0 commit comments