diff --git a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala index 603d7a3cb0e3..462148a5c758 100644 --- a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala @@ -693,7 +693,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds, def replaceParamIn(other: TypeParamRef) = val oldEntry = current.entry(other) val newEntry = oldEntry.substParam(param, replacement) match - case tp: TypeBounds => validBoundsFor(other, tp) + case tp: TypeBounds => current.validBoundsFor(other, tp) case tp => tp current = boundsLens.update(this, current, other, newEntry) var oldDepEntry = oldEntry diff --git a/compiler/test/dotty/tools/dotc/core/ConstraintsTest.scala b/compiler/test/dotty/tools/dotc/core/ConstraintsTest.scala index ad8578fa3e61..9ae3fda8c6b9 100644 --- a/compiler/test/dotty/tools/dotc/core/ConstraintsTest.scala +++ b/compiler/test/dotty/tools/dotc/core/ConstraintsTest.scala @@ -77,3 +77,17 @@ class ConstraintsTest: assert(lo =:= defn.NothingType, i"Unexpected lower bound $lo for $t: ${ctx.typerState.constraint}") assert(hi =:= (defn.StringType | defn.IntType), i"Unexpected upper bound $hi for $t: ${ctx.typerState.constraint}") } + + @Test def validBoundsReplace: Unit = inCompilerContext( + TestConfiguration.basicClasspath, + scalaSources = "trait X; trait A { def foo[S <: U | X, T, U]: Any }") { + val tvarTrees = constrained(requiredClass("A").typeRef.select("foo".toTermName).info.asInstanceOf[TypeLambda], EmptyTree, alwaysAddTypeVars = true)._2 + val tvars @ List(s, t, u) = tvarTrees.tpes.asInstanceOf[List[TypeVar]] + s =:= t + t =:= u + + for tvar <- tvars do + val entry = ctx.typerState.constraint.entry(tvar.origin) + assert(!ctx.typerState.constraint.occursAtToplevel(tvar.origin, entry), + i"cyclic bound for ${tvar.origin}: ${entry} in ${ctx.typerState.constraint}") + }