From 83bdd59d7dbbd4dec74effe9326184bce358db50 Mon Sep 17 00:00:00 2001 From: Puyan Lotfi Date: Tue, 13 Jun 2023 22:57:08 -0700 Subject: [PATCH] [cxx-interop] Add fix for corner case where NS_OPTIONS typedef has to be desugared This patch is an add-on to https://github.com/apple/swift/pull/64043. Essentially when encountering NS_OPTIONS enums, in C++-Interop mode if they are not specially handled then they can mangle differently than they do without C++-Interop. This patch adds logic to handle when a typedef and enum have additional clang::ElaboratedType sugar, but otherwise it does the same as the existing 64043 patch. The test case provided was encountered in a real app build. The problem came from when two modules are each compiled one with and one without C++-Interop. For the test case code provided the mangling of the protocol conformance is not consistent and the code in SILGenLazyConformance.cpp crashes on an invalid conformance with reason "Invalid conformance in type-checked AST". (cherry picked from commit fe6ccd7a29b5cbd6f193e151b57640f05f24f42b) --- lib/ClangImporter/ClangImporter.cpp | 23 ++++++++++++++----- .../Inputs/NSOptionsMangling.apinotes | 5 ++++ .../Inputs/NSOptionsMangling.h | 22 ++++++++++++++++++ .../objc-correctness/Inputs/module.modulemap | 3 +++ .../ns-options-mangling.swift | 11 +++++++++ 5 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 test/Interop/Cxx/objc-correctness/Inputs/NSOptionsMangling.apinotes create mode 100644 test/Interop/Cxx/objc-correctness/Inputs/NSOptionsMangling.h create mode 100644 test/Interop/Cxx/objc-correctness/ns-options-mangling.swift diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 6088ffe0a15bd..bf39202408267 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -6854,14 +6854,25 @@ const clang::TypedefType *ClangImporter::getTypeDefForCXXCFOptionsDefinition( if (!enumDecl->getDeclName().isEmpty()) return nullptr; - if (auto typedefType = dyn_cast( - enumDecl->getIntegerType().getTypePtr())) { - if (auto enumExtensibilityAttr = - typedefType->getDecl()->getAttr(); - enumExtensibilityAttr && + const clang::ElaboratedType *elaboratedType = + dyn_cast(enumDecl->getIntegerType().getTypePtr()); + if (auto typedefType = + elaboratedType + ? dyn_cast(elaboratedType->desugar()) + : dyn_cast( + enumDecl->getIntegerType().getTypePtr())) { + auto enumExtensibilityAttr = + elaboratedType + ? enumDecl->getAttr() + : typedefType->getDecl()->getAttr(); + const bool hasFlagEnumAttr = + elaboratedType ? enumDecl->hasAttr() + : typedefType->getDecl()->hasAttr(); + + if (enumExtensibilityAttr && enumExtensibilityAttr->getExtensibility() == clang::EnumExtensibilityAttr::Open && - typedefType->getDecl()->hasAttr()) { + hasFlagEnumAttr) { return Impl.isUnavailableInSwift(typedefType->getDecl()) ? typedefType : nullptr; } diff --git a/test/Interop/Cxx/objc-correctness/Inputs/NSOptionsMangling.apinotes b/test/Interop/Cxx/objc-correctness/Inputs/NSOptionsMangling.apinotes new file mode 100644 index 0000000000000..e8f3dbd34b73b --- /dev/null +++ b/test/Interop/Cxx/objc-correctness/Inputs/NSOptionsMangling.apinotes @@ -0,0 +1,5 @@ +--- +Name: NSOptionsMangling +Tags: +- Name: UIControlState + SwiftName: UIControl.State diff --git a/test/Interop/Cxx/objc-correctness/Inputs/NSOptionsMangling.h b/test/Interop/Cxx/objc-correctness/Inputs/NSOptionsMangling.h new file mode 100644 index 0000000000000..6618d1c03c164 --- /dev/null +++ b/test/Interop/Cxx/objc-correctness/Inputs/NSOptionsMangling.h @@ -0,0 +1,22 @@ +#define __CF_OPTIONS_ATTRIBUTES __attribute__((flag_enum,enum_extensibility(open))) +#if (__cplusplus) +#define CF_OPTIONS(_type, _name) __attribute__((availability(swift,unavailable))) _type _name; enum __CF_OPTIONS_ATTRIBUTES : _name +#else +#define CF_OPTIONS(_type, _name) enum __CF_OPTIONS_ATTRIBUTES _name : _type _name; enum _name : _type +#endif + +typedef CF_OPTIONS(unsigned, UIControlState) { UIControlStateNormal = 0 }; + +#ifdef __cplusplus +#define UIKIT_EXTERN extern "C" __attribute__((visibility ("default"))) +#else +#define UIKIT_EXTERN extern __attribute__((visibility ("default"))) +#endif + +@interface UIView +@end + +UIKIT_EXTERN +@interface UIControl : UIView +@end + diff --git a/test/Interop/Cxx/objc-correctness/Inputs/module.modulemap b/test/Interop/Cxx/objc-correctness/Inputs/module.modulemap index a7b2476b3b07a..0d922784daf2a 100644 --- a/test/Interop/Cxx/objc-correctness/Inputs/module.modulemap +++ b/test/Interop/Cxx/objc-correctness/Inputs/module.modulemap @@ -9,3 +9,6 @@ module CxxClassWithNSStringInit [extern_c] { requires cplusplus } +module NSOptionsMangling { + header "NSOptionsMangling.h" +} diff --git a/test/Interop/Cxx/objc-correctness/ns-options-mangling.swift b/test/Interop/Cxx/objc-correctness/ns-options-mangling.swift new file mode 100644 index 0000000000000..4dc750a247a44 --- /dev/null +++ b/test/Interop/Cxx/objc-correctness/ns-options-mangling.swift @@ -0,0 +1,11 @@ +// RUN: %target-swift-frontend -I %S/Inputs -c -cxx-interoperability-mode=swift-5.9 %s -S -o - | %FileCheck %s +// RUN: %target-swift-frontend -I %S/Inputs -c %s -S -o - | %FileCheck %s + +// REQUIRES: objc_interop + +// CHECK: _$sSo14UIControlStateV4main7FooableACMc +// The following check is to ensure the conformance is mangled properly: +// protocol conformance descriptor for __C.UIControlState : main.Fooable in main +import NSOptionsMangling +protocol Fooable { } +extension UIControl.State: Fooable {}