Skip to content

Commit 55a5ed1

Browse files
committed
Accurately distinguish generic and non-generic cases.
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.
1 parent fa3407e commit 55a5ed1

File tree

4 files changed

+185
-7
lines changed

4 files changed

+185
-7
lines changed

include/swift/RemoteInspection/TypeRef.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,10 @@ class alignas(8) TypeRef {
216216
const TypeRef *subst(TypeRefBuilder &Builder,
217217
const GenericArgumentMap &Subs) const;
218218

219+
const TypeRef *subst(TypeRefBuilder &Builder,
220+
const GenericArgumentMap &Subs,
221+
bool &DidSubstitute) const;
222+
219223
llvm::Optional<GenericArgumentMap> getSubstMap() const;
220224

221225
virtual ~TypeRef() = default;

stdlib/public/RemoteInspection/TypeRef.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1215,11 +1215,15 @@ class TypeRefSubstitution
12151215
: public TypeRefVisitor<TypeRefSubstitution, const TypeRef *> {
12161216
TypeRefBuilder &Builder;
12171217
GenericArgumentMap Substitutions;
1218+
// Set true iff the Substitution map was actually used
1219+
bool DidSubstitute;
12181220
public:
12191221
using TypeRefVisitor<TypeRefSubstitution, const TypeRef *>::visit;
12201222

12211223
TypeRefSubstitution(TypeRefBuilder &Builder, GenericArgumentMap Substitutions)
1222-
: Builder(Builder), Substitutions(Substitutions) {}
1224+
: Builder(Builder), Substitutions(Substitutions), DidSubstitute(false) {}
1225+
1226+
bool didSubstitute() const { return DidSubstitute; }
12231227

12241228
const TypeRef *visitBuiltinTypeRef(const BuiltinTypeRef *B) {
12251229
return B;
@@ -1340,6 +1344,7 @@ class TypeRefSubstitution
13401344
if (found == Substitutions.end())
13411345
return GTP;
13421346
assert(found->second->isConcrete());
1347+
DidSubstitute = true; // We actually used the Substitutions
13431348

13441349
// When substituting a concrete type containing a metatype into a
13451350
// type parameter, (eg: T, T := C.Type), we must also represent
@@ -1451,6 +1456,15 @@ const TypeRef *TypeRef::subst(TypeRefBuilder &Builder,
14511456
return TypeRefSubstitution(Builder, Subs).visit(this);
14521457
}
14531458

1459+
const TypeRef *TypeRef::subst(TypeRefBuilder &Builder,
1460+
const GenericArgumentMap &Subs,
1461+
bool &DidSubstitute) const {
1462+
auto subst = TypeRefSubstitution(Builder, Subs);
1463+
auto TR = subst.visit(this);
1464+
DidSubstitute = subst.didSubstitute();
1465+
return TR;
1466+
}
1467+
14541468
bool TypeRef::deriveSubstitutions(GenericArgumentMap &Subs,
14551469
const TypeRef *OrigTR,
14561470
const TypeRef *SubstTR) {

stdlib/public/RemoteInspection/TypeRefBuilder.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -373,12 +373,11 @@ bool TypeRefBuilder::getFieldTypeRefs(
373373
if (!Unsubstituted)
374374
return false;
375375

376-
// TODO: Consider `struct S<T> { enum E { case a(T); case b(T?) }}`
377-
// This test identifies `a` as a generic case, but not `b`
378-
bool IsGeneric = isa<GenericTypeParameterTypeRef>(Unsubstituted);
379-
380-
auto Substituted = Unsubstituted->subst(*this, *Subs);
381-
376+
// We need this for enums; an enum case "is generic" if any generic type
377+
// parameter substitutions occurred on the payload. E.g.,
378+
// `case a([T?])` is generic, but `case a([Int?])` is not.
379+
bool IsGeneric = false;
380+
auto Substituted = Unsubstituted->subst(*this, *Subs, IsGeneric);
382381
bool IsIndirect = FD->isEnum() && Field->isIndirectCase();
383382

384383
auto FieldTI = FieldTypeInfo(FieldName.str(), FieldValue, Substituted, IsIndirect, IsGeneric);
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -lswiftSwiftReflectionTest %s -o %t/reflect_Enum_MultiPayload_generic8
3+
// RUN: %target-codesign %t/reflect_Enum_MultiPayload_generic8
4+
5+
// RUN: %target-run %target-swift-reflection-test %t/reflect_Enum_MultiPayload_generic8 | tee /dev/stderr | %FileCheck %s --check-prefix=CHECK --check-prefix=X%target-ptrsize --dump-input=fail
6+
7+
// REQUIRES: reflection_test_support
8+
// REQUIRES: executable_test
9+
// UNSUPPORTED: use_os_stdlib
10+
11+
import SwiftReflectionTest
12+
import Darwin
13+
14+
struct S<T> { var t: T }
15+
16+
enum A<T> {
17+
case a(S<T>)
18+
case b(Int)
19+
case c(Void)
20+
}
21+
22+
reflect(enum: A<Void>.a(S<Void>(t: ())) as A<Void>?)
23+
24+
// CHECK: Reflecting an enum.
25+
// CHECK-NEXT: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}}
26+
// CHECK-NEXT: Type reference:
27+
// CHECK-NEXT: (bound_generic_enum Swift.Optional
28+
// CHECK-NEXT: (bound_generic_enum reflect_Enum_MultiPayload_generic8.A
29+
// CHECK-NEXT: (tuple)))
30+
31+
// Note: MemoryLayout<A<Void>?>.size == MemoryLayout<A<Void>>.size == 9
32+
// This means the MPE is exporting XIs from the tag, which means
33+
// it's an MPE layout, not an SPE layout.
34+
// Explanation: S<T> is generic, thus treated as a non-empty payload
35+
// even though it is in fact zero-sized.
36+
37+
// CHECK: Type info:
38+
// X64-NEXT: (single_payload_enum size=9 alignment=8 stride=16 num_extra_inhabitants=252 bitwise_takable=1
39+
// X64-NEXT: (case name=some index=0 offset=0
40+
// X64-NEXT: (multi_payload_enum size=9 alignment=8 stride=16 num_extra_inhabitants=253 bitwise_takable=1
41+
// X64-NEXT: (case name=a index=0 offset=0
42+
// X64-NEXT: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1
43+
// X64-NEXT: (field name=t offset=0
44+
// X64-NEXT: (tuple size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1))))
45+
// X64-NEXT: (case name=b index=1 offset=0
46+
// X64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1
47+
// X64-NEXT: (field name=_value offset=0
48+
// X64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1))))
49+
// X64-NEXT: (case name=c index=2 offset=0
50+
// X64-NEXT: (tuple size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1))))
51+
// X64-NEXT: (case name=none index=1))
52+
53+
// X32: FAIL
54+
55+
// CHECK-NEXT: Mangled name: $s34reflect_Enum_MultiPayload_generic81AOyytGSg
56+
// CHECK-NEXT: Demangled name: Swift.Optional<reflect_Enum_MultiPayload_generic8.A<()>>
57+
58+
// CHECK: Enum value:
59+
// CHECK-NEXT: (enum_value name=some index=0
60+
// CHECK-NEXT: (bound_generic_enum reflect_Enum_MultiPayload_generic8.A
61+
// CHECK-NEXT: (tuple))
62+
// CHECK-NEXT: )
63+
64+
reflect(enum: A<Void>.a(S<Void>(t: ())))
65+
66+
// CHECK: Enum value:
67+
// CHECK-NEXT: (enum_value name=a index=0
68+
// CHECK-NEXT: (bound_generic_struct reflect_Enum_MultiPayload_generic8.S
69+
// CHECK-NEXT: (tuple))
70+
// CHECK-NEXT: )
71+
72+
reflect(enum: A<Void>?.none)
73+
74+
// CHECK: Enum value:
75+
// CHECK-NEXT: (enum_value name=none index=1)
76+
77+
reflect(enum: A<Void>.c(()))
78+
79+
// CHECK: Enum value:
80+
// CHECK-NEXT: (enum_value name=c index=2
81+
// CHECK-NEXT: (tuple)
82+
// CHECK-NEXT: )
83+
84+
85+
enum B<T> {
86+
case a(S<Void>)
87+
case b(S<T>)
88+
case c(Void)
89+
}
90+
91+
reflect(enum: B<Int>.a(S<Void>(t: ())) as B<Int>?)
92+
93+
// CHECK: Reflecting an enum.
94+
// CHECK-NEXT: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}}
95+
// CHECK-NEXT: Type reference:
96+
// CHECK-NEXT: (bound_generic_enum Swift.Optional
97+
// CHECK-NEXT: (bound_generic_enum reflect_Enum_MultiPayload_generic8.B
98+
// CHECK-NEXT: (struct Swift.Int)))
99+
100+
// X64: Type info:
101+
// X64-NEXT: (single_payload_enum size=10 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1
102+
// X64-NEXT: (case name=some index=0 offset=0
103+
// X64-NEXT: (multi_payload_enum size=9 alignment=8 stride=16 num_extra_inhabitants=0 bitwise_takable=1
104+
// X64-NEXT: (case name=b index=0 offset=0
105+
// X64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1
106+
// X64-NEXT: (field name=t offset=0
107+
// X64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1
108+
// X64-NEXT: (field name=_value offset=0
109+
// X64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0 bitwise_takable=1))))))
110+
// X64-NEXT: (case name=a index=1 offset=0
111+
// X64-NEXT: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1
112+
// X64-NEXT: (field name=t offset=0
113+
// X64-NEXT: (tuple size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1))))
114+
// X64-NEXT: (case name=c index=2 offset=0
115+
// X64-NEXT: (tuple size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1))))
116+
// X64-NEXT: (case name=none index=1))
117+
118+
// X32: FAIL
119+
120+
// CHECK-NEXT: Mangled name: $s34reflect_Enum_MultiPayload_generic81BOySiGSg
121+
// CHECK-NEXT: Demangled name: Swift.Optional<reflect_Enum_MultiPayload_generic8.B<Swift.Int>>
122+
123+
// CHECK: Enum value:
124+
// CHECK-NEXT: (enum_value name=some index=0
125+
// CHECK-NEXT: (bound_generic_enum reflect_Enum_MultiPayload_generic8.B
126+
// CHECK-NEXT: (struct Swift.Int))
127+
// CHECK-NEXT: )
128+
129+
reflect(enum: B<Int>?.none)
130+
131+
// CHECK: Enum value:
132+
// CHECK-NEXT: (enum_value name=none index=1)
133+
134+
reflect(enum: B<Int>.a(S<Void>(t: ())))
135+
136+
// CHECK: Enum value:
137+
// CHECK-NEXT: (enum_value name=a index=1
138+
// CHECK-NEXT: (bound_generic_struct reflect_Enum_MultiPayload_generic8.S
139+
// CHECK-NEXT: (tuple))
140+
// CHECK-NEXT: )
141+
142+
143+
reflect(enum: B<Int>.b(S<Int>(t: 17)))
144+
145+
// CHECK: Enum value:
146+
// CHECK-NEXT: (enum_value name=b index=0
147+
// CHECK-NEXT: (bound_generic_struct reflect_Enum_MultiPayload_generic8.S
148+
// CHECK-NEXT: (struct Swift.Int))
149+
// CHECK-NEXT: )
150+
151+
reflect(enum: B<Int>.c(()))
152+
153+
// CHECK: Enum value:
154+
// CHECK-NEXT: (enum_value name=c index=2
155+
// CHECK-NEXT: (tuple)
156+
// CHECK-NEXT: )
157+
158+
doneReflecting()
159+
160+
// CHECK: Done.
161+

0 commit comments

Comments
 (0)