Skip to content

Commit 0cef2ce

Browse files
committed
[cxx-interop] [IRGen] TypeInfo for address-only types.
The current "ClangRecordTypeInfo" derives from "LoadableTypeInfo" and is only meant for loadable types. While we have not yet run into problems, this may cause issues in the future and as more logic is needed around copying, moving, and destroying C++ objects, this needs to be fixed.
1 parent 1105843 commit 0cef2ce

File tree

5 files changed

+432
-28
lines changed

5 files changed

+432
-28
lines changed

lib/IRGen/GenStruct.cpp

Lines changed: 89 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ enum class StructTypeInfoKind {
5353
LoadableStructTypeInfo,
5454
FixedStructTypeInfo,
5555
LoadableClangRecordTypeInfo,
56+
AddressOnlyClangRecordTypeInfo,
5657
NonFixedStructTypeInfo,
5758
ResilientStructTypeInfo
5859
};
@@ -83,6 +84,12 @@ namespace {
8384
/// A field-info implementation for fields of Clang types.
8485
class ClangFieldInfo : public RecordField<ClangFieldInfo> {
8586
public:
87+
ClangFieldInfo(VarDecl *swiftField, const ElementLayout &layout,
88+
const TypeInfo &typeInfo)
89+
: RecordField(typeInfo), Field(swiftField) {
90+
completeFrom(layout);
91+
}
92+
8693
ClangFieldInfo(VarDecl *swiftField, const ElementLayout &layout,
8794
unsigned explosionBegin, unsigned explosionEnd)
8895
: RecordField(layout, explosionBegin, explosionEnd),
@@ -290,7 +297,7 @@ namespace {
290297
}
291298
}
292299
};
293-
300+
294301
/// A type implementation for loadable record types imported from Clang.
295302
class LoadableClangRecordTypeInfo final :
296303
public StructTypeInfoBase<LoadableClangRecordTypeInfo, LoadableTypeInfo,
@@ -339,6 +346,50 @@ namespace {
339346
}
340347
};
341348

