From 863979350bd9c775855a32a852f4531f386abb9e Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Wed, 27 Sep 2017 02:22:36 +0200 Subject: [PATCH 1/3] fix #3182: return correct signatures for extractors --- .../tools/dotc/transform/patmat/Space.scala | 38 +++++++++++-------- tests/patmat/t10502.check | 2 + tests/patmat/t10502.scala | 20 ++++++++++ 3 files changed, 44 insertions(+), 16 deletions(-) create mode 100644 tests/patmat/t10502.check create mode 100644 tests/patmat/t10502.scala diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index da53a106edad..ed070bbdfc67 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -451,30 +451,36 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { /** Parameter types of the case class type `tp`. Adapted from `unapplyPlan` in patternMatcher */ def signature(unapp: Type, unappSym: Symbol, argLen: Int): List[Type] = { def caseClass = unappSym.owner.linkedClass + lazy val caseAccessors = caseClass.caseAccessors.filter(_.is(Method)) def isSyntheticScala2Unapply(sym: Symbol) = sym.is(SyntheticCase) && sym.owner.is(Scala2x) - val mt @ MethodType(_) = unapp.widen + val mt@MethodType(_) = unapp.widen - if (isSyntheticScala2Unapply(unappSym) && caseAccessors.length == argLen) - caseAccessors.map(_.info.asSeenFrom(mt.paramInfos.head, caseClass).widen) - else if (mt.resultType.isRef(defn.BooleanClass)) - List() - else { - val isUnapplySeq = unappSym.name == nme.unapplySeq - if (isProductMatch(mt.resultType, argLen) && !isUnapplySeq) { - productSelectors(mt.resultType).take(argLen) - .map(_.info.asSeenFrom(mt.resultType, mt.resultType.classSymbol).widen) - } + val sig = + if (isSyntheticScala2Unapply(unappSym) && caseAccessors.length == argLen) + caseAccessors.map(_.info.asSeenFrom(mt.paramInfos.head, caseClass).widen) + else if (mt.resultType.isRef(defn.BooleanClass)) + List() else { - val resTp = mt.resultType.select(nme.get).resultType.widen - if (isUnapplySeq) scalaListType.appliedTo(resTp.argTypes.head) :: Nil - else if (argLen == 0) Nil - else productSelectors(resTp).map(_.info.asSeenFrom(resTp, resTp.classSymbol).widen) + val isUnapplySeq = unappSym.name == nme.unapplySeq + if (isProductMatch(mt.resultType, argLen) && !isUnapplySeq) { + productSelectors(mt.resultType).take(argLen) + .map(_.info.asSeenFrom(mt.resultType, mt.resultType.classSymbol).widen) + } + else { + val resTp = mt.resultType.select(nme.get).resultType.widen + if (isUnapplySeq) scalaListType.appliedTo(resTp.argTypes.head) :: Nil + else if (argLen == 0) Nil + else resTp :: Nil + } } - } + + debug.println(s"signature of ${unappSym.showFullName} ----> ${sig.map(_.show).mkString(", ")}") + + sig } /** Decompose a type into subspaces -- assume the type can be decomposed */ diff --git a/tests/patmat/t10502.check b/tests/patmat/t10502.check new file mode 100644 index 000000000000..4c514eca2799 --- /dev/null +++ b/tests/patmat/t10502.check @@ -0,0 +1,2 @@ +5: Pattern Match Exhaustivity: Option(None) +15: Pattern Match Exhaustivity: Nil diff --git a/tests/patmat/t10502.scala b/tests/patmat/t10502.scala new file mode 100644 index 000000000000..31f4c19c796c --- /dev/null +++ b/tests/patmat/t10502.scala @@ -0,0 +1,20 @@ +object Perhaps { + + def unapply[A](oa: Option[A]): Some[Option[A]] = Some(oa) + + Option("hello") match { + case Perhaps(Some(s)) => println(s) + } + + List(Option("hello")) match { + case Perhaps(Some(s)) :: t => println(s) + case Perhaps(None ) :: t => () + case Nil => () + } + + List(Option("hello")) match { + case Perhaps(Some(s)) :: t => println(s) + case Perhaps(None ) :: t => () + // case Nil => () + } +} From 17694b2ab2cbde0cc7ffd9ae47007f9ee751c213 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Wed, 27 Sep 2017 21:09:39 +0200 Subject: [PATCH 2/3] fix signature if unapply return Option[(T1, T2)] --- .../dotty/tools/dotc/transform/patmat/Space.scala | 6 ++++-- tests/patmat/t10502.check | 4 +++- tests/patmat/t10502.scala | 14 ++++++++++++++ 3 files changed, 21 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 ed070bbdfc67..a1be1e640ff7 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -474,6 +474,8 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { val resTp = mt.resultType.select(nme.get).resultType.widen if (isUnapplySeq) scalaListType.appliedTo(resTp.argTypes.head) :: Nil else if (argLen == 0) Nil + else if (isProductMatch(resTp, argLen)) + productSelectors(resTp).map(_.info.asSeenFrom(resTp, resTp.classSymbol).widen) else resTp :: Nil } } @@ -711,14 +713,14 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { showType(tp) + params(tp).map(_ => "_").mkString("(", ", ", ")") else if (decomposed) "_: " + showType(tp) else "_" - case Prod(tp, fun, _, params, _) => + case Prod(tp, fun, sym, params, _) => if (ctx.definitions.isTupleType(tp)) "(" + params.map(doShow(_)).mkString(", ") + ")" else if (tp.isRef(scalaConsType.symbol)) if (mergeList) params.map(doShow(_, mergeList)).mkString(", ") else params.map(doShow(_, true)).filter(_ != "Nil").mkString("List(", ", ", ")") else - showType(tp) + params.map(doShow(_)).mkString("(", ", ", ")") + showType(sym.owner.typeRef) + params.map(doShow(_)).mkString("(", ", ", ")") case Or(_) => throw new Exception("incorrect flatten result " + s) } diff --git a/tests/patmat/t10502.check b/tests/patmat/t10502.check index 4c514eca2799..806ade731cb9 100644 --- a/tests/patmat/t10502.check +++ b/tests/patmat/t10502.check @@ -1,2 +1,4 @@ -5: Pattern Match Exhaustivity: Option(None) +5: Pattern Match Exhaustivity: Perhaps(None) 15: Pattern Match Exhaustivity: Nil +31: Pattern Match Exhaustivity: Multi(None, _) + diff --git a/tests/patmat/t10502.scala b/tests/patmat/t10502.scala index 31f4c19c796c..3abd17fb7ee2 100644 --- a/tests/patmat/t10502.scala +++ b/tests/patmat/t10502.scala @@ -17,4 +17,18 @@ object Perhaps { case Perhaps(None ) :: t => () // case Nil => () } + +} + +object Multi { + def unapply(str: String): Some[(Option[Int], Int)] = ??? + + "hello" match { + case Multi(Some(i), x) => + case Multi(None, x) => + } + + "hello" match { + case Multi(Some(i), x) => + } } From 7fa4e6c9beeb7952594dc7d1ff3990a08e5a6ea1 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Wed, 27 Sep 2017 22:07:57 +0200 Subject: [PATCH 3/3] document signature and add product pattern test --- .../tools/dotc/transform/patmat/Space.scala | 27 +++++++++++++------ tests/patmat/optionless.check | 2 -- tests/patmat/t10502.check | 2 +- tests/patmat/t10502.scala | 13 +++++++++ 4 files changed, 33 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index a1be1e640ff7..82c5bd64f34b 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -377,8 +377,9 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { /* Whether the extractor is irrefutable */ def irrefutable(unapp: tpd.Tree): Boolean = { // TODO: optionless patmat - unapp.tpe.widen.resultType.isRef(scalaSomeClass) || - (unapp.symbol.is(Synthetic) && unapp.symbol.owner.linkedClass.is(Case)) + unapp.tpe.widen.finalResultType.isRef(scalaSomeClass) || + (unapp.symbol.is(Synthetic) && unapp.symbol.owner.linkedClass.is(Case)) || + productArity(unapp.tpe.widen.finalResultType) > 0 } /** Return the space that represents the pattern `pat` @@ -457,21 +458,31 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { def isSyntheticScala2Unapply(sym: Symbol) = sym.is(SyntheticCase) && sym.owner.is(Scala2x) - val mt@MethodType(_) = unapp.widen + val mt @ MethodType(_) = unapp.widen + + // Case unapply: + // 1. return types of constructor fields if the extractor is synthesized for Scala2 case classes & length match + // 2. return Nil if unapply returns Boolean (boolean pattern) + // 3. return product selector types if unapply returns a product type (product pattern) + // 4. return product selectors of `T` where `def get: T` is a member of the return type of unapply & length match (named-based pattern) + // 5. otherwise, return `T` where `def get: T` is a member of the return type of unapply + // + // Case unapplySeq: + // 1. return the type `List[T]` where `T` is the element type of the unapplySeq return type `Seq[T]` val sig = if (isSyntheticScala2Unapply(unappSym) && caseAccessors.length == argLen) caseAccessors.map(_.info.asSeenFrom(mt.paramInfos.head, caseClass).widen) - else if (mt.resultType.isRef(defn.BooleanClass)) + else if (mt.finalResultType.isRef(defn.BooleanClass)) List() else { val isUnapplySeq = unappSym.name == nme.unapplySeq - if (isProductMatch(mt.resultType, argLen) && !isUnapplySeq) { - productSelectors(mt.resultType).take(argLen) - .map(_.info.asSeenFrom(mt.resultType, mt.resultType.classSymbol).widen) + if (isProductMatch(mt.finalResultType, argLen) && !isUnapplySeq) { + productSelectors(mt.finalResultType).take(argLen) + .map(_.info.asSeenFrom(mt.finalResultType, mt.resultType.classSymbol).widen) } else { - val resTp = mt.resultType.select(nme.get).resultType.widen + val resTp = mt.finalResultType.select(nme.get).finalResultType.widen if (isUnapplySeq) scalaListType.appliedTo(resTp.argTypes.head) :: Nil else if (argLen == 0) Nil else if (isProductMatch(resTp, argLen)) diff --git a/tests/patmat/optionless.check b/tests/patmat/optionless.check index 350b9178c50d..c199746c1e0e 100644 --- a/tests/patmat/optionless.check +++ b/tests/patmat/optionless.check @@ -1,3 +1 @@ -20: Pattern Match Exhaustivity: _: Tree -24: Pattern Match Exhaustivity: _: Tree 28: Pattern Match Exhaustivity: _: Tree diff --git a/tests/patmat/t10502.check b/tests/patmat/t10502.check index 806ade731cb9..b92e0d7cae8a 100644 --- a/tests/patmat/t10502.check +++ b/tests/patmat/t10502.check @@ -1,4 +1,4 @@ 5: Pattern Match Exhaustivity: Perhaps(None) 15: Pattern Match Exhaustivity: Nil 31: Pattern Match Exhaustivity: Multi(None, _) - +44: Pattern Match Exhaustivity: Prod(None, _) diff --git a/tests/patmat/t10502.scala b/tests/patmat/t10502.scala index 3abd17fb7ee2..adf6b4df49fa 100644 --- a/tests/patmat/t10502.scala +++ b/tests/patmat/t10502.scala @@ -32,3 +32,16 @@ object Multi { case Multi(Some(i), x) => } } + +object Prod { + def unapply(str: String): (Option[Int], Int) = ??? + + "hello" match { + case Prod(Some(i), x) => + case Prod(None, x) => + } + + "hello" match { + case Prod(Some(i), x) => + } +}