From eca0834145b9ca59b207359f5a65e509210a15c2 Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Wed, 24 Feb 2021 19:00:34 +0800 Subject: [PATCH 1/3] Add GADT test for upcasting --- tests/pos/gadt-upcast.scala | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 tests/pos/gadt-upcast.scala 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) +} From 7a4422f6a6083137180909fd131c539b42785306 Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Wed, 24 Feb 2021 19:03:36 +0800 Subject: [PATCH 2/3] Consider all parents when upcasting --- .../dotc/core/PatternTypeConstrainer.scala | 66 ++++++++++++------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala b/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala index d0f9a49cf216..340025ba7890 100644 --- a/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala +++ b/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala @@ -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) 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) { + buildAndType(baseClasses) + } + 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 { From 3dbffc131c49c3fa33417fb93411aae733c1f09d Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Fri, 12 Mar 2021 21:16:45 +0800 Subject: [PATCH 3/3] Remove useless trace and add existence checking --- .../tools/dotc/core/PatternTypeConstrainer.scala | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala b/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala index 340025ba7890..aa1797d5fd69 100644 --- a/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala +++ b/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala @@ -110,10 +110,8 @@ trait PatternTypeConstrainer { self: TypeComparer => case scrut: TypeRef if scrut.symbol.isClass => // consider all parents val parents = scrut.parents - val andType = trace(i"andType of scrut", gadts) { - buildAndType(parents) - } - constrainPatternType(pat, andType) + val andType = buildAndType(parents) + !andType.exists || constrainPatternType(pat, andType) case scrut @ AppliedType(tycon: TypeRef, _) if tycon.symbol.isClass => val patClassSym = pat.classSymbol // find all shared parents in the inheritance hierarchy between pat and scrut @@ -129,10 +127,8 @@ trait PatternTypeConstrainer { self: TypeComparer => } val allSyms = allParentsSharedWithPat(tycon, tycon.symbol.asClass) val baseClasses = allSyms map scrut.baseType - val andType = trace(i"andType of scrut", gadts) { - buildAndType(baseClasses) - } - constrainPatternType(pat, andType) + val andType = buildAndType(baseClasses) + !andType.exists || constrainPatternType(pat, andType) case _ => val upcasted: Type = scrut match { case scrut: TypeProxy => scrut.superType