For the following discussion, let OP be the switch's operand. This is
implemented by:
* Modeling switch_enum_addr as not forwarding OP and instead delegate the
forwarding of OP to be done by each enum case. This matches what SILGen is
actually doing since the actual taking of the address in SIL is done in each
enum case block by an unchecked_take_enum_data_addr.
* In each enum case, I treat OP as being forwarded into an irrefutable
sub-tree. I follow the pattern of other places this is done by creating a
CleanupStateRestorationScope and using forwardIntoIrrefutableSubTree. This
ensures that if I forward OP in the enum case, it just becomes dormant instead
of being thrown away.
* Inside each case, there is a bunch of code that does some final preparations
to src before dispatching to the inner dispatch. This code was written using
old low-level SILValue APIs where ownership is done by hand. I replaced all of
that by hand ownership with higher level Managed Value APIs that automatically
handle ownership for the user. This simplified the implementation and ensured
correctness via SILGenBuilder API invariants.
The end result of all of these together is that:
1. The cleanup on OP is still live when we emit the default case later than the
cleanups. This eliminates the leak that I am fixing.
2. We have greater correctness since the SILGenBuilder APIs automatically handle
ownership for us.
3. We have eliminated some brittle logic that could in the future introduce
bugs. Specifically, I noticed that if we were ever given a
ConsumableManagedValue that was CopyOnSuccess or BorrowAlways but its
ManagedValue was a +1 value, we would leak. I could not figure out how to
create a Swift test case that would go down this code path though = (. But
that being said, it is one new language feature away from being broken. I
added some asserts to ConsumableManagedValue that ensures this invariant, so
we are safe. If it is inconvenient, we can also cause ConsumableManagedValue
to create a new ManagedValue when it detects this condition without the
cleanup. But lets see how difficult it is to keep this invariant.
In terms of testing, I put in both a SILGen test and also an end<->end
interpreter test to ensure this doesn't break again.
rdar://71992652
SR-13926
Since we are working with values here, it can be confusing when reading the code
that elt is an EnumElementDecl. The rename to eltDecl will make it much clearer.
It would be more abstractly correct if this got DI support so
that we destroy the member if the constructor terminates
abnormally, but we can get to that later.
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.
implicitly-async calls are calls to synchronous
actor-isolated functions. Synchronous functions
cannot perform hop_to_executor, so implicitly
async calls have the convention that the caller
is responsible for switching to the right
executor prior to entering the actor-isolated
callee.
It follows naturally that the caller must then
switch back to the appropriate executor after
the implicitly-async call completed.
Now, if the caller is not isolated to a
_specific_ actor, then we are (currently)
_not_ emitting a hop to go back to the
caller's executor, because that caller's
executor is unspecified (and currently not
accessable in SIL). This behavior may change
in the future; tracked in rdar://71905765
of adding a property.
This better matches what the actual implementation expects,
and it avoids some possibilities of weird mismatches. However,
it also requires special-case initialization, destruction, and
dynamic-layout support, none of which I've added yet.
In order to get NSObject default actor subclasses to use Swift
refcounting (and thus avoid the need for the default actor runtime
to generally use ObjC refcounting), I've had to introduce a
SwiftNativeNSObject which we substitute as the superclass when
inheriting directly from NSObject. This is something we could
do in all NSObject subclasses; for now, I'm just doing it in
actors, although it's all actors and not just default actors.
We are not yet taking advantage of our special knowledge of this
class anywhere except the reference-counting code.
I went around in circles exploring a number of alternatives for
doing this; at one point I basically had a completely parallel
"ForImplementation" superclass query. That proved to be a lot
of added complexity and created more problems than it solved.
We also don't *really* get any benefit from this subclassing
because there still wouldn't be a consistent superclass for all
actors. So instead it's very ad-hoc.
An asyncHandler function is split into two functions:
1. The asyncHandler body function: it contains the body of the function, but is emitted as an async function.
2. The original function: it just contains
_runAsyncHandler(operation: asyncHandlerBodyFunction)
rdar://problem/71247879
It fixes a crash in SILGen if Builtin.createAsyncTaskFuture is used in a non-generic context.
I found this by experiment - we don't use it currently in the stdlib. But it doesn't harm to fix this.
In derivatives of loops, no longer allocate boxes for indirect case payloads. Instead, use a custom pullback context in the runtime which contains a bump-pointer allocator.
When a function contains a differentiated loop, the closure context is a `Builtin.NativeObject`, which contains a `swift::AutoDiffLinearMapContext` and a tail-allocated top-level linear map struct (which represents the linear map struct that was previously directly partial-applied into the pullback). In branching trace enums, the payloads of previously indirect cases will be allocated by `swift::AutoDiffLinearMapContext::allocate` and stored as a `Builtin.RawPointer`.
Implement SIL generation for "async let" constructs, which involves:
1. Creating a child task future at the point of declaration of the "async let",
which runs the initializer in an async closure.
2. Entering a cleanup to destroy the child task.
3. Entering a cleanup to cancel the child task.
4. Waiting for the child task when any of the variables is reference.
5. Decomposing the result of the child task to write the results into the
appropriate variables.
Implements rdar://71123479.
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.
@asyncHandler is currently unimplemented in SILGen, and will cause
SIL verifier assertions if used. Rather than trigger assertions, emit
a trap for the body. Obviously, this is a temporary hack.
Otherwise, if we move in a formal access cleanup value into switch emission
using a cleanup cloner, we will improperly have a formal access cleanup that
will be invalidated by a normal Scope which violates SILGen invariants. Also in
a certain sense the bindings performed by SILGenPattern that do this type of
forwarding are arguments in a certain sense so it seems reasonable to do this.
<rdar://problem/70736924>
This makes it easier to understand conceptually why a ValueOwnershipKind with
Any ownership is invalid and also allowed me to explicitly document the lattice
that relates ownership constraints/value ownership kinds.
Adds a new frontend option
"-experimental-allow-module-with-compiler-errors". If any compilation
errors occur while generating the .swiftmodule, this mode will skip SIL
entirely and only serialize the (likey invalid) AST.
This existence of this option during generation is serialized into the
resulting .swiftmodule. Errors found in deserialization are only allowed
if it is set.
Primarily intended for IDE requests (eg. indexing and code completion)
to ensure robust cross-module results, despite possible errors.
Resolves rdar://69815975
`Builtin.createAsyncTask` takes flags, an optional parent task, and an
async/throwing function to execute, and passes it along to the
`swift_task_create_f` entry point to create a new (potentially child)
task, returning the new task and its initial context.
Adds a new flag "-experimental-skip-all-function-bodies" that skips
typechecking and SIL generation for all function bodies (where
possible).
`didSet` functions are still typechecked and have SIL generated as their
body is checked for the `oldValue` parameter, but are not serialized.
Parsing will generally be skipped as well, but this isn't necessarily
the case since other flags (eg. "-verify-syntax-tree") may force delayed
parsing off.
Implement a new builtin, `cancelAsyncTask()`, to cancel the given
asynchronous task. This lowers down to a call into the runtime
operation `swift_task_cancel()`.
Use this builtin to implement Task.Handle.cancel().
Rather than produce an "unowned" result from `getCurrentAsyncTask()`,
take advantage of the fact that the task is effectively guaranteed in
the scope. Do so be returning it as "unowned", and push an
end_lifetime cleanup to end the lifetime. This eliminates unnecessary
ref-count traffic as well as introducing another use of unowned.
Approach is thanks to Michael Gottesman, bugs are mine.
This introduces a new builtin, `getCurrentAsyncTask()`, that produces a
reference to the current task. This builtin can only be used within
`async` functions, and IR generation merely grabs the task argument
and packages it up.
The type of this function is `() -> Builtin.NativeObject`, because we
don't currently have a Swift-level representation of tasks, and can
probably handle everything through builtins or runtime calls.
To manage code size in user binaries, we want to be able to implement common completion handler signatures in
the Swift runtime once. Using a different mangling for these lets us add new ones without clobbering symbols in
existing binaries.