-
Notifications
You must be signed in to change notification settings - Fork 1.1k
[compiler crash | "Recursion limit exceeded"][regression] caused by self-types + with
in generic method signature + quasi-covariant wildcards (e.g. (that: PThis with HasThisType[PThis])
)
#5877
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
Part2 Other issues appears when one may wont to "expose" self-type constraint "to public" type HasThisTypeSelf[PThis] = PThis with HasThisType[PThis]
trait HasThisType[PThis] {
this: PThis =>
type This = PThis
// inline // uncommenting `inline` cause problem in scastie dotty version, but is fixed in dotty `master`
def self(): This with this.type = this
} (more advanced - with recursive invariant generic) type HasThisTypeSelf[PThis <: HasThisType[PThis]] = PThis with HasThisType[PThis]
trait HasThisType[PThis <: HasThisType[PThis]] {
this: PThis =>
type This = PThis
// inline // uncommenting `inline` cause problem in scastie dotty version, but is fixed in dotty `master`
def self(): This with this.type = this
} everything works as expected! // def doSmthWithSelfImpl[PThis](that: PThis with HasThisType[PThis]): Unit = ???
def doSmthWithSelfImpl[PThis <: HasThisType[PThis]](that: HasThisTypeSelf[PThis]): Unit = ??? is adapted to def doSmthWithSelf1[PThis <: HasThisType[PThis]](that: HasThisType[PThis]): Unit or to wildcard version def doSmthWithSelfWildcard(that: HasThisType[_]): Unit Using explicit or implicit conversion of form // inline
def exposeItSelf1[PThis <: HasThisType[PThis]](that: HasThisType[PThis]): that.type with PThis = that.self()
// inline
def exposeItSelf2[PThis <: HasThisType[PThis]](that: HasThisType[PThis]): HasThisType[PThis] with PThis = that.self() As shown in aforementioned scasite snippet, all this works fine for simple self-typed generics, and for most common recursive invariant generics. However for more complicated (recursive "quasi covariant" generics) that approach does not work ... type HasThisTypeSelf[PThis <: HasThisType[_ <: PThis]] = PThis with HasThisType[PThis]
trait HasThisType[PThis <: HasThisType[_ <: PThis]] {
this: PThis =>
type This = PThis
// inline // uncommenting `inline` cause problem in scastie dotty version, but is fixed in dotty `master`
def self(): This with this.type = this
} Here is complete problem-demonstrating snippet, same code but with commented problematic lines here def doSmthWithSelf2[PThis <: HasThisType[_ <: PThis]](that: HasThisType[PThis]): Unit = {
// makes error on both master and scasite version of Dotty
// doSmthWithSelfImpl(exposeItSelf2(that))
// this is the only version that works in both master and scasite version of Dotty
doSmthWithSelfImpl(exposeItSelf2[PThis](that))
}
// def doSmthWithSelfImpl[PThis](that: PThis with HasThisType[PThis]): Unit = ???
def doSmthWithSelfImpl[PThis <: HasThisType[_ <: PThis]](that: HasThisTypeSelf[PThis]): Unit = ???
// inline
def exposeItSelf1[PThis <: HasThisType[_ <: PThis]](that: HasThisType[PThis]): that.type with PThis = that.self()
// inline
def exposeItSelf2[PThis <: HasThisType[_ <: PThis]](that: HasThisType[PThis]): HasThisType[PThis] with PThis = that.self() While in Dotty version used by scasite more ways are working (however still less then expected) Basically all tried adaptations looks like this (extract from aforementioned snippets 1, 2) // def doSmthWithSelfImpl[PThis](that: PThis with HasThisType[PThis]): Unit = ???
def doSmthWithSelfImpl[PThis <: HasThisType[_ <: PThis]](that: HasThisTypeSelf[PThis]): Unit = {
println(s"doSmthWithSelfImpl: that: $that")
}
def doSmthWithSelf1[PThis <: HasThisType[_ <: PThis]](that: HasThisType[PThis]): Unit = {
// makes error only on master version of Dotty ; WORKS on scasite version of Dotty
doSmthWithSelfImpl(exposeItSelf1(that))
}
def doSmthWithSelf2[PThis <: HasThisType[_ <: PThis]](that: HasThisType[PThis]): Unit = {
// makes error on both master and scasite version of Dotty
// doSmthWithSelfImpl(exposeItSelf2(that))
// this is the only version that works in both master and scasite version of Dotty
doSmthWithSelfImpl(exposeItSelf2[PThis](that))
}
def doSmthWithSelf3[PThis <: HasThisType[_ <: PThis]](that: HasThisType[PThis]): Unit = {
import scala.language.implicitConversions
import exposeItSelf1Impl._
// makes error only on master version of Dotty ; WORKS on scasite version of Dotty
doSmthWithSelfImpl(that)
}
def doSmthWithSelf4[PThis <: HasThisType[_ <: PThis]](that: HasThisType[PThis]): Unit = {
import scala.language.implicitConversions
import exposeItSelf2Impl._
// makes error on both master and scasite version of Dotty
// doSmthWithSelfImpl(that)
}
def doSmthWithSelfWildcard(that: HasThisType[_]): Unit = {
// makes error only on master version of Dotty ; WORKS on scasite version of Dotty
doSmthWithSelfImpl(exposeItSelf1(that))
// makes error on both master and scasite version of Dotty
// doSmthWithSelfImpl(exposeItSelf2(that))
import scala.language.implicitConversions
{
import exposeItSelf1Impl._
// makes error on both master and scasite version of Dotty
// doSmthWithSelfImpl(that)
}
{
import exposeItSelf2Impl._
// makes error on both master and scasite version of Dotty
// doSmthWithSelfImpl(that)
}
} |
I think this is crazy code that we do not want to support. Instead of trying to make it work, can I put down the opposite challenge for you: Can you think of a simple rule set that would prevent code like this? I.e. the compiler should flag this as an error rather than trying too hard and going into infinite recursions and the like. But the question is: what would a good error condition be? |
probably Dotty should have better support of use-site co-variance (it works poorly only with [_ < T])
I guess you may forgive me code quality, since I look at it as some data for Dotty compiler testing (it was not intended for use in production so far 😄 ) |
Fair enough 😉 But it still looks to me more like an abuse of Scala's features. We have to fix it for sure, but I am not sure yet whether the fix will outlaw idioms like that instead of supporting it. |
Closes scala#5288 Closes scala#5877 Closes scala#6778
When testing
HasThisType
trait defined in following from (PThis
parameter itself is invariant, but recursive constraint is deliberately covariant)During verification whether "Self-type" constraint is visible or not in particular contexts (especially from outside of that trait) some problems with
HasThisType[_]
and generic methods signatures was revealed.Part1
In particular, snippet (complete code here)
makes Dotty compiler crash
assertion failure for Main.HasThisType[_ <: LazyRef(Main.HasThisType[_]#PThis)] <:< Main.HasThisType ...
After rephrasing generic method signature to some alternative equivalents
compiler crash could be reduced to regular compilation error.
While for "rewrite#2" compiler provides some error message of quite reasonable size (couple of lines)
for "rewrite#1" it shows "significantly" multiline error message like this:
Also, essentially, that problem with tricky signature reproduces only with argument of wildcard (
HasThisType[_]
) type.For normal (not wildcard) types matching that signature everything works well
(complete code here)
Other snippet shows that problem with aforementioned signature could also happens not only when signature doesn't match passed argument, but in potentially positive case (but when some wildcard-ed type need to be passed/inferred through 'PThis')
Considering following example (complete code here)
One may expect no error there, while in fact it end up with similar compile runtime crash.
However in this case one may resolve problem "manually" by explicitly hinting
PThis
parameter (instead of expecting it's correct inference).So that following fixed (explicit) snippet (complete code here)
will become compilable under Dotty compiler
Eventually, in case of invariant
PThis
parameter definition, which may looks likeRuntime crash is not reproducible, however unreasonably long compilation error messages still appears.
Demonstrating snippet
Also rewriting signature using intermediate aliasing type as
allows to see error message of more reasonable size, and may look like
The text was updated successfully, but these errors were encountered: