Previously, region-based isolation was not diagnosing cases where a non-Sendable
value projected from a Sendable base that was MainActor isolated. For example:
```swift
@MainActor
struct MainActorBox {
var value: NonSendableKlass?
mutating func take() -> sending NonSendableKlass {
if let value {
self.value = nil
return value // Should warn: main actor-isolated value cannot be 'sending'
} else {
preconditionFailure("Consumed twice")
}
}
}
```
This was caused by two issues:
1. A logic bug in `AddressBaseComputingVisitor::visitAll` where we overwrote an
already-found Sendable value when recursing. The visitor should only record
the first Sendable value found, not subsequent ones. This caused us to
incorrectly return the `@MainActor` `MainActorBox` as the base instead of
emitting a require for the projected value.
2. struct_element_addr being transparent unconditionally causing us to not even
emit a require at all.
This commit has two parts:
1. I fixed the first two issues by fixing the logic bug and by making
struct_element_addr only transparent if its operand and result are
non-Sendable. I added logic that we have used in other such cases to handle
non-Sendable operand/Sendable result as well as Sendable operand and
non-Sendable result. I then added the same support for tuple_element_addr and
unchecked_take_enum_data_addr since they suffered from a similar problem.
2. Adding the logic to handle variants where the operand was non-Sendable and
the result was Sendable, caused a bunch of tests to break so I had to fix
that.
To fix the broken test cases, I had to make the compiler smarter about how it
was inserting this require. To do this:
1. I checked if the access base of the projection was actually immutable or if
we are an alloc_stack that there was a prefix path in the projection path of
immutable projections that end in the alloc_stack. In such a case, we know
that we do not need to require that the operand is available in the current
isolation domain since we will never write to that piece of memory and once
we have loaded the result from memory, we know that the value is Sendable so
nothing bad can happen.
2. If the access base of the projection was mutable, I used the same logic that
we used for alloc_box that were non-Sendable that stored a Sendable thing by
changing operand to be a `require [mutable_base_of_sending]` on the result of
the projection instead of requiring the projection's operand. This ensures
that we handled important flow sensitive cases.
rdar://169626088
Fixes a false "used before being initialized" error when using self-mutating functions inside conditional operators (`&&`, `||`) in initializers.
This is a follow-up on https://github.com/swiftlang/swift/pull/35276.
rdar://168784050
For stores to unaliased, non-aggregate-projected destinations, switch from
translateSILAssignDirect(destValue, src) to translateSILAssignIndirect(dest, src).
This passes the Operand* rather than just the SILValue, enabling proper tracking
of the destination address in region analysis.
First in a series migrating instruction handlers to indirect assignment.
NOTE: I originally thought that pack_element_set would also have this property,
but alloc_pack today is always assumed to be to be MayAlias and thus we emit a
merge. I think this is fine since one can never actually assign over a pack
variable like one can other things (that is I think they are generally just used
for marshalling and the like). If I am wrong, I can just fix it later.
rdar://156024613
https://github.com/swiftlang/swift/issues/83121
Explicitly pass an empty indirect results array to translateSILMultiAssign when
translating partial_apply instructions, since partial_apply does not produce
indirect results. This transitions the call site from the backward-compatible
overload to the full signature introduced in bc8eadad12f.
Add two template overloads of translateSILAssignIndirect that wrap
translateSILMultiAssign for the common case of assigning to a single
indirect parameter destination:
1. A generic template accepting any collection of source operands
2. A specialization for a single source operand
These simplify call sites that only need to handle one destination.
Add a new IndirectResultsRangeTy template parameter and indirectResultAddresses
parameter to translateSILMultiAssign, allowing it to process both direct result
values and indirect result operands.
I also added overload that takes only direct results for backward
compatibility. I am going to get rid of this eventually once we have finished
the transition.
It previously just accepted ArrayRef<TrackableValue>. I am going to be using
this with a transform + concat range in a subsequent change to pass a combined
ArrayRef<TrackableValue> + ArrayRef<std::pair<Operand, TrackableValue>>.second.
Make sure that debug locations are correct so that instruction which are inserted by DI after a no-return call doesn't cause DiagnoseUnreachable to print a wrong warning.
Also, add `extend_lifetime` to the list of excluded instructions in DiagnoseUnreachable.
Computing dominance relation between instructions in the same block was done with linear search, e.g. when checking if a value-use is before its lifetime ending instruction.
This resulted in quadratic complexity and very long compile times for very large basic blocks.
Now we do the dominance check with pre-computed instruction indices, which is O(0) instead of O(n).
https://github.com/swiftlang/swift/issues/86663
rdar://168511262
The OwnershipRAUW utility is called by CSE, SILCombine, etc. whenever OSSA
values are substituted or combined. It handles ownership corner cases by
creating new copies. Destroys need to be insert for those new copies after all
original uses. It is impossible to do that when phis are involved. The utility
already checks for phis involving owned or guaranteed values, but unowned phis
were not anticipated.
Fixes rdar://168620481 swift-frontend crash: While running pass
SILFunctionTransform "GenericSpecializer"
With this option an MD5 hash of the SIL is printed after each function or module pass - if the pass has changed the SIL.
This is useful for finding non-determinisms in the optimizer.
To catch those errors also in a non-assert build of the compiler.
I recently investigated a compiler crash with a non-assert compiler and would have found the root cause much faster if those asserts would have triggered.