Open adriaanm-da opened 2 years ago
I guess by calling info
you mean:
def baseTypeSeqLength(sym: Symbol) =
if (sym.isAbstractType) 1 + sym.info.upperBound.baseTypeSeq.length
else sym.info.baseTypeSeq.length
Yep!
I should disclaim that I never fully internalized this symbol comparison logic and the way lub is built on top of it, so please take my analysis with the appropriate amount of salt.
I think the comment explains it:
A total ordering between symbols that refines the class inheritance graph (i.e. subclass.isLess(superclass) always holds). the ordering is given by: (.isType, -.baseTypeSeq.length) for type symbols, followed by id.
A subtype will always have a longer base type sequence - so it's a more efficient proxy to order symbols than checking for a subtype relationship. But clearly it breaks down for an applied abstract type.
So the solution in theory is simple enough - switch to comparing types instead of comparing symbols
Something like this:
/** A total ordering between symbols that refines the class
* inheritance graph (i.e. subclass.isLess(superclass) always holds).
* the ordering is given by: (_.isType, -_.baseTypeSeq.length) for type symbols, followed by `id`.
*/
final def isLess(that: Symbol): Boolean = isLessBy(that) { sym =>
if (sym.isAbstractType) 1 + sym.info.upperBound.baseTypeSeq.length
else sym.info.baseTypeSeq.length
}
final def isLessBy(that: Symbol)(rank: Symbol => Int): Boolean =
(this ne that) && {
if (this.isType)
that.isType && {
val diff = rank(this) - rank(that)
diff > 0 || diff == 0 && this.id < that.id
}
else
that.isType || this.id < that.id
}
And the in GlbLubs
use the BTS that we already have for ranking
That makes sense to me. Since it works in Scala 3, I suggest taking a look at how it's implemented there.
Scala 3 has union types so I don't think the LUB can be simply backported
Good point -- I'm getting rusty :-)
@adriaanm-da I tried but a test fails and I don't understand why: scala/scala#9937
This fails to type check (2.13.8) -- Scala 3.1.1 (correctly, IMO) accepts it:
I would expect the
lub of List(Tst.T[Nothing], Tst.F[Tst.T[String]])
to beTst.T[String]
.I added some logging to see what's going on, and this seems suspicious:
minSym for List(Tst.T[Nothing], Tst.F[Tst.T[String]]): class T
I would expect the "minimal" symbol in this list to be
F
(as it's applied toT[String]
, which means it's bounded byT[String]
, which impliesTst.F[Tst.T[String]]
should be less thanTst.T[String]
(andTst.T[Nothing]
), but that gets lost because scalac gets theinfo
for the symbolF
, and doesn't apply it to the arguments in the typeTst.F[Tst.T[String]]
.I couldn't look into this deeper, apologies for the handwavy diagnosis.