349+
class AddressOnlyClangRecordTypeInfo final
350+
: public StructTypeInfoBase<AddressOnlyClangRecordTypeInfo, FixedTypeInfo,
351+
ClangFieldInfo> {
352+
const clang::RecordDecl *ClangDecl;
353+
354+
public:
355+
AddressOnlyClangRecordTypeInfo(ArrayRef<ClangFieldInfo> fields,
356+
llvm::Type *storageType, Size size,
357+
Alignment align,
358+
const clang::RecordDecl *clangDecl)
359+
: StructTypeInfoBase(StructTypeInfoKind::AddressOnlyClangRecordTypeInfo,
360+
fields, storageType, size,
361+
// We can't assume any spare bits in a C++ type
362+
// with user-defined special member functions.
363+
SpareBitVector(llvm::Optional<APInt>{
364+
llvm::APInt(size.getValueInBits(), 0)}),
365+
align, IsPOD, IsNotBitwiseTakable, IsFixedSize),
366+
ClangDecl(clangDecl) {
367+
(void)ClangDecl;
368+
}
369+
370+
TypeLayoutEntry *buildTypeLayoutEntry(IRGenModule &IGM,
371+
SILType T) const override {
372+
return IGM.typeLayoutCache.getOrCreateScalarEntry(*this, T);
373+
}
374+
375+
void initializeFromParams(IRGenFunction &IGF, Explosion &params,
376+
Address addr, SILType T,
377+
bool isOutlined) const override {
378+
llvm_unreachable("Address-only C++ types must be created by C++ special "
379+
"member functions.");
380+
}
381+
382+
llvm::NoneType getNonFixedOffsets(IRGenFunction &IGF) const { return None; }
383+
llvm::NoneType getNonFixedOffsets(IRGenFunction &IGF, SILType T) const {
384+
return None;
385+
}
386+
MemberAccessStrategy
387+
getNonFixedFieldAccessStrategy(IRGenModule &IGM, SILType T,
388+
const ClangFieldInfo &field) const {
389+
llvm_unreachable("non-fixed field in Clang type?");
390+
}
391+
};
392+
342393
/// A type implementation for loadable struct types.
343394
class LoadableStructTypeInfo final
344395
: public StructTypeInfoBase<LoadableStructTypeInfo, LoadableTypeInfo> {
@@ -680,6 +731,10 @@ class ClangRecordLowering {
680731

681732
const TypeInfo *createTypeInfo(llvm::StructType *llvmType) {
682733
llvmType->setBody(LLVMFields, /*packed*/ true);
734+
if (SwiftType.getStructOrBoundGenericStruct()->isCxxNonTrivial()) {
735+
return AddressOnlyClangRecordTypeInfo::create(
736+
FieldInfos, llvmType, TotalStride, TotalAlignment, ClangDecl);
737+
}
683738
return LoadableClangRecordTypeInfo::create(FieldInfos, NextExplosionIndex,
684739
llvmType, TotalStride,
685740
std::move(SpareBits), TotalAlignment,
@@ -773,7 +828,7 @@ class ClangRecordLowering {
773828

774829
// If we have a Swift import of this type, use our lowered information.
775830
if (swiftField) {
776-
auto &fieldTI = cast<LoadableTypeInfo>(IGM.getTypeInfo(
831+
auto &fieldTI = cast<FixedTypeInfo>(IGM.getTypeInfo(
777832
SwiftType.getFieldType(swiftField, IGM.getSILModule(),
778833
IGM.getMaximalTypeExpansionContext())));
779834
addField(swiftField, offset, fieldTI);
@@ -812,7 +867,7 @@ class ClangRecordLowering {
812867

813868
/// Add storage for an (optional) Swift field at the given offset.
814869
void addField(VarDecl *swiftField, Size offset,
815-
const LoadableTypeInfo &fieldType) {
870+
const FixedTypeInfo &fieldType) {
816871
assert(offset >= NextOffset && "adding fields out of order");
817872

818873
// Add a padding field if required.
@@ -823,8 +878,11 @@ class ClangRecordLowering {
823878
}
824879

825880
/// Add information to track a value field at the current offset.
826-
void addFieldInfo(VarDecl *swiftField, const LoadableTypeInfo &fieldType) {
827-
unsigned explosionSize = fieldType.getExplosionSize();
881+
void addFieldInfo(VarDecl *swiftField, const FixedTypeInfo &fieldType) {
882+
bool isLoadableField = isa<LoadableTypeInfo>(fieldType);
883+
unsigned explosionSize = 0;
884+
if (isLoadableField)
885+
explosionSize = cast<LoadableTypeInfo>(fieldType).getExplosionSize();
828886
unsigned explosionBegin = NextExplosionIndex;
829887
NextExplosionIndex += explosionSize;
830888
unsigned explosionEnd = NextExplosionIndex;
@@ -838,9 +896,12 @@ class ClangRecordLowering {
838896
layout.completeFixed(fieldType.isPOD(ResilienceExpansion::Maximal),
839897
NextOffset, LLVMFields.size());
840898

841-
FieldInfos.push_back(
842-
ClangFieldInfo(swiftField, layout, explosionBegin, explosionEnd));
843-
899+
if (isLoadableField)
900+
FieldInfos.push_back(
901+
ClangFieldInfo(swiftField, layout, explosionBegin, explosionEnd));
902+
else
903+
FieldInfos.push_back(ClangFieldInfo(swiftField, layout, fieldType));
904+
844905
if (!isEmpty) {
845906
LLVMFields.push_back(fieldType.getStorageType());
846907
NextOffset += fieldType.getFixedSize();
@@ -862,22 +923,26 @@ class ClangRecordLowering {
862923

863924
/// A convenient macro for delegating an operation to all of the
864925
/// various struct implementations.
865-
#define FOR_STRUCT_IMPL(IGF, type, op, ...) do { \
866-
auto &structTI = IGF.getTypeInfo(type); \
867-
switch (getStructTypeInfoKind(structTI)) { \
868-
case StructTypeInfoKind::LoadableClangRecordTypeInfo: \
869-
return structTI.as<LoadableClangRecordTypeInfo>().op(IGF, __VA_ARGS__); \
870-
case StructTypeInfoKind::LoadableStructTypeInfo: \
871-
return structTI.as<LoadableStructTypeInfo>().op(IGF, __VA_ARGS__); \
872-
case StructTypeInfoKind::FixedStructTypeInfo: \
873-
return structTI.as<FixedStructTypeInfo>().op(IGF, __VA_ARGS__); \
874-
case StructTypeInfoKind::NonFixedStructTypeInfo: \
875-
return structTI.as<NonFixedStructTypeInfo>().op(IGF, __VA_ARGS__); \
876-
case StructTypeInfoKind::ResilientStructTypeInfo: \
877-
llvm_unreachable("resilient structs are opaque"); \
878-
} \
879-
llvm_unreachable("bad struct type info kind!"); \
880-
} while (0)
926+
#define FOR_STRUCT_IMPL(IGF, type, op, ...) \
927+
do { \
928+
auto &structTI = IGF.getTypeInfo(type); \
929+
switch (getStructTypeInfoKind(structTI)) { \
930+
case StructTypeInfoKind::LoadableClangRecordTypeInfo: \
931+
return structTI.as<LoadableClangRecordTypeInfo>().op(IGF, __VA_ARGS__); \
932+
case StructTypeInfoKind::AddressOnlyClangRecordTypeInfo: \
933+
return structTI.as<AddressOnlyClangRecordTypeInfo>().op(IGF, \
934+
__VA_ARGS__); \
935+
case StructTypeInfoKind::LoadableStructTypeInfo: \
936+
return structTI.as<LoadableStructTypeInfo>().op(IGF, __VA_ARGS__); \
937+
case StructTypeInfoKind::FixedStructTypeInfo: \
938+
return structTI.as<FixedStructTypeInfo>().op(IGF, __VA_ARGS__); \
939+
case StructTypeInfoKind::NonFixedStructTypeInfo: \
940+
return structTI.as<NonFixedStructTypeInfo>().op(IGF, __VA_ARGS__); \
941+
case StructTypeInfoKind::ResilientStructTypeInfo: \
942+
llvm_unreachable("resilient structs are opaque"); \
943+
} \
944+
llvm_unreachable("bad struct type info kind!"); \
945+
} while (0)
881946

882947
Address irgen::projectPhysicalStructMemberAddress(IRGenFunction &IGF,
883948
Address base,

test/Interop/Cxx/class/Inputs/type-classification.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,4 +155,22 @@ struct StructDeletedDestructor {
155155
~StructDeletedDestructor() = delete;
156156
};
157157

158+
struct StructWithCopyConstructorAndValue {
159+
int value;
160+
StructWithCopyConstructorAndValue(
161+
const StructWithCopyConstructorAndValue &other)
162+
: value(other.value) {}
163+
};
164+
165+
struct StructWithSubobjectCopyConstructorAndValue {
166+
StructWithCopyConstructorAndValue member;
167+
};
168+
169+
struct StructWithCopyConstructorAndSubobjectCopyConstructorAndValue {
170+
StructWithCopyConstructorAndValue member;
171+
StructWithCopyConstructorAndSubobjectCopyConstructorAndValue(
172+
const StructWithCopyConstructorAndSubobjectCopyConstructorAndValue &other)
173+
: member(other.member) {}
174+
};
175+
158176
#endif
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// RUN: %target-swift-frontend -enable-cxx-interop -I %S/Inputs %s -emit-ir | %FileCheck %s
2+
3+
// Verify that non-trival/address-only C++ classes are constructed and accessed
4+
// correctly. Make sure that we correctly IRGen functions that construct
5+
// non-trivial C++ classes, take those classes as a parameter, and access those
6+
// classes members.
7+
8+
import TypeClassification
9+
10+
// TODO: C++ objects with destructors should be tested here once we fully
11+
// support them.
12+
13+
// CHECK-LABEL: define {{.*}}i1 @"$s4main37testStructWithCopyConstructorAndValueSbyF"
14+
// CHECK: [[OBJ:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV
15+
// CHECK: [[VAL_ELEMENT:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[OBJ]], i32 0, i32 0
16+
// CHECK: [[VAL_INT:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[VAL_ELEMENT]], i32 0, i32 0
17+
// CHECK: store i32 42, i32* [[VAL_INT]]
18+
// CHECK: ret i1 true
19+
public func testStructWithCopyConstructorAndValue() -> Bool {
20+
let obj = StructWithCopyConstructorAndValue(value: 42)
21+
return obj.value == 42
22+
}
23+
24+
// CHECK-LABEL: define {{.*}}i1 @"$s4main46testStructWithSubobjectCopyConstructorAndValueSbyF"()
25+
// CHECK: [[MEMBER:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV
26+
// CHECK: [[OBJ:%.*]] = alloca %TSo42StructWithSubobjectCopyConstructorAndValueV
27+
// CHECK: alloca %TSo33StructWithCopyConstructorAndValueV
28+
// CHECK: [[TMP:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV
29+
// CHECK: [[MEMBER_ELEMENT:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[MEMBER]], i32 0, i32 0
30+
// CHECK: [[MEMBER_VALUE:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[MEMBER_ELEMENT]], i32 0, i32 0
31+
// CHECK: store i32 42, i32* [[MEMBER_VALUE]]
32+
// CHECK: %obj.member = getelementptr inbounds %TSo42StructWithSubobjectCopyConstructorAndValueV, %TSo42StructWithSubobjectCopyConstructorAndValueV* [[OBJ]], i32 0, i32 0
33+
// CHECK: [[TEMP_MEMBER:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[TMP]], i32 0, i32 0
34+
// CHECK: [[TEMP_MEMBER_VALUE:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[TEMP_MEMBER]], i32 0, i32 0
35+
// CHECK: [[LHS:%.*]] = load i32, i32* [[TEMP_MEMBER_VALUE]]
36+
// CHECK: [[OUT:%.*]] = icmp eq i32 [[LHS]], 42
37+
// CHECK: ret i1 [[OUT]]
38+
public func testStructWithSubobjectCopyConstructorAndValue() -> Bool {
39+
let member = StructWithCopyConstructorAndValue(value: 42)
40+
let obj = StructWithSubobjectCopyConstructorAndValue(member: member)
41+
return obj.member.value == 42
42+
}
43+
44+
// CHECK-LABEL: define {{.*}}i1 @"$s4main041testStructWithCopyConstructorAndSubobjectefG5ValueSbyF"()
45+
// CHECK: [[MEMBER:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV
46+
// CHECK: alloca %TSo037StructWithCopyConstructorAndSubobjectcdE5ValueV
47+
// CHECK: alloca %TSo33StructWithCopyConstructorAndValueV
48+
// CHECK: [[TEMP:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV
49+
// CHECK: [[MEMBER_VAL:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[MEMBER]], i32 0, i32 0
50+
// CHECK: [[MEMBER_VAL_VAL:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[MEMBER_VAL]], i32 0, i32 0
51+
// CHECK: store i32 42, i32* [[MEMBER_VAL_VAL]]
52+
// CHECK: [[TEMP_MEMBER:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[TEMP]], i32 0, i32 0
53+
// CHECK: [[TEMP_MEMBER_VAL:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[TEMP_MEMBER]], i32 0, i32 0
54+
// CHECK: [[LHS:%.*]] = load i32, i32* [[TEMP_MEMBER_VAL]]
55+
// CHECK: [[OUT:%.*]] = icmp eq i32 [[LHS]], 42
56+
// CHECK: ret i1 [[OUT]]
57+
public func testStructWithCopyConstructorAndSubobjectCopyConstructorAndValue()
58+
-> Bool {
59+
let member = StructWithCopyConstructorAndValue(value: 42)
60+
let obj = StructWithCopyConstructorAndSubobjectCopyConstructorAndValue(
61+
member: member
62+
)
63+
return obj.member.value == 42
64+
}
65+
66+
// CHECK-LABEL: define {{.*}}i1 @"$s4main4test3objSbSo33StructWithCopyConstructorAndValueV_tF"(%TSo33StructWithCopyConstructorAndValueV* noalias nocapture dereferenceable(4) %0)
67+
// CHECK: [[VAL:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* %0, i32 0, i32 0
68+
// CHECK: [[VAL_VAL:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[VAL]], i32 0, i32 0
69+
// CHECK: [[LHS:%.*]] = load i32, i32* [[VAL_VAL]]
70+
// CHECK: [[OUT:%.*]] = icmp eq i32 %1, 42
71+
// CHECK: ret i1 [[OUT]]
72+
public func test(obj: StructWithCopyConstructorAndValue) -> Bool {
73+
return obj.value == 42
74+
}
75+
76+
// CHECK-LABEL: define {{.*}}i1 @"$s4main4test3objSbSo42StructWithSubobjectCopyConstructorAndValueV_tF"(%TSo42StructWithSubobjectCopyConstructorAndValueV* noalias nocapture dereferenceable(4) %0)
77+
// CHECK: [[TMP:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV
78+
// CHECK: [[MEMBER:%.*]] = getelementptr inbounds %TSo42StructWithSubobjectCopyConstructorAndValueV, %TSo42StructWithSubobjectCopyConstructorAndValueV* %0, i32 0, i32 0
79+
// CHECK: [[VAL:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[TMP]], i32 0, i32 0
80+
// CHECK: [[VAL_VAL:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[VAL]], i32 0, i32 0
81+
// CHECK: [[LHS:%.*]] = load i32, i32* [[VAL_VAL]]
82+
// CHECK: [[OUT:%.*]] = icmp eq i32 [[LHS]], 42
83+
// CHECK: ret i1 [[OUT]]
84+
public func test(obj: StructWithSubobjectCopyConstructorAndValue) -> Bool {
85+
return obj.member.value == 42
86+
}
87+
88+
// CHECK-LABEL: define {{.*}}i1 @"$s4main4test3objSbSo037StructWithCopyConstructorAndSubobjectfgH5ValueV_tF"(%TSo037StructWithCopyConstructorAndSubobjectcdE5ValueV* noalias nocapture dereferenceable(4) %0)
89+
// CHECK:[[TEMP:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV
90+
// CHECK:[[VAL:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[TEMP]], i32 0, i32 0
91+
// CHECK:[[VAL_VAL:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[VAL]], i32 0, i32 0
92+
// CHECK:[[LHS:%.*]] = load i32, i32* [[VAL_VAL]]
93+
// CHECK:[[OUT:%.*]] = icmp eq i32 [[LHS]], 42
94+
// CHECK:ret i1 [[OUT]]
95+
public func test(
96+
obj: StructWithCopyConstructorAndSubobjectCopyConstructorAndValue
97+
) -> Bool {
98+
return obj.member.value == 42
99+
}

0 commit comments

Comments
 (0)