We want SILGen to have a simplified view of its executor and know that whenever
one sees an Actor, it is an actual actor instead of a Builtin.Executor. This
just simplifies code. Also, we should eventually have an invariant that
Builtin.Executor should only be allowed in LoweredSIL after LowerHopToExecutor
has run. But that is a change for another day.
Specifically:
1. When we convert a function to nonisolated(nonsending), we need to
make sure that in the thunk we hop upon return since nonisolated(nonsending)
functions are assumed to preserve the caller's isolation.
2. When we convert a function from nonisolated(nonsending), we need to
make sure that in the thunk we hop onto the actor that we are passing in as the
isolated parameter of the nonisolated(nonsending) function. This ensures that
the nonisolated(nonsending) function can assume that it is already on its
isolated parameter's actor at function entry.
rdar://155905383
Currently only declarations would get `nonisolated(nonsending)`
inferred if the upcoming flag is enabled, this changes extend
this to apply to asynchronous nonisolated function types as well.
Resolves: rdar://154808850
This is a follow-up to https://github.com/swiftlang/swift/pull/82085
which made it so async variant doesn't get `@Sendable` inferred because
the proposal specified that inference should happen only on completion
handler parameter type of a synchronous variant of an imported API.
This runs into implementation issues related to thunking in some
cases were async convention expects the type of a completion handler
to match exactly for both variants of the imported API.
Resolves: rdar://154695053
We sometimes mangle SILFunctionTypes when generating debug info
for reabstraction thunks, and these can have various exotic
parameter and result attributes. Two recent additions were
never plumbed through the mangler, causing assertion failures
when emitting debug info.
Fixes rdar://153730847.
This lifts the check for the feature flag up into the `importParameterType`
from `importType` and means that completion handler type for `async` variant
is no longer gains `@Sendable` attribute.
Some notes:
1. In most cases, I think we were getting lucky with this by just inferring the
closure's isolation from its decl context. In the specific case that we were
looking at here, this was not true since we are returning from an @concurrent
async function a nonisolated(nonsending) method that closes over self. This
occurs since even when NonisolatedNonsendingByDefault we want to start importing
objc async functions as nonisolated(nonsending).
2. I also discovered that in the ActorIsolationChecker we were not visiting the
inner autoclosure meaning that we never set the ActorIsolation field on the
closure. After some discussion with @xedin about potentially visiting the
function in the ActorIsolationChecker, we came to the conclusion that this was
likely to result in source stability changes. So we put in a targeted fix just
for autoclosures in this specific case by setting their actor isolation in the
type checker.
3. Beyond adding tests to objc_async_from_swift to make sure that when
NonisolatedNonsendingByDefault is disabled we do the right thing, I noticed that
we did not have any tests that actually tested the behavior around
objc_async_from_swift when NonisolatedNonsendingByDefault is enabled. So I added
the relevant test lines so we can be sure that we get correct behavior in such a
case.
rdar://150209093
This is behavior pre-SE-0461 which is the safest option instead
of inferring `nonisolated(nonsending)` and changing the calling
convention and foregoing Sendable checking.
Type annotations for instruction operands are omitted, e.g.
```
%3 = struct $S(%1, %2)
```
Operand types are redundant anyway and were only used for sanity checking in the SIL parser.
But: operand types _are_ printed if the definition of the operand value was not printed yet.
This happens:
* if the block with the definition appears after the block where the operand's instruction is located
* if a block or instruction is printed in isolation, e.g. in a debugger
The old behavior can be restored with `-Xllvm -sil-print-types`.
This option is added to many existing test files which check for operand types in their check-lines.
Use the `%target-swift-5.1-abi-triple` substitution to compile the tests for
deployment to the minimum OS versions required for use of _Concurrency APIs,
instead of disabling availability checking.
Fix a leak when emitting the native to foreign thunk for an async
function which fulfills an Objective-C protocol requirement which can be
fulfilled with either a value or an error via a nullable completion.
Previously, the SIL in question used to look like this:
```sil
%maybe_completion = ...
try_apply %impl..., normal success, ...
success(%value):
switch_enum %maybe_completion...
case some!enumelt: invoke
case none!enumelt: ignore
ignore:
br join
invoke(%completion):
%some_value = enum Optional, some!enumelt, %value // consumes %value
%guaranteed_some_value = begin_borrow %some_value
%none_error = enum Optional, none!enumelt
apply %completion(%guaranteed_some_value, %none_error)
end_borrow %guaranteed_some_value
destroy_value %some_value
br join
join:
destroy_value %maybe_completion
...
```
which leaks %value on the codepath through `ignore`.
Note that `%value` is consumed by the `enum` instruction, but
`%completion` is invoked with `%guaranteed_some_value`, a guaranteed
value. So there is no need to consume %value in `invoke`.
Here, `%value` itself is borrowed and forwarded into an enum instruction
whose result is passed to `%completion`:
```sil
%maybe_completion = ...
try_apply %impl..., normal success, ...
success(%value):
switch_enum %maybe_completion...
case some!enumelt: invoke
case none!enumelt: ignore
ignore:
br join
invoke(%completion):
%guaranteed_value = begin_borrow %value
%guaranteed_some_value = enum Optional, some!enumelt, %guaranteed_value
%none_error = enum Optional, none!enumelt
apply %completion(%guaranteed_some_value, %none_error)
end_borrow %guaranteed_some_value
br join
join:
destroy_value %maybe_completion
destroy_value %value
...
```
Because an argument scope was already being created and a cleanup was
already being pushed for `%value`, nothing more is required to fix the
issue than to reorder the enum and the borrow.
rdar://119732084
Previously we gave them the same SIL linkage as the method, then changed
the LLVM IR linkage to 'internal' (which is roughly equivalent to
SIL 'private') in IRGen.
This would crash in the SIL verifier if an @objc method was
'@_alwaysEmitIntoClient'. While such a combination of attributes is
silly since '@objc' methods are intrinsically part of the ABI, we
should not crash in this case.
The simplest fix is to just set the linkage to private at the SIL
level, avoiding the IRGen hack entirely.
when two objc async functions are composed with each other,
i.e., f(g()), then the clean-ups for g() would get emitted
at an unexpected time, namely, during the suspension for
the call to f(). This means that using a clean-up to emit
the executor-hop breadcrumb was incorrect. The hop could
appear between a get_async continuation and its matching
await_continuation, which is an unsupported nested suspension.
This commit fixes that by removing the use of the breadcrumb
clean-up in favor of providing that breadcrumb directly to
the result plan, so that it may be emitted later on when the
result plan sees fit.
Fixes rdar://91502776
This reverts commit afd26d3974 to solve
a critical miscompile caused by a hop_to_executor appearing between
a get_continuation and await_continuation instruction in SIL.
A reimplementation of SR-15703 will be forthcoming.
Fixes rdar://91502776
Async functions are now expected to set ExpectedExecutor in their
prologue (and, generally, immediately hop to it). I updated the
prologue code for a bunch of function emission, most of which was
uninteresting. Top-level code was not returning to the main
executor, which is now fixed; fortunately, we weren't assuming
that we were on the main executor yet.
We had some code that only kicked in when an ExpectedExecutor
wasn't set which made us capture the current executor before
a hop and then return to it later. This code has been removed;
there's no situation in which save-and-return is the semantically
correct thing to do given the possibility of hop optimization.
I suspect it could also have led to crashes if the current
executor is being kept alive only because it's currently running
code. If we ever add async functions that are supposed to inherit
their caller's executor, we should have the caller pass the right
executor down to it.
This is the first half of SE-0338; the second, sendability
enforcement, is much more complicated, and Doug has volunteered
to do it.
Fixes rdar://79284465, as well as some tests that were XFAILed
on Windows.
An @objc async thunk does not need to perform a
hop_to_executor if the native method the thunk is
wrapping is async, since the native method will do
its own hop.
This redundant hop was causing a crash in SILGen
with async actor-instance isolated @objc methods
declared in an actor, because SILGen was not
prepared to add the hop to to `self` within
the @objc thunk.
resolves rdar://80130628
If a Swift async method is actor-constrained, then when it's projected into ObjC, it should still
run its task on the correct actor, along with the completion handler that ObjC passes into it.
Fixes rdar://76415650.
The `try await` ordering is both easier to read and indicates the order
of operations better, because the suspension point occurs first and
then one can observe a thrown error.
Bridging an async Swift method back to an ObjC completion-handler-based API requires
that the ObjC thunk spawn a task on which to execute the Swift async API and pass
its results back on to the completion handler.
So if we use an ObjC imported as async to conform to a Swift protocol, delegate a Swift subclass initializer, etc.,
we generate the conversion thunk to the Swift calling convention for the imported API.