-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Implement verification that noescape closures passed to Objective-C are not escaped #16236
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
Implement verification that noescape closures passed to Objective-C are not escaped #16236
Conversation
@swift-ci Please test |
@swift-ci Please test source compatibility |
@swift-ci Please test |
1 similar comment
@swift-ci Please test |
Build failed |
Build failed |
@swift-ci Please test |
Build failed |
Build failed |
@swift-ci Please test |
Build failed |
Build failed |
@swift-ci Please clean test linux |
@swift-ci Please test source compatibility |
Build failed |
@swift-ci Please test |
Build failed |
Build failed |
@swift-ci Please test source compatibility |
Latest source compatibility run here: https://ci.swift.org/view/Pull%20Request/job/swift-PR-source-compat-suite/1124/ |
@swift-ci Please benchmark |
benchmark run here: https://ci.swift.org/view/Pull%20Request/job/swift-PR-osx-perf/215 |
Build comment file:Optimized (O)Regression (5)
Improvement (5)
No Changes (414)
Unoptimized (Onone)Regression (12)
Improvement (22)
No Changes (390)
Hardware Overview
|
…truction Mandatory pass will clean it up and replace it by a copy_block and is_escaping/cond_fail/release combination on the %closure in follow-up patches. The instruction marks the dependence of a block on a closure that is used as an 'withoutActuallyEscaping' sentinel. rdar://39682865
rdar://39682865
…res stored in blocks rdar://39682865
I am going to introduce a third use in a follow-up
…tructions Replace: %copy = copy_block_without_escaping %block withoutEscaping %closure ... destroy_value %copy by (roughly) the instruction sequence: %copy = copy_block %block ... destroy_value %copy %e = is_escaping %closure cond_fail %e destroy_value %closure rdar://39682865
After the copy_block_without_actually_escaping change it might see a mark_dependence instruction on a noescape closure. rdar://39682865
The pattern we see for noescape closure passed to objective c is different: There is the additional without actually escaping closure sentinel. rdar://39682865
rdar://39682865
escapes the closure. We expect the program to crash with an explanation. rdar://39682865
Objective C closures when reporting that a closure has escaped rdar://39682865
950dbb8
to
1f65ee2
Compare
@swift-ci Please test |
Build failed |
Build failed |
…ve-C are not escaped The series of commit introduces a ``copy_block_without_escaping`` block instruction that ties a swift closure sentinel to the block that is escaped. SILGen emits this instruction when it generates a conversion from a ``@noescape`` swift closure to a block. ``` %noescape_closure = ... %wae_Thunk = function_ref @$withoutActuallyEscapingThunk %sentinel = partial_apply [callee_guaranteed] %wae_thunk(%noescape_closure) %noescaped_wrapped = mark_dependence %sentinel on %noescape_closure %sentinel_copy = copy_value %noescaped_wrapped %storage = alloc_stack %storage_address = project_block_storage %storage store %noescaped_wrapped to [init] %storage_address %block = init_block_storage_header %storage ... %block = copy_block_without_escaping %block withoutEscaping %noescaped_wrapped ``` The ClosureLifetimeFixup pass then lowers ``copy_block_without_escaping`` into a ``copy_block`` and ``is_escaping``/``cond_fail``/``destroy_value`` combination at a point that succeeds the end of the block's lifetime. As a result whenever a semantic noescape Swift closure synchronously escapes to Objective C we will trap with an error after we return from the Objective C call. rdar://39682865 Cherry-pick of swiftlang#16236 Commits: * Add a copy_block_without_escaping %block withoutEscaping %closure instruction Mandatory pass will clean it up and replace it by a copy_block and is_escaping/cond_fail/release combination on the %closure in follow-up patches. The instruction marks the dependence of a block on a closure that is used as an 'withoutActuallyEscaping' sentinel. * SIL: Allow is_escaping_closure on optional closure types * SILGen: Emit withoutActuallyEscaping verification for @NoEscape closures stored in blocks * SIL: Add getSingleDealloc to AllocStack and remove two copies of it I am going to introduce a third use in a follow-up * ClosureLifetimeFixup: Add support for copy_block_without_escaping instructions Replace: %copy = copy_block_without_escaping %block withoutEscaping %closure ... destroy_value %copy by (roughly) the instruction sequence: %copy = copy_block %block ... destroy_value %copy %e = is_escaping %closure cond_fail %e destroy_value %closure * Fix DiagnoseStaticExclusivity After the copy_block_without_actually_escaping change it might see a mark_dependence instruction on a noescape closure. * Fixes to AccessSummaryAnalysis The pattern we see for noescape closure passed to objective c is different: There is the additional without actually escaping closure sentinel. * Fix test cases after SIL representation changes * Executable test case for passing a noescape closure to Objective-c which escapes the closure. We expect the program to crash with an explanation. * Distinguish between withoutActuallyEscaping and passing @NoEscape Objective C closures when reporting that a closure has escaped rdar://39682865
…ve-C are not escaped The series of commit introduces a ``copy_block_without_escaping`` block instruction that ties a swift closure sentinel to the block that is escaped. SILGen emits this instruction when it generates a conversion from a ``@noescape`` swift closure to a block. ``` %noescape_closure = ... %wae_Thunk = function_ref @$withoutActuallyEscapingThunk %sentinel = partial_apply [callee_guaranteed] %wae_thunk(%noescape_closure) %noescaped_wrapped = mark_dependence %sentinel on %noescape_closure %sentinel_copy = copy_value %noescaped_wrapped %storage = alloc_stack %storage_address = project_block_storage %storage store %noescaped_wrapped to [init] %storage_address %block = init_block_storage_header %storage ... %block = copy_block_without_escaping %block withoutEscaping %noescaped_wrapped ``` The ClosureLifetimeFixup pass then lowers ``copy_block_without_escaping`` into a ``copy_block`` and ``is_escaping``/``cond_fail``/``destroy_value`` combination at a point that succeeds the end of the block's lifetime. As a result whenever a semantic noescape Swift closure synchronously escapes to Objective C we will trap with an error after we return from the Objective C call. rdar://39682865 Cherry-pick of swiftlang#16236 Commits: * Add a copy_block_without_escaping %block withoutEscaping %closure instruction Mandatory pass will clean it up and replace it by a copy_block and is_escaping/cond_fail/release combination on the %closure in follow-up patches. The instruction marks the dependence of a block on a closure that is used as an 'withoutActuallyEscaping' sentinel. * SIL: Allow is_escaping_closure on optional closure types * SILGen: Emit withoutActuallyEscaping verification for @NoEscape closures stored in blocks * SIL: Add getSingleDealloc to AllocStack and remove two copies of it I am going to introduce a third use in a follow-up * ClosureLifetimeFixup: Add support for copy_block_without_escaping instructions Replace: %copy = copy_block_without_escaping %block withoutEscaping %closure ... destroy_value %copy by (roughly) the instruction sequence: %copy = copy_block %block ... destroy_value %copy %e = is_escaping %closure cond_fail %e destroy_value %closure * Fix DiagnoseStaticExclusivity After the copy_block_without_actually_escaping change it might see a mark_dependence instruction on a noescape closure. * Fixes to AccessSummaryAnalysis The pattern we see for noescape closure passed to objective c is different: There is the additional without actually escaping closure sentinel. * Fix test cases after SIL representation changes * Executable test case for passing a noescape closure to Objective-c which escapes the closure. We expect the program to crash with an explanation. * Distinguish between withoutActuallyEscaping and passing @NoEscape Objective C closures when reporting that a closure has escaped rdar://39682865
The series of commit introduces a
copy_block_without_escaping
block instruction that ties a swift closure sentinel to the block that is escaped.SILGen emits this instruction when it generates a conversion from a
@noescape
swift closure to a block.The ClosureLifetimeFixup pass then lowers
copy_block_without_escaping
into acopy_block
andis_escaping
/cond_fail
/destroy_value
combination at a point that succeeds the end of the block's lifetime.As a result whenever a semantic noescape Swift closure synchronously escapes to Objective C we will trap with an error after we return from the Objective C call.
rdar://39682865