@@ -18,6 +18,7 @@ import Names.*
18
18
import StdNames .*
19
19
import ContextOps .*
20
20
import NameKinds .DefaultGetterName
21
+ import Typer .tryEither
21
22
import ProtoTypes .*
22
23
import Inferencing .*
23
24
import reporting .*
@@ -134,37 +135,37 @@ object Applications {
134
135
sels.takeWhile(_.exists).toList
135
136
}
136
137
137
- def getUnapplySelectors (tp : Type , args : List [untpd.Tree ], pos : SrcPos )(using Context ): List [Type ] =
138
- if (args.length > 1 && ! (tp.derivesFrom(defn.SeqClass ))) {
139
- val sels = productSelectorTypes(tp, pos)
140
- if (sels.length == args.length) sels
141
- else tp :: Nil
142
- }
143
- else tp :: Nil
144
-
145
138
def productSeqSelectors (tp : Type , argsNum : Int , pos : SrcPos )(using Context ): List [Type ] = {
146
139
val selTps = productSelectorTypes(tp, pos)
147
140
val arity = selTps.length
148
141
val elemTp = unapplySeqTypeElemTp(selTps.last)
149
142
(0 until argsNum).map(i => if (i < arity - 1 ) selTps(i) else elemTp).toList
150
143
}
151
144
152
- def unapplyArgs (unapplyResult : Type , unapplyFn : Tree , args : List [untpd.Tree ], pos : SrcPos )(using Context ): List [Type ] =
153
- def getName (fn : Tree ): Name =
145
+ /** A utility class that matches results of unapplys with patterns. Two queriable members:
146
+ * val argTypes: List[Type]
147
+ * def typedPatterns(qual: untpd.Tree, typer: Typer): List[Tree]
148
+ * TODO: Move into Applications trait. No need to keep it outside. But it's a large
149
+ * refactor, so do this when the rest is merged.
150
+ */
151
+ class UnapplyArgs (unapplyResult : Type , unapplyFn : Tree , unadaptedArgs : List [untpd.Tree ], pos : SrcPos )(using Context ):
152
+ private var args = unadaptedArgs
153
+
154
+ private def getName (fn : Tree ): Name =
154
155
fn match
155
156
case TypeApply (fn, _) => getName(fn)
156
157
case Apply (fn, _) => getName(fn)
157
158
case fn : RefTree => fn.name
158
- val unapplyName = getName(unapplyFn) // tolerate structural `unapply`, which does not have a symbol
159
+ private val unapplyName = getName(unapplyFn) // tolerate structural `unapply`, which does not have a symbol
159
160
160
- def getTp = extractorMemberType(unapplyResult, nme.get, pos)
161
+ private def getTp = extractorMemberType(unapplyResult, nme.get, pos)
161
162
162
- def fail = {
163
+ private def fail = {
163
164
report.error(UnapplyInvalidReturnType (unapplyResult, unapplyName), pos)
164
165
Nil
165
166
}
166
167
167
- def unapplySeq (tp : Type )(fallback : => List [Type ]): List [Type ] =
168
+ private def unapplySeq (tp : Type )(fallback : => List [Type ]): List [Type ] =
168
169
val elemTp = unapplySeqTypeElemTp(tp)
169
170
if elemTp.exists then
170
171
args.map(Function .const(elemTp))
@@ -174,26 +175,84 @@ object Applications {
174
175
tp.tupleElementTypes.getOrElse(Nil )
175
176
else fallback
176
177
177
- if unapplyName == nme.unapplySeq then
178
- unapplySeq(unapplyResult):
179
- if (isGetMatch(unapplyResult, pos)) unapplySeq(getTp)(fail)
180
- else fail
181
- else
182
- assert(unapplyName == nme.unapply)
183
- if isProductMatch(unapplyResult, args.length, pos) then
184
- productSelectorTypes(unapplyResult, pos)
185
- else if isGetMatch(unapplyResult, pos) then
186
- getUnapplySelectors(getTp, args, pos)
187
- else if unapplyResult.derivesFrom(defn.BooleanClass ) then
188
- Nil
189
- else if defn.isProductSubType(unapplyResult) && productArity(unapplyResult, pos) != 0 then
190
- productSelectorTypes(unapplyResult, pos)
191
- // this will cause a "wrong number of arguments in pattern" error later on,
192
- // which is better than the message in `fail`.
193
- else if unapplyResult.derivesFrom(defn.NonEmptyTupleClass ) then
194
- unapplyResult.tupleElementTypes.getOrElse(Nil )
195
- else fail
196
- end unapplyArgs
178
+ private def tryAdaptPatternArgs (elems : List [untpd.Tree ], pt : Type )(using Context ): Option [List [untpd.Tree ]] =
179
+ tryEither[Option [List [untpd.Tree ]]]
180
+ (Some (desugar.adaptPatternArgs(elems, pt)))
181
+ ((_, _) => None )
182
+
183
+ private def getUnapplySelectors (tp : Type )(using Context ): List [Type ] =
184
+ if args.length > 1 && ! (tp.derivesFrom(defn.SeqClass )) then
185
+ productUnapplySelectors(tp).getOrElse:
186
+ // There are unapplys with return types which have `get` and `_1, ..., _n`
187
+ // as members, but which are not subtypes of Product. So `productUnapplySelectors`
188
+ // would return None for these, but they are still valid types
189
+ // for a get match. A test case is pos/extractors.scala.
190
+ val sels = productSelectorTypes(tp, pos)
191
+ if (sels.length == args.length) sels
192
+ else tp :: Nil
193
+ else tp :: Nil
194
+
195
+ private def productUnapplySelectors (tp : Type )(using Context ): Option [List [Type ]] =
196
+ if defn.isProductSubType(tp) then
197
+ tryAdaptPatternArgs(args, tp) match
198
+ case Some (args1) if isProductMatch(tp, args1.length, pos) =>
199
+ args = args1
200
+ Some (productSelectorTypes(tp, pos))
201
+ case _ => None
202
+ else tp.widen.normalized.dealias match
203
+ case tp @ defn.NamedTuple (_, tt) =>
204
+ tryAdaptPatternArgs(args, tp) match
205
+ case Some (args1) =>
206
+ args = args1
207
+ tt.tupleElementTypes
208
+ case _ => None
209
+ case _ => None
210
+
211
+ /** The computed argument types which will be the scutinees of the sub-patterns. */
212
+ val argTypes : List [Type ] =
213
+ if unapplyName == nme.unapplySeq then
214
+ unapplySeq(unapplyResult):
215
+ if (isGetMatch(unapplyResult, pos)) unapplySeq(getTp)(fail)
216
+ else fail
217
+ else
218
+ assert(unapplyName == nme.unapply)
219
+ productUnapplySelectors(unapplyResult).getOrElse:
220
+ if isGetMatch(unapplyResult, pos) then
221
+ getUnapplySelectors(getTp)
222
+ else if unapplyResult.derivesFrom(defn.BooleanClass ) then
223
+ Nil
224
+ else if unapplyResult.derivesFrom(defn.NonEmptyTupleClass ) then
225
+ unapplyResult.tupleElementTypes.getOrElse(Nil )
226
+ else if defn.isProductSubType(unapplyResult) && productArity(unapplyResult, pos) != 0 then
227
+ productSelectorTypes(unapplyResult, pos)
228
+ // this will cause a "wrong number of arguments in pattern" error later on,
229
+ // which is better than the message in `fail`.
230
+ else fail
231
+
232
+ /** The typed pattens of this unapply */
233
+ def typedPatterns (qual : untpd.Tree , typer : Typer ): List [Tree ] =
234
+ unapp.println(i " unapplyQual = $qual, unapplyArgs = ${unapplyResult} with $argTypes / $args" )
235
+ for argType <- argTypes do
236
+ assert(! isBounds(argType), unapplyResult.show)
237
+ val alignedArgs = argTypes match
238
+ case argType :: Nil
239
+ if args.lengthCompare(1 ) > 0
240
+ && Feature .autoTuplingEnabled
241
+ && defn.isTupleNType(argType) =>
242
+ untpd.Tuple (args) :: Nil
243
+ case _ =>
244
+ args
245
+ val alignedArgTypes =
246
+ if argTypes.length == alignedArgs.length then
247
+ argTypes
248
+ else
249
+ report.error(UnapplyInvalidNumberOfArguments (qual, argTypes), pos)
250
+ argTypes.take(args.length) ++
251
+ List .fill(argTypes.length - args.length)(WildcardType )
252
+ alignedArgs.lazyZip(alignedArgTypes).map(typer.typed(_, _))
253
+ .showing(i " unapply patterns = $result" , unapp)
254
+
255
+ end UnapplyArgs
197
256
198
257
def wrapDefs (defs : mutable.ListBuffer [Tree ] | Null , tree : Tree )(using Context ): Tree =
199
258
if (defs != null && defs.nonEmpty) tpd.Block (defs.toList, tree) else tree
@@ -1452,28 +1511,10 @@ trait Applications extends Compatibility {
1452
1511
loop(unapp)
1453
1512
res.result()
1454
1513
}
1455
- val args = desugar.adaptPatternArgs(unadaptedArgs, unapplyApp.tpe)
1456
-
1457
- var argTypes = unapplyArgs(unapplyApp.tpe.stripNamedTuple, unapplyFn, args, tree.srcPos)
1458
- unapp.println(i " unapplyArgs = ${unapplyApp.tpe} with $argTypes / $args" )
1459
- for (argType <- argTypes) assert(! isBounds(argType), unapplyApp.tpe.show)
1460
- val bunchedArgs = argTypes match {
1461
- case argType :: Nil =>
1462
- if args.lengthCompare(1 ) > 0
1463
- && Feature .autoTuplingEnabled
1464
- && defn.isTupleNType(argType)
1465
- then untpd.Tuple (args) :: Nil
1466
- else args
1467
- case _ => args
1468
- }
1469
- if (argTypes.length != bunchedArgs.length) {
1470
- report.error(UnapplyInvalidNumberOfArguments (qual, argTypes), tree.srcPos)
1471
- argTypes = argTypes.take(args.length) ++
1472
- List .fill(argTypes.length - args.length)(WildcardType )
1473
- }
1474
- val unapplyPatterns = bunchedArgs.lazyZip(argTypes) map (typed(_, _))
1514
+
1515
+ val unapplyPatterns = UnapplyArgs (unapplyApp.tpe, unapplyFn, unadaptedArgs, tree.srcPos)
1516
+ .typedPatterns(qual, this )
1475
1517
val result = assignType(cpy.UnApply (tree)(unapplyFn, unapplyImplicits(unapplyApp), unapplyPatterns), ownType)
1476
- unapp.println(s " unapply patterns = $unapplyPatterns" )
1477
1518
if (ownType.stripped eq selType.stripped) || ownType.isError then result
1478
1519
else tryWithTypeTest(Typed (result, TypeTree (ownType)), selType)
1479
1520
case tp =>
0 commit comments