Skip to content

Commit ae9f551

Browse files
committed
PrintAsClang: Print #include for headers used from @cdecl functions
Start printing `#include` for headers referenced from `@cdecl` function signatures. This adds on top of the existing tiered imports. We already print each module referenced from decls printed in the compatibility header. Previously we printed mostly `@import` with an option to fallback on a `#import`. This change adds a third fallback to `#include` when the module is referenced from a `@cdecl` function signature. The bridging header can also be imported in a similar way.
1 parent 9c7f0c7 commit ae9f551

File tree

3 files changed

+226
-15
lines changed

3 files changed

+226
-15
lines changed

lib/PrintAsClang/PrintAsClang.cpp

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,10 @@ writeImports(raw_ostream &out, llvm::SmallPtrSetImpl<ImportModuleTy> &imports,
409409
const FrontendOptions &frontendOpts,
410410
clang::HeaderSearch &clangHeaderSearchInfo,
411411
const llvm::StringMap<StringRef> &exposedModuleHeaderNames,
412-
bool useCxxImport = false) {
412+
bool useCxxImport = false,
413+
bool useNonModularIncludes = false) {
414+
useNonModularIncludes |= frontendOpts.EmitClangHeaderWithNonModularIncludes;
415+
413416
// Note: we can't use has_feature(modules) as it's always enabled in C++20
414417
// mode.
415418
out << "#if __has_feature(objc_modules)\n";
@@ -446,7 +449,7 @@ writeImports(raw_ostream &out, llvm::SmallPtrSetImpl<ImportModuleTy> &imports,
446449
llvm::vfs::FileSystem &fileSystem = fileManager.getVirtualFileSystem();
447450
llvm::ErrorOr<std::string> cwd = fileSystem.getCurrentWorkingDirectory();
448451

449-
if (frontendOpts.EmitClangHeaderWithNonModularIncludes) {
452+
if (useNonModularIncludes) {
450453
assert(cwd && "Access to current working directory required");
451454

452455
for (auto searchDir = clangHeaderSearchInfo.search_dir_begin();
@@ -498,7 +501,7 @@ writeImports(raw_ostream &out, llvm::SmallPtrSetImpl<ImportModuleTy> &imports,
498501
}
499502
if (seenImports.insert(Name).second) {
500503
out << importDirective << ' ' << Name.str() << importDirectiveLineEnd;
501-
if (frontendOpts.EmitClangHeaderWithNonModularIncludes) {
504+
if (useNonModularIncludes) {
502505
if (const clang::Module *underlyingClangModule =
503506
swiftModule->findUnderlyingClangModule()) {
504507
collectClangModuleHeaderIncludes(
@@ -521,19 +524,21 @@ writeImports(raw_ostream &out, llvm::SmallPtrSetImpl<ImportModuleTy> &imports,
521524
out << importDirective << ' ';
522525
ModuleDecl::ReverseFullNameIterator(clangModule).printForward(out);
523526
out << importDirectiveLineEnd;
524-
if (frontendOpts.EmitClangHeaderWithNonModularIncludes) {
527+
if (useNonModularIncludes) {
525528
collectClangModuleHeaderIncludes(
526529
clangModule, fileManager, requiredTextualIncludes, visitedModules,
527530
includeDirs, cwd.get());
528531
}
529532
}
530533
}
531534

532-
if (frontendOpts.EmitClangHeaderWithNonModularIncludes) {
533-
out << "#else\n";
534-
for (auto header : requiredTextualIncludes) {
535+
if (useNonModularIncludes && !requiredTextualIncludes.empty()) {
536+
out << "#elif defined(__OBJC__)\n";
537+
for (auto header : requiredTextualIncludes)
535538
out << "#import <" << header << ">\n";
536-
}
539+
out << "#else\n";
540+
for (auto header : requiredTextualIncludes)
541+
out << "#include <" << header << ">\n";
537542
}
538543
out << "#endif\n\n";
539544
for (const auto header : textualIncludes) {
@@ -544,8 +549,13 @@ writeImports(raw_ostream &out, llvm::SmallPtrSetImpl<ImportModuleTy> &imports,
544549
if (bridgingHeader.empty())
545550
out << "#import <" << M.getName().str() << '/' << M.getName().str()
546551
<< ".h>\n\n";
547-
else
548-
out << "#import \"" << bridgingHeader << "\"\n\n";
552+
else {
553+
out << "#if defined(__OBJC__)\n";
554+
out << "#import \"" << bridgingHeader << "\"\n";
555+
out << "#else\n";
556+
out << "#include \"" << bridgingHeader << "\"\n";
557+
out << "#endif\n\n";
558+
}
549559
}
550560
}
551561

@@ -606,17 +616,24 @@ bool swift::printAsClangHeader(raw_ostream &os, ModuleDecl *M,
606616
writePrologue(os, M->getASTContext(), computeMacroGuard(M));
607617

608618
// C content (@cdecl)
619+
std::string moduleContentsScratch;
609620
if (M->getASTContext().LangOpts.hasFeature(Feature::CDecl)) {
610621
SmallPtrSet<ImportModuleTy, 8> imports;
611-
emitExternC(os, [&] {
612-
printModuleContentsAsC(os, imports, *M, interopContext);
613-
});
622+
llvm::raw_string_ostream cModuleContents{moduleContentsScratch};
623+
printModuleContentsAsC(cModuleContents, imports, *M, interopContext);
624+
625+
llvm::StringMap<StringRef> exposedModuleHeaderNames;
626+
writeImports(os, imports, *M, bridgingHeader, frontendOpts,
627+
clangHeaderSearchInfo, exposedModuleHeaderNames,
628+
/*useCxxImport=*/false, /*useNonModularIncludes*/true);
629+
630+
emitExternC(os, [&] { os << "\n" << cModuleContents.str(); });
631+
moduleContentsScratch.clear();
614632
}
615633

616634
// Objective-C content
617635
SmallPtrSet<ImportModuleTy, 8> imports;
618-
std::string objcModuleContentsBuf;
619-
llvm::raw_string_ostream objcModuleContents{objcModuleContentsBuf};
636+
llvm::raw_string_ostream objcModuleContents{moduleContentsScratch};
620637
printModuleContentsAsObjC(objcModuleContents, imports, *M, interopContext);
621638
emitObjCConditional(os, [&] {
622639
llvm::StringMap<StringRef> exposedModuleHeaderNames;
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/// Include module for use from both C and Objective-C @cdecl variants.
2+
3+
// RUN: %empty-directory(%t)
4+
// RUN: split-file %s %t --leading-lines
5+
6+
/// Generate the compatibility header cdecl.h
7+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) %t/Lib.swift \
8+
// RUN: -emit-module -o %t -verify \
9+
// RUN: -emit-clang-header-path %t/cdecl.h \
10+
// RUN: -enable-experimental-feature CDecl
11+
12+
/// Check compatibility header directly
13+
// RUN: %FileCheck %s --input-file %t/cdecl.h
14+
// RUN: %check-in-clang %t/cdecl.h
15+
// RUN: %check-in-clang-c %t/cdecl.h
16+
// RUN: %check-in-clang-cxx %t/cdecl.h
17+
18+
// REQUIRES: swift_feature_CDecl
19+
// REQUIRES: objc_interop
20+
21+
//--- Lib.swift
22+
23+
import CoreGraphics
24+
import Foundation
25+
26+
// CHECK-NOT: Foundation;
27+
28+
/// Imports for C variant to @_cdecl
29+
30+
// CHECK: #if __has_feature(objc_modules)
31+
// CHECK: @import CoreGraphics;
32+
// CHECK-NEXT: #elif defined(__OBJC__)
33+
// CHECK-NEXT: #import <CoreGraphics.h>
34+
// CHECK-NEXT: #else
35+
// CHECK-NEXT: #include <CoreGraphics.h>
36+
// CHECK-NEXT: #endif
37+
38+
// CHECK: #if defined(__cplusplus)
39+
// CHECK: extern "C" {
40+
// CHECK: #endif
41+
42+
@cdecl("imports_cgpoint")
43+
public func importsCGPoint(pt: CGPoint) { }
44+
// CHECK: SWIFT_EXTERN void imports_cgpoint(CGPoint pt) SWIFT_NOEXCEPT;
45+
46+
// CHECK: #if defined(__cplusplus)
47+
// CHECK: } // extern "C"
48+
// CHECK: #endif
49+
50+
/// Imports for Objective-C variant to @_cdecl
51+
52+
@_cdecl("imports_cgpoint_objc")
53+
public func importsCGPointObjC(pt: CGPoint) { }
54+
// CHECK: #if defined(__OBJC__)
55+
// CHECK: #if __has_feature(objc_modules)
56+
// CHECK-NEXT: #if __has_warning("-Watimport-in-framework-header")
57+
// CHECK-NEXT: #pragma clang diagnostic ignored "-Watimport-in-framework-header"
58+
// CHECK-NEXT: #endif
59+
// CHECK-NEXT: @import CoreGraphics;
60+
// CHECK-NEXT: #endif
61+
62+
// CHECK: #if defined(__OBJC__)
63+
// CHECK: SWIFT_EXTERN void imports_cgpoint_objc(CGPoint pt) SWIFT_NOEXCEPT;
64+
// CHECK: #endif

test/PrintAsObjC/cdecl-includes.swift

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/// Print #includes for C clients and reference imported types.
2+
/// This test shouldn't require the objc runtime.
3+
4+
// RUN: %empty-directory(%t)
5+
// RUN: split-file %s %t --leading-lines
6+
7+
/// Generate the compatibility header cdecl.h
8+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) %t/Lib.swift \
9+
// RUN: -emit-module -verify -o %t -I %t -F %t \
10+
// RUN: -import-bridging-header %t/BridgingHeader.h \
11+
// RUN: -emit-clang-header-path %t/cdecl.h \
12+
// RUN: -disable-objc-interop \
13+
// RUN: -enable-experimental-feature CDecl
14+
15+
/// Check compatibility header directly
16+
// RUN: %FileCheck %s --input-file %t/cdecl.h
17+
// RUN: %check-in-clang-c -I %t -F %t %t/cdecl.h \
18+
// RUN: -isysroot %S/../Inputs/clang-importer-sdk
19+
20+
/// Compile a client against the compatibility header
21+
// RUN: %clang-no-modules -c %t/Client.c -I %t -F %t -Werror \
22+
// RUN: -isysroot %S/../Inputs/clang-importer-sdk
23+
24+
// REQUIRES: swift_feature_CDecl
25+
26+
//--- module.modulemap
27+
28+
module CModule {
29+
header "CModule_FileA.h"
30+
header "sub/CModule_FileB.h"
31+
}
32+
33+
//--- CModule_FileA.h
34+
35+
struct CStruct { int a; };
36+
37+
//--- sub/CModule_FileB.h
38+
39+
union CUnion { long a; float b; };
40+
41+
//--- CFramework.framework/Modules/module.modulemap
42+
43+
framework module CFramework {
44+
umbrella header "CFramework.h"
45+
}
46+
47+
//--- CFramework.framework/Headers/CFramework.h
48+
49+
typedef int IntFromCFramework;
50+
51+
//--- Dependency.h
52+
53+
typedef enum TKTimeSetting {
54+
TKTimeSettingLight,
55+
TKTimeSettingNormal,
56+
TKTimeSettingDark
57+
} TKTimeSetting;
58+
59+
//--- BridgingHeader.h
60+
61+
#include "Dependency.h"
62+
63+
//--- Lib.swift
64+
65+
import CModule
66+
import CFramework
67+
68+
// CHECK: #if __has_feature(objc_modules)
69+
// CHECK: @import CFramework;
70+
// CHECK-NEXT: @import CModule;
71+
// CHECK-NEXT: #elif defined(__OBJC__)
72+
// CHECK-NEXT: #import <CFramework{{/|\\\\}}CFramework.h>
73+
// CHECK-NEXT: #import <CModule_FileA.h>
74+
// CHECK-NEXT: #import <sub{{/|\\\\}}CModule_FileB.h>
75+
// CHECK-NEXT: #else
76+
// CHECK-NEXT: #include <CFramework{{/|\\\\}}CFramework.h>
77+
// CHECK-NEXT: #include <CModule_FileA.h>
78+
// CHECK-NEXT: #include <sub{{/|\\\\}}CModule_FileB.h>
79+
// CHECK-NEXT: #endif
80+
81+
// CHECK: #if defined(__OBJC__)
82+
// CHECK: #import "
83+
// CHECK-SAME: BridgingHeader.h"
84+
// CHECK-NEXT: #else
85+
// CHECK-NEXT: #include "
86+
// CHECK-SAME: BridgingHeader.h"
87+
// CHECK-NEXT: #endif
88+
89+
// CHECK-NOT: BridgingHeader
90+
91+
// CHECK: #if defined(__cplusplus)
92+
// CHECK: extern "C" {
93+
// CHECK: #endif
94+
95+
@cdecl("mirror_struct")
96+
public func a_mirrorStruct(_ a: CStruct) -> CStruct { a }
97+
// CHECK: SWIFT_EXTERN struct CStruct mirror_struct(struct CStruct a) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
98+
99+
@cdecl("mirror_union")
100+
public func b_mirrorStruct(_ a: CUnion) -> CUnion { a }
101+
// CHECK: SWIFT_EXTERN union CUnion mirror_union(union CUnion a) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
102+
103+
104+
@cdecl("TKGetDefaultToastSetting")
105+
public func c_defaultToastSetting() -> TKTimeSetting { TKTimeSettingNormal } // It would be nice to import TKTimeSettingNormal as a member.
106+
// CHECK: SWIFT_EXTERN TKTimeSetting TKGetDefaultToastSetting(void) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
107+
108+
@cdecl("get_int_alias")
109+
public func d_getIntAlias() -> IntFromCFramework { 42 }
110+
// CHECK: SWIFT_EXTERN IntFromCFramework get_int_alias(void) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
111+
112+
// CHECK: #if defined(__cplusplus)
113+
// CHECK-NEXT: }
114+
// CHECK-NEXT: #endif
115+
116+
//--- Client.c
117+
118+
#include "cdecl.h"
119+
120+
int main() {
121+
struct CStruct s = { 42 };
122+
struct CStruct s_out = mirror_struct(s);
123+
124+
union CUnion u = { 43 };
125+
union CUnion u_out = mirror_union(u);
126+
127+
TKTimeSetting def = TKGetDefaultToastSetting();
128+
129+
IntFromCFramework i = get_int_alias();
130+
}

0 commit comments

Comments
 (0)