Skip to content

[interop][SwiftToCxx] Declare the generic usability type trait before… #61225

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/PrintAsClang/DeclAndTypePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,8 @@ class DeclAndTypePrinter::Implementation
if (outputLang == OutputLanguageMode::Cxx) {
// FIXME: Non objc class.
// FIXME: Print availability.
// FIXME: forward decl should be handled by ModuleWriter.
ClangValueTypePrinter::forwardDeclType(os, CD);
ClangClassTypePrinter(os).printClassTypeDecl(
CD, [&]() { printMembers(CD->getMembers()); });
return;
Expand Down
18 changes: 11 additions & 7 deletions lib/PrintAsClang/ModuleContentsWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -274,17 +274,19 @@ class ModuleWriter {
ClangValueTypePrinter::printClangTypeSwiftGenericTraits(os, typeDecl, &M);
}

void forwardDeclareCxxValueTypeIfNeeded(const NominalTypeDecl *NTD) {
forwardDeclare(NTD,
[&]() { ClangValueTypePrinter::forwardDeclType(os, NTD); });
}

void forwardDeclareType(const TypeDecl *TD) {
if (outputLangMode == OutputLanguageMode::Cxx) {
if (isa<StructDecl>(TD) || isa<EnumDecl>(TD)) {
auto *NTD = cast<NominalTypeDecl>(TD);
if (!addImport(NTD)) {
forwardDeclare(
NTD, [&]() { ClangValueTypePrinter::forwardDeclType(os, NTD); });
} else {
if (isa<StructDecl>(TD) && NTD->hasClangNode())
emitReferencedClangTypeMetadata(NTD);
}
if (!addImport(NTD))
forwardDeclareCxxValueTypeIfNeeded(NTD);
else if (isa<StructDecl>(TD) && NTD->hasClangNode())
emitReferencedClangTypeMetadata(NTD);
}
return;
}
Expand Down Expand Up @@ -471,6 +473,7 @@ class ModuleWriter {
printer.getInteropContext().getExtensionsForNominalType(SD)) {
(void)forwardDeclareMemberTypes(ed->getMembers(), SD);
}
forwardDeclareCxxValueTypeIfNeeded(SD);
}
printer.print(SD);
return true;
Expand Down Expand Up @@ -536,6 +539,7 @@ class ModuleWriter {

if (outputLangMode == OutputLanguageMode::Cxx) {
forwardDeclareMemberTypes(ED->getMembers(), ED);
forwardDeclareCxxValueTypeIfNeeded(ED);
}

if (seenTypes[ED].first == EmissionState::Defined)
Expand Down
31 changes: 29 additions & 2 deletions lib/PrintAsClang/PrintClangValueType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ void ClangValueTypePrinter::forwardDeclType(raw_ostream &os,
os << "class ";
ClangSyntaxPrinter(os).printBaseName(typeDecl);
os << ";\n";
printTypePrecedingGenericTraits(os, typeDecl, typeDecl->getModuleContext());
}

static void addCppExtensionsToStdlibType(const NominalTypeDecl *typeDecl,
Expand Down Expand Up @@ -471,6 +472,32 @@ void ClangValueTypePrinter::printClangTypeSwiftGenericTraits(
/*typeMetadataFuncRequirements=*/{}, moduleContext);
}

void ClangValueTypePrinter::printTypePrecedingGenericTraits(
raw_ostream &os, const NominalTypeDecl *typeDecl,
const ModuleDecl *moduleContext) {
assert(!typeDecl->hasClangNode());
ClangSyntaxPrinter printer(os);
// FIXME: avoid popping out of the module's namespace here.
os << "} // end namespace \n\n";
os << "namespace swift {\n";

os << "#pragma clang diagnostic push\n";
os << "#pragma clang diagnostic ignored \"-Wc++17-extensions\"\n";
if (!typeDecl->isGeneric()) {
// FIXME: generic type support.
os << "template<>\n";
os << "static inline const constexpr bool isUsableInGenericContext<";
printer.printNominalTypeReference(typeDecl,
/*moduleContext=*/nullptr);
os << "> = true;\n";
}
os << "#pragma clang diagnostic pop\n";
os << "} // namespace swift\n";
os << "\nnamespace ";
printer.printBaseName(moduleContext);
os << " {\n";
}

void ClangValueTypePrinter::printTypeGenericTraits(
raw_ostream &os, const NominalTypeDecl *typeDecl,
StringRef typeMetadataFuncName,
Expand All @@ -492,8 +519,8 @@ void ClangValueTypePrinter::printTypeGenericTraits(

os << "#pragma clang diagnostic push\n";
os << "#pragma clang diagnostic ignored \"-Wc++17-extensions\"\n";
if (typeMetadataFuncRequirements.empty()) {
// FIXME: generic type support.
if (typeDecl->hasClangNode()) {
// FIXME: share the code.
os << "template<>\n";
os << "static inline const constexpr bool isUsableInGenericContext<";
printer.printNominalTypeReference(typeDecl,
Expand Down
4 changes: 4 additions & 0 deletions lib/PrintAsClang/PrintClangValueType.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ class ClangValueTypePrinter {
ArrayRef<GenericRequirement> typeMetadataFuncRequirements,
const ModuleDecl *moduleContext);

static void printTypePrecedingGenericTraits(raw_ostream &os,
const NominalTypeDecl *typeDecl,
const ModuleDecl *moduleContext);

static void forwardDeclType(raw_ostream &os, const NominalTypeDecl *typeDecl);

/// Print out the type traits that allow a C++ type be used a Swift generic
Expand Down
20 changes: 17 additions & 3 deletions test/Interop/SwiftToCxx/class/swift-class-in-cxx.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,23 @@ public final class ClassWithIntField {

// CHECK: namespace Class {

// CHECK: namespace _impl {
// CHECK: class ClassWithIntField;
// CHECK-NEXT: } // end namespace

// CHECK: namespace
// CHECK-SAME: swift {
// CHECK-NEXT: #pragma clang diagnostic push
// CHECK-NEXT: #pragma clang diagnostic ignored "-Wc++17-extensions"
// CHECK-NEXT: template<>
// CHECK-NEXT: static inline const constexpr bool isUsableInGenericContext<Class::ClassWithIntField> = true;
// CHECK-NEXT: #pragma clang diagnostic pop
// CHECK-NEXT: } // namespace swift

// CHECK: namespace
// CHECK-SAME: Class {

// CHECK: namespace
// CHECK-SAME: _impl {
// CHECK-EMPTY:
// CHECK-NEXT: class _impl_ClassWithIntField;
// CHECK-NEXT: // Type metadata accessor for ClassWithIntField
Expand Down Expand Up @@ -64,8 +80,6 @@ public final class ClassWithIntField {
// CHECK-NEXT: #pragma clang diagnostic push
// CHECK-NEXT: #pragma clang diagnostic ignored "-Wc++17-extensions"
// CHECK-NEXT: template<>
// CHECK-NEXT: static inline const constexpr bool isUsableInGenericContext<Class::ClassWithIntField> = true;
// CHECK-NEXT: template<>
// CHECK-NEXT: struct TypeMetadataTrait<Class::ClassWithIntField> {
// CHECK-NEXT: inline void * _Nonnull getTypeMetadata() {
// CHECK-NEXT: return Class::_impl::$s5Class0A12WithIntFieldCMa(0)._0;
Expand Down
4 changes: 2 additions & 2 deletions test/Interop/SwiftToCxx/enums/zero-sized-enum-in-cxx.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ public enum EmptyEnum {}
public enum SingleCaseEnum { case first }

// CHECK: namespace Enums {
// CHECK-NOT: EmptyEnum
// CHECK-NOT: SingleCaseEnum
// CHECK-NOT: class EmptyEnum final {
// CHECK-NOT: class SingleCaseEnum final {
// CHECK: } // namespace Enums
4 changes: 4 additions & 0 deletions test/Interop/SwiftToCxx/generics/generic-enum-in-cxx.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ public func inoutConcreteOpt(_ x: inout GenericOpt<UInt16>) {
}
}

// CHECK: template<class T_0_0>
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0>
// CHECK-NEXT: class GenericOpt;

// CHECK: template<class T_0_0>
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0>
// CHECK-NEXT: class _impl_GenericOpt;
Expand Down
10 changes: 5 additions & 5 deletions test/Interop/SwiftToCxx/generics/generic-struct-in-cxx.swift
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,10 @@ public func inoutConcretePair(_ x: UInt16, _ y: inout GenericPair<UInt16, UInt16
// CHECK-NEXT: SWIFT_EXTERN void $s8Generics16takeConcretePairyyAA07GenericD0Vys6UInt16VAFGF(struct swift_interop_passStub_Generics_uint32_t_0_4 x) SWIFT_NOEXCEPT SWIFT_CALL; // takeConcretePair(_:)
// CHECK-NEXT: SWIFT_EXTERN void $s8Generics15takeGenericPairyyAA0cD0Vyxq_Gr0_lF(const void * _Nonnull x, void * _Nonnull , void * _Nonnull ) SWIFT_NOEXCEPT SWIFT_CALL; // takeGenericPair(_:)

// CHECK: template<class T_0_0, class T_0_1>
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0> && swift::isUsableInGenericContext<T_0_1>
// CHECK-NEXT: class GenericPair;

// CHECK: template<class T_0_0, class T_0_1>
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0> && swift::isUsableInGenericContext<T_0_1>
// CHECK-NEXT: class _impl_GenericPair;
Expand Down Expand Up @@ -247,11 +251,7 @@ public func inoutConcretePair(_ x: UInt16, _ y: inout GenericPair<UInt16, UInt16
// CHECK-NEXT: #pragma clang diagnostic pop
// CHECK-NEXT: } // namespace swift

// CHECK: template<class T_0_0, class T_0_1>
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0> && swift::isUsableInGenericContext<T_0_1>
// CHECK-NEXT: class GenericPair;
// CHECK-EMPTY:
// CHECK-NEXT: inline void inoutConcretePair(uint16_t x, GenericPair<uint16_t, uint16_t>& y) noexcept {
// CHECK: inline void inoutConcretePair(uint16_t x, GenericPair<uint16_t, uint16_t>& y) noexcept {
// CHECK-NEXT: return _impl::$s8Generics17inoutConcretePairyys6UInt16V_AA07GenericD0VyA2DGztF(x, _impl::_impl_GenericPair<uint16_t, uint16_t>::getOpaquePointer(y));
// CHECK-NEXT: }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@
// CHECK-NEXT: SWIFT_EXTERN void $s8Generics16takeConcretePairyyAA07GenericD0Vys6UInt16VAFGF(struct swift_interop_passStub_Generics_[[PTRPTRENC]] x) SWIFT_NOEXCEPT SWIFT_CALL; // takeConcretePair(_:)
// CHECK-NEXT: SWIFT_EXTERN void $s8Generics15takeGenericPairyyAA0cD0Vyxq_Gr0_lF(struct swift_interop_passStub_Generics_[[PTRPTRENC]] x, void * _Nonnull , void * _Nonnull ) SWIFT_NOEXCEPT SWIFT_CALL; // takeGenericPair(_:)

// CHECK: template<class T_0_0, class T_0_1>
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0> && swift::isUsableInGenericContext<T_0_1>
// CHECK-NEXT: class GenericPair;

// CHECK: template<class T_0_0, class T_0_1>
// CHECK: template<class T_0_0, class T_0_1>
Expand Down
50 changes: 50 additions & 0 deletions test/Interop/SwiftToCxx/generics/generic-type-traits-fwd.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -typecheck -module-name Generics -enable-experimental-cxx-interop -emit-clang-header-path %t/generics.h
// RUN: %FileCheck %s < %t/generics.h
// RUN: %check-generic-interop-cxx-header-in-clang(%t/generics.h)

@_expose(Cxx)
public enum ComesFirstEnum {
case A
case B

public func returnsLaterOpt() -> LaterGeneric<ComesFirstEnum> { return LaterGeneric(x: ComesFirstEnum.A) }

public var first: ComesFirstStruct {
return ComesFirstStruct(x: 42)
}
}

@_expose(Cxx)
public struct ComesFirstStruct {
let x: Int

public func returnsLaterOpt() -> LaterGeneric<ComesFirstStruct> { return LaterGeneric(x: ComesFirstStruct(x: 0)) }
}

@_expose(Cxx)
public struct LaterGeneric<T> {
let x: T
}


// CHECK: class LaterGeneric;

// CHECK: class ComesFirstStruct;
// CHECK: static inline const constexpr bool isUsableInGenericContext<Generics::ComesFirstStruct> = true;

// CHECK: class ComesFirstEnum;
// CHECK: static inline const constexpr bool isUsableInGenericContext<Generics::ComesFirstEnum> = true;

// CHECK: class ComesFirstEnum final {
// CHECK: LaterGeneric<ComesFirstEnum> returnsLaterOpt() const;

// CHECK: namespace Generics {
// CHECK-EMPTY:
// CHECK-NEXT: namespace _impl {
// CHECK-EMPTY:
// CHECK-NEXT: class _impl_ComesFirstStruct;

// CHECK: class ComesFirstStruct final {
// CHECK: LaterGeneric<ComesFirstStruct> returnsLaterOpt() const;
// CHECK: class LaterGeneric final {
7 changes: 7 additions & 0 deletions test/Interop/SwiftToCxx/stdlib/swift-stdlib-in-cxx.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,15 @@

// CHECK: namespace Swift {

// CHECK: template<class T_0_0>
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0>
// CHECK-NEXT: class Optional;

// CHECK: class String;

// CHECK: template<class T_0_0>
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0>
// CHECK-NEXT: class Array;
// CHECK: template<class T_0_0>
// CHECK: template<class T_0_0>
// CHECK-NEXT: requires swift::isUsableInGenericContext<T_0_0>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ public struct FirstSmallStruct {
}
}

// CHECK: class FirstSmallStruct;

// CHECK: template<>
// CHECK-NEXT: static inline const constexpr bool isUsableInGenericContext<Structs::FirstSmallStruct> = true;

// CHECK: class FirstSmallStruct final {
// CHECK-NEXT: public:
// CHECK: inline FirstSmallStruct(const FirstSmallStruct &other) {
Expand Down Expand Up @@ -71,8 +76,6 @@ public struct FirstSmallStruct {
// CHECK-NEXT: #pragma clang diagnostic push
// CHECK-NEXT: #pragma clang diagnostic ignored "-Wc++17-extensions"
// CHECK-NEXT: template<>
// CHECK-NEXT: static inline const constexpr bool isUsableInGenericContext<Structs::FirstSmallStruct> = true;
// CHECK-NEXT: template<>
// CHECK-NEXT: struct TypeMetadataTrait<Structs::FirstSmallStruct> {
// CHECK-NEXT: inline void * _Nonnull getTypeMetadata() {
// CHECK-NEXT: return Structs::_impl::$s7Structs16FirstSmallStructVMa(0)._0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,20 @@ public struct B {
}

// CHECK: class B;
// CHECK-NEXT: namespace _impl {
// CHECK: class A;
// CHECK: namespace _impl {
// CHECK-EMPTY:
// CHECK-NEXT: class _impl_A;

// CHECK: class A final {

// CHECK: B returnsB() const;

// CHECK: class A;
// CHECK-NEXT: namespace _impl {
// CHECK: namespace _impl {
// CHECK-EMPTY:
// CHECK-NEXT: class _impl_A {

// CHECK: namespace _impl {
// CHECK-EMPTY:
// CHECK-NEXT: class _impl_B;

Expand Down
15 changes: 13 additions & 2 deletions test/Interop/SwiftToCxx/structs/swift-struct-in-cxx.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,19 @@

// CHECK: namespace Structs {

// CHECK: class StructWithIntField;
// CHECK-NEXT: } // end namespace

// CHECK: namespace swift {
// CHECK-NEXT: #pragma clang diagnostic push
// CHECK-NEXT: #pragma clang diagnostic ignored "-Wc++17-extensions"
// CHECK-NEXT: template<>
// CHECK-NEXT: static inline const constexpr bool isUsableInGenericContext<Structs::StructWithIntField> = true;
// CHECK-NEXT: #pragma clang diagnostic pop
// CHECK-NEXT: } // namespace swift

// CHECK: namespace Structs {

// CHECK: namespace _impl {
// CHECK-EMPTY:
// CHECK-NEXT: class _impl_StructWithIntField;
Expand Down Expand Up @@ -68,8 +81,6 @@
// CHECK-NEXT: #pragma clang diagnostic push
// CHECK-NEXT: #pragma clang diagnostic ignored "-Wc++17-extensions"
// CHECK-NEXT: template<>
// CHECK-NEXT: static inline const constexpr bool isUsableInGenericContext<Structs::StructWithIntField> = true;
// CHECK-NEXT: template<>
// CHECK-NEXT: struct TypeMetadataTrait<Structs::StructWithIntField>
// CHECK-NEXT: inline void * _Nonnull getTypeMetadata() {
// CHECK-NEXT: return Structs::_impl::$s7Structs18StructWithIntFieldVMa(0)._0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

// CHECK: namespace Structs {

// CHECK-NOT: ZeroSizedStruct
// CHECK-NOT: class ZeroSizedStruct final {

public struct ZeroSizedStruct {}

Expand Down