Skip to content

Commit 29d729b

Browse files
authored
Merge pull request #81481 from xymus/cdecl-includes
PrintAsClang: Print `#include` to import headers used from `@cdecl` functions
2 parents 7d6447b + 4f71913 commit 29d729b

7 files changed

+242
-21
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: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
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 -F %t \
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 -F %t
15+
// RUN: %check-in-clang-c %t/cdecl.h -F %t
16+
// RUN: %check-in-clang-cxx %t/cdecl.h -F %t
17+
18+
// REQUIRES: swift_feature_CDecl
19+
// REQUIRES: objc_interop
20+
21+
//--- CFramework.framework/Modules/module.modulemap
22+
23+
framework module CFramework {
24+
umbrella header "CFramework.h"
25+
}
26+
27+
//--- CFramework.framework/Headers/CFramework.h
28+
29+
typedef int IntFromCFramework;
30+
31+
//--- Lib.swift
32+
33+
import CFramework
34+
import CoreGraphics
35+
import Foundation
36+
37+
// CHECK-NOT: Foundation;
38+
39+
/// Imports for C variant to @_cdecl
40+
41+
// CHECK: #if __has_feature(objc_modules)
42+
// CHECK: @import CFramework;
43+
// CHECK-NEXT: @import CoreGraphics;
44+
// CHECK-NEXT: #elif defined(__OBJC__)
45+
// CHECK-NEXT: #import <CFramework{{[/\\]}}CFramework.h>
46+
// CHECK-NEXT: #import <CoreGraphics.h>
47+
// CHECK-NEXT: #else
48+
// CHECK-NEXT: #include <CFramework{{[/\\]}}CFramework.h>
49+
// CHECK-NEXT: #include <CoreGraphics.h>
50+
// CHECK-NEXT: #endif
51+
52+
// CHECK: #if defined(__cplusplus)
53+
// CHECK: extern "C" {
54+
// CHECK: #endif
55+
56+
@cdecl("get_int_alias")
57+
public func getIntAlias() -> IntFromCFramework { 42 }
58+
// CHECK: SWIFT_EXTERN IntFromCFramework get_int_alias(void) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
59+
60+
@cdecl("imports_cgpoint")
61+
public func importsCGPoint(pt: CGPoint) { }
62+
// CHECK: SWIFT_EXTERN void imports_cgpoint(CGPoint pt) SWIFT_NOEXCEPT;
63+
64+
// CHECK: #if defined(__cplusplus)
65+
// CHECK: } // extern "C"
66+
// CHECK: #endif
67+
68+
/// Imports for Objective-C variant to @_cdecl
69+
70+
@_cdecl("imports_cgpoint_objc")
71+
public func importsCGPointObjC(pt: CGPoint) { }
72+
// CHECK: #if defined(__OBJC__)
73+
// CHECK: #if __has_feature(objc_modules)
74+
// CHECK-NEXT: #if __has_warning("-Watimport-in-framework-header")
75+
// CHECK-NEXT: #pragma clang diagnostic ignored "-Watimport-in-framework-header"
76+
// CHECK-NEXT: #endif
77+
// CHECK-NEXT: @import CoreGraphics;
78+
// CHECK-NEXT: #endif
79+
80+
// CHECK: #if defined(__OBJC__)
81+
// CHECK: SWIFT_EXTERN void imports_cgpoint_objc(CGPoint pt) SWIFT_NOEXCEPT;
82+
// CHECK: #endif

test/PrintAsObjC/cdecl-includes.swift

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
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 \
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 %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 -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+
//--- Dependency.h
42+
43+
typedef enum TKTimeSetting {
44+
TKTimeSettingLight,
45+
TKTimeSettingNormal,
46+
TKTimeSettingDark
47+
} TKTimeSetting;
48+
49+
//--- BridgingHeader.h
50+
51+
#include "Dependency.h"
52+
53+
//--- Lib.swift
54+
55+
import CModule
56+
57+
// CHECK: #if __has_feature(objc_modules)
58+
// CHECK: @import CModule;
59+
// CHECK-NEXT: #elif defined(__OBJC__)
60+
// CHECK-NEXT: #import <CModule_FileA.h>
61+
// CHECK-NEXT: #import <sub{{[/\\]}}CModule_FileB.h>
62+
// CHECK-NEXT: #else
63+
// CHECK-NEXT: #include <CModule_FileA.h>
64+
// CHECK-NEXT: #include <sub{{[/\\]}}CModule_FileB.h>
65+
// CHECK-NEXT: #endif
66+
67+
// CHECK: #if defined(__OBJC__)
68+
// CHECK: #import "
69+
// CHECK-SAME: BridgingHeader.h"
70+
// CHECK-NEXT: #else
71+
// CHECK-NEXT: #include "
72+
// CHECK-SAME: BridgingHeader.h"
73+
// CHECK-NEXT: #endif
74+
75+
// CHECK-NOT: BridgingHeader
76+
77+
// CHECK: #if defined(__cplusplus)
78+
// CHECK: extern "C" {
79+
// CHECK: #endif
80+
81+
@cdecl("mirror_struct")
82+
public func a_mirrorStruct(_ a: CStruct) -> CStruct { a }
83+
// CHECK: SWIFT_EXTERN struct CStruct mirror_struct(struct CStruct a) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
84+
85+
@cdecl("mirror_union")
86+
public func b_mirrorStruct(_ a: CUnion) -> CUnion { a }
87+
// CHECK: SWIFT_EXTERN union CUnion mirror_union(union CUnion a) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
88+
89+
90+
@cdecl("TKGetDefaultToastSetting")
91+
public func c_defaultToastSetting() -> TKTimeSetting { TKTimeSettingNormal } // It would be nice to import TKTimeSettingNormal as a member.
92+
// CHECK: SWIFT_EXTERN TKTimeSetting TKGetDefaultToastSetting(void) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
93+
94+
// CHECK: #if defined(__cplusplus)
95+
// CHECK-NEXT: }
96+
// CHECK-NEXT: #endif
97+
98+
//--- Client.c
99+
100+
#include "cdecl.h"
101+
102+
int main() {
103+
struct CStruct s = { 42 };
104+
struct CStruct s_out = mirror_struct(s);
105+
106+
union CUnion u = { 43 };
107+
union CUnion u_out = mirror_union(u);
108+
109+
TKTimeSetting def = TKGetDefaultToastSetting();
110+
}

