Skip to content

[interop][SwiftToCxx] support instance property setters in C++ #60098

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
Jul 19, 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 @@ -1372,6 +1372,8 @@ class DeclAndTypePrinter::Implementation
return;
auto *getter = VD->getOpaqueAccessor(AccessorKind::Get);
printAbstractFunctionAsMethod(getter, /*isStatic=*/false);
if (auto *setter = VD->getOpaqueAccessor(AccessorKind::Set))
printAbstractFunctionAsMethod(setter, /*isStatic=*/false);
return;
}

Expand Down
41 changes: 17 additions & 24 deletions lib/PrintAsClang/PrintClangFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -480,34 +480,27 @@ void DeclAndTypeClangFunctionPrinter::printCxxMethod(
void DeclAndTypeClangFunctionPrinter::printCxxPropertyAccessorMethod(
const NominalTypeDecl *typeDeclContext, const AccessorDecl *accessor,
StringRef swiftSymbolName, Type resultTy, bool isDefinition) {
assert(accessor->getParameters()->size() == 0);
assert(accessor->isSetter() || accessor->getParameters()->size() == 0);
os << " inline ";

OptionalTypeKind retKind;
Type objTy;
std::tie(objTy, retKind) =
DeclAndTypePrinter::getObjectTypeAndOptionality(accessor, resultTy);
CFunctionSignatureTypePrinter typePrinter(
os, cPrologueOS, typeMapping, OutputLanguageMode::Cxx, interopContext,
CFunctionSignatureTypePrinterModifierDelegate(),
accessor->getModuleContext(), FunctionSignatureTypeUse::ReturnType);
typePrinter.visit(objTy, retKind, /*isInOut=*/false);

ClangSyntaxPrinter printer(os);
os << ' ';
if (isDefinition) {
// FIXME: Full qualifiers for nested types?
printer.printBaseName(typeDeclContext);
os << "::";
}

StringRef name;
StringRef propertyName;
// For a getter or setter, go through the variable or subscript decl.
name = accessor->getStorage()->getBaseIdentifier().str();
propertyName = accessor->getStorage()->getBaseIdentifier().str();

std::string name;
llvm::raw_string_ostream nameOS(name);
// FIXME: some names are remapped differently. (e.g. isX).
os << "get" << char(std::toupper(name[0])) << name.drop_front();
os << "() const";
nameOS << (accessor->isSetter() ? "set" : "get")
<< char(std::toupper(propertyName[0])) << propertyName.drop_front();

FunctionSignatureModifiers modifiers;
if (isDefinition)
modifiers.qualifierContext = typeDeclContext;
printFunctionSignature(accessor, nameOS.str(), resultTy,
FunctionSignatureKind::CxxInlineThunk, {}, modifiers);
if (accessor->isGetter()) {
os << " const";
}
if (!isDefinition) {
os << ";\n";
return;
Expand All @@ -518,7 +511,7 @@ void DeclAndTypeClangFunctionPrinter::printCxxPropertyAccessorMethod(
accessor->getParameters(),
{AdditionalParam{AdditionalParam::Role::Self,
typeDeclContext->getDeclaredType(),
/*isIndirect=*/false}});
/*isIndirect=*/accessor->isSetter()}});
os << " }\n";
}

Expand Down
51 changes: 51 additions & 0 deletions test/Interop/SwiftToCxx/properties/setter-in-cxx-execution.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// RUN: %empty-directory(%t)

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

// RUN: %target-interop-build-clangxx -c %s -I %t -o %t/swift-props-execution.o
// RUN: %target-interop-build-swift %S/setter-in-cxx.swift -o %t/swift-props-execution -Xlinker %t/swift-props-execution.o -module-name Properties -Xfrontend -entry-point-function-name -Xfrontend swiftMain

// RUN: %target-codesign %t/swift-props-execution
// RUN: %target-run %t/swift-props-execution | %FileCheck %s

// REQUIRES: executable_test

