From 0d93a8285be16e88c82335c45ba08ee2447e5d93 Mon Sep 17 00:00:00 2001 From: noti0na1 Date: Mon, 16 Jun 2025 02:19:58 +0200 Subject: [PATCH 1/5] Optimize simple tuple extraction to avoid creating unnecessary tuple objects --- .../src/dotty/tools/dotc/ast/Desugar.scala | 32 +++++++++++++---- .../src/dotty/tools/dotc/typer/Typer.scala | 13 +++++-- tests/pos/simple-tuple-extract.scala | 34 +++++++++++++++++++ 3 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 tests/pos/simple-tuple-extract.scala diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 98eb8f895f5b..5732fecf1800 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1483,14 +1483,14 @@ object desugar { |please bind to an identifier and use an alias given.""", bind) false - def isTuplePattern(arity: Int): Boolean = pat match { - case Tuple(pats) if pats.size == arity => - pats.forall(isVarPattern) - case _ => false + // The arity of the tuple pattern if it only contains simple variables or wildcards. + val varTuplePatternArity = pat match { + case Tuple(pats) if pats.forall(isVarPattern) => pats.length + case _ => -1 } val isMatchingTuple: Tree => Boolean = { - case Tuple(es) => isTuplePattern(es.length) && !hasNamedArg(es) + case Tuple(es) => varTuplePatternArity == es.length && !hasNamedArg(es) case _ => false } @@ -1519,10 +1519,28 @@ object desugar { val ids = for ((named, _) <- vars) yield Ident(named.name) val matchExpr = - if (tupleOptimizable) rhs + if tupleOptimizable then rhs else - val caseDef = CaseDef(pat, EmptyTree, makeTuple(ids).withAttachment(ForArtifact, ())) + val caseDef = + if varTuplePatternArity >= 0 && ids.length > 1 then + // If the pattern contains only simple variables or wildcards, + // we don't need to create a new tuple. + // If there is only one variable (ids.length == 1), + // `makeTuple` will optimize it to `Ident(named)`, + // so we don't need to handle that case here. + val tmpTuple = UniqueName.fresh() + // Replace all variables with wildcards in the pattern + val pat1 = pat match + case Tuple(pats) => + Tuple(pats.map(pat => Ident(nme.WILDCARD).withSpan(pat.span))) + CaseDef( + Bind(tmpTuple, pat1), + EmptyTree, + Ident(tmpTuple).withAttachment(ForArtifact, ()) + ) + else CaseDef(pat, EmptyTree, makeTuple(ids).withAttachment(ForArtifact, ())) Match(makeSelector(rhs, MatchCheck.IrrefutablePatDef), caseDef :: Nil) + vars match { case Nil if !mods.is(Lazy) => matchExpr diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 0af1685d0857..b50ebdef3d9a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2774,6 +2774,16 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if !isFullyDefined(pt, ForceDegree.all) then return errorTree(tree, em"expected type of $tree is not fully defined") val body1 = typed(tree.body, pt) + + // If the body is a named tuple pattern, we need to use pt for symbol type, + // because the desugared body is a regular tuple unapply. + def isNamedTuplePattern = + ctx.mode.is(Mode.Pattern) + && pt.dealias.isNamedTupleType + && tree.body.match + case untpd.Tuple((_: NamedArg) :: _) => true + case _ => false + body1 match { case UnApply(fn, Nil, arg :: Nil) if fn.symbol.exists && (fn.symbol.owner.derivesFrom(defn.TypeTestClass) || fn.symbol.owner == defn.ClassTagClass) && !body1.tpe.isError => @@ -2799,8 +2809,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer body1.isInstanceOf[RefTree] && !isWildcardArg(body1) || body1.isInstanceOf[Literal] val symTp = - if isStableIdentifierOrLiteral || pt.dealias.isNamedTupleType then pt - // need to combine tuple element types with expected named type + if isStableIdentifierOrLiteral || isNamedTuplePattern then pt else if isWildcardStarArg(body1) || pt == defn.ImplicitScrutineeTypeRef || body1.tpe <:< pt // There is some strange interaction with gadt matching. diff --git a/tests/pos/simple-tuple-extract.scala b/tests/pos/simple-tuple-extract.scala new file mode 100644 index 000000000000..849473e8b87d --- /dev/null +++ b/tests/pos/simple-tuple-extract.scala @@ -0,0 +1,34 @@ + +class Test: + def f1: (Int, Int, Int) = (1, 2, 3) + def f2: (x: Int, y: Int) = (3, 4) + + def test1 = + val (a, b, c) = f1 + // Desugared to: + // val $2$: (Int, Int, Int) = + // this.f1:(Int, Int, Int) @unchecked match + // { + // case $1$ @ Tuple3.unapply[Int, Int, Int](_, _, _) => + // $1$:(Int, Int, Int) + // } + // val a: Int = $2$._1 + // val b: Int = $2$._2 + // val c: Int = $2$._3 + a + b + c + + def test2 = + val (_, d, e) = f1 + e + e + + def test3 = + val (_, f, _) = f1 + f + f + + def test4 = + val (x, y) = f2 + x + y + + def test5 = + val (_, a) = f2 + a + a From 232835a76cd7bcb764ac9b2863dce48a8c717642 Mon Sep 17 00:00:00 2001 From: noti0na1 Date: Mon, 16 Jun 2025 03:14:13 +0200 Subject: [PATCH 2/5] Fix patterns containing wildcards; enhance test --- .../src/dotty/tools/dotc/ast/Desugar.scala | 11 ++++-- .../src/dotty/tools/dotc/ast/TreeInfo.scala | 6 ++++ tests/pos/simple-tuple-extract.scala | 35 ++++++++++--------- 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 5732fecf1800..f173cd5e7ba5 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1489,13 +1489,18 @@ object desugar { case _ => -1 } + val nonWildcardVars = pat match { + case Tuple(pats) => pats.filterNot(isWildcardPattern).length + case _ => -1 + } + val isMatchingTuple: Tree => Boolean = { case Tuple(es) => varTuplePatternArity == es.length && !hasNamedArg(es) case _ => false } // We can only optimize `val pat = if (...) e1 else e2` if: - // - `e1` and `e2` are both tuples of arity N + // - `e1` and `e2` are both literal tuples of arity N // - `pat` is a tuple of N variables or wildcard patterns like `(x1, x2, ..., xN)` val tupleOptimizable = forallResults(rhs, isMatchingTuple) @@ -1504,7 +1509,7 @@ object desugar { case _ => false val vars = - if (tupleOptimizable) // include `_` + if varTuplePatternArity > 0 && nonWildcardVars > 1 then // include `_` pat match case Tuple(pats) => pats.map { case id: Ident => id -> TypeTree() } else @@ -1522,7 +1527,7 @@ object desugar { if tupleOptimizable then rhs else val caseDef = - if varTuplePatternArity >= 0 && ids.length > 1 then + if varTuplePatternArity > 0 && ids.length > 1 then // If the pattern contains only simple variables or wildcards, // we don't need to create a new tuple. // If there is only one variable (ids.length == 1), diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 2a9e643aa5b0..3bd8015310eb 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -207,6 +207,12 @@ trait TreeInfo[T <: Untyped] { self: Trees.Instance[T] => case _ => false } + /** Is tree a wildcard pattern? Not including `x @ _` */ + def isWildcardPattern(pat: Tree): Boolean = unsplice(pat) match { + case x: Ident => x.name == nme.WILDCARD && !isBackquoted(x) + case _ => false + } + /** The first constructor definition in `stats` */ def firstConstructor(stats: List[Tree]): Tree = stats match { case (meth: DefDef) :: _ if meth.name.isConstructorName => meth diff --git a/tests/pos/simple-tuple-extract.scala b/tests/pos/simple-tuple-extract.scala index 849473e8b87d..ee8ce04084ee 100644 --- a/tests/pos/simple-tuple-extract.scala +++ b/tests/pos/simple-tuple-extract.scala @@ -1,34 +1,37 @@ class Test: - def f1: (Int, Int, Int) = (1, 2, 3) - def f2: (x: Int, y: Int) = (3, 4) + def f1: (Int, String, AnyRef) = (1, "2", "3") + def f2: (x: Int, y: String) = (0, "y") def test1 = val (a, b, c) = f1 // Desugared to: - // val $2$: (Int, Int, Int) = - // this.f1:(Int, Int, Int) @unchecked match + // val $2$: (Int, String, AnyRef) = + // this.f1:(Int, String, AnyRef) @unchecked match // { - // case $1$ @ Tuple3.unapply[Int, Int, Int](_, _, _) => - // $1$:(Int, Int, Int) + // case $1$ @ Tuple3.unapply[Int, String, Object](_, _, _) => + // $1$:(Int, String, AnyRef) // } // val a: Int = $2$._1 - // val b: Int = $2$._2 - // val c: Int = $2$._3 - a + b + c + // val b: String = $2$._2 + // val c: AnyRef = $2$._3 + a + b.length() + c.toString.length() def test2 = - val (_, d, e) = f1 - e + e + val (_, b, c) = f1 + b.length() + c.toString.length() + + val (a2, _, c2) = f1 + a2 + c2.toString.length() def test3 = - val (_, f, _) = f1 - f + f + val (_, b, _) = f1 + b.length() + 1 def test4 = val (x, y) = f2 - x + y + x + y.length() def test5 = - val (_, a) = f2 - a + a + val (_, b) = f2 + b.length() + 1 \ No newline at end of file From a3cf92713928d796b0c24f036d89f9497389fc26 Mon Sep 17 00:00:00 2001 From: noti0na1 Date: Mon, 16 Jun 2025 21:25:32 +0200 Subject: [PATCH 3/5] Try to optimize typed vars --- .../src/dotty/tools/dotc/ast/Desugar.scala | 109 +++++++++++++----- .../src/dotty/tools/dotc/ast/TreeInfo.scala | 7 ++ tests/pos/simple-tuple-extract.scala | 3 + 3 files changed, 88 insertions(+), 31 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index f173cd5e7ba5..9aa1aa7f7b78 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1450,6 +1450,34 @@ object desugar { sel end match + case class TuplePatternInfo(arity: Int, varNum: Int, wildcardNum: Int, typedVarNum: Int, typedWildcardNum: Int) + object TuplePatternInfo: + def apply(pat: Tree)(using Context): TuplePatternInfo = pat match + case Tuple(pats) => + var arity = 0 + var varNum = 0 + var wildcardNum = 0 + var typedVarNum = 0 + var typedWildcardNum = 0 + pats.foreach: p => + arity += 1 + p match + case id: Ident if !isBackquoted(id) => + if id.name.isVarPattern then + varNum += 1 + if id.name == nme.WILDCARD then + wildcardNum += 1 + case Typed(id: Ident, _) if !isBackquoted(id) => + if id.name.isVarPattern then + typedVarNum += 1 + if id.name == nme.WILDCARD then + typedWildcardNum += 1 + case _ => + TuplePatternInfo(arity, varNum, wildcardNum, typedVarNum, typedWildcardNum) + case _ => + TuplePatternInfo(-1, -1, -1, -1, -1) + end TuplePatternInfo + /** If `pat` is a variable pattern, * * val/var/lazy val p = e @@ -1483,35 +1511,50 @@ object desugar { |please bind to an identifier and use an alias given.""", bind) false - // The arity of the tuple pattern if it only contains simple variables or wildcards. - val varTuplePatternArity = pat match { - case Tuple(pats) if pats.forall(isVarPattern) => pats.length - case _ => -1 - } - - val nonWildcardVars = pat match { - case Tuple(pats) => pats.filterNot(isWildcardPattern).length - case _ => -1 - } - - val isMatchingTuple: Tree => Boolean = { - case Tuple(es) => varTuplePatternArity == es.length && !hasNamedArg(es) - case _ => false - } + val tuplePatternInfo = TuplePatternInfo(pat) + + // When desugaring a PatDef in general, we use pattern matching on the rhs + // and collect the variable values in a tuple, then outside the match + // we destructure the tuple to get the individual variables. + // We can achieve two kinds of tuple optimizations if the pattern is a tuple + // of simple variables or wildcards: + // 1. Full optimization: + // If the rhs is known to produce a literal tuple of the same arity, + // we can directly fetch the values from the tuple. + // For example: `val (x, y) = if ... then (1, "a") else (2, "b")` becomes + // `val $1$ = if ...; val x = $1$._1; val y = $1$._2`. + // 2. Partial optimization: + // If the rhs can be typed as a tuple and matched with correct arity, + // we can return the tuple itself if there are no more than one variable + // in the pattern, or return the the value if there is only one variable. + + val fullTupleOptimizable = + val isMatchingTuple: Tree => Boolean = { + case Tuple(es) => tuplePatternInfo.varNum == es.length && !hasNamedArg(es) + case _ => false + } + tuplePatternInfo.arity > 0 + && tuplePatternInfo.arity == tuplePatternInfo.varNum + && forallResults(rhs, isMatchingTuple) - // We can only optimize `val pat = if (...) e1 else e2` if: - // - `e1` and `e2` are both literal tuples of arity N - // - `pat` is a tuple of N variables or wildcard patterns like `(x1, x2, ..., xN)` - val tupleOptimizable = forallResults(rhs, isMatchingTuple) + val partialTupleOptimizable = + tuplePatternInfo.arity > 0 + && tuplePatternInfo.arity == tuplePatternInfo.varNum + tuplePatternInfo.typedVarNum + // We exclude the case where there is only one variable, + // because it should be handled by `makeTuple` directly. + && tuplePatternInfo.wildcardNum + tuplePatternInfo.typedWildcardNum < tuplePatternInfo.arity - 1 val inAliasGenerator = original match case _: GenAlias => true case _ => false - val vars = - if varTuplePatternArity > 0 && nonWildcardVars > 1 then // include `_` + val vars: List[VarInfo] = + if fullTupleOptimizable || partialTupleOptimizable then // include `_` pat match - case Tuple(pats) => pats.map { case id: Ident => id -> TypeTree() } + case Tuple(pats) => pats.map { + case id: Ident => (id, TypeTree()) + case Typed(id: Ident, tpt) => (id, tpt) + } else getVariables( tree = pat, @@ -1522,22 +1565,24 @@ object desugar { errorOnGivenBinding ) // no `_` - val ids = for ((named, _) <- vars) yield Ident(named.name) + val ids = for ((named, tpt) <- vars) yield Ident(named.name) + + // println(s"fullTupleOptimizable = $fullTupleOptimizable, partialTupleOptimizable = $partialTupleOptimizable, ids = $ids") + val matchExpr = - if tupleOptimizable then rhs + if fullTupleOptimizable then rhs else val caseDef = - if varTuplePatternArity > 0 && ids.length > 1 then - // If the pattern contains only simple variables or wildcards, - // we don't need to create a new tuple. - // If there is only one variable (ids.length == 1), - // `makeTuple` will optimize it to `Ident(named)`, - // so we don't need to handle that case here. + if partialTupleOptimizable then val tmpTuple = UniqueName.fresh() // Replace all variables with wildcards in the pattern val pat1 = pat match case Tuple(pats) => - Tuple(pats.map(pat => Ident(nme.WILDCARD).withSpan(pat.span))) + val wildcardPats = pats.map { + case id: Ident => Ident(nme.WILDCARD).withSpan(id.span) + case p @ Typed(_: Ident, tpt) => Typed(Ident(nme.WILDCARD), tpt).withSpan(p.span) + } + Tuple(wildcardPats).withSpan(pat.span) CaseDef( Bind(tmpTuple, pat1), EmptyTree, @@ -1546,6 +1591,8 @@ object desugar { else CaseDef(pat, EmptyTree, makeTuple(ids).withAttachment(ForArtifact, ())) Match(makeSelector(rhs, MatchCheck.IrrefutablePatDef), caseDef :: Nil) + // println(i"matchExpr = $matchExpr") + vars match { case Nil if !mods.is(Lazy) => matchExpr diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 3bd8015310eb..e04032494fa8 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -213,6 +213,11 @@ trait TreeInfo[T <: Untyped] { self: Trees.Instance[T] => case _ => false } + def isTypedVarPattern(pat: Tree): Boolean = unsplice(pat) match { + case Typed(id: Ident, _) if id.name.isVarPattern && !isBackquoted(id) => true + case _ => false + } + /** The first constructor definition in `stats` */ def firstConstructor(stats: List[Tree]): Tree = stats match { case (meth: DefDef) :: _ if meth.name.isConstructorName => meth @@ -412,6 +417,8 @@ trait TreeInfo[T <: Untyped] { self: Trees.Instance[T] => tree.tpe.isInstanceOf[ThisType] } + + /** Under x.modularity: Extractor for `annotation.internal.WitnessNames(name_1, ..., name_n)` * represented as an untyped or typed tree. */ diff --git a/tests/pos/simple-tuple-extract.scala b/tests/pos/simple-tuple-extract.scala index ee8ce04084ee..1325cd0ecfc2 100644 --- a/tests/pos/simple-tuple-extract.scala +++ b/tests/pos/simple-tuple-extract.scala @@ -24,6 +24,9 @@ class Test: val (a2, _, c2) = f1 a2 + c2.toString.length() + val (a3, _, _) = f1 + a3 + 1 + def test3 = val (_, b, _) = f1 b.length() + 1 From d884df33271ae8111865a7c78ed3c5b9638af6d3 Mon Sep 17 00:00:00 2001 From: noti0na1 Date: Mon, 16 Jun 2025 21:39:16 +0200 Subject: [PATCH 4/5] Remove typed vars logic --- .../src/dotty/tools/dotc/ast/Desugar.scala | 35 ++++++------------- .../src/dotty/tools/dotc/ast/TreeInfo.scala | 13 ------- tests/pos/simple-tuple-extract.scala | 3 ++ 3 files changed, 13 insertions(+), 38 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 9aa1aa7f7b78..9eee43959a75 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1450,15 +1450,13 @@ object desugar { sel end match - case class TuplePatternInfo(arity: Int, varNum: Int, wildcardNum: Int, typedVarNum: Int, typedWildcardNum: Int) + case class TuplePatternInfo(arity: Int, varNum: Int, wildcardNum: Int) object TuplePatternInfo: def apply(pat: Tree)(using Context): TuplePatternInfo = pat match case Tuple(pats) => var arity = 0 var varNum = 0 var wildcardNum = 0 - var typedVarNum = 0 - var typedWildcardNum = 0 pats.foreach: p => arity += 1 p match @@ -1467,15 +1465,10 @@ object desugar { varNum += 1 if id.name == nme.WILDCARD then wildcardNum += 1 - case Typed(id: Ident, _) if !isBackquoted(id) => - if id.name.isVarPattern then - typedVarNum += 1 - if id.name == nme.WILDCARD then - typedWildcardNum += 1 case _ => - TuplePatternInfo(arity, varNum, wildcardNum, typedVarNum, typedWildcardNum) + TuplePatternInfo(arity, varNum, wildcardNum) case _ => - TuplePatternInfo(-1, -1, -1, -1, -1) + TuplePatternInfo(-1, -1, -1) end TuplePatternInfo /** If `pat` is a variable pattern, @@ -1514,7 +1507,7 @@ object desugar { val tuplePatternInfo = TuplePatternInfo(pat) // When desugaring a PatDef in general, we use pattern matching on the rhs - // and collect the variable values in a tuple, then outside the match + // and collect the variable values in a tuple, then outside the match, // we destructure the tuple to get the individual variables. // We can achieve two kinds of tuple optimizations if the pattern is a tuple // of simple variables or wildcards: @@ -1524,8 +1517,8 @@ object desugar { // For example: `val (x, y) = if ... then (1, "a") else (2, "b")` becomes // `val $1$ = if ...; val x = $1$._1; val y = $1$._2`. // 2. Partial optimization: - // If the rhs can be typed as a tuple and matched with correct arity, - // we can return the tuple itself if there are no more than one variable + // If the rhs can be typed as a tuple and matched with correct arity, we can + // return the tuple itself in the case if there are no more than one variable // in the pattern, or return the the value if there is only one variable. val fullTupleOptimizable = @@ -1539,10 +1532,10 @@ object desugar { val partialTupleOptimizable = tuplePatternInfo.arity > 0 - && tuplePatternInfo.arity == tuplePatternInfo.varNum + tuplePatternInfo.typedVarNum + && tuplePatternInfo.arity == tuplePatternInfo.varNum // We exclude the case where there is only one variable, // because it should be handled by `makeTuple` directly. - && tuplePatternInfo.wildcardNum + tuplePatternInfo.typedWildcardNum < tuplePatternInfo.arity - 1 + && tuplePatternInfo.wildcardNum < tuplePatternInfo.arity - 1 val inAliasGenerator = original match case _: GenAlias => true @@ -1551,10 +1544,7 @@ object desugar { val vars: List[VarInfo] = if fullTupleOptimizable || partialTupleOptimizable then // include `_` pat match - case Tuple(pats) => pats.map { - case id: Ident => (id, TypeTree()) - case Typed(id: Ident, tpt) => (id, tpt) - } + case Tuple(pats) => pats.map { case id: Ident => (id, TypeTree()) } else getVariables( tree = pat, @@ -1578,10 +1568,7 @@ object desugar { // Replace all variables with wildcards in the pattern val pat1 = pat match case Tuple(pats) => - val wildcardPats = pats.map { - case id: Ident => Ident(nme.WILDCARD).withSpan(id.span) - case p @ Typed(_: Ident, tpt) => Typed(Ident(nme.WILDCARD), tpt).withSpan(p.span) - } + val wildcardPats = pats.map(p => Ident(nme.WILDCARD).withSpan(p.span)) Tuple(wildcardPats).withSpan(pat.span) CaseDef( Bind(tmpTuple, pat1), @@ -1591,8 +1578,6 @@ object desugar { else CaseDef(pat, EmptyTree, makeTuple(ids).withAttachment(ForArtifact, ())) Match(makeSelector(rhs, MatchCheck.IrrefutablePatDef), caseDef :: Nil) - // println(i"matchExpr = $matchExpr") - vars match { case Nil if !mods.is(Lazy) => matchExpr diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index e04032494fa8..2a9e643aa5b0 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -207,17 +207,6 @@ trait TreeInfo[T <: Untyped] { self: Trees.Instance[T] => case _ => false } - /** Is tree a wildcard pattern? Not including `x @ _` */ - def isWildcardPattern(pat: Tree): Boolean = unsplice(pat) match { - case x: Ident => x.name == nme.WILDCARD && !isBackquoted(x) - case _ => false - } - - def isTypedVarPattern(pat: Tree): Boolean = unsplice(pat) match { - case Typed(id: Ident, _) if id.name.isVarPattern && !isBackquoted(id) => true - case _ => false - } - /** The first constructor definition in `stats` */ def firstConstructor(stats: List[Tree]): Tree = stats match { case (meth: DefDef) :: _ if meth.name.isConstructorName => meth @@ -417,8 +406,6 @@ trait TreeInfo[T <: Untyped] { self: Trees.Instance[T] => tree.tpe.isInstanceOf[ThisType] } - - /** Under x.modularity: Extractor for `annotation.internal.WitnessNames(name_1, ..., name_n)` * represented as an untyped or typed tree. */ diff --git a/tests/pos/simple-tuple-extract.scala b/tests/pos/simple-tuple-extract.scala index 1325cd0ecfc2..736deb7ada3a 100644 --- a/tests/pos/simple-tuple-extract.scala +++ b/tests/pos/simple-tuple-extract.scala @@ -17,6 +17,9 @@ class Test: // val c: AnyRef = $2$._3 a + b.length() + c.toString.length() + // This pattern will not be optimized: + // val (a1, b1, c1: String) = f1 + def test2 = val (_, b, c) = f1 b.length() + c.toString.length() From 7c4950662562e1e7a1120b6390fa977af1635167 Mon Sep 17 00:00:00 2001 From: noti0na1 Date: Mon, 16 Jun 2025 21:50:33 +0200 Subject: [PATCH 5/5] Enhance forallResults to handle Try --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 2 -- compiler/src/dotty/tools/dotc/ast/TreeInfo.scala | 10 ++++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 9eee43959a75..4aec8fc87234 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1557,8 +1557,6 @@ object desugar { val ids = for ((named, tpt) <- vars) yield Ident(named.name) - // println(s"fullTupleOptimizable = $fullTupleOptimizable, partialTupleOptimizable = $partialTupleOptimizable, ids = $ids") - val matchExpr = if fullTupleOptimizable then rhs else diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 2a9e643aa5b0..f57c1b74939f 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -350,14 +350,16 @@ trait TreeInfo[T <: Untyped] { self: Trees.Instance[T] => } /** Checks whether predicate `p` is true for all result parts of this expression, - * where we zoom into Ifs, Matches, and Blocks. + * where we zoom into Ifs, Matches, Tries, and Blocks. */ - def forallResults(tree: Tree, p: Tree => Boolean): Boolean = tree match { + def forallResults(tree: Tree, p: Tree => Boolean): Boolean = tree match case If(_, thenp, elsep) => forallResults(thenp, p) && forallResults(elsep, p) - case Match(_, cases) => cases forall (c => forallResults(c.body, p)) + case Match(_, cases) => cases.forall(c => forallResults(c.body, p)) + case Try(_, cases, finalizer) => + cases.forall(c => forallResults(c.body, p)) + && (finalizer.isEmpty || forallResults(finalizer, p)) case Block(_, expr) => forallResults(expr, p) case _ => p(tree) - } /** The tree stripped of the possibly nested applications (term and type). * The original tree if it's not an application.