Skip to content

Commit 77187a0

Browse files
author
Gabor Horvath
committed
[cxx-interop] Introduce SWIFT_REFCOUNTED_PTR
This attribute introduces some conversions between the annotated smart pointer type and the native swift reference type. In the future we plan to introduce bridging so the smart pointer type can be hidden in the signature and we can automatically convert it to the native Swift reference type on the ABI boundaries. This PR is using a new way to use swift_attr attributes. Instead of doing the parsing in the clang importer it falls back to Swift's attribute parsing and makes sure that the annotation has valid Swift attribute syntax. rdar://156521316
1 parent c73951b commit 77187a0

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.
@@ -2736,6 +2816,9 @@ namespace {
27362816
// If we need it, add an explicit "deinit" to this type.
27372817
synthesizer.addExplicitDeinitIfRequired(result, decl);
27382818

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

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)