Skip to content

Commit 8c5b00d

Browse files
authored
Fix cyclic check, regardless of definition order (#22342)
2 parents 3f2365d + 2bdf8dd commit 8c5b00d

File tree

9 files changed

+177
-16
lines changed

9 files changed

+177
-16
lines changed

Diff for: compiler/src/dotty/tools/dotc/typer/Checking.scala

+25-7
Original file line numberDiff line numberDiff line change
@@ -287,11 +287,16 @@ object Checking {
287287
*/
288288
def checkInfo(tp: Type): Type = tp match {
289289
case tp @ TypeAlias(alias) =>
290-
tp.derivedAlias(checkPart(alias, "alias"))
290+
val lo1 = atVariance(-1)(checkPart(alias, "alias"))
291+
val hi1 = checkUpper(alias, "alias")
292+
if lo1 eq hi1 then
293+
tp.derivedAlias(lo1)
294+
else
295+
tp.derivedTypeBounds(lo1, hi1)
291296
case tp @ MatchAlias(alias) =>
292-
tp.derivedAlias(checkUpper(alias, "match"))
297+
tp.derivedAlias(atVariance(0)(checkUpper(alias, "match")))
293298
case tp @ TypeBounds(lo, hi) =>
294-
tp.derivedTypeBounds(checkPart(lo, "lower bound"), checkUpper(hi, "upper bound"))
299+
tp.derivedTypeBounds(atVariance(-1)(checkPart(lo, "lower bound")), checkUpper(hi, "upper bound"))
295300
case _ =>
296301
tp
297302
}
@@ -312,12 +317,12 @@ object Checking {
312317
case tp: TermRef =>
313318
this(tp.info)
314319
mapOver(tp)
315-
case tp @ AppliedType(tycon, args) =>
316-
tp.derivedAppliedType(this(tycon), args.mapConserve(this(_, nestedCycleOK, nestedCycleOK)))
317320
case tp @ RefinedType(parent, name, rinfo) =>
318321
tp.derivedRefinedType(this(parent), name, this(rinfo, nestedCycleOK, nestedCycleOK))
319322
case tp: RecType =>
320323
tp.rebind(this(tp.parent))
324+
case tp: LazyRef =>
325+
tp
321326
case tp @ TypeRef(pre, _) =>
322327
try {
323328
// A prefix is interesting if it might contain (transitively) a reference
@@ -350,14 +355,17 @@ object Checking {
350355

351356
if isInteresting(pre) then
352357
CyclicReference.trace(i"explore ${tp.symbol} for cyclic references"):
353-
val pre1 = this(pre, false, false)
358+
val pre1 = atVariance(variance max 0)(this(pre, false, false))
354359
if locked.contains(tp)
355360
|| tp.symbol.infoOrCompleter.isInstanceOf[NoCompleter]
361+
&& tp.symbol == sym
356362
then
357363
throw CyclicReference(tp.symbol)
358364
locked += tp
359365
try
360-
if tp.symbol.isOpaqueAlias then
366+
if tp.symbol.infoOrCompleter.isInstanceOf[NoCompleter] then
367+
; // skip checking info (and avoid forcing the symbol with .isOpaqueAlias/etc)
368+
else if tp.symbol.isOpaqueAlias then
361369
checkInfo(TypeAlias(tp.translucentSuperType))
362370
else if !tp.symbol.isClass then
363371
checkInfo(tp.info)
@@ -375,6 +383,16 @@ object Checking {
375383
}
376384
case _ => mapOver(tp)
377385
}
386+
387+
override def mapArg(arg: Type, tparam: ParamInfo): Type =
388+
val varianceDiff = variance != tparam.paramVarianceSign
389+
atVariance(variance * tparam.paramVarianceSign):
390+
// Using tests/pos/i22257.scala as an example,
391+
// if we consider FP's lower-bound of Fixed[Node]
392+
// than `Node` is a type argument in contravariant
393+
// position, while the type parameter is covariant.
394+
val nestedCycleOK1 = nestedCycleOK || variance != 0 && varianceDiff
395+
this(arg, nestedCycleOK, nestedCycleOK1)
378396
}
379397

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

Diff for: 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 }

Diff for: 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 }

Diff for: tests/neg/mt-deskolemize-2.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ trait Description:
1919
type Elem <: Tuple
2020

2121
class PrimBroken extends Expr:
22-
type Value = Alias
23-
type Alias = Value // error
22+
type Value = Alias // error
23+
type Alias = Value
2424

2525
class Prim extends Expr:
2626
type Value = BigInt

Diff for: tests/neg/toplevel-cyclic/defs_1.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
type A = B
1+
type A = B // error: recursion limit exceeded

Diff for: tests/neg/toplevel-cyclic/moredefs_1.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
type B = A // error: recursion limit exceeded
1+
type B = A

Diff for: 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+
}

Diff for: 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+
}

Diff for: 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)