Skip to content

Commit 8c049fd

Browse files
authored
Merge pull request #69991 from apple/egorzhdan/using-arithmetic-op
[cxx-interop] Import using decls that refer to member operators of a base class
2 parents 55032ea + ece33a4 commit 8c049fd

File tree

7 files changed

+111
-60
lines changed

7 files changed

+111
-60
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 71 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2294,56 +2294,6 @@ namespace {
22942294
}
22952295

22962296
if (auto MD = dyn_cast<FuncDecl>(member)) {
2297-
if (auto cxxMethod = dyn_cast<clang::CXXMethodDecl>(m)) {
2298-
ImportedName methodImportedName =
2299-
Impl.importFullName(cxxMethod, getActiveSwiftVersion());
2300-
auto cxxOperatorKind = cxxMethod->getOverloadedOperator();
2301-
2302-
if (cxxOperatorKind == clang::OverloadedOperatorKind::OO_PlusPlus) {
2303-
// Make sure the type is not a foreign reference type.
2304-
// We cannot handle `operator++` for those types, since the
2305-
// current implementation creates a new instance of the type.
2306-
if (cxxMethod->param_empty() && !isa<ClassDecl>(result)) {
2307-
// This is a pre-increment operator. We synthesize a
2308-
// non-mutating function called `successor() -> Self`.
2309-
FuncDecl *successorFunc = synthesizer.makeSuccessorFunc(MD);
2310-
result->addMember(successorFunc);
2311-
2312-
Impl.markUnavailable(MD, "use .successor()");
2313-
} else {
2314-
Impl.markUnavailable(MD, "unable to create .successor() func");
2315-
}
2316-
MD->overwriteAccess(AccessLevel::Private);
2317-
}
2318-
// Check if this method _is_ an overloaded operator but is not a
2319-
// call / subscript / dereference / increment. Those
2320-
// operators do not need static versions.
2321-
else if (cxxOperatorKind !=
2322-
clang::OverloadedOperatorKind::OO_None &&
2323-
cxxOperatorKind !=
2324-
clang::OverloadedOperatorKind::OO_PlusPlus &&
2325-
cxxOperatorKind !=
2326-
clang::OverloadedOperatorKind::OO_Call &&
2327-
!methodImportedName.isSubscriptAccessor() &&
2328-
!methodImportedName.isDereferenceAccessor()) {
2329-
2330-
auto opFuncDecl = synthesizer.makeOperator(MD, cxxMethod);
2331-
2332-
Impl.addAlternateDecl(MD, opFuncDecl);
2333-
2334-
auto msg = "use " + std::string{clang::getOperatorSpelling(cxxOperatorKind)} + " instead";
2335-
Impl.markUnavailable(MD,msg);
2336-
2337-
// Make the actual member operator private.
2338-
MD->overwriteAccess(AccessLevel::Private);
2339-
2340-
// Make sure the synthesized decl can be found by lookupDirect.
2341-
result->addMemberToLookupTable(opFuncDecl);
2342-
2343-
addEntryToLookupTable(*Impl.findLookupTable(decl), cxxMethod,
2344-
Impl.getNameImporter());
2345-
}
2346-
}
23472297
methods.push_back(MD);
23482298
continue;
23492299
}
@@ -3005,6 +2955,21 @@ namespace {
30052955
if (isSpecializationDepthGreaterThan(def, 8))
30062956
return nullptr;
30072957

2958+
// For class template instantiations, we need to add their member
2959+
// operators to the lookup table to make them discoverable with
2960+
// unqualified lookup. This makes it possible to implement a Swift
2961+
// protocol requirement with an instantiation of a C++ member operator.
2962+
// This cannot be done when building the lookup table,
2963+
// because templates are instantiated lazily.
2964+
for (auto member : def->decls()) {
2965+
if (auto method = dyn_cast<clang::CXXMethodDecl>(member)) {
2966+
if (method->isOverloadedOperator()) {
2967+
addEntryToLookupTable(*Impl.findLookupTable(decl), method,
2968+
Impl.getNameImporter());
2969+
}
2970+
}
2971+
}
2972+
30082973
return VisitCXXRecordDecl(def);
30092974
}
30102975

@@ -3263,12 +3228,19 @@ namespace {
32633228
}
32643229

