@@ -17,14 +17,14 @@ import symtab._
17
17
import Flags ._
18
18
import scala .collection ._
19
19
import scala .tools .nsc .Reporting .WarningCategory
20
+ import scala .util .chaining ._
20
21
21
22
abstract class CleanUp extends Statics with Transform with ast.TreeDSL {
22
23
import global ._
23
24
import definitions ._
24
25
import CODE ._
25
- import treeInfo .StripCast
26
+ import treeInfo .{ SYNTH_CASE_FLAGS , isDefaultCase , StripCast }
26
27
27
- /** the following two members override abstract members in Transform */
28
28
val phaseName : String = " cleanup"
29
29
30
30
/* used in GenBCode: collects ClassDef symbols owning a main(Array[String]) method */
@@ -398,105 +398,94 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL {
398
398
}
399
399
}
400
400
401
- // transform scrutinee of all matches to ints
402
- def transformSwitch (sw : Match ): Tree = { import CODE ._
403
- sw.selector.tpe.widen match {
404
- case IntTpe => sw // can switch directly on ints
405
- case StringTpe =>
406
- // these assumptions about the shape of the tree are justified by the codegen in MatchOptimization
407
- val Match (Typed (selTree, _), cases) = sw : @ unchecked
408
- def selArg = selTree match {
409
- case x : Ident => REF (x.symbol)
410
- case x : Literal => x
411
- case x => throw new MatchError (x)
412
- }
413
- val restpe = sw.tpe
414
- val swPos = sw.pos.focus
415
-
416
- /* From this:
417
- * string match { case "AaAa" => 1 case "BBBB" | "c" => 2 case _ => 3}
418
- * Generate this:
419
- * string.## match {
420
- * case 2031744 =>
421
- * if ("AaAa" equals string) goto match1
422
- * else if ("BBBB" equals string) goto match2
423
- * else goto matchFailure
424
- * case 99 =>
425
- * if ("c" equals string) goto match2
426
- * else goto matchFailure
427
- * case _ => goto matchFailure
428
- * }
429
- * match1: goto matchSuccess (1)
430
- * match2: goto matchSuccess (2)
431
- * matchFailure: goto matchSuccess (3) // would be throw new MatchError(string) if no default was given
432
- * matchSuccess(res: Int): res
433
- * This proliferation of labels is needed to handle alternative patterns, since multiple branches in the
434
- * resulting switch may need to correspond to a single case body.
435
- */
436
-
437
- val stats = mutable.ListBuffer .empty[Tree ]
438
- var failureBody = Throw (New (definitions.MatchErrorClass .tpe_* , selArg)) : Tree
439
-
440
- // genbcode isn't thrilled about seeing labels with Unit arguments, so `success`'s type is one of
441
- // `${sw.tpe} => ${sw.tpe}` or `() => Unit` depending.
442
- val success = {
443
- val lab = currentOwner.newLabel(unit.freshTermName(" matchEnd" ), swPos)
444
- if (restpe =:= UnitTpe ) {
445
- lab.setInfo(MethodType (Nil , restpe))
446
- } else {
447
- lab.setInfo(MethodType (lab.newValueParameter(nme.x_1).setInfo(restpe) :: Nil , restpe))
448
- }
449
- }
450
- def succeed (res : Tree ): Tree =
451
- if (restpe =:= UnitTpe ) BLOCK (res, REF (success) APPLY Nil ) else REF (success) APPLY res
452
-
453
- val failure = currentOwner.newLabel(unit.freshTermName(" matchEnd" ), swPos).setInfo(MethodType (Nil , restpe))
454
- def fail (): Tree = atPos(swPos) { Apply (REF (failure), Nil ) }
455
-
456
- val ifNull = LIT (0 )
457
- val noNull = Apply (selArg DOT Object_hashCode , Nil )
458
-
459
- val newSel = selTree match {
460
- case _ : Ident => atPos(selTree.symbol.pos) { IF (selTree.symbol OBJ_EQ NULL ) THEN ifNull ELSE noNull }
461
- case x : Literal => atPos(selTree.pos) { if (x.value.value == null ) ifNull else noNull }
462
- case x => throw new MatchError (x)
401
+ private def transformStringSwitch (sw : Match ): Tree = { import CODE ._
402
+ // these assumptions about the shape of the tree are justified by the codegen in MatchOptimization
403
+ val Match (Typed (selTree, _), cases) = sw : @ unchecked
404
+ def selArg = selTree match {
405
+ case x : Ident => REF (x.symbol)
406
+ case x : Literal => x
407
+ case x => throw new MatchError (x)
408
+ }
409
+ val newSel = selTree match {
410
+ case x : Ident => atPos(x.symbol.pos)(IF (x.symbol OBJ_EQ NULL ) THEN ZERO ELSE selArg.OBJ_## )
411
+ case x : Literal => atPos(x.pos) (if (x.value.value == null ) ZERO else selArg.OBJ_## )
412
+ case x => throw new MatchError (x)
413
+ }
414
+ val restpe = sw.tpe
415
+ val resUnit = restpe =:= UnitTpe
416
+ val swPos = sw.pos.focus
417
+
418
+ /* From this:
419
+ * string match { case "AaAa" => 1 case "BBBB" | "c" => 2 case _ => 3 }
420
+ * Generate this:
421
+ * string.## match {
422
+ * case 2031744 =>
423
+ * if ("AaAa" equals string) goto matchEnd (1)
424
+ * else if ("BBBB" equals string) goto case2
425
+ * else goto defaultCase
426
+ * case 99 =>
427
+ * if ("c" equals string) goto case2
428
+ * else goto defaultCase
429
+ * case _ => goto defaultCase
430
+ * }
431
+ * case2: goto matchEnd (2)
432
+ * defaultCase: goto matchEnd (3) // or `goto matchEnd (throw new MatchError(string))` if no default was given
433
+ * matchEnd(res: Int): res
434
+ * Extra labels are added for alternative patterns branches, since multiple branches in the
435
+ * resulting switch may need to correspond to a single case body.
436
+ */
437
+
438
+ val labels = mutable.ListBuffer .empty[LabelDef ]
439
+ var defaultCaseBody = Throw (New (MatchErrorClass .tpe_* , selArg)): Tree
440
+
441
+ def LABEL (name : String ) = currentOwner.newLabel(unit.freshTermName(name), swPos).setFlag(SYNTH_CASE_FLAGS )
442
+ def newCase () = LABEL ( " case" ).setInfo(MethodType (Nil , restpe))
443
+ val defaultCase = LABEL (" defaultCase" ).setInfo(MethodType (Nil , restpe))
444
+ val matchEnd = LABEL (" matchEnd" ).tap { lab =>
445
+ // genbcode isn't thrilled about seeing labels with Unit arguments, so `success`'s type is one of
446
+ // `${sw.tpe} => ${sw.tpe}` or `() => Unit` depending.
447
+ lab.setInfo(MethodType (if (resUnit) Nil else List (lab.newSyntheticValueParam(restpe)), restpe))
448
+ }
449
+ def goto (sym : Symbol , params : Tree * ) = REF (sym) APPLY (params : _* )
450
+ def gotoEnd (body : Tree ) = if (resUnit) BLOCK (body, goto(matchEnd)) else goto(matchEnd, body)
451
+
452
+ val casesByHash = cases.flatMap {
453
+ case cd@ CaseDef (StringsPattern (strs), _, body) =>
454
+ val jump = newCase() // always create a label so when its used it matches the source case (e.g. `case4()`)
455
+ strs match {
456
+ case str :: Nil => List ((str, gotoEnd(body), cd.pat.pos))
457
+ case _ =>
458
+ labels += LabelDef (jump, Nil , gotoEnd(body))
459
+ strs.map((_, goto(jump), cd.pat.pos))
463
460
}
464
- val casesByHash =
465
- cases.flatMap {
466
- case cd@ CaseDef (StringsPattern (strs), _, body) =>
467
- val jump = currentOwner.newLabel(unit.freshTermName(" case" ), swPos).setInfo(MethodType (Nil , restpe))
468
- stats += LabelDef (jump, Nil , succeed(body))
469
- strs.map((_, jump, cd.pat.pos))
470
- case cd@ CaseDef (Ident (nme.WILDCARD ), _, body) =>
471
- failureBody = succeed(body)
472
- None
473
- case cd => globalError(s " unhandled in switch: $cd" ); None
474
- }.groupBy(_._1.## )
475
- val newCases = casesByHash.toList.sortBy(_._1).map {
476
- case (hash, cases) =>
477
- val newBody = cases.foldLeft(fail()) {
478
- case (next, (pat, jump, pos)) =>
479
- val comparison = if (pat == null ) Object_eq else Object_equals
480
- atPos(pos) {
481
- IF (LIT (pat) DOT comparison APPLY selArg) THEN (REF (jump) APPLY Nil ) ELSE next
482
- }
483
- }
484
- CaseDef (LIT (hash), EmptyTree , newBody)
461
+ case cd if isDefaultCase(cd) => defaultCaseBody = gotoEnd(cd.body); None
462
+ case cd => globalError(s " unhandled in switch: $cd" ); None
463
+ }.groupBy(_._1.## )
464
+
465
+ val newCases = casesByHash.toList.sortBy(_._1).map {
466
+ case (hash, cases) =>
467
+ val newBody = cases.foldRight(atPos(swPos)(goto(defaultCase): Tree )) {
468
+ case ((null , rhs, pos), next) => atPos(pos)(IF (NULL OBJ_EQ selArg) THEN rhs ELSE next)
469
+ case ((str, rhs, pos), next) => atPos(pos)(IF (LIT (str) OBJ_== selArg) THEN rhs ELSE next)
485
470
}
471
+ CASE (LIT (hash)) ==> newBody
472
+ }
486
473
487
- stats += LabelDef (failure, Nil , failureBody)
474
+ labels += LabelDef (defaultCase, Nil , defaultCaseBody)
475
+ labels += LabelDef (matchEnd, matchEnd.info.params, matchEnd.info.params.headOption.fold(UNIT : Tree )(REF ))
488
476
489
- stats += (if (restpe =:= UnitTpe ) {
490
- LabelDef (success, Nil , gen.mkLiteralUnit)
491
- } else {
492
- LabelDef (success, success.info.params.head :: Nil , REF (success.info.params.head))
493
- })
477
+ val stats = Match (newSel, newCases :+ (DEFAULT ==> goto(defaultCase))) :: labels.toList
494
478
495
- stats prepend Match (newSel, newCases :+ CaseDef (Ident (nme.WILDCARD ), EmptyTree , fail()))
479
+ val res = Block (stats : _* )
480
+ localTyper.typedPos(sw.pos)(res)
481
+ }
496
482
497
- val res = Block (stats.result() : _* )
498
- localTyper.typedPos(sw.pos)(res)
499
- case _ => globalError(s " unhandled switch scrutinee type ${sw.selector.tpe}: $sw" ); sw
483
+ // transform scrutinee of all matches to switchable types (ints, strings)
484
+ def transformSwitch (sw : Match ): Tree = {
485
+ sw.selector.tpe.widen match {
486
+ case IntTpe => sw // can switch directly on ints
487
+ case StringTpe => transformStringSwitch(sw)
488
+ case _ => globalError(s " unhandled switch scrutinee type ${sw.selector.tpe}: $sw" ); sw
500
489
}
501
490
}
502
491
0 commit comments