Skip to content

[interop][SwiftToCxx] copy construct & assign should do ARC operations for class reference values #60359

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 6 commits into from
Aug 3, 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
41 changes: 16 additions & 25 deletions lib/PrintAsClang/PrintClangClassType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,32 +31,24 @@ void ClangClassTypePrinter::printClassTypeDecl(
os << ";\n";
});

StringRef baseClassName = "RefCountedClass";
StringRef baseClassQualifiedName = "swift::_impl::RefCountedClass";

os << "class ";
printer.printBaseName(typeDecl);
// FIXME: Add support for inherintance.
os << " final";
os << " final : public " << baseClassQualifiedName;
os << " {\n";
os << "public:\n";

// Destructor releases the object.
os << " inline ~";
printer.printBaseName(typeDecl);
os << "() { swift::" << cxx_synthesis::getCxxImplNamespaceName()
<< "::swift_release(_opaquePointer); }\n";

// FIXME: move semantics should be restricted?
os << " inline ";
printer.printBaseName(typeDecl);
os << "(";
printer.printBaseName(typeDecl);
os << "&&) noexcept = default;\n";
os << " using " << baseClassName << "::" << baseClassName << ";\n";
os << " using " << baseClassName << "::operator=;\n";

os << "private:\n";
os << " inline ";
printer.printBaseName(typeDecl);
os << "(void * _Nonnull ptr) noexcept : _opaquePointer(ptr) {}\n";
os << "\n void * _Nonnull _opaquePointer;\n";
os << " friend class " << cxx_synthesis::getCxxImplNamespaceName() << "::";
os << "(void * _Nonnull ptr) noexcept : " << baseClassName << "(ptr) {}\n";
os << "\n friend class " << cxx_synthesis::getCxxImplNamespaceName() << "::";
printCxxImplClassName(os, typeDecl);
os << ";\n";
os << "};\n\n";
Expand All @@ -73,9 +65,6 @@ void ClangClassTypePrinter::printClassTypeDecl(
os << " makeRetained(void * _Nonnull ptr) noexcept { return ";
printer.printBaseName(typeDecl);
os << "(ptr); }\n";
os << "static inline void * _Nonnull getOpaquePointer(const ";
printer.printBaseName(typeDecl);
os << " &object) noexcept { return object._opaquePointer; }\n";
os << "};\n";
});
}
Expand All @@ -96,12 +85,14 @@ void ClangClassTypePrinter::printClassTypeReturnScaffold(
void ClangClassTypePrinter::printParameterCxxtoCUseScaffold(
raw_ostream &os, const ClassDecl *type, const ModuleDecl *moduleContext,
llvm::function_ref<void(void)> bodyPrinter, bool isInOut) {
// FIXME: Handle isInOut
ClangSyntaxPrinter(os).printModuleNamespaceQualifiersIfNeeded(
type->getModuleContext(), moduleContext);
os << cxx_synthesis::getCxxImplNamespaceName() << "::";
ClangValueTypePrinter::printCxxImplClassName(os, type);
os << "::getOpaquePointer(";
if (isInOut)
os << '&';
os << "::swift::" << cxx_synthesis::getCxxImplNamespaceName()
<< "::_impl_RefCountedClass"
<< "::getOpaquePointer";
if (isInOut)
os << "Ref";
os << '(';
bodyPrinter();
os << ')';
}
5 changes: 3 additions & 2 deletions lib/PrintAsClang/PrintClangFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,13 @@ class CFunctionSignatureTypePrinter
void visitClassType(ClassType *CT, Optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
// FIXME: handle optionalKind.
// FIXME: handle isInOutParam.
if (languageMode != OutputLanguageMode::Cxx) {
os << "void * _Nonnull";
if (isInOutParam)
os << " * _Nonnull";
return;
}
if (typeUseKind == FunctionSignatureTypeUse::ParamType)
if (typeUseKind == FunctionSignatureTypeUse::ParamType && !isInOutParam)
os << "const ";
ClangSyntaxPrinter(os).printBaseName(CT->getDecl());
if (typeUseKind == FunctionSignatureTypeUse::ParamType)
Expand Down
35 changes: 35 additions & 0 deletions stdlib/public/SwiftShims/_SwiftCxxInteroperability.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,41 @@ inline void opaqueFree(void *_Nonnull p) noexcept {
#endif
}

/// Base class for a Swift reference counted class value.
class RefCountedClass {
public:
inline ~RefCountedClass() { swift_release(_opaquePointer); }
inline RefCountedClass(const RefCountedClass &other) noexcept
: _opaquePointer(other._opaquePointer) {
swift_retain(_opaquePointer);
}
inline RefCountedClass &operator=(const RefCountedClass &other) noexcept {
swift_retain(other._opaquePointer);
swift_release(_opaquePointer);
_opaquePointer = other._opaquePointer;
return *this;
}
// FIXME: What to do in 'move'?
inline RefCountedClass(RefCountedClass &&) noexcept = default;

protected:
inline RefCountedClass(void *_Nonnull ptr) noexcept : _opaquePointer(ptr) {}

private:
void *_Nonnull _opaquePointer;
friend class _impl_RefCountedClass;
};

class _impl_RefCountedClass {
public:
static inline void *_Nonnull getOpaquePointer(const RefCountedClass &object) {
return object._opaquePointer;
}
static inline void *_Nonnull &getOpaquePointerRef(RefCountedClass &object) {
return object._opaquePointer;
}
};

} // namespace _impl

/// Swift's Int type.
Expand Down
12 changes: 12 additions & 0 deletions test/Interop/SwiftToCxx/class/class-api-prohibited.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// RUN: %empty-directory(%t)

// RUN: %target-swift-frontend %S/swift-class-in-cxx.swift -typecheck -module-name Class -clang-header-expose-public-decls -emit-clang-header-path %t/class.h

// RUN: not %target-interop-build-clangxx -c %s -I %t -o %t/swift-class-execution.o

#include "class.h"

void test(void * _Nonnull p) {
// Prohibited to construct class reference directly from opaque pointer.
Class::ClassWithIntField x(p);
}
46 changes: 44 additions & 2 deletions test/Interop/SwiftToCxx/class/swift-class-execution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
extern "C" size_t swift_retainCount(void * _Nonnull obj);

size_t getRetainCount(const Class::ClassWithIntField & swiftClass) {
void *p = Class::_impl::_impl_ClassWithIntField::getOpaquePointer(swiftClass);
return swift_retainCount(p);
void *p = swift::_impl::_impl_RefCountedClass::getOpaquePointer(swiftClass);
return swift_retainCount(p);
}

int main() {
Expand Down Expand Up @@ -50,6 +50,48 @@ int main() {
// CHECK-NEXT: ClassWithIntField: 0;
// CHECK-NEXT: ClassWithIntField: 42;
// CHECK-NEXT: ClassWithIntField: 42;
// CHECK-NEXT: destroy ClassWithIntField

{
auto x = returnClassWithIntField();
assert(getRetainCount(x) == 1);
takeClassWithIntFieldInout(x);
assert(getRetainCount(x) == 1);
takeClassWithIntField(x);
}
// CHECK-NEXT: init ClassWithIntField
// CHECK-NEXT: init ClassWithIntField
// CHECK-NEXT: destroy ClassWithIntField
// CHECK-NEXT: ClassWithIntField: -11;
// CHECK-NEXT: destroy ClassWithIntField

{
auto x = returnClassWithIntField();
{
auto x2 = x;
assert(getRetainCount(x) == 2);
}
assert(getRetainCount(x) == 1);
}
// CHECK-NEXT: init ClassWithIntField
// CHECK-NEXT: destroy ClassWithIntField

{
auto x = returnClassWithIntField();
{
auto x2 = returnClassWithIntField();
assert(getRetainCount(x2) == 1);
assert(getRetainCount(x) == 1);
x = x2;
assert(getRetainCount(x) == 2);
}
takeClassWithIntField(x);
assert(getRetainCount(x) == 1);
}
// CHECK-NEXT: init ClassWithIntField
// CHECK-NEXT: init ClassWithIntField
// CHECK-NEXT: destroy ClassWithIntField
// CHECK-NEXT: ClassWithIntField: 0;
// CHECK-NEXT: destroy ClassWithIntField
return 0;
}
22 changes: 13 additions & 9 deletions test/Interop/SwiftToCxx/class/swift-class-in-cxx.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public final class ClassWithIntField {
// CHECK: SWIFT_EXTERN void * _Nonnull $s5Class011passThroughA12WithIntFieldyAA0adeF0CADF(void * _Nonnull x) SWIFT_NOEXCEPT SWIFT_CALL; // passThroughClassWithIntField(_:)
// CHECK-NEXT: SWIFT_EXTERN void * _Nonnull $s5Class06returnA12WithIntFieldAA0acdE0CyF(void) SWIFT_NOEXCEPT SWIFT_CALL; // returnClassWithIntField()
// CHECK-NEXT: SWIFT_EXTERN void $s5Class04takeA12WithIntFieldyyAA0acdE0CF(void * _Nonnull x) SWIFT_NOEXCEPT SWIFT_CALL; // takeClassWithIntField(_:)
// CHECK-NEXT: SWIFT_EXTERN void $s5Class04takeA17WithIntFieldInoutyyAA0acdE0CzF(void * _Nonnull * _Nonnull x) SWIFT_NOEXCEPT SWIFT_CALL; // takeClassWithIntFieldInout(_:)

// CHECK: namespace Class {

Expand All @@ -30,14 +31,13 @@ public final class ClassWithIntField {
// CHECK-EMPTY:
// CHECK-NEXT: } // namespace _impl
// CHECK-EMPTY:
// CHECK-NEXT: class ClassWithIntField final {
// CHECK-NEXT: class ClassWithIntField final : public swift::_impl::RefCountedClass {
// CHECK-NEXT: public:
// CHECK-NEXT: inline ~ClassWithIntField() { swift::_impl::swift_release(_opaquePointer); }
// CHECK-NEXT: inline ClassWithIntField(ClassWithIntField&&) noexcept = default;
// CHECK-NEXT: using RefCountedClass::RefCountedClass;
// CHECK-NEXT: using RefCountedClass::operator=;
// CHECK-NEXT: private:
// CHECK-NEXT: inline ClassWithIntField(void * _Nonnull ptr) noexcept : _opaquePointer(ptr) {}
// CHECK-NEXT: inline ClassWithIntField(void * _Nonnull ptr) noexcept : RefCountedClass(ptr) {}
// CHECK-EMPTY:
// CHECK-NEXT: void * _Nonnull _opaquePointer;
// CHECK-NEXT: friend class _impl::_impl_ClassWithIntField;
// CHECK-NEXT: };
// CHECK-EMPTY:
Expand All @@ -46,18 +46,17 @@ public final class ClassWithIntField {
// CHECK-NEXT:class _impl_ClassWithIntField {
// CHECK-NEXT:public:
// CHECK-NEXT:static inline ClassWithIntField makeRetained(void * _Nonnull ptr) noexcept { return ClassWithIntField(ptr); }
// CHECK-NEXT:static inline void * _Nonnull getOpaquePointer(const ClassWithIntField &object) noexcept { return object._opaquePointer; }
// CHECK-NEXT:};
// CHECK-EMPTY:
// CHECK-NEXT:} // namespace _impl

// CHECK: inline ClassWithIntField passThroughClassWithIntField(const ClassWithIntField& x) noexcept SWIFT_WARN_UNUSED_RESULT {
// CHECK-NEXT: return _impl::_impl_ClassWithIntField::makeRetained(_impl::$s5Class011passThroughA12WithIntFieldyAA0adeF0CADF(_impl::_impl_ClassWithIntField::getOpaquePointer(x)));
// CHECK-NEXT: return _impl::_impl_ClassWithIntField::makeRetained(_impl::$s5Class011passThroughA12WithIntFieldyAA0adeF0CADF(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(x)));
// CHECK-NEXT: }

public final class register { }

// CHECK: class register_ final {
// CHECK: class register_ final : public swift::_impl::RefCountedClass {

public func returnClassWithIntField() -> ClassWithIntField {
return ClassWithIntField()
Expand All @@ -73,10 +72,15 @@ public func takeClassWithIntField(_ x: ClassWithIntField) {
print("ClassWithIntField: \(x.field);")
}

public func takeClassWithIntFieldInout(_ x: inout ClassWithIntField) {
x = ClassWithIntField()
x.field = -11
}

// CHECK: inline ClassWithIntField returnClassWithIntField() noexcept SWIFT_WARN_UNUSED_RESULT {
// CHECK-NEXT: return _impl::_impl_ClassWithIntField::makeRetained(_impl::$s5Class06returnA12WithIntFieldAA0acdE0CyF());
// CHECK-NEXT: }

// CHECK: inline void takeClassWithIntField(const ClassWithIntField& x) noexcept {
// CHECK-NEXT: return _impl::$s5Class04takeA12WithIntFieldyyAA0acdE0CF(_impl::_impl_ClassWithIntField::getOpaquePointer(x));
// CHECK-NEXT: return _impl::$s5Class04takeA12WithIntFieldyyAA0acdE0CF(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(x));
// CHECK-NEXT: }