Skip to content

Commit cf0763d

Browse files
committed
[interop] Swift -> C++ -> Swift class type bridging
1 parent ce96484 commit cf0763d

File tree

8 files changed

+372
-2
lines changed

8 files changed

+372
-2
lines changed

include/swift/ClangImporter/ClangImporterRequests.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,9 @@ enum class CxxRecordSemanticsKind {
311311
// A record that is either not copyable or not destructible.
312312
MissingLifetimeOperation,
313313
// A record that contains a pointer (aka non-trivial type).
314-
UnsafePointerMember
314+
UnsafePointerMember,
315+
// A C++ record that represents a Swift class type exposed to C++ from Swift.
316+
SwiftClassType
315317
};
316318

317319
struct CxxRecordSemanticsDescriptor final {
@@ -366,6 +368,23 @@ class CxxRecordSemantics
366368
CxxRecordSemanticsDescriptor) const;
367369
};
368370

371+
/// Does this C++ record represent a Swift type.
372+
class CxxRecordAsSwiftType
373+
: public SimpleRequest<CxxRecordAsSwiftType,
374+
ValueDecl *(CxxRecordSemanticsDescriptor),
375+
RequestFlags::Uncached> {
376+
public:
377+
using SimpleRequest::SimpleRequest;
378+
379+
// Source location
380+
SourceLoc getNearestLoc() const { return SourceLoc(); };
381+
382+
private:
383+
friend SimpleRequest;
384+
385+
ValueDecl *evaluate(Evaluator &evaluator, CxxRecordSemanticsDescriptor) const;
386+
};
387+
369388
struct SafeUseOfCxxDeclDescriptor final {
370389
const clang::Decl *decl;
371390
ASTContext &ctx;

include/swift/ClangImporter/ClangImporterTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ SWIFT_REQUEST(ClangImporter, ObjCInterfaceAndImplementationRequest,
3333
SWIFT_REQUEST(ClangImporter, CxxRecordSemantics,
3434
CxxRecordSemanticsKind(const clang::CXXRecordDecl *), Cached,
3535
NoLocationInfo)
36+
SWIFT_REQUEST(ClangImporter, CxxRecordAsSwiftType,
37+
ValueDecl *(CxxRecordSemanticsDescriptor), Cached,
38+
NoLocationInfo)
3639
SWIFT_REQUEST(ClangImporter, IsSafeUseOfCxxDecl,
3740
bool(SafeUseOfCxxRecordDescriptor), Cached,
3841
NoLocationInfo)

lib/ClangImporter/ClangImporter.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6498,6 +6498,29 @@ static bool hasRequiredValueTypeOperations(const clang::CXXRecordDecl *decl) {
64986498
return true;
64996499
}
65006500

6501+
static bool isSwiftClassType(const clang::CXXRecordDecl *decl) {
6502+
// Swift type must be annotated with external_source_symbol attribute.
6503+
auto essAttr = decl->getAttr<clang::ExternalSourceSymbolAttr>();
6504+
if (!essAttr || essAttr->getLanguage() != "Swift" ||
6505+
essAttr->getDefinedIn().empty() || essAttr->getUSR().empty())
6506+
return false;
6507+
6508+
// Ensure that the baseclass is swift::RefCountedClass.
6509+
auto baseDecl = decl;
6510+
do {
6511+
if (baseDecl->getNumBases() != 1)
6512+
return false;
6513+
auto baseClassSpecifier = *baseDecl->bases_begin();
6514+
auto Ty = baseClassSpecifier.getType();
6515+
auto nextBaseDecl = Ty->getAsCXXRecordDecl();
6516+
if (!nextBaseDecl)
6517+
return false;
6518+
baseDecl = nextBaseDecl;
6519+
} while (baseDecl->getName() != "RefCountedClass");
6520+
6521+
return true;
6522+
}
6523+
65016524
CxxRecordSemanticsKind
65026525
CxxRecordSemantics::evaluate(Evaluator &evaluator,
65036526
CxxRecordSemanticsDescriptor desc) const {
@@ -6512,6 +6535,9 @@ CxxRecordSemantics::evaluate(Evaluator &evaluator,
65126535
return CxxRecordSemanticsKind::Trivial;
65136536
}
65146537

6538+
if (isSwiftClassType(cxxDecl))
6539+
return CxxRecordSemanticsKind::SwiftClassType;
6540+
65156541
if (!hasRequiredValueTypeOperations(cxxDecl)) {
65166542
if (hasUnsafeAPIAttr(cxxDecl))
65176543
desc.ctx.Diags.diagnose({}, diag::api_pattern_attr_ignored,
@@ -6549,6 +6575,34 @@ CxxRecordSemantics::evaluate(Evaluator &evaluator,
65496575
return CxxRecordSemanticsKind::Owned;
65506576
}
65516577

6578+
ValueDecl *
6579+
CxxRecordAsSwiftType::evaluate(Evaluator &evaluator,
6580+
CxxRecordSemanticsDescriptor desc) const {
6581+
auto cxxDecl = dyn_cast<clang::CXXRecordDecl>(desc.decl);
6582+
if (!cxxDecl)
6583+
return nullptr;
6584+
if (!isSwiftClassType(cxxDecl))
6585+
return nullptr;
6586+
6587+
SmallVector<ValueDecl *, 1> results;
6588+
auto *essaAttr = cxxDecl->getAttr<clang::ExternalSourceSymbolAttr>();
6589+
auto *mod = desc.ctx.getModuleByName(essaAttr->getDefinedIn());
6590+
if (!mod) {
6591+
// TODO: warn about missing 'import'.
6592+
return nullptr;
6593+
}
6594+
// FIXME: Support renamed declarations.
6595+
auto swiftName = cxxDecl->getName();
6596+
// FIXME: handle nested Swift types once they're supported.
6597+
mod->lookupValue(desc.ctx.getIdentifier(swiftName), NLKind::UnqualifiedLookup,
6598+
results);
6599+
if (results.size() == 1) {
6600+
if (dyn_cast<ClassDecl>(results[0]))
6601+
return results[0];
6602+
}
6603+
return nullptr;
6604+
}
6605+
65526606
bool IsSafeUseOfCxxDecl::evaluate(Evaluator &evaluator,
65536607
SafeUseOfCxxDeclDescriptor desc) const {
65546608
const clang::Decl *decl = desc.decl;
@@ -6577,6 +6631,8 @@ bool IsSafeUseOfCxxDecl::evaluate(Evaluator &evaluator,
65776631
method->getReturnType().getCanonicalType())) {
65786632
if (auto cxxRecordReturnType =
65796633
dyn_cast<clang::CXXRecordDecl>(returnType->getDecl())) {
6634+
if (isSwiftClassType(cxxRecordReturnType))
6635+
return true;
65806636
if (hasIteratorAPIAttr(cxxRecordReturnType) ||
65816637
isIterator(cxxRecordReturnType)) {
65826638
return false;

lib/ClangImporter/ImportDecl.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2624,6 +2624,11 @@ namespace {
26242624
decl->getLocation());
26252625
return nullptr;
26262626
}
2627+
if (semanticsKind == CxxRecordSemanticsKind::SwiftClassType) {
2628+
// FIXME: add a diagnostic here for unsupported imported use of Swift
2629+
// type?
2630+
return nullptr;
2631+
}
26272632

26282633
auto result = VisitRecordDecl(decl);
26292634
if (!result)

lib/ClangImporter/ImportType.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@
3232
#include "swift/AST/ParameterList.h"
3333
#include "swift/AST/PrettyStackTrace.h"
3434
#include "swift/AST/Type.h"
35-
#include "swift/AST/Types.h"
3635
#include "swift/AST/TypeVisitor.h"
36+
#include "swift/AST/Types.h"
37+
#include "swift/ClangImporter/ClangImporterRequests.h"
3738
#include "swift/ClangImporter/ClangModule.h"
3839
#include "swift/Parse/Token.h"
3940
#include "swift/Strings.h"
@@ -2142,6 +2143,20 @@ ImportedType ClangImporter::Implementation::importFunctionReturnType(
21422143
}
21432144
}
21442145

2146+
// Import the underlying result type.
2147+
if (clangDecl) {
2148+
if (auto recordType = returnType->getAsCXXRecordDecl()) {
2149+
if (auto *vd = evaluateOrDefault(
2150+
SwiftContext.evaluator,
2151+
CxxRecordAsSwiftType({recordType, SwiftContext}), nullptr)) {
2152+
if (auto *cd = dyn_cast<ClassDecl>(vd)) {
2153+
Type t = ClassType::get(cd, Type(), SwiftContext);
2154+
return ImportedType(t, /*implicitlyUnwraps=*/false);
2155+
}
2156+
}
2157+
}
2158+
}
2159+
21452160
// Import the result type.
21462161
return importType(returnType,
21472162
(isAuditedResult ? ImportTypeKind::AuditedResult
@@ -2358,6 +2373,21 @@ ClangImporter::Implementation::importParameterType(
23582373
optionalityOfParam == OTK_ImplicitlyUnwrappedOptional;
23592374
}
23602375

2376+
if (!swiftParamTy) {
2377+
if (auto recordType = paramTy->getAsCXXRecordDecl()) {
2378+
2379+
if (auto *vd = evaluateOrDefault(
2380+
SwiftContext.evaluator,
2381+
CxxRecordAsSwiftType({recordType, SwiftContext}), nullptr)) {
2382+
2383+
if (auto *cd = dyn_cast<ClassDecl>(vd)) {
2384+
2385+
swiftParamTy = ClassType::get(cd, Type(), SwiftContext);
2386+
}
2387+
}
2388+
}
2389+
}
2390+
23612391
if (!swiftParamTy) {
23622392
bool sendableByDefault =
23632393
paramIsCompletionHandler &&

lib/SIL/IR/SILFunctionType.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1433,6 +1433,20 @@ static bool isFormallyPassedIndirectly(TypeConverter &TC,
14331433
AbstractionPattern origType,
14341434
CanType substType,
14351435
const TypeLowering &substTL) {
1436+
// If this is a native Swift class that's passed directly to C/C++, treat it
1437+
// as indirect.
1438+
if (origType.isClangType()) {
1439+
if (auto *classDecl = substType->lookThroughAllOptionalTypes()
1440+
->getClassOrBoundGenericClass()) {
1441+
if (!classDecl->isForeignReferenceType()) {
1442+
if (origType.getClangType()
1443+
->getUnqualifiedDesugaredType()
1444+
->getAsCXXRecordDecl())
1445+
return true;
1446+
}
1447+
}
1448+
}
1449+
14361450
// If the C type of the argument is a const pointer, but the Swift type
14371451
// isn't, treat it as indirect.
14381452
if (origType.isClangType()
@@ -3301,6 +3315,23 @@ class CFunctionTypeConventions : public Conventions {
33013315
ParameterConvention getIndirectParameter(unsigned index,
33023316
const AbstractionPattern &type,
33033317
const TypeLowering &substTL) const override {
3318+
if (type.isClangType()) {
3319+
if (type.getClangType()
3320+
->getUnqualifiedDesugaredType()
3321+
->getAsCXXRecordDecl()) {
3322+
auto t = substTL.getLoweredType().getASTType();
3323+
if (auto *classDecl = t.getPointer()
3324+
->lookThroughAllOptionalTypes()
3325+
->getClassOrBoundGenericClass()) {
3326+
if (!classDecl->isForeignReferenceType()) {
3327+
assert(!classDecl->hasClangNode() &&
3328+
"unexpected imported class type in C function");
3329+
assert(!classDecl->isGeneric());
3330+
return ParameterConvention::Indirect_In_Guaranteed;
3331+
}
3332+
}
3333+
}
3334+
}
33043335
return getIndirectCParameterConvention(getParamType(index));
33053336
}
33063337

@@ -3328,6 +3359,20 @@ class CFunctionTypeConventions : public Conventions {
33283359
return ResultConvention::Owned;
33293360
if (tl.getLoweredType().isForeignReferenceType())
33303361
return ResultConvention::Unowned;
3362+
if (FnType->getReturnType()
3363+
->getUnqualifiedDesugaredType()
3364+
->getAsCXXRecordDecl()) {
3365+
auto t = tl.getLoweredType().getASTType();
3366+
if (auto *classDecl = t.getPointer()
3367+
->lookThroughAllOptionalTypes()
3368+
->getClassOrBoundGenericClass()) {
3369+
assert(!classDecl->hasClangNode() &&
3370+
"unexpected imported class type in C function");
3371+
assert(!classDecl->isGeneric());
3372+
return ResultConvention::Owned;
3373+
}
3374+
}
3375+
33313376
return ResultConvention::Autoreleased;
33323377
}
33333378

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t
3+
4+
// RUN: %target-swift-frontend -typecheck %t/swiftMod.swift -typecheck -module-name SwiftMod -emit-clang-header-path %t/swiftMod.h -I %t -enable-experimental-cxx-interop -Xcc -DFIRSTPASS
5+
6+
// RUN: %target-interop-build-swift %t/swiftMod.swift -o %t/swift-execution -module-name SwiftMod -I %t -g -DSECOND_PASS -Xcc -DSWIFT_CXX_INTEROP_HIDE_SWIFT_ERROR
7+
8+
// RUN: %target-codesign %t/swift-execution
9+
// RUN: %target-run %t/swift-execution | %FileCheck %s
10+
11+
// REQUIRES: executable_test
12+
13+
//--- header.h
14+
#ifndef FIRSTPASS
15+
16+
#include "swiftMod.h"
17+
18+
inline SwiftMod::ExposedToCxx createSwiftClassInCxx() {
19+
return SwiftMod::ExposedToCxx::init();
20+
}
21+
22+
inline void passSwiftClassToCxx(SwiftMod::ExposedToCxx value) {
23+
value.setI(-11);
24+
value.testMethod();
25+
}
26+
27+
// FIXME: Add test for `moved` passThrough once 'move' is supported
28+
// for Swift class typesin C++.
29+
inline SwiftMod::ExposedToCxx passThroughSwiftClass(SwiftMod::ExposedToCxx value) {
30+
auto copy = value;
31+
value.setI(2);
32+
return copy;
33+
}
34+
35+
int puts(const char *);
36+
37+
// FIXME: There's a bug where destructor isn't called if
38+
// it's not explicitly defined.
39+
class __attribute__((swift_attr("import_owned"))) InClass {
40+
SwiftMod::ExposedToCxx value;
41+
public:
42+
~InClass() {
43+
puts("destroy ~InClass()");
44+
}
45+
inline InClass(SwiftMod::ExposedToCxx value) : value(value) {}
46+
47+
inline SwiftMod::ExposedToCxx getValue() const {
48+
return value;
49+
}
50+
};
51+
52+
#endif
53+
54+
//--- module.modulemap
55+
module SwiftToCxxTest {
56+
header "header.h"
57+
requires cplusplus
58+
}
59+
60+
//--- swiftMod.swift
61+
import SwiftToCxxTest
62+
63+
public class ExposedToCxx {
64+
public init() {
65+
i = 0
66+
print("ExposedToCxx.init")
67+
}
68+
deinit {
69+
print("ExposedToCxx\(i).deinit")
70+
}
71+
72+
public final func testMethod() {
73+
print("ExposedToCxx\(i).testMethod")
74+
}
75+
76+
public var i: Int
77+
}
78+
79+
#if SECOND_PASS
80+
81+
func testReceiveAndPassSwiftClass() {
82+
let classInstance = createSwiftClassInCxx()
83+
classInstance.testMethod()
84+
classInstance.i = 12
85+
classInstance.testMethod()
86+
passSwiftClassToCxx(classInstance)
87+
classInstance.testMethod()
88+
passThroughSwiftClass(classInstance).testMethod()
89+
}
90+
91+
testReceiveAndPassSwiftClass()
92+
93+
func testReceiveAndPassSwiftClassInClass() {
94+
let v = InClass(ExposedToCxx())
95+
v.getValue().i = 75
96+
v.getValue().testMethod()
97+
}
98+
99+
testReceiveAndPassSwiftClassInClass()
100+
101+
#endif
102+
103+
// CHECK: ExposedToCxx.init
104+
// CHECK-NEXT: ExposedToCxx0.testMethod
105+
// CHECK-NEXT: ExposedToCxx12.testMethod
106+
// CHECK-NEXT: ExposedToCxx-11.testMethod
107+
// CHECK-NEXT: ExposedToCxx-11.testMethod
108+
// CHECK-NEXT: ExposedToCxx2.testMethod
109+
// CHECK-NEXT: ExposedToCxx2.deinit
110+
111+
// CHECK: ExposedToCxx.init
112+
// CHECK-NEXT: ExposedToCxx75.testMethod
113+
// CHECK-NEXT: destroy ~InClass()
114+
// CHECK-NEXT: ExposedToCxx75.deinit

0 commit comments

Comments
 (0)