mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[cxx-interop] Use formal C++ interop mode to fix name lookup in module interfaces (#79984)
It is possible for a module interface (e.g., ModuleA) to be generated with C++ interop disabled, and then rebuilt with C++ interop enabled (e.g., because ModuleB, which imports ModuleA, has C++ interop enabled). This circumstance can lead to various issues when name lookup behaves differently depending on whether C++ interop is enabled, e.g., when a module name is shadowed by a namespace of the same name---this only happens in C++ because namespaces do not exist in C. Unfortunately, naming namespaces the same as a module is a common C++ convention, leading to many textual interfaces whose fully-qualified identifiers (e.g., c_module.c_member) cannot be correctly resolved when C++ interop is enabled (because c_module is shadowed by a namespace of the same name). This patch does two things. First, it introduces a new frontend flag, -formal-cxx-interoperability-mode, which records the C++ interop mode a module interface was originally compiled with. Doing so allows subsequent consumers of that interface to interpret it according to the formal C++ interop mode. Note that the actual "versioning" used by this flag is very crude: "off" means disabled, and "swift-6" means enabled. This is done to be compatible with C++ interop compat versioning scheme, which seems to produce some invalid (but unused) version numbers. The versioning scheme for both the formal and actual C++ interop modes should be clarified and fixed in a subsequent patch. The second thing this patch does is fix the module/namespace collision issue in module interface files. It uses the formal C++ interop mode to determine whether it should resolve C++-only decls during name lookup. For now, the fix is very minimal and conservative: it only filters out C++ namespaces during unqualified name lookup in an interface that was originally generated without C++ interop. Doing so should fix the issue while minimizing the chance for collateral breakge. More cases other than C++ namespaces should be added in subsequent patches, with sufficient testing and careful consideration. rdar://144566922
This commit is contained in:
@@ -644,7 +644,8 @@ static void diagnoseCxxInteropCompatMode(Arg *verArg, ArgList &Args,
|
||||
auto validVers = {llvm::StringRef("off"), llvm::StringRef("default"),
|
||||
llvm::StringRef("swift-6"), llvm::StringRef("swift-5.9")};
|
||||
auto versStr = "'" + llvm::join(validVers, "', '") + "'";
|
||||
diags.diagnose(SourceLoc(), diag::valid_cxx_interop_modes, versStr);
|
||||
diags.diagnose(SourceLoc(), diag::valid_cxx_interop_modes,
|
||||
verArg->getSpelling(), versStr);
|
||||
}
|
||||
|
||||
void LangOptions::setCxxInteropFromArgs(ArgList &Args,
|
||||
@@ -679,6 +680,54 @@ void LangOptions::setCxxInteropFromArgs(ArgList &Args,
|
||||
cxxInteropCompatVersion =
|
||||
validateCxxInteropCompatibilityMode("swift-5.9").second;
|
||||
}
|
||||
|
||||
if (Arg *A = Args.getLastArg(options::OPT_formal_cxx_interoperability_mode)) {
|
||||
// Take formal version from explicitly specified formal version flag
|
||||
StringRef version = A->getValue();
|
||||
|
||||
// FIXME: the only valid modes are 'off' and 'swift-6'; see below.
|
||||
if (version == "off") {
|
||||
FormalCxxInteropMode = std::nullopt;
|
||||
} else if (version == "swift-6") {
|
||||
FormalCxxInteropMode = {6};
|
||||
} else {
|
||||
Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,
|
||||
A->getAsString(Args), A->getValue());
|
||||
Diags.diagnose(SourceLoc(), diag::valid_cxx_interop_modes,
|
||||
A->getSpelling(), "'off', 'swift-6'");
|
||||
}
|
||||
} else {
|
||||
// In the absence of a formal mode flag, we capture it from the current
|
||||
// C++ compat version (if C++ interop is enabled).
|
||||
//
|
||||
// FIXME: cxxInteropCompatVersion is computed based on the Swift language
|
||||
// version, and is either 4, 5, 6, or 7 (even though only 5.9 and 6.* make
|
||||
// any sense). For now, we don't actually care about the version, so we'll
|
||||
// just use version 6 (i.e., 'swift-6') to mean that C++ interop mode is on.
|
||||
if (EnableCXXInterop)
|
||||
FormalCxxInteropMode = {6};
|
||||
else
|
||||
FormalCxxInteropMode = std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
static std::string printFormalCxxInteropVersion(const LangOptions &Opts) {
|
||||
std::string str;
|
||||
llvm::raw_string_ostream OS(str);
|
||||
|
||||
OS << "-formal-cxx-interoperability-mode=";
|
||||
|
||||
// We must print a 'stable' C++ interop version here, which cannot be
|
||||
// 'default' and 'upcoming-swift' (since those are relative to the current
|
||||
// version, which may change in the future).
|
||||
if (!Opts.FormalCxxInteropMode) {
|
||||
OS << "off";
|
||||
} else {
|
||||
// FIXME: FormalCxxInteropMode will always be 6 (or nullopt); see above
|
||||
OS << "swift-6";
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static std::optional<swift::StrictConcurrency>
|
||||
@@ -925,6 +974,7 @@ static bool ParseEnabledFeatureArgs(LangOptions &Opts, ArgList &Args,
|
||||
|
||||
static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
|
||||
DiagnosticEngine &Diags,
|
||||
ModuleInterfaceOptions &ModuleInterfaceOpts,
|
||||
const FrontendOptions &FrontendOpts) {
|
||||
using namespace options;
|
||||
bool buildingFromInterface =
|
||||
@@ -1478,6 +1528,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
|
||||
Opts.ClangTargetVariant = llvm::Triple(A->getValue());
|
||||
|
||||
Opts.setCxxInteropFromArgs(Args, Diags);
|
||||
if (!Args.hasArg(options::OPT_formal_cxx_interoperability_mode))
|
||||
ModuleInterfaceOpts.PublicFlags.IgnorableFlags +=
|
||||
" " + printFormalCxxInteropVersion(Opts);
|
||||
|
||||
Opts.EnableObjCInterop =
|
||||
Args.hasFlag(OPT_enable_objc_interop, OPT_disable_objc_interop,
|
||||
@@ -3893,7 +3946,8 @@ bool CompilerInvocation::parseArgs(
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ParseLangArgs(LangOpts, ParsedArgs, Diags, FrontendOpts)) {
|
||||
if (ParseLangArgs(LangOpts, ParsedArgs, Diags, ModuleInterfaceOpts,
|
||||
FrontendOpts)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user