Skip to content

Commit 25f0279

Browse files
committed
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.
1 parent d759b75 commit 25f0279

File tree

6 files changed

+163
-1
lines changed

6 files changed

+163
-1
lines changed

lib/PrintAsClang/DeclAndTypePrinter.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3006,6 +3006,14 @@ bool DeclAndTypePrinter::shouldInclude(const ValueDecl *VD) {
30063006
return false;
30073007
}
30083008

3009+
// In C output mode print only @cdecls and skip them in other modes.
3010+
bool isCDeclForC = false;
3011+
auto *FD = dyn_cast<AbstractFunctionDecl>(VD);
3012+
if (FD)
3013+
isCDeclForC = FD->getCDeclKind() == ForeignLanguage::C;
3014+
if (isCDeclForC != (outputLang == OutputLanguageMode::C))
3015+
return false;
3016+
30093017
if (VD->getAttrs().hasAttribute<ImplementationOnlyAttr>())
30103018
return false;
30113019

lib/PrintAsClang/ModuleContentsWriter.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,6 +1118,17 @@ void swift::printModuleContentsAsObjC(
11181118
.write();
11191119
}
11201120

1121+
void swift::printModuleContentsAsC(
1122+
raw_ostream &os, llvm::SmallPtrSetImpl<ImportModuleTy> &imports,
1123+
ModuleDecl &M, SwiftToClangInteropContext &interopContext) {
1124+
llvm::raw_null_ostream prologueOS;
1125+
llvm::StringSet<> exposedModules;
1126+
ModuleWriter(os, prologueOS, imports, M, interopContext, getRequiredAccess(M),
1127+
/*requiresExposedAttribute=*/false, exposedModules,
1128+
OutputLanguageMode::C)
1129+
.write();
1130+
}
1131+
11211132
EmittedClangHeaderDependencyInfo swift::printModuleContentsAsCxx(
11221133
raw_ostream &os, ModuleDecl &M, SwiftToClangInteropContext &interopContext,
11231134
bool requiresExposedAttribute, llvm::StringSet<> &exposedModules) {

lib/PrintAsClang/ModuleContentsWriter.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ void printModuleContentsAsObjC(raw_ostream &os,
3737
ModuleDecl &M,
3838
SwiftToClangInteropContext &interopContext);
3939

40+
void printModuleContentsAsC(raw_ostream &os,
41+
llvm::SmallPtrSetImpl<ImportModuleTy> &imports,
42+
ModuleDecl &M,
43+
SwiftToClangInteropContext &interopContext);
44+
4045
struct EmittedClangHeaderDependencyInfo {
4146
/// The set of imported modules used by this module.
4247
SmallPtrSet<ImportModuleTy, 8> imports;

lib/PrintAsClang/PrintAsClang.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,17 @@ static void emitObjCConditional(raw_ostream &out,
5858
out << "#endif\n";
5959
}
6060

61+
static void emitExternC(raw_ostream &out,
62+
llvm::function_ref<void()> cCase) {
63+
emitCxxConditional(out, [&] {
64+
out << "extern \"C\" {\n";
65+
});
66+
cCase();
67+
emitCxxConditional(out, [&] {
68+
out << "} // extern \"C\"\n";
69+
});
70+
}
71+
6172
static void writePtrauthPrologue(raw_ostream &os, ASTContext &ctx) {
6273
emitCxxConditional(os, [&]() {
6374
ClangSyntaxPrinter(ctx, os).printIgnoredDiagnosticBlock(
@@ -586,12 +597,21 @@ bool swift::printAsClangHeader(raw_ostream &os, ModuleDecl *M,
586597
llvm::PrettyStackTraceString trace("While generating Clang header");
587598

588599
SwiftToClangInteropContext interopContext(*M, irGenOpts);
600+
writePrologue(os, M->getASTContext(), computeMacroGuard(M));
589601

602+
// C content (@cdecl)
603+
if (M->getASTContext().LangOpts.hasFeature(Feature::CDecl)) {
604+
SmallPtrSet<ImportModuleTy, 8> imports;
605+
emitExternC(os, [&] {
606+
printModuleContentsAsC(os, imports, *M, interopContext);
607+
});
608+
}
609+
610+
// Objective-C content
590611
SmallPtrSet<ImportModuleTy, 8> imports;
591612
std::string objcModuleContentsBuf;
592613
llvm::raw_string_ostream objcModuleContents{objcModuleContentsBuf};
593614
printModuleContentsAsObjC(objcModuleContents, imports, *M, interopContext);
594-
writePrologue(os, M->getASTContext(), computeMacroGuard(M));
595615
emitObjCConditional(os, [&] {
596616
llvm::StringMap<StringRef> exposedModuleHeaderNames;
597617
writeImports(os, imports, *M, bridgingHeader, frontendOpts,
@@ -600,6 +620,8 @@ bool swift::printAsClangHeader(raw_ostream &os, ModuleDecl *M,
600620
writePostImportPrologue(os, *M);
601621
emitObjCConditional(os, [&] { os << "\n" << objcModuleContents.str(); });
602622
writeObjCEpilogue(os);
623+
624+
// C++ content
603625
emitCxxConditional(os, [&] {
604626
// FIXME: Expose Swift with @expose by default.
605627
bool enableCxx = frontendOpts.ClangHeaderExposedDecls.has_value() ||

test/PrintAsObjC/cdecl-official.swift

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t --leading-lines
3+
4+
/// Generate cdecl.h
5+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) \
6+
// RUN: %t/Lib.swift -emit-module -verify -o %t -emit-module-doc \
7+
// RUN: -emit-objc-header-path %t/cdecl.h \
8+
// RUN: -enable-experimental-feature CDecl
9+
10+
/// Check cdecl.h directly
11+
// RUN: %FileCheck %s --input-file %t/cdecl.h
12+
// RUN: %check-in-clang %t/cdecl.h
13+
// RUN: %check-in-clang-c %t/cdecl.h -Wnullable-to-nonnull-conversion
14+
// RUN: %check-in-clang-cxx %t/cdecl.h
15+
16+
/// Build a client against cdecl.h
17+
// RUN: %clang -c %t/Client.c -fobjc-arc -fmodules -I %t \
18+
// RUN: -F %S/../Inputs/clang-importer-sdk-path/frameworks \
19+
// RUN: -I %clang-include-dir -Werror \
20+
// RUN: -isysroot %S/../Inputs/clang-importer-sdk
21+
22+
// REQUIRES: swift_feature_CDecl
23+
24+
//--- Lib.swift
25+
26+
// CHECK: #if defined(__cplusplus)
27+
// CHECK: extern "C" {
28+
// CHECK: #endif
29+
30+
/// My documentation
31+
@cdecl("simple")
32+
func a_simple(x: Int, bar y: Int) -> Int { return x }
33+
// CHECK-LABEL: // My documentation
34+
// CHECK-LABEL: SWIFT_EXTERN ptrdiff_t simple(ptrdiff_t x, ptrdiff_t y) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
35+
36+
@cdecl("primitiveTypes")
37+
public func b_primitiveTypes(i: Int, ci: CInt, l: CLong, c: CChar, f: Float, d: Double, b: Bool) {}
38+
// CHECK-LABEL: SWIFT_EXTERN void primitiveTypes(ptrdiff_t i, int ci, long l, char c, float f, double d, bool b) SWIFT_NOEXCEPT;
39+
40+
@cdecl("has_keyword_arg_names")
41+
func c_keywordArgNames(auto: Int, union: Int) {}
42+
// CHECK-LABEL: SWIFT_EXTERN void has_keyword_arg_names(ptrdiff_t auto_, ptrdiff_t union_) SWIFT_NOEXCEPT;
43+
44+
@cdecl("return_never")
45+
func d_returnNever() -> Never { fatalError() }
46+
// CHECK-LABEL: SWIFT_EXTERN void return_never(void) SWIFT_NOEXCEPT SWIFT_NORETURN;
47+
48+
@cdecl("block_nightmare")
49+
public func s_block_nightmare(x: @convention(block) (Int) -> Float)
50+
-> @convention(block) (CChar) -> Double { return { _ in 0 } }
51+
// CHECK-LABEL: SWIFT_EXTERN double (^ _Nonnull block_nightmare(SWIFT_NOESCAPE float (^ _Nonnull x)(ptrdiff_t)))(char) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
52+
53+
@cdecl("block_recurring_nightmare")
54+
public func t_block_recurring_nightmare(x: @escaping @convention(block) (@convention(block) (Double) -> Int) -> Float)
55+
-> @convention(block) (_ asdfasdf: @convention(block) (CUnsignedChar) -> CChar) -> Double {
56+
fatalError()
57+
}
58+
// 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;
59+
60+
@cdecl("function_pointer_nightmare")
61+
func u_function_pointer_nightmare(x: @convention(c) (Int) -> Float)
62+
-> @convention(c) (CChar) -> Double { return { _ in 0 } }
63+
// CHECK-LABEL: SWIFT_EXTERN double (* _Nonnull function_pointer_nightmare(float (* _Nonnull x)(ptrdiff_t)))(char) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
64+
65+
@cdecl("function_pointer_recurring_nightmare")
66+
public func v_function_pointer_recurring_nightmare(x: @escaping @convention(c) (@convention(c) (Double) -> Int) -> Float)
67+
-> @convention(c) (@convention(c) (CUnsignedChar) -> CChar) -> Double {
68+
fatalError()
69+
}
70+
// 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;
71+
72+
// CHECK: #if defined(__cplusplus)
73+
// CHECK-NEXT: }
74+
// CHECK-NEXT: #endif
75+
76+
//--- Client.c
77+
78+
#include "cdecl.h"
79+
80+
int main() {
81+
ptrdiff_t x = simple(42, 43);
82+
primitiveTypes(1, 2, 3, 'a', 1.0f, 2.0, true);
83+
has_keyword_arg_names(1, 2);
84+
return_never();
85+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/// Ensure we print @cdecl and @_cdecl only once.
2+
3+
/// Generate cdecl.h
4+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) \
5+
// RUN: %s -emit-module -verify -o %t -emit-module-doc \
6+
// RUN: -emit-objc-header-path %t/cdecl.h \
7+
// RUN: -disable-objc-attr-requires-foundation-module \
8+
// RUN: -enable-experimental-feature CDecl
9+
10+
/// Check cdecl.h directly
11+
// RUN: %FileCheck %s --input-file %t/cdecl.h
12+
// RUN: %check-in-clang %t/cdecl.h
13+
// RUN: %check-in-clang-c %t/cdecl.h -Wnullable-to-nonnull-conversion
14+
// RUN: %check-in-clang-cxx %t/cdecl.h
15+
16+
// REQUIRES: swift_feature_CDecl
17+
// REQUIRES: objc_interop
18+
19+
@cdecl("cFunc")
20+
func cFunc() { }
21+
// CHECK: cFunc
22+
// CHECK-NOT: cFunc
23+
24+
/// The class would break C parsing if printed in wrong block
25+
@objc
26+
class ObjCClass {}
27+
28+
@_cdecl("objcFunc")
29+
func objcFunc() -> ObjCClass! { return ObjCClass() }
30+
// CHECK: objcFunc
31+
// CHECK-NOT: objcFunc

0 commit comments

Comments
 (0)