Skip to content

Commit 7384ee2

Browse files
authored
Merge pull request #60381 from hyp/eng/i/class-members-docs
[interop][SwiftToCxx] add support for members in class types
2 parents ff21023 + 372f7ee commit 7384ee2

11 files changed

+277
-16
lines changed

docs/CppInteroperability/CppInteroperabilityStatus.md

+11-3
Original file line numberDiff line numberDiff line change
@@ -154,17 +154,25 @@ This status table describes which of the following Swift language features have
154154
| Copy and destroy semantics | Yes |
155155
| Initializers | Partially, as static `init` methods. No failable support |
156156

157+
**Class types**
158+
159+
| **Swift Language Feature** | **Implemented Experimental Support For Using It In C++** |
160+
|--------------------------------|----------------------------------------------------------|
161+
| Class reference values | Yes |
162+
| ARC semantics | Yes (C++ copy constructor,assignment operator, destructor perform ARC operations) |
163+
| Initializers | No |
164+
157165
**Methods**
158166

159167
| **Swift Language Feature** | **Implemented Experimental Support For Using It In C++** |
160168
|--------------------------------|----------------------------------------------------------|
161-
| Instance methods | Yes, for structs only |
169+
| Instance methods | Yes, for structs and classes only |
162170
| Static methods | No |
163171

164172
**Properties**
165173

166174
| **Swift Language Feature** | **Implemented Experimental Support For Using It In C++** |
167175
|--------------------------------|----------------------------------------------------------|
168-
| Getter accessors | Yes, via `get<name>`. Boolean properties that start with `is` or `has` are remapped directly to a getter method using their original name. For structs only |
169-
| Setter accessors | Yes, via `set<name>`. For structs only |
176+
| Getter accessors | Yes, via `get<name>`. Boolean properties that start with `is` or `has` are remapped directly to a getter method using their original name. For structs and classes only |
177+
| Setter accessors | Yes, via `set<name>`. For structs and classes only |
170178
| Mutation accessors | No |

docs/CppInteroperability/UserGuide-CallingSwiftFromC++.md

+41-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ A Swift library author might want to expose their interface to C++, to allow a C
77
**NOTE:** This document does not go over the following Swift language features yet:
88

99
* Closures
10-
* inheritance
10+
* overriden methods/properties in classes
1111
* Existential types (any P)
1212
* Nested types
1313
* Operators
@@ -626,6 +626,46 @@ The Swift `Person` class instance that C++ variable `p` referenced gets dealloca
626626
at the end of `createAndUsePerson` as the two C++ values that referenced it
627627
inside of `createAndUsePerson` were destroyed.
628628
629+
### Class inheritance
630+
631+
A Swift class that inherits from another class is bridged to C++ with that inheritance
632+
relationship preserved in the C++ class hierarchy generated for these Swift classes. For example, given the following two Swift classes:
633+
634+
```swift
635+
// Swift module 'Transport'
636+
public class Vehicle {
637+
}
638+
public final class Bicycle: Vehicle {
639+
}
640+
```
641+
642+
Get a corresponding C++ class hierachy in C++:
643+
644+
```c++
645+
class Vehicle { ... };
646+
class Bicycle final : public Vehicle {};
647+
```
648+
649+
This allows C++ code to implicitly cast derived class instances to base class reference values, like in the example below:
650+
651+
```c++
652+
#include "Transport-Swift.h"
653+
654+
using namespace Transport;
655+
656+
void doSomethingWithVehicle(Transport::Vehicle vehicle) {
657+
...
658+
}
659+
660+
void useBicycle() {
661+
auto bike = Bicycle::init();
662+
doSomethingWithVehicle(bike);
663+
}
664+
```
665+
666+
Swift classes that are marked as `final` are also marked `final` in C++.
667+
Swift classes that are not marked as `final` should not be derived from in C++.
668+
629669
## Accessing Properties In C++
630670

631671
Swift allows structures and classes to define stored and computed properties. Stored properties store constant and variable values as part of an instance, whereas computed properties calculate (rather than store) a value. The stored and the computed properties from Swift types are bridged over as getter `get...` and setter `set...` methods in C++. Setter methods are not marked as `const` and should only be invoked on non `const` instances of the bridged types.

lib/PrintAsClang/DeclAndTypePrinter.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -300,9 +300,10 @@ class DeclAndTypePrinter::Implementation
300300
if (outputLang == OutputLanguageMode::Cxx) {
301301
// FIXME: Non objc class.
302302
// FIXME: Print availability.
303-
ClangClassTypePrinter(os).printClassTypeDecl(CD, []() {
304-
// FIXME: print class body.
305-
});
303+
ClangClassTypePrinter(os).printClassTypeDecl(
304+
CD, [&]() { printMembers(CD->getMembers()); });
305+
os << outOfLineDefinitions;
306+
outOfLineDefinitions.clear();
306307
return;
307308
}
308309

