@@ -1450,6 +1450,34 @@ object desugar {
1450
1450
sel
1451
1451
end match
1452
1452
1453
+ case class TuplePatternInfo (arity : Int , varNum : Int , wildcardNum : Int , typedVarNum : Int , typedWildcardNum : 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
+ var typedVarNum = 0
1461
+ var typedWildcardNum = 0
1462
+ pats.foreach: p =>
1463
+ arity += 1
1464
+ p match
1465
+ case id : Ident if ! isBackquoted(id) =>
1466
+ if id.name.isVarPattern then
1467
+ varNum += 1
1468
+ if id.name == nme.WILDCARD then
1469
+ wildcardNum += 1
1470
+ case Typed (id : Ident , _) if ! isBackquoted(id) =>
1471
+ if id.name.isVarPattern then
1472
+ typedVarNum += 1
1473
+ if id.name == nme.WILDCARD then
1474
+ typedWildcardNum += 1
1475
+ case _ =>
1476
+ TuplePatternInfo (arity, varNum, wildcardNum, typedVarNum, typedWildcardNum)
1477
+ case _ =>
1478
+ TuplePatternInfo (- 1 , - 1 , - 1 , - 1 , - 1 )
1479
+ end TuplePatternInfo
1480
+
1453
1481
/** If `pat` is a variable pattern,
1454
1482
*
1455
1483
* val/var/lazy val p = e
@@ -1483,35 +1511,50 @@ object desugar {
1483
1511
|please bind to an identifier and use an alias given. """ , bind)
1484
1512
false
1485
1513
1486
- // The arity of the tuple pattern if it only contains simple variables or wildcards.
1487
- val varTuplePatternArity = pat match {
1488
- case Tuple (pats) if pats.forall(isVarPattern) => pats.length
1489
- case _ => - 1
1490
- }
1491
-
1492
- val nonWildcardVars = pat match {
1493
- case Tuple (pats) => pats.filterNot(isWildcardPattern).length
1494
- case _ => - 1
1495
- }
1496
-
1497
- val isMatchingTuple : Tree => Boolean = {
1498
- case Tuple (es) => varTuplePatternArity == es.length && ! hasNamedArg(es)
1499
- case _ => false
1500
- }
1514
+ val tuplePatternInfo = TuplePatternInfo (pat)
1515
+
1516
+ // When desugaring a PatDef in general, we use pattern matching on the rhs
1517
+ // and collect the variable values in a tuple, then outside the match
1518
+ // we destructure the tuple to get the individual variables.
1519
+ // We can achieve two kinds of tuple optimizations if the pattern is a tuple
1520
+ // of simple variables or wildcards:
1521
+ // 1. Full optimization:
1522
+ // If the rhs is known to produce a literal tuple of the same arity,
1523
+ // we can directly fetch the values from the tuple.
1524
+ // For example: `val (x, y) = if ... then (1, "a") else (2, "b")` becomes
1525
+ // `val $1$ = if ...; val x = $1$._1; val y = $1$._2`.
1526
+ // 2. Partial optimization:
1527
+ // If the rhs can be typed as a tuple and matched with correct arity,
1528
+ // we can return the tuple itself if there are no more than one variable
1529
+ // in the pattern, or return the the value if there is only one variable.
1530
+
1531
+ val fullTupleOptimizable =
1532
+ val isMatchingTuple : Tree => Boolean = {
1533
+ case Tuple (es) => tuplePatternInfo.varNum == es.length && ! hasNamedArg(es)
1534
+ case _ => false
1535
+ }
1536
+ tuplePatternInfo.arity > 0
1537
+ && tuplePatternInfo.arity == tuplePatternInfo.varNum
1538
+ && forallResults(rhs, isMatchingTuple)
1501
1539
1502
- // We can only optimize `val pat = if (...) e1 else e2` if:
1503
- // - `e1` and `e2` are both literal tuples of arity N
1504
- // - `pat` is a tuple of N variables or wildcard patterns like `(x1, x2, ..., xN)`
1505
- val tupleOptimizable = forallResults(rhs, isMatchingTuple)
1540
+ val partialTupleOptimizable =
1541
+ tuplePatternInfo.arity > 0
1542
+ && tuplePatternInfo.arity == tuplePatternInfo.varNum + tuplePatternInfo.typedVarNum
1543
+ // We exclude the case where there is only one variable,
1544
+ // because it should be handled by `makeTuple` directly.
1545
+ && tuplePatternInfo.wildcardNum + tuplePatternInfo.typedWildcardNum < tuplePatternInfo.arity - 1
1506
1546
1507
1547
val inAliasGenerator = original match
1508
1548
case _ : GenAlias => true
1509
1549
case _ => false
1510
1550
1511
- val vars =
1512
- if varTuplePatternArity > 0 && nonWildcardVars > 1 then // include `_`
1551
+ val vars : List [ VarInfo ] =
1552
+ if fullTupleOptimizable || partialTupleOptimizable then // include `_`
1513
1553
pat match
1514
- case Tuple (pats) => pats.map { case id : Ident => id -> TypeTree () }
1554
+ case Tuple (pats) => pats.map {
1555
+ case id : Ident => (id, TypeTree ())
1556
+ case Typed (id : Ident , tpt) => (id, tpt)
1557
+ }
1515
1558
else
1516
1559
getVariables(
1517
1560
tree = pat,
@@ -1522,22 +1565,24 @@ object desugar {
1522
1565
errorOnGivenBinding
1523
1566
) // no `_`
1524
1567
1525
- val ids = for ((named, _) <- vars) yield Ident (named.name)
1568
+ val ids = for ((named, tpt) <- vars) yield Ident (named.name)
1569
+
1570
+ // println(s"fullTupleOptimizable = $fullTupleOptimizable, partialTupleOptimizable = $partialTupleOptimizable, ids = $ids")
1571
+
1526
1572
val matchExpr =
1527
- if tupleOptimizable then rhs
1573
+ if fullTupleOptimizable then rhs
1528
1574
else
1529
1575
val caseDef =
1530
- if varTuplePatternArity > 0 && ids.length > 1 then
1531
- // If the pattern contains only simple variables or wildcards,
1532
- // we don't need to create a new tuple.
1533
- // If there is only one variable (ids.length == 1),
1534
- // `makeTuple` will optimize it to `Ident(named)`,
1535
- // so we don't need to handle that case here.
1576
+ if partialTupleOptimizable then
1536
1577
val tmpTuple = UniqueName .fresh()
1537
1578
// Replace all variables with wildcards in the pattern
1538
1579
val pat1 = pat match
1539
1580
case Tuple (pats) =>
1540
- Tuple (pats.map(pat => Ident (nme.WILDCARD ).withSpan(pat.span)))
1581
+ val wildcardPats = pats.map {
1582
+ case id : Ident => Ident (nme.WILDCARD ).withSpan(id.span)
1583
+ case p @ Typed (_ : Ident , tpt) => Typed (Ident (nme.WILDCARD ), tpt).withSpan(p.span)
1584
+ }
1585
+ Tuple (wildcardPats).withSpan(pat.span)
1541
1586
CaseDef (
1542
1587
Bind (tmpTuple, pat1),
1543
1588
EmptyTree ,
@@ -1546,6 +1591,8 @@ object desugar {
1546
1591
else CaseDef (pat, EmptyTree , makeTuple(ids).withAttachment(ForArtifact , ()))
1547
1592
Match (makeSelector(rhs, MatchCheck .IrrefutablePatDef ), caseDef :: Nil )
1548
1593
1594
+ // println(i"matchExpr = $matchExpr")
1595
+
1549
1596
vars match {
1550
1597
case Nil if ! mods.is(Lazy ) =>
1551
1598
matchExpr
0 commit comments