@@ -303,8 +303,133 @@ 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 =>
343
+ case List (_ : SubstOnlyTreeMaker , _ : BodyTreeMaker ) =>
344
+ true
345
+
346
+ case _ =>
347
+ false
348
+ }
349
+
350
+ /* (Nil, body) means that `body` is the default case
351
+ * It's a bit hacky but it simplifies manipulations.
352
+ */
353
+ def extractSwitchCase (treeMakers : List [TreeMaker ]): (List [Int ], BodyTreeMaker ) = treeMakers match {
354
+ // case 5 =>
355
+ case List (IntEqualityTestTreeMaker (intValue), body : BodyTreeMaker ) =>
356
+ (List (intValue), body)
357
+
358
+ // case 5 | 6 =>
359
+ case List (AlternativesTreeMaker (_, alts, _), body : BodyTreeMaker ) =>
360
+ val intValues = alts.map {
361
+ case List (IntEqualityTestTreeMaker (intValue)) => intValue
362
+ }
363
+ (intValues, body)
364
+
365
+ // case _ =>
366
+ case List (body : BodyTreeMaker ) =>
367
+ (Nil , body)
368
+
369
+ // case x =>
370
+ case List (subst : SubstOnlyTreeMaker , body : BodyTreeMaker ) =>
371
+ /* Rebindings have been propagated, so `body` contains all the
372
+ * necessary information. `subst` can be dropped at this point.
373
+ */
374
+ (Nil , body)
375
+ }
376
+
377
+ def doOverlap (a : List [Int ], b : List [Int ]): Boolean =
378
+ a.exists(b.contains _)
379
+
380
+ def makeSwitch (valuesToCases : List [(List [Int ], BodyTreeMaker )]): Tree = {
381
+ def genBody (body : BodyTreeMaker ): Tree = {
382
+ val valDefs = body.rebindings.emitValDefs
383
+ if (valDefs.isEmpty) body.body
384
+ else Block (valDefs, body.body)
385
+ }
386
+
387
+ val intScrut =
388
+ if (pt isRef defn.IntClass ) ref(scrutSym)
389
+ else Select (ref(scrutSym), nme.toInt)
390
+
391
+ val (normalCases, defaultCaseAndRest) = valuesToCases.span(_._1.nonEmpty)
392
+
393
+ val newCases = for {
394
+ (values, body) <- normalCases
395
+ } yield {
396
+ val literals = values.map(v => Literal (Constant (v)))
397
+ val pat =
398
+ if (literals.size == 1 ) literals.head
399
+ else Alternative (literals)
400
+ CaseDef (pat, EmptyTree , genBody(body))
401
+ }
402
+
403
+ val catchAllDef = {
404
+ if (defaultCaseAndRest.isEmpty) {
405
+ matchFailGenOverride.fold[Tree ](
406
+ Throw (New (defn.MatchErrorType , List (ref(scrutSym)))))(
407
+ _(scrutSym))
408
+ } else {
409
+ /* After the default case, assuming the IR even allows anything,
410
+ * things are unreachable anyway and can be removed.
411
+ */
412
+ genBody(defaultCaseAndRest.head._2)
413
+ }
414
+ }
415
+ val defaultCase = CaseDef (Underscore (defn.IntType ), EmptyTree , catchAllDef)
416
+
417
+ Match (intScrut, newCases :+ defaultCase)
418
+ }
419
+
420
+ if (isSwitchableType(scrut.tpe.widenDealias) && cases.forall(isSwitchCase)) {
421
+ val valuesToCases = cases.map(extractSwitchCase)
422
+ val values = valuesToCases.map(_._1)
423
+ if (values.tails.exists { tail => tail.nonEmpty && tail.tail.exists(doOverlap(_, tail.head)) }) {
424
+ // TODO Deal with overlapping cases (mostly useless without guards)
425
+ None
426
+ } else {
427
+ Some (makeSwitch(valuesToCases))
428
+ }
429
+ } else {
430
+ None
431
+ }
432
+ }
308
433
309
434
// for catch (no need to customize match failure)
310
435
def emitTypeSwitch (bindersAndCases : List [(Symbol , List [TreeMaker ])], pt : Type ): Option [List [CaseDef ]] =
0 commit comments