Skip to content

Commit b78c050

Browse files
authored
Merge pull request #3184 from dotty-staging/fix-3182
Fix #3182: return correct signatures for extractors
2 parents 0baa6dc + 7fa4e6c commit b78c050

File tree

4 files changed

+89
-21
lines changed

4 files changed

+89
-21
lines changed

compiler/src/dotty/tools/dotc/transform/patmat/Space.scala

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -377,8 +377,9 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
377377
/* Whether the extractor is irrefutable */
378378
def irrefutable(unapp: tpd.Tree): Boolean = {
379379
// TODO: optionless patmat
380-
unapp.tpe.widen.resultType.isRef(scalaSomeClass) ||
381-
(unapp.symbol.is(Synthetic) && unapp.symbol.owner.linkedClass.is(Case))
380+
unapp.tpe.widen.finalResultType.isRef(scalaSomeClass) ||
381+
(unapp.symbol.is(Synthetic) && unapp.symbol.owner.linkedClass.is(Case)) ||
382+
productArity(unapp.tpe.widen.finalResultType) > 0
382383
}
383384

384385
/** Return the space that represents the pattern `pat`
@@ -451,30 +452,48 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
451452
/** Parameter types of the case class type `tp`. Adapted from `unapplyPlan` in patternMatcher */
452453
def signature(unapp: Type, unappSym: Symbol, argLen: Int): List[Type] = {
453454
def caseClass = unappSym.owner.linkedClass
455+
454456
lazy val caseAccessors = caseClass.caseAccessors.filter(_.is(Method))
455457

456458
def isSyntheticScala2Unapply(sym: Symbol) =
457459
sym.is(SyntheticCase) && sym.owner.is(Scala2x)
458460

459461
val mt @ MethodType(_) = unapp.widen
460462

461-
if (isSyntheticScala2Unapply(unappSym) && caseAccessors.length == argLen)
462-
caseAccessors.map(_.info.asSeenFrom(mt.paramInfos.head, caseClass).widen)
463-
else if (mt.resultType.isRef(defn.BooleanClass))
464-
List()
465-
else {
466-
val isUnapplySeq = unappSym.name == nme.unapplySeq
467-
if (isProductMatch(mt.resultType, argLen) && !isUnapplySeq) {
468-
productSelectors(mt.resultType).take(argLen)
469-
.map(_.info.asSeenFrom(mt.resultType, mt.resultType.classSymbol).widen)
470-
}
463+
// Case unapply:
464+
// 1. return types of constructor fields if the extractor is synthesized for Scala2 case classes & length match
465+
// 2. return Nil if unapply returns Boolean (boolean pattern)
466+
// 3. return product selector types if unapply returns a product type (product pattern)
467+
// 4. return product selectors of `T` where `def get: T` is a member of the return type of unapply & length match (named-based pattern)
468+
// 5. otherwise, return `T` where `def get: T` is a member of the return type of unapply
469+
//
470+
// Case unapplySeq:
471+
// 1. return the type `List[T]` where `T` is the element type of the unapplySeq return type `Seq[T]`
472+
473+
val sig =
474+
if (isSyntheticScala2Unapply(unappSym) && caseAccessors.length == argLen)
475+
caseAccessors.map(_.info.asSeenFrom(mt.paramInfos.head, caseClass).widen)
476+
else if (mt.finalResultType.isRef(defn.BooleanClass))
477+
List()
471478
else {
472-
val resTp = mt.resultType.select(nme.get).resultType.widen
473-
if (isUnapplySeq) scalaListType.appliedTo(resTp.argTypes.head) :: Nil
474-
else if (argLen == 0) Nil
475-
else productSelectors(resTp).map(_.info.asSeenFrom(resTp, resTp.classSymbol).widen)
479+
val isUnapplySeq = unappSym.name == nme.unapplySeq
480+
if (isProductMatch(mt.finalResultType, argLen) && !isUnapplySeq) {
481+
productSelectors(mt.finalResultType).take(argLen)
482+
.map(_.info.asSeenFrom(mt.finalResultType, mt.resultType.classSymbol).widen)
483+
}
484+
else {
485+
val resTp = mt.finalResultType.select(nme.get).finalResultType.widen
486+
if (isUnapplySeq) scalaListType.appliedTo(resTp.argTypes.head) :: Nil
487+
else if (argLen == 0) Nil
488+
else if (isProductMatch(resTp, argLen))
489+
productSelectors(resTp).map(_.info.asSeenFrom(resTp, resTp.classSymbol).widen)
490+
else resTp :: Nil
491+
}
476492
}
477-
}
493+
494+
debug.println(s"signature of ${unappSym.showFullName} ----> ${sig.map(_.show).mkString(", ")}")
495+
496+
sig
478497
}
479498

