Skip to content

Commit 18c6bdd

Browse files
committed
[Cxx iterop] Disambiguate between methods with the same name but different constness
1 parent f8f8e16 commit 18c6bdd

File tree

8 files changed

+194
-11
lines changed

8 files changed

+194
-11
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3438,9 +3438,29 @@ namespace {
34383438
SmallVector<FuncDecl *, 4> methods;
34393439
SmallVector<ConstructorDecl *, 4> ctors;
34403440

3441+
// Cxx methods may have the same name but differ in "constness".
3442+
// In such a case we must differentiate in swift (See VisitFunction).
3443+
// Before importing the different CXXMethodDecl's we track functions
3444+
// that differ this way so we can disambiguate later
3445+
for (auto m : decl->decls()) {
3446+
if (auto method = dyn_cast<clang::CXXMethodDecl>(m)) {
3447+
if(method->getDeclName().isIdentifier()) {
3448+
if(Impl.cxxMethods.find(method->getName()) == Impl.cxxMethods.end()) {
3449+
Impl.cxxMethods[method->getName()] = {};
3450+
}
3451+
if(method->isConst()) {
3452+
// Add to const set
3453+
Impl.cxxMethods[method->getName()].first.insert(method);
3454+
} else {
3455+
// Add to mutable set
3456+
Impl.cxxMethods[method->getName()].second.insert(method);
3457+
}
3458+
}
3459+
}
3460+
}
3461+
34413462
// FIXME: Import anonymous union fields and support field access when
34423463
// it is nested in a struct.
3443-
34443464
for (auto m : decl->decls()) {
34453465
if (isa<clang::AccessSpecDecl>(m)) {
34463466
// The presence of AccessSpecDecls themselves does not influence
@@ -3983,6 +4003,25 @@ namespace {
39834003
if (!dc)
39844004
return nullptr;
39854005

4006+
// Handle cases where 2 CXX methods differ strictly in "constness"
4007+
// In such a case append a suffix ("Mutating") to the mutable version
4008+
// of the method when importing to swift
4009+
if(decl->getDeclName().isIdentifier()) {
4010+
const auto &cxxMethodPair = Impl.cxxMethods[decl->getName()];
4011+
const auto &constFuncPtrs = cxxMethodPair.first;
4012+
const auto &mutFuncPtrs = cxxMethodPair.second;
4013+
4014+
// Check to see if this function has both const & mut versions and
4015+
// that this decl refers to the mutable version.
4016+
if (!constFuncPtrs.empty() && mutFuncPtrs.contains(decl)) {
4017+
auto newName = decl->getName().str() + "Mutating";
4018+
auto newId = dc->getASTContext().getIdentifier(newName);
4019+
auto oldArgNames = importedName.getDeclName().getArgumentNames();
4020+
auto newDeclName = DeclName(Impl.SwiftContext, newId, oldArgNames);
4021+
importedName.setDeclName(newDeclName);
4022+
}
4023+
}
4024+
39864025
DeclName name = accessorInfo ? DeclName() : importedName.getDeclName();
39874026
auto selfIdx = importedName.getSelfIndex();
39884027

lib/ClangImporter/ImporterImpl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,10 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
569569
llvm::MapVector<std::pair<NominalTypeDecl *, Type>,
570570
std::pair<FuncDecl *, FuncDecl *>> cxxSubscripts;
571571

572+
/// Keep track of cxx function names, params etc in order to
573+
/// allow for de-duping functions that differ strictly on "constness".
574+
llvm::DenseMap<llvm::StringRef, std::pair<llvm::DenseSet<clang::FunctionDecl *>, llvm::DenseSet<clang::FunctionDecl *>>> cxxMethods;
575+
572576
/// Keeps track of the Clang functions that have been turned into
573577
/// properties.
574578
llvm::DenseMap<const clang::FunctionDecl *, VarDecl *> FunctionsAsProperties;
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#ifndef TEST_INTEROP_CXX_CLASS_AMBIGUOUS_METHOD_METHODS_H
2+
#define TEST_INTEROP_CXX_CLASS_AMBIGUOUS_METHOD_METHODS_H
3+
4+
struct HasAmbiguousMethods {
5+
6+
// No input
7+
void ping() { ++mutableMethodsCalledCount; }
8+
void ping() const {}
9+
10+
// One input
11+
int increment(int a) {
12+
++mutableMethodsCalledCount;
13+
return a + 1;
14+
}
15+
16+
int increment(int a) const {
17+
return a + 1;
18+
}
19+
20+
// Multiple input with out param
21+
void increment(int a, int b, int &c) {
22+
++mutableMethodsCalledCount;
23+
c = a + b;
24+
}
25+
26+
void increment(int a, int b, int &c) const {
27+
c = a + b;
28+
}
29+
30+
// Multiple input with inout param
31+
void increment(int &a, int b) {
32+
++mutableMethodsCalledCount;
33+
a += b;
34+
}
35+
36+
void increment(int &a, int b) const {
37+
a += b;
38+
}
39+
40+
// No input with output
41+
int numberOfMutableMethodsCalled() { return ++mutableMethodsCalledCount; }
42+
int numberOfMutableMethodsCalled() const { return mutableMethodsCalledCount; }
43+
44+
private:
45+
int mutableMethodsCalledCount = 0;
46+
};
47+
48+
#endif // TEST_INTEROP_CXX_CLASS_AMBIGUOUS_METHOD_METHODS_H

test/Interop/Cxx/class/method/Inputs/methods.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@ struct HasMethods {
2525

2626
NonTrivialInWrapper nonConstPassThroughAsWrapper(int a) { return {a}; }
2727
NonTrivialInWrapper constPassThroughAsWrapper(int a) const { return {a}; }
28-
29-
int increment(int a) { return a + 1; }
30-
int increment(int a) const { return a + 1; }
3128
};
3229

3330
#endif // TEST_INTEROP_CXX_CLASS_METHOD_METHODS_H
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
module Methods {
22
header "methods.h"
33
requires cplusplus
4+
}
5+
6+
module AmbiguousMethods {
7+
header "ambiguous_methods.h"
8+
requires cplusplus
49
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %target-swift-ide-test -print-module -module-to-print=AmbiguousMethods -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s
2+
3+
// CHECK: mutating func pingMutating()
4+
// CHECK: func ping()
5+
6+
// CHECK: mutating func incrementMutating(_ a: Int32) -> Int32
7+
// CHECK: func increment(_ a: Int32) -> Int32
8+
9+
// CHECK: mutating func incrementMutating(_ a: Int32, _ b: Int32, _ c: inout Int32)
10+
// CHECK: func increment(_ a: Int32, _ b: Int32, _ c: inout Int32)
11+
12+
// CHECK: mutating func incrementMutating(_ a: inout Int32, _ b: Int32)
13+
// CHECK: func increment(_ a: inout Int32, _ b: Int32)
14+
15+
// CHECK: mutating func numberOfMutableMethodsCalledMutating() -> Int32
16+
// CHECK: func numberOfMutableMethodsCalled() -> Int32
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// RUN: %target-run-simple-swift(-I %S/Inputs/ -Xfrontend -enable-cxx-interop)
2+
//
3+
// REQUIRES: executable_test
4+
//
5+
// Crash when running on windows: rdar://88391102
6+
// XFAIL: OS=windows-msvc
7+
8+
import StdlibUnittest
9+
import AmbiguousMethods
10+
11+
var CxxAmbiguousMethodTestSuite = TestSuite("CxxAmbiguousMethods")
12+
13+
CxxAmbiguousMethodTestSuite.test("numberOfMutableMethodsCalled: () -> Int") {
14+
var instance = HasAmbiguousMethods()
15+
16+
// Sanity check. Make sure we start at 0
17+
expectEqual(0, instance.numberOfMutableMethodsCalled())
18+
19+
// Make sure calling numberOfMutableMethodsCalled above didn't
20+
// change the count
21+
expectEqual(0, instance.numberOfMutableMethodsCalled())
22+
23+
// Check that mutable version _does_ change the mutable call count
24+
expectEqual(1, instance.numberOfMutableMethodsCalledMutating())
25+
26+
expectEqual(1, instance.numberOfMutableMethodsCalled())
27+
}
28+
29+
CxxAmbiguousMethodTestSuite.test("Basic Increment: (Int) -> Int") {
30+
var instance = HasAmbiguousMethods()
31+
var a: Int32 = 0
32+
33+
// Sanity check. Make sure we start at 0
34+
expectEqual(0, instance.numberOfMutableMethodsCalled())
35+
36+
// Non mutable version should NOT change count
37+
a = instance.increment(a);
38+
expectEqual(1, a)
39+
expectEqual(0, instance.numberOfMutableMethodsCalled())
40+
41+
a = instance.incrementMutating(a);
42+
expectEqual(2, a)
43+
expectEqual(1, instance.numberOfMutableMethodsCalled())
44+
}
45+
46+
CxxAmbiguousMethodTestSuite.test("Out Param Increment: (Int, Int, inout Int) -> Void") {
47+
var instance = HasAmbiguousMethods()
48+
var out: Int32 = 0
49+
50+
// Sanity check. Make sure we start at 0
51+
expectEqual(0, instance.numberOfMutableMethodsCalled())
52+
53+
// Non mutable version should NOT change count
54+
instance.increment(0, 1, &out);
55+
expectEqual(1, out)
56+
expectEqual(0, instance.numberOfMutableMethodsCalled())
57+
58+
instance.incrementMutating(5, 2, &out);
59+
expectEqual(7, out)
60+
expectEqual(1, instance.numberOfMutableMethodsCalled())
61+
}
62+
63+
CxxAmbiguousMethodTestSuite.test("Inout Param Increment: (inout Int, Int) -> Void") {
64+
var instance = HasAmbiguousMethods()
65+
var inoutVal: Int32 = 0
66+
67+
// Sanity check. Make sure we start at 0
68+
expectEqual(0, instance.numberOfMutableMethodsCalled())
69+
70+
// Non mutable version should NOT change count
71+
instance.increment(&inoutVal, 1);
72+
expectEqual(1, inoutVal)
73+
expectEqual(0, instance.numberOfMutableMethodsCalled())
74+
75+
instance.incrementMutating(&inoutVal, 2);
76+
expectEqual(3, inoutVal)
77+
expectEqual(1, instance.numberOfMutableMethodsCalled())
78+
}
79+
80+
runAllTests()

test/Interop/Cxx/class/method/methods-module-interface.swift

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,4 @@
1616
// CHECK: func constSumAsWrapper(_ a: NonTrivialInWrapper, _ b: NonTrivialInWrapper) -> NonTrivialInWrapper
1717

1818
// CHECK: mutating func nonConstPassThroughAsWrapper(_ a: Int32) -> NonTrivialInWrapper
19-
// CHECK: func constPassThroughAsWrapper(_ a: Int32) -> NonTrivialInWrapper
20-
21-
// CHECK: mutating func incrementMutable(_ a: Int32) -> Int32
22-
// CHECK: func incrementMutable(_ a: Int32) -> Int32
23-
24-
// CHECK: const func increment(_ a: Int32) -> Int32
25-
// CHECK: func increment(_ a: Int32) -> Int32
19+
// CHECK: func constPassThroughAsWrapper(_ a: Int32) -> NonTrivialInWrapper

0 commit comments

Comments
 (0)