diff --git a/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala b/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala index d0f9a49cf216..aa1797d5fd69 100644 --- a/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala +++ b/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala @@ -94,38 +94,50 @@ 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 = buildAndType(parents) + !andType.exists || constrainPatternType(pat, andType) 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 = buildAndType(baseClasses) + !andType.exists || constrainPatternType(pat, andType) + 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 { diff --git a/tests/pos/gadt-upcast.scala b/tests/pos/gadt-upcast.scala new file mode 100644 index 000000000000..00895f35843a --- /dev/null +++ b/tests/pos/gadt-upcast.scala @@ -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) +}