Skip to content

Commit 56cc467

Browse files
committed
[cxx-interop] Import mutating dereference operators
C++ `T& operator*()` is mapped to a Swift computed property `var pointee: T`. Previously `var pointee` only had a getter, after this change it will also have a setter if the C++ type declares an overload of `operator*` that returns a mutable reference. rdar://112471779
1 parent 0a1ae45 commit 56cc467

File tree

7 files changed

+141
-48
lines changed

7 files changed

+141
-48
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 53 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2186,17 +2186,6 @@ namespace {
21862186
// The name of every member.
21872187
llvm::DenseSet<StringRef> allMemberNames;
21882188

2189-
bool hasConstOperatorStar = false;
2190-
for (auto member : decl->decls()) {
2191-
if (auto method = dyn_cast<clang::CXXMethodDecl>(member)) {
2192-
if (method->getOverloadedOperator() ==
2193-
clang::OverloadedOperatorKind::OO_Star &&
2194-
method->param_empty() && method->isConst())
2195-
hasConstOperatorStar = true;
2196-
}
2197-
}
2198-
bool hasSynthesizedPointeeProperty = false;
2199-
22002189
// FIXME: Import anonymous union fields and support field access when
22012190
// it is nested in a struct.
22022191
for (auto m : decl->decls()) {
@@ -2275,26 +2264,11 @@ namespace {
22752264

22762265
if (auto MD = dyn_cast<FuncDecl>(member)) {
22772266
if (auto cxxMethod = dyn_cast<clang::CXXMethodDecl>(m)) {
2267+
ImportedName methodImportedName =
2268+
Impl.importFullName(cxxMethod, getActiveSwiftVersion());
22782269
auto cxxOperatorKind = cxxMethod->getOverloadedOperator();
22792270

2280-
if (cxxOperatorKind == clang::OO_Star && cxxMethod->param_empty()) {
2281-
// This is a dereference operator. We synthesize a computed
2282-
// property called `pointee` for it.
2283-
2284-
// If this record has multiple overloads of `operator*`, prefer
2285-
// the const overload if it exists.
2286-
if ((cxxMethod->isConst() || !hasConstOperatorStar) &&
2287-
!hasSynthesizedPointeeProperty) {
2288-
VarDecl *pointeeProperty =
2289-
synthesizer.makeDereferencedPointeeProperty(MD);
2290-
result->addMember(pointeeProperty);
2291-
hasSynthesizedPointeeProperty = true;
2292-
}
2293-
2294-
Impl.markUnavailable(MD, "use .pointee property");
2295-
MD->overwriteAccess(AccessLevel::Private);
2296-
} else if (cxxOperatorKind ==
2297-
clang::OverloadedOperatorKind::OO_PlusPlus) {
2271+
if (cxxOperatorKind == clang::OverloadedOperatorKind::OO_PlusPlus) {
22982272
// Make sure the type is not a foreign reference type.
22992273
// We cannot handle `operator++` for those types, since the
23002274
// current implementation creates a new instance of the type.
@@ -2319,8 +2293,8 @@ namespace {
23192293
clang::OverloadedOperatorKind::OO_PlusPlus &&
23202294
cxxOperatorKind !=
23212295
clang::OverloadedOperatorKind::OO_Call &&
2322-
cxxOperatorKind !=
2323-
clang::OverloadedOperatorKind::OO_Subscript) {
2296+
!methodImportedName.isSubscriptAccessor() &&
2297+
!methodImportedName.isDereferenceAccessor()) {
23242298

23252299
auto opFuncDecl = synthesizer.makeOperator(MD, cxxMethod);
23262300

@@ -2551,6 +2525,18 @@ namespace {
25512525
auto *subscriptImpl = getterAndSetter.first ? getterAndSetter.first : getterAndSetter.second;
25522526
Impl.addAlternateDecl(subscriptImpl, subscript);
25532527
}
2528+
2529+
if (Impl.cxxDereferenceOperators.find(result) !=
2530+
Impl.cxxDereferenceOperators.end()) {
2531+
// If this type has a dereference operator, synthesize a computed
2532+
// property called `pointee` for it.
2533+
auto getterAndSetter = Impl.cxxDereferenceOperators[result];
2534+
2535+
VarDecl *pointeeProperty =
2536+
synthesizer.makeDereferencedPointeeProperty(
2537+
getterAndSetter.first, getterAndSetter.second);
2538+
result->addMember(pointeeProperty);
2539+
}
25542540
}
25552541

25562542
result->setMemberLoader(&Impl, 0);
@@ -3191,6 +3177,8 @@ namespace {
31913177
case ImportedAccessorKind::None:
31923178
case ImportedAccessorKind::SubscriptGetter:
31933179
case ImportedAccessorKind::SubscriptSetter:
3180+
case ImportedAccessorKind::DereferenceGetter:
3181+
case ImportedAccessorKind::DereferenceSetter:
31943182
break;
31953183

31963184
case ImportedAccessorKind::PropertyGetter: {
@@ -3531,6 +3519,8 @@ namespace {
35313519
}
35323520
}
35333521

3522+
bool makePrivate = false;
3523+
35343524
if (importedName.isSubscriptAccessor() && !importFuncWithoutSignature) {
35353525
assert(func->getParameters()->size() == 1);
35363526
auto typeDecl = dc->getSelfNominalTypeDecl();
@@ -3558,8 +3548,32 @@ namespace {
35583548

35593549
Impl.markUnavailable(func, "use subscript");
35603550
}
3561-
// Someday, maybe this will need to be 'open' for C++ virtual methods.
3562-
func->setAccess(AccessLevel::Public);
3551+
3552+
if (importedName.isDereferenceAccessor() &&
3553+
!importFuncWithoutSignature) {
3554+
auto typeDecl = dc->getSelfNominalTypeDecl();
3555+
auto &getterAndSetter = Impl.cxxDereferenceOperators[typeDecl];
3556+
3557+
switch (importedName.getAccessorKind()) {
3558+
case ImportedAccessorKind::DereferenceGetter:
3559+
getterAndSetter.first = func;
3560+
break;
3561+
case ImportedAccessorKind::DereferenceSetter:
3562+
getterAndSetter.second = func;
3563+
break;
3564+
default:
3565+
llvm_unreachable("invalid dereference operator kind");
3566+
}
3567+
3568+
Impl.markUnavailable(func, "use .pointee property");
3569+
makePrivate = true;
3570+
}
3571+
3572+
if (makePrivate)
3573+
func->setAccess(AccessLevel::Private);
3574+
else
3575+
// Someday, maybe this will need to be 'open' for C++ virtual methods.
3576+
func->setAccess(AccessLevel::Public);
35633577
}
35643578

35653579
result->setIsObjC(false);
@@ -3983,6 +3997,10 @@ namespace {
39833997
case ImportedAccessorKind::SubscriptSetter:
39843998
case ImportedAccessorKind::None:
39853999
return importObjCMethodDecl(decl, dc, llvm::None);
4000+
4001+
case ImportedAccessorKind::DereferenceGetter:
4002+
case ImportedAccessorKind::DereferenceSetter:
4003+
llvm_unreachable("dereference operators only exist in C++");
39864004
}
39874005
}
39884006

@@ -6045,6 +6063,8 @@ SwiftDeclConverter::getImplicitProperty(ImportedName importedName,
60456063
case ImportedAccessorKind::None:
60466064
case ImportedAccessorKind::SubscriptGetter:
60476065
case ImportedAccessorKind::SubscriptSetter:
6066+
case ImportedAccessorKind::DereferenceGetter:
6067+
case ImportedAccessorKind::DereferenceSetter:
60486068
llvm_unreachable("Not a property accessor");
60496069

60506070
case ImportedAccessorKind::PropertyGetter:

lib/ClangImporter/ImportName.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,8 @@ void ClangImporter::Implementation::printSwiftName(ImportedName name,
334334
bool isSetter = false;
335335
switch (name.getAccessorKind()) {
336336
case ImportedAccessorKind::None:
337+
case ImportedAccessorKind::DereferenceGetter:
338+
case ImportedAccessorKind::DereferenceSetter:
337339
break;
338340

339341
case ImportedAccessorKind::PropertyGetter:
@@ -1916,6 +1918,15 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
19161918
baseName = swiftCtx.getIdentifier(operatorName).str();
19171919
isFunction = true;
19181920
addEmptyArgNamesForClangFunction(functionDecl, argumentNames);
1921+
if (auto cxxMethod = dyn_cast<clang::CXXMethodDecl>(functionDecl)) {
1922+
if (op == clang::OverloadedOperatorKind::OO_Star &&
1923+
cxxMethod->param_empty()) {
1924+
if (cxxMethod->isConst())
1925+
result.info.accessorKind = ImportedAccessorKind::DereferenceGetter;
1926+
else
1927+
result.info.accessorKind = ImportedAccessorKind::DereferenceSetter;
1928+
}
1929+
}
19191930
break;
19201931
}
19211932
case clang::OverloadedOperatorKind::OO_Call:

lib/ClangImporter/ImportName.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ enum class ImportedAccessorKind : unsigned {
3838
PropertySetter,
3939
SubscriptGetter,
4040
SubscriptSetter,
41+
DereferenceGetter,
42+
DereferenceSetter,
4143
};
4244
enum { NumImportedAccessorKindBits = 3 };
4345

@@ -324,6 +326,8 @@ class ImportedName {
324326
case ImportedAccessorKind::None:
325327
case ImportedAccessorKind::SubscriptGetter:
326328
case ImportedAccessorKind::SubscriptSetter:
329+
case ImportedAccessorKind::DereferenceGetter:
330+
case ImportedAccessorKind::DereferenceSetter:
327331
return false;
328332

329333
case ImportedAccessorKind::PropertyGetter:
@@ -338,6 +342,8 @@ class ImportedName {
338342
case ImportedAccessorKind::None:
339343
case ImportedAccessorKind::PropertyGetter:
340344
case ImportedAccessorKind::PropertySetter:
345+
case ImportedAccessorKind::DereferenceGetter:
346+
case ImportedAccessorKind::DereferenceSetter:
341347
return false;
342348

343349
case ImportedAccessorKind::SubscriptGetter:
@@ -347,6 +353,23 @@ class ImportedName {
347353

348354
llvm_unreachable("Invalid ImportedAccessorKind.");
349355
}
356+
357+
bool isDereferenceAccessor() const {
358+
switch (getAccessorKind()) {
359+
case ImportedAccessorKind::None:
360+
case ImportedAccessorKind::PropertyGetter:
361+
case ImportedAccessorKind::PropertySetter:
362+
case ImportedAccessorKind::SubscriptGetter:
363+
case ImportedAccessorKind::SubscriptSetter:
364+
return false;
365+
366+
case ImportedAccessorKind::DereferenceGetter:
367+
case ImportedAccessorKind::DereferenceSetter:
368+
return true;
369+
}
370+
371+
llvm_unreachable("Invalid ImportedAccessorKind.");
372+
}
350373
};
351374

352375
/// Strips a trailing "Notification", if present. Returns {} if name doesn't end

lib/ClangImporter/ImporterImpl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,9 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
642642
llvm::MapVector<std::pair<NominalTypeDecl *, Type>,
643643
std::pair<FuncDecl *, FuncDecl *>> cxxSubscripts;
644644

645+
llvm::MapVector<NominalTypeDecl *, std::pair<FuncDecl *, FuncDecl *>>
646+
cxxDereferenceOperators;
647+
645648
private:
646649
// Keep track of the decls that were already cloned for this specific class.
647650
llvm::DenseMap<std::pair<ValueDecl *, DeclContext *>, ValueDecl *>

lib/ClangImporter/SwiftDeclSynthesizer.cpp

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1663,28 +1663,34 @@ SubscriptDecl *SwiftDeclSynthesizer::makeSubscript(FuncDecl *getter,
16631663

16641664
// MARK: C++ dereference operator
16651665

1666-
VarDecl *SwiftDeclSynthesizer::makeDereferencedPointeeProperty(
1667-
FuncDecl *dereferenceFunc) {
1666+
VarDecl *
1667+
SwiftDeclSynthesizer::makeDereferencedPointeeProperty(FuncDecl *getter,
1668+
FuncDecl *setter) {
1669+
assert((getter || setter) &&
1670+
"getter or setter required to generate a pointee property");
1671+
16681672
auto &ctx = ImporterImpl.SwiftContext;
1669-
auto dc = dereferenceFunc->getDeclContext();
1673+
FuncDecl *getterImpl = getter ? getter : setter;
1674+
FuncDecl *setterImpl = setter;
1675+
auto dc = getterImpl->getDeclContext();
16701676

16711677
// Get the return type wrapped in `Unsafe(Mutable)Pointer<T>`.
1672-
const auto rawElementTy = dereferenceFunc->getResultInterfaceType();
1678+
const auto rawElementTy = getterImpl->getResultInterfaceType();
16731679
// Unwrap `T`. Use rawElementTy for return by value.
16741680
const auto elementTy = rawElementTy->getAnyPointerElementType()
16751681
? rawElementTy->getAnyPointerElementType()
16761682
: rawElementTy;
16771683

16781684
auto result = new (ctx)
16791685
VarDecl(/*isStatic*/ false, VarDecl::Introducer::Var,
1680-
dereferenceFunc->getStartLoc(), ctx.getIdentifier("pointee"), dc);
1686+
getterImpl->getStartLoc(), ctx.getIdentifier("pointee"), dc);
16811687
result->setInterfaceType(elementTy);
16821688
result->setAccess(AccessLevel::Public);
16831689
result->setImplInfo(StorageImplInfo::getImmutableComputed());
16841690

16851691
AccessorDecl *getterDecl = AccessorDecl::create(
1686-
ctx, dereferenceFunc->getLoc(), dereferenceFunc->getLoc(),
1687-
AccessorKind::Get, result, SourceLoc(), StaticSpellingKind::None,
1692+
ctx, getterImpl->getLoc(), getterImpl->getLoc(), AccessorKind::Get,
1693+
result, SourceLoc(), StaticSpellingKind::None,
16881694
/*async*/ false, SourceLoc(),
16891695
/*throws*/ false, SourceLoc(), ParameterList::createEmpty(ctx), elementTy,
16901696
dc);
@@ -1693,14 +1699,43 @@ VarDecl *SwiftDeclSynthesizer::makeDereferencedPointeeProperty(
16931699
getterDecl->setIsDynamic(false);
16941700
getterDecl->setIsTransparent(true);
16951701
getterDecl->setBodySynthesizer(synthesizeUnwrappingGetterBody,
1696-
dereferenceFunc);
1702+
getterImpl);
16971703

1698-
if (dereferenceFunc->isMutating()) {
1704+
if (getterImpl->isMutating()) {
16991705
getterDecl->setSelfAccessKind(SelfAccessKind::Mutating);
17001706
result->setIsGetterMutating(true);
17011707
}
17021708

1703-
ImporterImpl.makeComputed(result, getterDecl, /*setter*/ nullptr);
1709+
AccessorDecl *setterDecl = nullptr;
1710+
if (setterImpl) {
1711+
auto paramVarDecl =
1712+
new (ctx) ParamDecl(SourceLoc(), SourceLoc(), Identifier(), SourceLoc(),
1713+
ctx.getIdentifier("newValue"), dc);
1714+
paramVarDecl->setSpecifier(ParamSpecifier::Default);
1715+
paramVarDecl->setInterfaceType(elementTy);
1716+
1717+
auto setterParamList =
1718+
ParameterList::create(ctx, {paramVarDecl});
1719+
1720+
setterDecl = AccessorDecl::create(
1721+
ctx, setterImpl->getLoc(), setterImpl->getLoc(), AccessorKind::Set,
1722+
result, SourceLoc(), StaticSpellingKind::None,
1723+
/*async*/ false, SourceLoc(),
1724+
/*throws*/ false, SourceLoc(), setterParamList,
1725+
TupleType::getEmpty(ctx), dc);
1726+
setterDecl->setAccess(AccessLevel::Public);
1727+
setterDecl->setImplicit();
1728+
setterDecl->setIsDynamic(false);
1729+
setterDecl->setIsTransparent(true);
1730+
setterDecl->setBodySynthesizer(synthesizeSubscriptSetterBody, setterImpl);
1731+
1732+
if (setterImpl->isMutating()) {
1733+
setterDecl->setSelfAccessKind(SelfAccessKind::Mutating);
1734+
result->setIsSetterMutating(true);
1735+
}
1736+
}
1737+
1738+
ImporterImpl.makeComputed(result, getterDecl, setterDecl);
17041739
return result;
17051740
}
17061741

lib/ClangImporter/SwiftDeclSynthesizer.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,9 +275,10 @@ class SwiftDeclSynthesizer {
275275
/// Given an imported C++ dereference operator (`operator*()`), create a
276276
/// `pointee` computed property.
277277
///
278-
/// \param dereferenceFunc function returning `Unsafe(Mutable)?Pointer<T>`
278+
/// \param getter function returning `UnsafePointer<T>`
279+
/// \param setter function returning `UnsafeMutablePointer<T>`
279280
/// \return computed property declaration
280-
VarDecl *makeDereferencedPointeeProperty(FuncDecl *dereferenceFunc);
281+
VarDecl *makeDereferencedPointeeProperty(FuncDecl *getter, FuncDecl *setter);
281282

282283
/// Given a C++ pre-increment operator (`operator++()`). create a non-mutating
283284
/// function `successor() -> Self`.

test/Interop/Cxx/operators/member-inline-module-interface.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@
206206
// CHECK: }
207207

208208
// CHECK: struct Iterator {
209-
// CHECK: var pointee: Int32 { mutating get }
209+
// CHECK: var pointee: Int32 { mutating get set }
210210
// CHECK: @available(*, unavailable, message: "use .pointee property")
211211
// CHECK: mutating func __operatorStar() -> UnsafeMutablePointer<Int32>
212212
// CHECK: }
@@ -224,17 +224,17 @@
224224
// CHECK: }
225225

226226
// CHECK: struct AmbiguousOperatorStar {
227-
// CHECK-NEXT: var pointee: Int32 { get }
228227
// CHECK-NEXT: init()
228+
// CHECK-NEXT: var pointee: Int32
229229
// CHECK-NEXT: @available(*, unavailable, message: "use .pointee property")
230230
// CHECK-NEXT: mutating func __operatorStar() -> UnsafeMutablePointer<Int32>
231231
// CHECK-NEXT: @available(*, unavailable, message: "use .pointee property")
232232
// CHECK-NEXT: func __operatorStar() -> UnsafePointer<Int32>
233233
// CHECK-NEXT: }
234234

235235
// CHECK: struct AmbiguousOperatorStar2 {
236-
// CHECK-NEXT: var pointee: Int32 { get }
237236
// CHECK-NEXT: init()
237+
// CHECK-NEXT: var pointee: Int32
238238
// CHECK-NEXT: @available(*, unavailable, message: "use .pointee property")
239239
// CHECK-NEXT: mutating func __operatorStar() -> UnsafeMutablePointer<Int32>
240240
// CHECK-NEXT: @available(*, unavailable, message: "use .pointee property")

0 commit comments

Comments
 (0)