From 0cef2ceaee13ea6bf19a838a6f6c361d489138d0 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Sat, 25 Jul 2020 20:32:09 -0700 Subject: [PATCH] [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. --- lib/IRGen/GenStruct.cpp | 113 +++++++--- .../Cxx/class/Inputs/type-classification.h | 18 ++ ...ype-classification-non-trivial-irgen.swift | 99 +++++++++ ...pe-classification-non-trivial-silgen.swift | 195 +++++++++++++++++- .../type-classification-non-trivial.swift | 35 ++++ 5 files changed, 432 insertions(+), 28 deletions(-) create mode 100644 test/Interop/Cxx/class/type-classification-non-trivial-irgen.swift create mode 100644 test/Interop/Cxx/class/type-classification-non-trivial.swift diff --git a/lib/IRGen/GenStruct.cpp b/lib/IRGen/GenStruct.cpp index b8c647fbefc6d..3f1961f80664b 100644 --- a/lib/IRGen/GenStruct.cpp +++ b/lib/IRGen/GenStruct.cpp @@ -53,6 +53,7 @@ enum class StructTypeInfoKind { LoadableStructTypeInfo, FixedStructTypeInfo, LoadableClangRecordTypeInfo, + AddressOnlyClangRecordTypeInfo, NonFixedStructTypeInfo, ResilientStructTypeInfo }; @@ -83,6 +84,12 @@ namespace { /// A field-info implementation for fields of Clang types. class ClangFieldInfo : public RecordField { public: + ClangFieldInfo(VarDecl *swiftField, const ElementLayout &layout, + const TypeInfo &typeInfo) + : RecordField(typeInfo), Field(swiftField) { + completeFrom(layout); + } + ClangFieldInfo(VarDecl *swiftField, const ElementLayout &layout, unsigned explosionBegin, unsigned explosionEnd) : RecordField(layout, explosionBegin, explosionEnd), @@ -290,7 +297,7 @@ namespace { } } }; - + /// A type implementation for loadable record types imported from Clang. class LoadableClangRecordTypeInfo final : public StructTypeInfoBase { + const clang::RecordDecl *ClangDecl; + + public: + AddressOnlyClangRecordTypeInfo(ArrayRef fields, + llvm::Type *storageType, Size size, + Alignment align, + const clang::RecordDecl *clangDecl) + : StructTypeInfoBase(StructTypeInfoKind::AddressOnlyClangRecordTypeInfo, + fields, storageType, size, + // We can't assume any spare bits in a C++ type + // with user-defined special member functions. + SpareBitVector(llvm::Optional{ + llvm::APInt(size.getValueInBits(), 0)}), + align, IsPOD, IsNotBitwiseTakable, IsFixedSize), + ClangDecl(clangDecl) { + (void)ClangDecl; + } + + TypeLayoutEntry *buildTypeLayoutEntry(IRGenModule &IGM, + SILType T) const override { + return IGM.typeLayoutCache.getOrCreateScalarEntry(*this, T); + } + + void initializeFromParams(IRGenFunction &IGF, Explosion ¶ms, + Address addr, SILType T, + bool isOutlined) const override { + llvm_unreachable("Address-only C++ types must be created by C++ special " + "member functions."); + } + + llvm::NoneType getNonFixedOffsets(IRGenFunction &IGF) const { return None; } + llvm::NoneType getNonFixedOffsets(IRGenFunction &IGF, SILType T) const { + return None; + } + MemberAccessStrategy + getNonFixedFieldAccessStrategy(IRGenModule &IGM, SILType T, + const ClangFieldInfo &field) const { + llvm_unreachable("non-fixed field in Clang type?"); + } + }; + /// A type implementation for loadable struct types. class LoadableStructTypeInfo final : public StructTypeInfoBase { @@ -680,6 +731,10 @@ class ClangRecordLowering { const TypeInfo *createTypeInfo(llvm::StructType *llvmType) { llvmType->setBody(LLVMFields, /*packed*/ true); + if (SwiftType.getStructOrBoundGenericStruct()->isCxxNonTrivial()) { + return AddressOnlyClangRecordTypeInfo::create( + FieldInfos, llvmType, TotalStride, TotalAlignment, ClangDecl); + } return LoadableClangRecordTypeInfo::create(FieldInfos, NextExplosionIndex, llvmType, TotalStride, std::move(SpareBits), TotalAlignment, @@ -773,7 +828,7 @@ class ClangRecordLowering { // If we have a Swift import of this type, use our lowered information. if (swiftField) { - auto &fieldTI = cast(IGM.getTypeInfo( + auto &fieldTI = cast(IGM.getTypeInfo( SwiftType.getFieldType(swiftField, IGM.getSILModule(), IGM.getMaximalTypeExpansionContext()))); addField(swiftField, offset, fieldTI); @@ -812,7 +867,7 @@ class ClangRecordLowering { /// Add storage for an (optional) Swift field at the given offset. void addField(VarDecl *swiftField, Size offset, - const LoadableTypeInfo &fieldType) { + const FixedTypeInfo &fieldType) { assert(offset >= NextOffset && "adding fields out of order"); // Add a padding field if required. @@ -823,8 +878,11 @@ class ClangRecordLowering { } /// Add information to track a value field at the current offset. - void addFieldInfo(VarDecl *swiftField, const LoadableTypeInfo &fieldType) { - unsigned explosionSize = fieldType.getExplosionSize(); + void addFieldInfo(VarDecl *swiftField, const FixedTypeInfo &fieldType) { + bool isLoadableField = isa(fieldType); + unsigned explosionSize = 0; + if (isLoadableField) + explosionSize = cast(fieldType).getExplosionSize(); unsigned explosionBegin = NextExplosionIndex; NextExplosionIndex += explosionSize; unsigned explosionEnd = NextExplosionIndex; @@ -838,9 +896,12 @@ class ClangRecordLowering { layout.completeFixed(fieldType.isPOD(ResilienceExpansion::Maximal), NextOffset, LLVMFields.size()); - FieldInfos.push_back( - ClangFieldInfo(swiftField, layout, explosionBegin, explosionEnd)); - + if (isLoadableField) + FieldInfos.push_back( + ClangFieldInfo(swiftField, layout, explosionBegin, explosionEnd)); + else + FieldInfos.push_back(ClangFieldInfo(swiftField, layout, fieldType)); + if (!isEmpty) { LLVMFields.push_back(fieldType.getStorageType()); NextOffset += fieldType.getFixedSize(); @@ -862,22 +923,26 @@ class ClangRecordLowering { /// A convenient macro for delegating an operation to all of the /// various struct implementations. -#define FOR_STRUCT_IMPL(IGF, type, op, ...) do { \ - auto &structTI = IGF.getTypeInfo(type); \ - switch (getStructTypeInfoKind(structTI)) { \ - case StructTypeInfoKind::LoadableClangRecordTypeInfo: \ - return structTI.as().op(IGF, __VA_ARGS__); \ - case StructTypeInfoKind::LoadableStructTypeInfo: \ - return structTI.as().op(IGF, __VA_ARGS__); \ - case StructTypeInfoKind::FixedStructTypeInfo: \ - return structTI.as().op(IGF, __VA_ARGS__); \ - case StructTypeInfoKind::NonFixedStructTypeInfo: \ - return structTI.as().op(IGF, __VA_ARGS__); \ - case StructTypeInfoKind::ResilientStructTypeInfo: \ - llvm_unreachable("resilient structs are opaque"); \ - } \ - llvm_unreachable("bad struct type info kind!"); \ -} while (0) +#define FOR_STRUCT_IMPL(IGF, type, op, ...) \ + do { \ + auto &structTI = IGF.getTypeInfo(type); \ + switch (getStructTypeInfoKind(structTI)) { \ + case StructTypeInfoKind::LoadableClangRecordTypeInfo: \ + return structTI.as().op(IGF, __VA_ARGS__); \ + case StructTypeInfoKind::AddressOnlyClangRecordTypeInfo: \ + return structTI.as().op(IGF, \ + __VA_ARGS__); \ + case StructTypeInfoKind::LoadableStructTypeInfo: \ + return structTI.as().op(IGF, __VA_ARGS__); \ + case StructTypeInfoKind::FixedStructTypeInfo: \ + return structTI.as().op(IGF, __VA_ARGS__); \ + case StructTypeInfoKind::NonFixedStructTypeInfo: \ + return structTI.as().op(IGF, __VA_ARGS__); \ + case StructTypeInfoKind::ResilientStructTypeInfo: \ + llvm_unreachable("resilient structs are opaque"); \ + } \ + llvm_unreachable("bad struct type info kind!"); \ + } while (0) Address irgen::projectPhysicalStructMemberAddress(IRGenFunction &IGF, Address base, diff --git a/test/Interop/Cxx/class/Inputs/type-classification.h b/test/Interop/Cxx/class/Inputs/type-classification.h index 785c5ef278a73..09322bb5d0d1e 100644 --- a/test/Interop/Cxx/class/Inputs/type-classification.h +++ b/test/Interop/Cxx/class/Inputs/type-classification.h @@ -155,4 +155,22 @@ struct StructDeletedDestructor { ~StructDeletedDestructor() = delete; }; +struct StructWithCopyConstructorAndValue { + int value; + StructWithCopyConstructorAndValue( + const StructWithCopyConstructorAndValue &other) + : value(other.value) {} +}; + +struct StructWithSubobjectCopyConstructorAndValue { + StructWithCopyConstructorAndValue member; +}; + +struct StructWithCopyConstructorAndSubobjectCopyConstructorAndValue { + StructWithCopyConstructorAndValue member; + StructWithCopyConstructorAndSubobjectCopyConstructorAndValue( + const StructWithCopyConstructorAndSubobjectCopyConstructorAndValue &other) + : member(other.member) {} +}; + #endif diff --git a/test/Interop/Cxx/class/type-classification-non-trivial-irgen.swift b/test/Interop/Cxx/class/type-classification-non-trivial-irgen.swift new file mode 100644 index 0000000000000..fcbf32cff73d5 --- /dev/null +++ b/test/Interop/Cxx/class/type-classification-non-trivial-irgen.swift @@ -0,0 +1,99 @@ +// RUN: %target-swift-frontend -enable-cxx-interop -I %S/Inputs %s -emit-ir | %FileCheck %s + +// Verify that non-trival/address-only C++ classes are constructed and accessed +// correctly. Make sure that we correctly IRGen functions that construct +// non-trivial C++ classes, take those classes as a parameter, and access those +// classes members. + +import TypeClassification + +// TODO: C++ objects with destructors should be tested here once we fully +// support them. + +// CHECK-LABEL: define {{.*}}i1 @"$s4main37testStructWithCopyConstructorAndValueSbyF" +// CHECK: [[OBJ:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV +// CHECK: [[VAL_ELEMENT:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[OBJ]], i32 0, i32 0 +// CHECK: [[VAL_INT:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[VAL_ELEMENT]], i32 0, i32 0 +// CHECK: store i32 42, i32* [[VAL_INT]] +// CHECK: ret i1 true +public func testStructWithCopyConstructorAndValue() -> Bool { + let obj = StructWithCopyConstructorAndValue(value: 42) + return obj.value == 42 +} + +// CHECK-LABEL: define {{.*}}i1 @"$s4main46testStructWithSubobjectCopyConstructorAndValueSbyF"() +// CHECK: [[MEMBER:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV +// CHECK: [[OBJ:%.*]] = alloca %TSo42StructWithSubobjectCopyConstructorAndValueV +// CHECK: alloca %TSo33StructWithCopyConstructorAndValueV +// CHECK: [[TMP:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV +// CHECK: [[MEMBER_ELEMENT:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[MEMBER]], i32 0, i32 0 +// CHECK: [[MEMBER_VALUE:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[MEMBER_ELEMENT]], i32 0, i32 0 +// CHECK: store i32 42, i32* [[MEMBER_VALUE]] +// CHECK: %obj.member = getelementptr inbounds %TSo42StructWithSubobjectCopyConstructorAndValueV, %TSo42StructWithSubobjectCopyConstructorAndValueV* [[OBJ]], i32 0, i32 0 +// CHECK: [[TEMP_MEMBER:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[TMP]], i32 0, i32 0 +// CHECK: [[TEMP_MEMBER_VALUE:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[TEMP_MEMBER]], i32 0, i32 0 +// CHECK: [[LHS:%.*]] = load i32, i32* [[TEMP_MEMBER_VALUE]] +// CHECK: [[OUT:%.*]] = icmp eq i32 [[LHS]], 42 +// CHECK: ret i1 [[OUT]] +public func testStructWithSubobjectCopyConstructorAndValue() -> Bool { + let member = StructWithCopyConstructorAndValue(value: 42) + let obj = StructWithSubobjectCopyConstructorAndValue(member: member) + return obj.member.value == 42 +} + +// CHECK-LABEL: define {{.*}}i1 @"$s4main041testStructWithCopyConstructorAndSubobjectefG5ValueSbyF"() +// CHECK: [[MEMBER:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV +// CHECK: alloca %TSo037StructWithCopyConstructorAndSubobjectcdE5ValueV +// CHECK: alloca %TSo33StructWithCopyConstructorAndValueV +// CHECK: [[TEMP:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV +// CHECK: [[MEMBER_VAL:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[MEMBER]], i32 0, i32 0 +// CHECK: [[MEMBER_VAL_VAL:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[MEMBER_VAL]], i32 0, i32 0 +// CHECK: store i32 42, i32* [[MEMBER_VAL_VAL]] +// CHECK: [[TEMP_MEMBER:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[TEMP]], i32 0, i32 0 +// CHECK: [[TEMP_MEMBER_VAL:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[TEMP_MEMBER]], i32 0, i32 0 +// CHECK: [[LHS:%.*]] = load i32, i32* [[TEMP_MEMBER_VAL]] +// CHECK: [[OUT:%.*]] = icmp eq i32 [[LHS]], 42 +// CHECK: ret i1 [[OUT]] +public func testStructWithCopyConstructorAndSubobjectCopyConstructorAndValue() +-> Bool { + let member = StructWithCopyConstructorAndValue(value: 42) + let obj = StructWithCopyConstructorAndSubobjectCopyConstructorAndValue( + member: member + ) + return obj.member.value == 42 +} + +// CHECK-LABEL: define {{.*}}i1 @"$s4main4test3objSbSo33StructWithCopyConstructorAndValueV_tF"(%TSo33StructWithCopyConstructorAndValueV* noalias nocapture dereferenceable(4) %0) +// CHECK: [[VAL:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* %0, i32 0, i32 0 +// CHECK: [[VAL_VAL:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[VAL]], i32 0, i32 0 +// CHECK: [[LHS:%.*]] = load i32, i32* [[VAL_VAL]] +// CHECK: [[OUT:%.*]] = icmp eq i32 %1, 42 +// CHECK: ret i1 [[OUT]] +public func test(obj: StructWithCopyConstructorAndValue) -> Bool { + return obj.value == 42 +} + +// CHECK-LABEL: define {{.*}}i1 @"$s4main4test3objSbSo42StructWithSubobjectCopyConstructorAndValueV_tF"(%TSo42StructWithSubobjectCopyConstructorAndValueV* noalias nocapture dereferenceable(4) %0) +// CHECK: [[TMP:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV +// CHECK: [[MEMBER:%.*]] = getelementptr inbounds %TSo42StructWithSubobjectCopyConstructorAndValueV, %TSo42StructWithSubobjectCopyConstructorAndValueV* %0, i32 0, i32 0 +// CHECK: [[VAL:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[TMP]], i32 0, i32 0 +// CHECK: [[VAL_VAL:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[VAL]], i32 0, i32 0 +// CHECK: [[LHS:%.*]] = load i32, i32* [[VAL_VAL]] +// CHECK: [[OUT:%.*]] = icmp eq i32 [[LHS]], 42 +// CHECK: ret i1 [[OUT]] +public func test(obj: StructWithSubobjectCopyConstructorAndValue) -> Bool { + return obj.member.value == 42 +} + +// CHECK-LABEL: define {{.*}}i1 @"$s4main4test3objSbSo037StructWithCopyConstructorAndSubobjectfgH5ValueV_tF"(%TSo037StructWithCopyConstructorAndSubobjectcdE5ValueV* noalias nocapture dereferenceable(4) %0) +// CHECK:[[TEMP:%.*]] = alloca %TSo33StructWithCopyConstructorAndValueV +// CHECK:[[VAL:%.*]] = getelementptr inbounds %TSo33StructWithCopyConstructorAndValueV, %TSo33StructWithCopyConstructorAndValueV* [[TEMP]], i32 0, i32 0 +// CHECK:[[VAL_VAL:%.*]] = getelementptr inbounds %Ts5Int32V, %Ts5Int32V* [[VAL]], i32 0, i32 0 +// CHECK:[[LHS:%.*]] = load i32, i32* [[VAL_VAL]] +// CHECK:[[OUT:%.*]] = icmp eq i32 [[LHS]], 42 +// CHECK:ret i1 [[OUT]] +public func test( + obj: StructWithCopyConstructorAndSubobjectCopyConstructorAndValue +) -> Bool { + return obj.member.value == 42 +} diff --git a/test/Interop/Cxx/class/type-classification-non-trivial-silgen.swift b/test/Interop/Cxx/class/type-classification-non-trivial-silgen.swift index 3ed07447a4cfe..87766b5a01bdb 100644 --- a/test/Interop/Cxx/class/type-classification-non-trivial-silgen.swift +++ b/test/Interop/Cxx/class/type-classification-non-trivial-silgen.swift @@ -1,25 +1,212 @@ -// RUN: %target-swift-frontend -I %S/Inputs -enable-cxx-interop -emit-sil %s | %FileCheck %s +// RUN: %target-swift-frontend -I %S/Inputs -enable-cxx-interop -emit-silgen %s | %FileCheck %s import TypeClassification // Make sure that "StructWithDestructor" is marked as non-trivial by checking for a // "destroy_addr". -// CHECK-LABEL: @$s4main24testStructWithDestructoryyF +// CHECK-LABEL: sil [ossa] @$s4main24testStructWithDestructoryyF // CHECK: [[AS:%.*]] = alloc_stack $StructWithDestructor +// CHECK: [[META:%.*]] = metatype $@thin StructWithDestructor.Type +// CHECK: [[FN:%.*]] = function_ref @$sSo20StructWithDestructorVABycfC : $@convention(method) (@thin StructWithDestructor.Type) -> @out StructWithDestructor +// CHECK: apply [[FN]]([[AS]], [[META]]) : $@convention(method) (@thin StructWithDestructor.Type) -> @out StructWithDestructor // CHECK: destroy_addr [[AS]] +// CHECK: dealloc_stack %0 : $*StructWithDestructor // CHECK-LABEL: end sil function '$s4main24testStructWithDestructoryyF' public func testStructWithDestructor() { let d = StructWithDestructor() } -// Make sure that "StructWithSubobjectDestructor" is marked as non-trivial by checking +// StructWithDestructor.init() +// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo20StructWithDestructorVABycfC : $@convention(method) (@thin StructWithDestructor.Type) -> @out StructWithDestructor +// CHECK: [[BOX:%.*]] = alloc_box ${ var StructWithDestructor } +// CHECK: [[UBOX:%.*]] = mark_uninitialized [rootself] [[BOX]] : ${ var StructWithDestructor } +// CHECK: [[BOX_ADDR:%.*]] = project_box [[UBOX]] : ${ var StructWithDestructor }, 0 +// CHECK: [[SA:%.*]] = alloc_stack $StructWithDestructor +// CHECK: builtin "zeroInitializer"([[SA]] : $*StructWithDestructor) : $() +// CHECK: [[BA:%.*]] = begin_access [modify] [unknown] [[BOX_ADDR]] : $*StructWithDestructor +// CHECK: copy_addr [take] [[SA]] to [[BA]] : $*StructWithDestructor +// CHECK: copy_addr [[BOX_ADDR]] to [initialization] %0 : $*StructWithDestructor +// CHECK: destroy_value [[UBOX]] : ${ var StructWithDestructor } +// CHECK-LABEL: end sil function '$sSo20StructWithDestructorVABycfC' + +// Make sure that "HasMemberWithDestructor" is marked as non-trivial by checking // for a "destroy_addr". -// CHECK-LABEL: @$s4main33testStructWithSubobjectDestructoryyF +// CHECK-LABEL: sil [ossa] @$s4main33testStructWithSubobjectDestructoryyF // CHECK: [[AS:%.*]] = alloc_stack $StructWithSubobjectDestructor +// CHECK: [[META:%.*]] = metatype $@thin StructWithSubobjectDestructor.Type +// CHECK: [[FN:%.*]] = function_ref @$sSo29StructWithSubobjectDestructorVABycfC : $@convention(method) (@thin StructWithSubobjectDestructor.Type) -> @out StructWithSubobjectDestructor +// CHECK: apply [[FN]]([[AS]], [[META]]) : $@convention(method) (@thin StructWithSubobjectDestructor.Type) -> @out StructWithSubobjectDestructor // CHECK: destroy_addr [[AS]] // CHECK-LABEL: end sil function '$s4main33testStructWithSubobjectDestructoryyF' public func testStructWithSubobjectDestructor() { let d = StructWithSubobjectDestructor() } +// StructWithSubobjectDestructor.init() +// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo29StructWithSubobjectDestructorVABycfC : $@convention(method) (@thin StructWithSubobjectDestructor.Type) -> @out StructWithSubobjectDestructor +// CHECK: [[BOX:%.*]] = alloc_box ${ var StructWithSubobjectDestructor } +// CHECK: [[UBOX:%.*]] = mark_uninitialized [rootself] [[BOX]] : ${ var StructWithSubobjectDestructor } +// CHECK: [[ADDR:%.*]] = project_box [[UBOX]] : ${ var StructWithSubobjectDestructor }, 0 +// CHECK: [[AS:%.*]] = alloc_stack $StructWithSubobjectDestructor +// CHECK: builtin "zeroInitializer"([[AS]] : $*StructWithSubobjectDestructor) : $() +// CHECK: [[BA:%.*]] = begin_access [modify] [unknown] [[ADDR]] : $*StructWithSubobjectDestructor +// CHECK: copy_addr [take] [[AS]] to [[BA]] : $*StructWithSubobjectDestructor +// CHECK: copy_addr [[ADDR]] to [initialization] %0 : $*StructWithSubobjectDestructor +// CHECK: destroy_value [[UBOX]] : ${ var StructWithSubobjectDestructor } +// CHECK-LABEL: end sil function '$sSo29StructWithSubobjectDestructorVABycfC' + +// CHECK-LABLE: sil [ossa] @$s4main37testStructWithCopyConstructorAndValueSbyF +// CHECK: [[AS:%.*]] = alloc_stack $StructWithCopyConstructorAndValue +// CHECK: [[META:%.*]] = metatype $@thin StructWithCopyConstructorAndValue.Type +// CHECK: [[FN:%.*]] = function_ref @$sSo33StructWithCopyConstructorAndValueV5valueABs5Int32V_tcfC : $@convention(method) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue +// CHECK: apply [[FN]]([[AS]], %{{.*}}, [[META]]) : $@convention(method) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue +// CHECK: [[OBJ_VAL_ADDR:%.*]] = struct_element_addr [[AS]] : $*StructWithCopyConstructorAndValue, #StructWithCopyConstructorAndValue.value +// CHECK: [[OBJ_VAL:%.*]] = load [trivial] [[OBJ_VAL_ADDR]] : $*Int32 +// CHECK: [[IL_42:%.*]] = integer_literal $Builtin.IntLiteral, 42 +// CHECK: [[MAKE_INT_FN:%.*]] = function_ref @$ss5Int32V22_builtinIntegerLiteralABBI_tcfC : $@convention(method) (Builtin.IntLiteral, @thin Int32.Type) -> Int32 +// CHECK: [[INT_42:%.*]] = apply [[MAKE_INT_FN]]([[IL_42]], %{{.*}}) : $@convention(method) (Builtin.IntLiteral, @thin Int32.Type) -> Int32 +// CHECK: [[CMP_FN:%.*]] = function_ref @$ss5Int32V2eeoiySbAB_ABtFZ : $@convention(method) (Int32, Int32, @thin Int32.Type) -> Bool +// CHECK: [[OUT:%.*]] = apply [[CMP_FN]]([[OBJ_VAL]], [[INT_42]], %{{.*}}) : $@convention(method) (Int32, Int32, @thin Int32.Type) -> Bool +// CHECK: destroy_addr [[AS]] : $*StructWithCopyConstructorAndValue +// CHECK: return [[OUT]] : $Bool +// CHECK-LABLE: end sil function '$s4main37testStructWithCopyConstructorAndValueSbyF' +public func testStructWithCopyConstructorAndValue() -> Bool { + let obj = StructWithCopyConstructorAndValue(value: 42) + return obj.value == 42 +} + +// StructWithCopyConstructorAndValue.init(value:) +// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo33StructWithCopyConstructorAndValueV5valueABs5Int32V_tcfC : $@convention(method) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue +// CHECK: [[VAL:%.*]] = struct_element_addr %0 : $*StructWithCopyConstructorAndValue, #StructWithCopyConstructorAndValue.value +// CHECK: store %1 to [trivial] [[VAL]] : $*Int32 +// CHECK-LABEL: end sil function '$sSo33StructWithCopyConstructorAndValueV5valueABs5Int32V_tcfC' + +// CHECK-LABEL: sil [ossa] @$s4main46testStructWithSubobjectCopyConstructorAndValueSbyF : $@convention(thin) () -> Bool +// CHECK: [[MEMBER_0:%.*]] = alloc_stack $StructWithCopyConstructorAndValue +// CHECK: [[MEMBER_META:%.*]] = metatype $@thin StructWithCopyConstructorAndValue.Type +// CHECK: [[MAKE_MEMBER_FN:%.*]] = function_ref @$sSo33StructWithCopyConstructorAndValueV5valueABs5Int32V_tcfC : $@convention(method) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue +// CHECK: apply [[MAKE_MEMBER_FN]]([[MEMBER_0]], %{{.*}}, [[MEMBER_META]]) : $@convention(method) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue +// CHECK: [[AS:%.*]] = alloc_stack $StructWithSubobjectCopyConstructorAndValue +// CHECK: [[META:%.*]] = metatype $@thin StructWithSubobjectCopyConstructorAndValue.Type +// CHECK: [[MEMBER_1:%.*]] = alloc_stack $StructWithCopyConstructorAndValue +// CHECK: copy_addr %0 to [initialization] [[MEMBER_1]] : $*StructWithCopyConstructorAndValue +// CHECK: [[FN:%.*]] = function_ref @$sSo42StructWithSubobjectCopyConstructorAndValueV6memberABSo0abdefG0V_tcfC : $@convention(method) (@in StructWithCopyConstructorAndValue, @thin StructWithSubobjectCopyConstructorAndValue.Type) -> @out StructWithSubobjectCopyConstructorAndValue +// CHECK: apply [[FN]]([[AS]], [[MEMBER_1]], [[META]]) : $@convention(method) (@in StructWithCopyConstructorAndValue, @thin StructWithSubobjectCopyConstructorAndValue.Type) -> @out StructWithSubobjectCopyConstructorAndValue +// CHECK: [[OBJ_MEMBER:%.*]] = struct_element_addr [[AS]] : $*StructWithSubobjectCopyConstructorAndValue, #StructWithSubobjectCopyConstructorAndValue.member +// CHECK: [[MEMBER_2:%.*]] = alloc_stack $StructWithCopyConstructorAndValue +// CHECK: copy_addr [[OBJ_MEMBER]] to [initialization] [[MEMBER_2]] : $*StructWithCopyConstructorAndValue +// CHECK: [[OBJ_VALUE_ADDR:%.*]] = struct_element_addr [[MEMBER_2]] : $*StructWithCopyConstructorAndValue, #StructWithCopyConstructorAndValue.value +// CHECK: [[OBJ_VALUE:%.*]] = load [trivial] [[OBJ_VALUE_ADDR]] : $*Int32 +// CHECK: [[MAKE_INT:%.*]] = function_ref @$ss5Int32V22_builtinIntegerLiteralABBI_tcfC : $@convention(method) (Builtin.IntLiteral, @thin Int32.Type) -> Int32 +// CHECK: [[INT_42:%.*]] = apply [[MAKE_INT]]({{.*}}) : $@convention(method) (Builtin.IntLiteral, @thin Int32.Type) -> Int32 +// CHECK: [[ICMP:%.*]] = function_ref @$ss5Int32V2eeoiySbAB_ABtFZ : $@convention(method) (Int32, Int32, @thin Int32.Type) -> Bool +// CHECK: [[OUT:%.*]] = apply [[ICMP]]([[OBJ_VALUE]], [[INT_42]], %{{.*}}) : $@convention(method) (Int32, Int32, @thin Int32.Type) -> Bool +// CHECK: return [[OUT]] : $Bool +// CHECK-LABEL: end sil function '$s4main46testStructWithSubobjectCopyConstructorAndValueSbyF' +public func testStructWithSubobjectCopyConstructorAndValue() -> Bool { + let member = StructWithCopyConstructorAndValue(value: 42) + let obj = StructWithSubobjectCopyConstructorAndValue(member: member) + return obj.member.value == 42 +} + +// StructWithSubobjectCopyConstructorAndValue.init(member:) +// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo42StructWithSubobjectCopyConstructorAndValueV6memberABSo0abdefG0V_tcfC : $@convention(method) (@in StructWithCopyConstructorAndValue, @thin StructWithSubobjectCopyConstructorAndValue.Type) -> @out StructWithSubobjectCopyConstructorAndValue +// CHECK: [[MEMBER:%.*]] = struct_element_addr %0 : $*StructWithSubobjectCopyConstructorAndValue, #StructWithSubobjectCopyConstructorAndValue.member +// CHECK: copy_addr [take] %1 to [initialization] [[MEMBER]] : $*StructWithCopyConstructorAndValue +// CHECK-LABEL: end sil function '$sSo42StructWithSubobjectCopyConstructorAndValueV6memberABSo0abdefG0V_tcfC' +// testStructWithCopyConstructorAndSubobjectCopyConstructorAndValue() +// CHECK-LABEL: sil [ossa] @$s4main041testStructWithCopyConstructorAndSubobjectefG5ValueSbyF : $@convention(thin) () -> Bool +// CHECK: [[MEMBER_0:%.*]] = alloc_stack $StructWithCopyConstructorAndValue +// CHECK: [[META_MEMBER:%.*]] = metatype $@thin StructWithCopyConstructorAndValue.Type +// CHECK: [[CREATE_MEMBER_FN:%.*]] = function_ref @$sSo33StructWithCopyConstructorAndValueV5valueABs5Int32V_tcfC : $@convention(method) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue +// CHECK: apply [[CREATE_MEMBER_FN]]([[MEMBER_0]], %{{.*}}, [[META_MEMBER]]) : $@convention(method) (Int32, @thin StructWithCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndValue +// CHECK: [[AS:%.*]] = alloc_stack $StructWithCopyConstructorAndSubobjectCopyConstructorAndValue +// CHECK: [[META:%.*]] = metatype $@thin StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.Type +// CHECK: [[MEMBER_1:%.*]] = alloc_stack $StructWithCopyConstructorAndValue +// CHECK: copy_addr [[MEMBER_0]] to [initialization] [[MEMBER_1]] : $*StructWithCopyConstructorAndValue +// CHECK: [[FN:%.*]] = function_ref @$sSo037StructWithCopyConstructorAndSubobjectcdE5ValueV6memberABSo0abcdeG0V_tcfC : $@convention(method) (@in StructWithCopyConstructorAndValue, @thin StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndSubobjectCopyConstructorAndValue +// CHECK: apply [[FN]]([[AS]], [[MEMBER_1]], [[META]]) : $@convention(method) (@in StructWithCopyConstructorAndValue, @thin StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndSubobjectCopyConstructorAndValue +// CHECK: [[OBJ_MEMBER_ADDR:%.*]] = struct_element_addr [[AS]] : $*StructWithCopyConstructorAndSubobjectCopyConstructorAndValue, #StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.member +// CHECK: [[MEMBER_2:%.*]] = alloc_stack $StructWithCopyConstructorAndValue +// CHECK: copy_addr [[OBJ_MEMBER_ADDR]] to [initialization] [[MEMBER_2]] : $*StructWithCopyConstructorAndValue +// CHECK: [[OBJ_MEMBER_VALUE_ADDR:%.*]] = struct_element_addr [[MEMBER_2]] : $*StructWithCopyConstructorAndValue, #StructWithCopyConstructorAndValue.value +// CHECK: [[OBJ_MEMBER_VALUE:%.*]] = load [trivial] [[OBJ_MEMBER_VALUE_ADDR]] : $*Int32 +// CHECK: [[ICMP:%.*]] = function_ref @$ss5Int32V2eeoiySbAB_ABtFZ : $@convention(method) (Int32, Int32, @thin Int32.Type) -> Bool +// CHECK: [[OUT:%.*]] = apply [[ICMP]]([[OBJ_MEMBER_VALUE]], %{{.*}}) : $@convention(method) (Int32, Int32, @thin Int32.Type) -> Bool +// CHECK: return [[OUT]] : $Bool +// CHECK-LABEL: end sil function '$s4main041testStructWithCopyConstructorAndSubobjectefG5ValueSbyF' +public func testStructWithCopyConstructorAndSubobjectCopyConstructorAndValue() +-> Bool { + let member = StructWithCopyConstructorAndValue(value: 42) + let obj = StructWithCopyConstructorAndSubobjectCopyConstructorAndValue( + member: member + ) + return obj.member.value == 42 +} + +// StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.init(member:) +// CHECK-LABEL: sil shared [transparent] [serializable] [ossa] @$sSo037StructWithCopyConstructorAndSubobjectcdE5ValueV6memberABSo0abcdeG0V_tcfC : $@convention(method) (@in StructWithCopyConstructorAndValue, @thin StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.Type) -> @out StructWithCopyConstructorAndSubobjectCopyConstructorAndValue +// CHECK: [[MEMBER:%.*]] = struct_element_addr %0 : $*StructWithCopyConstructorAndSubobjectCopyConstructorAndValue, #StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.member +// CHECK: copy_addr [take] %1 to [initialization] [[MEMBER]] : $*StructWithCopyConstructorAndValue +// CHECK-LABEL: end sil function '$sSo037StructWithCopyConstructorAndSubobjectcdE5ValueV6memberABSo0abcdeG0V_tcfC' + +// CHECK-LABEL: sil [ossa] @$s4main4test3objSbSo33StructWithCopyConstructorAndValueV_tF : $@convention(thin) (@in_guaranteed StructWithCopyConstructorAndValue) -> Bool +// CHECK: [[META_1:%.*]] = metatype $@thin Int32.Type +// CHECK: [[OBJ_VAL_ADDR:%.*]] = struct_element_addr %0 : $*StructWithCopyConstructorAndValue, #StructWithCopyConstructorAndValue.value +// CHECK: [[OBJ_VAL:%.*]] = load [trivial] [[OBJ_VAL_ADDR]] : $*Int32 +// CHECK: [[IL_42:%.*]] = integer_literal $Builtin.IntLiteral, 42 +// CHECK: [[META_2:%.*]] = metatype $@thin Int32.Type +// CHECK: [[FN:%.*]] = function_ref @$ss5Int32V22_builtinIntegerLiteralABBI_tcfC : $@convention(method) (Builtin.IntLiteral, @thin Int32.Type) -> Int32 +// CHECK: [[INT:%.*]] = apply [[FN]]([[IL_42]], [[META_2]]) : $@convention(method) (Builtin.IntLiteral, @thin Int32.Type) -> Int32 +// CHECK: [[FN_2:%.*]] = function_ref @$ss5Int32V2eeoiySbAB_ABtFZ : $@convention(method) (Int32, Int32, @thin Int32.Type) -> Bool +// CHECK: [[OUT:%.*]] = apply [[FN_2]]([[OBJ_VAL]], [[INT]], [[META_1]]) : $@convention(method) (Int32, Int32, @thin Int32.Type) -> Bool +// CHECK: return [[OUT]] : $Bool +// CHECK-LABEL: end sil function '$s4main4test3objSbSo33StructWithCopyConstructorAndValueV_tF' +public func test(obj: StructWithCopyConstructorAndValue) -> Bool { + return obj.value == 42 +} + +// CHECK-LABEL: sil [ossa] @$s4main4test3objSbSo42StructWithSubobjectCopyConstructorAndValueV_tF : $@convention(thin) (@in_guaranteed StructWithSubobjectCopyConstructorAndValue) -> Bool +// CHECK: [[INT_META:%.*]] = metatype $@thin Int32.Type +// CHECK: [[OBJ_MEMBER:%.*]] = struct_element_addr %0 : $*StructWithSubobjectCopyConstructorAndValue, #StructWithSubobjectCopyConstructorAndValue.member +// CHECK: [[MEMBER_TMP:%.*]] = alloc_stack $StructWithCopyConstructorAndValue +// CHECK: copy_addr [[OBJ_MEMBER]] to [initialization] [[MEMBER_TMP]] : $*StructWithCopyConstructorAndValue +// CHECK: [[OBJ_MEMBER_VALUE_ADDR:%.*]] = struct_element_addr [[MEMBER_TMP]] : $*StructWithCopyConstructorAndValue, #StructWithCopyConstructorAndValue.value +// CHECK: [[OBJ_MEMBER_VALUE:%.*]] = load [trivial] [[OBJ_MEMBER_VALUE_ADDR]] : $*Int32 +// CHECK: destroy_addr [[MEMBER_TMP]] : $*StructWithCopyConstructorAndValue +// CHECK: [[IL_42:%.*]] = integer_literal $Builtin.IntLiteral, 42 +// CHECK: [[INT_META_2:%.*]] = metatype $@thin Int32.Type +// CHECK: [[MAKE_INT_FN:%.*]] = function_ref @$ss5Int32V22_builtinIntegerLiteralABBI_tcfC : $@convention(method) (Builtin.IntLiteral, @thin Int32.Type) -> Int32 +// CHECK: [[INT_42:%.*]] = apply [[MAKE_INT_FN]]([[IL_42]], [[INT_META_2]]) : $@convention(method) (Builtin.IntLiteral, @thin Int32.Type) -> Int32 +// CHECK: [[FN:%.*]] = function_ref @$ss5Int32V2eeoiySbAB_ABtFZ : $@convention(method) (Int32, Int32, @thin Int32.Type) -> Bool +// CHECK: [[OUT:%.*]] = apply [[FN]]([[OBJ_MEMBER_VALUE]], [[INT_42]], [[INT_META]]) : $@convention(method) (Int32, Int32, @thin Int32.Type) -> Bool +// CHECK: dealloc_stack [[MEMBER_TMP]] : $*StructWithCopyConstructorAndValue +// CHECK: return [[OUT]] : $Bool +// CHECK-LABEL: end sil function '$s4main4test3objSbSo42StructWithSubobjectCopyConstructorAndValueV_tF' +public func test(obj: StructWithSubobjectCopyConstructorAndValue) -> Bool { + return obj.member.value == 42 +} + +// CHECK-LABEL: sil [ossa] @$s4main4test3objSbSo037StructWithCopyConstructorAndSubobjectfgH5ValueV_tF +// CHECK: [[META_INT_1:%.*]] = metatype $@thin Int32.Type +// CHECK: [[OBJ_MEMBER:%.*]] = struct_element_addr %0 : $*StructWithCopyConstructorAndSubobjectCopyConstructorAndValue, #StructWithCopyConstructorAndSubobjectCopyConstructorAndValue.member +// CHECK: [[MEMBER_TMP:%.*]] = alloc_stack $StructWithCopyConstructorAndValue +// CHECK: copy_addr [[OBJ_MEMBER]] to [initialization] [[MEMBER_TMP]] : $*StructWithCopyConstructorAndValue +// CHECK: [[OBJ_MEMBER_VAL_ADDR:%.*]] = struct_element_addr [[MEMBER_TMP]] : $*StructWithCopyConstructorAndValue, #StructWithCopyConstructorAndValue.value +// CHECK: [[OBJ_MEMBER_VAL:%.*]] = load [trivial] [[OBJ_MEMBER_VAL_ADDR]] : $*Int32 +// CHECK: destroy_addr [[MEMBER_TMP]] : $*StructWithCopyConstructorAndValue +// CHECK: [[IL_42:%.*]] = integer_literal $Builtin.IntLiteral, 42 +// CHECK: [[META_INT_2:%.*]] = metatype $@thin Int32.Type +// CHECK: [[MAKE_INT_FN:%.*]] = function_ref @$ss5Int32V22_builtinIntegerLiteralABBI_tcfC : $@convention(method) (Builtin.IntLiteral, @thin Int32.Type) -> Int32 +// CHECK: [[INT_42:%.*]] = apply [[MAKE_INT_FN]]([[IL_42]], [[META_INT_2]]) : $@convention(method) (Builtin.IntLiteral, @thin Int32.Type) -> Int32 +// CHECK: [[ICMP_FN:%.*]] = function_ref @$ss5Int32V2eeoiySbAB_ABtFZ : $@convention(method) (Int32, Int32, @thin Int32.Type) -> Bool +// CHECK: [[OUT:%.*]] = apply [[ICMP_FN]]([[OBJ_MEMBER_VAL]], [[INT_42]], [[META_INT_1]]) : $@convention(method) (Int32, Int32, @thin Int32.Type) -> Bool +// CHECK: dealloc_stack [[MEMBER_TMP]] : $*StructWithCopyConstructorAndValue +// CHECK: return [[OUT]] : $Bool +// CHECK-LABEL: end sil function '$s4main4test3objSbSo037StructWithCopyConstructorAndSubobjectfgH5ValueV_tF' +public func test( + obj: StructWithCopyConstructorAndSubobjectCopyConstructorAndValue +) -> Bool { + return obj.member.value == 42 +} diff --git a/test/Interop/Cxx/class/type-classification-non-trivial.swift b/test/Interop/Cxx/class/type-classification-non-trivial.swift new file mode 100644 index 0000000000000..489e6f71c7a40 --- /dev/null +++ b/test/Interop/Cxx/class/type-classification-non-trivial.swift @@ -0,0 +1,35 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -I %S/Inputs -o %t/address_only -Xfrontend -enable-cxx-interop +// RUN: %target-codesign %t/address_only +// RUN: %target-run %t/address_only 2&>1 + +// REQUIRES: executable_test + +import TypeClassification +import StdlibUnittest + +var AddressOnlyTestSuite = TestSuite("Address Only Types") + +AddressOnlyTestSuite.test("Test struct with copy constructor") { + let obj = StructWithCopyConstructorAndValue(value: 42) + expectEqual(obj.value, 42) +} + +AddressOnlyTestSuite.test("Test struct with member with copy constructor") { + let member = StructWithCopyConstructorAndValue(value: 42) + let obj = StructWithSubobjectCopyConstructorAndValue(member: member) + expectEqual(obj.member.value, 42) +} + +AddressOnlyTestSuite.test( + "Test struct with copy constructor and member with copy constructor" +) { + let member = StructWithCopyConstructorAndValue(value: 42) + let obj = StructWithCopyConstructorAndSubobjectCopyConstructorAndValue( + member: member + ) + expectEqual(obj.member.value, 42) +} + +runAllTests() +