Skip to content

Commit b13d617

Browse files
authored
Check for mismatched argument type length in PatternMatcher due to Named Tuple :* syntax (#23602)
Fixes #23155 After discussing at the compiler meeting, it was decided that the Named Tuple behavior should be the same as the Tuple behavior with respect to pattern matching with the `:*` syntax. To error in the exact same way, it should be caught here: https://github.com/scala/scala3/blob/00d19dfd0906d2b4a8c076ded8af15cc3a6f2dc9/compiler/src/dotty/tools/dotc/typer/Applications.scala#L291 For regular tuples, `argTypes` is an `AppliedType` of `:*` and is not yet reduced, so has length 1, so the arity check will fail. However, at this point Named Tuple types are fully reduced by `tupleElementTypes` here: https://github.com/scala/scala3/blob/00d19dfd0906d2b4a8c076ded8af15cc3a6f2dc9/compiler/src/dotty/tools/dotc/typer/Applications.scala#L249 So for Named Tuples, `argTypes` at the time of the check will be `(Int, Int)` so the arity check will pass, causing a later crash in PatternMatcher here: https://github.com/scala/scala3/blob/6146b90b8e32ac81d2703fccdebbc85e72cc5c5b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala#L270 We cannot un-reduce or fail to reduce for Named Tuple types because it will cause a host of other tests to fail that rely on the fully reduced Named Tuple-value types. For now I have added a check in PatternMatcher because it’s better to fix the compiler crash ASAP, but I think we could revisit if we want something like `Int :* Int :* EmptyTuple` to really behave differently from `(Int, Int)` here, since pattern matching currently works for both Tuples and Named Tuples using `(Int, Int)` or `Tuple2[Int, Int]` , just not `:*`.
2 parents a85c429 + 180b4eb commit b13d617

File tree

3 files changed

+21
-2
lines changed

3 files changed

+21
-2
lines changed

compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,8 +267,14 @@ object PatternMatcher {
267267
def matchArgsPatternPlan(args: List[Tree], syms: List[Symbol]): Plan =
268268
args match {
269269
case arg :: args1 =>
270-
val sym :: syms1 = syms: @unchecked
271-
patternPlan(sym, arg, matchArgsPatternPlan(args1, syms1))
270+
if (args.length != syms.length)
271+
report.error(UnapplyInvalidNumberOfArguments(tree, tree.tpe :: Nil), arg.srcPos)
272+
// Generate a throwaway but type-correct plan.
273+
// This plan will never execute because it'll be guarded by a `NonNullTest`.
274+
ResultPlan(tpd.Throw(tpd.nullLiteral))
275+
else
276+
val sym :: syms1 = syms: @unchecked
277+
patternPlan(sym, arg, matchArgsPatternPlan(args1, syms1))
272278
case Nil =>
273279
assert(syms.isEmpty)
274280
onSuccess

tests/neg/i23155a.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import scala.NamedTuple
2+
object Unpack_NT {
3+
(1, 2) match {
4+
case Unpack_NT(first, _) => first // error
5+
}
6+
def unapply(e: (Int, Int)): Some[NamedTuple.NamedTuple["x" *: "y" *: EmptyTuple, Int *: Int *: EmptyTuple]] = ???
7+
}

tests/neg/i23155b.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
object Unpack_T {
2+
(1, 2) match {
3+
case Unpack_T(first, _) => first // error
4+
}
5+
def unapply(e: (Int, Int)): Some[Int *: Int *: EmptyTuple] = ???
6+
}

0 commit comments

Comments
 (0)