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() +