diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index 31fe80a118ad3..53b2afac7e026 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -16,6 +16,9 @@ #include "GenClass.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/RecordLayout.h" #include "swift/ABI/Class.h" #include "swift/ABI/MetadataValues.h" #include "swift/AST/ASTContext.h" @@ -28,6 +31,7 @@ #include "swift/AST/SemanticAttrs.h" #include "swift/AST/TypeMemberVisitor.h" #include "swift/AST/Types.h" +#include "swift/Basic/Defer.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/IRGen/Linking.h" #include "swift/SIL/SILModule.h" @@ -281,6 +285,55 @@ namespace { superclass); } + void maybeAddCxxRecordBases(ClassDecl *cd) { + auto cxxRecord = dyn_cast_or_null(cd->getClangDecl()); + if (!cxxRecord) + return; + + auto &layout = cxxRecord->getASTContext().getASTRecordLayout(cxxRecord); + + for (auto base : cxxRecord->bases()) { + if (base.isVirtual()) + continue; + + auto baseType = base.getType().getCanonicalType(); + + auto baseRecord = cast(baseType)->getDecl(); + auto baseCxxRecord = cast(baseRecord); + + if (baseCxxRecord->isEmpty()) + continue; + + auto offset = Size(layout.getBaseClassOffset(baseCxxRecord).getQuantity()); + auto size = Size(cxxRecord->getASTContext().getTypeSizeInChars(baseType).getQuantity()); + + if (offset != CurSize) { + assert(offset > CurSize); + auto paddingSize = offset - CurSize; + auto &opaqueTI = IGM.getOpaqueStorageTypeInfo(paddingSize, Alignment(1)); + auto element = ElementLayout::getIncomplete(opaqueTI); + addField(element, LayoutStrategy::Universal); + } + + auto &opaqueTI = IGM.getOpaqueStorageTypeInfo(size, Alignment(1)); + auto element = ElementLayout::getIncomplete(opaqueTI); + addField(element, LayoutStrategy::Universal); + } + } + + void addPaddingBeforeClangField(const clang::FieldDecl *fd) { + auto offset = Size(fd->getASTContext().toCharUnitsFromBits( + fd->getASTContext().getFieldOffset(fd)).getQuantity()); + + if (offset != CurSize) { + assert(offset > CurSize); + auto paddingSize = offset - CurSize; + auto &opaqueTI = IGM.getOpaqueStorageTypeInfo(paddingSize, Alignment(1)); + auto element = ElementLayout::getIncomplete(opaqueTI); + addField(element, LayoutStrategy::Universal); + } + } + void addDirectFieldsFromClass(ClassDecl *rootClass, SILType rootClassType, ClassDecl *theClass, SILType classType, bool superclass) { @@ -290,7 +343,9 @@ namespace { ->getRecursiveProperties() .hasUnboundGeneric()); - forEachField(IGM, theClass, [&](Field field) { + maybeAddCxxRecordBases(theClass); + + auto fn = [&](Field field) { // Ignore missing properties here; we should have flagged these // with the classHasIncompleteLayout call above. if (!field.isConcrete()) { @@ -325,7 +380,25 @@ namespace { AllStoredProperties.push_back(field); AllFieldAccesses.push_back(getFieldAccess(isKnownEmpty)); } - }); + }; + + auto classDecl = dyn_cast(theClass); + if (classDecl && classDecl->isRootDefaultActor()) { + fn(Field::DefaultActorStorage); + } + + for (auto decl : + theClass->getStoredPropertiesAndMissingMemberPlaceholders()) { + if (decl->getClangDecl()) + if (auto clangField = cast(decl->getClangDecl())) + addPaddingBeforeClangField(clangField); + + if (auto var = dyn_cast(decl)) { + fn(var); + } else { + fn(cast(decl)); + } + } if (!superclass) { // If we're calculating the layout of a specialized generic class type, diff --git a/test/Interop/Cxx/foreign-reference/Inputs/member-layout.h b/test/Interop/Cxx/foreign-reference/Inputs/member-layout.h new file mode 100644 index 0000000000000..a655ac54ebd87 --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/Inputs/member-layout.h @@ -0,0 +1,56 @@ +#ifndef TEST_INTEROP_CXX_FOREIGN_REFERENCE_INPUTS_NULLABLE_H +#define TEST_INTEROP_CXX_FOREIGN_REFERENCE_INPUTS_NULLABLE_H + +struct IntHolder { int value; }; + +struct + __attribute__((swift_attr("import_reference"))) + __attribute__((swift_attr("retain:immortal"))) + __attribute__((swift_attr("release:immortal"))) + IntBase : IntHolder { + int i; +}; + +struct NoDtorThreeByte { + char x; + char y; + char z; + ~NoDtorThreeByte() = delete; +}; + +struct + __attribute__((swift_attr("import_reference"))) + __attribute__((swift_attr("retain:immortal"))) + __attribute__((swift_attr("release:immortal"))) + IntCharRef { + int i; + char b; +}; + +struct + __attribute__((swift_attr("import_reference"))) + __attribute__((swift_attr("retain:immortal"))) + __attribute__((swift_attr("release:immortal"))) + IntCharValue { + int i; + char b; +}; + + +struct + __attribute__((swift_attr("import_reference"))) + __attribute__((swift_attr("retain:immortal"))) + __attribute__((swift_attr("release:immortal"))) + UnimportableMemberRef { + int z; int zz; NoDtorThreeByte x; NoDtorThreeByte xx; int y; +}; + +struct + __attribute__((swift_attr("import_reference"))) + __attribute__((swift_attr("retain:immortal"))) + __attribute__((swift_attr("release:immortal"))) + UnimportableMemberValue { + int z; int zz; NoDtorThreeByte x; NoDtorThreeByte xx; int y; +}; + +#endif // TEST_INTEROP_CXX_FOREIGN_REFERENCE_INPUTS_NULLABLE_H diff --git a/test/Interop/Cxx/foreign-reference/Inputs/module.modulemap b/test/Interop/Cxx/foreign-reference/Inputs/module.modulemap index 173238cf21711..159b2b182094a 100644 --- a/test/Interop/Cxx/foreign-reference/Inputs/module.modulemap +++ b/test/Interop/Cxx/foreign-reference/Inputs/module.modulemap @@ -27,3 +27,8 @@ module ReferenceCounted { header "reference-counted.h" requires cplusplus } + +module MemberLayout { + header "member-layout.h" + requires cplusplus +} diff --git a/test/Interop/Cxx/foreign-reference/base-class-layout-irgen.swift b/test/Interop/Cxx/foreign-reference/base-class-layout-irgen.swift new file mode 100644 index 0000000000000..6038052a5f590 --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/base-class-layout-irgen.swift @@ -0,0 +1,13 @@ +// RUN: %target-swift-emit-ir %s -I %S/Inputs -enable-experimental-cxx-interop -module-name=test | %FileCheck %s +// +// XFAIL: OS=linux-android, OS=linux-androideabi + +import MemberLayout + +// CHECK: %TSo7IntBaseV = type <{ [4 x i8], %Ts5Int32V }> + +// CHECK-LABEL: define {{.*}}swiftcc i32 @"$s4testAA1ys5Int32VSo7IntBaseV_tF"(%TSo7IntBaseV* %0) +// CHECK: getelementptr inbounds %TSo7IntBaseV, %TSo7IntBaseV* %0, i32 0, i32 1 +public func test(y: IntBase) -> CInt { + return y.i +} diff --git a/test/Interop/Cxx/foreign-reference/unimportable-member-layout-irgen.swift b/test/Interop/Cxx/foreign-reference/unimportable-member-layout-irgen.swift new file mode 100644 index 0000000000000..54ac0b27df418 --- /dev/null +++ b/test/Interop/Cxx/foreign-reference/unimportable-member-layout-irgen.swift @@ -0,0 +1,38 @@ +// RUN: %target-swift-emit-ir %s -I %S/Inputs -enable-experimental-cxx-interop -module-name=test | %FileCheck %s +// +// XFAIL: OS=linux-android, OS=linux-androideabi + +import MemberLayout + +// Make sure the "refs" and "values" look *exactly* the same. + +// CHECK: %TSo10IntCharRefV = type <{ %Ts5Int32V, %Ts4Int8V }> +// CHECK: %TSo12IntCharValueV = type <{ %Ts5Int32V, %Ts4Int8V }> + +// CHECK: %TSo21UnimportableMemberRefV = type <{ %Ts5Int32V, %Ts5Int32V, [8 x i8], %Ts5Int32V }> +// CHECK: %TSo23UnimportableMemberValueV = type <{ %Ts5Int32V, %Ts5Int32V, [8 x i8], %Ts5Int32V }> + +// CHECK-LABEL: define {{.*}}swiftcc i8 @"$s4testAA1ys4Int8VSo10IntCharRefV_tF"(%TSo10IntCharRefV* %0) +// CHECK: getelementptr inbounds %TSo10IntCharRefV, %TSo10IntCharRefV* %0, i32 0, i32 1 +public func test(y: IntCharRef) -> CChar { + return y.b +} + +// CHECK-LABEL: define {{.*}}swiftcc i8 @"$s4testAA1ys4Int8VSo12IntCharValueV_tF"(%TSo12IntCharValueV* %0) +// CHECK: getelementptr inbounds %TSo12IntCharValueV, %TSo12IntCharValueV* %0, i32 0, i32 1 +public func test(y: IntCharValue) -> CChar { + return y.b +} + +// CHECK-LABEL: define {{.*}}swiftcc i32 @"$s4testAA1ys5Int32VSo21UnimportableMemberRefV_tF"(%TSo21UnimportableMemberRefV* %0) +// CHECK: getelementptr inbounds %TSo21UnimportableMemberRefV, %TSo21UnimportableMemberRefV* %0, i32 0, i32 3 +public func test(y: UnimportableMemberRef) -> CInt { + return y.y +} + +// CHECK-LABEL: define {{.*}}swiftcc i32 @"$s4testAA1ys5Int32VSo23UnimportableMemberValueV_tF"(%TSo23UnimportableMemberValueV* %0) +// CHECK: getelementptr inbounds %TSo23UnimportableMemberValueV, %TSo23UnimportableMemberValueV* %0, i32 0, i32 3 +public func test(y: UnimportableMemberValue) -> CInt { + return y.y +} +