My goal was to reduce the size of SILLocation. It now contains only of a storage union, which is basically a pointer and a bitfield containing the Kind, StorageKind and flags. By far, most locations are only single pointers to an AST node. For the few cases where more data needs to be stored, this data is allocated separately: with the SILModule's bump pointer allocator.
While working on this, I couldn't resist to do a major refactoring to simplify the code:
* removed unused stuff
* The term "DebugLoc" was used for 3 completely different things:
- for `struct SILLocation::DebugLoc` -> renamed it to `FilePosition`
- for `hasDebugLoc()`/`getDebugSourceLoc()` -> renamed it to `hasASTNodeForDebugging()`/`getSourceLocForDebugging()`
- for `class SILDebugLocation` -> kept it as it is (though, `SILScopedLocation` would be a better name, IMO)
* made SILLocation more "functional", i.e. replaced some setters with corresponding constructors
* replaced the hand-written bitfield `KindData` with C bitfields
* updated and improved comments
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
Since we are working with values here, it can be confusing when reading the code
that elt is an EnumElementDecl. The rename to eltDecl will make it much clearer.
This makes it easier to understand conceptually why a ValueOwnershipKind with
Any ownership is invalid and also allowed me to explicitly document the lattice
that relates ownership constraints/value ownership kinds.
VarPattern is today used to implement both 'let' and 'var' pattern bindings, so
today is already misleading. The reason why the name Var was chosen was done b/c
it is meant to represent a pattern that performs 'variable binding'. Given that
I am going to add a new 'inout' pattern binding to this, it makes sense to
give it now a better fitting name before I make things more confusing.
* [SILGenFunction] Don't create redundant nested debug scopes
Instead of emitting:
```
sil_scope 4 { loc "main.swift":6:19 parent 3 }
sil_scope 5 { loc "main.swift":7:3 parent 4 }
sil_scope 6 { loc "main.swift":7:3 parent 5 }
sil_scope 7 { loc "main.swift":7:3 parent 5 }
sil_scope 8 { loc "main.swift":9:5 parent 4 }
```
Emit:
```
sil_scope 4 { loc "main.swift":6:19 parent 3 }
sil_scope 5 { loc "main.swift":7:3 parent 4 }
sil_scope 6 { loc "main.swift":9:5 parent 5 }
```
* [IRGenSIL] Diagnose conflicting shadow copies
If we attempt to store a value with the wrong type into a slot reserved
for a shadow copy, diagnose what went wrong.
* [SILGenPattern] Defer debug description of case variables
Create unique nested debug scopes for a switch, each of its case labels,
and each of its case bodies. This looks like:
```
switch ... { // Enter scope 1.
case ... : // Enter scope 2, nested within scope 1.
<body-1> // Enter scope 3, nested within scope 2.
case ... : // Enter scope 4, nested within scope 1.
<body-2> // Enter scope 5, nested within scope 4.
}
```
Use the new scope structure to defer emitting debug descriptions of case
bindings. Specifically, defer the work until we can nest the scope for a
case body under the scope for a pattern match.
This fixes SR-7973, a problem where it was impossible to inspect a case
binding in lldb when stopped at a case with multiple items.
Previously, we would emit the debug descriptions too early (in the
pattern match), leading to duplicate/conflicting descriptions. The only
reason that the ambiguous description was allowed to compile was because
the debug scopes were nested incorrectly.
rdar://41048339
* Update tests
Like switch cases, a catch clause may now include a comma-
separated list of patterns. The body will be executed if any
one of those patterns is matched.
This patch replaces `CatchStmt` with `CaseStmt` as the children
of `DoCatchStmt` in the AST. This necessitates a number of changes
throughout the compiler, including:
- Parser & libsyntax support for the new syntax and AST structure
- Typechecking of multi-pattern catches, including those which
contain bindings.
- SILGen support
- Code completion updates
- Profiler updates
- Name lookup changes
For those who are unaware, a transformation terminator is a terminator like
switch_enum/checked_cast_br that always dominate their successor blocks. Since
they dominate their successor blocks by design and transform their input into
the args form, we can validate that they obey guaranteed ownership semantics
just like a forwarding instruction.
Beyond removing unnecessary code bloat, this also makes it significantly more
easier to optimize/work with transformation terminators when converting @owned
-> @guaranteed since we do not need to find end_borrow points when the owned
value is consumed.
<rdar://problem/59097063>
Structurally prevent a number of common anti-patterns involving generic
signatures by separating the interface into GenericSignature and the
implementation into GenericSignatureBase. In particular, this allows
the comparison operators to be deleted which forces callers to
canonicalize the signature or ask to compare pointers explicitly.
Looks like a think-o; even if we see an address-only binding in a shared pattern block, we still
expect bb args in the lowered basic block for anything that comes after it. rdar://problem/53956564
Add `llvm_unreachable` to mark covered switches which MSVC does not
analyze correctly and believes that there exists a path through the
function without a return value.
In SILGenPattern, we need to be able to unforward cleanups when we explode
tuples. Thus we can't use RValue in SILGenPattern since it may implicitly
explode tuples (which without modifying RValue itself we can not
unforward). This patch removes a specific RValue usage that we can replace with
the use of a ManagedValue instead.
rdar://49903264
This is a large patch; I couldn't split it up further while still
keeping things working. There are four things being changed at
once here:
- Places that call SILType::isAddressOnly()/isLoadable() now call
the SILFunction overload and not the SILModule one.
- SILFunction's overloads of getTypeLowering() and getLoweredType()
now pass the function's resilience expansion down, instead of
hardcoding ResilienceExpansion::Minimal.
- Various other places with '// FIXME: Expansion' now use a better
resilience expansion.
- A few tests were updated to reflect SILGen's improved code
generation, and some new tests are added to cover more code paths
that previously were uncovered and only manifested themselves as
standard library build failures while I was working on this change.
Specifically, in order to generate runtime errors when we do not handle an enum
appropriately, we form a metatype from the input of the switch. The problem is
that by the time we get to the leaf of the emission tree where this metatype is
created, the input of the switch may have already been consumed. This means
creating the metatype could be a use after free.
This patch fixes the problem by emitting the value_metatype after we emit the
subject of the switch, but before we emit the switch itself.
rdar://49562761
Specifically the bad pattern was:
```
for (auto *vd : *caseStmt->getCaseBodyVariables()) { ... }
```
The problem is that the optional is not lifetime extended over the for loop. To
work around this, I changed the API of CaseStmt's getCaseBodyVariable methods to
never return the inner Optional<MutableArrayRef<T>>. Now we have the following 3
methods (ignoring const differences):
1. CaseStmt::hasCaseBodyVariables().
2. CaseStmt::getCaseBodyVariables(). Asserts if the case body variable array was
never specified.
3. CaseStmt::getCaseBodyVariablesOrEmptyArray(). Returns either the case body
variables array or an empty array if we were never given any case body
variable array.
This should prevent anyone else in the future from hitting this type of bug.
radar://49609717
The problem would occur if after we accessed the value, we grew the DenseMap to
insert the value again. But putting in an extra auto I avoid the problem here.
rdar://49609717
Previously we were handling this like we did not have any associated type. This
caused us to break the requirement in [ossa] that all switch_enum successors
associated with payloaded enums must have arguments. We still emit the empty
tuple value (or an undef tuple) if we do not have any associated type.
The ownership kind is Any for trivial types, or Owned otherwise, but
whether a type is trivial or not will soon depend on the resilience
expansion.
This means that a SILModule now uniques two SILUndefs per type instead
of one, and serialization uses two distinct sentinel IDs for this
purpose as well.
For now, the resilience expansion is not actually used here, so this
change is NFC, other than changing the module format.
There was a bug here where we were emitting a shared case if we had arguments.
That is why we needed the hacked in deletion of that block when we emitted
shared blocks. I was able to replace that with an assert to make sure that we do
not regress.
This additionally improved our code generation by eliminating a potential extra
copy when we had such a case. That is where the test updates come from.
NFC unless -enable-verify-exclusivity is on.
Boxed (indirect) enum loads are not formal accesses so they should not
have access markers. For a while, we used UnenforcedAccess in this
case to signal to the verifier that this is actually a safe load. But
that isn't needed. The verifier just ignores loads from unidentified
storage instead.
There are two code paths that had this UnenforcedAccess marker. One
was cleaned up, and the other was not. So finish the cleanup and add a
unit test for both SILGen code paths.
Today SILGenPattern maintains the following invariants:
1. All values passed into a switch must be either TakeAlways or BorrowAlways and
loadable input values will be loaded.
2. Loadable types passed to a subtree must be loaded.
3. TakeOnSuccess can only occur with address only types and only in subtrees.
4. A TakeOnSuccess value must go through "fake borrowing"
(i.e. forward/unforwarding) to ensure that along failing cases, we properly
re-enable the cleanup on the aggregate. This means that TakeOnSuccess can
never be handled as a loadable value with ownership enabled and that any
take_on_success value since the original cleanup on the parent value was
disabled must be at +1.
5. We use BorrowAlways instead of TakeOnSuccess for objects to express the
notion that the object is not owned by the sub-tree.
The bug in this tuple code occured at the a place in the code where we go from
an address only parent type to a loadable subtype via TakeOnSuccess. I was
trying to follow (5) and thus I converted the subvalue to use a {load_borrow,
BorrowAlways}. The problem with this is that I lost the cleanup from the tuple
subvalue since take_on_success is just simulating +0 and thus entails having a
cleanup for each of the underlying tuple values.
The fix here was to:
* Create a cleanup for the loadable subvalue leaving it in memory. This address
version of the subvalue we use for the purposes of unforwarding. This avoids
(4).
* load_borrow the subvalue and pass that off to the subtree. This ensures that
we respect (2), (3), (4).
* Unforward in the failure case the in memory subvalue.
This gives us both characteristics as well as in the future the possibility of
enforcing via the ownership verifier that the borrow ends before the
destroy_addr.
I also sprinkled some assertions liberally to maintain invariants.
rdar://47034816