From 1fbaafcc12b1bc241c706fcb53f68d6b345916db Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Mon, 6 Jan 2025 16:56:44 +0100 Subject: [PATCH] Fail more eagerly when trying to adapt named unapply patterns --- .../dotty/tools/dotc/typer/Applications.scala | 17 ++++++++--- tests/neg/i22192.check | 30 +++++++++++++++++++ tests/neg/i22192.scala | 11 +++++++ tests/neg/i22192a.check | 20 +++++++++++++ tests/neg/i22192a.scala | 13 ++++++++ tests/pos/i22192.scala | 15 ++++++++++ 6 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 tests/neg/i22192.check create mode 100644 tests/neg/i22192.scala create mode 100644 tests/neg/i22192a.check create mode 100644 tests/neg/i22192a.scala create mode 100644 tests/pos/i22192.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 73719cc287a4..c2864093ff70 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -109,6 +109,10 @@ object Applications { if (isValid) elemTp else NoType } + def namedTupleOrProductTypes(tp: Type)(using Context): List[Type] = + if tp.isNamedTupleType then tp.namedTupleElementTypes.map(_(1)) + else productSelectorTypes(tp, NoSourcePosition) + def productSelectorTypes(tp: Type, errorPos: SrcPos)(using Context): List[Type] = { val sels = for (n <- Iterator.from(0)) yield extractorMemberType(tp, nme.selectorName(n), errorPos) sels.takeWhile(_.exists).toList @@ -177,9 +181,14 @@ object Applications { else fallback private def tryAdaptPatternArgs(elems: List[untpd.Tree], pt: Type)(using Context): Option[List[untpd.Tree]] = - tryEither[Option[List[untpd.Tree]]] - (Some(desugar.adaptPatternArgs(elems, pt))) - ((_, _) => None) + namedTupleOrProductTypes(pt) match + case List(defn.NamedTuple(_, _))=> + // if the product types list is a singleton named tuple, autotupling might be applied, so don't fail eagerly + tryEither[Option[List[untpd.Tree]]] + (Some(desugar.adaptPatternArgs(elems, pt))) + ((_, _) => None) + case pts => + Some(desugar.adaptPatternArgs(elems, pt)) private def getUnapplySelectors(tp: Type)(using Context): List[Type] = // We treat patterns as product elements if @@ -199,7 +208,7 @@ object Applications { else tp :: Nil private def productUnapplySelectors(tp: Type)(using Context): Option[List[Type]] = - if defn.isProductSubType(tp) then + if defn.isProductSubType(tp) && args.lengthCompare(productArity(tp)) <= 0 then tryAdaptPatternArgs(args, tp) match case Some(args1) if isProductMatch(tp, args1.length, pos) => args = args1 diff --git a/tests/neg/i22192.check b/tests/neg/i22192.check new file mode 100644 index 000000000000..35ed426ccb11 --- /dev/null +++ b/tests/neg/i22192.check @@ -0,0 +1,30 @@ +-- Error: tests/neg/i22192.scala:6:12 ---------------------------------------------------------------------------------- +6 | case City(iam = n, confused = p) => // error // error + | ^^^^^^^ + | No element named `iam` is defined in selector type City +-- Error: tests/neg/i22192.scala:6:21 ---------------------------------------------------------------------------------- +6 | case City(iam = n, confused = p) => // error // error + | ^^^^^^^^^^^^ + | No element named `confused` is defined in selector type City +-- [E006] Not Found Error: tests/neg/i22192.scala:7:7 ------------------------------------------------------------------ +7 | s"$n has a population of $p" // error // error + | ^ + | Not found: n + | + | longer explanation available when compiling with `-explain` +-- [E006] Not Found Error: tests/neg/i22192.scala:7:30 ----------------------------------------------------------------- +7 | s"$n has a population of $p" // error // error + | ^ + | Not found: p + | + | longer explanation available when compiling with `-explain` +-- Error: tests/neg/i22192.scala:10:12 --------------------------------------------------------------------------------- +10 | case Some(iam = n) => // error + | ^^^^^^^ + | No element named `iam` is defined in selector type City +-- [E006] Not Found Error: tests/neg/i22192.scala:11:4 ----------------------------------------------------------------- +11 | n // error + | ^ + | Not found: n + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i22192.scala b/tests/neg/i22192.scala new file mode 100644 index 000000000000..16eeb643dee0 --- /dev/null +++ b/tests/neg/i22192.scala @@ -0,0 +1,11 @@ +import scala.language.experimental.namedTuples + +case class City(name: String, population: Int) + +def getCityInfo(city: City) = city match + case City(iam = n, confused = p) => // error // error + s"$n has a population of $p" // error // error + +def getCityInfo1(city: Option[City]) = city match + case Some(iam = n) => // error + n // error \ No newline at end of file diff --git a/tests/neg/i22192a.check b/tests/neg/i22192a.check new file mode 100644 index 000000000000..8bfbcb127c54 --- /dev/null +++ b/tests/neg/i22192a.check @@ -0,0 +1,20 @@ +-- Error: tests/neg/i22192a.scala:6:12 --------------------------------------------------------------------------------- +6 | case Some(iam = n) => // error + | ^^^^^^^ + | No element named `iam` is defined in selector type (name : String) +-- [E006] Not Found Error: tests/neg/i22192a.scala:7:4 ----------------------------------------------------------------- +7 | n // error + | ^ + | Not found: n + | + | longer explanation available when compiling with `-explain` +-- Error: tests/neg/i22192a.scala:11:12 -------------------------------------------------------------------------------- +11 | case Some(iam = n) => // error + | ^^^^^^^ + | No element named `iam` is defined in selector type (name : String, population : Int) +-- [E006] Not Found Error: tests/neg/i22192a.scala:12:4 ---------------------------------------------------------------- +12 | n // error + | ^ + | Not found: n + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i22192a.scala b/tests/neg/i22192a.scala new file mode 100644 index 000000000000..6460e613d517 --- /dev/null +++ b/tests/neg/i22192a.scala @@ -0,0 +1,13 @@ +import scala.language.experimental.namedTuples + +type City = (name: String) + +def getCityInfo(city: Option[City]) = city match + case Some(iam = n) => // error + n // error + case _ => + +def getCiryInfo1(city: Option[(name: String, population: Int)]) = city match + case Some(iam = n) => // error + n // error + case _ => \ No newline at end of file diff --git a/tests/pos/i22192.scala b/tests/pos/i22192.scala new file mode 100644 index 000000000000..4214a56f4b38 --- /dev/null +++ b/tests/pos/i22192.scala @@ -0,0 +1,15 @@ +import scala.language.experimental.namedTuples + +case class City(name: String, population: Int) + +def getCityInfo(city: City) = city match + case City(population = p, name = n) => + s"$n has a population of $p" + +def getCityInfo1(city: Option[(name: String)]) = city match + case Some(name = n) => n + case _ => + +def getCityInfo2(city: Option[(name: String, population: Int)]) = city match + case Some(name = n) => n + case _ =>