Right now it is basically a version of nonisolated beyond a few simple cases
like constructors/destructors where we are pretty sure we want to not support
this.
This is part of my bringup strategy for changing nonisolated/unspecified to be
caller isolation inheriting.
This requires two major changes.
The first is that we need to teach SILGen that the isolation of an initializer
is essentially dynamic (as far as SILGen is concerned) --- that it needs to emit
code in order to get the isolation reference. To make this work, I needed to
refactor how we store the expected executor of a function so that it's not
always a constant value; instead, we'll need to emit code that DI will lower
properly. Fortunately, I can largely build on top of the work that Doug previously
did to support #isolation in these functions. The SIL we emit here around delegating
initializer calls is not ideal --- the breadcrumb hop ends up jumping to the
generic executor, and then DI actually emits the hop to the actor. This is a little
silly, but it's hard to eliminate without special-casing the self-rebinding, which
honestly we should consider rather than the weirdly global handling of that in
SILGen today. The optimizer should eliminate this hop pretty reliably, at least.
The second is that we need to teach DI to handle the pattern of code we get in
delegating initializers, where the builtin actually has to be passed the self var
rather than a class reference. This is because we don't *have* a class reference
that's consistently correct in these cases. This ended up being a fairly
straightforward generalization.
I also taught the hop_to_executor optimizer to skip over the initialization of
the default-actor header; there are a lot of simple cases where we still do emit
the prologue generic-executor hop, but at least the most trivial case is handled.
To do this better, we'd need to teach this bit of the optimizer that the properties
of self can be stored to in an initializer prior to the object having escaped, and
we don't have that information easily at hand, I think.
Fixes rdar://87485045.
Distributed actors can be treated as actors by accessing the `asLocalActor`
property. When lowering `#isolation` in a distributed actor initializer,
use a separate builtin `flowSensitiveDistributedSelfIsolation` to
capture the conformance to `DistributedActor`, and have Definite
Initialization introduce the call to the `asLocalActor` getter when
needed.
Actor initializers have a flow-sensitive property where they are isolated
to the actor being initialized only after the actor instance itself is
fully-initialized. However, this behavior was not being reflected in
the expansion of `#isolation`, which was always expanding to `self`,
even before `self` is fully formed.
This led to a source compatibility issue with code that used the async
for..in loop within an actor initializer *prior* to the point where the
actor was fully initialized, because the type checker is introducing
the `#isolation` (SE-0421) but Definite Initialization properly rejects
the use of `self` before it is initialized.
Address this issue by delaying the expansion of `#isolation` until
after the actor is fully initialized. In SILGen, we introduce a new
builtin for this case (and *just* this case) called
`flowSensitiveSelfIsolation`, which takes in `self` as its argument
and produces an `(any Actor)?`. Definite initialization does not treat
this as a use of `self`. Rather, it tracks these builtins and
replaces them either with `self` (if it is fully-initialized at this
point) or `nil` (if it is not fully-initialized at this point),
mirroring the flow-sensitive isolation semantics described in SE-0327.
Fixes rdar://127080037.
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)
When deallocating a partially allocated class in the trapping branch of
an unfailable cast, cast back down to the subclass which is being
partially deallocated before emitting the dealloc partial ref
instruction.
I also included changes to the rest of the SIL optimizer pipeline to ensure that
the part of the optimizer pipeline before we lower tuple_addr_constructor (which
is right after we run TransferNonSendable) work as before.
The reason why I am doing this is that this ensures that diagnostic passes can
tell the difference in between:
```
x = (a, b, c)
```
and
```
x.0 = a
x.1 = b
x.2 = c
```
This is important for things like TransferNonSendable where assigning over the
entire tuple element is treated differently from if one were to initialize it in
pieces using projections.
rdar://117880194
For cases where init accessor field has a nonmutating set we need
ignore copies and borrows associated with load of "self" because
they are going to be erased together with the setter application
by DI.
It lowers let property accesses of classes.
Lowering consists of two tasks:
* In class initializers, insert `end_init_let_ref` instructions at places where all let-fields are initialized.
This strictly separates the life-range of the class into a region where let fields are still written during
initialization and a region where let fields are truly immutable.
* Add the `[immutable]` flag to all `ref_element_addr` instructions (for let-fields) which are in the "immutable"
region. This includes the region after an inserted `end_init_let_ref` in an class initializer, but also all
let-field accesses in other functions than the initializer and the destructor.
This pass should run after DefiniteInitialization but before RawSILInstLowering (because it relies on `mark_uninitialized` still present in the class initializer).
Note that it's not mandatory to run this pass. If it doesn't run, SIL is still correct.
Simplified example (after lowering):
bb0(%0 : @owned C): // = self of the class initializer
%1 = mark_uninitialized %0
%2 = ref_element_addr %1, #C.l // a let-field
store %init_value to %2
%3 = end_init_let_ref %1 // inserted by lowering
%4 = ref_element_addr [immutable] %3, #C.l // set to immutable by lowering
%5 = load %4
I was originally hoping to reuse mark_must_check for multiple types of checkers.
In practice, this is not what happened... so giving it a name specifically to do
with non copyable types makes more sense and makes the code clearer.
Just a pure rename.
`nonmutating set` needs to load of "self" but that load could be
ignored safely because it's only viable when self is determined
to be fully initialized by DI. The same applies to `assign_by_wrapper`.
This is a futile attempt to discourage future use of getType() by
giving it a "scary" name.
We want people to use getInterfaceType() like with the other decl kinds.
Reformatting everything now that we have `llvm` namespaces. I've
separated this from the main commit to help manage merge-conflicts and
for making it a bit easier to read the mega-patch.
This is phase-1 of switching from llvm::Optional to std::optional in the
next rebranch. llvm::Optional was removed from upstream LLVM, so we need
to migrate off rather soon. On Darwin, std::optional, and llvm::Optional
have the same layout, so we don't need to be as concerned about ABI
beyond the name mangling. `llvm::Optional` is only returned from one
function in
```
getStandardTypeSubst(StringRef TypeName,
bool allowConcurrencyManglings);
```
It's the return value, so it should not impact the mangling of the
function, and the layout is the same as `std::optional`, so it should be
mostly okay. This function doesn't appear to have users, and the ABI was
already broken 2 years ago for concurrency and no one seemed to notice
so this should be "okay".
I'm doing the migration incrementally so that folks working on main can
cherry-pick back to the release/5.9 branch. Once 5.9 is done and locked
away, then we can go through and finish the replacement. Since `None`
and `Optional` show up in contexts where they are not `llvm::None` and
`llvm::Optional`, I'm preparing the work now by going through and
removing the namespace unwrapping and making the `llvm` namespace
explicit. This should make it fairly mechanical to go through and
replace llvm::Optional with std::optional, and llvm::None with
std::nullopt. It's also a change that can be brought onto the
release/5.9 with minimal impact. This should be an NFC change.