Skip to content

Commit 911f05d

Browse files
committed
Fix #4935: make desugaring of PatDef sound
We can only optimize `val pat = if (...) e1 else e2` if: - `e1` and `e2` are both tuples of arity N - `pat` is a tuple of N variables or wildcard patterns like `(x1, x2, ..., xN)` Otherwise, we need to generate pattern matches.
1 parent 25a3773 commit 911f05d

File tree

5 files changed

+37
-6
lines changed

5 files changed

+37
-6
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -913,16 +913,34 @@ object desugar {
913913
case IdPattern(named, tpt) =>
914914
derivedValDef(original, named, tpt, rhs, mods)
915915
case _ =>
916-
val rhsUnchecked = makeAnnotated("scala.unchecked", rhs)
917-
val vars = getVariables(pat)
916+
def isTuplePattern(arity: Int): Boolean = pat match {
917+
case Tuple(pats) if pats.size == arity =>
918+
pats.forall(id => id.isInstanceOf[Ident] && isVarPattern(id))
919+
case _ => false
920+
}
918921
val isMatchingTuple: Tree => Boolean = {
919-
case Tuple(es) => es.length == vars.length
922+
case Tuple(es) => isTuplePattern(es.length)
920923
case _ => false
921924
}
925+
926+
// We can only optimize `val pat = if (...) e1 else e2` if:
927+
// - `e1` and `e2` are both tuples of arity N
928+
// - `pat` is a tuple of N variables or wildcard patterns like `(x1, x2, ..., xN)`
929+
val tupleOptimizable = forallResults(rhs, isMatchingTuple)
930+
931+
val rhsUnchecked = makeAnnotated("scala.unchecked", rhs)
932+
val vars =
933+
if (tupleOptimizable) // include `_`
934+
pat match {
935+
case Tuple(pats) =>
936+
pats.map { case id: Ident => id -> TypeTree() }
937+
}
938+
else getVariables(pat) // no `_`
939+
922940
val ids = for ((named, _) <- vars) yield Ident(named.name)
923941
val caseDef = CaseDef(pat, EmptyTree, makeTuple(ids))
924942
val matchExpr =
925-
if (forallResults(rhs, isMatchingTuple)) rhs
943+
if (tupleOptimizable) rhs
926944
else Match(rhsUnchecked, caseDef :: Nil)
927945
vars match {
928946
case Nil =>
@@ -938,7 +956,7 @@ object desugar {
938956
.withSpan(pat.span.union(rhs.span)).withMods(patMods)
939957
def selector(n: Int) = Select(Ident(tmpName), nme.selectorName(n))
940958
val restDefs =
941-
for (((named, tpt), n) <- vars.zipWithIndex)
959+
for (((named, tpt), n) <- vars.zipWithIndex if named.name != nme.WILDCARD)
942960
yield
943961
if (mods is Lazy) derivedDefDef(original, named, tpt, selector(n), mods &~ Lazy)
944962
else derivedValDef(original, named, tpt, selector(n), mods)

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2405,7 +2405,7 @@ object Parsers {
24052405
}
24062406
makeConstructor(Nil, vparamss, rhs).withMods(mods).setComment(in.getDocComment(start))
24072407
} else {
2408-
val (leadingParamss: List[List[ValDef]], flags: FlagSet) =
2408+
val (leadingParamss, flags) =
24092409
if (in.token == LPAREN)
24102410
(paramClause(prefix = true) :: Nil, Method | Extension)
24112411
else

tests/neg/i4935.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object Foo {
2+
val (A, B) = () // error // error
3+
}

tests/run/i4935.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
object Test {
2+
val (Some(a), b) = (Some(3), 6)
3+
4+
def main(args: Array[String]) = assert(a == 3)
5+
}

tests/run/i4935b.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
object Test {
2+
val (a, _, b) = (Some(3), 6, Some(9))
3+
4+
def main(args: Array[String]) = assert(a == Some(3) && b == Some(9))
5+
}

0 commit comments

Comments
 (0)