@@ -303,8 +303,139 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
303303 def optimizeCases (prevBinder : Symbol , cases : List [List [TreeMaker ]], pt : Type ): (List [List [TreeMaker ]], List [Tree ])
304304 def analyzeCases (prevBinder : Symbol , cases : List [List [TreeMaker ]], pt : Type , suppression : Suppression ): Unit = {}
305305
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+ }
308439
309440 // for catch (no need to customize match failure)
310441 def emitTypeSwitch (bindersAndCases : List [(Symbol , List [TreeMaker ])], pt : Type ): Option [List [CaseDef ]] =
0 commit comments