#include <assert.h>
#include "properties.h"

int main() {
using namespace Properties;

auto smallStructWithProps = createSmallStructWithProps();
smallStructWithProps.setStoredInt(12);
assert(smallStructWithProps.getStoredInt() == 12);
assert(smallStructWithProps.getComputedInt() == 14);
smallStructWithProps.setComputedInt(45);
assert(smallStructWithProps.getStoredInt() == 43);
assert(smallStructWithProps.getComputedInt() == 45);

auto largeStructWithProps = smallStructWithProps.getLargeStructWithProps();
assert(largeStructWithProps.getStoredSmallStruct().getX() == 0xFAE);
largeStructWithProps.setStoredSmallStruct(createFirstSmallStruct(999));
assert(largeStructWithProps.getStoredSmallStruct().getX() == 999);

auto firstSmallStruct = largeStructWithProps.getStoredSmallStruct();
assert(firstSmallStruct.getX() == 999);
firstSmallStruct.setX(42);
assert(firstSmallStruct.getX() == 42);

largeStructWithProps.setStoredLargeStruct(largeStructWithProps.getStoredLargeStruct());

smallStructWithProps.setLargeStructWithProps(largeStructWithProps);
// CHECK: SET: LargeStruct(x1: 90, x2: 1, x3: 2, x4: 3, x5: 4, x6: 5), FirstSmallStruct(x: 999)

auto largeStruct = largeStructWithProps.getStoredLargeStruct();
largeStruct.setX1(0);
largeStruct.setX2(largeStruct.getX2() * 2);
largeStruct.setX3(-72);
largeStructWithProps.setStoredLargeStruct(largeStruct);

smallStructWithProps.setLargeStructWithProps(largeStructWithProps);
// CHECK-NEXT: SET: LargeStruct(x1: 0, x2: 2, x3: -72, x4: 3, x5: 4, x6: 5), FirstSmallStruct(x: 999)
return 0;
}
140 changes: 140 additions & 0 deletions test/Interop/SwiftToCxx/properties/setter-in-cxx.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -typecheck -module-name Properties -clang-header-expose-public-decls -emit-clang-header-path %t/properties.h
// RUN: %FileCheck %s < %t/properties.h

// RUN: %check-interop-cxx-header-in-clang(%t/properties.h)

public struct FirstSmallStruct {
public var x: UInt32
}

// CHECK: class FirstSmallStruct final {
// CHECK: public:
// CHECK: inline FirstSmallStruct(FirstSmallStruct &&) = default;
// CHECK-NEXT: inline uint32_t getX() const;
// CHECK-NEXT: inline void setX(uint32_t value);
// CHECK-NEXT: private:

// CHECK: inline uint32_t FirstSmallStruct::getX() const {
// CHECK-NEXT: return _impl::$s10Properties16FirstSmallStructV1xs6UInt32Vvg(_impl::swift_interop_passDirect_Properties_FirstSmallStruct(_getOpaquePointer()));
// CHECK-NEXT: }
// CHECK-NEXT: inline void FirstSmallStruct::setX(uint32_t value) {
// CHECK-NEXT: return _impl::$s10Properties16FirstSmallStructV1xs6UInt32Vvs(value, _getOpaquePointer());
// CHECK-NEXT: }

public struct LargeStruct {
public var x1, x2, x3, x4, x5, x6: Int
}

// CHECK: class LargeStruct final {
// CHECK: public:
// CHECK: inline LargeStruct(LargeStruct &&) = default;
// CHECK-NEXT: inline swift::Int getX1() const;
// CHECK-NEXT: inline void setX1(swift::Int value);
// CHECK-NEXT: inline swift::Int getX2() const;
// CHECK-NEXT: inline void setX2(swift::Int value);
// CHECK-NEXT: inline swift::Int getX3() const;
// CHECK-NEXT: inline void setX3(swift::Int value);
// CHECK-NEXT: inline swift::Int getX4() const;
// CHECK-NEXT: inline void setX4(swift::Int value);
// CHECK-NEXT: inline swift::Int getX5() const;
// CHECK-NEXT: inline void setX5(swift::Int value);
// CHECK-NEXT: inline swift::Int getX6() const;
// CHECK-NEXT: inline void setX6(swift::Int value);
// CHECK-NEXT: private:

