First, notice the circular F‑bound” in the definitions/
X is parameterized by T and U, with the constraints
- T⊆X[T,U]
- U⊆Z[T,U]
Y is parameterized by the same pair (T,U)( but again requires
- T⊆X[T,U]
- U⊆Z[T,U]
Z also uses the same pair (T,U) and itself requires
- T⊆X[T,U]
- U⊆Z[T,U] and extends Y[T,U].
When we write trait Z[T <: X[T, U], U <: Z[T, U]] extends Y[T, U]
we are telling the compiler that inside Z, the second type parameter U must itself be a subtype of Z[T, U] (the same trait we are defining). But at the same time, Z must extend Y[T, U], which also requires that this very same U be a subtype of Z[T, U].
Put differently, the compiler sees for Z[T, U] to extend Y[T, U], the second type parameter U must satisfy the upper bound required by Y, i.e. U⊆Z[T,U] and U ⊆ Z[T, U].
But U is the type parameter in Z[T, U] that is still being resolved. Scala 3’s type checker is stricter about such forward references or “F‑bound” cycles. Even though it looks consistent (“U should be a subtype of Z[T, U]” is exactly what you wrote), the compiler does not accept that U already “proves” it is a subtype of Z[T, U] merely by being used in the Z header itself.
In simpler terms you are defining trait Z, saying “U is a subtype of Z[T, U].” But you also do extends Y[T, U], so Y sees that “U must be a subtype of Z[T, U]” already. Scala 3 cannot confirm that requirement at the point where the trait is still being defined. It treats this as a circular, self-referential bound and fails with the message “type argument U does not conform to upper bound Z[T, U]”
This is precisely the F‑bounded polymorphism pitfall: you are “F‑bounding” over the same parameters in multiple traits (X, Y, Z) in a way that ends up circular from the compiler’s point of view. In Scala 2, certain self-referential or F‑bounded patterns were accepted more readily, sometimes in ways that weren’t fully safe. Scala 3 tightened the rules and disallows this direct cycle, thus producing the error.