From 30cd37c8abc4386777645389dd3f41226f03af9a Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Tue, 7 Apr 2020 23:46:15 +0200 Subject: [PATCH 01/13] Fix #8690: the signature for product should come from expected type --- .../tools/dotc/transform/patmat/Space.scala | 102 ++++++++++-------- tests/patmat/i8690.scala | 7 ++ 2 files changed, 67 insertions(+), 42 deletions(-) create mode 100644 tests/patmat/i8690.scala diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index b1710569e931..b61439944f95 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -69,7 +69,7 @@ case object Empty extends Space case class Typ(tp: Type, decomposed: Boolean = true) extends Space /** Space representing an extractor pattern */ -case class Prod(tp: Type, unappTp: Type, unappSym: Symbol, params: List[Space], full: Boolean) extends Space +case class Prod(tp: Type, unappTp: TermRef, params: List[Space], full: Boolean) extends Space /** Union of spaces */ case class Or(spaces: List[Space]) extends Space @@ -97,7 +97,7 @@ trait SpaceLogic { def canDecompose(tp: Type): Boolean /** Return term parameter types of the extractor `unapp` */ - def signature(unapp: Type, unappSym: Symbol, argLen: Int): List[Type] + def signature(unapp: TermRef, scrutineeTp: Type, argLen: Int): List[Type] /** Get components of decomposable types */ def decompose(tp: Type): List[Space] @@ -113,8 +113,8 @@ trait SpaceLogic { * This reduces noise in counterexamples. */ def simplify(space: Space, aggressive: Boolean = false)(implicit ctx: Context): Space = trace(s"simplify ${show(space)}, aggressive = $aggressive --> ", debug, x => show(x.asInstanceOf[Space]))(space match { - case Prod(tp, fun, sym, spaces, full) => - val sp = Prod(tp, fun, sym, spaces.map(simplify(_)), full) + case Prod(tp, fun, spaces, full) => + val sp = Prod(tp, fun, spaces.map(simplify(_)), full) if (sp.params.contains(Empty)) Empty else if (canDecompose(tp) && decompose(tp).isEmpty) Empty else sp @@ -143,13 +143,13 @@ trait SpaceLogic { /** Flatten space to get rid of `Or` for pretty print */ def flatten(space: Space)(implicit ctx: Context): List[Space] = space match { - case Prod(tp, fun, sym, spaces, full) => + case Prod(tp, fun, spaces, full) => spaces.map(flatten) match { - case Nil => Prod(tp, fun, sym, Nil, full) :: Nil + case Nil => Prod(tp, fun, Nil, full) :: Nil case ss => ss.foldLeft(List[Prod]()) { (acc, flat) => - if (acc.isEmpty) flat.map(s => Prod(tp, fun, sym, s :: Nil, full)) - else for (Prod(tp, fun, sym, ss, full) <- acc; s <- flat) yield Prod(tp, fun, sym, ss :+ s, full) + if (acc.isEmpty) flat.map(s => Prod(tp, fun, s :: Nil, full)) + else for (Prod(tp, fun, ss, full) <- acc; s <- flat) yield Prod(tp, fun, ss :+ s, full) } } case Or(spaces) => @@ -173,13 +173,13 @@ trait SpaceLogic { ss.exists(isSubspace(a, _)) || tryDecompose1(tp1) case (_, Or(_)) => simplify(minus(a, b)) == Empty - case (Prod(tp1, _, _, _, _), Typ(tp2, _)) => + case (Prod(tp1, _, _, _), Typ(tp2, _)) => isSubType(tp1, tp2) - case (Typ(tp1, _), Prod(tp2, fun, sym, ss, full)) => + case (Typ(tp1, _), Prod(tp2, fun, ss, full)) => // approximation: a type can never be fully matched by a partial extractor - full && isSubType(tp1, tp2) && isSubspace(Prod(tp2, fun, sym, signature(fun, sym, ss.length).map(Typ(_, false)), full), b) - case (Prod(_, fun1, sym1, ss1, _), Prod(_, fun2, sym2, ss2, _)) => - sym1 == sym2 && isEqualType(fun1, fun2) && ss1.zip(ss2).forall((isSubspace _).tupled) + full && isSubType(tp1, tp2) && isSubspace(Prod(tp2, fun, signature(fun, tp2, ss.length).map(Typ(_, false)), full), b) + case (Prod(_, fun1, ss1, _), Prod(_, fun2, ss2, _)) => + isEqualType(fun1, fun2) && ss1.zip(ss2).forall((isSubspace _).tupled) } } @@ -198,28 +198,28 @@ trait SpaceLogic { else if (canDecompose(tp1)) tryDecompose1(tp1) else if (canDecompose(tp2)) tryDecompose2(tp2) else intersectUnrelatedAtomicTypes(tp1, tp2) - case (Typ(tp1, _), Prod(tp2, fun, _, ss, true)) => + case (Typ(tp1, _), Prod(tp2, fun, ss, true)) => if (isSubType(tp2, tp1)) b else if (isSubType(tp1, tp2)) a // problematic corner case: inheriting a case class else if (canDecompose(tp1)) tryDecompose1(tp1) else Empty - case (Typ(tp1, _), Prod(tp2, _, _, _, false)) => + case (Typ(tp1, _), Prod(tp2, _, _, false)) => if (isSubType(tp1, tp2) || isSubType(tp2, tp1)) b // prefer extractor space for better approximation else if (canDecompose(tp1)) tryDecompose1(tp1) else Empty - case (Prod(tp1, fun, _, ss, true), Typ(tp2, _)) => + case (Prod(tp1, fun, ss, true), Typ(tp2, _)) => if (isSubType(tp1, tp2)) a else if (isSubType(tp2, tp1)) a // problematic corner case: inheriting a case class else if (canDecompose(tp2)) tryDecompose2(tp2) else Empty - case (Prod(tp1, _, _, _, false), Typ(tp2, _)) => + case (Prod(tp1, _, _, false), Typ(tp2, _)) => if (isSubType(tp1, tp2) || isSubType(tp2, tp1)) a else if (canDecompose(tp2)) tryDecompose2(tp2) else Empty - case (Prod(tp1, fun1, sym1, ss1, full), Prod(tp2, fun2, sym2, ss2, _)) => - if (sym1 != sym2 || !isEqualType(fun1, fun2)) Empty + case (Prod(tp1, fun1, ss1, full), Prod(tp2, fun2, ss2, _)) => + if (!isEqualType(fun1, fun2)) Empty else if (ss1.zip(ss2).exists(p => simplify(intersect(p._1, p._2)) == Empty)) Empty - else Prod(tp1, fun1, sym1, ss1.zip(ss2).map((intersect _).tupled), full) + else Prod(tp1, fun1, ss1.zip(ss2).map((intersect _).tupled), full) } } @@ -236,34 +236,34 @@ trait SpaceLogic { else if (canDecompose(tp1)) tryDecompose1(tp1) else if (canDecompose(tp2)) tryDecompose2(tp2) else a - case (Typ(tp1, _), Prod(tp2, fun, sym, ss, true)) => + case (Typ(tp1, _), Prod(tp2, fun, ss, true)) => // rationale: every instance of `tp1` is covered by `tp2(_)` - if (isSubType(tp1, tp2)) minus(Prod(tp1, fun, sym, signature(fun, sym, ss.length).map(Typ(_, false)), true), b) + if (isSubType(tp1, tp2)) minus(Prod(tp1, fun, signature(fun, tp1, ss.length).map(Typ(_, false)), true), b) else if (canDecompose(tp1)) tryDecompose1(tp1) else a case (_, Or(ss)) => ss.foldLeft(a)(minus) case (Or(ss), _) => Or(ss.map(minus(_, b))) - case (Prod(tp1, fun, _, ss, true), Typ(tp2, _)) => + case (Prod(tp1, fun, ss, true), Typ(tp2, _)) => // uncovered corner case: tp2 :< tp1 if (isSubType(tp1, tp2)) Empty else if (simplify(a) == Empty) Empty else if (canDecompose(tp2)) tryDecompose2(tp2) else a - case (Prod(tp1, _, _, _, false), Typ(tp2, _)) => + case (Prod(tp1, _, _, false), Typ(tp2, _)) => if (isSubType(tp1, tp2)) Empty else a - case (Typ(tp1, _), Prod(tp2, _, _, _, false)) => + case (Typ(tp1, _), Prod(tp2, _, _, false)) => a // approximation - case (Prod(tp1, fun1, sym1, ss1, full), Prod(tp2, fun2, sym2, ss2, _)) => - if (sym1 != sym2 || !isEqualType(fun1, fun2)) a + case (Prod(tp1, fun1, ss1, full), Prod(tp2, fun2, ss2, _)) => + if (!isEqualType(fun1, fun2)) a else if (ss1.zip(ss2).exists(p => simplify(intersect(p._1, p._2)) == Empty)) a else if (ss1.zip(ss2).forall((isSubspace _).tupled)) Empty else // `(_, _, _) - (Some, None, _)` becomes `(None, _, _) | (_, Some, _) | (_, _, Empty)` Or(ss1.zip(ss2).map((minus _).tupled).zip(0 to ss2.length - 1).map { - case (ri, i) => Prod(tp1, fun1, sym1, ss1.updated(i, ri), full) + case (ri, i) => Prod(tp1, fun1, ss1.updated(i, ri), full) }) } @@ -360,18 +360,20 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { case Bind(_, pat) => project(pat) case SeqLiteral(pats, _) => projectSeq(pats) case UnApply(fun, _, pats) => + val (fun1, _, _) = decomposeCall(fun) + val funRef = fun1.tpe.asInstanceOf[TermRef] if (fun.symbol.name == nme.unapplySeq) if (fun.symbol.owner == scalaSeqFactoryClass) projectSeq(pats) else { val (arity, elemTp, resultTp) = unapplySeqInfo(fun.tpe.widen.finalResultType, fun.sourcePos) if (elemTp.exists) - Prod(erase(pat.tpe.stripAnnots), fun.tpe, fun.symbol, projectSeq(pats) :: Nil, isIrrefutableUnapplySeq(fun, pats.size)) + Prod(erase(pat.tpe.stripAnnots), funRef, projectSeq(pats) :: Nil, isIrrefutableUnapplySeq(fun, pats.size)) else - Prod(erase(pat.tpe.stripAnnots), fun.tpe, fun.symbol, pats.take(arity - 1).map(project) :+ projectSeq(pats.drop(arity - 1)), isIrrefutableUnapplySeq(fun, pats.size)) + Prod(erase(pat.tpe.stripAnnots), funRef, pats.take(arity - 1).map(project) :+ projectSeq(pats.drop(arity - 1)), isIrrefutableUnapplySeq(fun, pats.size)) } else - Prod(erase(pat.tpe.stripAnnots), erase(fun.tpe), fun.symbol, pats.map(project), isIrrefutableUnapply(fun, pats.length)) + Prod(erase(pat.tpe.stripAnnots), funRef, pats.map(project), isIrrefutableUnapply(fun, pats.length)) case Typed(pat @ UnApply(_, _, _), _) => project(pat) case Typed(expr, _) => Typ(erase(expr.tpe.stripAnnots), true) @@ -386,6 +388,11 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { Typ(pat.tpe.narrow, false) } + private def project(tp: Type): Space = tp match { + case OrType(tp1, tp2) => Or(project(tp1) :: project(tp2) :: Nil) + case tp => Typ(tp, decomposed = true) + } + private def unapplySeqInfo(resTp: Type, pos: SourcePosition)(implicit ctx: Context): (Int, Type, Type) = { var resultTp = resTp var elemTp = unapplySeqTypeElemTp(resultTp) @@ -458,11 +465,10 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { else (pats, Typ(scalaNilType, false)) + val unapplyTp = scalaConsType.classSymbol.companionModule.termRef.select(nme.unapply) items.foldRight[Space](zero) { (pat, acc) => val consTp = scalaConsType.appliedTo(pats.head.tpe.widen) - val unapplySym = consTp.classSymbol.linkedClass.info.member(nme.unapply).symbol - val unapplyTp = unapplySym.info.appliedTo(pats.head.tpe.widen) - Prod(consTp, unapplyTp, unapplySym, project(pat) :: acc :: Nil, true) + Prod(consTp, unapplyTp, project(pat) :: acc :: Nil, true) } } @@ -480,7 +486,8 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { def isEqualType(tp1: Type, tp2: Type): Boolean = tp1 =:= tp2 /** Parameter types of the case class type `tp`. Adapted from `unapplyPlan` in patternMatcher */ - def signature(unapp: Type, unappSym: Symbol, argLen: Int): List[Type] = { + def signature(unapp: TermRef, scrutineeTp: Type, argLen: Int): List[Type] = { + val unappSym = unapp.symbol def caseClass = unappSym.owner.linkedClass lazy val caseAccessors = caseClass.caseAccessors.filter(_.is(Method)) @@ -488,7 +495,17 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { def isSyntheticScala2Unapply(sym: Symbol) = sym.isAllOf(SyntheticCase) && sym.owner.is(Scala2x) - val mt @ MethodType(_) = unapp.widen + val mt: MethodType = unapp.widen match { + case mt: MethodType => mt + case pt: PolyType => + inContext(ctx.fresh.setNewTyperState()) { + val tvars = pt.paramInfos.map(newTypeVar) + val mt = pt.instantiate(tvars).asInstanceOf[MethodType] + scrutineeTp <:< mt.paramInfos(0) + instantiateSelected(mt, tvars) + mt + } + } // Case unapply: // 1. return types of constructor fields if the extractor is synthesized for Scala2 case classes & length match @@ -657,8 +674,8 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { def impossible: Nothing = throw new AssertionError("`satisfiable` only accepts flattened space.") def genConstraint(space: Space): List[(Type, Type)] = space match { - case Prod(tp, unappTp, unappSym, ss, _) => - val tps = signature(unappTp, unappSym, ss.length) + case Prod(tp, unappTp, ss, _) => + val tps = signature(unappTp, tp, ss.length) ss.zip(tps).flatMap { case (sp : Prod, tp) => sp.tp -> tp :: genConstraint(sp) case (Typ(tp1, _), tp2) => tp1 -> tp2 :: Nil @@ -717,13 +734,14 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { showType(tp) + params(tp).map(_ => "_").mkString("(", ", ", ")") else if (decomposed) "_: " + showType(tp, showTypeArgs = true) else "_" - case Prod(tp, fun, sym, params, _) => + case Prod(tp, fun, params, _) => if (ctx.definitions.isTupleType(tp)) "(" + params.map(doShow(_)).mkString(", ") + ")" else if (tp.isRef(scalaConsType.symbol)) if (flattenList) params.map(doShow(_, flattenList)).mkString(", ") else params.map(doShow(_, flattenList = true)).filter(!_.isEmpty).mkString("List(", ", ", ")") else { + val sym = fun.symbol val isUnapplySeq = sym.name.eq(nme.unapplySeq) val paramsStr = params.map(doShow(_, flattenList = isUnapplySeq)).mkString("(", ", ", ")") showType(sym.owner.typeRef) + paramsStr @@ -781,7 +799,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { val checkGADTSAT = shouldCheckExamples(selTyp) val uncovered = - flatten(simplify(minus(Typ(selTyp, true), patternSpace), aggressive = true)).filter { s => + flatten(simplify(minus(project(selTyp), patternSpace), aggressive = true)).filter { s => s != Empty && (!checkGADTSAT || satisfiable(s)) } @@ -805,9 +823,9 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { val targetSpace = if (ctx.explicitNulls || selTyp.classSymbol.isPrimitiveValueClass) - Typ(selTyp, true) + project(selTyp) else - Or(Typ(selTyp, true) :: constantNullSpace :: Nil) + project(OrType(selTyp, constantNullType)) // in redundancy check, take guard as false in order to soundly approximate def projectPrevCases(cases: List[CaseDef]): Space = diff --git a/tests/patmat/i8690.scala b/tests/patmat/i8690.scala new file mode 100644 index 000000000000..de706ec86d44 --- /dev/null +++ b/tests/patmat/i8690.scala @@ -0,0 +1,7 @@ +class A +class B + +def test(x: (A, B) | (B, A)) = x match { + case (u: A, v) => (u, v) + case (u: B, v) => (v, u) +} From 18564c8f7fa5e301c5e2195a2d5bd7767655ff33 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Wed, 8 Apr 2020 00:23:59 +0200 Subject: [PATCH 02/13] Erase pattern bound variables --- .../tools/dotc/transform/patmat/Space.scala | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index b61439944f95..2ab6d6f9cb78 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -351,14 +351,25 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { Typ(c.value.asInstanceOf[Symbol].termRef, false) else Typ(ConstantType(c), false) - case pat: Ident if isBackquoted(pat) => Typ(pat.tpe, false) + + case pat: Ident if isBackquoted(pat) => + Typ(pat.tpe, false) + case Ident(nme.WILDCARD) => - Or(Typ(pat.tpe.stripAnnots, false) :: constantNullSpace :: Nil) + Or(Typ(erase(pat.tpe.stripAnnots), false) :: constantNullSpace :: Nil) + case Ident(_) | Select(_, _) => Typ(erase(pat.tpe.stripAnnots), false) - case Alternative(trees) => Or(trees.map(project(_))) - case Bind(_, pat) => project(pat) - case SeqLiteral(pats, _) => projectSeq(pats) + + case Alternative(trees) => + Or(trees.map(project(_))) + + case Bind(_, pat) => + project(pat) + + case SeqLiteral(pats, _) => + projectSeq(pats) + case UnApply(fun, _, pats) => val (fun1, _, _) = decomposeCall(fun) val funRef = fun1.tpe.asInstanceOf[TermRef] @@ -374,15 +385,22 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { } else Prod(erase(pat.tpe.stripAnnots), funRef, pats.map(project), isIrrefutableUnapply(fun, pats.length)) - case Typed(pat @ UnApply(_, _, _), _) => project(pat) + + case Typed(pat @ UnApply(_, _, _), _) => + project(pat) + case Typed(expr, _) => Typ(erase(expr.tpe.stripAnnots), true) + case This(_) => Typ(pat.tpe.stripAnnots, false) + case EmptyTree => // default rethrow clause of try/catch, check tests/patmat/try2.scala Typ(WildcardType, false) + case Block(Nil, expr) => project(expr) + case _ => // Pattern is an arbitrary expression; assume a skolem (i.e. an unknown value) of the pattern type Typ(pat.tpe.narrow, false) @@ -490,6 +508,8 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { val unappSym = unapp.symbol def caseClass = unappSym.owner.linkedClass + // println("scrutineeTp = " + scrutineeTp.show) + lazy val caseAccessors = caseClass.caseAccessors.filter(_.is(Method)) def isSyntheticScala2Unapply(sym: Symbol) = @@ -503,6 +523,8 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { val mt = pt.instantiate(tvars).asInstanceOf[MethodType] scrutineeTp <:< mt.paramInfos(0) instantiateSelected(mt, tvars) + // isFullyDefined(mt, ForceDegree.flipBottom) + // println("mt = " + mt.show) mt } } @@ -648,7 +670,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { case _ => tp.show } - def refine(tp: Type): String = tp.stripAnnots match { + def refine(tp: Type): String = tp.stripAnnots.stripTypeVar match { case tp: RefinedType => refine(tp.parent) case tp: AppliedType => refine(tp.typeConstructor) + ( @@ -744,7 +766,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { val sym = fun.symbol val isUnapplySeq = sym.name.eq(nme.unapplySeq) val paramsStr = params.map(doShow(_, flattenList = isUnapplySeq)).mkString("(", ", ", ")") - showType(sym.owner.typeRef) + paramsStr + showType(fun.prefix) + paramsStr } case Or(_) => throw new Exception("incorrect flatten result " + s) From 4dfb8381128364650499397a1cf7af13735c894e Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Wed, 8 Apr 2020 17:09:45 +0200 Subject: [PATCH 03/13] Code refactoring --- .../tools/dotc/transform/patmat/Space.scala | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 2ab6d6f9cb78..2755ff86513b 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -521,10 +521,10 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { inContext(ctx.fresh.setNewTyperState()) { val tvars = pt.paramInfos.map(newTypeVar) val mt = pt.instantiate(tvars).asInstanceOf[MethodType] + // println("mt.paramInfos(0) = " + mt.paramInfos(0)) scrutineeTp <:< mt.paramInfos(0) - instantiateSelected(mt, tvars) - // isFullyDefined(mt, ForceDegree.flipBottom) - // println("mt = " + mt.show) + isFullyDefined(mt, ForceDegree.flipBottom) + // println("mt = " + mt) mt } } @@ -539,16 +539,18 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { // Case unapplySeq: // 1. return the type `List[T]` where `T` is the element type of the unapplySeq return type `Seq[T]` + val resTp = mt.finalResultType + val sig = if (isSyntheticScala2Unapply(unappSym) && caseAccessors.length == argLen) caseAccessors.map(_.info.asSeenFrom(mt.paramInfos.head, caseClass).widenExpr) - else if (mt.finalResultType.isRef(defn.BooleanClass)) + else if (resTp.isRef(defn.BooleanClass)) List() else { val isUnapplySeq = unappSym.name == nme.unapplySeq if (isUnapplySeq) { - val (arity, elemTp, resultTp) = unapplySeqInfo(mt.finalResultType, unappSym.sourcePos) + val (arity, elemTp, resultTp) = unapplySeqInfo(resTp, unappSym.sourcePos) if (elemTp.exists) scalaListType.appliedTo(elemTp) :: Nil else { val sels = productSeqSelectors(resultTp, arity, unappSym.sourcePos) @@ -556,15 +558,13 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { } } else { - val arity = productArity(mt.finalResultType, unappSym.sourcePos) + val arity = productArity(resTp, unappSym.sourcePos) if (arity > 0) - productSelectors(mt.finalResultType) - .map(_.info.asSeenFrom(mt.finalResultType, mt.resultType.classSymbol).widenExpr) + productSelectorTypes(resTp, unappSym.sourcePos) else { - val resTp = mt.finalResultType.select(nme.get).finalResultType.widen - val arity = productArity(resTp, unappSym.sourcePos) - if (argLen == 1) resTp :: Nil - else productSelectors(resTp).map(_.info.asSeenFrom(resTp, resTp.classSymbol).widenExpr) + val getTp = resTp.select(nme.get).finalResultType.widen + if (argLen == 1) getTp :: Nil + else productSelectorTypes(getTp, unappSym.sourcePos) } } } From fca668800511b7f8af814a161daf6bd0171c206b Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Wed, 8 Apr 2020 22:11:00 +0200 Subject: [PATCH 04/13] Fix tests --- compiler/src/dotty/tools/dotc/transform/patmat/Space.scala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 2755ff86513b..12d3366d402e 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -521,10 +521,11 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { inContext(ctx.fresh.setNewTyperState()) { val tvars = pt.paramInfos.map(newTypeVar) val mt = pt.instantiate(tvars).asInstanceOf[MethodType] - // println("mt.paramInfos(0) = " + mt.paramInfos(0)) scrutineeTp <:< mt.paramInfos(0) - isFullyDefined(mt, ForceDegree.flipBottom) - // println("mt = " + mt) + // force type inference to infer a narrower type: could be singleton + // see tests/patmat/i4227.scala + mt.paramInfos(0) <:< scrutineeTp + isFullyDefined(mt, ForceDegree.all) mt } } From 911a81898abc0c1ba9f93137473b38529243c27a Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Thu, 9 Apr 2020 11:55:37 +0200 Subject: [PATCH 05/13] Add test --- tests/patmat/t11620b.scala | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tests/patmat/t11620b.scala diff --git a/tests/patmat/t11620b.scala b/tests/patmat/t11620b.scala new file mode 100644 index 000000000000..40fb444b0499 --- /dev/null +++ b/tests/patmat/t11620b.scala @@ -0,0 +1,16 @@ +sealed abstract class Length + +object Length { + case class Num(n: Int) extends Length + case object StateColumn extends Length +} + +import Length._ + +case class Indent[T <: Length](length: T) + +def withIndent[T <: Length](indent: => Indent[_]): Unit = + indent match { + case Indent(Num(0)) => println("this") + case x => println(x) // "unreachable" + } \ No newline at end of file From 00ee7e2f5ef1d024510959e3a9ab5725cc0184eb Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Thu, 9 Apr 2020 12:05:03 +0200 Subject: [PATCH 06/13] Add more test --- tests/patmat/t11457b.scala | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 tests/patmat/t11457b.scala diff --git a/tests/patmat/t11457b.scala b/tests/patmat/t11457b.scala new file mode 100644 index 000000000000..82e6b082c03f --- /dev/null +++ b/tests/patmat/t11457b.scala @@ -0,0 +1,24 @@ +sealed trait Command { + type Reply +} + +final case class Create() extends Command { + type Reply = CreateReply + val reply: Reply = CreateReply() +} + +final case class Set() extends Command { + type Reply = SetReply + val reply: Reply = SetReply() +} + +case class CreateReply() +case class SetReply() + +def process[R](command: Command { type Reply = R }): R = + command match { + case create: Create => create.reply + case set: Set => set.reply +// ^ +// Warning: unreachable code + } From aff3b0893cc98e8d27ed7ad6ae62266e4f7f1c5a Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Thu, 9 Apr 2020 16:10:34 +0200 Subject: [PATCH 07/13] Fix CI --- compiler/src/dotty/tools/dotc/transform/patmat/Space.scala | 4 +--- tests/neg-custom-args/isInstanceOf/enum-approx2.scala | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 12d3366d402e..bd525077adfe 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -466,9 +466,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { case tp @ RefinedType(parent, _, _) => erase(parent) case tref: TypeRef if isPatternTypeSymbol(tref.typeSymbol) => - if (inArray) tref.underlying else WildcardType(tref.underlying.bounds) - case mt: MethodType => - mt.derivedLambdaType(mt.paramNames, mt.paramInfos.map(info => erase(info)), erase(mt.resType)) + if (inArray) tref.underlying else WildcardType case _ => tp } } diff --git a/tests/neg-custom-args/isInstanceOf/enum-approx2.scala b/tests/neg-custom-args/isInstanceOf/enum-approx2.scala index ec72af6ed9ad..4319ba819114 100644 --- a/tests/neg-custom-args/isInstanceOf/enum-approx2.scala +++ b/tests/neg-custom-args/isInstanceOf/enum-approx2.scala @@ -2,7 +2,7 @@ sealed trait Exp[T] case class Fun[A, B](f: Exp[A => B]) extends Exp[A => B] class Test { - def eval[T](e: Exp[T]) = e match { + def eval[T](e: Exp[T]) = e match { // error case Fun(x: Fun[Int, Double]) => ??? // error case Fun(x: Exp[Int => String]) => ??? // error } From ba2b6784ed4635f0f12680dc28b9cfb45747650f Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Thu, 9 Apr 2020 16:24:31 +0200 Subject: [PATCH 08/13] Fix test We need to adapt the test, otherwise the exhaustivity warning, which comes before, will stop the compilation, the isInstanceOf warnings will not be issued. --- tests/neg-custom-args/isInstanceOf/enum-approx2.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/neg-custom-args/isInstanceOf/enum-approx2.scala b/tests/neg-custom-args/isInstanceOf/enum-approx2.scala index 4319ba819114..8350f9cf4b9c 100644 --- a/tests/neg-custom-args/isInstanceOf/enum-approx2.scala +++ b/tests/neg-custom-args/isInstanceOf/enum-approx2.scala @@ -2,7 +2,7 @@ sealed trait Exp[T] case class Fun[A, B](f: Exp[A => B]) extends Exp[A => B] class Test { - def eval[T](e: Exp[T]) = e match { // error + def eval(e: Fun[Int, Int]) = e match { case Fun(x: Fun[Int, Double]) => ??? // error case Fun(x: Exp[Int => String]) => ??? // error } From f0a060f2632cbb8f41e6705d0fe2bec23aed9c4b Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Thu, 9 Apr 2020 19:46:46 +0200 Subject: [PATCH 09/13] Add test --- tests/patmat/i8690b.scala | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 tests/patmat/i8690b.scala diff --git a/tests/patmat/i8690b.scala b/tests/patmat/i8690b.scala new file mode 100644 index 000000000000..311900712941 --- /dev/null +++ b/tests/patmat/i8690b.scala @@ -0,0 +1,4 @@ +def test[A, B](x: (A, B) | (B, A)) = x match { + case (u: A, v: B) => (u, v) + case (u: B, v: A) => (v, u) +} From 030249b183601fb80c35bdf98b1069b4e6120579 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Thu, 9 Apr 2020 21:00:23 +0200 Subject: [PATCH 10/13] Adapt test: use class instead of type params --- tests/patmat/i8690b.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/patmat/i8690b.scala b/tests/patmat/i8690b.scala index 311900712941..ad4452e784da 100644 --- a/tests/patmat/i8690b.scala +++ b/tests/patmat/i8690b.scala @@ -1,4 +1,7 @@ -def test[A, B](x: (A, B) | (B, A)) = x match { +class A +class B + +def test(x: (A, B) | (B, A)) = x match { case (u: A, v: B) => (u, v) case (u: B, v: A) => (v, u) } From d5f9047e48943bd232d9e9e3a04ded7d419a4ba3 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Sat, 11 Apr 2020 18:50:08 +0200 Subject: [PATCH 11/13] Fix #8708: treat guard as false --- .../src/dotty/tools/dotc/transform/patmat/Space.scala | 2 +- tests/patmat/i8708.check | 1 + tests/patmat/i8708.scala | 8 ++++++++ tests/patmat/patmatexhaust.check | 1 + tests/patmat/t7631.check | 2 +- tests/patmat/virtpatmat_apply.check | 2 +- 6 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 tests/patmat/i8708.check create mode 100644 tests/patmat/i8708.scala diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index bd525077adfe..a4fc29575c57 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -812,7 +812,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { if (!exhaustivityCheckable(sel)) return val patternSpace = cases.map({ x => - val space = project(x.pat) + val space = if (x.guard.isEmpty) project(x.pat) else Empty debug.println(s"${x.pat.show} ====> ${show(space)}") space }).reduce((a, b) => Or(List(a, b))) diff --git a/tests/patmat/i8708.check b/tests/patmat/i8708.check new file mode 100644 index 000000000000..c2e4655c3e0f --- /dev/null +++ b/tests/patmat/i8708.check @@ -0,0 +1 @@ +2: Pattern Match Exhaustivity: Some(_) diff --git a/tests/patmat/i8708.scala b/tests/patmat/i8708.scala new file mode 100644 index 000000000000..53c26921f8b3 --- /dev/null +++ b/tests/patmat/i8708.scala @@ -0,0 +1,8 @@ +object Main { + def foo(x: Option[Int]): Int = x match { + case Some(n) if n % 2 == 0 => n + case None => 0 + } + + def main(args: Array[String]): Unit = println(foo(Some(1))) +} \ No newline at end of file diff --git a/tests/patmat/patmatexhaust.check b/tests/patmat/patmatexhaust.check index 79b700ea37f6..d32bd2291194 100644 --- a/tests/patmat/patmatexhaust.check +++ b/tests/patmat/patmatexhaust.check @@ -2,6 +2,7 @@ 11: Pattern Match Exhaustivity: Bar(_) 23: Pattern Match Exhaustivity: (Qult(), Qult()), (Kult(_), Kult(_)) 49: Pattern Match Exhaustivity: _: Gp +53: Pattern Match Exhaustivity: _: Gp 59: Pattern Match Exhaustivity: Nil 75: Pattern Match Exhaustivity: _: B 87: Pattern Match Exhaustivity: _: C1 diff --git a/tests/patmat/t7631.check b/tests/patmat/t7631.check index 78dc1ac36c19..9e45c2cd7136 100644 --- a/tests/patmat/t7631.check +++ b/tests/patmat/t7631.check @@ -1 +1 @@ -8: Pattern Match Exhaustivity: TestB() +8: Pattern Match Exhaustivity: _: Test diff --git a/tests/patmat/virtpatmat_apply.check b/tests/patmat/virtpatmat_apply.check index aa4d0b884a7e..f84573ccee9c 100644 --- a/tests/patmat/virtpatmat_apply.check +++ b/tests/patmat/virtpatmat_apply.check @@ -1 +1 @@ -2: Pattern Match Exhaustivity: List(_) +2: Pattern Match Exhaustivity: List(_, _: _*) From 2d697454e8f598159f8896437c4e553559c19d66 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Sat, 11 Apr 2020 20:38:30 +0200 Subject: [PATCH 12/13] Fix exhaustivity warning --- compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala index 4b4a7d18667a..95415e7ceeff 100644 --- a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala +++ b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala @@ -144,7 +144,7 @@ object PickledQuotes { case tp: TypeRef => typeSpliceMap.get(tp.symbol) match case Some(t) if tp.typeSymbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot) => t - case None => tp + case _ => tp case _ => tp } mapOver(tp1) From 41f4cbbda1f3e27c0b168ba62b2d55eed4a56d23 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Sat, 11 Apr 2020 21:35:37 +0200 Subject: [PATCH 13/13] Add check file --- tests/patmat/exhaustive_heuristics.check | 1 + 1 file changed, 1 insertion(+) create mode 100644 tests/patmat/exhaustive_heuristics.check diff --git a/tests/patmat/exhaustive_heuristics.check b/tests/patmat/exhaustive_heuristics.check new file mode 100644 index 000000000000..ecfa6bbdb102 --- /dev/null +++ b/tests/patmat/exhaustive_heuristics.check @@ -0,0 +1 @@ +11: Pattern Match Exhaustivity: _: List