// CHECK: inline swift::Int LargeStruct::getX1() const {
// CHECK-NEXT: return _impl::$s10Properties11LargeStructV2x1Sivg(_getOpaquePointer());
// CHECK-NEXT: }
// CHECK-NEXT: inline void LargeStruct::setX1(swift::Int value) {
// CHECK-NEXT: return _impl::$s10Properties11LargeStructV2x1Sivs(value, _getOpaquePointer());
// CHECK-NEXT: }

public struct LargeStructWithProps {
public var storedLargeStruct: LargeStruct
public var storedSmallStruct: FirstSmallStruct
}

// CHECK: class LargeStructWithProps final {
// CHECK-NEXT: public:
// CHECK: inline LargeStruct getStoredLargeStruct() const;
// CHECK-NEXT: inline void setStoredLargeStruct(const LargeStruct& value);
// CHECK-NEXT: inline FirstSmallStruct getStoredSmallStruct() const;
// CHECK-NEXT: inline void setStoredSmallStruct(const FirstSmallStruct& value);

// CHECK: inline LargeStruct LargeStructWithProps::getStoredLargeStruct() const {
// CHECK-NEXT: return _impl::_impl_LargeStruct::returnNewValue([&](void * _Nonnull result) {
// CHECK-NEXT: _impl::$s10Properties20LargeStructWithPropsV06storedbC0AA0bC0Vvg(result, _getOpaquePointer());
// CHECK-NEXT: });
// CHECK-NEXT: }
// CHECK-NEXT: inline void LargeStructWithProps::setStoredLargeStruct(const LargeStruct& value) {
// CHECK-NEXT: return _impl::$s10Properties20LargeStructWithPropsV06storedbC0AA0bC0Vvs(_impl::_impl_LargeStruct::getOpaquePointer(value), _getOpaquePointer());
// CHECK-NEXT: }
// CHECK-NEXT: inline FirstSmallStruct LargeStructWithProps::getStoredSmallStruct() const {
// CHECK-NEXT: return _impl::_impl_FirstSmallStruct::returnNewValue([&](char * _Nonnull result) {
// CHECK-NEXT: _impl::swift_interop_returnDirect_Properties_FirstSmallStruct(result, _impl::$s10Properties20LargeStructWithPropsV011storedSmallC0AA05FirstgC0Vvg(_getOpaquePointer()));
// CHECK-NEXT: });
// CHECK-NEXT: }
// CHECK-NEXT: inline void LargeStructWithProps::setStoredSmallStruct(const FirstSmallStruct& value) {
// CHECK-NEXT: return _impl::$s10Properties20LargeStructWithPropsV011storedSmallC0AA05FirstgC0Vvs(_impl::swift_interop_passDirect_Properties_FirstSmallStruct(_impl::_impl_FirstSmallStruct::getOpaquePointer(value)), _getOpaquePointer());
// CHECK-NEXT: }

public struct SmallStructWithProps {
public var storedInt: UInt32
public var computedInt: Int {
get {
return Int(storedInt) + 2
} set {
storedInt = UInt32(newValue - 2)
}
}

public var largeStructWithProps: LargeStructWithProps {
get {
return LargeStructWithProps(storedLargeStruct: LargeStruct(x1: computedInt * 2, x2: 1, x3: 2, x4: 3, x5: 4, x6: 5),
storedSmallStruct:FirstSmallStruct(x: 0xFAE))
} set {
print("SET: \(newValue.storedLargeStruct), \(newValue.storedSmallStruct)")
}
}
}

