From d5b31328cdaa96472c686421f0183fe3de27cd83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Thu, 10 Apr 2025 15:42:47 -0700 Subject: [PATCH 01/11] PrintAsObjc Test: Test compatibility headers for C --- test/PrintAsObjC/cdecl-imports.swift | 1 + test/PrintAsObjC/cdecl.swift | 1 + test/PrintAsObjC/lit.local.cfg | 9 +++++++++ 3 files changed, 11 insertions(+) diff --git a/test/PrintAsObjC/cdecl-imports.swift b/test/PrintAsObjC/cdecl-imports.swift index 41540286ccc23..7b153be5e6883 100644 --- a/test/PrintAsObjC/cdecl-imports.swift +++ b/test/PrintAsObjC/cdecl-imports.swift @@ -2,6 +2,7 @@ // RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) %s -parse-as-library -typecheck -verify -emit-objc-header-path %t/swift.h // RUN: %FileCheck %s < %t/swift.h // RUN: %check-in-clang %t/swift.h +// RUN: %check-in-clang-c %t/swift.h // RUN: %check-in-clang-cxx %t/swift.h // REQUIRES: objc_interop diff --git a/test/PrintAsObjC/cdecl.swift b/test/PrintAsObjC/cdecl.swift index 307159d956171..780121bb97d68 100644 --- a/test/PrintAsObjC/cdecl.swift +++ b/test/PrintAsObjC/cdecl.swift @@ -4,6 +4,7 @@ // RUN: %FileCheck %s < %t/cdecl.h // RUN: %check-in-clang %t/cdecl.h // RUN: %check-in-clang -fno-modules -Qunused-arguments %t/cdecl.h -include ctypes.h -include CoreFoundation.h +// RUN: %check-in-clang-c -fno-modules -Qunused-arguments %t/cdecl.h -include ctypes.h -include CoreFoundation.h // RUN: %check-in-clang-cxx -fno-modules -Qunused-arguments %t/cdecl.h -include ctypes.h -include CoreFoundation.h // REQUIRES: objc_interop diff --git a/test/PrintAsObjC/lit.local.cfg b/test/PrintAsObjC/lit.local.cfg index a517a4a80ca14..411b884f7af39 100644 --- a/test/PrintAsObjC/lit.local.cfg +++ b/test/PrintAsObjC/lit.local.cfg @@ -10,6 +10,15 @@ config.substitutions.insert(0, ('%check-in-clang', '-I %%clang-include-dir ' '-isysroot %r/Inputs/clang-importer-sdk' % config.test_source_root) ) +config.substitutions.insert(0, ('%check-in-clang-c', + '%%clang -fsyntax-only -x c-header -std=c99 -fobjc-arc -fmodules ' + '-fmodules-validate-system-headers ' + '-Weverything -Werror -Wno-unused-macros -Wno-incomplete-module ' + '-Wno-auto-import ' + '-F %%clang-importer-sdk-path/frameworks ' + '-I %%clang-include-dir ' + '-isysroot %r/Inputs/clang-importer-sdk' % config.test_source_root) ) + config.substitutions.insert(0, ('%check-in-clang-cxx', '%%clang -fsyntax-only -x objective-c++-header -std=c++17 ' '-fobjc-arc -fmodules -fmodules-validate-system-headers ' From e65a1fbf5123165e3e6f670666c1033aeacc8672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Mon, 14 Apr 2025 12:05:42 -0700 Subject: [PATCH 02/11] PrintAsClang: Intro and use getKnownType for C compatibility --- lib/PrintAsClang/DeclAndTypePrinter.cpp | 11 +++++++++-- lib/PrintAsClang/OutputLanguageMode.h | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/PrintAsClang/DeclAndTypePrinter.cpp b/lib/PrintAsClang/DeclAndTypePrinter.cpp index 3faea40cab102..44b4f8ec73c94 100644 --- a/lib/PrintAsClang/DeclAndTypePrinter.cpp +++ b/lib/PrintAsClang/DeclAndTypePrinter.cpp @@ -2289,6 +2289,14 @@ class DeclAndTypePrinter::Implementation return false; } + std::optional + getKnownType(const TypeDecl *typeDecl) { + if (outputLang == OutputLanguageMode::C) + return owningPrinter.typeMapping.getKnownCTypeInfo(typeDecl); + + return owningPrinter.typeMapping.getKnownObjCTypeInfo(typeDecl); + } + /// If \p typeDecl is one of the standard library types used to map in Clang /// primitives and basic types, print out the appropriate spelling and /// return true. @@ -2297,8 +2305,7 @@ class DeclAndTypePrinter::Implementation /// for interfacing with C and Objective-C. bool printIfKnownSimpleType(const TypeDecl *typeDecl, std::optional optionalKind) { - auto knownTypeInfo = - owningPrinter.typeMapping.getKnownObjCTypeInfo(typeDecl); + auto knownTypeInfo = getKnownType(typeDecl); if (!knownTypeInfo) return false; os << knownTypeInfo->name; diff --git a/lib/PrintAsClang/OutputLanguageMode.h b/lib/PrintAsClang/OutputLanguageMode.h index 5828aa0306239..b39c625c044a1 100644 --- a/lib/PrintAsClang/OutputLanguageMode.h +++ b/lib/PrintAsClang/OutputLanguageMode.h @@ -15,7 +15,7 @@ namespace swift { -enum class OutputLanguageMode { ObjC, Cxx }; +enum class OutputLanguageMode { ObjC, Cxx, C }; } // end namespace swift From 02933b5b282017f7b6b56936728ed36e07f8e0be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Tue, 1 Apr 2025 16:07:52 -0700 Subject: [PATCH 03/11] PrintAsClang: Print @cdecl in their own block in compatibility headers Add a block for C clients in the compatibility header. This block contains only the `@cdecl` functions that are printed using only C types. This C block is printed above the Objective-C and C++ blocks as if we add support for `@cdecl` types other languages should be able to reference them in function signatures. Other languages block don't duplicate printing the `@cdecl` functions either as they are already accessible to them. --- lib/PrintAsClang/DeclAndTypePrinter.cpp | 8 +++ lib/PrintAsClang/ModuleContentsWriter.cpp | 11 +++ lib/PrintAsClang/ModuleContentsWriter.h | 5 ++ lib/PrintAsClang/PrintAsClang.cpp | 24 ++++++- test/PrintAsObjC/cdecl-official.swift | 85 +++++++++++++++++++++++ test/PrintAsObjC/cdecl-with-objc.swift | 33 +++++++++ 6 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 test/PrintAsObjC/cdecl-official.swift create mode 100644 test/PrintAsObjC/cdecl-with-objc.swift diff --git a/lib/PrintAsClang/DeclAndTypePrinter.cpp b/lib/PrintAsClang/DeclAndTypePrinter.cpp index 44b4f8ec73c94..edfb16bf13a3b 100644 --- a/lib/PrintAsClang/DeclAndTypePrinter.cpp +++ b/lib/PrintAsClang/DeclAndTypePrinter.cpp @@ -3006,6 +3006,14 @@ bool DeclAndTypePrinter::shouldInclude(const ValueDecl *VD) { return false; } + // In C output mode print only @cdecls and skip them in other modes. + bool isCDeclForC = false; + auto *FD = dyn_cast(VD); + if (FD) + isCDeclForC = FD->getCDeclKind() == ForeignLanguage::C; + if (isCDeclForC != (outputLang == OutputLanguageMode::C)) + return false; + if (VD->getAttrs().hasAttribute()) return false; diff --git a/lib/PrintAsClang/ModuleContentsWriter.cpp b/lib/PrintAsClang/ModuleContentsWriter.cpp index ef164898bbaed..be886fa13667d 100644 --- a/lib/PrintAsClang/ModuleContentsWriter.cpp +++ b/lib/PrintAsClang/ModuleContentsWriter.cpp @@ -1118,6 +1118,17 @@ void swift::printModuleContentsAsObjC( .write(); } +void swift::printModuleContentsAsC( + raw_ostream &os, llvm::SmallPtrSetImpl &imports, + ModuleDecl &M, SwiftToClangInteropContext &interopContext) { + llvm::raw_null_ostream prologueOS; + llvm::StringSet<> exposedModules; + ModuleWriter(os, prologueOS, imports, M, interopContext, getRequiredAccess(M), + /*requiresExposedAttribute=*/false, exposedModules, + OutputLanguageMode::C) + .write(); +} + EmittedClangHeaderDependencyInfo swift::printModuleContentsAsCxx( raw_ostream &os, ModuleDecl &M, SwiftToClangInteropContext &interopContext, bool requiresExposedAttribute, llvm::StringSet<> &exposedModules) { diff --git a/lib/PrintAsClang/ModuleContentsWriter.h b/lib/PrintAsClang/ModuleContentsWriter.h index f6a175fc4b6e4..0e7edca689caa 100644 --- a/lib/PrintAsClang/ModuleContentsWriter.h +++ b/lib/PrintAsClang/ModuleContentsWriter.h @@ -37,6 +37,11 @@ void printModuleContentsAsObjC(raw_ostream &os, ModuleDecl &M, SwiftToClangInteropContext &interopContext); +void printModuleContentsAsC(raw_ostream &os, + llvm::SmallPtrSetImpl &imports, + ModuleDecl &M, + SwiftToClangInteropContext &interopContext); + struct EmittedClangHeaderDependencyInfo { /// The set of imported modules used by this module. SmallPtrSet imports; diff --git a/lib/PrintAsClang/PrintAsClang.cpp b/lib/PrintAsClang/PrintAsClang.cpp index 0cfdf1c389708..37a16d5b15f28 100644 --- a/lib/PrintAsClang/PrintAsClang.cpp +++ b/lib/PrintAsClang/PrintAsClang.cpp @@ -58,6 +58,17 @@ static void emitObjCConditional(raw_ostream &out, out << "#endif\n"; } +static void emitExternC(raw_ostream &out, + llvm::function_ref cCase) { + emitCxxConditional(out, [&] { + out << "extern \"C\" {\n"; + }); + cCase(); + emitCxxConditional(out, [&] { + out << "} // extern \"C\"\n"; + }); +} + static void writePtrauthPrologue(raw_ostream &os, ASTContext &ctx) { emitCxxConditional(os, [&]() { ClangSyntaxPrinter(ctx, os).printIgnoredDiagnosticBlock( @@ -577,12 +588,21 @@ bool swift::printAsClangHeader(raw_ostream &os, ModuleDecl *M, llvm::PrettyStackTraceString trace("While generating Clang header"); SwiftToClangInteropContext interopContext(*M, irGenOpts); + writePrologue(os, M->getASTContext(), computeMacroGuard(M)); + // C content (@cdecl) + if (M->getASTContext().LangOpts.hasFeature(Feature::CDecl)) { + SmallPtrSet imports; + emitExternC(os, [&] { + printModuleContentsAsC(os, imports, *M, interopContext); + }); + } + + // Objective-C content SmallPtrSet imports; std::string objcModuleContentsBuf; llvm::raw_string_ostream objcModuleContents{objcModuleContentsBuf}; printModuleContentsAsObjC(objcModuleContents, imports, *M, interopContext); - writePrologue(os, M->getASTContext(), computeMacroGuard(M)); emitObjCConditional(os, [&] { llvm::StringMap exposedModuleHeaderNames; writeImports(os, imports, *M, bridgingHeader, frontendOpts, @@ -591,6 +611,8 @@ bool swift::printAsClangHeader(raw_ostream &os, ModuleDecl *M, writePostImportPrologue(os, *M); emitObjCConditional(os, [&] { os << "\n" << objcModuleContents.str(); }); writeObjCEpilogue(os); + + // C++ content emitCxxConditional(os, [&] { // FIXME: Expose Swift with @expose by default. bool enableCxx = frontendOpts.ClangHeaderExposedDecls.has_value() || diff --git a/test/PrintAsObjC/cdecl-official.swift b/test/PrintAsObjC/cdecl-official.swift new file mode 100644 index 0000000000000..a0b3f05d6b070 --- /dev/null +++ b/test/PrintAsObjC/cdecl-official.swift @@ -0,0 +1,85 @@ +// RUN: %empty-directory(%t) +// RUN: split-file %s %t --leading-lines + +/// Generate cdecl.h +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) \ +// RUN: %t/Lib.swift -emit-module -verify -o %t -emit-module-doc \ +// RUN: -emit-objc-header-path %t/cdecl.h \ +// RUN: -enable-experimental-feature CDecl + +/// Check cdecl.h directly +// RUN: %FileCheck %s --input-file %t/cdecl.h +// RUN: %check-in-clang %t/cdecl.h +// RUN: %check-in-clang-c %t/cdecl.h -Wnullable-to-nonnull-conversion +// RUN: %check-in-clang-cxx %t/cdecl.h + +/// Build a client against cdecl.h +// RUN: %clang -c %t/Client.c -fobjc-arc -fmodules -I %t \ +// RUN: -F %S/../Inputs/clang-importer-sdk-path/frameworks \ +// RUN: -I %clang-include-dir -Werror \ +// RUN: -isysroot %S/../Inputs/clang-importer-sdk + +// REQUIRES: swift_feature_CDecl + +//--- Lib.swift + +// CHECK: #if defined(__cplusplus) +// CHECK: extern "C" { +// CHECK: #endif + +/// My documentation +@cdecl("simple") +func a_simple(x: Int, bar y: Int) -> Int { return x } +// CHECK-LABEL: // My documentation +// CHECK-LABEL: SWIFT_EXTERN ptrdiff_t simple(ptrdiff_t x, ptrdiff_t y) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT; + +@cdecl("primitiveTypes") +public func b_primitiveTypes(i: Int, ci: CInt, l: CLong, c: CChar, f: Float, d: Double, b: Bool) {} +// CHECK-LABEL: SWIFT_EXTERN void primitiveTypes(ptrdiff_t i, int ci, long l, char c, float f, double d, bool b) SWIFT_NOEXCEPT; + +@cdecl("has_keyword_arg_names") +func c_keywordArgNames(auto: Int, union: Int) {} +// CHECK-LABEL: SWIFT_EXTERN void has_keyword_arg_names(ptrdiff_t auto_, ptrdiff_t union_) SWIFT_NOEXCEPT; + +@cdecl("return_never") +func d_returnNever() -> Never { fatalError() } +// CHECK-LABEL: SWIFT_EXTERN void return_never(void) SWIFT_NOEXCEPT SWIFT_NORETURN; + +@cdecl("block_nightmare") +public func s_block_nightmare(x: @convention(block) (Int) -> Float) + -> @convention(block) (CChar) -> Double { return { _ in 0 } } +// CHECK-LABEL: SWIFT_EXTERN double (^ _Nonnull block_nightmare(SWIFT_NOESCAPE float (^ _Nonnull x)(ptrdiff_t)))(char) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT; + +@cdecl("block_recurring_nightmare") +public func t_block_recurring_nightmare(x: @escaping @convention(block) (@convention(block) (Double) -> Int) -> Float) + -> @convention(block) (_ asdfasdf: @convention(block) (CUnsignedChar) -> CChar) -> Double { + fatalError() +} +// CHECK-LABEL: SWIFT_EXTERN double (^ _Nonnull block_recurring_nightmare(float (^ _Nonnull x)(SWIFT_NOESCAPE ptrdiff_t (^ _Nonnull)(double))))(SWIFT_NOESCAPE char (^ _Nonnull)(unsigned char)) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT; + +@cdecl("function_pointer_nightmare") +func u_function_pointer_nightmare(x: @convention(c) (Int) -> Float) + -> @convention(c) (CChar) -> Double { return { _ in 0 } } +// CHECK-LABEL: SWIFT_EXTERN double (* _Nonnull function_pointer_nightmare(float (* _Nonnull x)(ptrdiff_t)))(char) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT; + +@cdecl("function_pointer_recurring_nightmare") +public func v_function_pointer_recurring_nightmare(x: @escaping @convention(c) (@convention(c) (Double) -> Int) -> Float) + -> @convention(c) (@convention(c) (CUnsignedChar) -> CChar) -> Double { + fatalError() +} +// CHECK-LABEL: SWIFT_EXTERN double (* _Nonnull function_pointer_recurring_nightmare(float (* _Nonnull x)(ptrdiff_t (* _Nonnull)(double))))(char (* _Nonnull)(unsigned char)) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT; + +// CHECK: #if defined(__cplusplus) +// CHECK-NEXT: } +// CHECK-NEXT: #endif + +//--- Client.c + +#include "cdecl.h" + +int main() { + ptrdiff_t x = simple(42, 43); + primitiveTypes(1, 2, 3, 'a', 1.0f, 2.0, true); + has_keyword_arg_names(1, 2); + return_never(); +} diff --git a/test/PrintAsObjC/cdecl-with-objc.swift b/test/PrintAsObjC/cdecl-with-objc.swift new file mode 100644 index 0000000000000..f6cd9a4bc0b4f --- /dev/null +++ b/test/PrintAsObjC/cdecl-with-objc.swift @@ -0,0 +1,33 @@ +/// Ensure we print @cdecl and @_cdecl only once. + +// RUN: %empty-directory(%t) + +/// Generate cdecl.h +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) \ +// RUN: %s -emit-module -verify -o %t -emit-module-doc \ +// RUN: -emit-objc-header-path %t/cdecl.h \ +// RUN: -disable-objc-attr-requires-foundation-module \ +// RUN: -enable-experimental-feature CDecl + +/// Check cdecl.h directly +// RUN: %FileCheck %s --input-file %t/cdecl.h +// RUN: %check-in-clang %t/cdecl.h +// RUN: %check-in-clang-c %t/cdecl.h -Wnullable-to-nonnull-conversion +// RUN: %check-in-clang-cxx %t/cdecl.h + +// REQUIRES: swift_feature_CDecl +// REQUIRES: objc_interop + +@cdecl("cFunc") +func cFunc() { } +// CHECK: cFunc +// CHECK-NOT: cFunc + +/// The class would break C parsing if printed in wrong block +@objc +class ObjCClass {} + +@_cdecl("objcFunc") +func objcFunc() -> ObjCClass! { return ObjCClass() } +// CHECK: objcFunc +// CHECK-NOT: objcFunc From bcbdfeeb430be9161836e35d297f306f47e35827 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Fri, 18 Apr 2025 13:08:18 -0700 Subject: [PATCH 04/11] PrintAsObjC Test: Don't use -fobjc-arc when targeting the C language --- test/PrintAsObjC/cdecl-official.swift | 2 +- test/PrintAsObjC/lit.local.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/PrintAsObjC/cdecl-official.swift b/test/PrintAsObjC/cdecl-official.swift index a0b3f05d6b070..8d316e526688d 100644 --- a/test/PrintAsObjC/cdecl-official.swift +++ b/test/PrintAsObjC/cdecl-official.swift @@ -14,7 +14,7 @@ // RUN: %check-in-clang-cxx %t/cdecl.h /// Build a client against cdecl.h -// RUN: %clang -c %t/Client.c -fobjc-arc -fmodules -I %t \ +// RUN: %clang -c %t/Client.c -fmodules -I %t \ // RUN: -F %S/../Inputs/clang-importer-sdk-path/frameworks \ // RUN: -I %clang-include-dir -Werror \ // RUN: -isysroot %S/../Inputs/clang-importer-sdk diff --git a/test/PrintAsObjC/lit.local.cfg b/test/PrintAsObjC/lit.local.cfg index 411b884f7af39..076d14241e149 100644 --- a/test/PrintAsObjC/lit.local.cfg +++ b/test/PrintAsObjC/lit.local.cfg @@ -11,7 +11,7 @@ config.substitutions.insert(0, ('%check-in-clang', '-isysroot %r/Inputs/clang-importer-sdk' % config.test_source_root) ) config.substitutions.insert(0, ('%check-in-clang-c', - '%%clang -fsyntax-only -x c-header -std=c99 -fobjc-arc -fmodules ' + '%%clang -fsyntax-only -x c-header -std=c99 -fmodules ' '-fmodules-validate-system-headers ' '-Weverything -Werror -Wno-unused-macros -Wno-incomplete-module ' '-Wno-auto-import ' From a55b190be24ab45868307977437a8001b93f498e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Fri, 18 Apr 2025 13:20:32 -0700 Subject: [PATCH 05/11] PrintAsObjC Test: Extract what needs Objective-C out of cdecl-official --- .../cdecl-official-for-objc-clients.swift | 23 +++++++++++++++++++ test/PrintAsObjC/cdecl-official.swift | 4 +--- 2 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 test/PrintAsObjC/cdecl-official-for-objc-clients.swift diff --git a/test/PrintAsObjC/cdecl-official-for-objc-clients.swift b/test/PrintAsObjC/cdecl-official-for-objc-clients.swift new file mode 100644 index 0000000000000..94be91e72f2c0 --- /dev/null +++ b/test/PrintAsObjC/cdecl-official-for-objc-clients.swift @@ -0,0 +1,23 @@ +/// Similar test to cdecl-official but gated to objc-interop compatibility + +// RUN: %empty-directory(%t) +// RUN: split-file %S/cdecl-official.swift %t --leading-lines + +/// Generate cdecl.h +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) \ +// RUN: %t/Lib.swift -emit-module -verify -o %t -emit-module-doc \ +// RUN: -emit-clang-header-path %t/cdecl.h \ +// RUN: -enable-experimental-feature CDecl + +/// Check cdecl.h directly +// RUN: %check-in-clang %t/cdecl.h +// RUN: %check-in-clang-cxx %t/cdecl.h + +/// Build an Objective-C client against cdecl.h +// RUN: %clang -c %t/Client.c -fmodules -I %t \ +// RUN: -F %S/../Inputs/clang-importer-sdk-path/frameworks \ +// RUN: -I %clang-include-dir -Werror \ +// RUN: -isysroot %S/../Inputs/clang-importer-sdk + +// REQUIRES: swift_feature_CDecl +// REQUIRES: objc_interop diff --git a/test/PrintAsObjC/cdecl-official.swift b/test/PrintAsObjC/cdecl-official.swift index 8d316e526688d..b92b9ea3272dd 100644 --- a/test/PrintAsObjC/cdecl-official.swift +++ b/test/PrintAsObjC/cdecl-official.swift @@ -4,14 +4,12 @@ /// Generate cdecl.h // RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) \ // RUN: %t/Lib.swift -emit-module -verify -o %t -emit-module-doc \ -// RUN: -emit-objc-header-path %t/cdecl.h \ +// RUN: -emit-clang-header-path %t/cdecl.h \ // RUN: -enable-experimental-feature CDecl /// Check cdecl.h directly // RUN: %FileCheck %s --input-file %t/cdecl.h -// RUN: %check-in-clang %t/cdecl.h // RUN: %check-in-clang-c %t/cdecl.h -Wnullable-to-nonnull-conversion -// RUN: %check-in-clang-cxx %t/cdecl.h /// Build a client against cdecl.h // RUN: %clang -c %t/Client.c -fmodules -I %t \ From 20e1ac687aa14c52e70c7c0ddf679b8a1962dac3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Fri, 18 Apr 2025 10:52:40 -0700 Subject: [PATCH 06/11] PrintAsClang: Ignore nullability attributes for C clients In C mode we still print nullability attributes. Don't let clang warn on them and ignore the attribute if the C compiler doesn't know them. --- lib/PrintAsClang/PrintAsClang.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/PrintAsClang/PrintAsClang.cpp b/lib/PrintAsClang/PrintAsClang.cpp index 37a16d5b15f28..9ae35dd83a35f 100644 --- a/lib/PrintAsClang/PrintAsClang.cpp +++ b/lib/PrintAsClang/PrintAsClang.cpp @@ -218,6 +218,21 @@ static void writePrologue(raw_ostream &out, ASTContext &ctx, static_assert(SWIFT_MAX_IMPORTED_SIMD_ELEMENTS == 4, "need to add SIMD typedefs here if max elements is increased"); + + if (ctx.LangOpts.hasFeature(Feature::CDecl)) { + // For C compilers which don’t support nullability attributes, ignore them; + // for ones which do, suppress warnings about them being an extension. + out << "#if !__has_feature(nullability)\n" + "# define _Nonnull\n" + "# define _Nullable\n" + "# define _Null_unspecified\n" + "#elif !defined(__OBJC__)\n" + "# pragma clang diagnostic ignored \"-Wnullability-extension\"\n" + "#endif\n" + "#if !__has_feature(nullability_nullable_result)\n" + "# define _Nullable_result _Nullable\n" + "#endif\n"; + } } static int compareImportModulesByName(const ImportModuleTy *left, From 1b0a3d28dcc1ea7e0b044bbb7d989ec3fa6b90ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Mon, 5 May 2025 11:26:05 -0700 Subject: [PATCH 07/11] PrintAsObjC Test: Ignore poison directories warnings for C --- test/PrintAsObjC/lit.local.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/PrintAsObjC/lit.local.cfg b/test/PrintAsObjC/lit.local.cfg index 076d14241e149..065a5410ffb91 100644 --- a/test/PrintAsObjC/lit.local.cfg +++ b/test/PrintAsObjC/lit.local.cfg @@ -14,7 +14,7 @@ config.substitutions.insert(0, ('%check-in-clang-c', '%%clang -fsyntax-only -x c-header -std=c99 -fmodules ' '-fmodules-validate-system-headers ' '-Weverything -Werror -Wno-unused-macros -Wno-incomplete-module ' - '-Wno-auto-import ' + '-Wno-auto-import -Wno-poison-system-directories ' '-F %%clang-importer-sdk-path/frameworks ' '-I %%clang-include-dir ' '-isysroot %r/Inputs/clang-importer-sdk' % config.test_source_root) ) From 3623c1deb8a92d117d77bf61e1e7ed15a547f01a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Mon, 5 May 2025 11:21:58 -0700 Subject: [PATCH 08/11] PrintAsClang: Apply review comments to check on should print of @cdecl --- lib/PrintAsClang/DeclAndTypePrinter.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/PrintAsClang/DeclAndTypePrinter.cpp b/lib/PrintAsClang/DeclAndTypePrinter.cpp index edfb16bf13a3b..771782096e88e 100644 --- a/lib/PrintAsClang/DeclAndTypePrinter.cpp +++ b/lib/PrintAsClang/DeclAndTypePrinter.cpp @@ -3006,14 +3006,22 @@ bool DeclAndTypePrinter::shouldInclude(const ValueDecl *VD) { return false; } - // In C output mode print only @cdecls and skip them in other modes. - bool isCDeclForC = false; - auto *FD = dyn_cast(VD); - if (FD) - isCDeclForC = FD->getCDeclKind() == ForeignLanguage::C; - if (isCDeclForC != (outputLang == OutputLanguageMode::C)) + // In C output mode print only the C variant `@cdecl` (no `@_cdecl`), + // while in other modes print only `@_cdecl`. + std::optional cdeclKind = std::nullopt; + if (auto *FD = dyn_cast(VD)) + cdeclKind = FD->getCDeclKind(); + if (cdeclKind && + (*cdeclKind == ForeignLanguage::C) != + (outputLang == OutputLanguageMode::C)) return false; + // C output mode only accepts @cdecl functions. + if (outputLang == OutputLanguageMode::C && + !cdeclKind) { + return false; + } + if (VD->getAttrs().hasAttribute()) return false; From 5e7887de64fe2d1b17aa0550e140d40d4252035c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Mon, 5 May 2025 11:07:21 -0700 Subject: [PATCH 09/11] PrintAsObjC Test: Don't use blocks in the @cdecl test --- test/PrintAsObjC/cdecl-official.swift | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/test/PrintAsObjC/cdecl-official.swift b/test/PrintAsObjC/cdecl-official.swift index b92b9ea3272dd..8ff35094fa243 100644 --- a/test/PrintAsObjC/cdecl-official.swift +++ b/test/PrintAsObjC/cdecl-official.swift @@ -43,30 +43,6 @@ func c_keywordArgNames(auto: Int, union: Int) {} func d_returnNever() -> Never { fatalError() } // CHECK-LABEL: SWIFT_EXTERN void return_never(void) SWIFT_NOEXCEPT SWIFT_NORETURN; -@cdecl("block_nightmare") -public func s_block_nightmare(x: @convention(block) (Int) -> Float) - -> @convention(block) (CChar) -> Double { return { _ in 0 } } -// CHECK-LABEL: SWIFT_EXTERN double (^ _Nonnull block_nightmare(SWIFT_NOESCAPE float (^ _Nonnull x)(ptrdiff_t)))(char) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT; - -@cdecl("block_recurring_nightmare") -public func t_block_recurring_nightmare(x: @escaping @convention(block) (@convention(block) (Double) -> Int) -> Float) - -> @convention(block) (_ asdfasdf: @convention(block) (CUnsignedChar) -> CChar) -> Double { - fatalError() -} -// CHECK-LABEL: SWIFT_EXTERN double (^ _Nonnull block_recurring_nightmare(float (^ _Nonnull x)(SWIFT_NOESCAPE ptrdiff_t (^ _Nonnull)(double))))(SWIFT_NOESCAPE char (^ _Nonnull)(unsigned char)) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT; - -@cdecl("function_pointer_nightmare") -func u_function_pointer_nightmare(x: @convention(c) (Int) -> Float) - -> @convention(c) (CChar) -> Double { return { _ in 0 } } -// CHECK-LABEL: SWIFT_EXTERN double (* _Nonnull function_pointer_nightmare(float (* _Nonnull x)(ptrdiff_t)))(char) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT; - -@cdecl("function_pointer_recurring_nightmare") -public func v_function_pointer_recurring_nightmare(x: @escaping @convention(c) (@convention(c) (Double) -> Int) -> Float) - -> @convention(c) (@convention(c) (CUnsignedChar) -> CChar) -> Double { - fatalError() -} -// CHECK-LABEL: SWIFT_EXTERN double (* _Nonnull function_pointer_recurring_nightmare(float (* _Nonnull x)(ptrdiff_t (* _Nonnull)(double))))(char (* _Nonnull)(unsigned char)) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT; - // CHECK: #if defined(__cplusplus) // CHECK-NEXT: } // CHECK-NEXT: #endif From 0f98021708e1286df988d038795da1e3a138818e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Mon, 5 May 2025 11:08:08 -0700 Subject: [PATCH 10/11] PrintAsObjC Test: Don't use modules for C compatibility header tests --- test/PrintAsObjC/cdecl-official.swift | 2 +- test/PrintAsObjC/lit.local.cfg | 3 +-- test/lit.cfg | 5 +++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/test/PrintAsObjC/cdecl-official.swift b/test/PrintAsObjC/cdecl-official.swift index 8ff35094fa243..0be323d6364c5 100644 --- a/test/PrintAsObjC/cdecl-official.swift +++ b/test/PrintAsObjC/cdecl-official.swift @@ -12,7 +12,7 @@ // RUN: %check-in-clang-c %t/cdecl.h -Wnullable-to-nonnull-conversion /// Build a client against cdecl.h -// RUN: %clang -c %t/Client.c -fmodules -I %t \ +// RUN: %clang-no-modules -c %t/Client.c -I %t \ // RUN: -F %S/../Inputs/clang-importer-sdk-path/frameworks \ // RUN: -I %clang-include-dir -Werror \ // RUN: -isysroot %S/../Inputs/clang-importer-sdk diff --git a/test/PrintAsObjC/lit.local.cfg b/test/PrintAsObjC/lit.local.cfg index 065a5410ffb91..c7f4cd06a3431 100644 --- a/test/PrintAsObjC/lit.local.cfg +++ b/test/PrintAsObjC/lit.local.cfg @@ -11,8 +11,7 @@ config.substitutions.insert(0, ('%check-in-clang', '-isysroot %r/Inputs/clang-importer-sdk' % config.test_source_root) ) config.substitutions.insert(0, ('%check-in-clang-c', - '%%clang -fsyntax-only -x c-header -std=c99 -fmodules ' - '-fmodules-validate-system-headers ' + '%%clang-no-modules -fsyntax-only -x c-header -std=c99 ' '-Weverything -Werror -Wno-unused-macros -Wno-incomplete-module ' '-Wno-auto-import -Wno-poison-system-directories ' '-F %%clang-importer-sdk-path/frameworks ' diff --git a/test/lit.cfg b/test/lit.cfg index 061f2947e6bcf..977fda686c7ab 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -789,6 +789,11 @@ config.substitutions.append( ('%clangxx', "%r %s" % (config.clangxx, clang_mcp_opt)) ) +# Alternative to %clang that doesn't require -fmodules. +config.substitutions.append( ('%clang-no-modules', + "%r" % + (config.clang)) ) + # This must come after all substitutions containing "%clang". # Note: %clang is the locally-built clang. # To get Xcode's clang, use %target-clang. From f6dd43245064e99ec6651a87e4f7fa980e921ff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Tue, 6 May 2025 14:34:07 -0700 Subject: [PATCH 11/11] PrintAsObjC Test: Test pointer types with @cdecl --- test/PrintAsObjC/cdecl-official.swift | 19 +++++++++++++++++++ test/attr/attr_cdecl_official.swift | 16 ++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/test/PrintAsObjC/cdecl-official.swift b/test/PrintAsObjC/cdecl-official.swift index 0be323d6364c5..012afa967262e 100644 --- a/test/PrintAsObjC/cdecl-official.swift +++ b/test/PrintAsObjC/cdecl-official.swift @@ -21,6 +21,8 @@ //--- Lib.swift +// CHECK-NOT: assume_nonnull + // CHECK: #if defined(__cplusplus) // CHECK: extern "C" { // CHECK: #endif @@ -43,6 +45,23 @@ func c_keywordArgNames(auto: Int, union: Int) {} func d_returnNever() -> Never { fatalError() } // CHECK-LABEL: SWIFT_EXTERN void return_never(void) SWIFT_NOEXCEPT SWIFT_NORETURN; +/// Pointer types +// CHECK: /// Pointer types + +@cdecl("pointers") +func f_pointers(_ x: UnsafeMutablePointer, + y: UnsafePointer, + z: UnsafeMutableRawPointer, + w: UnsafeRawPointer, + u: OpaquePointer) {} +// CHECK: SWIFT_EXTERN void pointers(ptrdiff_t * _Nonnull x, ptrdiff_t const * _Nonnull y, void * _Nonnull z, void const * _Nonnull w, void * _Nonnull u) SWIFT_NOEXCEPT; + +@cdecl("nullable_pointers") +func g_nullablePointers(_ x: UnsafeMutableRawPointer, + y: UnsafeMutableRawPointer?, + z: UnsafeMutableRawPointer!) {} +// CHECK: SWIFT_EXTERN void nullable_pointers(void * _Nonnull x, void * _Nullable y, void * _Null_unspecified z) SWIFT_NOEXCEPT; + // CHECK: #if defined(__cplusplus) // CHECK-NEXT: } // CHECK-NEXT: #endif diff --git a/test/attr/attr_cdecl_official.swift b/test/attr/attr_cdecl_official.swift index 2e8ecc4d857d1..4dc777e6c5993 100644 --- a/test/attr/attr_cdecl_official.swift +++ b/test/attr/attr_cdecl_official.swift @@ -55,3 +55,19 @@ class Foo { @cdecl("throwing") // expected-error{{raising errors from @cdecl functions is not supported}} func throwing() throws { } + +@cdecl("acceptedPointers") +func acceptedPointers(_ x: UnsafeMutablePointer, + y: UnsafePointer, + z: UnsafeMutableRawPointer, + w: UnsafeRawPointer, + u: OpaquePointer) {} + +@cdecl("rejectedPointers") +func rejectedPointers( // expected-error 6 {{global function cannot be marked '@cdecl' because the type of the parameter}} + x: UnsafePointer, // expected-note {{Swift structs cannot be represented in Objective-C}} // FIXME: Should reference C. + y: CVaListPointer, // expected-note {{Swift structs cannot be represented in Objective-C}} + z: UnsafeBufferPointer, // expected-note {{Swift structs cannot be represented in Objective-C}} + u: UnsafeMutableBufferPointer, // expected-note {{Swift structs cannot be represented in Objective-C}} + v: UnsafeRawBufferPointer, // expected-note {{Swift structs cannot be represented in Objective-C}} + t: UnsafeMutableRawBufferPointer) {} // expected-note {{Swift structs cannot be represented in Objective-C}}