This fills in a number of missing cases:
* MPEs with closure payloads
* MPEs with many non-payload cases
* MPEs with class existential payloads
* MPEs with existential metatype payloads
Resolves rdar://132270733
Resolves rdar://128705332
MPE layout code to exclusively use the new code. The key observation: existing
reflection metadata seems to already provide enough information in all cases, so
we can abandon an earlier effort to add spare bitmask data.
Resolves rdar://129281368
This adds a `getSpareBits` method to all the TypeInfo classes
that returns a suitable bitmask indicating the spare bits available
in values of this type. This gives us a way to recursively explore
the type tree and build up a full spare bit mask for an arbitrary type.
Happily, it appears we actually do have enough information to do this
calculation entirely from first principles, without requiring additional
reflection information. So once this is stable, we should remove my
earlier incomplete effort to publish spare bit mask info in the reflection
data, as that adds unnecessary metadata to every binary.
This doubtless still has plenty of holes, but seems sufficient
to handle a few basic enum types, including the stdlib DecodingError
which was used as an example to work out some key issues.
When a no-payload enum is stored inside a multi-payload enum,
the outer enum may be using some of the extra high-order bits.
So when we examine the inner enum, we should just strip those bits.
These tests had Darwin-specific code in them, mostly unnecessary,
that caused them to break on non-Darwin platforms.
Only one (reflect_Enum_SingleCaseCFPayload.swift) was actually
specific to Darwin. That one has been disabled. For the rest,
just delete the unnecessary `import Darwin` and similar.
A "trivial" enum is one that carries no information.
The previous logic asserted that a trivial enum had only one
case and that case had no payload.
But this is also a trivial payload:
```
// Trivial, so zero-sized
enum MyFormatVersion {
case v4
}
// Also trivial, since the payload carries no information
enum Format {
case MyFormat(MyFormatVersion)
}
```
This commit adds a test case similar to the above
and corrects the assertions for trivial enums to
assert that there is either no payload, or that the
payload is zero-sized.
Resolves rdar://116406504
Disable the assert:
// At least one payload is non-empty (otherwise this // would get laid out as a non-payload enum) assert(getNumNonEmptyPayloadCases() > 0);
in TaggedMultiPayloadEnumTypeInfo because it fails when you have
generic but zero-sized payload enum cases.
Also remove unnecessary "REQUIRES: objc_interop" lines from the enum
reflection tests.
I earlier overhauled the enum layout logic to correctly consider
enums with generic cases and cases that have zero size.
This updates the enum projection logic to use that information
as well.
In particular, this fixes a bug where an MPE with zero-sized cases
would be incorrectly projected by RemoteMirror (with consequences
for the `leaks` tool and lldb).
Resolves rdar://111705059
The new logic no longer includes non-generic parent types as part
of the type reference.
This also combines the 32- and 64-bit tests validations, since they
were identical for this test anyway.
This is a more correct fix for the issue that inspired PR #62854.
In particular, this does not change the numbering of generic
argument levels, and so does not break generic type parameter
lookups.
Added a new test case to verify that nested types that mix
generics and non-generics in different ways consistently identify
the correct generic argument labels.
In particular, the exactly internal layout of Array<> is quite
different between macOS and Linux, which makes it impractical
for use in tests like this. An empty class is still a reference
and doesn't introduce so many complications.
A "generic" case is any case whose payload type depends
on generic type parameters for the enum or any enclosing type.
Some examples:
```
struct S<T> {
enum E {
case a // Non-generic case
case b(Int) // Non-generic case
case c(T) // Generic case
case d([U]) // Generic case
case e([Int]) // Non-generic case
}
}
```
This is important for correctly distinguishing MPE versus
SPE layouts. A case is considered "empty" only if it
is either a non-payload case (`case a`) or if the payload
is zero-sized and non-generic. Generic cases are always
treated as non-zero-sized for purposes of determining
the layout strategy.
Correctly testing this is tricky, since some of the
layout strategies differ only in whether they export
extra tag values as XIs. The easiest way to verify is
to check whether wrapping the result in an `Optional<>`
adds another byte to the overall size.
This exercises a number of cases that the previous logic got
wrong, including cases where MPEs are laid out like SPEs,
and cases where we use the SPE or MPE layout strategies for enums
that have no non-empty payloads. (These cases are superficially
similar but differ in how they handle XIs.)
These new tests allowed me to correct a number logical flaws
about how layouts are selected. In particular, cases with
generic payloads are always considered to be non-empty for
purposes of selecting a layout strategy. Only cases that
are statically known to be empty are considered empty for
layout purposes.
that contain zero-sized payloads, which should resolve a number of issues with
RemoteMirror incorrectly reflecting enum cases and/or measuring the layout of
structures containing enums.
Background: Enum cases that have zero-sized payloads are handled
differently from other payload-bearing cases.
1. For layout purposes, they're treated as
non-payload cases. This can cause an MPE to
actually get represented in memory as a single-payload
or non-payload enum.
2. However, zero-sized payloads are still considered for
extra inhabitant calculations. Since they have no
extra inhabitants, this tends to cause such enums to
also not expose extra inhabitants to containing enums.
This commit makes several change to how RemoteMirror determines
enum layout:
* The various "*EnumTypeInfo" classes now represent
layout mechanisms -- as described in (1) above,
this can differ from the source code concept.
* An Enum "kind" is separately computed to reflect the
source code concept; this ensures that the dumped
type information reflects the source code.
* For single-payload and no-payload _layouts_,
the extra inhabitant calculation has been adjusted
to ensure that zero-sized payloads are correctly
considered.
Resolves: rdar://92945673
Fix computation of generic params per level when a generic type is
nested in a non-generic one. For example, for the following code:
```
class A {
class B<T, U> {
}
}
```
Fix the array of generic params per level from [2] to [0, 2].
rdar://103457629
Bools are special in that they only need 1 bit of storage, and have many
extra inhabitants available. This adds a test to check that an optional
whose payload is a bool is reflected correctly.
We can end up with a stack overflow if we encounter a very deeply nested type, or bad data that looks like one. Fail gracefully for types that are nested beyond a limit. By default, the limit is 50.
rdar://100847548
Apparently, RemoteMirror chokes on certain MPEs with generic payload types.
It does not recognize the generic payload type so ends up defaulting to a
zero size. This causes the overall enum size to be miscalculated unless there
is another non-generic payload that's at least as large.
The case below requires us to reprocess the metatype to "thicken" it.
That process inadvertently lost information about the depth of generic
nesting, which caused the RemoteMirror to be unable to resolve the type of
`C.E.t` at runtime.
```
class C<T> {
enum E<T> {
case t(T)
case u(Int)
}
var e: E<T>?
}
```
Solution: add code to the thickening logic to recursively thicken
parent types to preserve the nesting of generics.
Resolves rdar://90490128
This adds a new reflection record type carrying spare bit information for multi-payload enums.
The compiler includes this for any type that might need it in order to accurately reflect the contents of the enum. The RemoteMirror library will use this if present to determine how to project the contents of the enum. If not present (for example, in older binaries), the RemoteMirror library falls back on an internal calculation of the spare bitmask.
A few notes:
* The internal calculation is not perfect. In particular, it does not support MPEs that contain other enums (e.g., optionals). It should accurately refuse to project any MPE that it does not correctly support.
* The new reflection field is designed to be expandable; this might someday avoid the need for a new section.
Resolves rdar://61158214
Implement a version of projectExistential tailored for LLDB. There are 2
differences when projecting existentials for LLDB:
1 - When it comes to existentials, LLDB stores the address of the error
pointer, which must be dereferenced.
2 - When the existential wraps a class type, LLDB expects the address
returned is the class instance itself and not the address of the
reference.
This patch also adapts the swift reflection test machinery to test
projectExistentialAndUnwrapClass as well. This is done by exposing
the new functionality from swift reflection test. It is tested in
existentials.swift, and ensures that the typeref information is
exactly the same as what is expected from projectExistential,
except the out address.
(cherry picked from commit 55e971e06750c3ba29722d558cc5400298f6bdaf)
* Release node factory storage after each demangling operation
This adds missing clear() operations to a number of places in
RemoteMirror in order to reclaim memory after (de)mangling results
are no longer needed.
Before this, the RemoteMirror library had an unfortunate tendency to use
excessive amounts of memory as the demangler kept appending data to its
internal slab allocator.
Resolves rdar://72958641
* Include payload cases even if we cannot retrieve the typeinfo
Otherwise, we end up with inconsistent counts of payload and non-payload
cases, which screws up some of the enum management.
* Add a very basic check of enum with CF payload.