Skip to content

Commit 25a7327

Browse files
committed
fix missing hop in failable async actor inits
The returnsSelf check in DI was missing proper handling for failable inits. This patch fixes that by specifically recognizing the tail-end of the success path in a failable init, and treating that block as "returning self".
1 parent 0eee3bc commit 25a7327

File tree

2 files changed

+48
-5
lines changed

2 files changed

+48
-5
lines changed

lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -925,6 +925,8 @@ void LifetimeChecker::injectActorHopForBlock(
925925
injectHopToExecutorAfter(loc, bbi, TheMemory.getUninitializedValue());
926926
}
927927

928+
static bool isFailableInitReturnUseOfEnum(EnumInst *EI);
929+
928930
void 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
/////

test/SILOptimizer/definite_init_actor.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,22 @@ actor BoringActor {
108108
}
109109
myVar = 2
110110
}
111+
112+
// CHECK-LABEL: sil hidden @$s4test14SingleVarActorC14failable_asyncACSgSb_tYacfc : $@convention(method) @async (Bool, @owned SingleVarActor) -> @owned Optional<SingleVarActor> {
113+
// CHECK: bb0({{%[0-9]+}} : $Bool, {{%[0-9]+}} : $SingleVarActor):
114+
// CHECK: cond_br {{%[0-9]+}}, [[SUCCESS_BB:bb[0-9]+]], {{bb[0-9]+}}
115+
//
116+
// CHECK: [[SUCCESS_BB]]:
117+
// CHECK: store {{%[0-9]+}} to {{%[0-9]+}} : $*Int
118+
// CHECK: hop_to_executor {{%[0-9]+}}
119+
// CHECK: enum $Optional<SingleVarActor>, #Optional.some!enumelt
120+
//
121+
// CHECK: } // end sil function '$s4test14SingleVarActorC14failable_asyncACSgSb_tYacfc'
122+
init?(failable_async cond: Bool) async {
123+
guard cond else { return nil }
124+
myVar = 1
125+
}
126+
111127
}
112128

113129
actor DefaultInit {

0 commit comments

Comments
 (0)