Skip to content

Commit bc103b9

Browse files
authored
Merge pull request #64473 from hyp/eng/swift-class-to-cxx-to-swift-infinity
[interop] Swift -> C++ -> Swift class type bridging
2 parents 70b982f + 152daea commit bc103b9

15 files changed

+393
-12
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: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,6 +1106,9 @@ namespace {
11061106
auto parentNS = cast<clang::NamespaceDecl>(decl->getParent());
11071107
auto parent =
11081108
Impl.importDecl(parentNS, getVersion(), /*UseCanonicalDecl*/ false);
1109+
// The parent namespace might not be imported if it's `swift_private`.
1110+
if (!parent)
1111+
return nullptr;
11091112
dc = cast<EnumDecl>(parent);
11101113
}
11111114

@@ -2621,6 +2624,11 @@ namespace {
26212624
decl->getLocation());
26222625
return nullptr;
26232626
}
2627+
if (semanticsKind == CxxRecordSemanticsKind::SwiftClassType) {
2628+
// FIXME: add a diagnostic here for unsupported imported use of Swift
2629+
// type?
2630+
return nullptr;
2631+
}
26242632

26252633
auto result = VisitRecordDecl(decl);
26262634
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/PrintAsClang/PrintClangValueType.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,7 @@ void ClangValueTypePrinter::printTypePrecedingGenericTraits(
544544
ClangSyntaxPrinter printer(os);
545545
// FIXME: avoid popping out of the module's namespace here.
546546
os << "} // end namespace \n\n";
547-
os << "namespace swift {\n";
547+
os << "namespace swift SWIFT_PRIVATE_ATTR {\n";
548548

549549
os << "#pragma clang diagnostic push\n";
550550
os << "#pragma clang diagnostic ignored \"-Wc++17-extensions\"\n";
@@ -570,7 +570,7 @@ void ClangValueTypePrinter::printTypeGenericTraits(
570570
ClangSyntaxPrinter printer(os);
571571
// FIXME: avoid popping out of the module's namespace here.
572572
os << "} // end namespace \n\n";
573-
os << "namespace swift {\n";
573+
os << "namespace swift SWIFT_PRIVATE_ATTR {\n";
574574

575575
if (typeDecl->hasClangNode()) {
576576
/// Print a reference to the type metadata fucntion for a C++ type.

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

test/Interop/CxxToSwiftToCxx/bridge-cxx-struct-back-to-cxx.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ public func takeTrivialInout(_ x: inout Trivial) {
166166

167167
// CHECK: } // end namespace
168168
// CHECK-EMPTY:
169-
// CHECK-NEXT: namespace swift {
169+
// CHECK-NEXT: namespace swift SWIFT_PRIVATE_ATTR {
170170
// CHECK-NEXT: namespace _impl {
171171
// CHECK-EMPTY:
172172
// CHECK-NEXT: // Type metadata accessor for NonTrivialTemplateInt
@@ -205,7 +205,7 @@ public func takeTrivialInout(_ x: inout Trivial) {
205205
// CHECK-EMPTY:
206206
// CHECK-NEXT: } // end namespace
207207
// CHECK-EMPTY:
208-
// CHECK-NEXT: namespace swift {
208+
// CHECK-NEXT: namespace swift SWIFT_PRIVATE_ATTR {
209209
// CHECK-NEXT: namespace _impl {
210210
// CHECK-EMPTY:
211211
// CHECK-NEXT: // Type metadata accessor for NonTrivialTemplateTrivial

test/Interop/SwiftToCxx/class/swift-class-in-cxx.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public final class ClassWithIntField {
3434
// CHECK-NEXT: } // end namespace
3535

3636
// CHECK: namespace
37-
// CHECK-SAME: swift {
37+
// CHECK-SAME: swift SWIFT_PRIVATE_ATTR {
3838
// CHECK-NEXT: #pragma clang diagnostic push
3939
// CHECK-NEXT: #pragma clang diagnostic ignored "-Wc++17-extensions"
4040
// CHECK-NEXT: template<>
@@ -84,7 +84,7 @@ public final class ClassWithIntField {
8484
// CHECK-EMPTY:
8585
// CHECK-NEXT: } // end namespace
8686
// CHECK-EMPTY:
87-
// CHECK-NEXT: namespace swift {
87+
// CHECK-NEXT: namespace swift SWIFT_PRIVATE_ATTR {
8888
// CHECK-NEXT: #pragma clang diagnostic push
8989
// CHECK-NEXT: #pragma clang diagnostic ignored "-Wc++17-extensions"
9090
// CHECK-NEXT: template<>

test/Interop/SwiftToCxx/generics/generic-struct-in-cxx.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ public func inoutConcretePair(_ x: UInt16, _ y: inout GenericPair<UInt16, UInt16
261261
// CHECK-NEXT: return result;
262262
// CHECK-NEXT: }
263263

264-
// CHECK: namespace swift {
264+
// CHECK: namespace swift SWIFT_PRIVATE_ATTR {
265265
// CHECK-NEXT: #pragma clang diagnostic push
266266
// CHECK-NEXT: #pragma clang diagnostic ignored "-Wc++17-extensions"
267267
// CHECK-NEXT: template<class T_0_0, class T_0_1>

0 commit comments

Comments
 (0)