Skip to content

Commit 403eaaa

Browse files
sjrdtgodzik
authored andcommitted
Fix scala#21841: Check more that an unapplySeq on a NonEmptyTuple is valid.
1 parent e69ca30 commit 403eaaa

File tree

3 files changed

+55
-7
lines changed

3 files changed

+55
-7
lines changed

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

+27-7
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,21 @@ object Applications {
6767
unapplySeqTypeElemTp(productSelectorTypes(tp, errorPos).last).exists
6868
}
6969

70+
/** Does `tp` fit the "product-seq match" conditions for a `NonEmptyTuple` as
71+
* an unapply result type for a pattern with `numArgs` subpatterns?
72+
* This is the case if (1) `tp` derives from `NonEmptyTuple`.
73+
* (2) `tp.tupleElementTypes` exists.
74+
* (3) `tp.tupleElementTypes.last` conforms to Seq match
75+
*/
76+
def isNonEmptyTupleSeqMatch(tp: Type, numArgs: Int, errorPos: SrcPos = NoSourcePosition)(using Context): Boolean = {
77+
tp.derivesFrom(defn.NonEmptyTupleClass)
78+
&& tp.tupleElementTypes.exists { elemTypes =>
79+
val arity = elemTypes.size
80+
arity > 0 && arity <= numArgs + 1 &&
81+
unapplySeqTypeElemTp(elemTypes.last).exists
82+
}
83+
}
84+
7085
/** Does `tp` fit the "get match" conditions as an unapply result type?
7186
* This is the case of `tp` has a `get` member as well as a
7287
* parameterless `isEmpty` member of result type `Boolean`.
@@ -143,12 +158,17 @@ object Applications {
143158
}
144159
else tp :: Nil
145160

146-
def productSeqSelectors(tp: Type, argsNum: Int, pos: SrcPos)(using Context): List[Type] = {
147-
val selTps = productSelectorTypes(tp, pos)
148-
val arity = selTps.length
149-
val elemTp = unapplySeqTypeElemTp(selTps.last)
150-
(0 until argsNum).map(i => if (i < arity - 1) selTps(i) else elemTp).toList
151-
}
161+
def productSeqSelectors(tp: Type, argsNum: Int, pos: SrcPos)(using Context): List[Type] =
162+
seqSelectors(productSelectorTypes(tp, pos), argsNum)
163+
164+
def nonEmptyTupleSeqSelectors(tp: Type, argsNum: Int, pos: SrcPos)(using Context): List[Type] =
165+
seqSelectors(tp.tupleElementTypes.get, argsNum)
166+
167+
private def seqSelectors(selectorTypes: List[Type], argsNum: Int)(using Context): List[Type] =
168+
val arity = selectorTypes.length
169+
val elemTp = unapplySeqTypeElemTp(selectorTypes.last)
170+
(0 until argsNum).map(i => if (i < arity - 1) selectorTypes(i) else elemTp).toList
171+
end seqSelectors
152172

153173
def unapplyArgs(unapplyResult: Type, unapplyFn: Tree, args: List[untpd.Tree], pos: SrcPos)(using Context): List[Type] = {
154174
def getName(fn: Tree): Name =
@@ -169,7 +189,7 @@ object Applications {
169189
val elemTp = unapplySeqTypeElemTp(tp)
170190
if (elemTp.exists) args.map(Function.const(elemTp))
171191
else if (isProductSeqMatch(tp, args.length, pos)) productSeqSelectors(tp, args.length, pos)
172-
else if tp.derivesFrom(defn.NonEmptyTupleClass) then foldApplyTupleType(tp)
192+
else if isNonEmptyTupleSeqMatch(tp, args.length, pos) then nonEmptyTupleSeqSelectors(tp, args.length, pos)
173193
else fallback
174194
}
175195

tests/neg/i21841.check

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
-- [E108] Declaration Error: tests/neg/i21841.scala:20:13 --------------------------------------------------------------
2+
20 | case v[T](l, r) => () // error
3+
| ^^^^^^^^^^
4+
| Option[(Test.Expr[Test.T], Test.Expr[Test.T])] is not a valid result type of an unapplySeq method of an extractor.
5+
|
6+
| longer explanation available when compiling with `-explain`

tests/neg/i21841.scala

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
object Test {
2+
3+
sealed trait T
4+
sealed trait Arrow[A, B]
5+
6+
type ArgsTo[S1, Target] <: NonEmptyTuple = S1 match {
7+
case Arrow[a, Target] => Tuple1[Expr[a]]
8+
case Arrow[a, b] => Expr[a] *: ArgsTo[b, Target]
9+
}
10+
11+
sealed trait Expr[S] :
12+
def unapplySeq[Target](e: Expr[Target]): Option[ArgsTo[S, Target]] = ???
13+
14+
case class Variable[S](id: String) extends Expr[S]
15+
16+
val v = Variable[Arrow[T, Arrow[T, T]]]("v")
17+
val e : Expr[T] = ???
18+
19+
e match
20+
case v[T](l, r) => () // error
21+
case _ => ()
22+
}

0 commit comments

Comments
 (0)