From e58347e8d234db1a5056073f62cc0260fcd1fd77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Horv=C3=A1th?= Date: Fri, 5 Sep 2025 09:07:17 +0100 Subject: [PATCH 1/3] [6.2][cxx-interop] Basic support for anonymous structs with non-copyable fields Explanation: Anonymous structs cannot be copied or moved, these operations only can happen to their enclosing non-anonymous types. Stop trying to emit special member functions and value witness tables for these structs. This fix is required to unblock a high priority libc++ change that fixes an unintended ABI break. Issues: rdar://159928354 Original PRs: #84105 Risk: Medium. I believe this is the right change but hard to anticipate if something depends on the presence of these operations (which is likely to be a bug). But this is required to unblock an important libc++ fix. Testing: Added a compiler test. Reviewers: @rjmccall --- lib/ClangImporter/ImportDecl.cpp | 4 ++-- lib/IRGen/GenMeta.cpp | 4 ++++ .../move-only/Inputs/move-only-cxx-value-type.h | 12 ++++++++++++ .../class/move-only/move-only-cxx-value-type.swift | 4 ++++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 937f6f1052e00..2c6eb45aae0e5 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -3064,11 +3064,11 @@ namespace { } } } - if (copyCtor) { + if (copyCtor && !decl->isAnonymousStructOrUnion()) { clangSema.DefineImplicitCopyConstructor(clang::SourceLocation(), copyCtor); } - if (moveCtor) { + if (moveCtor && !decl->isAnonymousStructOrUnion()) { clangSema.DefineImplicitMoveConstructor(clang::SourceLocation(), moveCtor); } diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index f52b85106b27a..15d4ab0c24dcb 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -6935,6 +6935,10 @@ namespace { } void addValueWitnessTable() { + if (auto cd = Target->getClangDecl()) + if (auto rd = dyn_cast(cd)) + if (rd->isAnonymousStructOrUnion()) + return; auto vwtPointer = emitValueWitnessTable(/*relative*/ false).getValue(); B.addSignedPointer(vwtPointer, IGM.getOptions().PointerAuth.ValueWitnessTable, diff --git a/test/Interop/Cxx/class/move-only/Inputs/move-only-cxx-value-type.h b/test/Interop/Cxx/class/move-only/Inputs/move-only-cxx-value-type.h index 388a686dd233c..16c4f1bf65504 100644 --- a/test/Interop/Cxx/class/move-only/Inputs/move-only-cxx-value-type.h +++ b/test/Interop/Cxx/class/move-only/Inputs/move-only-cxx-value-type.h @@ -56,4 +56,16 @@ struct NonCopyableHolderDerivedDerived: NonCopyableHolderDerived { inline NonCopyable *getNonCopyablePtr() { return nullptr; } inline NonCopyableDerived *getNonCopyableDerivedPtr() { return nullptr; } +template +struct FieldInAnonStruct { + FieldInAnonStruct() : field(5) {} + FieldInAnonStruct(const FieldInAnonStruct &) = delete; + FieldInAnonStruct(FieldInAnonStruct &&) = default; + struct { + T field; + }; +}; + +using FieldInAnonStructNC = FieldInAnonStruct; + #endif // TEST_INTEROP_CXX_CLASS_MOVE_ONLY_VT_H diff --git a/test/Interop/Cxx/class/move-only/move-only-cxx-value-type.swift b/test/Interop/Cxx/class/move-only/move-only-cxx-value-type.swift index a9ccb692a3431..7e7a306192de6 100644 --- a/test/Interop/Cxx/class/move-only/move-only-cxx-value-type.swift +++ b/test/Interop/Cxx/class/move-only/move-only-cxx-value-type.swift @@ -76,4 +76,8 @@ MoveOnlyCxxValueType.test("Test move only field access in derived holder") { } #endif +MoveOnlyCxxValueType.test("Test move only field in anonymous struct") { + let a = FieldInAnonStructNC() + let b = a +} runAllTests() From 22f5eb79aa5a88c5c9f0855d19055f88284eeb4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Horv=C3=A1th?= Date: Mon, 8 Sep 2025 17:44:43 +0100 Subject: [PATCH 2/3] Merge pull request #84152 from Xazax-hun/add-nullptr-to-metadata [cxx-interop] Make the size of anonymous types metadata is unchanged --- lib/IRGen/GenMeta.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 15d4ab0c24dcb..09e6ca686fe0a 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -6935,11 +6935,13 @@ namespace { } void addValueWitnessTable() { + llvm::Constant* vwtPointer = nullptr; if (auto cd = Target->getClangDecl()) if (auto rd = dyn_cast(cd)) if (rd->isAnonymousStructOrUnion()) - return; - auto vwtPointer = emitValueWitnessTable(/*relative*/ false).getValue(); + vwtPointer = llvm::Constant::getNullValue(IGM.WitnessTablePtrTy); + if (!vwtPointer) + vwtPointer = emitValueWitnessTable(/*relative*/ false).getValue(); B.addSignedPointer(vwtPointer, IGM.getOptions().PointerAuth.ValueWitnessTable, PointerAuthEntity()); From 95ff51c739597558ae105c40ade45c21b52c0b8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Horv=C3=A1th?= Date: Wed, 10 Sep 2025 20:40:03 +0100 Subject: [PATCH 3/3] Merge pull request #84199 from Xazax-hun/no-reflection-anon-types [cxx-interop] Restrict the uses of anonymous types --- lib/ClangImporter/ImportDecl.cpp | 4 ++++ lib/IRGen/StructLayout.cpp | 15 +++++++++++---- test/Interop/Cxx/class/Inputs/simple-structs.h | 9 +++++++++ .../Cxx/class/access-anonymous-field.swift | 12 ++++++++++++ test/Interop/Cxx/class/print-simple-structs.swift | 8 ++++++++ 5 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 test/Interop/Cxx/class/access-anonymous-field.swift diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 2c6eb45aae0e5..e4768a3851931 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -4598,6 +4598,10 @@ namespace { // FIXME: Temporarily unreachable because of check above. markAsVariant(result, *correctSwiftName); + if (decl->isAnonymousStructOrUnion()) + Impl.markUnavailable( + result, "refer to the members of the anonymous type instead"); + return result; } diff --git a/lib/IRGen/StructLayout.cpp b/lib/IRGen/StructLayout.cpp index 368ff61dbf9fc..964696f5f3e30 100644 --- a/lib/IRGen/StructLayout.cpp +++ b/lib/IRGen/StructLayout.cpp @@ -590,10 +590,17 @@ unsigned irgen::getNumFields(const NominalTypeDecl *target) { } bool irgen::isExportableField(Field field) { - if (field.getKind() == Field::Kind::Var && - field.getVarDecl()->getClangDecl() && - field.getVarDecl()->getFormalAccess() == AccessLevel::Private) - return false; + if (field.getKind() == Field::Kind::Var) { + if (field.getVarDecl()->getClangDecl() && + field.getVarDecl()->getFormalAccess() == AccessLevel::Private) + return false; + // We should not be able to refer to anonymous types. + if (const auto *vd = dyn_cast_or_null( + field.getVarDecl()->getClangDecl())) + if (const auto *rd = vd->getType()->getAsRecordDecl()) + if (rd->isAnonymousStructOrUnion()) + return false; + } // All other fields are exportable return true; } diff --git a/test/Interop/Cxx/class/Inputs/simple-structs.h b/test/Interop/Cxx/class/Inputs/simple-structs.h index cdda0654dbb5f..c8f84b4e7eea2 100644 --- a/test/Interop/Cxx/class/Inputs/simple-structs.h +++ b/test/Interop/Cxx/class/Inputs/simple-structs.h @@ -17,6 +17,15 @@ struct HasPublicFieldsOnly { HasPublicFieldsOnly(int i1, int i2) : publ1(i1), publ2(i2) {} }; +struct HasAnonymousType { + HasAnonymousType(int a, int b, int c) : a(a), b(b), c(c) {} + + struct { + int a, b; + }; + int c; +}; + struct HasPrivatePublicProtectedFields { private: int priv1; diff --git a/test/Interop/Cxx/class/access-anonymous-field.swift b/test/Interop/Cxx/class/access-anonymous-field.swift new file mode 100644 index 0000000000000..767d70baee38d --- /dev/null +++ b/test/Interop/Cxx/class/access-anonymous-field.swift @@ -0,0 +1,12 @@ +// RUN: %target-typecheck-verify-swift -I %S/Inputs -swift-version 6 -cxx-interoperability-mode=upcoming-swift + +// CHECK: Foobar + +import SimpleStructs + +let s = HasAnonymousType(1, 2, 3) +let _ = s.__Anonymous_field0 // expected-error {{'__Anonymous_field0' is unavailable: refer to the members of the anonymous type instead}} +// Referring to the members of the anonymous type directly. +let _ = s.a +let _ = s.b +let _ = s.c diff --git a/test/Interop/Cxx/class/print-simple-structs.swift b/test/Interop/Cxx/class/print-simple-structs.swift index 27088944d40d0..ee8e3209e0add 100644 --- a/test/Interop/Cxx/class/print-simple-structs.swift +++ b/test/Interop/Cxx/class/print-simple-structs.swift @@ -24,6 +24,11 @@ func printCxxStructNested() { print(s) } +func printCxxStructWithAnonType() { + let s = HasAnonymousType(1, 2, 3) + print(s) +} + printCxxStructPrivateFields() // CHECK: HasPrivateFieldsOnly() @@ -35,3 +40,6 @@ printCxxStructPrivatePublicProtectedFields() printCxxStructNested() // CHECK: Outer(publStruct: {{.*}}.HasPrivatePublicProtectedFields(publ1: 8, publ2: 12)) + +printCxxStructWithAnonType() +// CHECK: HasAnonymousType(c: 3)