-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Re-implement "no-escape parameter call restriction" as SIL pass #23907
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
Re-implement "no-escape parameter call restriction" as SIL pass #23907
Conversation
This fixes a test involving transitive captures of local functions, as well as an infinite recursion possible with the old code. Fixes <rdar://problem/34496304>.
@swift-ci Please test |
@swift-ci Please test source compatibility |
Build failed |
@swift-ci Please test Linux |
@slavapestov I'm a little worried that we don't have any verification to ensure that follows the uses of non-escaping function parameters to ensure that all possible SIL patterns are recognized, thereby proving that this diagnostic is complete. But this is still a big improvement! |
static void checkForViolationsAtInstruction(ASTContext &Context, | ||
DeclContext *DC, | ||
SILInstruction *I) { | ||
if (auto *PAI = dyn_cast<PartialApplyInst>(I)) | ||
checkPartialApply(Context, DC, PAI); | ||
|
||
if (isa<ApplyInst>(I) || isa<TryApplyInst>(I)) { |
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 use FullApplySite::isa
here? The code above seems like it would work with coroutines, and there might be coroutine closures in the future.
@slavapestov Is there some intuition behind this new error? I see it's about overlapping variable modifications, but I can't quite wrap my head around what a pathological case would look like. I came across this error in the real world while trying to de-duplicate some wrapper functions I made. A reduced example: Given some library functions I don't control, func foo(_ f: () -> Void) -> Error?
func bar(_ f: () -> Void) -> Error? I wrapped them like so to make them more Swift-friendly: struct DefaultError: Error {}
func betterFoo<T>(_ body: () throws -> T) throws -> T { // (not rethrows, because foo() itself can return an error)
var result = Result<T, Error>.failure(DefaultError())
if let error = foo({ result = Result(catching: body) }) {
throw error
}
return try result.get()
}
func betterBar<T>(_ body: () throws -> T) throws -> T {
var result = Result<T, Error>.failure(DefaultError())
if let error = bar({ result = Result(catching: body) }) {
throw error
}
return try result.get()
} I didn't like having two copies of the same code, so I tried to deduplicate them with a helper function...but now I get this error. func betterFoo<T>(_ body: () throws -> T) throws -> T {
return try betterize(foo, body)
}
func betterBar<T>(_ body: () throws -> T) throws -> T {
return try betterize(bar, body)
}
func betterize<T>(_ original: (() -> Void) -> Error?, _ body: () throws -> T) throws -> T {
var result = Result<T, Error>.failure(DefaultError())
// error: passing a closure which captures a non-escaping function parameter 'body' to a call to a non-escaping function parameter can allow re-entrant modification of a variable
if let error = original({ result = Result(catching: body) }) {
throw error
}
return try result.get()
} Is there a workaround (other than presumably sprinkling in some Thanks! :) |
My previous PR #23878 exposed a correctness issue. The following code was formerly rejected as an escaping use of a noescape function:
However, there is no escaping violation here, and the new implementation of escaping capture diagnostics made the bogus error go away. On the other hand, this example does violate the "no-escape parameter call restriction" rule, but we failed to diagnose it because the implementation of that rule was not taking transitive captures of local functions into account.
Finally, the Sema implementation of the NPCR rule had a bug that could cause an infinite recursion.
The new implementation looks at SIL instead, which makes it a bit simpler and fixes these correctness issues.
Fixes rdar://problem/34496304.