Skip to content

[cxx-interop] Import mutating dereference operators #67375

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 53 additions & 33 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2186,17 +2186,6 @@ namespace {
// The name of every member.
llvm::DenseSet<StringRef> allMemberNames;

bool hasConstOperatorStar = false;
for (auto member : decl->decls()) {
if (auto method = dyn_cast<clang::CXXMethodDecl>(member)) {
if (method->getOverloadedOperator() ==
clang::OverloadedOperatorKind::OO_Star &&
method->param_empty() && method->isConst())
hasConstOperatorStar = true;
}
}
bool hasSynthesizedPointeeProperty = false;

// FIXME: Import anonymous union fields and support field access when
// it is nested in a struct.
for (auto m : decl->decls()) {
Expand Down Expand Up @@ -2275,26 +2264,11 @@ namespace {

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

if (cxxOperatorKind == clang::OO_Star && cxxMethod->param_empty()) {
// This is a dereference operator. We synthesize a computed
// property called `pointee` for it.

// If this record has multiple overloads of `operator*`, prefer
// the const overload if it exists.
if ((cxxMethod->isConst() || !hasConstOperatorStar) &&
!hasSynthesizedPointeeProperty) {
VarDecl *pointeeProperty =
synthesizer.makeDereferencedPointeeProperty(MD);
result->addMember(pointeeProperty);
hasSynthesizedPointeeProperty = true;
}

Impl.markUnavailable(MD, "use .pointee property");
MD->overwriteAccess(AccessLevel::Private);
} else if (cxxOperatorKind ==
clang::OverloadedOperatorKind::OO_PlusPlus) {
if (cxxOperatorKind == clang::OverloadedOperatorKind::OO_PlusPlus) {
// Make sure the type is not a foreign reference type.
// We cannot handle `operator++` for those types, since the
// current implementation creates a new instance of the type.
Expand All @@ -2319,8 +2293,8 @@ namespace {
clang::OverloadedOperatorKind::OO_PlusPlus &&
cxxOperatorKind !=
clang::OverloadedOperatorKind::OO_Call &&
cxxOperatorKind !=
clang::OverloadedOperatorKind::OO_Subscript) {
!methodImportedName.isSubscriptAccessor() &&
!methodImportedName.isDereferenceAccessor()) {

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

Expand Down Expand Up @@ -2551,6 +2525,18 @@ namespace {
auto *subscriptImpl = getterAndSetter.first ? getterAndSetter.first : getterAndSetter.second;
Impl.addAlternateDecl(subscriptImpl, subscript);
}

if (Impl.cxxDereferenceOperators.find(result) !=
Impl.cxxDereferenceOperators.end()) {
// If this type has a dereference operator, synthesize a computed
// property called `pointee` for it.
auto getterAndSetter = Impl.cxxDereferenceOperators[result];

VarDecl *pointeeProperty =
synthesizer.makeDereferencedPointeeProperty(
getterAndSetter.first, getterAndSetter.second);
result->addMember(pointeeProperty);
}
}

result->setMemberLoader(&Impl, 0);
Expand Down Expand Up @@ -3191,6 +3177,8 @@ namespace {
case ImportedAccessorKind::None:
case ImportedAccessorKind::SubscriptGetter:
case ImportedAccessorKind::SubscriptSetter:
case ImportedAccessorKind::DereferenceGetter:
case ImportedAccessorKind::DereferenceSetter:
break;

case ImportedAccessorKind::PropertyGetter: {
Expand Down Expand Up @@ -3531,6 +3519,8 @@ namespace {
}
}

bool makePrivate = false;

if (importedName.isSubscriptAccessor() && !importFuncWithoutSignature) {
assert(func->getParameters()->size() == 1);
auto typeDecl = dc->getSelfNominalTypeDecl();
Expand Down Expand Up @@ -3558,8 +3548,32 @@ namespace {

Impl.markUnavailable(func, "use subscript");
}
// Someday, maybe this will need to be 'open' for C++ virtual methods.
func->setAccess(AccessLevel::Public);

if (importedName.isDereferenceAccessor() &&
!importFuncWithoutSignature) {
auto typeDecl = dc->getSelfNominalTypeDecl();
auto &getterAndSetter = Impl.cxxDereferenceOperators[typeDecl];

switch (importedName.getAccessorKind()) {
case ImportedAccessorKind::DereferenceGetter:
getterAndSetter.first = func;
break;
case ImportedAccessorKind::DereferenceSetter:
getterAndSetter.second = func;
break;
default:
llvm_unreachable("invalid dereference operator kind");
}

Impl.markUnavailable(func, "use .pointee property");
makePrivate = true;
}

if (makePrivate)
func->setAccess(AccessLevel::Private);
else
// Someday, maybe this will need to be 'open' for C++ virtual methods.
func->setAccess(AccessLevel::Public);
}

result->setIsObjC(false);
Expand Down Expand Up @@ -3983,6 +3997,10 @@ namespace {
case ImportedAccessorKind::SubscriptSetter:
case ImportedAccessorKind::None:
return importObjCMethodDecl(decl, dc, llvm::None);

case ImportedAccessorKind::DereferenceGetter:
case ImportedAccessorKind::DereferenceSetter:
llvm_unreachable("dereference operators only exist in C++");
}
}

Expand Down Expand Up @@ -6045,6 +6063,8 @@ SwiftDeclConverter::getImplicitProperty(ImportedName importedName,
case ImportedAccessorKind::None:
case ImportedAccessorKind::SubscriptGetter:
case ImportedAccessorKind::SubscriptSetter:
case ImportedAccessorKind::DereferenceGetter:
case ImportedAccessorKind::DereferenceSetter:
llvm_unreachable("Not a property accessor");

case ImportedAccessorKind::PropertyGetter:
Expand Down
11 changes: 11 additions & 0 deletions lib/ClangImporter/ImportName.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,8 @@ void ClangImporter::Implementation::printSwiftName(ImportedName name,
bool isSetter = false;
switch (name.getAccessorKind()) {
case ImportedAccessorKind::None:
case ImportedAccessorKind::DereferenceGetter:
case ImportedAccessorKind::DereferenceSetter:
break;

case ImportedAccessorKind::PropertyGetter:
Expand Down Expand Up @@ -1916,6 +1918,15 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
baseName = swiftCtx.getIdentifier(operatorName).str();
isFunction = true;
addEmptyArgNamesForClangFunction(functionDecl, argumentNames);
if (auto cxxMethod = dyn_cast<clang::CXXMethodDecl>(functionDecl)) {
if (op == clang::OverloadedOperatorKind::OO_Star &&
cxxMethod->param_empty()) {
if (cxxMethod->isConst())
result.info.accessorKind = ImportedAccessorKind::DereferenceGetter;
else
result.info.accessorKind = ImportedAccessorKind::DereferenceSetter;
}
}
break;
}
case clang::OverloadedOperatorKind::OO_Call:
Expand Down
23 changes: 23 additions & 0 deletions lib/ClangImporter/ImportName.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ enum class ImportedAccessorKind : unsigned {
PropertySetter,
SubscriptGetter,
SubscriptSetter,
DereferenceGetter,
DereferenceSetter,
};
enum { NumImportedAccessorKindBits = 3 };

Expand Down Expand Up @@ -324,6 +326,8 @@ class ImportedName {
case ImportedAccessorKind::None:
case ImportedAccessorKind::SubscriptGetter:
case ImportedAccessorKind::SubscriptSetter:
case ImportedAccessorKind::DereferenceGetter:
case ImportedAccessorKind::DereferenceSetter:
return false;

case ImportedAccessorKind::PropertyGetter:
Expand All @@ -338,6 +342,8 @@ class ImportedName {
case ImportedAccessorKind::None:
case ImportedAccessorKind::PropertyGetter:
case ImportedAccessorKind::PropertySetter:
case ImportedAccessorKind::DereferenceGetter:
case ImportedAccessorKind::DereferenceSetter:
return false;

case ImportedAccessorKind::SubscriptGetter:
Expand All @@ -347,6 +353,23 @@ class ImportedName {

llvm_unreachable("Invalid ImportedAccessorKind.");
}

bool isDereferenceAccessor() const {
switch (getAccessorKind()) {
case ImportedAccessorKind::None:
case ImportedAccessorKind::PropertyGetter:
case ImportedAccessorKind::PropertySetter:
case ImportedAccessorKind::SubscriptGetter:
case ImportedAccessorKind::SubscriptSetter:
return false;

case ImportedAccessorKind::DereferenceGetter:
case ImportedAccessorKind::DereferenceSetter:
return true;
}

llvm_unreachable("Invalid ImportedAccessorKind.");
}
};

/// Strips a trailing "Notification", if present. Returns {} if name doesn't end
Expand Down
3 changes: 3 additions & 0 deletions lib/ClangImporter/ImporterImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,9 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
llvm::MapVector<std::pair<NominalTypeDecl *, Type>,
std::pair<FuncDecl *, FuncDecl *>> cxxSubscripts;

llvm::MapVector<NominalTypeDecl *, std::pair<FuncDecl *, FuncDecl *>>
cxxDereferenceOperators;

llvm::SmallPtrSet<const clang::Decl *, 1> synthesizedAndAlwaysVisibleDecls;

private:
Expand Down
68 changes: 54 additions & 14 deletions lib/ClangImporter/SwiftDeclSynthesizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1538,17 +1538,22 @@ synthesizeUnwrappingGetterBody(AbstractFunctionDecl *afd, void *context) {
return {body, /*isTypeChecked*/ true};
}

/// Synthesizer callback for a subscript setter.
/// Synthesizer callback for a subscript setter or a setter for a dereference
/// property (`var pointee`).
static std::pair<BraceStmt *, bool>
synthesizeSubscriptSetterBody(AbstractFunctionDecl *afd, void *context) {
synthesizeUnwrappingSetterBody(AbstractFunctionDecl *afd, void *context) {
auto setterDecl = cast<AccessorDecl>(afd);
auto setterImpl = static_cast<FuncDecl *>(context);

ASTContext &ctx = setterDecl->getASTContext();

auto selfArg = createSelfArg(setterDecl);
DeclRefExpr *valueParamRefExpr = createParamRefExpr(setterDecl, 0);
DeclRefExpr *keyParamRefExpr = createParamRefExpr(setterDecl, 1);
// For a subscript this decl will have two parameters, for a pointee property
// it will only have one.
DeclRefExpr *keyParamRefExpr = setterDecl->getParameters()->size() == 1
? nullptr
: createParamRefExpr(setterDecl, 1);

Type elementTy = valueParamRefExpr->getDecl()->getInterfaceType();

Expand Down Expand Up @@ -1644,7 +1649,7 @@ SubscriptDecl *SwiftDeclSynthesizer::makeSubscript(FuncDecl *getter,
setterDecl->setImplicit();
setterDecl->setIsDynamic(false);
setterDecl->setIsTransparent(true);
setterDecl->setBodySynthesizer(synthesizeSubscriptSetterBody, setterImpl);
setterDecl->setBodySynthesizer(synthesizeUnwrappingSetterBody, setterImpl);

if (setterImpl->isMutating()) {
setterDecl->setSelfAccessKind(SelfAccessKind::Mutating);
Expand All @@ -1663,28 +1668,34 @@ SubscriptDecl *SwiftDeclSynthesizer::makeSubscript(FuncDecl *getter,

// MARK: C++ dereference operator

VarDecl *SwiftDeclSynthesizer::makeDereferencedPointeeProperty(
FuncDecl *dereferenceFunc) {
VarDecl *
SwiftDeclSynthesizer::makeDereferencedPointeeProperty(FuncDecl *getter,
FuncDecl *setter) {
assert((getter || setter) &&
"getter or setter required to generate a pointee property");

auto &ctx = ImporterImpl.SwiftContext;
auto dc = dereferenceFunc->getDeclContext();
FuncDecl *getterImpl = getter ? getter : setter;
FuncDecl *setterImpl = setter;
auto dc = getterImpl->getDeclContext();

// Get the return type wrapped in `Unsafe(Mutable)Pointer<T>`.
const auto rawElementTy = dereferenceFunc->getResultInterfaceType();
const auto rawElementTy = getterImpl->getResultInterfaceType();
// Unwrap `T`. Use rawElementTy for return by value.
const auto elementTy = rawElementTy->getAnyPointerElementType()
? rawElementTy->getAnyPointerElementType()
: rawElementTy;

auto result = new (ctx)
VarDecl(/*isStatic*/ false, VarDecl::Introducer::Var,
dereferenceFunc->getStartLoc(), ctx.getIdentifier("pointee"), dc);
getterImpl->getStartLoc(), ctx.getIdentifier("pointee"), dc);
result->setInterfaceType(elementTy);
result->setAccess(AccessLevel::Public);
result->setImplInfo(StorageImplInfo::getImmutableComputed());

AccessorDecl *getterDecl = AccessorDecl::create(
ctx, dereferenceFunc->getLoc(), dereferenceFunc->getLoc(),
AccessorKind::Get, result, SourceLoc(), StaticSpellingKind::None,
ctx, getterImpl->getLoc(), getterImpl->getLoc(), AccessorKind::Get,
result, SourceLoc(), StaticSpellingKind::None,
/*async*/ false, SourceLoc(),
/*throws*/ false, SourceLoc(), ParameterList::createEmpty(ctx), elementTy,
dc);
Expand All @@ -1693,14 +1704,43 @@ VarDecl *SwiftDeclSynthesizer::makeDereferencedPointeeProperty(
getterDecl->setIsDynamic(false);
getterDecl->setIsTransparent(true);
getterDecl->setBodySynthesizer(synthesizeUnwrappingGetterBody,
dereferenceFunc);
getterImpl);

if (dereferenceFunc->isMutating()) {
if (getterImpl->isMutating()) {
getterDecl->setSelfAccessKind(SelfAccessKind::Mutating);
result->setIsGetterMutating(true);
}

ImporterImpl.makeComputed(result, getterDecl, /*setter*/ nullptr);
AccessorDecl *setterDecl = nullptr;
if (setterImpl) {
auto paramVarDecl =
new (ctx) ParamDecl(SourceLoc(), SourceLoc(), Identifier(), SourceLoc(),
ctx.getIdentifier("newValue"), dc);
paramVarDecl->setSpecifier(ParamSpecifier::Default);
paramVarDecl->setInterfaceType(elementTy);

auto setterParamList =
ParameterList::create(ctx, {paramVarDecl});

setterDecl = AccessorDecl::create(
ctx, setterImpl->getLoc(), setterImpl->getLoc(), AccessorKind::Set,
result, SourceLoc(), StaticSpellingKind::None,
/*async*/ false, SourceLoc(),
/*throws*/ false, SourceLoc(), setterParamList,
TupleType::getEmpty(ctx), dc);
setterDecl->setAccess(AccessLevel::Public);
setterDecl->setImplicit();
setterDecl->setIsDynamic(false);
setterDecl->setIsTransparent(true);
setterDecl->setBodySynthesizer(synthesizeUnwrappingSetterBody, setterImpl);

if (setterImpl->isMutating()) {
setterDecl->setSelfAccessKind(SelfAccessKind::Mutating);
result->setIsSetterMutating(true);
}
}

ImporterImpl.makeComputed(result, getterDecl, setterDecl);
return result;
}

Expand Down
5 changes: 3 additions & 2 deletions lib/ClangImporter/SwiftDeclSynthesizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -275,9 +275,10 @@ class SwiftDeclSynthesizer {
/// Given an imported C++ dereference operator (`operator*()`), create a
/// `pointee` computed property.
///
/// \param dereferenceFunc function returning `Unsafe(Mutable)?Pointer<T>`
/// \param getter function returning `UnsafePointer<T>`
/// \param setter function returning `UnsafeMutablePointer<T>`
/// \return computed property declaration
VarDecl *makeDereferencedPointeeProperty(FuncDecl *dereferenceFunc);
VarDecl *makeDereferencedPointeeProperty(FuncDecl *getter, FuncDecl *setter);

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