Skip to content

Commit ab12392

Browse files
Xazax-hunGabor Horvath
authored andcommitted
[6.2][cxx-interop] Types exposed from ObjC modules should be behind a macro
Explanation: We the generated reverse interop headers to be valid C++, so every declaration coming from an Obj-C module should be behind an ifdef. Unfortunately, we do not always have this information but we do know that our frameworks contain Obj-C code. So this PR makes sure every entity coming from our frameworks are behind ifdef. Issues: rdar://152836730 Original PRs: #83002 Risk: Low, the change is narrow and straightforward. Testing: Added a compiler test. Reviewers: @egorzhdan
1 parent 234a415 commit ab12392

File tree

5 files changed

+63
-5
lines changed

5 files changed

+63
-5
lines changed

include/swift/AST/SwiftNameTranslation.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,9 @@ inline bool isExposableToCxx(
112112
return !getDeclRepresentation(VD, isZeroSized).isUnsupported();
113113
}
114114

115+
bool isObjCxxOnly(const ValueDecl *VD);
116+
bool isObjCxxOnly(const clang::Decl *D);
117+
115118
/// Returns true if the given value decl D is visible to C++ of its
116119
/// own accord (i.e. without considering its context)
117120
bool isVisibleToCxx(const ValueDecl *VD, AccessLevel minRequiredAccess,

lib/AST/SwiftNameTranslation.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "swift/Basic/StringExtras.h"
2929

3030
#include "clang/AST/DeclObjC.h"
31+
#include "clang/Basic/Module.h"
3132
#include "llvm/ADT/SmallString.h"
3233
#include <optional>
3334

@@ -238,6 +239,39 @@ swift::cxx_translation::getNameForCxx(const ValueDecl *VD,
238239
return VD->getBaseIdentifier().str();
239240
}
240241

242+
namespace {
243+
struct ObjCTypeWalker : TypeWalker {
244+
bool hadObjCType = false;
245+
Action walkToTypePre(Type ty) override {
246+
if (auto *nominal = ty->getNominalOrBoundGenericNominal()) {
247+
if (auto clangDecl = nominal->getClangDecl()) {
248+
if (cxx_translation::isObjCxxOnly(clangDecl)) {
249+
hadObjCType = true;
250+
return Action::Stop;
251+
}
252+
}
253+
}
254+
return Action::Continue;
255+
}
256+
};
257+
} // namespace
258+
259+
bool swift::cxx_translation::isObjCxxOnly(const ValueDecl *VD) {
260+
ObjCTypeWalker walker;
261+
VD->getInterfaceType().walk(walker);
262+
return walker.hadObjCType;
263+
}
264+
265+
bool swift::cxx_translation::isObjCxxOnly(const clang::Decl *D) {
266+
// By default, we import all modules in Obj-C++ mode, so there is no robust
267+
// way to tell if something is coming from an Obj-C module. The best
268+
// approximation is to check if something is a framework module as most of
269+
// those are written in Obj-C. Ideally, we want to add `requires ObjC` to all
270+
// ObjC modules and respect that here to make this completely precise.
271+
return D->getASTContext().getLangOpts().ObjC &&
272+
D->getOwningModule()->isPartOfFramework();
273+
}
274+
241275
swift::cxx_translation::DeclRepresentation
242276
swift::cxx_translation::getDeclRepresentation(
243277
const ValueDecl *VD,
@@ -324,6 +358,9 @@ swift::cxx_translation::getDeclRepresentation(
324358
return {Unsupported, UnrepresentableGenericRequirements};
325359
}
326360

361+
if (isObjCxxOnly(VD))
362+
return {ObjCxxOnly, std::nullopt};
363+
327364
return {Representable, std::nullopt};
328365
}
329366

lib/PrintAsClang/PrintClangFunction.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -812,7 +812,8 @@ ClangRepresentation DeclAndTypeClangFunctionPrinter::printFunctionSignature(
812812
ClangSyntaxPrinter(FD->getASTContext(), functionSignatureOS).printInlineForThunk();
813813

814814
ClangRepresentation resultingRepresentation =
815-
ClangRepresentation::representable;
815+
cxx_translation::isObjCxxOnly(FD) ? ClangRepresentation::objcxxonly
816+
: ClangRepresentation::representable;
816817

817818
// Print out the return type.
818819
if (FD->hasThrows() && outputLang == OutputLanguageMode::Cxx)

lib/PrintAsClang/PrintClangValueType.cpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "swift/ClangImporter/ClangImporter.h"
2323
#include "swift/IRGen/IRABIDetailsProvider.h"
2424
#include "swift/IRGen/Linking.h"
25+
#include "clang/Basic/Module.h"
2526
#include "llvm/ADT/STLExtras.h"
2627
#include "llvm/Support/raw_ostream.h"
2728

@@ -642,20 +643,28 @@ void ClangValueTypePrinter::printTypeGenericTraits(
642643
});
643644
}
644645

