mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[cxx-interop] Configure requires ObjC from frontend option
We sometimes don't have the information in the modulemaps whether a module requires ObjC or not. This info is useful for reverse interop. This PR introduces a frontend flag to have a comma separated list of modules that we should import as if they had "requires ObjC" in their modulemaps.
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
#ifndef SWIFT_NAME_TRANSLATION_H
|
||||
#define SWIFT_NAME_TRANSLATION_H
|
||||
|
||||
#include "swift/AST/ASTContext.h"
|
||||
#include "swift/AST/AttrKind.h"
|
||||
#include "swift/AST/Decl.h"
|
||||
#include "swift/AST/DiagnosticEngine.h"
|
||||
@@ -113,7 +114,7 @@ inline bool isExposableToCxx(
|
||||
}
|
||||
|
||||
bool isObjCxxOnly(const ValueDecl *VD);
|
||||
bool isObjCxxOnly(const clang::Decl *D);
|
||||
bool isObjCxxOnly(const clang::Decl *D, const ASTContext &ctx);
|
||||
|
||||
/// Returns true if the given value decl D is visible to C++ of its
|
||||
/// own accord (i.e. without considering its context)
|
||||
|
||||
@@ -640,6 +640,10 @@ namespace swift {
|
||||
/// All block list configuration files to be honored in this compilation.
|
||||
std::vector<std::string> BlocklistConfigFilePaths;
|
||||
|
||||
/// List of top level modules to be considered as if they had require ObjC
|
||||
/// in their module map.
|
||||
llvm::SmallVector<StringRef> ModulesRequiringObjC;
|
||||
|
||||
/// Whether to ignore checks that a module is resilient during
|
||||
/// type-checking, SIL verification, and IR emission,
|
||||
bool BypassResilienceChecks = false;
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
#include "clang/AST/DeclObjC.h"
|
||||
#include "clang/Basic/Module.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include <optional>
|
||||
|
||||
@@ -241,10 +242,12 @@ swift::cxx_translation::getNameForCxx(const ValueDecl *VD,
|
||||
namespace {
|
||||
struct ObjCTypeWalker : TypeWalker {
|
||||
bool hadObjCType = false;
|
||||
const ASTContext &ctx;
|
||||
ObjCTypeWalker(const ASTContext &ctx) : ctx(ctx) {}
|
||||
Action walkToTypePre(Type ty) override {
|
||||
if (auto *nominal = ty->getNominalOrBoundGenericNominal()) {
|
||||
if (auto clangDecl = nominal->getClangDecl()) {
|
||||
if (cxx_translation::isObjCxxOnly(clangDecl)) {
|
||||
if (cxx_translation::isObjCxxOnly(clangDecl, ctx)) {
|
||||
hadObjCType = true;
|
||||
return Action::Stop;
|
||||
}
|
||||
@@ -256,19 +259,29 @@ struct ObjCTypeWalker : TypeWalker {
|
||||
} // namespace
|
||||
|
||||
bool swift::cxx_translation::isObjCxxOnly(const ValueDecl *VD) {
|
||||
ObjCTypeWalker walker;
|
||||
ObjCTypeWalker walker{VD->getASTContext()};
|
||||
VD->getInterfaceType().walk(walker);
|
||||
return walker.hadObjCType;
|
||||
}
|
||||
|
||||
bool swift::cxx_translation::isObjCxxOnly(const clang::Decl *D) {
|
||||
bool swift::cxx_translation::isObjCxxOnly(const clang::Decl *D,
|
||||
const ASTContext &ctx) {
|
||||
// By default, we import all modules in Obj-C++ mode, so there is no robust
|
||||
// way to tell if something is coming from an Obj-C module. The best
|
||||
// approximation is to check if something is a framework module as most of
|
||||
// those are written in Obj-C. Ideally, we want to add `requires ObjC` to all
|
||||
// ObjC modules and respect that here to make this completely precise.
|
||||
return D->getASTContext().getLangOpts().ObjC &&
|
||||
D->getOwningModule()->isPartOfFramework();
|
||||
// way to tell if something is coming from an Obj-C module. Use the
|
||||
// requirements and the language options to check if we should actually
|
||||
// consider the module to have ObjC constructs.
|
||||
const auto &langOpts = D->getASTContext().getLangOpts();
|
||||
auto clangModule = D->getOwningModule()->getTopLevelModule();
|
||||
bool requiresObjC = false;
|
||||
for (auto req : clangModule->Requirements)
|
||||
if (req.RequiredState && req.FeatureName == "objc")
|
||||
requiresObjC = true;
|
||||
return langOpts.ObjC &&
|
||||
(requiresObjC ||
|
||||
llvm::any_of(ctx.LangOpts.ModulesRequiringObjC,
|
||||
[clangModule](StringRef moduleName) {
|
||||
return clangModule->getFullModuleName() == moduleName;
|
||||
}));
|
||||
}
|
||||
|
||||
swift::cxx_translation::DeclRepresentation
|
||||
|
||||
@@ -850,7 +850,8 @@ static bool ParseEnabledFeatureArgs(LangOptions &Opts, ArgList &Args,
|
||||
// Collect some special case pseudo-features which should be processed
|
||||
// separately.
|
||||
if (argValue.starts_with("StrictConcurrency") ||
|
||||
argValue.starts_with("AvailabilityMacro=")) {
|
||||
argValue.starts_with("AvailabilityMacro=") ||
|
||||
argValue.starts_with("RequiresObjC=")) {
|
||||
if (isEnableFeatureFlag)
|
||||
psuedoFeatures.push_back(argValue);
|
||||
continue;
|
||||
@@ -977,6 +978,11 @@ static bool ParseEnabledFeatureArgs(LangOptions &Opts, ArgList &Args,
|
||||
Opts.AvailabilityMacros.push_back(availability.str());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (featureName->starts_with("RequiresObjC")) {
|
||||
auto modules = featureName->split("=").second;
|
||||
modules.split(Opts.ModulesRequiringObjC, ",");
|
||||
}
|
||||
}
|
||||
|
||||
// Map historical flags over to experimental features. We do this for all
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "OutputLanguageMode.h"
|
||||
#include "PrimitiveTypeMapping.h"
|
||||
#include "SwiftToClangInteropContext.h"
|
||||
#include "swift/AST/ASTContext.h"
|
||||
#include "swift/AST/Decl.h"
|
||||
#include "swift/AST/SwiftNameTranslation.h"
|
||||
#include "swift/AST/Type.h"
|
||||
@@ -619,7 +620,7 @@ void ClangValueTypePrinter::printTypeGenericTraits(
|
||||
|
||||
bool objCxxOnly = false;
|
||||
if (const auto *clangDecl = typeDecl->getClangDecl()) {
|
||||
if (cxx_translation::isObjCxxOnly(clangDecl))
|
||||
if (cxx_translation::isObjCxxOnly(clangDecl, typeDecl->getASTContext()))
|
||||
objCxxOnly = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
|
||||
// RUN: %target-swift-frontend %s -module-name UseFoundation -enable-experimental-cxx-interop -typecheck -verify -emit-clang-header-path %t/UseFoundation.h
|
||||
// RUN: %target-swift-frontend %s -module-name UseFoundation -enable-experimental-cxx-interop -typecheck -verify -emit-clang-header-path %t/UseFoundation.h -enable-experimental-feature RequiresObjC=CoreGraphics,Foundation
|
||||
// RUN: %FileCheck %s < %t/UseFoundation.h
|
||||
|
||||
// RUN: %check-interop-cxx-header-in-clang(%t/UseFoundation.h -DSWIFT_CXX_INTEROP_HIDE_STL_OVERLAY)
|
||||
|
||||
@@ -21,6 +21,8 @@ EXCEPTIONAL_FILES = [
|
||||
pathlib.Path("test/ModuleInterface/swift-export-as.swift"),
|
||||
# Uses the pseudo-feature AvailabilityMacro=
|
||||
pathlib.Path("test/Availability/availability_define.swift"),
|
||||
# Uses the pseudo-feature RequiresObjC=
|
||||
pathlib.Path("test/Interop/SwiftToCxx/stdlib/foundation-type-not-exposed-by-default-to-cxx.swift"),
|
||||
# Tests behavior when you try to use a feature without enabling it
|
||||
pathlib.Path("test/attr/feature_requirement.swift"),
|
||||
# Tests completion with features both enabled and disabled
|
||||
|
||||
Reference in New Issue
Block a user