From 61bb3941af4eebcdfcc5299b6aa5f6cd0235b05a Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 19 Jan 2022 10:45:13 +0000 Subject: [PATCH] For generic tuples, call Tuples.apply instead of _1/_2/.. --- .../tools/dotc/transform/PatternMatcher.scala | 22 +++++++++++----- tests/run/i13968.scala | 26 +++++++++++++++++++ 2 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 tests/run/i13968.scala diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index 7546c1fd854d..cafa552ff5ec 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -231,8 +231,8 @@ object PatternMatcher { case _ => tree.tpe - /** Plan for matching `selectors` against argument patterns `args` */ - def matchArgsPlan(selectors: List[Tree], args: List[Tree], onSuccess: Plan): Plan = { + /** Plan for matching `components` against argument patterns `args` */ + def matchArgsPlan(components: List[Tree], args: List[Tree], onSuccess: Plan): Plan = { /* For a case with arguments that have some test on them such as * ``` * case Foo(1, 2) => someCode @@ -249,9 +249,9 @@ object PatternMatcher { * } else () * ``` */ - def matchArgsSelectorsPlan(selectors: List[Tree], syms: List[Symbol]): Plan = - selectors match { - case selector :: selectors1 => letAbstract(selector, selector.avoidPatBoundType())(sym => matchArgsSelectorsPlan(selectors1, sym :: syms)) + def matchArgsComponentsPlan(components: List[Tree], syms: List[Symbol]): Plan = + components match { + case component :: components1 => letAbstract(component, component.avoidPatBoundType())(sym => matchArgsComponentsPlan(components1, sym :: syms)) case Nil => matchArgsPatternPlan(args, syms.reverse) } def matchArgsPatternPlan(args: List[Tree], syms: List[Symbol]): Plan = @@ -263,7 +263,7 @@ object PatternMatcher { assert(syms.isEmpty) onSuccess } - matchArgsSelectorsPlan(selectors, Nil) + matchArgsComponentsPlan(components, Nil) } /** Plan for matching the sequence in `seqSym` against sequence elements `args`. @@ -326,7 +326,15 @@ object PatternMatcher { sym.isAllOf(SyntheticCase) && sym.owner.is(Scala2x) if (isSyntheticScala2Unapply(unapp.symbol) && caseAccessors.length == args.length) - matchArgsPlan(caseAccessors.map(ref(scrutinee).select(_)), args, onSuccess) + def tupleSel(sym: Symbol) = ref(scrutinee).select(sym) + def tupleApp(i: Int) = // manually inlining the call to NonEmptyTuple#apply, because it's an inline method + ref(defn.RuntimeTuplesModule) + .select(defn.RuntimeTuples_apply) + .appliedTo(ref(scrutinee), Literal(Constant(i))) + .cast(args(i).tpe.widen) + val isGenericTuple = defn.isTupleClass(caseClass) && !defn.isTupleNType(tree.tpe) + val components = if isGenericTuple then caseAccessors.indices.toList.map(tupleApp) else caseAccessors.map(tupleSel) + matchArgsPlan(components, args, onSuccess) else if (unapp.tpe <:< (defn.BooleanType)) TestPlan(GuardTest, unapp, unapp.span, onSuccess) else diff --git a/tests/run/i13968.scala b/tests/run/i13968.scala new file mode 100644 index 000000000000..8e63d57ecf1b --- /dev/null +++ b/tests/run/i13968.scala @@ -0,0 +1,26 @@ +object Bar { + def unapply(x: Any): Option[Int *: Int *: EmptyTuple] = Some(1 *: 2 *: Tuple()) +} + +object Bar23 { + def unapply(x: Any): Option[ + Int *: Int *: Int *: Int *: Int *: Int *: Int *: Int *: Int *: Int *: + Int *: Int *: Int *: Int *: Int *: Int *: Int *: Int *: Int *: Int *: + Int *: Int *: Int *: EmptyTuple + ] = Some( + 1 *: 2 *: 3 *: 4 *: 5 *: 6 *: 7 *: 8 *: 9 *: 10 *: + 11 *: 12 *: 13 *: 14 *: 15 *: 16 *: 17 *: 18 *: 19 *: 20 *: + 21 *: 22 *: 23 *: Tuple() + ) +} + +@main def Test() = + "" match + case Bar((a, b)) => assert(a == 1 && b == 2, (a, b)) + + "" match + case Bar23(( + u1, u2, u3, u4, u5, u6, u7, u8, u9, u10, + u11, u12, u13, u14, u15, u16, u17, u18, u19, u20, + u21, u22, u23 + )) => assert(u1 == 1 && u23 == 23, (u1, u23))