@@ -925,6 +925,8 @@ void LifetimeChecker::injectActorHopForBlock(
925925 injectHopToExecutorAfter (loc, bbi, TheMemory.getUninitializedValue ());
926926}
927927
928+ static bool isFailableInitReturnUseOfEnum (EnumInst *EI);
929+
928930void LifetimeChecker::injectActorHops () {
929931 auto ctor = TheMemory.getActorInitSelf ();
930932
@@ -947,11 +949,36 @@ void LifetimeChecker::injectActorHops() {
947949 // Returns true iff a block returns normally from the initializer,
948950 // which means that it returns `self` in some way (perhaps optional-wrapped).
949951 auto returnsSelf = [](SILBasicBlock &block) -> bool {
950- // This check relies on the fact that failable initializers are emitted by
951- // SILGen to perform their return in a fresh block with either:
952- // 1. No non-load uses of `self` (e.g., failing case)
953- // 2. An all-Yes in-availability. (e.g., success case)
954- return block.getTerminator ()->getTermKind () == TermKind::ReturnInst;
952+ auto term = block.getTerminator ();
953+ auto kind = term->getTermKind ();
954+
955+ // Does this block return directly?
956+ if (kind == TermKind::ReturnInst)
957+ return true ;
958+
959+
960+ // Does this block return `self` wrapped in an Optional?
961+ // The pattern would look like:
962+ //
963+ // thisBB:
964+ // ...
965+ // %x = enum $Optional<Dactor>, #Optional.some!enumelt
966+ // br exitBB(%x : $Optional<Dactor>)
967+ //
968+ // exitBB(%y : $Optional<Dactor>):
969+ // return %y : $Optional<Dactor>
970+ //
971+ if (kind == TermKind::BranchInst)
972+ if (term->getNumOperands () == 1 )
973+ if (auto *passedVal = term->getOperand (0 )->getDefiningInstruction ())
974+ if (auto *ei = dyn_cast<EnumInst>(passedVal))
975+ if (isFailableInitReturnUseOfEnum (ei))
976+ // Once we've reached this point, we know it's an Optional enum.
977+ // To determine whether it's .some or .none, we can just check
978+ // the number of operands.
979+ return ei->getNumOperands () == 1 ; // is it .some ?
980+
981+ return false ;
955982 };
956983
957984 // ///
0 commit comments