@@ -198,6 +198,8 @@ object PatternMatcher {
198
198
case object NonNullTest extends Test // scrutinee ne null
199
199
case object GuardTest extends Test // scrutinee
200
200
201
+ val noLengthTest = LengthTest (0 , exact = false )
202
+
201
203
// ------- Generating plans from trees ------------------------
202
204
203
205
/** A set of variabes that are known to be not null */
@@ -291,38 +293,67 @@ object PatternMatcher {
291
293
/** Plan for matching the sequence in `seqSym` against sequence elements `args`.
292
294
* If `exact` is true, the sequence is not permitted to have any elements following `args`.
293
295
*/
294
- def matchElemsPlan (seqSym : Symbol , args : List [Tree ], exact : Boolean , onSuccess : Plan ) = {
295
- val selectors = args.indices.toList.map(idx =>
296
- ref(seqSym).select(defn.Seq_apply .matchingMember(seqSym.info)).appliedTo(Literal (Constant (idx))))
297
- TestPlan (LengthTest (args.length, exact), seqSym, seqSym.span,
298
- matchArgsPlan(selectors, args, onSuccess))
299
- }
296
+ def matchElemsPlan (seqSym : Symbol , args : List [Tree ], lengthTest : LengthTest , onSuccess : Plan ) =
297
+ val selectors = args.indices.toList.map: idx =>
298
+ ref(seqSym).select(defn.Seq_apply .matchingMember(seqSym.info)).appliedTo(Literal (Constant (idx)))
299
+ if lengthTest.len == 0 && lengthTest.exact == false then // redundant test
300
+ matchArgsPlan(selectors, args, onSuccess)
301
+ else
302
+ TestPlan (lengthTest, seqSym, seqSym.span,
303
+ matchArgsPlan(selectors, args, onSuccess))
300
304
301
305
/** Plan for matching the sequence in `getResult` against sequence elements
302
- * and a possible last varargs argument `args`.
306
+ * `args`. Sequence elements may contain a varargs argument.
307
+ * Example:
308
+ *
309
+ * lst match case Seq(1, xs*, 2, 3) => ...
310
+ *
311
+ * generates code which is equivalent to:
312
+ *
313
+ * if lst != null then
314
+ * if lst.lengthCompare >= 3 then
315
+ * if lst(0) == 1 then
316
+ * val x1 = lst.drop(1)
317
+ * val xs = x1.dropRight(2)
318
+ * val x2 = lst.takeRight(2)
319
+ * if x2(0) == 2 && x2(1) == 3 then
320
+ * return[matchResult] ...
303
321
*/
304
- def unapplySeqPlan (getResult : Symbol , args : List [Tree ]): Plan = args.lastOption match {
305
- case Some (VarArgPattern (arg)) =>
306
- val matchRemaining =
307
- if (args.length == 1 ) {
308
- val toSeq = ref(getResult)
309
- .select(defn.Seq_toSeq .matchingMember(getResult.info))
310
- letAbstract(toSeq) { toSeqResult =>
311
- patternPlan(toSeqResult, arg, onSuccess)
312
- }
313
- }
314
- else {
315
- val dropped = ref(getResult)
316
- .select(defn.Seq_drop .matchingMember(getResult.info))
317
- .appliedTo(Literal (Constant (args.length - 1 )))
318
- letAbstract(dropped) { droppedResult =>
319
- patternPlan(droppedResult, arg, onSuccess)
320
- }
321
- }
322
- matchElemsPlan(getResult, args.init, exact = false , matchRemaining)
323
- case _ =>
324
- matchElemsPlan(getResult, args, exact = true , onSuccess)
325
- }
322
+ def unapplySeqPlan (getResult : Symbol , args : List [Tree ]): Plan =
323
+ val (leading, varargAndRest) = args.span:
324
+ case VarArgPattern (_) => false
325
+ case _ => true
326
+ varargAndRest match
327
+ case VarArgPattern (arg) :: trailing =>
328
+ val remaining =
329
+ if leading.isEmpty then
330
+ ref(getResult)
331
+ .select(defn.Seq_toSeq .matchingMember(getResult.info))
332
+ else
333
+ ref(getResult)
334
+ .select(defn.Seq_drop .matchingMember(getResult.info))
335
+ .appliedTo(Literal (Constant (leading.length)))
336
+ val matchRemaining =
337
+ letAbstract(remaining): remainingResult =>
338
+ if trailing.isEmpty then
339
+ patternPlan(remainingResult, arg, onSuccess)
340
+ else
341
+ val seq = ref(remainingResult)
342
+ .select(defn.Seq_dropRight .matchingMember(remainingResult.info))
343
+ .appliedTo(Literal (Constant (trailing.length)))
344
+ letAbstract(seq): seqResult =>
345
+ val rest = ref(remainingResult)
346
+ .select(defn.Seq_takeRight .matchingMember(remainingResult.info))
347
+ .appliedTo(Literal (Constant (trailing.length)))
348
+ val matchTrailing =
349
+ letAbstract(rest): trailingResult =>
350
+ matchElemsPlan(trailingResult, trailing, noLengthTest, onSuccess)
351
+ patternPlan(seqResult, arg, matchTrailing)
352
+ matchElemsPlan(getResult, leading,
353
+ LengthTest (leading.length + trailing.length, exact = false ),
354
+ matchRemaining)
355
+ case _ =>
356
+ matchElemsPlan(getResult, args, LengthTest (args.length, exact = true ), onSuccess)
326
357
327
358
/** Plan for matching the sequence in `getResult`
328
359
*
@@ -491,7 +522,7 @@ object PatternMatcher {
491
522
case WildcardPattern () | This (_) =>
492
523
onSuccess
493
524
case SeqLiteral (pats, _) =>
494
- matchElemsPlan(scrutinee, pats, exact = true , onSuccess)
525
+ matchElemsPlan(scrutinee, pats, LengthTest (pats.length, exact = true ) , onSuccess)
495
526
case _ =>
496
527
TestPlan (EqualTest (tree), scrutinee, tree.span, onSuccess)
497
528
}
0 commit comments