33 */
44package scala .async .internal
55
6+ import scala .collection .mutable
67import scala .collection .mutable .ListBuffer
78import language .existentials
89
@@ -407,9 +408,10 @@ trait ExprBuilder {
407408 val stateMemberSymbol = symLookup.stateMachineMember(name.state)
408409 val stateMemberRef = symLookup.memberRef(name.state)
409410 val body = Match (stateMemberRef, mkCombinedHandlerCases[T ] ++ initStates.flatMap(_.mkOnCompleteHandler[T ]) ++ List (CaseDef (Ident (nme.WILDCARD ), EmptyTree , Throw (Apply (Select (New (Ident (defn.IllegalStateExceptionClass )), termNames.CONSTRUCTOR ), List ())))))
411+ val body1 = eliminateDeadStates(body)
410412
411413 maybeTry(
412- body ,
414+ body1 ,
413415 List (
414416 CaseDef (
415417 Bind (name.t, Typed (Ident (nme.WILDCARD ), Ident (defn.ThrowableClass ))),
@@ -423,8 +425,67 @@ trait ExprBuilder {
423425 If (Apply (Ident (defn.NonFatalClass ), List (Ident (name.t))), then , Throw (Ident (name.t)))
424426 then
425427 })), EmptyTree )
428+ }
426429
427- // body
430+ // Identify dead states: `case <id> => { state = nextId; (); (); ... }, eliminated, and compact state ids to
431+ // enable emission of a tableswitch.
432+ private def eliminateDeadStates (m : Match ): Tree = {
433+ object DeadState {
434+ private val liveStates = mutable.AnyRefMap [Integer , Integer ]()
435+ private val deadStates = mutable.AnyRefMap [Integer , Integer ]()
436+ private var compactedStateId = 1
437+ for (CaseDef (Literal (Constant (stateId : Integer )), EmptyTree , body) <- m.cases) {
438+ body match {
439+ case _ if (stateId == 0 ) => liveStates(stateId) = stateId
440+ case Block (Assign (_, Literal (Constant (nextState : Integer ))) :: rest, expr) if (expr :: rest).forall(t => isLiteralUnit(t)) =>
441+ deadStates(stateId) = nextState
442+ case _ =>
443+ liveStates(stateId) = compactedStateId
444+ compactedStateId += 1
445+ }
446+ }
447+ if (deadStates.nonEmpty)
448+ AsyncUtils .vprintln(s " ${deadStates.size} dead states eliminated " )
449+ def isDead (i : Integer ) = deadStates.contains(i)
450+ def translatedStateId (i : Integer , tree : Tree ): Integer = {
451+ def chaseDead (i : Integer ): Integer = {
452+ val replacement = deadStates.getOrNull(i)
453+ if (replacement == null ) i
454+ else chaseDead(replacement)
455+ }
456+
457+ val live = chaseDead(i)
458+ liveStates.get(live) match {
459+ case Some (x) => x
460+ case None => sys.error(s " $live, $liveStates \n $deadStates\n $m\n\n ==== \n $tree" )
461+ }
462+ }
463+ }
464+ val stateMemberSymbol = symLookup.stateMachineMember(name.state)
465+ // - remove CaseDef-s for dead states
466+ // - rewrite state transitions to dead states to instead transition to the
467+ // non-dead successor.
468+ val elimDeadStateTransform = new Transformer {
469+ override def transform (tree : Tree ): Tree = tree match {
470+ case as @ Assign (lhs, Literal (Constant (i : Integer ))) if lhs.symbol == stateMemberSymbol =>
471+ val replacement = DeadState .translatedStateId(i, as)
472+ treeCopy.Assign (tree, lhs, Literal (Constant (replacement)))
473+ case _ : Match | _ : CaseDef | _ : Block | _ : If =>
474+ super .transform(tree)
475+ case _ => tree
476+ }
477+ }
478+ val cases1 = m.cases.flatMap {
479+ case cd @ CaseDef (Literal (Constant (i : Integer )), EmptyTree , rhs) =>
480+ if (DeadState .isDead(i)) Nil
481+ else {
482+ val replacement = DeadState .translatedStateId(i, cd)
483+ val rhs1 = elimDeadStateTransform.transform(rhs)
484+ treeCopy.CaseDef (cd, Literal (Constant (replacement)), EmptyTree , rhs1) :: Nil
485+ }
486+ case x => x :: Nil
487+ }
488+ treeCopy.Match (m, m.selector, cases1)
428489 }
429490
430491 def forever (t : Tree ): Tree = {
0 commit comments