Merge pull request #84816 from Xazax-hun/reverse-interop-non-public

[cxx-interop] Add flag to set minimum access level for reverse interop
This commit is contained in:
Gábor Horváth
2025-10-20 16:08:19 -07:00
committed by GitHub
11 changed files with 201 additions and 23 deletions

View File

@@ -612,6 +612,11 @@ ERROR(package_cmo_requires_library_evolution, none,
WARNING(internal_bridging_header_without_library_evolution,none,
"using internal bridging headers without library evolution can cause instability", ())
ERROR(error_invalid_clang_header_access_level, none,
"invalid minimum clang header access level '%0'; chose from "
"'public'|'package'|'internal'",
(StringRef))
ERROR(experimental_not_supported_in_production,none,
"experimental feature '%0' cannot be enabled in production compiler",
(StringRef))

View File

@@ -13,6 +13,7 @@
#ifndef SWIFT_FRONTEND_FRONTENDOPTIONS_H
#define SWIFT_FRONTEND_FRONTENDOPTIONS_H
#include "swift/AST/AttrKind.h"
#include "swift/Basic/FileTypes.h"
#include "swift/Basic/PathRemapper.h"
#include "swift/Basic/Version.h"
@@ -501,6 +502,10 @@ public:
/// header.
std::optional<ClangHeaderExposeBehavior> ClangHeaderExposedDecls;
// Include declarations that are at least as visible as the acces specified
// by -emit-clang-header-min-access
std::optional<AccessLevel> ClangHeaderMinAccess;
struct ClangHeaderExposedImportedModule {
std::string moduleName;
std::string headerName;

View File

@@ -815,6 +815,11 @@ def emit_clang_header_path : Separate<["-"], "emit-clang-header-path">,
HelpText<"Emit an Objective-C and C++ header file to <path>">,
Alias<emit_objc_header_path>;
def emit_clang_header_min_access : Separate<["-"], "emit-clang-header-min-access">,
Flags<[FrontendOption, NoInteractiveOption, ArgumentIsPath, CacheInvariant]>,
MetaVarName<"<access-level>">,
HelpText<"The minimum access level of declarations to include in the emitted header.>">;
def static : Flag<["-"], "static">,
Flags<[FrontendOption, ModuleInterfaceOption, NoInteractiveOption]>,
HelpText<"Make this module statically linkable and make the output of -emit-library a static library.">;

View File

@@ -14,7 +14,7 @@
#include "ArgsToFrontendInputsConverter.h"
#include "ArgsToFrontendOutputsConverter.h"
#include "clang/Driver/Driver.h"
#include "swift/AST/AttrKind.h"
#include "swift/AST/DiagnosticsFrontend.h"
#include "swift/Basic/Assertions.h"
#include "swift/Basic/Platform.h"
@@ -24,18 +24,19 @@
#include "swift/Parse/Lexer.h"
#include "swift/Parse/ParseVersion.h"
#include "swift/Strings.h"
#include "clang/Driver/Driver.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/TargetParser/Triple.h"
#include "llvm/CAS/ObjectStore.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/Compression.h"
#include "llvm/Support/PrefixMapper.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/LineIterator.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/PrefixMapper.h"
#include "llvm/Support/Process.h"
#include "llvm/TargetParser/Triple.h"
using namespace swift;
using namespace llvm::opt;
@@ -400,6 +401,17 @@ bool ArgsToFrontendOptionsConverter::convert(
HasExposeAttrOrImplicitDeps)
.Default(std::nullopt);
}
if (const Arg *A = Args.getLastArg(OPT_emit_clang_header_min_access)) {
Opts.ClangHeaderMinAccess =
llvm::StringSwitch<std::optional<AccessLevel>>(A->getValue())
.Case("public", AccessLevel::Public)
.Case("package", AccessLevel::Package)
.Case("internal", AccessLevel::Internal)
.Default(std::nullopt);
if (!Opts.ClangHeaderMinAccess)
Diags.diagnose(SourceLoc(), diag::error_invalid_clang_header_access_level,
A->getValue());
}
for (const auto &arg :
Args.getAllArgValues(options::OPT_clang_header_expose_module)) {
auto splitArg = StringRef(arg).split('=');

View File

@@ -20,6 +20,7 @@
#include "PrintSwiftToClangCoreScaffold.h"
#include "SwiftToClangInteropContext.h"
#include "swift/AST/AttrKind.h"
#include "swift/AST/Decl.h"
#include "swift/AST/DiagnosticsSema.h"
#include "swift/AST/ExistentialLayout.h"
@@ -1151,16 +1152,21 @@ public:
};
} // end anonymous namespace
static AccessLevel getRequiredAccess(const ModuleDecl &M) {
static AccessLevel getRequiredAccess(const ModuleDecl &M,
std::optional<AccessLevel> minAccess) {
if (minAccess)
return *minAccess;
return M.isExternallyConsumed() ? AccessLevel::Public : AccessLevel::Internal;
}
void swift::printModuleContentsAsObjC(
raw_ostream &os, llvm::SmallPtrSetImpl<ImportModuleTy> &imports,
ModuleDecl &M, SwiftToClangInteropContext &interopContext) {
ModuleDecl &M, SwiftToClangInteropContext &interopContext,
std::optional<AccessLevel> minAccess) {
llvm::raw_null_ostream prologueOS;
llvm::StringSet<> exposedModules;
ModuleWriter(os, prologueOS, imports, M, interopContext, getRequiredAccess(M),
ModuleWriter(os, prologueOS, imports, M, interopContext,
getRequiredAccess(M, minAccess),
/*requiresExposedAttribute=*/false, exposedModules,
OutputLanguageMode::ObjC)
.write();
@@ -1168,10 +1174,12 @@ void swift::printModuleContentsAsObjC(
void swift::printModuleContentsAsC(
raw_ostream &os, llvm::SmallPtrSetImpl<ImportModuleTy> &imports,
ModuleDecl &M, SwiftToClangInteropContext &interopContext) {
ModuleDecl &M, SwiftToClangInteropContext &interopContext,
std::optional<AccessLevel> minAccess) {
llvm::raw_null_ostream prologueOS;
llvm::StringSet<> exposedModules;
ModuleWriter(os, prologueOS, imports, M, interopContext, getRequiredAccess(M),
ModuleWriter(os, prologueOS, imports, M, interopContext,
getRequiredAccess(M, minAccess),
/*requiresExposedAttribute=*/false, exposedModules,
OutputLanguageMode::C)
.write();
@@ -1179,7 +1187,8 @@ void swift::printModuleContentsAsC(
EmittedClangHeaderDependencyInfo swift::printModuleContentsAsCxx(
raw_ostream &os, ModuleDecl &M, SwiftToClangInteropContext &interopContext,
bool requiresExposedAttribute, llvm::StringSet<> &exposedModules) {
AccessLevel minAccess, bool requiresExposedAttribute,
llvm::StringSet<> &exposedModules) {
std::string moduleContentsBuf;
llvm::raw_string_ostream moduleOS{moduleContentsBuf};
std::string modulePrologueBuf;
@@ -1197,8 +1206,8 @@ EmittedClangHeaderDependencyInfo swift::printModuleContentsAsCxx(
// FIXME: Use getRequiredAccess once @expose is supported.
ModuleWriter writer(moduleOS, prologueOS, info.imports, M, interopContext,
AccessLevel::Public, requiresExposedAttribute,
exposedModules, OutputLanguageMode::Cxx);
minAccess, requiresExposedAttribute, exposedModules,
OutputLanguageMode::Cxx);
writer.write();
info.dependsOnStandardLibrary = writer.isStdlibRequired();
if (M.isStdlibModule()) {

View File

@@ -35,12 +35,14 @@ using ImportModuleTy = PointerUnion<ModuleDecl*, const clang::Module*>;
void printModuleContentsAsObjC(raw_ostream &os,
llvm::SmallPtrSetImpl<ImportModuleTy> &imports,
ModuleDecl &M,
SwiftToClangInteropContext &interopContext);
SwiftToClangInteropContext &interopContext,
std::optional<AccessLevel> minAccess);
void printModuleContentsAsC(raw_ostream &os,
llvm::SmallPtrSetImpl<ImportModuleTy> &imports,
ModuleDecl &M,
SwiftToClangInteropContext &interopContext);
SwiftToClangInteropContext &interopContext,
std::optional<AccessLevel> minAccess);
struct EmittedClangHeaderDependencyInfo {
/// The set of imported modules used by this module.
@@ -52,9 +54,11 @@ struct EmittedClangHeaderDependencyInfo {
/// 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, llvm::StringSet<> &exposedModules);
EmittedClangHeaderDependencyInfo
printModuleContentsAsCxx(raw_ostream &os, ModuleDecl &M,
SwiftToClangInteropContext &interopContext,
AccessLevel minAccess, bool requiresExposedAttribute,
llvm::StringSet<> &exposedModules);
} // end namespace swift

View File

@@ -17,6 +17,7 @@
#include "SwiftToClangInteropContext.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/AttrKind.h"
#include "swift/AST/Module.h"
#include "swift/AST/PrettyStackTrace.h"
#include "swift/Basic/Assertions.h"
@@ -620,7 +621,8 @@ bool swift::printAsClangHeader(raw_ostream &os, ModuleDecl *M,
if (M->getASTContext().LangOpts.hasFeature(Feature::CDecl)) {
SmallPtrSet<ImportModuleTy, 8> imports;
llvm::raw_string_ostream cModuleContents{moduleContentsScratch};
printModuleContentsAsC(cModuleContents, imports, *M, interopContext);
printModuleContentsAsC(cModuleContents, imports, *M, interopContext,
frontendOpts.ClangHeaderMinAccess);
llvm::StringMap<StringRef> exposedModuleHeaderNames;
writeImports(os, imports, *M, bridgingHeader, frontendOpts,
@@ -634,7 +636,8 @@ bool swift::printAsClangHeader(raw_ostream &os, ModuleDecl *M,
// Objective-C content
SmallPtrSet<ImportModuleTy, 8> imports;
llvm::raw_string_ostream objcModuleContents{moduleContentsScratch};
printModuleContentsAsObjC(objcModuleContents, imports, *M, interopContext);
printModuleContentsAsObjC(objcModuleContents, imports, *M, interopContext,
frontendOpts.ClangHeaderMinAccess);
emitObjCConditional(os, [&] {
llvm::StringMap<StringRef> exposedModuleHeaderNames;
writeImports(os, imports, *M, bridgingHeader, frontendOpts,
@@ -685,6 +688,7 @@ bool swift::printAsClangHeader(raw_ostream &os, ModuleDecl *M,
llvm::raw_string_ostream moduleContents{moduleContentsBuf};
auto deps = printModuleContentsAsCxx(
moduleContents, *M, interopContext,
frontendOpts.ClangHeaderMinAccess.value_or(AccessLevel::Public),
/*requiresExposedAttribute=*/requiresExplicitExpose, exposedModules);
// FIXME: In ObjC++ mode, we do not need to reimport duplicate modules.
llvm::StringMap<StringRef> exposedModuleHeaderNames;
@@ -701,9 +705,10 @@ bool swift::printAsClangHeader(raw_ostream &os, ModuleDecl *M,
auto macroGuard = computeMacroGuard(M->getASTContext().getStdlibModule());
os << "#ifndef " << macroGuard << "\n";
os << "#define " << macroGuard << "\n";
printModuleContentsAsCxx(
os, *M->getASTContext().getStdlibModule(), interopContext,
/*requiresExposedAttribute=*/true, exposedModules);
printModuleContentsAsCxx(os, *M->getASTContext().getStdlibModule(),
interopContext, AccessLevel::Public,
/*requiresExposedAttribute=*/true,
exposedModules);
os << "#endif // " << macroGuard << "\n";
}

View File

@@ -20,7 +20,7 @@ SwiftToClangInteropContext::SwiftToClangInteropContext(
ModuleDecl &mod, const IRGenOptions &irGenOpts)
: mod(mod), irGenOpts(irGenOpts) {}
SwiftToClangInteropContext::~SwiftToClangInteropContext() {}
SwiftToClangInteropContext::~SwiftToClangInteropContext() = default;
IRABIDetailsProvider &SwiftToClangInteropContext::getIrABIDetails() {
if (!irABIDetails)

View File

@@ -0,0 +1,46 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -module-name Core -typecheck -verify -emit-clang-header-path %t/core.h -clang-header-expose-decls=has-expose-attr -emit-clang-header-min-access public -package-name Core
// RUN: %FileCheck %s --check-prefix CHECK-PUBLIC < %t/core.h
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -module-name Core -typecheck -verify -emit-clang-header-path %t/core.h -clang-header-expose-decls=has-expose-attr -emit-clang-header-min-access package -package-name Core
// RUN: %FileCheck %s --check-prefix CHECK-PACKAGE < %t/core.h
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -module-name Core -typecheck -verify -emit-clang-header-path %t/core.h -clang-header-expose-decls=has-expose-attr -emit-clang-header-min-access internal -package-name Core
// RUN: %FileCheck %s --check-prefix CHECK-INTERNAL < %t/core.h
@_expose(Cxx)
public func publicFunc(_ x: Int) -> Int {
return x
}
@_expose(Cxx)
package func packageFunc(_ x: Int) -> Int {
return x
}
@_expose(Cxx)
internal func internalFunc(_ x: Int) -> Int {
return x
}
@_expose(Cxx)
private func privateFunc(_ x: Int) -> Int {
return x
}
// CHECK-PUBLIC-NOT: internalFunc
// CHECK-PUBLIC-NOT: packageFunc
// CHECK-PUBLIC-NOT: privateFunc
// CHECK-PUBLIC: SWIFT_INLINE_THUNK swift::Int publicFunc(swift::Int x) noexcept SWIFT_SYMBOL("s:4Core10publicFuncyS2iF") SWIFT_WARN_UNUSED_RESULT {
// CHECK-PACKAGE-NOT: internalFunc
// CHECK-PACKAGE: SWIFT_INLINE_THUNK swift::Int packageFunc(swift::Int x) noexcept SWIFT_SYMBOL("s:4Core11packageFuncyS2iF") SWIFT_WARN_UNUSED_RESULT {
// CHECK-PACKAGE-NOT: privateFunc
// CHECK-PACKAGE: SWIFT_INLINE_THUNK swift::Int publicFunc(swift::Int x) noexcept SWIFT_SYMBOL("s:4Core10publicFuncyS2iF") SWIFT_WARN_UNUSED_RESULT {
// CHECK-INTERNAL: SWIFT_INLINE_THUNK swift::Int internalFunc(swift::Int x) noexcept SWIFT_SYMBOL("s:4Core12internalFuncyS2iF") SWIFT_WARN_UNUSED_RESULT {
// CHECK-INTERNAL: SWIFT_INLINE_THUNK swift::Int packageFunc(swift::Int x) noexcept SWIFT_SYMBOL("s:4Core11packageFuncyS2iF") SWIFT_WARN_UNUSED_RESULT {
// CHECK-INTERNAL-NOT: privateFunc
// CHECK-INTERNAL: SWIFT_INLINE_THUNK swift::Int publicFunc(swift::Int x) noexcept SWIFT_SYMBOL("s:4Core10publicFuncyS2iF") SWIFT_WARN_UNUSED_RESULT {

View File

@@ -0,0 +1,79 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -module-name Core -typecheck -verify -emit-clang-header-path %t/core.h -clang-header-expose-decls=all-public -emit-clang-header-min-access public -package-name Core
// RUN: %FileCheck %s --check-prefix CHECK-PUBLIC < %t/core.h
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -module-name Core -typecheck -verify -emit-clang-header-path %t/core.h -clang-header-expose-decls=all-public -emit-clang-header-min-access package -package-name Core
// RUN: %FileCheck %s --check-prefix CHECK-PACKAGE < %t/core.h
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -module-name Core -typecheck -verify -emit-clang-header-path %t/core.h -clang-header-expose-decls=all-public -emit-clang-header-min-access internal -package-name Core
// RUN: %FileCheck %s --check-prefix CHECK-INTERNAL < %t/core.h
// RUN: %empty-directory(%t)
// RUN: not %target-swift-frontend %s -module-name Core -typecheck -emit-clang-header-path %t/core.h -clang-header-expose-decls=all-public -emit-clang-header-min-access inernal -package-name Core 2>&1 | %FileCheck %s --check-prefix CHECK-DIAGNOSTIC
public func publicFunc(_ x: Int) -> Int {
return x
}
package func packageFunc(_ x: Int) -> Int {
return x
}
internal func internalFunc(_ x: Int) -> Int {
return x
}
private func privateFunc(_ x: Int) -> Int {
return x
}
public struct S {
public func publicMethod(_ x: Int) -> Int {
return x
}
package func packageMethod(_ x: Int) -> Int {
return x
}
internal func internalMethod(_ x: Int) -> Int {
return x
}
private func privateMethod(_ x: Int) -> Int {
return x
}
private var x: Int
}
// CHECK-PUBLIC-NOT: internalFunc
// CHECK-PUBLIC-NOT: packageFunc
// CHECK-PUBLIC-NOT: privateFunc
// CHECK-PUBLIC: SWIFT_INLINE_THUNK swift::Int publicFunc(swift::Int x) noexcept SWIFT_SYMBOL("s:4Core10publicFuncyS2iF") SWIFT_WARN_UNUSED_RESULT {
// CHECK-PUBLIC: SWIFT_INLINE_THUNK swift::Int S::publicMethod(swift::Int x) const {
// CHECK-PUBLIC-NOT: packageMethod
// CHECK-PUBLIC-NOT: internalMethod
// CHECK-PUBLIC-NOT: privateMethod
// CHECK-PACKAGE-NOT: internalFunc
// CHECK-PACKAGE: SWIFT_INLINE_THUNK swift::Int packageFunc(swift::Int x) noexcept SWIFT_SYMBOL("s:4Core11packageFuncyS2iF") SWIFT_WARN_UNUSED_RESULT {
// CHECK-PACKAGE-NOT: privateFunc
// CHECK-PACKAGE: SWIFT_INLINE_THUNK swift::Int publicFunc(swift::Int x) noexcept SWIFT_SYMBOL("s:4Core10publicFuncyS2iF") SWIFT_WARN_UNUSED_RESULT {
// CHECK-PACKAGE: SWIFT_INLINE_THUNK swift::Int S::publicMethod(swift::Int x) const {
// CHECK-PACKAGE: SWIFT_INLINE_THUNK swift::Int S::packageMethod(swift::Int x) const {
// CHECK-PACKAGE-NOT: internalMethod
// CHECK-PACKAGE-NOT: privateMethod
// CHECK-INTERNAL: SWIFT_INLINE_THUNK swift::Int internalFunc(swift::Int x) noexcept SWIFT_SYMBOL("s:4Core12internalFuncyS2iF") SWIFT_WARN_UNUSED_RESULT {
// CHECK-INTERNAL: SWIFT_INLINE_THUNK swift::Int packageFunc(swift::Int x) noexcept SWIFT_SYMBOL("s:4Core11packageFuncyS2iF") SWIFT_WARN_UNUSED_RESULT {
// CHECK-INTERNAL-NOT: privateFunc
// CHECK-INTERNAL: SWIFT_INLINE_THUNK swift::Int publicFunc(swift::Int x) noexcept SWIFT_SYMBOL("s:4Core10publicFuncyS2iF") SWIFT_WARN_UNUSED_RESULT {
// CHECK-INTERNAL: SWIFT_INLINE_THUNK swift::Int S::publicMethod(swift::Int x) const {
// CHECK-INTERNAL: SWIFT_INLINE_THUNK swift::Int S::packageMethod(swift::Int x) const {
// CHECK-INTERNAL: SWIFT_INLINE_THUNK swift::Int S::internalMethod(swift::Int x) const {
// CHECK-INTERNAL-NOT: privateMethod
// CHECK-DIAGNOSTIC: error: invalid minimum clang header access level 'inernal'; chose from 'public'|'package'|'internal'

View File

@@ -23,6 +23,14 @@
// RUN: %FileCheck -check-prefix=CHECK -check-prefix=CHECK-PUBLIC %s < %t/accessibility-appextlib.h
// RUN: %check-in-clang %t/accessibility-appextlib.h
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -parse-as-library %s -application-extension-library -emit-clang-header-min-access internal -typecheck -verify -emit-objc-header-path %t/accessibility-explicit-internal.h -disable-objc-attr-requires-foundation-module
// RUN: %FileCheck -check-prefix=CHECK -check-prefix=CHECK-INTERNAL %s < %t/accessibility-explicit-internal.h
// RUN: %check-in-clang %t/accessibility-explicit-internal.h
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -parse-as-library %s -application-extension -emit-clang-header-min-access public -typecheck -verify -emit-objc-header-path %t/accessibility-explicit-public.h -disable-objc-attr-requires-foundation-module
// RUN: %FileCheck -check-prefix=CHECK -check-prefix=CHECK-PUBLIC %s < %t/accessibility-explicit-public.h
// RUN: %check-in-clang %t/accessibility-explicit-public.h
// REQUIRES: objc_interop
// CHECK: #ifndef ACCESSIBILITY_SWIFT_H