test/PrintAsObjC/emit-clang-header-nonmodular-includes-mock-sdk.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -F %S/Inputs/ -typecheck -verify -emit-objc-header-path %t/textual-imports.h -emit-clang-header-nonmodular-includes %s
55
// RUN: %FileCheck %s < %t/textual-imports.h
66
// RUN: %check-in-clang -fno-modules -Qunused-arguments %t/textual-imports.h -F %S/Inputs
7+
// RUN: %check-in-clang-c %t/textual-imports.h -F %S/Inputs
78

89
import Foundation
910
import Mixed
@@ -30,9 +31,14 @@ public class HelloWorld: NSObject {
3031
// CHECK-NEXT: @import CoreGraphics;
3132
// CHECK-NEXT: @import Mixed;
3233
// CHECK-NEXT: @import ObjectiveC;
33-
// CHECK-NEXT: #else
34+
// CHECK-NEXT: #elif defined(__OBJC__)
3435
// CHECK-NEXT: #import <CoreGraphics.h>
3536
// CHECK-NEXT: #import <Mixed/Mixed.h>
3637
// CHECK-NEXT: #import <objc/objc.h>
3738
// CHECK-NEXT: #import <objc/NSObject.h>
39+
// CHECK-NEXT: #else
40+
// CHECK-NEXT: #include <CoreGraphics.h>
41+
// CHECK-NEXT: #include <Mixed/Mixed.h>
42+
// CHECK-NEXT: #include <objc/objc.h>
43+
// CHECK-NEXT: #include <objc/NSObject.h>
3844
// CHECK-NEXT: #endif

test/PrintAsObjC/emit-clang-header-nonmodular-includes-modulemap-not-in-include-dir.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ public class Bar : Baz {}
1616
// CHECK-NEXT: #pragma clang diagnostic ignored "-Watimport-in-framework-header"
1717
// CHECK-NEXT: #endif
1818
// CHECK-NEXT: @import EmitClangHeaderNonmodularIncludesStressTest;
19-
// CHECK-NEXT: #else
20-
// CHECK: #import <header-regular.h>
19+
// CHECK-NEXT: #elif defined(__OBJC__)
20+
// CHECK-NEXT: #import <header-regular.h>
21+
// CHECK: #else
22+
// CHECK-NEXT: #include <header-regular.h>
2123
// CHECK: #endif

test/PrintAsObjC/emit-clang-header-nonmodular-includes-path-normalization.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ public class Bar : Baz {}
1414
// CHECK-NEXT: #pragma clang diagnostic ignored "-Watimport-in-framework-header"
1515
// CHECK-NEXT: #endif
1616
// CHECK-NEXT: @import EmitClangHeaderNonmodularIncludesStressTest;
17-
// CHECK-NEXT: #else
18-
// CHECK: #import <header-regular.h>
17+
// CHECK-NEXT: #elif defined(__OBJC__)
18+
// CHECK-NEXT: #import <header-regular.h>
19+
// CHECK: #else
20+
// CHECK-NEXT: #include <header-regular.h>
1921
// CHECK: #endif

test/PrintAsObjC/emit-clang-header-nonmodular-includes-symlinked-header.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ public class Bar : Foo {}
1616
// CHECK-NEXT: #pragma clang diagnostic ignored "-Watimport-in-framework-header"
1717
// CHECK-NEXT: #endif
1818
// CHECK-NEXT: @import EmitClangHeaderNonmodularIncludesStressTest;
19-
// CHECK-NEXT: #else
19+
// CHECK-NEXT: #elif defined(__OBJC__)
2020
// CHECK: #import <header-symlink.h>
21+
// CHECK-NEXT: #else
22+
// CHECK: #include <header-symlink.h>
2123
// CHECK: #endif

0 commit comments

Comments
 (0)