You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[silgenpattern] When emitting address only enum element dispatch, do not leak the switch's operand along the default path.
For the following discussion, let OP be the switch's operand. This is
implemented by:
* Modeling switch_enum_addr as not forwarding OP and instead delegate the
forwarding of OP to be done by each enum case. This matches what SILGen is
actually doing since the actual taking of the address in SIL is done in each
enum case block by an unchecked_take_enum_data_addr.
* In each enum case, I treat OP as being forwarded into an irrefutable
sub-tree. I follow the pattern of other places this is done by creating a
CleanupStateRestorationScope and using forwardIntoIrrefutableSubTree. This
ensures that if I forward OP in the enum case, it just becomes dormant instead
of being thrown away.
* Inside each case, there is a bunch of code that does some final preparations
to src before dispatching to the inner dispatch. This code was written using
old low-level SILValue APIs where ownership is done by hand. I replaced all of
that by hand ownership with higher level Managed Value APIs that automatically
handle ownership for the user. This simplified the implementation and ensured
correctness via SILGenBuilder API invariants.
The end result of all of these together is that:
1. The cleanup on OP is still live when we emit the default case later than the
cleanups. This eliminates the leak that I am fixing.
2. We have greater correctness since the SILGenBuilder APIs automatically handle
ownership for us.
3. We have eliminated some brittle logic that could in the future introduce
bugs. Specifically, I noticed that if we were ever given a
ConsumableManagedValue that was CopyOnSuccess or BorrowAlways but its
ManagedValue was a +1 value, we would leak. I could not figure out how to
create a Swift test case that would go down this code path though = (. But
that being said, it is one new language feature away from being broken. I
added some asserts to ConsumableManagedValue that ensures this invariant, so
we are safe. If it is inconvenient, we can also cause ConsumableManagedValue
to create a new ManagedValue when it detects this condition without the
cleanup. But lets see how difficult it is to keep this invariant.
In terms of testing, I put in both a SILGen test and also an end<->end
interpreter test to ensure this doesn't break again.
rdar://71992652
SR-13926
0 commit comments