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.