Not a definite answer, but some observations ...
First, let's see what works:
class C[B <: Foo](val f: B) { val x: fT = fx }
therefore, the compiler loses you when using type projection for the value of f . If you “fix” this projection, it also works:
class D[B <: Bar](val f: B#F) { val g: Foo = f val x: gT = gx }
I struggled with the F-limited types for a long time until I got them "waterproof." There is something about type parameters compared to type members that do the previous work. For instance:
trait FooRec[F <: FooRec[F]] extends Foo { type T = F } class E[F <: FooRec[F]](val f: F) { val x: fT = fx }
Finally, you can also fix a member of type Bar by passing it as a parameter of type:
class G[F1 <: Foo { type T = F1 }, B <: Bar { type F = F1 }](val f: B#F) { val x: fT = fx }
Similarly, if you correct the type already in the Bar definition, it works:
trait Baz { type F <: Foo { type T = F } // stable recursion } class H[B <: Baz](val f: B
So in your original definition and application that has upper bounds, it seems not enough. You can probably prove that the compiler is right about its failure, but I don't know how ...
source share