Skip to content

Commit d97fa92

Browse files
authored
Merge pull request #85650 from Xazax-hun/refcounted-ptr-conversions
[cxx-interop] Introduce SWIFT_REFCOUNTED_PTR
2 parents a6b30c4 + 77187a0 commit d97fa92

File tree

9 files changed

+365
-0
lines changed

9 files changed

+365
-0
lines changed

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,5 +388,32 @@ NOTE(nonescapable_field_of_escapable, none,
388388
"escapable record %0 cannot have non-escapable field '%1'",
389389
(const clang::NamedDecl *, StringRef))
390390

391+
NOTE(refcounted_ptr_missing_torawpointer, none,
392+
"SWIFT_REFCOUNTED_PTR on %0 is missing required "
393+
"'_toRawPointer' parameter",
394+
(const clang::NamedDecl *))
395+
396+
NOTE(refcounted_ptr_torawpointer_lookup_failure, none,
397+
"cannot find function '%0' specified in '_toRawPointer' "
398+
"parameter of SWIFT_REFCOUNTED_PTR %1",
399+
(StringRef, const clang::NamedDecl *))
400+
401+
NOTE(refcounted_ptr_torawpointer_lookup_ambiguity, none,
402+
"there are multiple candidates of function '%0' specified in "
403+
"'_toRawPointer' parameter of SWIFT_REFCOUNTED_PTR %1",
404+
(StringRef, const clang::NamedDecl *))
405+
406+
NOTE(refcounted_ptr_torawpointer_not_function, none,
407+
"%0 specified in '_toRawPointer' parameter of "
408+
"SWIFT_REFCOUNTED_PTR on %1 is not a function",
409+
(const Decl *, const clang::NamedDecl *))
410+
411+
NOTE(
412+
refcounted_ptr_torawpointer_wrong_signature, none,
413+
"function %0 specified in '_toRawPointer' parameter of "
414+
"SWIFT_REFCOUNTED_PTR on %1 has incorrect signature; expected a function "
415+
"that takes no arguments and returns a pointer to a foreign reference type",
416+
(const Decl *, const clang::NamedDecl *))
417+
391418
#define UNDEFINE_DIAGNOSTIC_MACROS
392419
#include "DefineDiagnosticMacros.h"

lib/ClangImporter/ImportDecl.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2127,6 +2127,86 @@ namespace {
21272127
return;
21282128
}
21292129

