Skip to content

[cxx-interop] Import using decls that refer to member operators of a base class #69991

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 2 commits into from
Dec 5, 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
126 changes: 71 additions & 55 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2294,56 +2294,6 @@ 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::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.
if (cxxMethod->param_empty() && !isa<ClassDecl>(result)) {
// This is a pre-increment operator. We synthesize a
// non-mutating function called `successor() -> Self`.
FuncDecl *successorFunc = synthesizer.makeSuccessorFunc(MD);
result->addMember(successorFunc);

Impl.markUnavailable(MD, "use .successor()");
} else {
Impl.markUnavailable(MD, "unable to create .successor() func");
}
MD->overwriteAccess(AccessLevel::Private);
}
// Check if this method _is_ an overloaded operator but is not a
// call / subscript / dereference / increment. Those
// operators do not need static versions.
else if (cxxOperatorKind !=
clang::OverloadedOperatorKind::OO_None &&
cxxOperatorKind !=
clang::OverloadedOperatorKind::OO_PlusPlus &&
cxxOperatorKind !=
clang::OverloadedOperatorKind::OO_Call &&
!methodImportedName.isSubscriptAccessor() &&
!methodImportedName.isDereferenceAccessor()) {

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

Impl.addAlternateDecl(MD, opFuncDecl);

auto msg = "use " + std::string{clang::getOperatorSpelling(cxxOperatorKind)} + " instead";
Impl.markUnavailable(MD,msg);

// Make the actual member operator private.
MD->overwriteAccess(AccessLevel::Private);

// Make sure the synthesized decl can be found by lookupDirect.
result->addMemberToLookupTable(opFuncDecl);

addEntryToLookupTable(*Impl.findLookupTable(decl), cxxMethod,
Impl.getNameImporter());
}
}
methods.push_back(MD);
continue;
}
Expand Down Expand Up @@ -3005,6 +2955,21 @@ namespace {
if (isSpecializationDepthGreaterThan(def, 8))
return nullptr;

// For class template instantiations, we need to add their member
// operators to the lookup table to make them discoverable with
// unqualified lookup. This makes it possible to implement a Swift
// protocol requirement with an instantiation of a C++ member operator.
// This cannot be done when building the lookup table,
// because templates are instantiated lazily.
for (auto member : def->decls()) {
if (auto method = dyn_cast<clang::CXXMethodDecl>(member)) {
if (method->isOverloadedOperator()) {
addEntryToLookupTable(*Impl.findLookupTable(decl), method,
Impl.getNameImporter());
}
}
}

return VisitCXXRecordDecl(def);
}

Expand Down Expand Up @@ -3263,12 +3228,19 @@ namespace {
}