480499
/** Decompose a type into subspaces -- assume the type can be decomposed */
@@ -705,14 +724,14 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
705724
showType(tp) + params(tp).map(_ => "_").mkString("(", ", ", ")")
706725
else if (decomposed) "_: " + showType(tp)
707726
else "_"
708-
case Prod(tp, fun, _, params, _) =>
727+
case Prod(tp, fun, sym, params, _) =>
709728
if (ctx.definitions.isTupleType(tp))
710729
"(" + params.map(doShow(_)).mkString(", ") + ")"
711730
else if (tp.isRef(scalaConsType.symbol))
712731
if (mergeList) params.map(doShow(_, mergeList)).mkString(", ")
713732
else params.map(doShow(_, true)).filter(_ != "Nil").mkString("List(", ", ", ")")
714733
else
715-
showType(tp) + params.map(doShow(_)).mkString("(", ", ", ")")
734+
showType(sym.owner.typeRef) + params.map(doShow(_)).mkString("(", ", ", ")")
716735
case Or(_) =>
717736
throw new Exception("incorrect flatten result " + s)
718737
}

tests/patmat/optionless.check

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
20: Pattern Match Exhaustivity: _: Tree
2-
24: Pattern Match Exhaustivity: _: Tree
31
28: Pattern Match Exhaustivity: _: Tree

tests/patmat/t10502.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
5: Pattern Match Exhaustivity: Perhaps(None)
2+
15: Pattern Match Exhaustivity: Nil
3+
31: Pattern Match Exhaustivity: Multi(None, _)
4+
44: Pattern Match Exhaustivity: Prod(None, _)

tests/patmat/t10502.scala

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
object Perhaps {
2+
3+
def unapply[A](oa: Option[A]): Some[Option[A]] = Some(oa)
4+
5+
Option("hello") match {
6+
case Perhaps(Some(s)) => println(s)
7+
}
8+
9+
List(Option("hello")) match {
10+
case Perhaps(Some(s)) :: t => println(s)
11+
case Perhaps(None ) :: t => ()
12+
case Nil => ()
13+
}
14+
15+
List(Option("hello")) match {
16+
case Perhaps(Some(s)) :: t => println(s)
17+
case Perhaps(None ) :: t => ()
18+
// case Nil => ()
19+
}
20+
21+
}
22+
23+
object Multi {
24+
def unapply(str: String): Some[(Option[Int], Int)] = ???
25+
26+
"hello" match {
27+
case Multi(Some(i), x) =>
28+
case Multi(None, x) =>
29+
}
30+
31+
"hello" match {
32+
case Multi(Some(i), x) =>
33+
}
34+
}
35+
36+
object Prod {
37+
def unapply(str: String): (Option[Int], Int) = ???
38+
39+
"hello" match {
40+
case Prod(Some(i), x) =>
41+
case Prod(None, x) =>
42+
}
43+
44+
"hello" match {
45+
case Prod(Some(i), x) =>
46+
}
47+
}

0 commit comments

Comments
 (0)