-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Type inference issue: No ClassTag available for Nothing #4742
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
This isn't specific to ClassTag: trait Inv[T]
class Test {
implicit def inv[T]: Inv[T] = ???
def foo[T <: String](f: T => Int)(implicit ev: Inv[T]) = 1
def bar(f: String => Int) = foo(f) // Infer T := Nothing in Dotty, T := String in Scala 2
} When we do the implicit search we need to either pick the lower or upper bound of |
Turns out that More generally we may want to revisit how we do instantiation for the variables present in the type of an implicit parameter, right now the following give different results (rest of the code is in the previous comment): // ...
def bar(f: String => Int) = foo(f)
def bar2(f: String => Int) = foo(f)(implicitly) I can't think of a good rule of thumb that wouldn't have this behavior right now. |
So here's the current rule, more or less: "Before doing an implicit search for a type I, find all type variables that occur in I and that have appeared in the type of a prefix of the current tree, then instantiate them using the normal rules for instantiation", currently the rules are: /** The accumulator which forces type variables using the policy encoded in `force`
* and returns whether the type is fully defined. The direction in which
* a type variable is instantiated is determined as follows:
* 1. T is minimized if the constraint over T is only from below (i.e.
* constrained lower bound != given lower bound and
* constrained upper bound == given upper bound).
* 2. T is maximized if the constraint over T is only from above (i.e.
* constrained upper bound != given upper bound and
* constrained lower bound == given lower bound).
* If (1) and (2) do not apply:
* 3. T is maximized if it appears only contravariantly in the given type.
* 4. T is minimized in all other cases. If a type variable has appeared in the type of a prefix, rule 1. or 2. usually apply. But they don't always unfortunately, e.g in: trait Inv[T]
class Test {
implicit val x: String = ""
def foo[T <: String](f: T => Int)(implicit g: T): T => Int = f
def foo2[T](f: T => Int)(implicit g: T): T => Int = f
def bar(f: String => Int) = foo(f)
// We will instantiate T because it appears in the type of foo[T]
// T := Nothing because constrained upper bound (String) == given upper bound (String), rule 2 does not apply.
def bar2(f: String => Int) = foo2(f)
// We will instantiate T because it appears in the type of foo2[T]
// T := String because constrained upper bound (String) != given upper bound (Any), rule 2 does apply. Given more precise bounds to a type parameter can make type inference worse, this is pretty unintuitive. I think this could be solved if we somehow recorded the fact that we did a comparison |
Here's how this could be implemented: instead of using a class MemoryTypeBounds(lo: Type, hi: Type, loTouched: Boolean = false, hiTouched: Boolean = false)
extends TypeBounds(lo, hi) {
val touched = loTouched || hiTouched
def updateLo(lo: Type) = {
if (lo == this.lo && loTouched) this
else new MemoryTypeBounds(lo, hi, loTouched = true, hiTouched)
}
def updateHi(hi: Type) = {
if (hi == this.hi && hiTouched) this
else new MemoryTypeBounds(lo, hi, loTouched, hiTouched = true)
}
} |
From what I understood, we infer foo[Nothing] in the first call because the compiler has to "complete" the type of Does that sound right? What's different in the |
@smarter I tried implementing your first suggestion (changing
However,
Which I think happens because Does this seem like a case where we should choose the lower bound, even if it's |
@smarter friendly ping |
@abeln IMHO (FWIW) it does seem like you should infer |
Don't be tricked by the type signature, on a transparent def it only serves as an upper bound (see https://github.com/lampepfl/dotty/blob/master/docs/docs/typelevel.md). I haven't looked at it in details but the failing test looks weird. Do we really want to typecheck code that produces values of type Nothing? Shouldn't we get a compiler error when a transparent call expands to something like that instead? In any case, I don't think my first suggestion was great. I would rather go in the other direction and drop all special cases involving Nothing in inference in favor of the second approach I outlined. |
Is fixed in master |
The text was updated successfully, but these errors were encountered: