diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index bf99def353b0..2757304f32e8 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -274,11 +274,16 @@ object Checking { */ def checkInfo(tp: Type): Type = tp match { case tp @ TypeAlias(alias) => - tp.derivedAlias(checkPart(alias, "alias")) + val lo1 = atVariance(-1)(checkPart(alias, "alias")) + val hi1 = checkUpper(alias, "alias") + if lo1 eq hi1 then + tp.derivedAlias(lo1) + else + tp.derivedTypeBounds(lo1, hi1) case tp @ MatchAlias(alias) => - tp.derivedAlias(checkUpper(alias, "match")) + tp.derivedAlias(atVariance(0)(checkUpper(alias, "match"))) case tp @ TypeBounds(lo, hi) => - tp.derivedTypeBounds(checkPart(lo, "lower bound"), checkUpper(hi, "upper bound")) + tp.derivedTypeBounds(atVariance(-1)(checkPart(lo, "lower bound")), checkUpper(hi, "upper bound")) case _ => tp } @@ -299,12 +304,12 @@ object Checking { case tp: TermRef => this(tp.info) mapOver(tp) - case tp @ AppliedType(tycon, args) => - tp.derivedAppliedType(this(tycon), args.mapConserve(this(_, nestedCycleOK, nestedCycleOK))) case tp @ RefinedType(parent, name, rinfo) => tp.derivedRefinedType(this(parent), name, this(rinfo, nestedCycleOK, nestedCycleOK)) case tp: RecType => tp.rebind(this(tp.parent)) + case tp: LazyRef => + tp case tp @ TypeRef(pre, _) => try { // A prefix is interesting if it might contain (transitively) a reference @@ -337,14 +342,17 @@ object Checking { if isInteresting(pre) then CyclicReference.trace(i"explore ${tp.symbol} for cyclic references"): - val pre1 = this(pre, false, false) + val pre1 = atVariance(variance max 0)(this(pre, false, false)) if locked.contains(tp) || tp.symbol.infoOrCompleter.isInstanceOf[NoCompleter] + && tp.symbol == sym then throw CyclicReference(tp.symbol) locked += tp try - if tp.symbol.isOpaqueAlias then + if tp.symbol.infoOrCompleter.isInstanceOf[NoCompleter] then + ; // skip checking info (and avoid forcing the symbol with .isOpaqueAlias/etc) + else if tp.symbol.isOpaqueAlias then checkInfo(TypeAlias(tp.translucentSuperType)) else if !tp.symbol.isClass then checkInfo(tp.info) @@ -362,6 +370,16 @@ object Checking { } case _ => mapOver(tp) } + + override def mapArg(arg: Type, tparam: ParamInfo): Type = + val varianceDiff = variance != tparam.paramVarianceSign + atVariance(variance * tparam.paramVarianceSign): + // Using tests/pos/i22257.scala as an example, + // if we consider FP's lower-bound of Fixed[Node] + // than `Node` is a type argument in contravariant + // position, while the type parameter is covariant. + val nestedCycleOK1 = nestedCycleOK || variance != 0 && varianceDiff + this(arg, nestedCycleOK, nestedCycleOK1) } /** Under -Yrequire-targetName, if `sym` has an operator name, check that it has a diff --git a/tests/neg/i4368.scala b/tests/neg/i4368.scala index 95c342d5fe2a..6718aece4eba 100644 --- a/tests/neg/i4368.scala +++ b/tests/neg/i4368.scala @@ -98,7 +98,7 @@ object Test6 { object Test7 { class Fix[F[_]] { - class Foo { type R >: F[T] <: F[T] } // error: cyclic + class Foo { type R >: F[T] <: F[T] } // error type T = F[Foo#R] } @@ -149,9 +149,9 @@ object Test9 { object i4369 { trait X { self => type R <: Z - type Z >: X { type R = self.R; type Z = self.R } // error: cyclic // error: cyclic // error: cyclic + type Z >: X { type R = self.R; type Z = self.R } // error: cyclic } - class Foo extends X { type R = Foo; type Z = Foo } + class Foo extends X { type R = Foo; type Z = Foo } // error } object i4370 { class Foo { type R = A } diff --git a/tests/neg/i4369c.scala b/tests/neg/i4369c.scala index a2bfbcb598d6..06a77d51cdbe 100644 --- a/tests/neg/i4369c.scala +++ b/tests/neg/i4369c.scala @@ -1,5 +1,18 @@ trait X { self => type R <: Z - type Z >: X { type R = self.R; type Z = self.R } // error // error // error + type Z >: + X { // error + type R = // was-error + self.R + type Z = // was-error + self.R + } +} + +class Foo // error + extends X { + type R = + Foo + type Z = + Foo } -class Foo extends X { type R = Foo; type Z = Foo } diff --git a/tests/neg/toplevel-cyclic/defs_1.scala b/tests/neg/toplevel-cyclic/defs_1.scala index 34b0475066b0..f40fa5bdebdd 100644 --- a/tests/neg/toplevel-cyclic/defs_1.scala +++ b/tests/neg/toplevel-cyclic/defs_1.scala @@ -1 +1 @@ -type A = B +type A = B // error: recursion limit exceeded diff --git a/tests/neg/toplevel-cyclic/moredefs_1.scala b/tests/neg/toplevel-cyclic/moredefs_1.scala index 3c8c3de93aa7..91360d0c0f4d 100644 --- a/tests/neg/toplevel-cyclic/moredefs_1.scala +++ b/tests/neg/toplevel-cyclic/moredefs_1.scala @@ -1 +1 @@ -type B = A // error: recursion limit exceeded +type B = A diff --git a/tests/pos/i22257.fixed.scala b/tests/pos/i22257.fixed.scala new file mode 100644 index 000000000000..d0fa1a0e81cb --- /dev/null +++ b/tests/pos/i22257.fixed.scala @@ -0,0 +1,52 @@ + +object Scaffold { + + trait Arrow + object Arrow { + trait Outbound extends Arrow + } + + trait NodeKOrGraphK {} + + trait NodeK extends NodeKOrGraphK { + + type FBound <: Induction + + protected def getInduction: Seq[FBound] + } + + trait Induction { + def arrow: Arrow + def node: NodeK + } + + object Induction { + + trait FP[+N <: NodeK] extends Induction { // short for "fixed point" + def node: N + } + } + + trait GraphK extends NodeKOrGraphK { + + type Batch[+T] <: Iterable[T] + + type _Node <: NodeK + + def entries: Batch[_Node] + } + + trait Topology { + + type Node = NodeK { type FBound <: Topology.this.FBound } + trait Node_ extends NodeK { + type FBound = Topology.this.FBound + } + + type FP = Induction.FP[Node] + type FBound <: FP + + type Graph = GraphK { type _Node <: Node } + } + +} diff --git a/tests/pos/i22257.orig.scala b/tests/pos/i22257.orig.scala new file mode 100644 index 000000000000..0be8d02d5a85 --- /dev/null +++ b/tests/pos/i22257.orig.scala @@ -0,0 +1,52 @@ + +object Scaffold { + + trait Arrow + object Arrow { + trait Outbound extends Arrow + } + + trait NodeKOrGraphK {} + + trait NodeK extends NodeKOrGraphK { + + type FBound <: Induction + + protected def getInduction: Seq[FBound] + } + + trait Induction { + def arrow: Arrow + def node: NodeK + } + + object Induction { + + trait FP[+N <: NodeK] extends Induction { // short for "fixed point" + def node: N + } + } + + trait GraphK extends NodeKOrGraphK { + + type Batch[+T] <: Iterable[T] + + type _Node <: NodeK + + def entries: Batch[_Node] + } + + trait Topology { + + type FP = Induction.FP[Node] + type FBound <: FP + + type Node = NodeK { type FBound <: Topology.this.FBound } + trait Node_ extends NodeK { + type FBound = Topology.this.FBound + } + + type Graph = GraphK { type _Node <: Node } + } + +} diff --git a/tests/pos/i22257.scala b/tests/pos/i22257.scala new file mode 100644 index 000000000000..8cd797529097 --- /dev/null +++ b/tests/pos/i22257.scala @@ -0,0 +1,26 @@ +trait NodeK { type FBound } +trait Fixed[+N <: NodeK] + +type Bound1 <: FP1 +type FP1 = Fixed[Node1] +type Node1 = NodeK { type FBound <: Bound1 } // was-error + +type FP2 = Fixed[Node2] // was-error +type Bound2 <: FP2 +type Node2 = NodeK { type FBound <: Bound2 } + +type Node3 = NodeK { type FBound <: Bound3 } +type FP3 = Fixed[Node3] +type Bound3 <: FP3 + +type Bound4 <: FP4 +type Node4 = NodeK { type FBound <: Bound4 } // was-error +type FP4 = Fixed[Node4] + +type FP5 = Fixed[Node5] // was-error +type Node5 = NodeK { type FBound <: Bound5 } +type Bound5 <: FP5 + +type Node6 = NodeK { type FBound <: Bound6 } +type Bound6 <: FP6 +type FP6 = Fixed[Node6]