From 1cde254dbeaf5ff530123933d8fb4a7a8835ca59 Mon Sep 17 00:00:00 2001 From: Gabor Horvath Date: Wed, 10 Sep 2025 12:12:14 +0100 Subject: [PATCH] [cxx-interop] Restrict the uses of anonymous types Make sure they are excluded from the reflection metadata (although in the future we want to make sure indirect fields are included). Make sure the users cannot refer to the anonymous field, only its members. --- 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 b951a3357a6ba..256de392a2c55 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -4546,6 +4546,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 f6eea34220094..a6a7f854af394 100644 --- a/lib/IRGen/StructLayout.cpp +++ b/lib/IRGen/StructLayout.cpp @@ -589,10 +589,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)