Previously, `end_borrow`s were rewritten last in order to be able to
find them when inserting `end_borrow`s on behalf of newly created
`load_borrow`s. Generalize this to rewriting all lifetime-ending users
last. This is necessary for the lifetime utilities used by `isLoadCopy`
to remain accurate when rewriting a `copy_value` previously determined
to be from a load-copy pair.
We can't really treat them as always-initialized because that makes move checking
think that there's a value to destroy even on initialization, causing deinits to
run on uninitialized memory. Remove my previous hack, and use a `zeroInitializer`
to initialize the value state when emitting `init`, which is where we really need
the bootstrapping-into-initialized behavior. rdar://113057256
We want these to be borrowed in most cases and to create an appropriate onion
wrapping. Since we are doing this in more cases now, we fix a bunch of cases
where we used to be forced to insert a copy since a coroutine or access would
end too early.
Projections may involve opened archetypes. They must be dominated by
the instructions which open those archetypes.
When determining the earliest storage point for a value and the point at
which to insert its projections, look for such obstructions in the
projection chain. The first one found is the earliest storage point.
The pass rewrites opaque values in reverse post order of their
definitions. When an opaque value is rewritten, both its definition and
its uses are rewritten. When a def or a use is rewritten, in order to
materialize an address with which to rewrite, projection instructions
are created. Previously, these projections were created at the site of
the def or use. Some of these projection instructions may be reused when
rewriting later opaque values.
As a result, it's possible to have two opaque values `A` and `B` (which
are rewritten in that order) such that rewriting a use of `A` which
occurs "after" the def (or a use) of `B` creates a projection `P` which
is then used by that "earlier" def (or use) of `B`:
```
A =
B = // relies on P
use B
use A // creates some projection P
```
When rewriting the def (or that use, respectively) of `B`, the
projection which was created for the use of `A` will be reused. And
previously, the projection would be created at the use of A. But that
projection instruction was "after" the place where it is used when
rewriting the def or use of `B`. That's invalid!
To address this, rather than creating projections at the instruction
being rewritten, instead create them "as early as possible". The
locations where they will be created are chosen so as to dominate all
uses.
Turns out if you write `_ = consume s` you get different enough
SIL than `_ = s` that it fools the ad-hoc test for whether we've
mark-must-check'd a closure capture, since the former will have
a begin_access. There doesn't appear to be a simple way to reuse
the existing information or checking routine for this that was
used by the checker itself to flag this.
`std::optional` doesn't have `hasValue` or `getPointer`. Using the
implicit decay to boolean, and grabbing the pointer to the element by
expanding the optional and grabbing the reference address.
llvm::SmallSetVector changed semantics
(https://reviews.llvm.org/D152497) resulting in build failures in Swift.
The old semantics allowed usage of types that did not have an
`operator==` because `SmallDenseSet` uses `DenseSetInfo<T>::isEqual` to
determine equality. The new implementation switched to using
`std::find`, which internally uses `operator==`. This type is used
pretty frequently with `swift::Type`, which intentionally deletes
`operator==` as it is not the canonical type and therefore cannot be
compared in normal circumstances.
This patch adds a new type-alias to the Swift namespace that provides
the old semantic behavior for `SmallSetVector`. I've also gone through
and replaced usages of `llvm::SmallSetVector` with the
`Swift::SmallSetVector` in places where we're storing a type that
doesn't implement or explicitly deletes `operator==`. The changes to
`llvm::SmallSetVector` should improve compile-time performance, so I
left the `llvm::SmallSetVector` where possible.
Moved the assert that the value which opens an archetype exists before
the call to get its defining instruction which would be an NPE if the
value were in fact null.
Promoted the functionality from createStackAllocation to a named member
AddressLoweringState::getLatestOpeningInst so that it can be employed
elsewhere.
This attribute can be attached to a noncopyable struct to specify that its
storage is raw, meaning the type definition is (with some limitations)
able to do as it pleases with the storage. This provides a basis for
implementing types for things like atomics, locks, and data structures
that use inline storage to store conditionally-initialized values.
The example in `test/Prototypes/UnfairLock.swift` demonstrates the use
of a raw layout type to wrap Darwin's `os_unfair_lock` APIs, allowing
a lock value to be stored inside of classes or other types without
needing a separate allocation, and using the borrow model to enforce
safe access to lock-guarded storage.
When determining whether a copy_value is part of a copy->store pair, if
the value being copied is guaranteed, it is checked whether the store is
within the lifetime of all its guaranteed roots. If one of those
guaranteed roots is a function argument, the store is certainly within
the lifetime, so exit early.
An extract may have users that have escaping operand ownership.
Consequently, we can't rely on findInnerTransitiveGuaranteedUses as used
by emitEndBorrows.
A destructure may have users that have escaping operand ownership.
Consequently, we can't rely on findInnerTransitiveGuaranteedUses as used
by emitEndBorrows.
Originally, we were relying on capture info to determine if we needed to insert
this mark_must_check. This ignored that the way that we are handling
escaping/non-escaping is something that is approximated in the AST but actually
determined at SIL level. With that in mind, rather than relying on the capture
info here, just rely on us having an inout argument. The later SIL level
checking for inout escapes is able to handle mark_must_check and knows how to
turn off noncopyable errors in the closure where we detect the error to prevent
us from emitting further errors due to the mark_must_check here.
I discovered this while playing with the previous commit.
rdar://112555589
When a `load_borrow` is created on behalf of a `begin_apply`, it's
possible that it may have escaping users. When it does,
`findInnerTransitiveGuaranteedUses` returns `false` and the uses it
finds are incomplete. As a result the boundary computed in
`emitEndBorrows` will be incomplete.