-
Notifications
You must be signed in to change notification settings - Fork 10.6k
Implement async defer #83891
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
base: main
Are you sure you want to change the base?
Implement async defer #83891
Conversation
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.
You can remove FuncDecl::isDeferBody() too then
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.
looks pretty promising so far. The escaping shouldn't really break anything concurrency checking wise as long as the closure is not sending/sendable; that's definitely worth adding a bunch of tests though
lib/AST/ASTVerifier.cpp
Outdated
auto FT = S->getBody()->getType()->castTo<AnyFunctionType>(); | ||
// TODO: Making these into PBDs made them get marked as escaping... are | ||
// there downstream effects? | ||
// assert(FT->isNoEscape() && "Defer statements must not escape"); |
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.
Perhaps we could relax this assertion that if the defer body is not async then we keep the check that it is not escaping, but if it is it can be? Not sure if strictly necessary but we'd err on the side of caution this way 🤔
Yeah the indiscriminate change to escaping won't fly because we break existing semantics, e.g.
So I'd look into if we're able to only be escaping when necessary due to the async-nature of the closure |
@ktoso welp, yeah, that's not an option. There's no reason this closure needs to be escaping except that IIUC we don't do any real escape analysis today for closures assigned to local bindings, we just assume they're always escaping. I don't think we'll want to apply this only to async defer bodies either, both because I think the model should stay consistent between the async/non-async case, and because I don't think the limitations like "can't capture mutating self" should apply to async defer bodies just because of weird implementation limitations... so seems like there's a couple paths:
Will think a bit... |
@ktoso Ah I commented from my work account by seems like you figured it out 😉 As a side note, if you have a gut sense about which of those avenues might be most fruitful I am all ears! |
I'm inclined to keep the body represented as a I think ideally we wouldn't actually create a function decl or a closure expression in the type checker for a defer body at all, and we'd just let the type checker look at the statements in the defer body and diagnose issues like missing Relatedly, we probably also need to be careful of the actor isolation for the closure / function that represents the async defer body to make sure that they always have the |
Thanks for taking a look @hborla!
Sounds good, it seems like eagerly looking ahead is probably the best route since it seems like there's a lot of downstream reliance on
Yeah, the number of times we have to explicitly check
Will add some tests for these cases to make sure they're handled correctly! |
@swift-ci please test |
@swift-ci please test |
1 similar comment
@swift-ci please test |
@swift-ci build toolchain |
@swift-ci please test macOS |
@swift-ci please test |
@swift-ci please build toolchain |
defer { await asyncFunc() } // expected-error {{'async' defer must appear within an 'async' context}} | ||
voidFunc() | ||
} | ||
|
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.
what behavior do we get if we have no source-level 'await
' in the defer?
func f() {
defer { async let _: () = voidFunc() }
voidFunc()
}
Tests etc tbc