Example: consume(x.field). This turned out to be a pretty simple extension of
the underlying model. The cases we are interested in are caused by a
non-reference nominal type having an extracted field being passed to a consuming
use. This always requires a copy.
The reason I missed this was I originally wrote the test cases around this for
classes which do not have this problem since the class is move only, not the
field due to class being a reference type. I then cargo culted this test case
for struct/other types and did not notice that we should have started to error
on these tests.
On an interesting note, I caught this on my branch where I am preparing the
optimizer to allow for values to have a move only bit. One of the constraints is
that once we are in guaranteed SIL, copy_value can not be used on any moveOnly
type (e.x.: $@moveOnly T). To ensure this doesn't happen, the move only checker:
1. Uses copy propagation to rewrite the copies of the base owned value.
2. Emit a diagnostic error upon any copies we found were needed due to the owned
value being consumed.
3. If a diagnostic was emitted, rewrite all copies of move only typed values to
be explicit_copy_value to ensure that in canonical SIL we do not violate the
invariant that copy_value can not be applied to move only type values. This
is ok to do since we are going to error and just want to avoid breaking the
invariant.
The end effect of this is that if we do not emit any diagnostic, any copy_value
on a move only typed value that is not eliminated by the checker hits an assert
in the verifier... allowing us to know that if a program successfully compiles
that all "move only" proofs successfully ran, guaranteeing safety!
SemanticARCOptVisitor::performGuaranteedCopyValueOptimization was
converting this SIL
%borrow = begin_borrow %copiedValue
%copy = copy_value %borrow
%borrowCopy = begin_borrow %copy
end_borrow %borrow
end_borrow %borrowCopy
destroy_value %copy
// something something
unreachable
into
%borrow = begin_borrow %copiedValue
%innerBorrow = begin_borrow %borrow
end_borrow %borrow
end_borrow %innerBorrow
// something something
unreachable
Dead-end blocks are simply irrelevant for this
optimization. Unfortunately, there were multiple layers of attempted
workarounds that were hiding the real problem, except in rare cases.
Thanks Nate Chandler for reducing the test.
This is just an initial prototype for people to play with. It is as always
behind the -enable-experimental-move-only flag.
NOTE: In this PR I implemented this only for 'local let' like things (local
lets/params). I did not implement in this PR support for local var and haven't
done anything with class ivars or globals.
rdar://83957028
Straighforward API to build pruned liveness from a single SSA value.
This 3-line function allows PrunedLiveness to be used anywhere
ValueLifetimeAnalysis was used in OSSA form. Aside from simplicity and
efficiency, the difference is that this composes with other utilities
that only care about PrunedLiveBlocks, PrunedLiveness, or
PrunesLivenessBounary independent of how those data structures were generated.
Simple wrapper to compute the boundary of PrunedLiveness.
Pervasively useful utility for working with OSSA reference lifetimes
and borrow scopes.
This can also replace the implementation in
CanonicalOSSALifetime. This will greatly simplify that utility's
logic, preparing it to handle diagnostics (like move-only SILValue
checking) and other features.