[PrintAsCxx] Fix printing of C++ enum args

Because imported enums are @objc, they were treated as unsupported in C++ and therefore ineligible to be printed in a C++ generated header. Narrow this logic so that only @objc *classes* are excluded, and update related printing logic to support enums correctly.

Fixes rdar://124262637.
This commit is contained in:
Becca Royal-Gordon
2024-06-17 10:17:55 -07:00
parent 4fc0f3ab61
commit 7fa35d4d6a
4 changed files with 95 additions and 24 deletions

View File

@@ -211,8 +211,6 @@ swift::cxx_translation::getNameForCxx(const ValueDecl *VD,
swift::cxx_translation::DeclRepresentation swift::cxx_translation::DeclRepresentation
swift::cxx_translation::getDeclRepresentation(const ValueDecl *VD) { swift::cxx_translation::getDeclRepresentation(const ValueDecl *VD) {
if (VD->isObjC())
return {Unsupported, UnrepresentableObjC};
if (getActorIsolation(const_cast<ValueDecl *>(VD)).isActorIsolated()) if (getActorIsolation(const_cast<ValueDecl *>(VD)).isActorIsolated())
return {Unsupported, UnrepresentableIsolatedInActor}; return {Unsupported, UnrepresentableIsolatedInActor};
if (isa<MacroDecl>(VD)) if (isa<MacroDecl>(VD))
@@ -238,6 +236,8 @@ swift::cxx_translation::getDeclRepresentation(const ValueDecl *VD) {
// Swift's consume semantics are not yet supported in C++. // Swift's consume semantics are not yet supported in C++.
if (!typeDecl->canBeCopyable()) if (!typeDecl->canBeCopyable())
return {Unsupported, UnrepresentableMoveOnly}; return {Unsupported, UnrepresentableMoveOnly};
if (isa<ClassDecl>(VD) && VD->isObjC())
return {Unsupported, UnrepresentableObjC};
if (typeDecl->isGeneric()) { if (typeDecl->isGeneric()) {
if (isa<ClassDecl>(VD)) if (isa<ClassDecl>(VD))
return {Unsupported, UnrepresentableGeneric}; return {Unsupported, UnrepresentableGeneric};

View File

@@ -2925,16 +2925,34 @@ static bool isEnumExposableToCxx(const ValueDecl *VD,
} }
bool DeclAndTypePrinter::shouldInclude(const ValueDecl *VD) { bool DeclAndTypePrinter::shouldInclude(const ValueDecl *VD) {
return !VD->isInvalid() && (!requiresExposedAttribute || hasExposeAttr(VD)) && if (VD->isInvalid())
(outputLang == OutputLanguageMode::Cxx return false;
? cxx_translation::isVisibleToCxx(VD, minRequiredAccess) &&
isExposedToThisModule(M, VD, exposedModules) && if (requiresExposedAttribute && !hasExposeAttr(VD))
cxx_translation::isExposableToCxx(VD) && return false;
isEnumExposableToCxx(VD, *this)
: isVisibleToObjC(VD, minRequiredAccess)) && if (!isVisible(VD))
!VD->getAttrs().hasAttribute<ImplementationOnlyAttr>() && return false;
!isAsyncAlternativeOfOtherDecl(VD) &&
!excludeForObjCImplementation(VD); if (outputLang == OutputLanguageMode::Cxx) {
if (!isExposedToThisModule(M, VD, exposedModules))
return false;
if (!cxx_translation::isExposableToCxx(VD))
return false;
if (!isEnumExposableToCxx(VD, *this))
return false;
}
if (VD->getAttrs().hasAttribute<ImplementationOnlyAttr>())
return false;
if (isAsyncAlternativeOfOtherDecl(VD))
return false;
if (excludeForObjCImplementation(VD))
return false;
return true;
} }
bool DeclAndTypePrinter::isVisible(const ValueDecl *vd) const { bool DeclAndTypePrinter::isVisible(const ValueDecl *vd) const {

View File

@@ -102,18 +102,45 @@ struct CFunctionSignatureTypePrinterModifierDelegate {
class ClangTypeHandler { class ClangTypeHandler {
public: public:
ClangTypeHandler(const clang::Decl *typeDecl) : typeDecl(typeDecl) {} ClangTypeHandler(const clang::Decl *typeDecl)
: typeDecl(dyn_cast<clang::TagDecl>(typeDecl)) {}
bool isRepresentable() const { bool isRepresentable() const {
// We can only return trivial types, or // We can only return tag types.
// types that can be moved or copied. if (typeDecl) {
if (auto *record = dyn_cast<clang::CXXRecordDecl>(typeDecl)) { // We can return trivial types.
return record->isTrivial() || record->hasMoveConstructor() || if (isTrivial(typeDecl))
record->hasCopyConstructorWithConstParam(); return true;
// We can return nontrivial types iff they can be moved or copied.
if (auto *record = dyn_cast<clang::CXXRecordDecl>(typeDecl)) {
return record->hasMoveConstructor() ||
record->hasCopyConstructorWithConstParam();
}
} }
// Otherwise, we can't return this type.
return false; return false;
} }
private:
/// Is the tag type trivial?
static bool isTrivial(const clang::TagDecl *typeDecl) {
if (!typeDecl)
return false;
if (auto *record = dyn_cast<clang::CXXRecordDecl>(typeDecl))
return record->isTrivial();
// FIXME: If we can get plain clang::RecordDecls here, we need to figure out
// how nontrivial (i.e. ARC) fields work.
assert(!isa<clang::RecordDecl>(typeDecl));
// C-family enums are always trivial.
return isa<clang::EnumDecl>(typeDecl);
}
public:
void printTypeName(raw_ostream &os) const { void printTypeName(raw_ostream &os) const {
ClangSyntaxPrinter(os).printClangTypeReference(typeDecl); ClangSyntaxPrinter(os).printClangTypeReference(typeDecl);
} }
@@ -133,7 +160,7 @@ public:
llvm::raw_string_ostream typeNameOS(fullQualifiedType); llvm::raw_string_ostream typeNameOS(fullQualifiedType);
printTypeName(typeNameOS); printTypeName(typeNameOS);
llvm::raw_string_ostream unqualTypeNameOS(typeName); llvm::raw_string_ostream unqualTypeNameOS(typeName);
unqualTypeNameOS << cast<clang::NamedDecl>(typeDecl)->getName(); unqualTypeNameOS << typeDecl->getName();
} }
printReturnScaffold(typeDecl, os, fullQualifiedType, typeName, printReturnScaffold(typeDecl, os, fullQualifiedType, typeName,
bodyOfReturn); bodyOfReturn);
@@ -141,7 +168,7 @@ public:
private: private:
static void static void
printReturnScaffold(const clang::Decl *typeDecl, raw_ostream &os, printReturnScaffold(const clang::TagDecl *typeDecl, raw_ostream &os,
StringRef fullQualifiedType, StringRef typeName, StringRef fullQualifiedType, StringRef typeName,
llvm::function_ref<void(StringRef)> bodyOfReturn) { llvm::function_ref<void(StringRef)> bodyOfReturn) {
os << "alignas(alignof(" << fullQualifiedType << ")) char storage[sizeof(" os << "alignas(alignof(" << fullQualifiedType << ")) char storage[sizeof("
@@ -150,7 +177,7 @@ private:
<< fullQualifiedType << " *>(storage);\n"; << fullQualifiedType << " *>(storage);\n";
bodyOfReturn("storage"); bodyOfReturn("storage");
os << ";\n"; os << ";\n";
if (typeDecl && cast<clang::CXXRecordDecl>(typeDecl)->isTrivial()) { if (isTrivial(typeDecl)) {
// Trivial object can be just copied and not destroyed. // Trivial object can be just copied and not destroyed.
os << "return *storageObjectPtr;\n"; os << "return *storageObjectPtr;\n";
return; return;
@@ -162,7 +189,7 @@ private:
os << "return result;\n"; os << "return result;\n";
} }
const clang::Decl *typeDecl; const clang::TagDecl *typeDecl;
}; };
// Prints types in the C function signature that corresponds to the // Prints types in the C function signature that corresponds to the
@@ -371,6 +398,7 @@ public:
return ClangRepresentation::unsupported; return ClangRepresentation::unsupported;
if (decl->hasClangNode()) { if (decl->hasClangNode()) {
assert(genericArgs.empty() && "this path doesn't support generic args");
ClangTypeHandler handler(decl->getClangDecl()); ClangTypeHandler handler(decl->getClangDecl());
if (!handler.isRepresentable()) if (!handler.isRepresentable())
return ClangRepresentation::unsupported; return ClangRepresentation::unsupported;

View File

@@ -3,11 +3,11 @@
// 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 -disable-availability-checking // 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 -disable-availability-checking
// RUN: %FileCheck %s < %t/UseCxxTy.h // RUN: %FileCheck %s --input-file %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 -clang-header-expose-decls=has-expose-attr -disable-availability-checking // 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 -disable-availability-checking
// RUN: %FileCheck %s < %t/UseCxxTyExposeOnly.h // RUN: %FileCheck %s --input-file %t/UseCxxTyExposeOnly.h
// FIXME: remove once https://github.com/apple/swift/pull/60971 lands. // FIXME: remove once https://github.com/apple/swift/pull/60971 lands.
// RUN: echo "#include \"header.h\"" > %t/full-cxx-swift-cxx-bridging.h // RUN: echo "#include \"header.h\"" > %t/full-cxx-swift-cxx-bridging.h
@@ -91,6 +91,9 @@ using anonStructInNS = struct { float row; };
} }
enum class SimpleScopedEnum { x = 0, y = 2 };
typedef SimpleScopedEnum SimpleScopedEnumTypedef;
//--- module.modulemap //--- module.modulemap
module CxxTest { module CxxTest {
header "header.h" header "header.h"
@@ -130,6 +133,16 @@ public func retNonTrivialTypeAlias() -> ns.TypeAlias {
return ns.TypeAlias() return ns.TypeAlias()
} }
@_expose(Cxx)
public func retSimpleScopedEnum() -> SimpleScopedEnum {
return .x
}
@_expose(Cxx)
public func retSimpleScopedEnumTypedef() -> SimpleScopedEnumTypedef {
return .x
}
@_expose(Cxx) @_expose(Cxx)
public func retSimpleTypedef() -> SimpleTypedef { public func retSimpleTypedef() -> SimpleTypedef {
return SimpleTypedef() return SimpleTypedef()
@@ -152,6 +165,10 @@ public func takeImmortalTemplate(_ x: ns.ImmortalCInt) {
public func takeNonTrivial2(_ x: ns.NonTrivialTemplateTrivial) { public func takeNonTrivial2(_ x: ns.NonTrivialTemplateTrivial) {
} }
@_expose(Cxx)
public func takeSimpleScopedEnum(_ x: SimpleScopedEnum) {
}
@_expose(Cxx) @_expose(Cxx)
public func takeTrivial(_ x: Trivial) { public func takeTrivial(_ x: Trivial) {
} }
@@ -275,6 +292,12 @@ public struct Strct {
// CHECK: ns::NonTrivialTemplate<ns::TrivialinNS> retNonTrivialTypeAlias() noexcept SWIFT_SYMBOL({{.*}}) SWIFT_WARN_UNUSED_RESULT { // CHECK: ns::NonTrivialTemplate<ns::TrivialinNS> retNonTrivialTypeAlias() noexcept SWIFT_SYMBOL({{.*}}) SWIFT_WARN_UNUSED_RESULT {
// CHECK: SimpleScopedEnum retSimpleScopedEnum() noexcept SWIFT_SYMBOL({{.*}}) SWIFT_WARN_UNUSED_RESULT {
// FIXME: Would we prefer to print these with the typedef names?
// CHECK: SimpleScopedEnum retSimpleScopedEnumTypedef() noexcept SWIFT_SYMBOL({{.*}}) SWIFT_WARN_UNUSED_RESULT {
// CHECK: int32_t retSimpleTypedef() noexcept SWIFT_SYMBOL({{.*}}) SWIFT_WARN_UNUSED_RESULT {
// CHECK: SWIFT_INLINE_THUNK Trivial retTrivial() noexcept SWIFT_SYMBOL({{.*}}) SWIFT_WARN_UNUSED_RESULT { // CHECK: SWIFT_INLINE_THUNK Trivial retTrivial() noexcept SWIFT_SYMBOL({{.*}}) SWIFT_WARN_UNUSED_RESULT {
// CHECK-NEXT: alignas(alignof(Trivial)) char storage[sizeof(Trivial)]; // CHECK-NEXT: alignas(alignof(Trivial)) char storage[sizeof(Trivial)];
// CHECK-NEXT: auto * _Nonnull storageObjectPtr = reinterpret_cast<Trivial *>(storage); // CHECK-NEXT: auto * _Nonnull storageObjectPtr = reinterpret_cast<Trivial *>(storage);
@@ -294,6 +317,8 @@ public struct Strct {
// CHECK-NEXT: _impl::$s8UseCxxTy15takeNonTrivial2yySo2nsO0037NonTrivialTemplateTrivialinNS_CsGGkdcVF(swift::_impl::getOpaquePointer(x)); // CHECK-NEXT: _impl::$s8UseCxxTy15takeNonTrivial2yySo2nsO0037NonTrivialTemplateTrivialinNS_CsGGkdcVF(swift::_impl::getOpaquePointer(x));
// CHECK-NEXT: } // CHECK-NEXT: }
// CHECK: SWIFT_INLINE_THUNK void takeSimpleScopedEnum(const SimpleScopedEnum& x) noexcept SWIFT_SYMBOL({{.*}}) {
// CHECK: SWIFT_INLINE_THUNK void takeTrivial(const Trivial& x) noexcept SWIFT_SYMBOL({{.*}}) { // CHECK: SWIFT_INLINE_THUNK void takeTrivial(const Trivial& x) noexcept SWIFT_SYMBOL({{.*}}) {
// CHECK-NEXT: _impl::$s8UseCxxTy11takeTrivialyySo0E0VF(_impl::swift_interop_passDirect_UseCxxTy_uint32_t_0_4(reinterpret_cast<const char *>(swift::_impl::getOpaquePointer(x)))); // CHECK-NEXT: _impl::$s8UseCxxTy11takeTrivialyySo0E0VF(_impl::swift_interop_passDirect_UseCxxTy_uint32_t_0_4(reinterpret_cast<const char *>(swift::_impl::getOpaquePointer(x))));
// CHECK-NEXT: } // CHECK-NEXT: }