`emitThrow` previously asserted that the only legal mismatch between the
in-flight error type and the throw destination was `any Error`, then
existential-erased to `Error`. Anything else Sema accepted — `do
throws(any P) where P: Error`, or a class subtype of the destination —
crashed the assertion (or, before that was tightened, miscompiled at
runtime).
Dispatch on the destination type:
- existential: erase, looking up conformance to each protocol in the
destination's existential layout (so `any P` works, not just `any
Error`);
- class: emit an `upcast`.
Anything else still hits an `unreachable` — Sema rejects those today, so
hitting it would indicate a Sema regression rather than a missing SILGen
path.
Fixes https://github.com/swiftlang/swift/issues/83826.
There's an argument that it would generally be better to do this for all
defers, but I really don't want to open that can of worms in this commit.
I'm just trying to make it possible to build specific SIL relationships
using decomposed builtins, and this hack works for that without changing
the normal code-generation pattern, since only a restricted set of code
should be calling builtins in the first place.
Allowing these SIL relationships to be built directly by (careful) Swift
code makes it much easier to model these things, since otherwise we need
a separate higher-order builtin that SILGen lowers.
Tested in a follow-up commit.
We can lock down the condition further if we have source compatibility
problems from people using -enable-builtin-module casually.
Fixes an emission issue with typed throws where the error
path after a `try_apply` is missing a `destroy_value`
on the unused basic block argument holding the error.
Forgetting to emit that makes the SIL verifier upset.
rdar://170353819
This builtin only needs to be supported as the return of a borrow accessor,
so special case its handling as part of a storage expression in SILGenLValue.
We were creating the JumpDests too early, so lowering a 'break' or 'continue'
statement would perform cleanups that were recorded while evaluating the
pack expansion expression, which would cause SIL verifier errors and
runtime crashes.
- Fixes https://github.com/swiftlang/swift/issues/78598
- Fixes rdar://131847933
Borrow accessor result can sometimes be generated from a local borrow and
returning the result produced within the local borrow scope will cause ownership errors.
We have to introduce new SIL semantics to make this possible.
Until then use a pair of unchecked_ownership_conversion instructions to silence the ownership errors.
We encounter this when we have:
Address-only self and @guaranteed result
Loadable self and @guaranteed result derived from an unsafe pointer stored property
This change also updates the result convention of borrow accessors with loadable result types
and indirect self argument type.
Introduce copy_value + mark_unresolved_non_copyable_value + begin_borrow at the return value of
borrow accessor apply to drive move-only diagnostics.
Also strip the copy_value + mark_unresolved_non_copyable_value + begin_borrow trio in a few places, since
they create an artificial scope out of which we cannot return values in a borrow accessor
without resorting to unsafe SIL operations currently.
Borrow accessor diagnostics allow stripping these instructions safely in the following places:
- return value of a borrow accessor
- self argument reference in the borrow accessor return expression and borrow accessor apply
We were not able to use an existential as the base
of an access that strictly borrows the existential,
because SILGen's RValue emission would establish
a fresh evaluation scope just for the existential's
opening, and then copy the opened value out.
This is problematic for noncopyable existentials.
So this patch moves & adds FormalEvaluationScope's
around so they're broad enough to enable a
borrow of an existential. The idea behind this
refactoring is to establish top-level
FormalEvaluationScopes when initially creating
RValue's for Expr's in SILGen. Any more-tightly
scoped operations will already establish their own
nested scope, so this is mostly adding safe-guards.
I've limited the existentials fix to noncopyables
for now.
part of rdar://159079818
Although I don't plan to bring over new assertions wholesale
into the current qualification branch, it's entirely possible
that various minor changes in main will use the new assertions;
having this basic support in the release branch will simplify that.
(This is why I'm adding the includes as a separate pass from
rewriting the individual assertions)
There's an unfortunate layering difference in the cleanup order between address-only
and loadable error values during `catch` pattern matching: for address-only values,
the value is copied into a temporary stack slot, and the stack slot is cleaned up
on exit from the pattern match, meaning the value must be moved into the error return
slot on the "no catch" case before cleanups run. But if it's a loadable value, then
we borrow it for the duration of the switch, and the borrow is released during cleanup
on exit from the pattern match, so the value must be forwarded after running cleanups.
The way the code is structured, it handles these cases properly when the convention of
the function being emitted is in sync with the fundamental properties of the error type
(when the error type is loadable and the error return is by value, or when the error
type is address-only and the error return is indirect, in other words). But when
a closure literal with a loadable error type is emitted in an argument context that
expects a function with an indirect error return, we would try to forward the loadable
error value into the error return slot while a borrow is still active on it, leading
to verifier errors. Defer forwarding the value into memory until after cleanups are
popped, fixing rdar://126576356.
A tidier solution might be to always emit the function body to use a bbarg on the
throw block to pass the error value from the body emission to the epilog when the
type is loadable, deferring the move into memory to the epilog block. This would
make the right behavior fall out of the existing implementation, but would require
a bit more invasive changes (pretty much everywhere that checks IndirectErrorReturn
would need to check a different-tracked AddressOnlyErrorType bit instead or in
addition). This change is more localized.