@@ -1450,6 +1450,34 @@ object desugar {
14501450 sel
14511451 end match
14521452
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+
14531481 /** If `pat` is a variable pattern,
14541482 *
14551483 * val/var/lazy val p = e
@@ -1483,35 +1511,50 @@ object desugar {
14831511 |please bind to an identifier and use an alias given. """ , bind)
14841512 false
14851513
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)
15011539
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
15061546
15071547 val inAliasGenerator = original match
15081548 case _ : GenAlias => true
15091549 case _ => false
15101550
1511- val vars =
1512- if varTuplePatternArity > 0 && nonWildcardVars > 1 then // include `_`
1551+ val vars : List [ VarInfo ] =
1552+ if fullTupleOptimizable || partialTupleOptimizable then // include `_`
15131553 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+ }
15151558 else
15161559 getVariables(
15171560 tree = pat,
@@ -1522,22 +1565,24 @@ object desugar {
15221565 errorOnGivenBinding
15231566 ) // no `_`
15241567
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+
15261572 val matchExpr =
1527- if tupleOptimizable then rhs
1573+ if fullTupleOptimizable then rhs
15281574 else
15291575 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
15361577 val tmpTuple = UniqueName .fresh()
15371578 // Replace all variables with wildcards in the pattern
15381579 val pat1 = pat match
15391580 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)
15411586 CaseDef (
15421587 Bind (tmpTuple, pat1),
15431588 EmptyTree ,
@@ -1546,6 +1591,8 @@ object desugar {
15461591 else CaseDef (pat, EmptyTree , makeTuple(ids).withAttachment(ForArtifact , ()))
15471592 Match (makeSelector(rhs, MatchCheck .IrrefutablePatDef ), caseDef :: Nil )
15481593
1594+ // println(i"matchExpr = $matchExpr")
1595+
15491596 vars match {
15501597 case Nil if ! mods.is(Lazy ) =>
15511598 matchExpr
0 commit comments