@@ -1450,6 +1450,27 @@ object desugar {
14501450 sel
14511451 end match
14521452
1453+ case class TuplePatternInfo (arity : Int , varNum : Int , wildcardNum : Int )
1454+ object TuplePatternInfo :
1455+ def apply (pat : Tree )(using Context ): TuplePatternInfo = pat match
1456+ case Tuple (pats) =>
1457+ var arity = 0
1458+ var varNum = 0
1459+ var wildcardNum = 0
1460+ pats.foreach: p =>
1461+ arity += 1
1462+ p match
1463+ case id : Ident if ! isBackquoted(id) =>
1464+ if id.name.isVarPattern then
1465+ varNum += 1
1466+ if id.name == nme.WILDCARD then
1467+ wildcardNum += 1
1468+ case _ =>
1469+ TuplePatternInfo (arity, varNum, wildcardNum)
1470+ case _ =>
1471+ TuplePatternInfo (- 1 , - 1 , - 1 )
1472+ end TuplePatternInfo
1473+
14531474 /** If `pat` is a variable pattern,
14541475 *
14551476 * val/var/lazy val p = e
@@ -1483,30 +1504,47 @@ object desugar {
14831504 |please bind to an identifier and use an alias given. """ , bind)
14841505 false
14851506
1486- def isTuplePattern (arity : Int ): Boolean = pat match {
1487- case Tuple (pats) if pats.size == arity =>
1488- pats.forall(isVarPattern)
1489- case _ => false
1490- }
1491-
1492- val isMatchingTuple : Tree => Boolean = {
1493- case Tuple (es) => isTuplePattern(es.length) && ! hasNamedArg(es)
1494- case _ => false
1495- }
1507+ val tuplePatternInfo = TuplePatternInfo (pat)
1508+
1509+ // When desugaring a PatDef in general, we use pattern matching on the rhs
1510+ // and collect the variable values in a tuple, then outside the match,
1511+ // we destructure the tuple to get the individual variables.
1512+ // We can achieve two kinds of tuple optimizations if the pattern is a tuple
1513+ // of simple variables or wildcards:
1514+ // 1. Full optimization:
1515+ // If the rhs is known to produce a literal tuple of the same arity,
1516+ // we can directly fetch the values from the tuple.
1517+ // For example: `val (x, y) = if ... then (1, "a") else (2, "b")` becomes
1518+ // `val $1$ = if ...; val x = $1$._1; val y = $1$._2`.
1519+ // 2. Partial optimization:
1520+ // If the rhs can be typed as a tuple and matched with correct arity, we can
1521+ // return the tuple itself in the case if there are no more than one variable
1522+ // in the pattern, or return the the value if there is only one variable.
1523+
1524+ val fullTupleOptimizable =
1525+ val isMatchingTuple : Tree => Boolean = {
1526+ case Tuple (es) => tuplePatternInfo.varNum == es.length && ! hasNamedArg(es)
1527+ case _ => false
1528+ }
1529+ tuplePatternInfo.arity > 0
1530+ && tuplePatternInfo.arity == tuplePatternInfo.varNum
1531+ && forallResults(rhs, isMatchingTuple)
14961532
1497- // We can only optimize `val pat = if (...) e1 else e2` if:
1498- // - `e1` and `e2` are both tuples of arity N
1499- // - `pat` is a tuple of N variables or wildcard patterns like `(x1, x2, ..., xN)`
1500- val tupleOptimizable = forallResults(rhs, isMatchingTuple)
1533+ val partialTupleOptimizable =
1534+ tuplePatternInfo.arity > 0
1535+ && tuplePatternInfo.arity == tuplePatternInfo.varNum
1536+ // We exclude the case where there is only one variable,
1537+ // because it should be handled by `makeTuple` directly.
1538+ && tuplePatternInfo.wildcardNum < tuplePatternInfo.arity - 1
15011539
15021540 val inAliasGenerator = original match
15031541 case _ : GenAlias => true
15041542 case _ => false
15051543
1506- val vars =
1507- if (tupleOptimizable) // include `_`
1544+ val vars : List [ VarInfo ] =
1545+ if fullTupleOptimizable || partialTupleOptimizable then // include `_`
15081546 pat match
1509- case Tuple (pats) => pats.map { case id : Ident => id -> TypeTree () }
1547+ case Tuple (pats) => pats.map { case id : Ident => (id, TypeTree () ) }
15101548 else
15111549 getVariables(
15121550 tree = pat,
@@ -1517,12 +1555,27 @@ object desugar {
15171555 errorOnGivenBinding
15181556 ) // no `_`
15191557
1520- val ids = for ((named, _) <- vars) yield Ident (named.name)
1558+ val ids = for ((named, tpt) <- vars) yield Ident (named.name)
1559+
15211560 val matchExpr =
1522- if (tupleOptimizable) rhs
1561+ if fullTupleOptimizable then rhs
15231562 else
1524- val caseDef = CaseDef (pat, EmptyTree , makeTuple(ids).withAttachment(ForArtifact , ()))
1563+ val caseDef =
1564+ if partialTupleOptimizable then
1565+ val tmpTuple = UniqueName .fresh()
1566+ // Replace all variables with wildcards in the pattern
1567+ val pat1 = pat match
1568+ case Tuple (pats) =>
1569+ val wildcardPats = pats.map(p => Ident (nme.WILDCARD ).withSpan(p.span))
1570+ Tuple (wildcardPats).withSpan(pat.span)
1571+ CaseDef (
1572+ Bind (tmpTuple, pat1),
1573+ EmptyTree ,
1574+ Ident (tmpTuple).withAttachment(ForArtifact , ())
1575+ )
1576+ else CaseDef (pat, EmptyTree , makeTuple(ids).withAttachment(ForArtifact , ()))
15251577 Match (makeSelector(rhs, MatchCheck .IrrefutablePatDef ), caseDef :: Nil )
1578+
15261579 vars match {
15271580 case Nil if ! mods.is(Lazy ) =>
15281581 matchExpr
0 commit comments