Skip to content
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
14 changes: 11 additions & 3 deletions docs/CppInteroperability/CppInteroperabilityStatus.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,17 +154,25 @@ This status table describes which of the following Swift language features have
| Copy and destroy semantics | Yes |
| Initializers | Partially, as static `init` methods. No failable support |

**Class types**

| **Swift Language Feature** | **Implemented Experimental Support For Using It In C++** |
|--------------------------------|----------------------------------------------------------|
| Class reference values | Yes |
| ARC semantics | Yes (C++ copy constructor,assignment operator, destructor perform ARC operations) |
| Initializers | No |

**Methods**

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

**Properties**

| **Swift Language Feature** | **Implemented Experimental Support For Using It In C++** |
|--------------------------------|----------------------------------------------------------|
| 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 |
| Setter accessors | Yes, via `set<name>`. For structs only |
| 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 |
| Setter accessors | Yes, via `set<name>`. For structs and classes only |
| Mutation accessors | No |
42 changes: 41 additions & 1 deletion docs/CppInteroperability/UserGuide-CallingSwiftFromC++.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ A Swift library author might want to expose their interface to C++, to allow a C
**NOTE:** This document does not go over the following Swift language features yet:

* Closures
* inheritance
* overriden methods/properties in classes
* Existential types (any P)
* Nested types
* Operators
Expand Down Expand Up @@ -626,6 +626,46 @@ The Swift `Person` class instance that C++ variable `p` referenced gets dealloca
at the end of `createAndUsePerson` as the two C++ values that referenced it
inside of `createAndUsePerson` were destroyed.

### Class inheritance

A Swift class that inherits from another class is bridged to C++ with that inheritance
relationship preserved in the C++ class hierarchy generated for these Swift classes. For example, given the following two Swift classes:

```swift
// Swift module 'Transport'
public class Vehicle {
}
public final class Bicycle: Vehicle {
}
```

Get a corresponding C++ class hierachy in C++:

```c++
class Vehicle { ... };
class Bicycle final : public Vehicle {};
```

This allows C++ code to implicitly cast derived class instances to base class reference values, like in the example below:

```c++
#include "Transport-Swift.h"

using namespace Transport;

void doSomethingWithVehicle(Transport::Vehicle vehicle) {
...
}

void useBicycle() {
auto bike = Bicycle::init();
doSomethingWithVehicle(bike);
}
```

Swift classes that are marked as `final` are also marked `final` in C++.
Swift classes that are not marked as `final` should not be derived from in C++.