/// Handles special functions such as subscripts and dereference operators.
bool processSpecialImportedFunc(FuncDecl *func, ImportedName importedName) {
bool
processSpecialImportedFunc(FuncDecl *func, ImportedName importedName,
clang::OverloadedOperatorKind cxxOperatorKind) {
if (cxxOperatorKind == clang::OverloadedOperatorKind::OO_None)
return true;

auto dc = func->getDeclContext();
auto typeDecl = dc->getSelfNominalTypeDecl();
if (!typeDecl)
return true;

if (importedName.isSubscriptAccessor()) {
assert(func->getParameters()->size() == 1);
auto typeDecl = dc->getSelfNominalTypeDecl();
auto parameter = func->getParameters()->get(0);
auto parameterType = parameter->getTypeInContext();
if (!typeDecl || !parameterType)
Expand Down Expand Up @@ -3298,10 +3270,10 @@ namespace {
}

Impl.markUnavailable(func, "use subscript");
return true;
}

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

switch (importedName.getAccessorKind()) {
Expand All @@ -3316,6 +3288,42 @@ namespace {
}

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

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.
if (func->getParameters()->size() == 0 && !isa<ClassDecl>(typeDecl)) {
// This is a pre-increment operator. We synthesize a
// non-mutating function called `successor() -> Self`.
FuncDecl *successorFunc = synthesizer.makeSuccessorFunc(func);
typeDecl->addMember(successorFunc);

Impl.markUnavailable(func, "use .successor()");
} else {
Impl.markUnavailable(func, "unable to create .successor() func");
}
func->overwriteAccess(AccessLevel::Private);
return true;
}

// Check if this method _is_ an overloaded operator but is not a
// call / subscript / dereference / increment. Those
// operators do not need static versions.
if (cxxOperatorKind != clang::OverloadedOperatorKind::OO_Call) {
auto opFuncDecl = synthesizer.makeOperator(func, cxxOperatorKind);
Impl.addAlternateDecl(func, opFuncDecl);

Impl.markUnavailable(
func, (Twine("use ") + clang::getOperatorSpelling(cxxOperatorKind) +
" instead")
.str());

// Make sure the synthesized decl can be found by lookupDirect.
typeDecl->addMemberToLookupTable(opFuncDecl);
return true;
}

return true;
Expand Down Expand Up @@ -3644,7 +3652,8 @@ namespace {
func->setAccess(AccessLevel::Public);

if (!importFuncWithoutSignature) {
bool success = processSpecialImportedFunc(func, importedName);
bool success = processSpecialImportedFunc(
func, importedName, decl->getOverloadedOperator());
if (!success)
return nullptr;
}
Expand Down Expand Up @@ -4000,6 +4009,12 @@ namespace {
if (!importedDC)
return nullptr;

// While importing the DeclContext, we might have imported the decl
// itself.
auto known = Impl.importDeclCached(decl, getVersion());
if (known.has_value())
return known.value();

if (isa<clang::TypeDecl>(decl->getTargetDecl())) {
Decl *SwiftDecl = Impl.importDecl(decl->getUnderlyingDecl(), getActiveSwiftVersion());
if (!SwiftDecl)
Expand Down Expand Up @@ -4048,7 +4063,8 @@ namespace {
if (!clonedMethod)
return nullptr;

bool success = processSpecialImportedFunc(clonedMethod, importedName);
bool success = processSpecialImportedFunc(
clonedMethod, importedName, targetMethod->getOverloadedOperator());
if (!success)
return nullptr;

Expand Down
7 changes: 3 additions & 4 deletions lib/ClangImporter/SwiftDeclSynthesizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1910,9 +1910,7 @@ synthesizeOperatorMethodBody(AbstractFunctionDecl *afd, void *context) {

FuncDecl *
SwiftDeclSynthesizer::makeOperator(FuncDecl *operatorMethod,
clang::CXXMethodDecl *clangOperator) {
clang::OverloadedOperatorKind opKind = clangOperator->getOverloadedOperator();

clang::OverloadedOperatorKind opKind) {
assert(opKind != clang::OverloadedOperatorKind::OO_None &&
"expected a C++ operator");

Expand Down Expand Up @@ -1970,7 +1968,8 @@ SwiftDeclSynthesizer::makeOperator(FuncDecl *operatorMethod,
operatorMethod);

// If this is a unary prefix operator (e.g. `!`), add a `prefix` attribute.
if (clangOperator->param_empty()) {
size_t numParams = operatorMethod->getParameters()->size();
if (numParams == 0 || (operatorMethod->isStatic() && numParams == 1)) {
topLevelStaticFuncDecl->getAttrs().add(new (ctx) PrefixAttr(SourceLoc()));
}

Expand Down
2 changes: 1 addition & 1 deletion lib/ClangImporter/SwiftDeclSynthesizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ class SwiftDeclSynthesizer {
FuncDecl *makeSuccessorFunc(FuncDecl *incrementFunc);

FuncDecl *makeOperator(FuncDecl *operatorMethod,
clang::CXXMethodDecl *clangOperator);
clang::OverloadedOperatorKind opKind);

VarDecl *makeComputedPropertyFromCXXMethods(FuncDecl *getter,
FuncDecl *setter);
Expand Down
12 changes: 12 additions & 0 deletions test/Interop/Cxx/operators/Inputs/member-inline.h
Original file line number Diff line number Diff line change
Expand Up @@ -440,4 +440,16 @@ struct DerivedFromAmbiguousOperatorStarPrivatelyWithUsingDecl
using AmbiguousOperatorStar::operator*;
};

struct DerivedFromLoadableIntWrapperWithUsingDecl : private LoadableIntWrapper {
using LoadableIntWrapper::operator-;
using LoadableIntWrapper::operator+=;

int getValue() const {
return value;
}
void setValue(int v) {
this->value = v;
}
};

#endif
12 changes: 12 additions & 0 deletions test/Interop/Cxx/operators/member-inline-module-interface.swift
Original file line number Diff line number Diff line change
Expand Up @@ -265,3 +265,15 @@
// CHECK-NEXT: @available(*, unavailable, message: "use .pointee property")
// CHECK-NEXT: func __operatorStar() -> UnsafePointer<Int32>
// CHECK-NEXT: }

// CHECK: struct DerivedFromLoadableIntWrapperWithUsingDecl {
// CHECK-NEXT: init()
// CHECK-NEXT: static func - (lhs: inout DerivedFromLoadableIntWrapperWithUsingDecl, rhs: LoadableIntWrapper) -> LoadableIntWrapper
// CHECK-NEXT: @available(*, unavailable, message: "use - instead")
// CHECK-NEXT: mutating func __operatorMinus(_ rhs: LoadableIntWrapper) -> LoadableIntWrapper
// CHECK-NEXT: static func += (lhs: inout DerivedFromLoadableIntWrapperWithUsingDecl, rhs: LoadableIntWrapper)
// CHECK-NEXT: @available(*, unavailable, message: "use += instead")
// CHECK-NEXT: mutating func __operatorPlusEqual(_ rhs: LoadableIntWrapper)
// CHECK-NEXT: func getValue() -> Int32
// CHECK-NEXT: mutating func setValue(_ v: Int32)
// CHECK-NEXT: }
3 changes: 3 additions & 0 deletions test/Interop/Cxx/operators/member-inline-typechecker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,6 @@ derivedConstIter.pointee // expected-error {{value of type 'DerivedFromConstIter

let derivedConstIterWithUD = DerivedFromConstIteratorPrivatelyWithUsingDecl()
let _ = derivedConstIterWithUD.pointee

var derivedIntWrapper = DerivedFromLoadableIntWrapperWithUsingDecl()
derivedIntWrapper += LoadableIntWrapper()
9 changes: 9 additions & 0 deletions test/Interop/Cxx/operators/member-inline.swift
Original file line number Diff line number Diff line change
Expand Up @@ -395,4 +395,13 @@ OperatorsTestSuite.test("DerivedFromAmbiguousOperatorStarPrivatelyWithUsingDecl.
expectEqual(567, res)
}

OperatorsTestSuite.test("DerivedFromLoadableIntWrapperWithUsingDecl") {
var d = DerivedFromLoadableIntWrapperWithUsingDecl()
d.setValue(123)
var d1 = LoadableIntWrapper()
d1.value = 543
d += d1
expectEqual(666, d.getValue())
}

runAllTests()