-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Change GADT constrainer to consider all parents of the scrutinee type when upcasting it #11521
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
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -94,38 +94,54 @@ trait PatternTypeConstrainer { self: TypeComparer => | |
} | ||
|
||
def constrainUpcasted(scrut: Type): Boolean = trace(i"constrainUpcasted($scrut)", gadts) { | ||
val upcasted: Type = scrut match { | ||
// Fold a list of types into an AndType | ||
def buildAndType(xs: List[Type]): Type = { | ||
@annotation.tailrec def recur(acc: Type, rem: List[Type]): Type = rem match { | ||
case Nil => acc | ||
case x :: rem => recur(AndType(acc, x), rem) | ||
} | ||
xs match { | ||
case Nil => NoType | ||
case x :: xs => recur(x, xs) | ||
} | ||
} | ||
|
||
scrut match { | ||
case scrut: TypeRef if scrut.symbol.isClass => | ||
// we do not infer constraints following from all parents for performance reasons | ||
// in principle however, if `A extends B, C`, then `A` can be treated as `B & C` | ||
scrut.firstParent | ||
// consider all parents | ||
val parents = scrut.parents | ||
val andType = trace(i"andType of scrut", gadts) { | ||
buildAndType(parents) | ||
} | ||
constrainPatternType(pat, andType) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you check if |
||
case scrut @ AppliedType(tycon: TypeRef, _) if tycon.symbol.isClass => | ||
val patClassSym = pat.classSymbol | ||
// as above, we do not consider all parents for performance reasons | ||
def firstParentSharedWithPat(tp: Type, tpClassSym: ClassSymbol): Symbol = { | ||
// find all shared parents in the inheritance hierarchy between pat and scrut | ||
def allParentsSharedWithPat(tp: Type, tpClassSym: ClassSymbol): List[Symbol] = { | ||
var parents = tpClassSym.info.parents | ||
parents match { | ||
case first :: rest => | ||
if (first.classSymbol == defn.ObjectClass) parents = rest | ||
case _ => ; | ||
} | ||
parents match { | ||
case first :: _ => | ||
val firstClassSym = first.classSymbol.asClass | ||
val res = if (patClassSym.derivesFrom(firstClassSym)) firstClassSym | ||
else firstParentSharedWithPat(first, firstClassSym) | ||
res | ||
case _ => NoSymbol | ||
if parents.nonEmpty && parents.head.classSymbol == defn.ObjectClass then | ||
parents = parents.tail | ||
parents flatMap { tp => | ||
val sym = tp.classSymbol.asClass | ||
if patClassSym.derivesFrom(sym) then List(sym) | ||
else allParentsSharedWithPat(tp, sym) | ||
} | ||
} | ||
val sym = firstParentSharedWithPat(tycon, tycon.symbol.asClass) | ||
if (sym.exists) scrut.baseType(sym) else NoType | ||
case scrut: TypeProxy => scrut.superType | ||
case _ => NoType | ||
val allSyms = allParentsSharedWithPat(tycon, tycon.symbol.asClass) | ||
val baseClasses = allSyms map scrut.baseType | ||
val andType = trace(i"andType of scrut", gadts) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto about the trace. |
||
buildAndType(baseClasses) | ||
} | ||
constrainPatternType(pat, andType) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto about |
||
case _ => | ||
val upcasted: Type = scrut match { | ||
case scrut: TypeProxy => scrut.superType | ||
case _ => NoType | ||
} | ||
if (upcasted.exists) | ||
constrainSimplePatternType(pat, upcasted) || constrainUpcasted(upcasted) | ||
else true | ||
} | ||
if (upcasted.exists) | ||
constrainSimplePatternType(pat, upcasted) || constrainUpcasted(upcasted) | ||
else true | ||
} | ||
|
||
scrut.dealias match { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
trait TagA[A] | ||
trait TagB[B] | ||
trait TagC[C] | ||
trait TriTag[A, B, C] extends TagA[A] with TagB[B] with TagC[C] | ||
class IntStrCharTag extends TagA[Int] with TagB[String] with TagC[Char] | ||
|
||
def get[A, B, C]: TriTag[A, B, C] => (A, B, C) = { | ||
case _: IntStrCharTag => (0, "zero", '0') | ||
} | ||
|
||
object GadtUpcast extends App { | ||
val ret = get(new IntStrCharTag with TriTag[Int, String, Char]) | ||
println(ret) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This trace doesn't seem overall very useful, could you remove it?