Skip to content

Commit 3f231ab

Browse files
committed
Fixes to checkNonCyclic
Simplified logic and now check prefixes of TypeRefs. Without the simplified logic we would get false cyclic errors for ski.scala. Test case: flowops.scala Fixes #1185.
1 parent 1b29119 commit 3f231ab

File tree

2 files changed

+49
-20
lines changed

2 files changed

+49
-20
lines changed

src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -156,16 +156,24 @@ object Checking {
156156
tp
157157
}
158158

159-
def apply(tp: Type) = tp match {
159+
private def apply(tp: Type, cycleOK: Boolean, nestedCycleOK: Boolean): Type = {
160+
val savedCycleOK = this.cycleOK
161+
val savedNestedCycleOK = this.nestedCycleOK
162+
this.cycleOK = cycleOK
163+
this.nestedCycleOK = nestedCycleOK
164+
try apply(tp)
165+
finally {
166+
this.cycleOK = savedCycleOK
167+
this.nestedCycleOK = savedNestedCycleOK
168+
}
169+
}
170+
171+
def apply(tp: Type): Type = tp match {
160172
case tp: TermRef =>
161173
this(tp.info)
162174
mapOver(tp)
163175
case tp @ RefinedType(parent, name) =>
164-
val parent1 = this(parent)
165-
val saved = cycleOK
166-
cycleOK = nestedCycleOK
167-
try tp.derivedRefinedType(parent1, name, this(tp.refinedInfo))
168-
finally cycleOK = saved
176+
tp.derivedRefinedType(this(parent), name, this(tp.refinedInfo, nestedCycleOK, nestedCycleOK))
169177
case tp @ TypeRef(pre, name) =>
170178
try {
171179
// A prefix is interesting if it might contain (transitively) a reference
@@ -182,19 +190,12 @@ object Checking {
182190
case _: RefinedType => true
183191
case _ => false
184192
}
185-
// If prefix is interesting, check info of typeref recursively, marking the referred symbol
186-
// with NoCompleter. This provokes a CyclicReference when the symbol
187-
// is hit again. Without this precaution we could stackoverflow here.
188193
if (isInteresting(pre)) {
189-
val info = tp.info
190-
val sym = tp.symbol
191-
if (sym.infoOrCompleter == SymDenotations.NoCompleter) throw CyclicReference(sym)
192-
val symInfo = sym.info
193-
if (sym.exists) sym.info = SymDenotations.NoCompleter
194-
try checkInfo(info)
195-
finally if (sym.exists) sym.info = symInfo
194+
val pre1 = this(pre, false, false)
195+
checkInfo(tp.info)
196+
if (pre1 eq pre) tp else tp.newLikeThis(pre1)
196197
}
197-
tp
198+
else tp
198199
} catch {
199200
case ex: CyclicReference =>
200201
ctx.debuglog(i"cycle detected for $tp, $nestedCycleOK, $cycleOK")
@@ -210,9 +211,6 @@ object Checking {
210211
* @pre sym is not yet initialized (i.e. its type is a Completer).
211212
* @return `info` where every legal F-bounded reference is proctected
212213
* by a `LazyRef`, or `ErrorType` if a cycle was detected and reported.
213-
* Furthermore: Add an #Apply to a fully instantiated type lambda, if none was
214-
* given before. This is necessary here because sometimes type lambdas are not
215-
* recognized when they are first formed.
216214
*/
217215
def checkNonCyclic(sym: Symbol, info: Type, reportErrors: Boolean)(implicit ctx: Context): Type = {
218216
val checker = new CheckNonCyclicMap(sym, reportErrors)(ctx.addMode(Mode.CheckCyclic))

tests/pos/flowops.scala

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
object Test {
2+
import language.higherKinds
3+
4+
class NotUsed
5+
6+
trait FO[+Out, +Mat] { self =>
7+
type Repr[+O] <: FO[O, Mat] {
8+
type Repr[+OO] = self.Repr[OO]
9+
}
10+
def map[T](f: Out => T): Repr[T] = ???
11+
}
12+
13+
class Source[+O, +M] extends FO[O, M] {
14+
type Repr[+OO] <: Source[OO, M]
15+
}
16+
17+
class Flow[-I, +O, +M] extends FO[O, M] {
18+
type Repr[+OO] <: Flow[I, OO, M]
19+
}
20+
21+
implicit class x[O, M, F[o, m] <: FO[o, m]](val f: F[O, M]) extends AnyVal {
22+
def xx(i: Int): f.Repr[O] = f.map(identity)
23+
}
24+
25+
type IntFlow[O, M] = Flow[Int, O, M]
26+
27+
val s1 = new Source[Int, NotUsed].xx(12)
28+
val s2: Source[Int, NotUsed] = s1
29+
val f1 = x[Int, NotUsed, IntFlow](new Flow[Int, Int, NotUsed]).xx(12)
30+
val f2: Flow[Int, Int, NotUsed] = f1
31+
}

0 commit comments

Comments
 (0)