Skip to content

Commit 5c4d462

Browse files
authored
Merge pull request #6309 from dotty-staging/fix-4935
Fix #4935: make desugaring optimization sound
2 parents 12adbea + 1e921b2 commit 5c4d462

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
@@ -939,16 +939,34 @@ object desugar {
939939
case IdPattern(named, tpt) =>
940940
derivedValDef(original, named, tpt, rhs, mods)
941941
case _ =>
942-
val rhsUnchecked = makeAnnotated("scala.unchecked", rhs)
943-
val vars = getVariables(pat)
942+
def isTuplePattern(arity: Int): Boolean = pat match {
943+
case Tuple(pats) if pats.size == arity =>
944+
pats.forall(isVarPattern)
945+
case _ => false
946+
}
944947
val isMatchingTuple: Tree => Boolean = {
945-
case Tuple(es) => es.length == vars.length
948+
case Tuple(es) => isTuplePattern(es.length)
946949
case _ => false
947950
}
951+
952+
// We can only optimize `val pat = if (...) e1 else e2` if:
953+
// - `e1` and `e2` are both tuples of arity N
954+
// - `pat` is a tuple of N variables or wildcard patterns like `(x1, x2, ..., xN)`
955+
val tupleOptimizable = forallResults(rhs, isMatchingTuple)
956+
957+
def rhsUnchecked = makeAnnotated("scala.unchecked", rhs)
958+
val vars =
959+
if (tupleOptimizable) // include `_`
960+
pat match {
961+
case Tuple(pats) =>
962+
pats.map { case id: Ident => id -> TypeTree() }
963+
}
964+
else getVariables(pat) // no `_`
965+
948966
val ids = for ((named, _) <- vars) yield Ident(named.name)
949967
val caseDef = CaseDef(pat, EmptyTree, makeTuple(ids))
950968
val matchExpr =
951-
if (forallResults(rhs, isMatchingTuple)) rhs
969+
if (tupleOptimizable) rhs
952970
else Match(rhsUnchecked, caseDef :: Nil)
953971
vars match {
954972
case Nil =>
@@ -964,7 +982,7 @@ object desugar {
964982
.withSpan(pat.span.union(rhs.span)).withMods(patMods)
965983
def selector(n: Int) = Select(Ident(tmpName), nme.selectorName(n))
966984
val restDefs =
967-
for (((named, tpt), n) <- vars.zipWithIndex)
985+
for (((named, tpt), n) <- vars.zipWithIndex if named.name != nme.WILDCARD)
968986
yield
969987
if (mods is Lazy) derivedDefDef(original, named, tpt, selector(n), mods &~ Lazy)
970988
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
@@ -2412,7 +2412,7 @@ object Parsers {
24122412
}
24132413
makeConstructor(Nil, vparamss, rhs).withMods(mods).setComment(in.getDocComment(start))
24142414
} else {
2415-
val (leadingParamss: List[List[ValDef]], flags: FlagSet) =
2415+
val (leadingParamss, flags) =
24162416
if (in.token == LPAREN)
24172417
(paramClause(prefix = true) :: Nil, Method | Extension)
24182418
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)