646+
bool objCxxOnly = false;
647+
if (const auto *clangDecl = typeDecl->getClangDecl()) {
648+
if (cxx_translation::isObjCxxOnly(clangDecl))
649+
objCxxOnly = true;
650+
}
651+
645652
// FIXME: avoid popping out of the module's namespace here.
646653
os << "} // end namespace \n\n";
647654
os << "namespace swift SWIFT_PRIVATE_ATTR {\n";
648655
auto classDecl = dyn_cast<ClassDecl>(typeDecl);
649656
bool addPointer =
650657
typeDecl->isObjC() || (classDecl && classDecl->isForeignReferenceType());
651658

659+
if (objCxxOnly)
660+
os << "#if defined(__OBJC__)\n";
652661
os << "#pragma clang diagnostic push\n";
653662
os << "#pragma clang diagnostic ignored \"-Wc++17-extensions\"\n";
654-
if (typeDecl->hasClangNode()) {
663+
if (const auto *clangDecl = typeDecl->getClangDecl()) {
655664
// FIXME: share the code.
656665
os << "template<>\n";
657666
os << "inline const constexpr bool isUsableInGenericContext<";
658-
printer.printClangTypeReference(typeDecl->getClangDecl());
667+
printer.printClangTypeReference(clangDecl);
659668
if (addPointer)
660669
os << "*";
661670
os << "> = true;\n";
@@ -665,8 +674,8 @@ void ClangValueTypePrinter::printTypeGenericTraits(
665674
os << "struct";
666675
declAndTypePrinter.printAvailability(os, typeDecl);
667676
os << " TypeMetadataTrait<";
668-
if (typeDecl->hasClangNode()) {
669-
printer.printClangTypeReference(typeDecl->getClangDecl());
677+
if (const auto *clangDecl = typeDecl->getClangDecl()) {
678+
printer.printClangTypeReference(clangDecl);
670679
if (addPointer)
671680
os << "*";
672681
} else {
@@ -746,6 +755,8 @@ void ClangValueTypePrinter::printTypeGenericTraits(
746755
}
747756
os << "} // namespace\n";
748757
os << "#pragma clang diagnostic pop\n";
758+
if (objCxxOnly)
759+
os << "#endif // #if defined(__OBJC__)\n";
749760
os << "} // namespace swift\n";
750761
os << "\n";
751762
printer.printModuleNamespaceStart(*moduleContext);

test/Interop/SwiftToCxx/stdlib/foundation-type-not-exposed-by-default-to-cxx.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
// RUN: %target-swift-frontend %s -module-name UseFoundation -enable-experimental-cxx-interop -typecheck -verify -emit-clang-header-path %t/UseFoundation.h
44
// RUN: %FileCheck %s < %t/UseFoundation.h
55

6+
// RUN: %check-interop-cxx-header-in-clang(%t/UseFoundation.h -DSWIFT_CXX_INTEROP_HIDE_STL_OVERLAY)
7+
68
// REQUIRES: objc_interop
79

810
import Foundation
@@ -12,4 +14,8 @@ public enum UseFoundationEnum {
1214
case B
1315
}
1416

17+
public func f() -> Decimal {
18+
Decimal()
19+
}
20+
1521
// CHECK: class UseFoundationEnum { } SWIFT_UNAVAILABLE_MSG("Swift enum 'UseFoundationEnum' cannot be represented in C++");

0 commit comments

Comments
 (0)