2130+
bool
2131+
injectBridgingConversionsForRefCountedSmartPtrs(NominalTypeDecl *smartPtr) {
2132+
for (const auto *attr : smartPtr->getAttrs()) {
2133+
if (const auto *customAttr = dyn_cast<CustomAttr>(attr)) {
2134+
if (!customAttr->getTypeRepr()->isSimpleUnqualifiedIdentifier(
2135+
"_refCountedPtr"))
2136+
continue;
2137+
auto clangDecl = cast<clang::TagDecl>(smartPtr->getClangDecl());
2138+
StringRef ToRawPtrFuncName;
2139+
for (auto arg : *customAttr->getArgs()) {
2140+
if (arg.getLabel().str() == "ToRawPointer") {
2141+
if (const auto *literal =
2142+
dyn_cast<StringLiteralExpr>(arg.getExpr()))
2143+
ToRawPtrFuncName = literal->getValue();
2144+
}
2145+
}
2146+
if (ToRawPtrFuncName.empty()) {
2147+
Impl.addImportDiagnostic(
2148+
clangDecl,
2149+
Diagnostic(diag::refcounted_ptr_missing_torawpointer,
2150+
clangDecl),
2151+
clangDecl->getLocation());
2152+
return false;
2153+
}
2154+
2155+
auto results = getValueDeclsForName(smartPtr, ToRawPtrFuncName);
2156+
if (results.empty()) {
2157+
Impl.addImportDiagnostic(
2158+
clangDecl,
2159+
Diagnostic(
2160+
diag::refcounted_ptr_torawpointer_lookup_failure,
2161+
Impl.SwiftContext.AllocateCopy(ToRawPtrFuncName.str()),
2162+
clangDecl),
2163+
clangDecl->getLocation());
2164+
return false;
2165+
}
2166+
if (results.size() > 1) {
2167+
Impl.addImportDiagnostic(
2168+
clangDecl,
2169+
Diagnostic(
2170+
diag::refcounted_ptr_torawpointer_lookup_ambiguity,
2171+
Impl.SwiftContext.AllocateCopy(ToRawPtrFuncName.str()),
2172+
clangDecl),
2173+
clangDecl->getLocation());
2174+
return false;
2175+
}
2176+
2177+
auto toRawPtrFunc = dyn_cast<FuncDecl>(results.front());
2178+
if (!toRawPtrFunc) {
2179+
Impl.addImportDiagnostic(
2180+
clangDecl,
2181+
Diagnostic(diag::refcounted_ptr_torawpointer_not_function,
2182+
toRawPtrFunc, clangDecl),
2183+
clangDecl->getLocation());
2184+
return false;
2185+
}
2186+
2187+
auto pointeeType = toRawPtrFunc->getResultInterfaceType()
2188+
->lookThroughSingleOptionalType();
2189+
ClassDecl *referenceDecl = pointeeType->getClassOrBoundGenericClass();
2190+
2191+
if (toRawPtrFunc->getParameters()->size() != 0 || !referenceDecl) {
2192+
Impl.addImportDiagnostic(
2193+
clangDecl,
2194+
Diagnostic(diag::refcounted_ptr_torawpointer_wrong_signature,
2195+
toRawPtrFunc, clangDecl),
2196+
clangDecl->getLocation());
2197+
return false;
2198+
}
2199+
2200+
auto asReferenceDecl =
2201+
synthesizer.createSmartPtrBridgingProperty(toRawPtrFunc);
2202+
smartPtr->addMember(asReferenceDecl);
2203+
smartPtr->addMemberToLookupTable(asReferenceDecl);
2204+
break;
2205+
}
2206+
}
2207+
return true;
2208+
}
2209+
21302210
Decl *VisitRecordDecl(const clang::RecordDecl *decl) {
21312211
// Track whether this record contains fields we can't reference in Swift
21322212
// as stored properties.
@@ -2737,6 +2817,9 @@ namespace {
27372817
// If we need it, add an explicit "deinit" to this type.
27382818
synthesizer.addExplicitDeinitIfRequired(result, decl);
27392819

2820+
if (!injectBridgingConversionsForRefCountedSmartPtrs(result))
2821+
return nullptr;
2822+
27402823
result->setMemberLoader(&Impl, 0);
27412824
return result;
27422825
}

lib/ClangImporter/SwiftBridging/swift/bridging

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,33 @@
111111
__attribute__((swift_attr("release:immortal"))) \
112112
__attribute__((swift_attr("unsafe")))
113113

114+
/// The annotated `class` or `struct` is a smart pointer to an intrusively
115+
/// reference counted object. The pointee type is expected to be a Swift shared
116+
/// reference. The presence of this annotation will introduce conversion
117+
/// functions between the smart pointer type and Swift's native reference type.
118+
/// The conversion function is expected to return a +0 raw pointer to a Swift
119+
/// shared reference type.
120+
///
121+
/// ```c++
122+
/// template <class T>
123+
/// struct SWIFT_REFCOUNTED_PTR(.getPtr) Ptr {
124+
/// T *_Nullable getPtr() const { return ptr; }
125+
/// };
126+
///
127+
/// using PtrOfSharedRef = Ptr<SharedRef>;
128+
/// ```
129+
///
130+
/// In Swift, we can convert the smart pointer type to the corresponding native
131+
/// Swift reference:
132+
/// ```swift
133+
/// func f(_ x: PtrOfSharedRef) {
134+
/// let y = x.asReference
135+
/// }
136+
/// ```
137+
#define SWIFT_REFCOUNTED_PTR(_toRawPointer) \
138+
__attribute__((swift_attr( \
139+
"@_refCountedPtr(ToRawPointer: \"" _CXX_INTEROP_STRINGIFY(_toRawPointer) "\")")))
140+
114141
/// Specifies a name that will be used in Swift for this declaration instead of its original name.
115142
#define SWIFT_NAME(_name) __attribute__((swift_name(#_name)))
116143

@@ -280,6 +307,7 @@
280307
#define SWIFT_SHARED_REFERENCE(_retain, _release)
281308
#define SWIFT_IMMORTAL_REFERENCE
282309
#define SWIFT_UNSAFE_REFERENCE
310+
#define SWIFT_REFCOUNTED_PTR(_toRawPointer)
283311
#define SWIFT_NAME(_name)
284312
#define SWIFT_CONFORMS_TO_PROTOCOL(_moduleName_protocolName)
285313
#define SWIFT_COMPUTED_PROPERTY

lib/ClangImporter/SwiftDeclSynthesizer.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,16 @@
2424
#include "swift/AST/Stmt.h"
2525
#include "swift/AST/TypeCheckRequests.h"
2626
#include "swift/Basic/Assertions.h"
27+
#include "swift/Basic/SourceLoc.h"
2728
#include "swift/ClangImporter/ClangImporterRequests.h"
29+
#include "clang/AST/DeclCXX.h"
30+
#include "clang/AST/DeclarationName.h"
31+
#include "clang/AST/Expr.h"
32+
#include "clang/AST/ExprCXX.h"
2833
#include "clang/AST/Mangle.h"
34+
#include "clang/AST/Stmt.h"
35+
#include "clang/AST/Type.h"
36+
#include "clang/Basic/SourceLocation.h"
2937
#include "clang/Sema/DelayedDiagnostic.h"
3038

3139
using namespace swift;
@@ -749,6 +757,53 @@ ConstructorDecl *SwiftDeclSynthesizer::createRawValueBridgingConstructor(
749757
return init;
750758
}
751759

760+
static std::pair<BraceStmt *, bool>
761+
synthesizeAsReferenceBody(AbstractFunctionDecl *afd, void *context) {
762+
auto getterDecl = cast<AccessorDecl>(afd);
763+
auto getterImpl = static_cast<FuncDecl *>(context);
764+
765+
ASTContext &ctx = getterDecl->getASTContext();
766+
767+
auto selfArg = createSelfArg(getterDecl);
768+
auto *getterImplCallExpr =
769+
createAccessorImplCallExpr(getterImpl, selfArg, {});
770+
771+
auto *returnStmt = ReturnStmt::createImplicit(ctx, getterImplCallExpr);
772+
773+
auto body = BraceStmt::create(ctx, SourceLoc(), {returnStmt}, SourceLoc(),
774+
/*implicit*/ true);
775+
return {body, /*isTypeChecked*/ true};
776+
}
777+
778+
VarDecl *SwiftDeclSynthesizer::createSmartPtrBridgingProperty(
779+
FuncDecl *bridgingFunction) {
780+
auto smartPtrType = bridgingFunction->getDeclContext();
781+
auto referenceType = bridgingFunction->getResultInterfaceType();
782+
auto result = new (ImporterImpl.SwiftContext) VarDecl(
783+
/*isStatic*/ false, VarDecl::Introducer::Var,
784+
bridgingFunction->getStartLoc(),
785+
ImporterImpl.SwiftContext.getIdentifier("asReference"), smartPtrType);
786+
result->setInterfaceType(referenceType);
787+
result->copyFormalAccessFrom(bridgingFunction);
788+
789+
AccessorDecl *getterDecl = AccessorDecl::create(
790+
ImporterImpl.SwiftContext, bridgingFunction->getLoc(),
791+
bridgingFunction->getLoc(), AccessorKind::Get, result,
792+
/*async*/ false, SourceLoc(),
793+
/*throws*/ false, SourceLoc(), /*ThrownType=*/TypeLoc(),
794+
ParameterList::createEmpty(ImporterImpl.SwiftContext), referenceType,
795+
smartPtrType);
796+
getterDecl->copyFormalAccessFrom(bridgingFunction);
797+
getterDecl->setImplicit();
798+
getterDecl->setIsDynamic(false);
799+
getterDecl->setIsTransparent(true);
800+
getterDecl->setBodySynthesizer(synthesizeAsReferenceBody, bridgingFunction);
801+
getterDecl->setSelfAccessKind(SelfAccessKind::NonMutating);
802+
result->setIsGetterMutating(false);
803+
ImporterImpl.makeComputed(result, getterDecl, nullptr);
804+
return result;
805+
}
806+
752807
void SwiftDeclSynthesizer::makeStructRawValuedWithBridge(
753808
StructDecl *structDecl, Type storedUnderlyingType, Type bridgedType,
754809
ArrayRef<KnownProtocolKind> synthesizedProtocolAttrs,

lib/ClangImporter/SwiftDeclSynthesizer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define SWIFT_SWIFT_DECL_SYNTHESIZER_H
1515

1616
#include "ImporterImpl.h"
17+
#include "swift/AST/Decl.h"
1718
#include "swift/ClangImporter/ClangImporter.h"
1819

1920
namespace swift {
@@ -134,6 +135,9 @@ class SwiftDeclSynthesizer {
134135
bool wantLabel,
135136
bool wantBody);
136137

138+
/// Create a constructor that initializes a class from a smart pointer.
139+
VarDecl *createSmartPtrBridgingProperty(FuncDecl *bridgingFunction);
140+
137141
/// Make a struct declaration into a raw-value-backed struct, with
138142
/// bridged computed rawValue property which differs from stored backing
139143
///

test/Interop/Cxx/foreign-reference/Inputs/module.modulemap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,8 @@ module FRTReferenceReturns {
9797
header "frt-reference-returns.h"
9898
requires cplusplus
9999
}
100+
101+
module RefCountedSmartPtrs {
102+
header "refcounted-smartptrs.h"
103+
requires cplusplus
104+
}

0 commit comments

Comments
 (0)