mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
LifetimeDependenceScopeFixup: crash handling dead-end coroutine
When extending a coroutine, handle the end_borrow instruction used to end a
coroutine lifetime at a dead-end block.
Fixes rdar://153479358 (Compiler crash when force-unwrapping optional ~Copyable type)
(cherry picked from commit 5b5f370ce1)
This commit is contained in:
@@ -801,7 +801,7 @@ extension ExtendableScope {
|
||||
func canExtend(beginApply: BeginApplyInst, over range: inout InstructionRange, _ context: some Context) -> Bool {
|
||||
let canEndAtBoundary = { (boundaryInst: Instruction) in
|
||||
switch beginApply.endReaches(block: boundaryInst.parentBlock, context) {
|
||||
case .abortReaches, .endReaches:
|
||||
case .abortReaches, .endReaches, .deadEndReaches:
|
||||
return true
|
||||
case .none:
|
||||
return false
|
||||
@@ -929,64 +929,62 @@ private extension BeginApplyInst {
|
||||
return builder.createEndApply(beginApply: self)
|
||||
case .abortReaches:
|
||||
return builder.createAbortApply(beginApply: self)
|
||||
case .deadEndReaches:
|
||||
return builder.createEndBorrow(of: self.token)
|
||||
}
|
||||
}
|
||||
|
||||
enum EndReaches {
|
||||
case endReaches
|
||||
case abortReaches
|
||||
case deadEndReaches
|
||||
}
|
||||
|
||||
/// Return the single kind of coroutine termination that reaches 'reachableBlock' or nil.
|
||||
func endReaches(block reachableBlock: BasicBlock, _ context: some Context) -> EndReaches? {
|
||||
var endBlocks = BasicBlockSet(context)
|
||||
var abortBlocks = BasicBlockSet(context)
|
||||
// TODO: use InlineArray<3> once bootstrapping is fixed.
|
||||
var endingBlockMap: [(EndReaches, BasicBlockSet)] = [
|
||||
(.endReaches, BasicBlockSet(context)),
|
||||
(.abortReaches, BasicBlockSet(context)),
|
||||
(.deadEndReaches, BasicBlockSet(context))
|
||||
]
|
||||
defer {
|
||||
endBlocks.deinitialize()
|
||||
abortBlocks.deinitialize()
|
||||
for index in endingBlockMap.indices {
|
||||
endingBlockMap[index].1.deinitialize()
|
||||
}
|
||||
}
|
||||
for endInst in endInstructions {
|
||||
let endKind: EndReaches
|
||||
switch endInst {
|
||||
case let endApply as EndApplyInst:
|
||||
// Cannot extend the scope of a coroutine when the resume produces a value.
|
||||
if !endApply.type.isEmpty(in: parentFunction) {
|
||||
return nil
|
||||
}
|
||||
endBlocks.insert(endInst.parentBlock)
|
||||
endKind = .endReaches
|
||||
case is AbortApplyInst:
|
||||
abortBlocks.insert(endInst.parentBlock)
|
||||
endKind = .abortReaches
|
||||
case is EndBorrowInst:
|
||||
endKind = .deadEndReaches
|
||||
default:
|
||||
fatalError("invalid begin_apply ending instruction")
|
||||
}
|
||||
let endingBlocksIndex = endingBlockMap.firstIndex(where: { $0.0 == endKind })!
|
||||
endingBlockMap[endingBlocksIndex].1.insert(endInst.parentBlock)
|
||||
}
|
||||
var endReaches: EndReaches?
|
||||
var backwardWalk = BasicBlockWorklist(context)
|
||||
defer { backwardWalk.deinitialize() }
|
||||
|
||||
let backwardVisit = { (block: BasicBlock) -> WalkResult in
|
||||
if endBlocks.contains(block) {
|
||||
switch endReaches {
|
||||
case .none:
|
||||
endReaches = .endReaches
|
||||
break
|
||||
case .endReaches:
|
||||
break
|
||||
case .abortReaches:
|
||||
return .abortWalk
|
||||
for (endKind, endingBlocks) in endingBlockMap {
|
||||
if endingBlocks.contains(block) {
|
||||
if let endReaches = endReaches, endReaches != endKind {
|
||||
return .abortWalk
|
||||
}
|
||||
endReaches = endKind
|
||||
return .continueWalk
|
||||
}
|
||||
return .continueWalk
|
||||
}
|
||||
if abortBlocks.contains(block) {
|
||||
switch endReaches {
|
||||
case .none:
|
||||
endReaches = .abortReaches
|
||||
break
|
||||
case .abortReaches:
|
||||
break
|
||||
case .endReaches:
|
||||
return .abortWalk
|
||||
}
|
||||
return .continueWalk
|
||||
}
|
||||
if block == self.parentBlock {
|
||||
// the insertion point is not dominated by the coroutine
|
||||
|
||||
Reference in New Issue
Block a user