// CHECK: class SmallStructWithProps final {
// CHECK: public:
// CHECK: inline SmallStructWithProps(SmallStructWithProps &&) = default;
// CHECK-NEXT: inline uint32_t getStoredInt() const;
// CHECK-NEXT: inline void setStoredInt(uint32_t value);
// CHECK-NEXT: inline swift::Int getComputedInt() const;
// CHECK-NEXT: inline void setComputedInt(swift::Int newValue);
// CHECK-NEXT: inline LargeStructWithProps getLargeStructWithProps() const;
// CHECK-NEXT: inline void setLargeStructWithProps(const LargeStructWithProps& newValue);
// CHECK-NEXT: private:

// CHECK: inline uint32_t SmallStructWithProps::getStoredInt() const {
// CHECK-NEXT: return _impl::$s10Properties20SmallStructWithPropsV9storedInts6UInt32Vvg(_impl::swift_interop_passDirect_Properties_SmallStructWithProps(_getOpaquePointer()));
// CHECK-NEXT: }
// CHECK-NEXT: inline void SmallStructWithProps::setStoredInt(uint32_t value) {
// CHECK-NEXT: return _impl::$s10Properties20SmallStructWithPropsV9storedInts6UInt32Vvs(value, _getOpaquePointer());
// CHECK-NEXT: }
// CHECK-NEXT: inline swift::Int SmallStructWithProps::getComputedInt() const {
// CHECK-NEXT: return _impl::$s10Properties20SmallStructWithPropsV11computedIntSivg(_impl::swift_interop_passDirect_Properties_SmallStructWithProps(_getOpaquePointer()));
// CHECK-NEXT: }
// CHECK-NEXT: inline void SmallStructWithProps::setComputedInt(swift::Int newValue) {
// CHECK-NEXT: return _impl::$s10Properties20SmallStructWithPropsV11computedIntSivs(newValue, _getOpaquePointer());
// CHECK-NEXT: }
// CHECK-NEXT: inline LargeStructWithProps SmallStructWithProps::getLargeStructWithProps() const {
// CHECK-NEXT: return _impl::_impl_LargeStructWithProps::returnNewValue([&](void * _Nonnull result) {
// CHECK-NEXT: _impl::$s10Properties20SmallStructWithPropsV05largecdE0AA05LargecdE0Vvg(result, _impl::swift_interop_passDirect_Properties_SmallStructWithProps(_getOpaquePointer()));
// CHECK-NEXT: });
// CHECK-NEXT: }
// CHECK-NEXT: inline void SmallStructWithProps::setLargeStructWithProps(const LargeStructWithProps& newValue) {
// CHECK-NEXT: return _impl::$s10Properties20SmallStructWithPropsV05largecdE0AA05LargecdE0Vvs(_impl::_impl_LargeStructWithProps::getOpaquePointer(newValue), _getOpaquePointer());
// CHECK-NEXT: }

public func createSmallStructWithProps() -> SmallStructWithProps {
return SmallStructWithProps(storedInt: 21)
}

public func createFirstSmallStruct(_ x: UInt32) -> FirstSmallStruct {
return FirstSmallStruct(x: x)
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ public struct FirstSmallStruct {
// CHECK: inline uint32_t FirstSmallStruct::getX() const {
// CHECK-NEXT: return _impl::$s7Structs16FirstSmallStructV1xs6UInt32Vvg(_getOpaquePointer());
// CHECK-NEXT: }
// CHECK: inline void FirstSmallStruct::setX(uint32_t value) {
// CHECK-NEXT: return _impl::$s7Structs16FirstSmallStructV1xs6UInt32Vvs(value, _getOpaquePointer());
// CHECK-NEXT: }
// CHECK-NEXT: inline void FirstSmallStruct::dump() const {
// CHECK-NEXT: return _impl::$s7Structs16FirstSmallStructV4dumpyyF(_getOpaquePointer());
// CHECK-NEXT: }
Expand Down