lib/PrintAsClang/PrintClangClassType.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ void ClangClassTypePrinter::printClassTypeDecl(
5959

6060
os << " using " << baseClassName << "::" << baseClassName << ";\n";
6161
os << " using " << baseClassName << "::operator=;\n";
62-
62+
bodyPrinter();
6363
os << "protected:\n";
6464
os << " inline ";
6565
printer.printBaseName(typeDecl);

lib/PrintAsClang/PrintClangFunction.cpp

+11-8
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,9 @@ bool isKnownCType(Type t, PrimitiveTypeMapping &typeMapping) {
7878
}
7979

8080
struct CFunctionSignatureTypePrinterModifierDelegate {
81-
/// Prefix the indirect value type param being printed in C mode.
81+
/// Prefix the indirect value type / class type param being printed in C mode.
8282
Optional<llvm::function_ref<void(raw_ostream &)>>
83-
prefixIndirectParamValueTypeInC = None;
83+
prefixIndirectlyPassedParamTypeInC = None;
8484
};
8585

8686
// Prints types in the C function signature that corresponds to the
@@ -152,6 +152,8 @@ class CFunctionSignatureTypePrinter
152152
bool isInOutParam) {
153153
// FIXME: handle optionalKind.
154154
if (languageMode != OutputLanguageMode::Cxx) {
155+
if (modifiersDelegate.prefixIndirectlyPassedParamTypeInC)
156+
(*modifiersDelegate.prefixIndirectlyPassedParamTypeInC)(os);
155157
os << "void * _Nonnull";
156158
if (isInOutParam)
157159
os << " * _Nonnull";
@@ -188,8 +190,8 @@ class CFunctionSignatureTypePrinter
188190
if (languageMode != OutputLanguageMode::Cxx &&
189191
(decl->isResilient() ||
190192
interopContext.getIrABIDetails().shouldPassIndirectly(NT))) {
191-
if (modifiersDelegate.prefixIndirectParamValueTypeInC)
192-
(*modifiersDelegate.prefixIndirectParamValueTypeInC)(os);
193+
if (modifiersDelegate.prefixIndirectlyPassedParamTypeInC)
194+
(*modifiersDelegate.prefixIndirectlyPassedParamTypeInC)(os);
193195
// FIXME: it would be nice to print out the C struct type here.
194196
if (isInOutParam) {
195197
os << "void * _Nonnull";
@@ -407,13 +409,13 @@ void DeclAndTypeClangFunctionPrinter::printFunctionSignature(
407409
interleaveComma(additionalParams, os, [&](const AdditionalParam &param) {
408410
if (param.role == AdditionalParam::Role::Self) {
409411
CFunctionSignatureTypePrinterModifierDelegate delegate;
410-
delegate.prefixIndirectParamValueTypeInC = [](raw_ostream &os) {
412+
delegate.prefixIndirectlyPassedParamTypeInC = [](raw_ostream &os) {
411413
os << "SWIFT_CONTEXT ";
412414
};
413415
if (FD->hasThrows())
414416
os << "SWIFT_CONTEXT ";
415417
if (param.isIndirect) {
416-
(*delegate.prefixIndirectParamValueTypeInC)(os);
418+
(*delegate.prefixIndirectlyPassedParamTypeInC)(os);
417419
os << "void * _Nonnull _self";
418420
} else {
419421
print(param.type, OptionalTypeKind::OTK_None, "_self",
@@ -649,7 +651,8 @@ void DeclAndTypeClangFunctionPrinter::printCxxMethod(
649651
modifiers.isInline = true;
650652
bool isMutating =
651653
isa<FuncDecl>(FD) ? cast<FuncDecl>(FD)->isMutating() : false;
652-
modifiers.isConst = !isMutating && !isConstructor;
654+
modifiers.isConst =
655+
!isa<ClassDecl>(typeDeclContext) && !isMutating && !isConstructor;
653656
printFunctionSignature(
654657
FD, isConstructor ? "init" : FD->getName().getBaseIdentifier().get(),
655658
resultTy, FunctionSignatureKind::CxxInlineThunk, {}, modifiers);
@@ -710,7 +713,7 @@ void DeclAndTypeClangFunctionPrinter::printCxxPropertyAccessorMethod(
710713
if (isDefinition)
711714
modifiers.qualifierContext = typeDeclContext;
712715
modifiers.isInline = true;
713-
modifiers.isConst = accessor->isGetter();
716+
modifiers.isConst = accessor->isGetter() && !isa<ClassDecl>(typeDeclContext);
714717
printFunctionSignature(accessor, remapPropertyName(accessor, resultTy),
715718
resultTy, FunctionSignatureKind::CxxInlineThunk, {},
716719
modifiers);

test/Interop/SwiftToCxx/methods/method-in-cxx-execution.cpp

+32
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,37 @@ int main() {
3131
auto largeStructAdded = largeStructDoubled.added(largeStruct);
3232
largeStructAdded.dump();
3333
// CHECK-NEXT: -3, 6, -300, 126, 201, -30303
34+
35+
{
36+
auto classValue = createClassWithMethods(42);
37+
classValue.dump();
38+
// CHECK-NEXT: ClassWithMethods 42;
39+
auto classValueRef = classValue.sameRet();
40+
auto classValueOther = classValue.deepCopy(2);
41+
classValue.mutate();
42+
classValue.dump();
43+
classValueRef.dump();
44+
classValueOther.dump();
45+
}
46+
// CHECK-NEXT: ClassWithMethods -42;
47+
// CHECK-NEXT: ClassWithMethods -42;
48+
// CHECK-NEXT: ClassWithMethods 44;
49+
// CHECK-NEXT: ClassWithMethods 44 deinit
50+
// CHECK-NEXT: ClassWithMethods -42 deinit
51+
52+
auto classWithStruct = createPassStructInClassMethod();
53+
{
54+
auto largeStruct = classWithStruct.retStruct(-11);
55+
largeStruct.dump();
56+
// CHECK-NEXT: PassStructInClassMethod.retStruct -11;
57+
// CHECK-NEXT: 1, 2, 3, 4, 5, 6
58+
}
59+
classWithStruct.updateStruct(-578, largeStruct);
60+
{
61+
auto largeStruct = classWithStruct.retStruct(2);
62+
largeStruct.dump();
63+
// CHECK-NEXT: PassStructInClassMethod.retStruct 2;
64+
// CHECK-NEXT: -578, 2, -100, 42, 67, -10101
65+
}
3466
return 0;
3567
}

test/Interop/SwiftToCxx/methods/method-in-cxx.swift

+85
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,78 @@ public struct LargeStruct {
2424
}
2525
}
2626

27+
public final class ClassWithMethods {
28+
var field: Int
29+
30+
init(_ x: Int) {
31+
field = x
32+
}
33+
34+
deinit {
35+
print("ClassWithMethods \(field) deinit")
36+
}
37+
38+
public func dump() {
39+
print("ClassWithMethods \(field);")
40+
}
41+
42+
public func sameRet() -> ClassWithMethods {
43+
return self
44+
}
45+
46+
public func mutate() {
47+
field = -field
48+
}
49+
50+
public func deepCopy(_ x: Int) -> ClassWithMethods {
51+
return ClassWithMethods(field + x)
52+
}
53+
}
54+
55+
public final class PassStructInClassMethod {
56+
var largeStruct: LargeStruct
57+
init() { largeStruct = LargeStruct(x1: 1, x2: 2, x3: 3, x4: 4, x5: 5, x6: 6) }
58+
59+
public func retStruct(_ x: Int) -> LargeStruct {
60+
print("PassStructInClassMethod.retStruct \(x);")
61+
return largeStruct
62+
}
63+
public func updateStruct(_ x: Int, _ y: LargeStruct) {
64+
largeStruct = LargeStruct(x1: x, x2: y.x2, x3: y.x3, x4: y.x4, x5: y.x5, x6: y.x6)
65+
}
66+
}
67+
68+
// CHECK: SWIFT_EXTERN void $s7Methods09ClassWithA0C4dumpyyF(SWIFT_CONTEXT void * _Nonnull _self) SWIFT_NOEXCEPT SWIFT_CALL; // dump()
69+
// CHECK: SWIFT_EXTERN void * _Nonnull $s7Methods09ClassWithA0C7sameRetACyF(SWIFT_CONTEXT void * _Nonnull _self) SWIFT_NOEXCEPT SWIFT_CALL; // sameRet()
70+
// CHECK: SWIFT_EXTERN void $s7Methods09ClassWithA0C6mutateyyF(SWIFT_CONTEXT void * _Nonnull _self) SWIFT_NOEXCEPT SWIFT_CALL; // mutate()
71+
// CHECK: SWIFT_EXTERN void * _Nonnull $s7Methods09ClassWithA0C8deepCopyyACSiF(ptrdiff_t x, SWIFT_CONTEXT void * _Nonnull _self) SWIFT_NOEXCEPT SWIFT_CALL; // deepCopy(_:)
2772
// CHECK: SWIFT_EXTERN void $s7Methods11LargeStructV7doubledACyF(SWIFT_INDIRECT_RESULT void * _Nonnull, SWIFT_CONTEXT const void * _Nonnull _self) SWIFT_NOEXCEPT SWIFT_CALL; // doubled()
2873
// CHECK: SWIFT_EXTERN void $s7Methods11LargeStructV4dumpyyF(SWIFT_CONTEXT const void * _Nonnull _self) SWIFT_NOEXCEPT SWIFT_CALL; // dump()
2974
// CHECK: SWIFT_EXTERN void $s7Methods11LargeStructV6scaledyACSi_SitF(SWIFT_INDIRECT_RESULT void * _Nonnull, ptrdiff_t x, ptrdiff_t y, SWIFT_CONTEXT const void * _Nonnull _self) SWIFT_NOEXCEPT SWIFT_CALL; // scaled(_:_:)
3075
// CHECK: SWIFT_EXTERN void $s7Methods11LargeStructV5addedyA2CF(SWIFT_INDIRECT_RESULT void * _Nonnull, const void * _Nonnull x, SWIFT_CONTEXT const void * _Nonnull _self) SWIFT_NOEXCEPT SWIFT_CALL; // added(_:)
76+
// CHECK: SWIFT_EXTERN void $s7Methods23PassStructInClassMethodC03retC0yAA05LargeC0VSiF(SWIFT_INDIRECT_RESULT void * _Nonnull, ptrdiff_t x, SWIFT_CONTEXT void * _Nonnull _self) SWIFT_NOEXCEPT SWIFT_CALL; // retStruct(_:)
77+
// CHECK: SWIFT_EXTERN void $s7Methods23PassStructInClassMethodC06updateC0yySi_AA05LargeC0VtF(ptrdiff_t x, const void * _Nonnull y, SWIFT_CONTEXT void * _Nonnull _self) SWIFT_NOEXCEPT SWIFT_CALL; // updateStruct(_:_:)
78+
79+
// CHECK: class ClassWithMethods final : public swift::_impl::RefCountedClass {
80+
// CHECK: using RefCountedClass::RefCountedClass;
81+
// CHECK-NEXT: using RefCountedClass::operator=;
82+
// CHECK-NEXT: inline void dump();
83+
// CHECK-NEXT: inline ClassWithMethods sameRet();
84+
// CHECK-NEXT: inline void mutate();
85+
// CHECK-NEXT: inline ClassWithMethods deepCopy(swift::Int x);
86+
87+
// CHECK: inline void ClassWithMethods::dump() {
88+
// CHECK-NEXT: return _impl::$s7Methods09ClassWithA0C4dumpyyF(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this));
89+
// CHECK-NEXT: }
90+
// CHECK-NEXT: inline ClassWithMethods ClassWithMethods::sameRet() {
91+
// CHECK-NEXT: return _impl::_impl_ClassWithMethods::makeRetained(_impl::$s7Methods09ClassWithA0C7sameRetACyF(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this)));
92+
// CHECK-NEXT: }
93+
// CHECK-NEXT: inline void ClassWithMethods::mutate() {
94+
// CHECK-NEXT: return _impl::$s7Methods09ClassWithA0C6mutateyyF(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this));
95+
// CHECK-NEXT: }
96+
// CHECK-NEXT: inline ClassWithMethods ClassWithMethods::deepCopy(swift::Int x) {
97+
// CHECK-NEXT: return _impl::_impl_ClassWithMethods::makeRetained(_impl::$s7Methods09ClassWithA0C8deepCopyyACSiF(x, ::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this)));
98+
// CHECK-NEXT: }
3199

32100
// CHECK: class LargeStruct final {
33101
// CHECK: inline LargeStruct(LargeStruct &&) = default;
@@ -56,6 +124,23 @@ public struct LargeStruct {
56124
// CHECK-NEXT: });
57125
// CHECK-NEXT: }
58126

127+
// CHECK: inline LargeStruct PassStructInClassMethod::retStruct(swift::Int x) {
128+
// CHECK-NEXT: return _impl::_impl_LargeStruct::returnNewValue([&](void * _Nonnull result) {
129+
// CHECK-NEXT: _impl::$s7Methods23PassStructInClassMethodC03retC0yAA05LargeC0VSiF(result, x, ::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this));
130+
// CHECK-NEXT: });
131+
// CHECK-NEXT: }
132+
// CHECK-NEXT: inline void PassStructInClassMethod::updateStruct(swift::Int x, const LargeStruct& y) {
133+
// CHECK-NEXT: return _impl::$s7Methods23PassStructInClassMethodC06updateC0yySi_AA05LargeC0VtF(x, _impl::_impl_LargeStruct::getOpaquePointer(y), ::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this));
134+
// CHECK-NEXT: }
135+
136+
public func createClassWithMethods(_ x: Int) -> ClassWithMethods {
137+
return ClassWithMethods(x)
138+
}
139+
59140
public func createLargeStruct() -> LargeStruct {
60141
return LargeStruct(x1: -1, x2: 2, x3: -100, x4: 42, x5: 67, x6: -10101)
61142
}
143+
144+
public func createPassStructInClassMethod() -> PassStructInClassMethod {
145+
return PassStructInClassMethod()
146+
}

test/Interop/SwiftToCxx/properties/getter-in-cxx-execution.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,11 @@ int main() {
5353
// CHECK-NEXT: create RefCountedClass 1
5454
// CHECK-NEXT: destroy RefCountedClass 1
5555
// CHECK-NEXT: destroy RefCountedClass 0
56+
57+
auto propsInClass = createPropsInClass(-1234);
58+
assert(propsInClass.getStoredInt() == -1234);
59+
assert(propsInClass.getComputedInt() == -1235);
60+
auto smallStructFromClass = propsInClass.getSmallStruct();
61+
assert(smallStructFromClass.getX() == 1234);
5662
return 0;
5763
}

test/Interop/SwiftToCxx/properties/getter-in-cxx.swift

+38
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,44 @@ public struct LargeStruct {
7272
// CHECK-NEXT: });
7373
// CHECK-NEXT: }
7474

75+
public final class PropertiesInClass {
76+
public let storedInt: Int32
77+
78+
init(_ x: Int32) {
79+
storedInt = x
80+
}
81+
82+
public var computedInt: Int {
83+
return Int(storedInt) - 1
84+
}
85+
86+
public var smallStruct: FirstSmallStruct {
87+
return FirstSmallStruct(x: UInt32(-storedInt));
88+
}
89+
}
90+
91+
// CHECK: class PropertiesInClass final : public swift::_impl::RefCountedClass {
92+
// CHECK: using RefCountedClass::operator=;
93+
// CHECK-NEXT: inline int32_t getStoredInt();
94+
// CHECK-NEXT: inline swift::Int getComputedInt();
95+
// CHECK-NEXT: inline FirstSmallStruct getSmallStruct();
96+
97+
// CHECK: inline int32_t PropertiesInClass::getStoredInt() {
98+
// CHECK-NEXT: return _impl::$s10Properties0A7InClassC9storedInts5Int32Vvg(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this));
99+
// CHECK-NEXT: }
100+
// CHECK-NEXT: inline swift::Int PropertiesInClass::getComputedInt() {
101+
// CHECK-NEXT: return _impl::$s10Properties0A7InClassC11computedIntSivg(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this));
102+
// CHECK-NEXT: }
103+
// CHECK-NEXT: inline FirstSmallStruct PropertiesInClass::getSmallStruct() {
104+
// CHECK-NEXT: return _impl::_impl_FirstSmallStruct::returnNewValue([&](char * _Nonnull result) {
105+
// CHECK-NEXT: _impl::swift_interop_returnDirect_Properties_FirstSmallStruct(result, _impl::$s10Properties0A7InClassC11smallStructAA010FirstSmallE0Vvg(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this)));
106+
// CHECK-NEXT: });
107+
// CHECK-NEXT: }
108+
109+
public func createPropsInClass(_ x: Int32) -> PropertiesInClass {
110+
return PropertiesInClass(x)
111+
}
112+
75113
public struct SmallStructWithGetters {
76114
public let storedInt: UInt32
77115
public var computedInt: Int {

test/Interop/SwiftToCxx/properties/setter-in-cxx-execution.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,13 @@ int main() {
4747

4848
smallStructWithProps.setLargeStructWithProps(largeStructWithProps);
4949
// CHECK-NEXT: SET: LargeStruct(x1: 0, x2: 2, x3: -72, x4: 3, x5: 4, x6: 5), FirstSmallStruct(x: 999)
50+
51+
auto propsInClass = createPropsInClass(-1234);
52+
assert(propsInClass.getStoredInt() == -1234);
53+
propsInClass.setStoredInt(45);
54+
assert(propsInClass.getStoredInt() == 45);
55+
propsInClass.setComputedInt(-11);
56+
assert(propsInClass.getComputedInt() == -11);
57+
assert(propsInClass.getStoredInt() == -13);
5058
return 0;
5159
}

0 commit comments

Comments
 (0)