Sorry for the late answer, my mail notifications seem to have malfunctioned.
I don’t think I’ve seen any use of both <: and >: on the same type parameter. This Stackoverflow post has pretty much the same question as you. The first answer shows how to add an upper and lower bound to a type parameter via an implicit <:<, but also notes, that this can be circumvented easily and accidentaly: A val t: TT1 = new TT3 is obviously legal, but now the compiler sees t as an instance of T1 and will accept it as fulfilling a T1 lower bound. I noticed that the solution I posted above has the same problem, it will compile if you tell the compiler, that tt3 is a TT1: callOnly1(tt3: TT1) compiles.
I don’t remember any changes related to upper and lower bounds in Dotty. As those checks are at compile time, I don’t think it would be possible to guarantee such a lower bound outside the same compilation unit anyways. The static return type of some compiled function is what the compiler sees, and the actual type is determined at runtime. So sadly the only options seem to be runtime reflection or adding an additional marker trait to the subclasses that the function should accept.