Skip to content

Commit 0ecc057

Browse files
authored
Fail more eagerly when trying to adapt named unapply patterns (#22315)
closes #22192
2 parents 045434c + 1fbaafc commit 0ecc057

File tree

6 files changed

+102
-4
lines changed

6 files changed

+102
-4
lines changed

compiler/src/dotty/tools/dotc/typer/Applications.scala

+13-4
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ object Applications {
109109
if (isValid) elemTp else NoType
110110
}
111111

112+
def namedTupleOrProductTypes(tp: Type)(using Context): List[Type] =
113+
if tp.isNamedTupleType then tp.namedTupleElementTypes.map(_(1))
114+
else productSelectorTypes(tp, NoSourcePosition)
115+
112116
def productSelectorTypes(tp: Type, errorPos: SrcPos)(using Context): List[Type] = {
113117
val sels = for (n <- Iterator.from(0)) yield extractorMemberType(tp, nme.selectorName(n), errorPos)
114118
sels.takeWhile(_.exists).toList
@@ -177,9 +181,14 @@ object Applications {
177181
else fallback
178182

179183
private def tryAdaptPatternArgs(elems: List[untpd.Tree], pt: Type)(using Context): Option[List[untpd.Tree]] =
180-
tryEither[Option[List[untpd.Tree]]]
181-
(Some(desugar.adaptPatternArgs(elems, pt)))
182-
((_, _) => None)
184+
namedTupleOrProductTypes(pt) match
185+
case List(defn.NamedTuple(_, _))=>
186+
// if the product types list is a singleton named tuple, autotupling might be applied, so don't fail eagerly
187+
tryEither[Option[List[untpd.Tree]]]
188+
(Some(desugar.adaptPatternArgs(elems, pt)))
189+
((_, _) => None)
190+
case pts =>
191+
Some(desugar.adaptPatternArgs(elems, pt))
183192

184193
private def getUnapplySelectors(tp: Type)(using Context): List[Type] =
185194
// We treat patterns as product elements if
@@ -199,7 +208,7 @@ object Applications {
199208
else tp :: Nil
200209

201210
private def productUnapplySelectors(tp: Type)(using Context): Option[List[Type]] =
202-
if defn.isProductSubType(tp) then
211+
if defn.isProductSubType(tp) && args.lengthCompare(productArity(tp)) <= 0 then
203212
tryAdaptPatternArgs(args, tp) match
204213
case Some(args1) if isProductMatch(tp, args1.length, pos) =>
205214
args = args1

tests/neg/i22192.check

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
-- Error: tests/neg/i22192.scala:6:12 ----------------------------------------------------------------------------------
2+
6 | case City(iam = n, confused = p) => // error // error
3+
| ^^^^^^^
4+
| No element named `iam` is defined in selector type City
5+
-- Error: tests/neg/i22192.scala:6:21 ----------------------------------------------------------------------------------
6+
6 | case City(iam = n, confused = p) => // error // error
7+
| ^^^^^^^^^^^^
8+
| No element named `confused` is defined in selector type City
9+
-- [E006] Not Found Error: tests/neg/i22192.scala:7:7 ------------------------------------------------------------------
10+
7 | s"$n has a population of $p" // error // error
11+
| ^
12+
| Not found: n
13+
|
14+
| longer explanation available when compiling with `-explain`
15+
-- [E006] Not Found Error: tests/neg/i22192.scala:7:30 -----------------------------------------------------------------
16+
7 | s"$n has a population of $p" // error // error
17+
| ^
18+
| Not found: p
19+
|
20+
| longer explanation available when compiling with `-explain`
21+
-- Error: tests/neg/i22192.scala:10:12 ---------------------------------------------------------------------------------
22+
10 | case Some(iam = n) => // error
23+
| ^^^^^^^
24+
| No element named `iam` is defined in selector type City
25+
-- [E006] Not Found Error: tests/neg/i22192.scala:11:4 -----------------------------------------------------------------
26+
11 | n // error
27+
| ^
28+
| Not found: n
29+
|
30+
| longer explanation available when compiling with `-explain`

tests/neg/i22192.scala

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import scala.language.experimental.namedTuples
2+
3+
case class City(name: String, population: Int)
4+
5+
def getCityInfo(city: City) = city match
6+
case City(iam = n, confused = p) => // error // error
7+
s"$n has a population of $p" // error // error
8+
9+
def getCityInfo1(city: Option[City]) = city match
10+
case Some(iam = n) => // error
11+
n // error

tests/neg/i22192a.check

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
-- Error: tests/neg/i22192a.scala:6:12 ---------------------------------------------------------------------------------
2+
6 | case Some(iam = n) => // error
3+
| ^^^^^^^
4+
| No element named `iam` is defined in selector type (name : String)
5+
-- [E006] Not Found Error: tests/neg/i22192a.scala:7:4 -----------------------------------------------------------------
6+
7 | n // error
7+
| ^
8+
| Not found: n
9+
|
10+
| longer explanation available when compiling with `-explain`
11+
-- Error: tests/neg/i22192a.scala:11:12 --------------------------------------------------------------------------------
12+
11 | case Some(iam = n) => // error
13+
| ^^^^^^^
14+
| No element named `iam` is defined in selector type (name : String, population : Int)
15+
-- [E006] Not Found Error: tests/neg/i22192a.scala:12:4 ----------------------------------------------------------------
16+
12 | n // error
17+
| ^
18+
| Not found: n
19+
|
20+
| longer explanation available when compiling with `-explain`

tests/neg/i22192a.scala

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import scala.language.experimental.namedTuples
2+
3+
type City = (name: String)
4+
5+
def getCityInfo(city: Option[City]) = city match
6+
case Some(iam = n) => // error
7+
n // error
8+
case _ =>
9+
10+
def getCiryInfo1(city: Option[(name: String, population: Int)]) = city match
11+
case Some(iam = n) => // error
12+
n // error
13+
case _ =>

tests/pos/i22192.scala

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import scala.language.experimental.namedTuples
2+
3+
case class City(name: String, population: Int)
4+
5+
def getCityInfo(city: City) = city match
6+
case City(population = p, name = n) =>
7+
s"$n has a population of $p"
8+
9+
def getCityInfo1(city: Option[(name: String)]) = city match
10+
case Some(name = n) => n
11+
case _ =>
12+
13+
def getCityInfo2(city: Option[(name: String, population: Int)]) = city match
14+
case Some(name = n) => n
15+
case _ =>

0 commit comments

Comments
 (0)