Skip to content

Commit 6486c3c

Browse files
committed
[interop] add an option to emit C++ header interface for a module
1 parent 4f7a205 commit 6486c3c

16 files changed

+183
-30
lines changed

include/swift/AST/DiagnosticsFrontend.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ ERROR(error_mode_cannot_emit_reference_dependencies,none,
119119
"this mode does not support emitting reference dependency files", ())
120120
ERROR(error_mode_cannot_emit_header,none,
121121
"this mode does not support emitting Objective-C headers", ())
122+
ERROR(error_mode_cannot_emit_cxx_header,none,
123+
"this mode does not support emitting C++ headers", ())
122124
ERROR(error_mode_cannot_emit_loaded_module_trace,none,
123125
"this mode does not support emitting the loaded module trace", ())
124126
ERROR(error_mode_cannot_emit_module,none,

include/swift/Basic/SupplementaryOutputPaths.h

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,17 @@ struct SupplementaryOutputPaths {
3131
/// \sa swift::printAsObjC
3232
std::string ObjCHeaderOutputPath;
3333

34+
/// The path to which we should emit a C++ header for the module.
35+
///
36+
/// Currently only makes sense when the compiler has whole module knowledge.
37+
/// The modes for which it makes sense include both WMO and the "merge
38+
/// modules" job that happens after the normal compilation jobs. That's where
39+
/// the header is emitted in single-file mode, since it needs whole-module
40+
/// information.
41+
///
42+
/// \sa swift::printAsCxx
43+
std::string CxxHeaderOutputPath;
44+
3445
/// The path to which we should emit a serialized module.
3546
/// It is valid whenever there are any inputs.
3647
///
@@ -160,7 +171,9 @@ struct SupplementaryOutputPaths {
160171
/// Apply a given function for each existing (non-empty string) supplementary output
161172
void forEachSetOutput(llvm::function_ref<void(const std::string&)> fn) const {
162173
if (!ObjCHeaderOutputPath.empty())
163-
fn(ObjCHeaderOutputPath);
174+
fn(ObjCHeaderOutputPath);
175+
if (!CxxHeaderOutputPath.empty())
176+
fn(CxxHeaderOutputPath);
164177
if (!ModuleOutputPath.empty())
165178
fn(ModuleOutputPath);
166179
if (!ModuleSourceInfoOutputPath.empty())
@@ -196,14 +209,16 @@ struct SupplementaryOutputPaths {
196209
}
197210

198211
bool empty() const {
199-
return ObjCHeaderOutputPath.empty() && ModuleOutputPath.empty() &&
200-
ModuleDocOutputPath.empty() && DependenciesFilePath.empty() &&
212+
return ObjCHeaderOutputPath.empty() && CxxHeaderOutputPath.empty() &&
213+
ModuleOutputPath.empty() && ModuleDocOutputPath.empty() &&
214+
DependenciesFilePath.empty() &&
201215
ReferenceDependenciesFilePath.empty() &&
202216
SerializedDiagnosticsPath.empty() && LoadedModuleTracePath.empty() &&
203217
TBDPath.empty() && ModuleInterfaceOutputPath.empty() &&
204-
ModuleSourceInfoOutputPath.empty() && ABIDescriptorOutputPath.empty() &&
205-
ModuleSemanticInfoOutputPath.empty() &&
206-
YAMLOptRecordPath.empty() && BitstreamOptRecordPath.empty();
218+
ModuleSourceInfoOutputPath.empty() &&
219+
ABIDescriptorOutputPath.empty() &&
220+
ModuleSemanticInfoOutputPath.empty() && YAMLOptRecordPath.empty() &&
221+
BitstreamOptRecordPath.empty();
207222
}
208223
};
209224
} // namespace swift

include/swift/Frontend/Frontend.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@ class CompilerInvocation {
388388
std::string getOutputFilenameForAtMostOnePrimary() const;
389389
std::string getMainInputFilenameForDebugInfoForAtMostOnePrimary() const;
390390
std::string getObjCHeaderOutputPathForAtMostOnePrimary() const;
391+
std::string getCxxHeaderOutputPathForAtMostOnePrimary() const;
391392
std::string getModuleOutputPathForAtMostOnePrimary() const;
392393
std::string
393394
getReferenceDependenciesFilePathForPrimary(StringRef filename) const;

include/swift/Frontend/FrontendInputsAndOutputs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ class FrontendInputsAndOutputs {
250250
bool hasDependenciesPath() const;
251251
bool hasReferenceDependenciesPath() const;
252252
bool hasObjCHeaderOutputPath() const;
253+
bool hasCxxHeaderOutputPath() const;
253254
bool hasLoadedModuleTracePath() const;
254255
bool hasModuleOutputPath() const;
255256
bool hasModuleDocOutputPath() const;

include/swift/Frontend/FrontendOptions.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ class FrontendOptions {
450450
private:
451451
static bool canActionEmitDependencies(ActionType);
452452
static bool canActionEmitReferenceDependencies(ActionType);
453-
static bool canActionEmitObjCHeader(ActionType);
453+
static bool canActionEmitClangHeader(ActionType);
454454
static bool canActionEmitLoadedModuleTrace(ActionType);
455455
static bool canActionEmitModule(ActionType);
456456
static bool canActionEmitModuleDoc(ActionType);

include/swift/Option/Options.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,14 @@ def emit_objc_header_path : Separate<["-"], "emit-objc-header-path">,
532532
SupplementaryOutput]>,
533533
MetaVarName<"<path>">, HelpText<"Emit an Objective-C header file to <path>">;
534534

535+
def emit_cxx_header : Flag<["-"], "emit-cxx-header">,
536+
Flags<[FrontendOption, NoInteractiveOption, SupplementaryOutput]>,
537+
HelpText<"Emit a C++ header file">;
538+
def emit_cxx_header_path : Separate<["-"], "emit-cxx-header-path">,
539+
Flags<[FrontendOption, NoInteractiveOption, ArgumentIsPath,
540+
SupplementaryOutput]>,
541+
MetaVarName<"<path>">, HelpText<"Emit a C++ header file to <path>">;
542+
535543
def static : Flag<["-"], "static">,
536544
Flags<[FrontendOption, ModuleInterfaceOption, NoInteractiveOption]>,
537545
HelpText<"Make this module statically linkable and make the output of -emit-library a static library.">;

include/swift/PrintAsObjC/PrintAsObjC.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ namespace swift {
2626
///
2727
/// Returns true on error.
2828
bool printAsObjC(raw_ostream &out, ModuleDecl *M, StringRef bridgingHeader);
29+
30+
/// Print the C++-compatible declarations in a module as a Clang header.
31+
///
32+
/// Returns true on error.
33+
bool printAsCXX(raw_ostream &os, ModuleDecl *M);
2934
}
3035

3136
#endif

lib/Frontend/ArgsToFrontendOptionsConverter.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -623,11 +623,16 @@ bool ArgsToFrontendOptionsConverter::checkUnusedSupplementaryOutputPaths()
623623
diag::error_mode_cannot_emit_reference_dependencies);
624624
return true;
625625
}
626-
if (!FrontendOptions::canActionEmitObjCHeader(Opts.RequestedAction) &&
626+
if (!FrontendOptions::canActionEmitClangHeader(Opts.RequestedAction) &&
627627
Opts.InputsAndOutputs.hasObjCHeaderOutputPath()) {
628628
Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_header);
629629
return true;
630630
}
631+
if (!FrontendOptions::canActionEmitClangHeader(Opts.RequestedAction) &&
632+
Opts.InputsAndOutputs.hasCxxHeaderOutputPath()) {
633+
Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_cxx_header);
634+
return true;
635+
}
631636
if (!FrontendOptions::canActionEmitLoadedModuleTrace(Opts.RequestedAction) &&
632637
Opts.InputsAndOutputs.hasLoadedModuleTracePath()) {
633638
Diags.diagnose(SourceLoc(),

lib/Frontend/ArgsToFrontendOutputsConverter.cpp

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,8 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments()
310310

311311
auto objCHeaderOutput = getSupplementaryFilenamesFromArguments(
312312
options::OPT_emit_objc_header_path);
313+
auto cxxHeaderOutput =
314+
getSupplementaryFilenamesFromArguments(options::OPT_emit_cxx_header_path);
313315
auto moduleOutput =
314316
getSupplementaryFilenamesFromArguments(options::OPT_emit_module_path);
315317
auto moduleDocOutput =
@@ -339,8 +341,8 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments()
339341
options::OPT_emit_module_semantic_info_path);
340342
auto optRecordOutput = getSupplementaryFilenamesFromArguments(
341343
options::OPT_save_optimization_record_path);
342-
if (!objCHeaderOutput || !moduleOutput || !moduleDocOutput ||
343-
!dependenciesFile || !referenceDependenciesFile ||
344+
if (!objCHeaderOutput || !cxxHeaderOutput || !moduleOutput ||
345+
!moduleDocOutput || !dependenciesFile || !referenceDependenciesFile ||
344346
!serializedDiagnostics || !fixItsOutput || !loadedModuleTrace || !TBD ||
345347
!moduleInterfaceOutput || !privateModuleInterfaceOutput ||
346348
!moduleSourceInfoOutput || !moduleSummaryOutput || !abiDescriptorOutput ||
@@ -354,6 +356,7 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments()
354356
for (unsigned i = 0; i < N; ++i) {
355357
SupplementaryOutputPaths sop;
356358
sop.ObjCHeaderOutputPath = (*objCHeaderOutput)[i];
359+
sop.CxxHeaderOutputPath = (*cxxHeaderOutput)[i];
357360
sop.ModuleOutputPath = (*moduleOutput)[i];
358361
sop.ModuleDocOutputPath = (*moduleDocOutput)[i];
359362
sop.DependenciesFilePath = (*dependenciesFile)[i];
@@ -439,6 +442,11 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput(
439442
file_types::TY_ObjCHeader, "",
440443
defaultSupplementaryOutputPathExcludingExtension);
441444

445+
auto cxxHeaderOutputPath = determineSupplementaryOutputFilename(
446+
OPT_emit_cxx_header, pathsFromArguments.CxxHeaderOutputPath,
447+
file_types::TY_ObjCHeader, "",
448+
defaultSupplementaryOutputPathExcludingExtension);
449+
442450
auto loadedModuleTracePath = determineSupplementaryOutputFilename(
443451
OPT_emit_loaded_module_trace, pathsFromArguments.LoadedModuleTracePath,
444452
file_types::TY_ModuleTrace, "",
@@ -493,6 +501,7 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput(
493501

494502
SupplementaryOutputPaths sop;
495503
sop.ObjCHeaderOutputPath = objcHeaderOutputPath;
504+
sop.CxxHeaderOutputPath = cxxHeaderOutputPath;
496505
sop.ModuleOutputPath = moduleOutputPath;
497506
sop.ModuleDocOutputPath = moduleDocOutputPath;
498507
sop.DependenciesFilePath = dependenciesFilePath;
@@ -578,6 +587,7 @@ createFromTypeToPathMap(const TypeToPathMap *map) {
578587
return paths;
579588
const std::pair<file_types::ID, std::string &> typesAndStrings[] = {
580589
{file_types::TY_ObjCHeader, paths.ObjCHeaderOutputPath},
590+
{file_types::TY_ObjCHeader, paths.CxxHeaderOutputPath},
581591
{file_types::TY_SwiftModuleFile, paths.ModuleOutputPath},
582592
{file_types::TY_SwiftModuleDocFile, paths.ModuleDocOutputPath},
583593
{file_types::TY_SwiftSourceInfoFile, paths.ModuleSourceInfoOutputPath},
@@ -606,17 +616,16 @@ createFromTypeToPathMap(const TypeToPathMap *map) {
606616
Optional<std::vector<SupplementaryOutputPaths>>
607617
SupplementaryOutputPathsComputer::readSupplementaryOutputFileMap() const {
608618
if (Arg *A = Args.getLastArg(
609-
options::OPT_emit_objc_header_path,
610-
options::OPT_emit_module_path,
611-
options::OPT_emit_module_doc_path,
612-
options::OPT_emit_dependencies_path,
613-
options::OPT_emit_reference_dependencies_path,
614-
options::OPT_serialize_diagnostics_path,
615-
options::OPT_emit_loaded_module_trace_path,
616-
options::OPT_emit_module_interface_path,
617-
options::OPT_emit_private_module_interface_path,
618-
options::OPT_emit_module_source_info_path,
619-
options::OPT_emit_tbd_path)) {
619+
options::OPT_emit_objc_header_path, options::OPT_emit_cxx_header_path,
620+
options::OPT_emit_module_path, options::OPT_emit_module_doc_path,
621+
options::OPT_emit_dependencies_path,
622+
options::OPT_emit_reference_dependencies_path,
623+
options::OPT_serialize_diagnostics_path,
624+
options::OPT_emit_loaded_module_trace_path,
625+
options::OPT_emit_module_interface_path,
626+
options::OPT_emit_private_module_interface_path,
627+
options::OPT_emit_module_source_info_path,
628+
options::OPT_emit_tbd_path)) {
620629
Diags.diagnose(SourceLoc(),
621630
diag::error_cannot_have_supplementary_outputs,
622631
A->getSpelling(), "-supplementary-output-file-map");

lib/Frontend/Frontend.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ CompilerInvocation::getObjCHeaderOutputPathForAtMostOnePrimary() const {
9696
return getPrimarySpecificPathsForAtMostOnePrimary()
9797
.SupplementaryOutputs.ObjCHeaderOutputPath;
9898
}
99+
std::string
100+
CompilerInvocation::getCxxHeaderOutputPathForAtMostOnePrimary() const {
101+
return getPrimarySpecificPathsForAtMostOnePrimary()
102+
.SupplementaryOutputs.CxxHeaderOutputPath;
103+
}
99104
std::string CompilerInvocation::getModuleOutputPathForAtMostOnePrimary() const {
100105
return getPrimarySpecificPathsForAtMostOnePrimary()
101106
.SupplementaryOutputs.ModuleOutputPath;

lib/Frontend/FrontendInputsAndOutputs.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,12 @@ bool FrontendInputsAndOutputs::hasObjCHeaderOutputPath() const {
467467
return outs.ObjCHeaderOutputPath;
468468
});
469469
}
470+
bool FrontendInputsAndOutputs::hasCxxHeaderOutputPath() const {
471+
return hasSupplementaryOutputPath(
472+
[](const SupplementaryOutputPaths &outs) -> const std::string & {
473+
return outs.CxxHeaderOutputPath;
474+
});
475+
}
470476
bool FrontendInputsAndOutputs::hasLoadedModuleTracePath() const {
471477
return hasSupplementaryOutputPath(
472478
[](const SupplementaryOutputPaths &outs) -> const std::string & {

lib/Frontend/FrontendOptions.cpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -231,12 +231,11 @@ void FrontendOptions::forAllOutputPaths(
231231
}
232232
const SupplementaryOutputPaths &outs =
233233
input.getPrimarySpecificPaths().SupplementaryOutputs;
234-
const std::string *outputs[] = {&outs.ModuleOutputPath,
235-
&outs.ModuleDocOutputPath,
236-
&outs.ModuleInterfaceOutputPath,
237-
&outs.PrivateModuleInterfaceOutputPath,
238-
&outs.ObjCHeaderOutputPath,
239-
&outs.ModuleSourceInfoOutputPath};
234+
const std::string *outputs[] = {
235+
&outs.ModuleOutputPath, &outs.ModuleDocOutputPath,
236+
&outs.ModuleInterfaceOutputPath, &outs.PrivateModuleInterfaceOutputPath,
237+
&outs.ObjCHeaderOutputPath, &outs.CxxHeaderOutputPath,
238+
&outs.ModuleSourceInfoOutputPath};
240239
for (const std::string *next : outputs) {
241240
if (!next->empty())
242241
fn(*next);
@@ -449,7 +448,7 @@ bool FrontendOptions::canActionEmitModuleSummary(ActionType action) {
449448
llvm_unreachable("unhandled action");
450449
}
451450

452-
bool FrontendOptions::canActionEmitObjCHeader(ActionType action) {
451+
bool FrontendOptions::canActionEmitClangHeader(ActionType action) {
453452
switch (action) {
454453
case ActionType::NoneAction:
455454
case ActionType::Parse:

lib/FrontendTool/FrontendTool.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,22 @@ static bool printAsObjCIfNeeded(StringRef outputPath, ModuleDecl *M,
186186
});
187187
}
188188

189+
/// Prints the C++ "generated header" interface for \p M to \p
190+
/// outputPath.
191+
///
192+
/// ...unless \p outputPath is empty, in which case it does nothing.
193+
///
194+
/// \returns true if there were any errors
195+
///
196+
/// \see swift::printAsCxx
197+
static bool printAsCxxIfNeeded(StringRef outputPath, ModuleDecl *M) {
198+
if (outputPath.empty())
199+
return false;
200+
return withOutputFile(
201+
M->getDiags(), outputPath,
202+
[&](raw_ostream &os) -> bool { return printAsCXX(os, M); });
203+
}
204+
189205
/// Prints the stable module interface for \p M to \p outputPath.
190206
///
191207
/// ...unless \p outputPath is empty, in which case it does nothing.
@@ -826,6 +842,12 @@ static bool emitAnyWholeModulePostTypeCheckSupplementaryOutputs(
826842
Invocation.getObjCHeaderOutputPathForAtMostOnePrimary(),
827843
Instance.getMainModule(), BridgingHeaderPathForPrint);
828844
}
845+
if ((!Context.hadError() || opts.AllowModuleWithCompilerErrors) &&
846+
opts.InputsAndOutputs.hasCxxHeaderOutputPath()) {
847+
hadAnyError |= printAsCxxIfNeeded(
848+
Invocation.getCxxHeaderOutputPathForAtMostOnePrimary(),
849+
Instance.getMainModule());
850+
}
829851

830852
// Only want the header if there's been any errors, ie. there's not much
831853
// point outputting a swiftinterface for an invalid module

lib/PrintAsObjC/PrintAsObjC.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,10 @@ static void writeEpilogue(raw_ostream &os) {
389389
"#endif\n";
390390
}
391391

392+
static std::string computeMacroGuard(const ModuleDecl *M) {
393+
return (llvm::Twine(M->getNameStr().upper()) + "_SWIFT_H").str();
394+
}
395+
392396
bool swift::printAsObjC(raw_ostream &os, ModuleDecl *M,
393397
StringRef bridgingHeader) {
394398
llvm::PrettyStackTraceString trace("While generating Objective-C header");
@@ -397,12 +401,22 @@ bool swift::printAsObjC(raw_ostream &os, ModuleDecl *M,
397401
std::string moduleContentsBuf;
398402
llvm::raw_string_ostream moduleContents{moduleContentsBuf};
399403
printModuleContentsAsObjC(moduleContents, imports, *M);
400-
std::string macroGuard = (llvm::Twine(M->getNameStr().upper()) + "_SWIFT_H").str();
401-
writePrologue(os, M->getASTContext(), macroGuard);
404+
writePrologue(os, M->getASTContext(), computeMacroGuard(M));
402405
writeImports(os, imports, *M, bridgingHeader);
403406
writePostImportPrologue(os, *M);
404407
os << moduleContents.str();
405408
writeEpilogue(os);
406409

407410
return false;
408411
}
412+
413+
bool swift::printAsCXX(raw_ostream &os, ModuleDecl *M) {
414+
llvm::PrettyStackTraceString trace("While generating C++ header");
415+
416+
writePrologue(os, M->getASTContext(), computeMacroGuard(M));
417+
writePostImportPrologue(os, *M);
418+
// TODO (Alex): emit module contents.
419+
writeEpilogue(os);
420+
421+
return false;
422+
}

test/Frontend/supplementary-output-support.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@
2424
// RUN: not %target-swift-frontend -resolve-imports -emit-objc-header %s 2>&1 | %FileCheck -check-prefix=RESOLVE_IMPORTS_NO_OBJC_HEADER %s
2525
// RESOLVE_IMPORTS_NO_OBJC_HEADER: error: this mode does not support emitting Objective-C headers{{$}}
2626

27+
// RUN: not %target-swift-frontend -parse -emit-cxx-header %s 2>&1 | %FileCheck -check-prefix=PARSE_NO_CXX_HEADER %s
28+
// PARSE_NO_CXX_HEADER: error: this mode does not support emitting C++ headers{{$}}
29+
// RUN: not %target-swift-frontend -dump-ast -emit-cxx-header %s 2>&1 | %FileCheck -check-prefix=DUMP_NO_CXX_HEADER %s
30+
// DUMP_NO_CXX_HEADER: error: this mode does not support emitting C++ headers{{$}}
31+
// RUN: not %target-swift-frontend -resolve-imports -emit-cxx-header %s 2>&1 | %FileCheck -check-prefix=RESOLVE_IMPORTS_NO_CXX_HEADER %s
32+
// RESOLVE_IMPORTS_NO_CXX_HEADER: error: this mode does not support emitting C++ headers{{$}}
33+
2734
// RUN: not %target-swift-frontend -parse -emit-module-interface-path %t %s 2>&1 | %FileCheck -check-prefix=PARSE_NO_INTERFACE %s
2835
// PARSE_NO_INTERFACE: error: this mode does not support emitting module interface files{{$}}
2936
// RUN: not %target-swift-frontend -emit-silgen -emit-module-interface-path %t %s 2>&1 | %FileCheck -check-prefix=SILGEN_NO_INTERFACE %s

0 commit comments

Comments
 (0)