Type inference ignores type bounds in some occasion - propose easy fix #5298
Description
Consider the following simplified example, in particular the failure of type inference in ViewNoTypeAnnot.copy
and the success in ViewNoTypeAnnotWorkingInference.copy
:
object BugReportTypeInference {
case class View1[T, Repr <: TraversableLike[T, Repr]](base: Repr) {
def copy(base: Repr): View1[T, Repr] = View1(base)
}
case class ViewNoTypeAnnot[T, Repr <: TraversableLike[T, Repr]](base: Repr)
{
//def copy(base: Repr) = ViewNoTypeAnnot(base) //Does not compile: Nothing is inferred instead of T
def copy(base: Repr) = ViewNoTypeAnnot[T, Repr](base)
}
case class ViewNoTypeAnnotWorkingInference[T, Repr <: TraversableLike[T, Repr]](base: Repr with TraversableLike[T, Repr]) {
def copy(base: Repr) = ViewNoTypeAnnotWorkingInference(base)
}
}
When applying ViewNoTypeAnnot
, T
cannot be inferred because it does not appear directly in the parameter list. Therefore, scalac
infers T = Nothing
, incorrectly, and Repr = Repr@(outer environment)
, correctly, and complains because type bounds are not satisfied.
However, the bounds do provide sufficient information to deduce T = T@(outer environment)
. In ViewNoTypeAnnotWorkingInference
, I intersected Repr
with its upper bound, which should not make any difference; however, since scalac
is happier inferring parameters which do appear in the signature, type inference now succeeds.
I propose to integrate into scalac
the transformation which I performed manually, for the purpose of helping type inference; the resulting types can be discarded after inference, but I'd guess that erasure would remove the "extra information" from bytecode easily - and at least javap
confirms this for the classes considered. I tried this out in a couple of other more complex examples, and the trick seems fairly reliable. The trick cannot be extended to lower bounds.
In particular, it helped in this slightly more complex example, where the type appears as a parameter of Exp
:
trait Exp[+T]
case class View[T, Repr <: TraversableLike[T, Repr]](base: Exp[Repr with TraversableLike[T, Repr]]) {
def copy(base: Exp[Repr]) = View(base)
}
And this also helped with real code I was writing, but which is too complex to post here. All the code above was indeed compiled (assuming I didn't mess up the copy-n-paste).