-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Fix DestroyHoisting: InteriorLiveness for noescape closures #79985
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@swift-ci test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
type.objectType.isEscapable(in: parentFunction) | ||
} | ||
|
||
public var mayEscape: Bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a comment also here? E.g. "Like isEscapable
, but also returns true for not-escaping functions.".
And ideally also for Type.mayEscape
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
extension Type {
/// Return true if this type conforms to Escapable.
///
/// Note: invalid types are non-Escapable, so take care not to make assumptions about non-Escapable types.
public func isEscapable(in function: Function) -> Bool {
bridged.isEscapable(function.bridged)
}
/// Return true if this type conforms to Escapable and is not a noescape function type.
///
/// Warning: may return true for (source-level) non-escaping closures. After SILGen, all partial_apply instructions
/// have an escaping function type. ClosureLifetimeFixup only changes them to noescape function type if they are
/// promoted to [on_stack]. But regardless of stack promotion, a non-escaping closure value's lifetime is constrained
/// by the lifetime of its captures. Use Value.mayEscape instead to check for this case.
public func mayEscape(in function: Function) -> Bool {
!isNoEscapeFunction && isEscapable(in: function)
}
extension Value {
/// Return true if the object type conforms to Escapable.
///
/// Note: noescape function types conform to Escapable, use mayEscape instead to exclude them.
public var isEscapable: Bool {
type.objectType.isEscapable(in: parentFunction)
}
/// Return true only if this value's lifetime is unconstrained by an outer lifetime. Requires all of the following:
/// - the object type conforms to Escapable
/// - the type is not a noescape function
/// - the value is not the direct result of a partial_apply with a noescape (inout_aliasable) capture.
public var mayEscape: Bool {
if !type.objectType.mayEscape(in: parentFunction) {
return false
}
// A noescape partial_apply has an escaping function type if it has not been promoted to on_stack, but it's value
// still cannot "escape" its captures.
//
// TODO: This would be much more robust if pai.hasNoescapeCapture simply implied !pai.type.isEscapable
if let pai = self as? PartialApplyInst {
return pai.mayEscape
}
return true
}
InteriorLiveness has a new "visitInnerUses" mode used by DestroyHoisting. That mode may visit dependent values, which was not valid for noescape closures. ClosureLifetimeFixup inserts destroys of noescape closures after the destroys of the captures. So following such dependent value could result in an apparent use-after-destroy. This causes DestroyHoisting to insert redundant destroys. Fix: InteriorUses will conservatively only follow dependent values if they are escapable. Non-escapable values, like noescape closures are now considered escapes of the original value that the non-escapable value depends on. This can be improved in the future, but we may want to rewrite ClosureLifetimeFixup first. Fixes the root cause of: rdar://146142041
444c718
to
995c749
Compare
@swift-ci smoke test |
lgtm |
@swift-ci smoke test |
1 similar comment
@swift-ci smoke test |
InteriorLiveness has a new "visitInnerUses" mode used by DestroyHoisting. That mode may visit dependent values, which was not valid for noescape closures. ClosureLifetimeFixup inserts destroys of noescape closures after the destroys of the captures. So following such dependent value could result in an apparent use-after-destroy. This causes DestroyHoisting to insert redundant destroys.
Fix: InteriorUses will conservatively only follow dependent values if they are escapable. Non-escapable values, like noescape closures are now considered escapes of the original value that the non-escapable value depends on. This can be improved in the future, but we may want to rewrite ClosureLifetimeFixup first.
Fixes the root cause of: rdar://146142041 (PR #79841)