@@ -1450,6 +1450,27 @@ object desugar {
1450
1450
sel
1451
1451
end match
1452
1452
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
+
1453
1474
/** If `pat` is a variable pattern,
1454
1475
*
1455
1476
* val/var/lazy val p = e
@@ -1483,30 +1504,47 @@ object desugar {
1483
1504
|please bind to an identifier and use an alias given. """ , bind)
1484
1505
false
1485
1506
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)
1496
1532
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
1501
1539
1502
1540
val inAliasGenerator = original match
1503
1541
case _ : GenAlias => true
1504
1542
case _ => false
1505
1543
1506
- val vars =
1507
- if (tupleOptimizable) // include `_`
1544
+ val vars : List [ VarInfo ] =
1545
+ if fullTupleOptimizable || partialTupleOptimizable then // include `_`
1508
1546
pat match
1509
- case Tuple (pats) => pats.map { case id : Ident => id -> TypeTree () }
1547
+ case Tuple (pats) => pats.map { case id : Ident => (id, TypeTree () ) }
1510
1548
else
1511
1549
getVariables(
1512
1550
tree = pat,
@@ -1517,12 +1555,27 @@ object desugar {
1517
1555
errorOnGivenBinding
1518
1556
) // no `_`
1519
1557
1520
- val ids = for ((named, _) <- vars) yield Ident (named.name)
1558
+ val ids = for ((named, tpt) <- vars) yield Ident (named.name)
1559
+
1521
1560
val matchExpr =
1522
- if (tupleOptimizable) rhs
1561
+ if fullTupleOptimizable then rhs
1523
1562
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 , ()))
1525
1577
Match (makeSelector(rhs, MatchCheck .IrrefutablePatDef ), caseDef :: Nil )
1578
+
1526
1579
vars match {
1527
1580
case Nil if ! mods.is(Lazy ) =>
1528
1581
matchExpr
0 commit comments