32653230
/// Handles special functions such as subscripts and dereference operators.
3266-
bool processSpecialImportedFunc(FuncDecl *func, ImportedName importedName) {
3231+
bool
3232+
processSpecialImportedFunc(FuncDecl *func, ImportedName importedName,
3233+
clang::OverloadedOperatorKind cxxOperatorKind) {
3234+
if (cxxOperatorKind == clang::OverloadedOperatorKind::OO_None)
3235+
return true;
3236+
32673237
auto dc = func->getDeclContext();
3238+
auto typeDecl = dc->getSelfNominalTypeDecl();
3239+
if (!typeDecl)
3240+
return true;
32683241

32693242
if (importedName.isSubscriptAccessor()) {
32703243
assert(func->getParameters()->size() == 1);
3271-
auto typeDecl = dc->getSelfNominalTypeDecl();
32723244
auto parameter = func->getParameters()->get(0);
32733245
auto parameterType = parameter->getTypeInContext();
32743246
if (!typeDecl || !parameterType)
@@ -3298,10 +3270,10 @@ namespace {
32983270
}
32993271

33003272
Impl.markUnavailable(func, "use subscript");
3273+
return true;
33013274
}
33023275

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

33073279
switch (importedName.getAccessorKind()) {
@@ -3316,6 +3288,42 @@ namespace {
33163288
}
33173289

33183290
Impl.markUnavailable(func, "use .pointee property");
3291+
return true;
3292+
}
3293+
3294+
if (cxxOperatorKind == clang::OverloadedOperatorKind::OO_PlusPlus) {
3295+
// Make sure the type is not a foreign reference type.
3296+
// We cannot handle `operator++` for those types, since the
3297+
// current implementation creates a new instance of the type.
3298+
if (func->getParameters()->size() == 0 && !isa<ClassDecl>(typeDecl)) {
3299+
// This is a pre-increment operator. We synthesize a
3300+
// non-mutating function called `successor() -> Self`.
3301+
FuncDecl *successorFunc = synthesizer.makeSuccessorFunc(func);
3302+
typeDecl->addMember(successorFunc);
3303+
3304+
Impl.markUnavailable(func, "use .successor()");
3305+
} else {
3306+
Impl.markUnavailable(func, "unable to create .successor() func");
3307+
}
3308+
func->overwriteAccess(AccessLevel::Private);
3309+
return true;
3310+
}
3311+
3312+
// Check if this method _is_ an overloaded operator but is not a
3313+
// call / subscript / dereference / increment. Those
3314+
// operators do not need static versions.
3315+
if (cxxOperatorKind != clang::OverloadedOperatorKind::OO_Call) {
3316+
auto opFuncDecl = synthesizer.makeOperator(func, cxxOperatorKind);
3317+
Impl.addAlternateDecl(func, opFuncDecl);
3318+
3319+
Impl.markUnavailable(
3320+
func, (Twine("use ") + clang::getOperatorSpelling(cxxOperatorKind) +
3321+
" instead")
3322+
.str());
3323+
3324+
// Make sure the synthesized decl can be found by lookupDirect.
3325+
typeDecl->addMemberToLookupTable(opFuncDecl);
3326+
return true;
33193327
}
33203328

33213329
return true;
@@ -3644,7 +3652,8 @@ namespace {
36443652
func->setAccess(AccessLevel::Public);
36453653

36463654
if (!importFuncWithoutSignature) {
3647-
bool success = processSpecialImportedFunc(func, importedName);
3655+
bool success = processSpecialImportedFunc(
3656+
func, importedName, decl->getOverloadedOperator());
36483657
if (!success)
36493658
return nullptr;
36503659
}
@@ -4024,6 +4033,12 @@ namespace {
40244033
if (!importedDC)
40254034
return nullptr;
40264035

4036+
// While importing the DeclContext, we might have imported the decl
4037+
// itself.
4038+
auto known = Impl.importDeclCached(decl, getVersion());
4039+
if (known.has_value())
4040+
return known.value();
4041+
40274042
if (isa<clang::TypeDecl>(decl->getTargetDecl())) {
40284043
Decl *SwiftDecl = Impl.importDecl(decl->getUnderlyingDecl(), getActiveSwiftVersion());
40294044
if (!SwiftDecl)
@@ -4072,7 +4087,8 @@ namespace {
40724087
if (!clonedMethod)
40734088
return nullptr;
40744089

4075-
bool success = processSpecialImportedFunc(clonedMethod, importedName);
4090+
bool success = processSpecialImportedFunc(
4091+
clonedMethod, importedName, targetMethod->getOverloadedOperator());
40764092
if (!success)
40774093
return nullptr;
40784094

lib/ClangImporter/SwiftDeclSynthesizer.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1910,9 +1910,7 @@ synthesizeOperatorMethodBody(AbstractFunctionDecl *afd, void *context) {
19101910

19111911
FuncDecl *
19121912
SwiftDeclSynthesizer::makeOperator(FuncDecl *operatorMethod,
1913-
clang::CXXMethodDecl *clangOperator) {
1914-
clang::OverloadedOperatorKind opKind = clangOperator->getOverloadedOperator();
1915-
1913+
clang::OverloadedOperatorKind opKind) {
19161914
assert(opKind != clang::OverloadedOperatorKind::OO_None &&
19171915
"expected a C++ operator");
19181916

@@ -1970,7 +1968,8 @@ SwiftDeclSynthesizer::makeOperator(FuncDecl *operatorMethod,
19701968
operatorMethod);
19711969

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

lib/ClangImporter/SwiftDeclSynthesizer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ class SwiftDeclSynthesizer {
285285
FuncDecl *makeSuccessorFunc(FuncDecl *incrementFunc);
286286

287287
FuncDecl *makeOperator(FuncDecl *operatorMethod,
288-
clang::CXXMethodDecl *clangOperator);
288+
clang::OverloadedOperatorKind opKind);
289289

290290
VarDecl *makeComputedPropertyFromCXXMethods(FuncDecl *getter,
291291
FuncDecl *setter);

test/Interop/Cxx/operators/Inputs/member-inline.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,4 +440,16 @@ struct DerivedFromAmbiguousOperatorStarPrivatelyWithUsingDecl
440440
using AmbiguousOperatorStar::operator*;
441441
};
442442

443+
struct DerivedFromLoadableIntWrapperWithUsingDecl : private LoadableIntWrapper {
444+
using LoadableIntWrapper::operator-;
445+
using LoadableIntWrapper::operator+=;
446+
447+
int getValue() const {
448+
return value;
449+
}
450+
void setValue(int v) {
451+
this->value = v;
452+
}
453+
};
454+
443455
#endif

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,3 +265,15 @@
265265
// CHECK-NEXT: @available(*, unavailable, message: "use .pointee property")
266266
// CHECK-NEXT: func __operatorStar() -> UnsafePointer<Int32>
267267
// CHECK-NEXT: }
268+
269+
// CHECK: struct DerivedFromLoadableIntWrapperWithUsingDecl {
270+
// CHECK-NEXT: init()
271+
// CHECK-NEXT: static func - (lhs: inout DerivedFromLoadableIntWrapperWithUsingDecl, rhs: LoadableIntWrapper) -> LoadableIntWrapper
272+
// CHECK-NEXT: @available(*, unavailable, message: "use - instead")
273+
// CHECK-NEXT: mutating func __operatorMinus(_ rhs: LoadableIntWrapper) -> LoadableIntWrapper
274+
// CHECK-NEXT: static func += (lhs: inout DerivedFromLoadableIntWrapperWithUsingDecl, rhs: LoadableIntWrapper)
275+
// CHECK-NEXT: @available(*, unavailable, message: "use += instead")
276+
// CHECK-NEXT: mutating func __operatorPlusEqual(_ rhs: LoadableIntWrapper)
277+
// CHECK-NEXT: func getValue() -> Int32
278+
// CHECK-NEXT: mutating func setValue(_ v: Int32)
279+
// CHECK-NEXT: }

test/Interop/Cxx/operators/member-inline-typechecker.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,6 @@ derivedConstIter.pointee // expected-error {{value of type 'DerivedFromConstIter
7373

7474
let derivedConstIterWithUD = DerivedFromConstIteratorPrivatelyWithUsingDecl()
7575
let _ = derivedConstIterWithUD.pointee
76+
77+
var derivedIntWrapper = DerivedFromLoadableIntWrapperWithUsingDecl()
78+
derivedIntWrapper += LoadableIntWrapper()

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,4 +395,13 @@ OperatorsTestSuite.test("DerivedFromAmbiguousOperatorStarPrivatelyWithUsingDecl.
395395
expectEqual(567, res)
396396
}
397397

398+
OperatorsTestSuite.test("DerivedFromLoadableIntWrapperWithUsingDecl") {
399+
var d = DerivedFromLoadableIntWrapperWithUsingDecl()
400+
d.setValue(123)
401+
var d1 = LoadableIntWrapper()
402+
d1.value = 543
403+
d += d1
404+
expectEqual(666, d.getValue())
405+
}
406+
398407
runAllTests()

0 commit comments

Comments
 (0)