Skip to content

[interop][SwiftToCxx] add support for emitting Swift stdlib dependenc… #61331

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions include/swift/Frontend/FrontendOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "swift/Frontend/FrontendInputsAndOutputs.h"
#include "swift/Frontend/InputFile.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringMap.h"

#include <string>
Expand Down Expand Up @@ -388,9 +389,16 @@ class FrontendOptions {
/// '.../lib/swift', otherwise '.../lib/swift_static'.
bool UseSharedResourceFolder = true;

/// Indicates whether to expose all public declarations in the generated clang
enum class ClangHeaderExposeBehavior {
/// Expose all public declarations in the generated header.
AllPublic,
/// Expose declarations only when they have expose attribute.
HasExposeAttr
};

/// Indicates which declarations should be exposed in the generated clang
/// header.
bool ExposePublicDeclsInClangHeader = false;
llvm::Optional<ClangHeaderExposeBehavior> ClangHeaderExposedDecls;

/// Emit C++ bindings for the exposed Swift declarations in the generated
/// clang header.
Expand Down
11 changes: 6 additions & 5 deletions include/swift/Option/FrontendOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -1081,11 +1081,12 @@ def skip_import_in_public_interface:
HelpText<"Skip the import statement corresponding to a module name "
"when printing the public interface.">;

def clang_header_expose_public_decls:
Flag<["-"], "clang-header-expose-public-decls">,
HelpText<"Expose all public declarations in the generated clang header">,
Flags<[FrontendOption, HelpHidden]>;

def clang_header_expose_decls:
Joined<["-"], "clang-header-expose-decls=">,
HelpText<"Which declarations should be exposed in the generated clang header.">,
Flags<[FrontendOption, HelpHidden]>,
MetaVarName<"all-public|has-expose-attr">;

def weak_link_at_target :
Flag<["-"], "weak-link-at-target">,
HelpText<"Weakly link symbols for declarations that were introduced at the "
Expand Down
9 changes: 7 additions & 2 deletions lib/Frontend/ArgsToFrontendOptionsConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -284,8 +284,13 @@ bool ArgsToFrontendOptionsConverter::convert(
Opts.EnableIncrementalDependencyVerifier |= Args.hasArg(OPT_verify_incremental_dependencies);
Opts.UseSharedResourceFolder = !Args.hasArg(OPT_use_static_resource_dir);
Opts.DisableBuildingInterface = Args.hasArg(OPT_disable_building_interface);
Opts.ExposePublicDeclsInClangHeader =
Args.hasArg(OPT_clang_header_expose_public_decls);
if (const Arg *A = Args.getLastArg(options::OPT_clang_header_expose_decls)) {
Opts.ClangHeaderExposedDecls =
llvm::StringSwitch<llvm::Optional<FrontendOptions::ClangHeaderExposeBehavior>>(A->getValue())
.Case("all-public", FrontendOptions::ClangHeaderExposeBehavior::AllPublic)
.Case("has-expose-attr", FrontendOptions::ClangHeaderExposeBehavior::HasExposeAttr)
.Default(llvm::None);
}
Opts.EnableExperimentalCxxInteropInClangHeader =
Args.hasArg(OPT_enable_experimental_cxx_interop_in_clang_header);

Expand Down
21 changes: 16 additions & 5 deletions lib/PrintAsClang/ModuleContentsWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ class ModuleWriter {
llvm::raw_string_ostream outOfLineDefinitionsOS;
DeclAndTypePrinter printer;
OutputLanguageMode outputLangMode;
bool dependsOnStdlib = false;

public:
ModuleWriter(raw_ostream &os, raw_ostream &prologueOS,
Expand All @@ -149,6 +150,11 @@ class ModuleWriter {

PrimitiveTypeMapping &getTypeMapping() { return typeMapping; }

/// Returns true if a Stdlib dependency was seen during the emission of this module.
bool isStdlibRequired() const {
return dependsOnStdlib;
}

/// Returns true if we added the decl's module to the import set, false if
/// the decl is a local decl.
///
Expand All @@ -159,8 +165,10 @@ class ModuleWriter {

if (otherModule == &M)
return false;
if (otherModule->isStdlibModule() ||
otherModule->isBuiltinModule())
if (otherModule->isStdlibModule()) {
dependsOnStdlib = true;
return true;
} else if (otherModule->isBuiltinModule())
return true;
// Don't need a module for SIMD types in C.
if (otherModule->getName() == M.getASTContext().Id_simd)
Expand Down Expand Up @@ -734,20 +742,22 @@ void swift::printModuleContentsAsObjC(
.write();
}

void swift::printModuleContentsAsCxx(
raw_ostream &os, llvm::SmallPtrSetImpl<ImportModuleTy> &imports,
EmittedClangHeaderDependencyInfo swift::printModuleContentsAsCxx(
raw_ostream &os,
ModuleDecl &M, SwiftToClangInteropContext &interopContext,
bool requiresExposedAttribute) {
std::string moduleContentsBuf;
llvm::raw_string_ostream moduleOS{moduleContentsBuf};
std::string modulePrologueBuf;
llvm::raw_string_ostream prologueOS{modulePrologueBuf};
EmittedClangHeaderDependencyInfo info;

// FIXME: Use getRequiredAccess once @expose is supported.
ModuleWriter writer(moduleOS, prologueOS, imports, M, interopContext,
ModuleWriter writer(moduleOS, prologueOS, info.imports, M, interopContext,
AccessLevel::Public, requiresExposedAttribute,
OutputLanguageMode::Cxx);
writer.write();
info.dependsOnStandardLibrary = writer.isStdlibRequired();

os << "#ifndef SWIFT_PRINTED_CORE\n";
os << "#define SWIFT_PRINTED_CORE\n";
Expand Down Expand Up @@ -778,4 +788,5 @@ void swift::printModuleContentsAsCxx(
ClangSyntaxPrinter(os).printNamespace(
[&](raw_ostream &os) { M.ValueDecl::getName().print(os); },
[&](raw_ostream &os) { os << moduleOS.str(); });
return info;
}
15 changes: 11 additions & 4 deletions lib/PrintAsClang/ModuleContentsWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,17 @@ void printModuleContentsAsObjC(raw_ostream &os,
ModuleDecl &M,
SwiftToClangInteropContext &interopContext);

/// Prints the declarations of \p M to \p os in C++ language mode and collects
/// imports in \p imports along the way.
void printModuleContentsAsCxx(raw_ostream &os,
llvm::SmallPtrSetImpl<ImportModuleTy> &imports,
struct EmittedClangHeaderDependencyInfo {
/// The set of imported modules used by this module.
SmallPtrSet<ImportModuleTy, 8> imports;
/// True if the printed module depends on types from the Stdlib module.
bool dependsOnStandardLibrary = false;
};

/// Prints the declarations of \p M to \p os in C++ language mode.
///
/// \returns Dependencies required by this module.
EmittedClangHeaderDependencyInfo printModuleContentsAsCxx(raw_ostream &os,
ModuleDecl &M,
SwiftToClangInteropContext &interopContext,
bool requiresExposedAttribute);
Expand Down
38 changes: 20 additions & 18 deletions lib/PrintAsClang/PrintAsClang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -495,16 +495,6 @@ static std::string computeMacroGuard(const ModuleDecl *M) {
return (llvm::Twine(M->getNameStr().upper()) + "_SWIFT_H").str();
}

static std::string getModuleContentsCxxString(
ModuleDecl &M, SmallPtrSet<ImportModuleTy, 8> &imports,
SwiftToClangInteropContext &interopContext, bool requiresExposedAttribute) {
std::string moduleContentsBuf;
llvm::raw_string_ostream moduleContents{moduleContentsBuf};
printModuleContentsAsCxx(moduleContents, imports, M, interopContext,
requiresExposedAttribute);
return std::move(moduleContents.str());
}

bool swift::printAsClangHeader(raw_ostream &os, ModuleDecl *M,
StringRef bridgingHeader,
const FrontendOptions &frontendOpts,
Expand All @@ -524,18 +514,30 @@ bool swift::printAsClangHeader(raw_ostream &os, ModuleDecl *M,
emitObjCConditional(os, [&] { os << objcModuleContents.str(); });
emitCxxConditional(os, [&] {
// FIXME: Expose Swift with @expose by default.
bool enableCxx = frontendOpts.ExposePublicDeclsInClangHeader ||
bool enableCxx = frontendOpts.ClangHeaderExposedDecls.hasValue() ||
frontendOpts.EnableExperimentalCxxInteropInClangHeader ||
M->DeclContext::getASTContext().LangOpts.EnableCXXInterop;
if (enableCxx) {
SmallPtrSet<ImportModuleTy, 8> imports;
auto contents = getModuleContentsCxxString(
*M, imports, interopContext,
/*requiresExposedAttribute=*/
!frontendOpts.ExposePublicDeclsInClangHeader);
bool requiresExplicitExpose = !frontendOpts.ClangHeaderExposedDecls.hasValue() ||
*frontendOpts.ClangHeaderExposedDecls == FrontendOptions::ClangHeaderExposeBehavior::HasExposeAttr;
// Default dependency behavior is used when the -clang-header-expose-decls flag is not specified.
bool defaultDependencyBehavior = !frontendOpts.ClangHeaderExposedDecls.hasValue();

std::string moduleContentsBuf;
llvm::raw_string_ostream moduleContents{moduleContentsBuf};
auto deps = printModuleContentsAsCxx(moduleContents, *M, interopContext,
/*requiresExposedAttribute=*/requiresExplicitExpose);
// FIXME: In ObjC++ mode, we do not need to reimport duplicate modules.
writeImports(os, imports, *M, bridgingHeader, /*useCxxImport=*/true);
os << contents;
writeImports(os, deps.imports, *M, bridgingHeader, /*useCxxImport=*/true);

// Embed the standard library directly.
if (defaultDependencyBehavior && deps.dependsOnStandardLibrary) {
assert(!M->isStdlibModule());
SwiftToClangInteropContext interopContext(*M->getASTContext().getStdlibModule(), irGenOpts);
printModuleContentsAsCxx(os, *M->getASTContext().getStdlibModule(), interopContext, /*requiresExposedAttribute=*/true);
}

os << moduleContents.str();
}
});
writeEpilogue(os);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

// RUN: %target-swift-frontend -parse-as-library %platform-module-dir/Swift.swiftmodule/%module-target-triple.swiftinterface -enable-library-evolution -disable-objc-attr-requires-foundation-module -typecheck -module-name Swift -parse-stdlib -enable-experimental-cxx-interop -emit-clang-header-path %t/Swift.h -experimental-skip-all-function-bodies

// RUN: %target-swift-frontend -typecheck %t/use-cxx-types.swift -typecheck -module-name UseCxx -emit-clang-header-path %t/UseCxx.h -I %t -enable-experimental-cxx-interop -clang-header-expose-public-decls
// RUN: %target-swift-frontend -typecheck %t/use-cxx-types.swift -typecheck -module-name UseCxx -emit-clang-header-path %t/UseCxx.h -I %t -enable-experimental-cxx-interop -clang-header-expose-decls=all-public

// RUN: %target-interop-build-clangxx -std=c++20 -c %t/use-swift-cxx-types.cpp -I %t -o %t/swift-cxx-execution.o -g
// RUN: %target-interop-build-swift %t/use-cxx-types.swift -o %t/swift-cxx-execution -Xlinker %t/swift-cxx-execution.o -module-name UseCxx -Xfrontend -entry-point-function-name -Xfrontend swiftMain -I %t -g
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// RUN: %empty-directory(%t)
// RUN: split-file %s %t

// RUN: %target-swift-frontend -typecheck %t/use-cxx-types.swift -typecheck -module-name UseCxxTy -emit-clang-header-path %t/UseCxxTy.h -I %t -enable-experimental-cxx-interop -clang-header-expose-public-decls
// RUN: %target-swift-frontend -typecheck %t/use-cxx-types.swift -typecheck -module-name UseCxxTy -emit-clang-header-path %t/UseCxxTy.h -I %t -enable-experimental-cxx-interop -clang-header-expose-decls=all-public

// RUN: %FileCheck %s < %t/UseCxxTy.h

// RUN: %target-swift-frontend -typecheck %t/use-cxx-types.swift -typecheck -module-name UseCxxTy -emit-clang-header-path %t/UseCxxTyExposeOnly.h -I %t -enable-experimental-cxx-interop
// RUN: %target-swift-frontend -typecheck %t/use-cxx-types.swift -typecheck -module-name UseCxxTy -emit-clang-header-path %t/UseCxxTyExposeOnly.h -I %t -enable-experimental-cxx-interop -clang-header-expose-decls=has-expose-attr

// RUN: %FileCheck %s < %t/UseCxxTyExposeOnly.h

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// RUN: %empty-directory(%t)
// RUN: split-file %s %t

// RUN: %target-swift-frontend -typecheck %t/use-objc-types.swift -typecheck -module-name UseObjCTy -emit-clang-header-path %t/UseObjCTy.h -I %t -enable-experimental-cxx-interop -clang-header-expose-public-decls
// RUN: %target-swift-frontend -typecheck %t/use-objc-types.swift -typecheck -module-name UseObjCTy -emit-clang-header-path %t/UseObjCTy.h -I %t -enable-experimental-cxx-interop -clang-header-expose-decls=all-public

// RUN: %target-interop-build-clangxx -std=c++20 -fobjc-arc -c %t/use-swift-objc-types.mm -I %t -o %t/swift-objc-execution.o
// RUN: %target-interop-build-swift %t/use-objc-types.swift -o %t/swift-objc-execution -Xlinker %t/swift-objc-execution.o -module-name UseObjCTy -Xfrontend -entry-point-function-name -Xfrontend swiftMain -I %t
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// RUN: %empty-directory(%t)
// RUN: split-file %s %t

// RUN: %target-swift-frontend -typecheck %t/use-objc-types.swift -typecheck -module-name UseObjCTy -emit-clang-header-path %t/UseObjCTy.h -I %t -enable-experimental-cxx-interop -clang-header-expose-public-decls
// RUN: %target-swift-frontend -typecheck %t/use-objc-types.swift -typecheck -module-name UseObjCTy -emit-clang-header-path %t/UseObjCTy.h -I %t -enable-experimental-cxx-interop -clang-header-expose-decls=all-public

// RUN: %FileCheck %s < %t/UseObjCTy.h

// RUN: %target-swift-frontend -typecheck %t/use-objc-types.swift -typecheck -module-name UseObjCTy -emit-clang-header-path %t/UseObjCTyExposeOnly.h -I %t -enable-experimental-cxx-interop
// RUN: %target-swift-frontend -typecheck %t/use-objc-types.swift -typecheck -module-name UseObjCTy -emit-clang-header-path %t/UseObjCTyExposeOnly.h -I %t -enable-experimental-cxx-interop -clang-header-expose-decls=has-expose-attr

// RUN: %FileCheck %s < %t/UseObjCTyExposeOnly.h

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -typecheck -module-name Functions -clang-header-expose-public-decls -emit-clang-header-path %t/functions.h
// RUN: %target-swift-frontend %s -typecheck -module-name Functions -clang-header-expose-decls=all-public -emit-clang-header-path %t/functions.h
// RUN: %FileCheck %s < %t/functions.h

// CHECK: SWIFT_EXTERN void $s9Functions19testKeywordArgument8registerySi_tF(ptrdiff_t register_) SWIFT_NOEXCEPT SWIFT_CALL;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -typecheck -module-name Functions -clang-header-expose-public-decls -emit-clang-header-path %t/functions.h
// RUN: %target-swift-frontend %s -typecheck -module-name Functions -clang-header-expose-decls=all-public -emit-clang-header-path %t/functions.h
// RUN: %FileCheck %s < %t/functions.h

// RUN: %check-interop-c-header-in-clang(%t/functions.h)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -typecheck -module-name Functions -clang-header-expose-public-decls -emit-clang-header-path %t/functions.h
// RUN: %target-swift-frontend %s -typecheck -module-name Functions -clang-header-expose-decls=all-public -emit-clang-header-path %t/functions.h
// RUN: %FileCheck %s < %t/functions.h

// RUN: %check-interop-c-header-in-clang(%t/functions.h)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// RUN: %empty-directory(%t)

// RUN: %target-swift-frontend %S/swift-primitive-functions-c-bridging-long-lp64.swift -typecheck -module-name Functions -clang-header-expose-public-decls -emit-clang-header-path %t/functions.h
// RUN: %target-swift-frontend %S/swift-primitive-functions-c-bridging-long-lp64.swift -typecheck -module-name Functions -clang-header-expose-decls=all-public -emit-clang-header-path %t/functions.h

// RUN: %target-interop-build-clang -c %s -I %t -o %t/swift-functions-execution.o
// RUN: %target-interop-build-swift %S/swift-primitive-functions-c-bridging-long-lp64.swift -o %t/swift-functions-execution -Xlinker %t/swift-functions-execution.o -module-name Functions -Xfrontend -entry-point-function-name -Xfrontend swiftMain
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// RUN: %empty-directory(%t)

// RUN: %target-swift-frontend %S/swift-primitive-functions-c-bridging.swift -typecheck -module-name Functions -clang-header-expose-public-decls -emit-clang-header-path %t/functions.h
// RUN: %target-swift-frontend %S/swift-primitive-functions-c-bridging.swift -typecheck -module-name Functions -clang-header-expose-decls=all-public -emit-clang-header-path %t/functions.h

// RUN: %target-interop-build-clang -c %s -I %t -o %t/swift-functions-execution.o
// RUN: %target-interop-build-swift %S/swift-primitive-functions-c-bridging.swift -o %t/swift-functions-execution -Xlinker %t/swift-functions-execution.o -module-name Functions -Xfrontend -entry-point-function-name -Xfrontend swiftMain
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// RUN: %empty-directory(%t)

// RUN: %target-swift-frontend %S/large-structs-pass-return-indirect-in-c.swift -typecheck -module-name Structs -clang-header-expose-public-decls -emit-clang-header-path %t/structs.h
// RUN: %target-swift-frontend %S/large-structs-pass-return-indirect-in-c.swift -typecheck -module-name Structs -clang-header-expose-decls=all-public -emit-clang-header-path %t/structs.h

// RUN: %target-interop-build-clang -c %s -I %t -o %t/swift-structs-execution.o
// RUN: %target-interop-build-swift %S/large-structs-pass-return-indirect-in-c.swift -o %t/swift-structs-execution -Xlinker %t/swift-structs-execution.o -module-name Structs -Xfrontend -entry-point-function-name -Xfrontend swiftMain
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -typecheck -module-name Structs -clang-header-expose-public-decls -emit-clang-header-path %t/structs.h
// RUN: %target-swift-frontend %s -typecheck -module-name Structs -clang-header-expose-decls=all-public -emit-clang-header-path %t/structs.h
// RUN: %FileCheck %s < %t/structs.h

// RUN: %check-interop-c-header-in-clang(%t/structs.h)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -typecheck -module-name Structs -clang-header-expose-public-decls -emit-clang-header-path %t/structs.h
// RUN: %target-swift-frontend %s -typecheck -module-name Structs -clang-header-expose-decls=all-public -emit-clang-header-path %t/structs.h
// RUN: %FileCheck %s < %t/structs.h

// RUN: %check-interop-c-header-in-clang(%t/structs.h -Wno-unused-function)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

// RUN: cat %S/small-structs-pass-return-direct-in-c.swift %S/small-structs-64-bit-pass-return-direct-in-c.swift > %t/full-small-structs-pass-return-direct-in-c.swift

// RUN: %target-swift-frontend %t/full-small-structs-pass-return-direct-in-c.swift -typecheck -module-name Structs -clang-header-expose-public-decls -emit-clang-header-path %t/structs.h
// RUN: %target-swift-frontend %t/full-small-structs-pass-return-direct-in-c.swift -typecheck -module-name Structs -clang-header-expose-decls=all-public -emit-clang-header-path %t/structs.h

// RUN: %target-interop-build-clang -c %s -I %t -o %t/swift-structs-execution.o -Wno-incompatible-pointer-types
// RUN: %target-interop-build-swift %t/full-small-structs-pass-return-direct-in-c.swift -o %t/swift-structs-execution -Xlinker %t/swift-structs-execution.o -module-name Structs -Xfrontend -entry-point-function-name -Xfrontend swiftMain
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -typecheck -module-name Structs -clang-header-expose-public-decls -emit-clang-header-path %t/structs.h
// RUN: %target-swift-frontend %s -typecheck -module-name Structs -clang-header-expose-decls=all-public -emit-clang-header-path %t/structs.h
// RUN: %FileCheck %s < %t/structs.h

// RUN: %check-interop-c-header-in-clang(%t/structs.h -Wno-unused-function)
Expand Down
2 changes: 1 addition & 1 deletion test/Interop/SwiftToCxx/class/class-api-prohibited.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// RUN: %empty-directory(%t)

// RUN: %target-swift-frontend %S/swift-class-in-cxx.swift -typecheck -module-name Class -clang-header-expose-public-decls -emit-clang-header-path %t/class.h
// RUN: %target-swift-frontend %S/swift-class-in-cxx.swift -typecheck -module-name Class -clang-header-expose-decls=all-public -emit-clang-header-path %t/class.h

// RUN: not %target-interop-build-clangxx -c %s -I %t -o %t/swift-class-execution.o

Expand Down
Loading