## Accessing Properties In C++

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.
Expand Down
7 changes: 4 additions & 3 deletions lib/PrintAsClang/DeclAndTypePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,9 +300,10 @@ class DeclAndTypePrinter::Implementation
if (outputLang == OutputLanguageMode::Cxx) {
// FIXME: Non objc class.
// FIXME: Print availability.
ClangClassTypePrinter(os).printClassTypeDecl(CD, []() {
// FIXME: print class body.
});
ClangClassTypePrinter(os).printClassTypeDecl(
CD, [&]() { printMembers(CD->getMembers()); });
os << outOfLineDefinitions;
outOfLineDefinitions.clear();
return;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/PrintAsClang/PrintClangClassType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ void ClangClassTypePrinter::printClassTypeDecl(

os << " using " << baseClassName << "::" << baseClassName << ";\n";
os << " using " << baseClassName << "::operator=;\n";

bodyPrinter();
os << "protected:\n";
os << " inline ";
printer.printBaseName(typeDecl);
Expand Down
19 changes: 11 additions & 8 deletions lib/PrintAsClang/PrintClangFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ bool isKnownCType(Type t, PrimitiveTypeMapping &typeMapping) {
}

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

// Prints types in the C function signature that corresponds to the
Expand Down Expand Up @@ -152,6 +152,8 @@ class CFunctionSignatureTypePrinter
bool isInOutParam) {
// FIXME: handle optionalKind.
if (languageMode != OutputLanguageMode::Cxx) {
if (modifiersDelegate.prefixIndirectlyPassedParamTypeInC)
(*modifiersDelegate.prefixIndirectlyPassedParamTypeInC)(os);
os << "void * _Nonnull";
if (isInOutParam)
os << " * _Nonnull";
Expand Down Expand Up @@ -188,8 +190,8 @@ class CFunctionSignatureTypePrinter
if (languageMode != OutputLanguageMode::Cxx &&
(decl->isResilient() ||
interopContext.getIrABIDetails().shouldPassIndirectly(NT))) {
if (modifiersDelegate.prefixIndirectParamValueTypeInC)
(*modifiersDelegate.prefixIndirectParamValueTypeInC)(os);
if (modifiersDelegate.prefixIndirectlyPassedParamTypeInC)
(*modifiersDelegate.prefixIndirectlyPassedParamTypeInC)(os);
// FIXME: it would be nice to print out the C struct type here.
if (isInOutParam) {
os << "void * _Nonnull";
Expand Down Expand Up @@ -407,13 +409,13 @@ void DeclAndTypeClangFunctionPrinter::printFunctionSignature(
interleaveComma(additionalParams, os, [&](const AdditionalParam &param) {
if (param.role == AdditionalParam::Role::Self) {
CFunctionSignatureTypePrinterModifierDelegate delegate;
delegate.prefixIndirectParamValueTypeInC = [](raw_ostream &os) {
delegate.prefixIndirectlyPassedParamTypeInC = [](raw_ostream &os) {
os << "SWIFT_CONTEXT ";
};
if (FD->hasThrows())
os << "SWIFT_CONTEXT ";
if (param.isIndirect) {
(*delegate.prefixIndirectParamValueTypeInC)(os);
(*delegate.prefixIndirectlyPassedParamTypeInC)(os);
os << "void * _Nonnull _self";
} else {
print(param.type, OptionalTypeKind::OTK_None, "_self",
Expand Down Expand Up @@ -649,7 +651,8 @@ void DeclAndTypeClangFunctionPrinter::printCxxMethod(
modifiers.isInline = true;
bool isMutating =
isa<FuncDecl>(FD) ? cast<FuncDecl>(FD)->isMutating() : false;
modifiers.isConst = !isMutating && !isConstructor;
modifiers.isConst =
!isa<ClassDecl>(typeDeclContext) && !isMutating && !isConstructor;
printFunctionSignature(
FD, isConstructor ? "init" : FD->getName().getBaseIdentifier().get(),
resultTy, FunctionSignatureKind::CxxInlineThunk, {}, modifiers);
Expand Down Expand Up @@ -710,7 +713,7 @@ void DeclAndTypeClangFunctionPrinter::printCxxPropertyAccessorMethod(
if (isDefinition)
modifiers.qualifierContext = typeDeclContext;
modifiers.isInline = true;
modifiers.isConst = accessor->isGetter();
modifiers.isConst = accessor->isGetter() && !isa<ClassDecl>(typeDeclContext);
printFunctionSignature(accessor, remapPropertyName(accessor, resultTy),
resultTy, FunctionSignatureKind::CxxInlineThunk, {},
modifiers);
Expand Down
32 changes: 32 additions & 0 deletions test/Interop/SwiftToCxx/methods/method-in-cxx-execution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,37 @@ int main() {
auto largeStructAdded = largeStructDoubled.added(largeStruct);
largeStructAdded.dump();
// CHECK-NEXT: -3, 6, -300, 126, 201, -30303

{
auto classValue = createClassWithMethods(42);
classValue.dump();
// CHECK-NEXT: ClassWithMethods 42;
auto classValueRef = classValue.sameRet();
auto classValueOther = classValue.deepCopy(2);
classValue.mutate();
classValue.dump();
classValueRef.dump();
classValueOther.dump();
}
// CHECK-NEXT: ClassWithMethods -42;
// CHECK-NEXT: ClassWithMethods -42;
// CHECK-NEXT: ClassWithMethods 44;
// CHECK-NEXT: ClassWithMethods 44 deinit
// CHECK-NEXT: ClassWithMethods -42 deinit

auto classWithStruct = createPassStructInClassMethod();
{
auto largeStruct = classWithStruct.retStruct(-11);
largeStruct.dump();
// CHECK-NEXT: PassStructInClassMethod.retStruct -11;
// CHECK-NEXT: 1, 2, 3, 4, 5, 6
}
classWithStruct.updateStruct(-578, largeStruct);
{
auto largeStruct = classWithStruct.retStruct(2);
largeStruct.dump();
// CHECK-NEXT: PassStructInClassMethod.retStruct 2;
// CHECK-NEXT: -578, 2, -100, 42, 67, -10101
}
return 0;
}
85 changes: 85 additions & 0 deletions test/Interop/SwiftToCxx/methods/method-in-cxx.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,78 @@ public struct LargeStruct {
}
}

public final class ClassWithMethods {
var field: Int

init(_ x: Int) {
field = x
}

deinit {
print("ClassWithMethods \(field) deinit")
}

public func dump() {
print("ClassWithMethods \(field);")
}

public func sameRet() -> ClassWithMethods {
return self
}

public func mutate() {
field = -field
}

public func deepCopy(_ x: Int) -> ClassWithMethods {
return ClassWithMethods(field + x)
}
}

public final class PassStructInClassMethod {
var largeStruct: LargeStruct
init() { largeStruct = LargeStruct(x1: 1, x2: 2, x3: 3, x4: 4, x5: 5, x6: 6) }

public func retStruct(_ x: Int) -> LargeStruct {
print("PassStructInClassMethod.retStruct \(x);")
return largeStruct
}
public func updateStruct(_ x: Int, _ y: LargeStruct) {
largeStruct = LargeStruct(x1: x, x2: y.x2, x3: y.x3, x4: y.x4, x5: y.x5, x6: y.x6)
}
}

// CHECK: SWIFT_EXTERN void $s7Methods09ClassWithA0C4dumpyyF(SWIFT_CONTEXT void * _Nonnull _self) SWIFT_NOEXCEPT SWIFT_CALL; // dump()
// CHECK: SWIFT_EXTERN void * _Nonnull $s7Methods09ClassWithA0C7sameRetACyF(SWIFT_CONTEXT void * _Nonnull _self) SWIFT_NOEXCEPT SWIFT_CALL; // sameRet()
// CHECK: SWIFT_EXTERN void $s7Methods09ClassWithA0C6mutateyyF(SWIFT_CONTEXT void * _Nonnull _self) SWIFT_NOEXCEPT SWIFT_CALL; // mutate()
// CHECK: SWIFT_EXTERN void * _Nonnull $s7Methods09ClassWithA0C8deepCopyyACSiF(ptrdiff_t x, SWIFT_CONTEXT void * _Nonnull _self) SWIFT_NOEXCEPT SWIFT_CALL; // deepCopy(_:)
// CHECK: SWIFT_EXTERN void $s7Methods11LargeStructV7doubledACyF(SWIFT_INDIRECT_RESULT void * _Nonnull, SWIFT_CONTEXT const void * _Nonnull _self) SWIFT_NOEXCEPT SWIFT_CALL; // doubled()
// CHECK: SWIFT_EXTERN void $s7Methods11LargeStructV4dumpyyF(SWIFT_CONTEXT const void * _Nonnull _self) SWIFT_NOEXCEPT SWIFT_CALL; // dump()
// 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(_:_:)
// 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(_:)
// CHECK: SWIFT_EXTERN void $s7Methods23PassStructInClassMethodC03retC0yAA05LargeC0VSiF(SWIFT_INDIRECT_RESULT void * _Nonnull, ptrdiff_t x, SWIFT_CONTEXT void * _Nonnull _self) SWIFT_NOEXCEPT SWIFT_CALL; // retStruct(_:)
// CHECK: SWIFT_EXTERN void $s7Methods23PassStructInClassMethodC06updateC0yySi_AA05LargeC0VtF(ptrdiff_t x, const void * _Nonnull y, SWIFT_CONTEXT void * _Nonnull _self) SWIFT_NOEXCEPT SWIFT_CALL; // updateStruct(_:_:)

// CHECK: class ClassWithMethods final : public swift::_impl::RefCountedClass {
// CHECK: using RefCountedClass::RefCountedClass;
// CHECK-NEXT: using RefCountedClass::operator=;
// CHECK-NEXT: inline void dump();
// CHECK-NEXT: inline ClassWithMethods sameRet();
// CHECK-NEXT: inline void mutate();
// CHECK-NEXT: inline ClassWithMethods deepCopy(swift::Int x);

// CHECK: inline void ClassWithMethods::dump() {
// CHECK-NEXT: return _impl::$s7Methods09ClassWithA0C4dumpyyF(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this));
// CHECK-NEXT: }
// CHECK-NEXT: inline ClassWithMethods ClassWithMethods::sameRet() {
// CHECK-NEXT: return _impl::_impl_ClassWithMethods::makeRetained(_impl::$s7Methods09ClassWithA0C7sameRetACyF(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this)));
// CHECK-NEXT: }
// CHECK-NEXT: inline void ClassWithMethods::mutate() {
// CHECK-NEXT: return _impl::$s7Methods09ClassWithA0C6mutateyyF(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this));
// CHECK-NEXT: }
// CHECK-NEXT: inline ClassWithMethods ClassWithMethods::deepCopy(swift::Int x) {
// CHECK-NEXT: return _impl::_impl_ClassWithMethods::makeRetained(_impl::$s7Methods09ClassWithA0C8deepCopyyACSiF(x, ::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this)));
// CHECK-NEXT: }

// CHECK: class LargeStruct final {
// CHECK: inline LargeStruct(LargeStruct &&) = default;
Expand Down Expand Up @@ -56,6 +124,23 @@ public struct LargeStruct {
// CHECK-NEXT: });
// CHECK-NEXT: }

// CHECK: inline LargeStruct PassStructInClassMethod::retStruct(swift::Int x) {
// CHECK-NEXT: return _impl::_impl_LargeStruct::returnNewValue([&](void * _Nonnull result) {
// CHECK-NEXT: _impl::$s7Methods23PassStructInClassMethodC03retC0yAA05LargeC0VSiF(result, x, ::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this));
// CHECK-NEXT: });
// CHECK-NEXT: }
// CHECK-NEXT: inline void PassStructInClassMethod::updateStruct(swift::Int x, const LargeStruct& y) {
// CHECK-NEXT: return _impl::$s7Methods23PassStructInClassMethodC06updateC0yySi_AA05LargeC0VtF(x, _impl::_impl_LargeStruct::getOpaquePointer(y), ::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this));
// CHECK-NEXT: }

public func createClassWithMethods(_ x: Int) -> ClassWithMethods {
return ClassWithMethods(x)
}

public func createLargeStruct() -> LargeStruct {
return LargeStruct(x1: -1, x2: 2, x3: -100, x4: 42, x5: 67, x6: -10101)
}

public func createPassStructInClassMethod() -> PassStructInClassMethod {
return PassStructInClassMethod()
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,11 @@ int main() {
// CHECK-NEXT: create RefCountedClass 1
// CHECK-NEXT: destroy RefCountedClass 1
// CHECK-NEXT: destroy RefCountedClass 0

auto propsInClass = createPropsInClass(-1234);
assert(propsInClass.getStoredInt() == -1234);
assert(propsInClass.getComputedInt() == -1235);
auto smallStructFromClass = propsInClass.getSmallStruct();
assert(smallStructFromClass.getX() == 1234);
return 0;
}
38 changes: 38 additions & 0 deletions test/Interop/SwiftToCxx/properties/getter-in-cxx.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,44 @@ public struct LargeStruct {
// CHECK-NEXT: });
// CHECK-NEXT: }

public final class PropertiesInClass {
public let storedInt: Int32

init(_ x: Int32) {
storedInt = x
}

public var computedInt: Int {
return Int(storedInt) - 1
}

public var smallStruct: FirstSmallStruct {
return FirstSmallStruct(x: UInt32(-storedInt));
}
}

// CHECK: class PropertiesInClass final : public swift::_impl::RefCountedClass {
// CHECK: using RefCountedClass::operator=;
// CHECK-NEXT: inline int32_t getStoredInt();
// CHECK-NEXT: inline swift::Int getComputedInt();
// CHECK-NEXT: inline FirstSmallStruct getSmallStruct();

// CHECK: inline int32_t PropertiesInClass::getStoredInt() {
// CHECK-NEXT: return _impl::$s10Properties0A7InClassC9storedInts5Int32Vvg(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this));
// CHECK-NEXT: }
// CHECK-NEXT: inline swift::Int PropertiesInClass::getComputedInt() {
// CHECK-NEXT: return _impl::$s10Properties0A7InClassC11computedIntSivg(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this));
// CHECK-NEXT: }
// CHECK-NEXT: inline FirstSmallStruct PropertiesInClass::getSmallStruct() {
// CHECK-NEXT: return _impl::_impl_FirstSmallStruct::returnNewValue([&](char * _Nonnull result) {
// CHECK-NEXT: _impl::swift_interop_returnDirect_Properties_FirstSmallStruct(result, _impl::$s10Properties0A7InClassC11smallStructAA010FirstSmallE0Vvg(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this)));
// CHECK-NEXT: });
// CHECK-NEXT: }

public func createPropsInClass(_ x: Int32) -> PropertiesInClass {
return PropertiesInClass(x)
}

public struct SmallStructWithGetters {
public let storedInt: UInt32
public var computedInt: Int {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,13 @@ int main() {

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

auto propsInClass = createPropsInClass(-1234);
assert(propsInClass.getStoredInt() == -1234);
propsInClass.setStoredInt(45);
assert(propsInClass.getStoredInt() == 45);
propsInClass.setComputedInt(-11);
assert(propsInClass.getComputedInt() == -11);
assert(propsInClass.getStoredInt() == -13);
return 0;
}
Loading