@@ -303,8 +303,139 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
303
303
def optimizeCases (prevBinder : Symbol , cases : List [List [TreeMaker ]], pt : Type ): (List [List [TreeMaker ]], List [Tree ])
304
304
def analyzeCases (prevBinder : Symbol , cases : List [List [TreeMaker ]], pt : Type , suppression : Suppression ): Unit = {}
305
305
306
- def emitSwitch (scrut : Tree , scrutSym : Symbol , cases : List [List [TreeMaker ]], pt : Type , matchFailGenOverride : Option [Symbol => Tree ], unchecked : Boolean ): Option [Tree ] =
307
- None // todo
306
+ def emitSwitch (scrut : Tree , scrutSym : Symbol , cases : List [List [TreeMaker ]], pt : Type , matchFailGenOverride : Option [Symbol => Tree ], unchecked : Boolean ): Option [Tree ] = {
307
+ // TODO Deal with guards?
308
+
309
+ def isSwitchableType (tpe : Type ): Boolean = {
310
+ (tpe isRef defn.IntClass ) ||
311
+ (tpe isRef defn.ByteClass ) ||
312
+ (tpe isRef defn.ShortClass ) ||
313
+ (tpe isRef defn.CharClass )
314
+ }
315
+
316
+ object IntEqualityTestTreeMaker {
317
+ def unapply (treeMaker : EqualityTestTreeMaker ): Option [Int ] = treeMaker match {
318
+ case EqualityTestTreeMaker (`scrutSym`, _, Literal (const), _) =>
319
+ if (const.isIntRange) Some (const.intValue)
320
+ else None
321
+ case _ =>
322
+ None
323
+ }
324
+ }
325
+
326
+ def isSwitchCase (treeMakers : List [TreeMaker ]): Boolean = treeMakers match {
327
+ // case 5 =>
328
+ case List (IntEqualityTestTreeMaker (_), _ : BodyTreeMaker ) =>
329
+ true
330
+
331
+ // case 5 | 6 =>
332
+ case List (AlternativesTreeMaker (`scrutSym`, alts, _), _ : BodyTreeMaker ) =>
333
+ alts.forall {
334
+ case List (IntEqualityTestTreeMaker (_)) => true
335
+ case _ => false
336
+ }
337
+
338
+ // case _ =>
339
+ case List (_ : BodyTreeMaker ) =>
340
+ true
341
+
342
+ /* case x @ pat =>
343
+ * This includes:
344
+ * case x =>
345
+ * case x @ 5 =>
346
+ * case x @ (5 | 6) =>
347
+ */
348
+ case (_ : SubstOnlyTreeMaker ) :: rest =>
349
+ isSwitchCase(rest)
350
+
351
+ case _ =>
352
+ false
353
+ }
354
+
355
+ /* (Nil, body) means that `body` is the default case
356
+ * It's a bit hacky but it simplifies manipulations.
357
+ */
358
+ def extractSwitchCase (treeMakers : List [TreeMaker ]): (List [Int ], BodyTreeMaker ) = treeMakers match {
359
+ // case 5 =>
360
+ case List (IntEqualityTestTreeMaker (intValue), body : BodyTreeMaker ) =>
361
+ (List (intValue), body)
362
+
363
+ // case 5 | 6 =>
364
+ case List (AlternativesTreeMaker (_, alts, _), body : BodyTreeMaker ) =>
365
+ val intValues = alts.map {
366
+ case List (IntEqualityTestTreeMaker (intValue)) => intValue
367
+ }
368
+ (intValues, body)
369
+
370
+ // case _ =>
371
+ case List (body : BodyTreeMaker ) =>
372
+ (Nil , body)
373
+
374
+ // case x @ pat =>
375
+ case (_ : SubstOnlyTreeMaker ) :: rest =>
376
+ /* Rebindings have been propagated, so the eventual body in `rest`
377
+ * contains all the necessary information. The substitution can be
378
+ * dropped at this point.
379
+ */
380
+ extractSwitchCase(rest)
381
+ }
382
+
383
+ def doOverlap (a : List [Int ], b : List [Int ]): Boolean =
384
+ a.exists(b.contains _)
385
+
386
+ def makeSwitch (valuesToCases : List [(List [Int ], BodyTreeMaker )]): Tree = {
387
+ def genBody (body : BodyTreeMaker ): Tree = {
388
+ val valDefs = body.rebindings.emitValDefs
389
+ if (valDefs.isEmpty) body.body
390
+ else Block (valDefs, body.body)
391
+ }
392
+
393
+ val intScrut =
394
+ if (pt isRef defn.IntClass ) ref(scrutSym)
395
+ else Select (ref(scrutSym), nme.toInt)
396
+
397
+ val (normalCases, defaultCaseAndRest) = valuesToCases.span(_._1.nonEmpty)
398
+
399
+ val newCases = for {
400
+ (values, body) <- normalCases
401
+ } yield {
402
+ val literals = values.map(v => Literal (Constant (v)))
403
+ val pat =
404
+ if (literals.size == 1 ) literals.head
405
+ else Alternative (literals)
406
+ CaseDef (pat, EmptyTree , genBody(body))
407
+ }
408
+
409
+ val catchAllDef = {
410
+ if (defaultCaseAndRest.isEmpty) {
411
+ matchFailGenOverride.fold[Tree ](
412
+ Throw (New (defn.MatchErrorType , List (ref(scrutSym)))))(
413
+ _(scrutSym))
414
+ } else {
415
+ /* After the default case, assuming the IR even allows anything,
416
+ * things are unreachable anyway and can be removed.
417
+ */
418
+ genBody(defaultCaseAndRest.head._2)
419
+ }
420
+ }
421
+ val defaultCase = CaseDef (Underscore (defn.IntType ), EmptyTree , catchAllDef)
422
+
423
+ Match (intScrut, newCases :+ defaultCase)
424
+ }
425
+
426
+ if (isSwitchableType(scrut.tpe.widenDealias) && cases.forall(isSwitchCase)) {
427
+ val valuesToCases = cases.map(extractSwitchCase)
428
+ val values = valuesToCases.map(_._1)
429
+ if (values.tails.exists { tail => tail.nonEmpty && tail.tail.exists(doOverlap(_, tail.head)) }) {
430
+ // TODO Deal with overlapping cases (mostly useless without guards)
431
+ None
432
+ } else {
433
+ Some (makeSwitch(valuesToCases))
434
+ }
435
+ } else {
436
+ None
437
+ }
438
+ }
308
439
309
440
// for catch (no need to customize match failure)
310
441
def emitTypeSwitch (bindersAndCases : List [(Symbol , List [TreeMaker ])], pt : Type ): Option [List [CaseDef ]] =
0 commit comments