Skip to content

Commit 6fa34a2

Browse files
committed
Fix cyclic check, regardless of definition order
[Cherry-picked 2bdf8dd][modified]
1 parent 75450da commit 6fa34a2

File tree

8 files changed

+175
-14
lines changed

8 files changed

+175
-14
lines changed

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

+25-7
Original file line numberDiff line numberDiff line change
@@ -274,11 +274,16 @@ object Checking {
274274
*/
275275
def checkInfo(tp: Type): Type = tp match {
276276
case tp @ TypeAlias(alias) =>
277-
tp.derivedAlias(checkPart(alias, "alias"))
277+
val lo1 = atVariance(-1)(checkPart(alias, "alias"))
278+
val hi1 = checkUpper(alias, "alias")
279+
if lo1 eq hi1 then
280+
tp.derivedAlias(lo1)
281+
else
282+
tp.derivedTypeBounds(lo1, hi1)
278283
case tp @ MatchAlias(alias) =>
279-
tp.derivedAlias(checkUpper(alias, "match"))
284+
tp.derivedAlias(atVariance(0)(checkUpper(alias, "match")))
280285
case tp @ TypeBounds(lo, hi) =>
281-
tp.derivedTypeBounds(checkPart(lo, "lower bound"), checkUpper(hi, "upper bound"))
286+
tp.derivedTypeBounds(atVariance(-1)(checkPart(lo, "lower bound")), checkUpper(hi, "upper bound"))
282287
case _ =>
283288
tp
284289
}
@@ -299,12 +304,12 @@ object Checking {
299304
case tp: TermRef =>
300305
this(tp.info)
301306
mapOver(tp)
302-
case tp @ AppliedType(tycon, args) =>
303-
tp.derivedAppliedType(this(tycon), args.mapConserve(this(_, nestedCycleOK, nestedCycleOK)))
304307
case tp @ RefinedType(parent, name, rinfo) =>
305308
tp.derivedRefinedType(this(parent), name, this(rinfo, nestedCycleOK, nestedCycleOK))
306309
case tp: RecType =>
307310
tp.rebind(this(tp.parent))
311+
case tp: LazyRef =>
312+
tp
308313
case tp @ TypeRef(pre, _) =>
309314
try {
310315
// A prefix is interesting if it might contain (transitively) a reference
@@ -337,14 +342,17 @@ object Checking {
337342

338343
if isInteresting(pre) then
339344
CyclicReference.trace(i"explore ${tp.symbol} for cyclic references"):
340-
val pre1 = this(pre, false, false)
345+
val pre1 = atVariance(variance max 0)(this(pre, false, false))
341346
if locked.contains(tp)
342347
|| tp.symbol.infoOrCompleter.isInstanceOf[NoCompleter]
348+
&& tp.symbol == sym
343349
then
344350
throw CyclicReference(tp.symbol)
345351
locked += tp
346352
try
347-
if tp.symbol.isOpaqueAlias then
353+
if tp.symbol.infoOrCompleter.isInstanceOf[NoCompleter] then
354+
; // skip checking info (and avoid forcing the symbol with .isOpaqueAlias/etc)
355+
else if tp.symbol.isOpaqueAlias then
348356
checkInfo(TypeAlias(tp.translucentSuperType))
349357
else if !tp.symbol.isClass then
350358
checkInfo(tp.info)
@@ -362,6 +370,16 @@ object Checking {
362370
}
363371
case _ => mapOver(tp)
364372
}
373+
374+
override def mapArg(arg: Type, tparam: ParamInfo): Type =
375+
val varianceDiff = variance != tparam.paramVarianceSign
376+
atVariance(variance * tparam.paramVarianceSign):
377+
// Using tests/pos/i22257.scala as an example,
378+
// if we consider FP's lower-bound of Fixed[Node]
379+
// than `Node` is a type argument in contravariant
380+
// position, while the type parameter is covariant.
381+
val nestedCycleOK1 = nestedCycleOK || variance != 0 && varianceDiff
382+
this(arg, nestedCycleOK, nestedCycleOK1)
365383
}
366384

367385
/** Under -Yrequire-targetName, if `sym` has an operator name, check that it has a

tests/neg/i4368.scala

+3-3
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ object Test6 {
9898

9999
object Test7 {
100100
class Fix[F[_]] {
101-
class Foo { type R >: F[T] <: F[T] } // error: cyclic
101+
class Foo { type R >: F[T] <: F[T] } // error
102102
type T = F[Foo#R]
103103
}
104104

@@ -149,9 +149,9 @@ object Test9 {
149149
object i4369 {
150150
trait X { self =>
151151
type R <: Z
152-
type Z >: X { type R = self.R; type Z = self.R } // error: cyclic // error: cyclic // error: cyclic
152+
type Z >: X { type R = self.R; type Z = self.R } // error: cyclic
153153
}
154-
class Foo extends X { type R = Foo; type Z = Foo }
154+
class Foo extends X { type R = Foo; type Z = Foo } // error
155155
}
156156
object i4370 {
157157
class Foo { type R = A }

tests/neg/i4369c.scala

+15-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
trait X { self =>
22
type R <: Z
3-
type Z >: X { type R = self.R; type Z = self.R } // error // error // error
3+
type Z >:
4+
X { // error
5+
type R = // was-error
6+
self.R
7+
type Z = // was-error
8+
self.R
9+
}
10+
}
11+
12+
class Foo // error
13+
extends X {
14+
type R =
15+
Foo
16+
type Z =
17+
Foo
418
}
5-
class Foo extends X { type R = Foo; type Z = Foo }
+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
type A = B
1+
type A = B // error: recursion limit exceeded
+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
type B = A // error: recursion limit exceeded
1+
type B = A

tests/pos/i22257.fixed.scala

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
2+
object Scaffold {
3+
4+
trait Arrow
5+
object Arrow {
6+
trait Outbound extends Arrow
7+
}
8+
9+
trait NodeKOrGraphK {}
10+
11+
trait NodeK extends NodeKOrGraphK {
12+
13+
type FBound <: Induction
14+
15+
protected def getInduction: Seq[FBound]
16+
}
17+
18+
trait Induction {
19+
def arrow: Arrow
20+
def node: NodeK
21+
}
22+
23+
object Induction {
24+
25+
trait FP[+N <: NodeK] extends Induction { // short for "fixed point"
26+
def node: N
27+
}
28+
}
29+
30+
trait GraphK extends NodeKOrGraphK {
31+
32+
type Batch[+T] <: Iterable[T]
33+
34+
type _Node <: NodeK
35+
36+
def entries: Batch[_Node]
37+
}
38+
39+
trait Topology {
40+
41+
type Node = NodeK { type FBound <: Topology.this.FBound }
42+
trait Node_ extends NodeK {
43+
type FBound = Topology.this.FBound
44+
}
45+
46+
type FP = Induction.FP[Node]
47+
type FBound <: FP
48+
49+
type Graph = GraphK { type _Node <: Node }
50+
}
51+
52+
}

tests/pos/i22257.orig.scala

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
2+
object Scaffold {
3+
4+
trait Arrow
5+
object Arrow {
6+
trait Outbound extends Arrow
7+
}
8+
9+
trait NodeKOrGraphK {}
10+
11+
trait NodeK extends NodeKOrGraphK {
12+
13+
type FBound <: Induction
14+
15+
protected def getInduction: Seq[FBound]
16+
}
17+
18+
trait Induction {
19+
def arrow: Arrow
20+
def node: NodeK
21+
}
22+
23+
object Induction {
24+
25+
trait FP[+N <: NodeK] extends Induction { // short for "fixed point"
26+
def node: N
27+
}
28+
}
29+
30+
trait GraphK extends NodeKOrGraphK {
31+
32+
type Batch[+T] <: Iterable[T]
33+
34+
type _Node <: NodeK
35+
36+
def entries: Batch[_Node]
37+
}
38+
39+
trait Topology {
40+
41+
type FP = Induction.FP[Node]
42+
type FBound <: FP
43+
44+
type Node = NodeK { type FBound <: Topology.this.FBound }
45+
trait Node_ extends NodeK {
46+
type FBound = Topology.this.FBound
47+
}
48+
49+
type Graph = GraphK { type _Node <: Node }
50+
}
51+
52+
}

tests/pos/i22257.scala

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
trait NodeK { type FBound }
2+
trait Fixed[+N <: NodeK]
3+
4+
type Bound1 <: FP1
5+
type FP1 = Fixed[Node1]
6+
type Node1 = NodeK { type FBound <: Bound1 } // was-error
7+
8+
type FP2 = Fixed[Node2] // was-error
9+
type Bound2 <: FP2
10+
type Node2 = NodeK { type FBound <: Bound2 }
11+
12+
type Node3 = NodeK { type FBound <: Bound3 }
13+
type FP3 = Fixed[Node3]
14+
type Bound3 <: FP3
15+
16+
type Bound4 <: FP4
17+
type Node4 = NodeK { type FBound <: Bound4 } // was-error
18+
type FP4 = Fixed[Node4]
19+
20+
type FP5 = Fixed[Node5] // was-error
21+
type Node5 = NodeK { type FBound <: Bound5 }
22+
type Bound5 <: FP5
23+
24+
type Node6 = NodeK { type FBound <: Bound6 }
25+
type Bound6 <: FP6
26+
type FP6 = Fixed[Node6]

0 commit comments

Comments
 (0)