Skip to content

Commit 2d67ab2

Browse files
committed
[cxx-interop] Import non-member operators as global functions
If a non-member operator is declared in a C++ namespace, we previously imported it as a static member of the enum that represents the C++ namespace. This is not always correct under Swift rules for operators. In pure Swift, this code is valid: ``` public protocol UnsafeCxxRandomAccessIterator { static func +=(lhs: inout Self, rhs: Int) } enum std { public struct A : UnsafeCxxRandomAccessIterator { public static func += (lhs: inout A, rhs: Int) { } } } ``` but this is not valid: ``` public protocol UnsafeCxxRandomAccessIterator { static func +=(lhs: inout Self, rhs: Int) } enum std { public struct A : UnsafeCxxRandomAccessIterator {} public static func += (lhs: inout A, rhs: Int) {} } // error: Member operator '+=' must have at least one argument of type 'std' ``` This caused assertion failures in SILGen when conforming C++ iterator types to `UnsafeCxxRandomAccessIterator`.
1 parent abfd790 commit 2d67ab2

File tree

3 files changed

+35
-7
lines changed

3 files changed

+35
-7
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8241,13 +8241,19 @@ ClangImporter::Implementation::importDeclContextOf(
82418241
if (dc->getDeclKind() == clang::Decl::LinkageSpec)
82428242
dc = dc->getParent();
82438243

8244-
// Treat friend decls like top-level decls.
82458244
if (auto functionDecl = dyn_cast<clang::FunctionDecl>(decl)) {
8245+
// Treat friend decls like top-level decls.
82468246
if (functionDecl->getFriendObjectKind()) {
82478247
// Find the top-level decl context.
82488248
while (isa<clang::NamedDecl>(dc))
82498249
dc = dc->getParent();
82508250
}
8251+
8252+
// If this is a non-member operator, import it as a top-level function.
8253+
if (functionDecl->isOverloadedOperator()) {
8254+
while (dc->isNamespace())
8255+
dc = dc->getParent();
8256+
}
82518257
}
82528258

82538259
if (dc->isTranslationUnit()) {

test/Interop/Cxx/namespace/Inputs/free-functions.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,13 @@ inline const char *sameNameInSibling() {
6060
}
6161
} // namespace FunctionsNS4
6262

63+
namespace FunctionsNS1 {
64+
namespace FunctionsNS2 {
65+
namespace FunctionsNS3 {
66+
struct Y {};
67+
inline bool operator==(Y, Y) { return true; }
68+
} // namespace FunctionsNS3
69+
} // namespace FunctionsNS2
70+
} // namespace FunctionsNS1
71+
6372
#endif // TEST_INTEROP_CXX_NAMESPACE_INPUTS_FREE_FUNCTION_H

test/Interop/Cxx/namespace/free-functions-module-interface.swift

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,34 @@
77
// CHECK-NEXT: struct X {
88
// CHECK-NEXT: init()
99
// CHECK-NEXT: }
10-
// CHECK-NEXT: static func + (_: FunctionsNS1.X, _: FunctionsNS1.X) -> UnsafePointer<CChar>!
11-
// CHECK-NEXT: static func sameNameInChild() -> UnsafePointer<CChar>!
12-
// CHECK-NEXT: static func sameNameInSibling() -> UnsafePointer<CChar>!
10+
11+
// FIXME: this seems wrong, the operator shouldn't be printed twice (https://github.com/apple/swift/issues/62727).
12+
// CHECK-NEXT: func + (_: FunctionsNS1.X, _: FunctionsNS1.X) -> UnsafePointer<CChar>!
13+
1314
// CHECK-NEXT: enum FunctionsNS2 {
14-
// CHECK-NEXT: static func basicFunctionSecondLevel() -> UnsafePointer<CChar>!
15-
// CHECK-NEXT: static func sameNameInChild() -> UnsafePointer<CChar>!
1615
// CHECK-NEXT: enum FunctionsNS3 {
16+
// CHECK-NEXT: struct Y {
17+
// CHECK-NEXT: init()
18+
// CHECK-NEXT: }
19+
20+
// FIXME: this seems wrong, the operator shouldn't be printed twice (https://github.com/apple/swift/issues/62727).
21+
// CHECK-NEXT: func == (_: FunctionsNS1.FunctionsNS2.FunctionsNS3.Y, _: FunctionsNS1.FunctionsNS2.FunctionsNS3.Y) -> Bool
22+
1723
// CHECK-NEXT: static func basicFunctionLowestLevel() -> UnsafePointer<CChar>!
1824
// CHECK-NEXT: }
25+
// CHECK-NEXT: static func sameNameInChild() -> UnsafePointer<CChar>!
26+
// CHECK-NEXT: static func basicFunctionSecondLevel() -> UnsafePointer<CChar>!
1927
// CHECK-NEXT: }
28+
29+
// CHECK-NEXT: static func sameNameInChild() -> UnsafePointer<CChar>!
30+
// CHECK-NEXT: static func sameNameInSibling() -> UnsafePointer<CChar>!
2031
// CHECK-NEXT: static func definedInDefs() -> UnsafePointer<CChar>!
2132
// CHECK-NEXT: }
2233

23-
// CHECK-NEXT: static func + (_: FunctionsNS1.X, _: FunctionsNS1.X) -> UnsafePointer<CChar>!
34+
// CHECK-NEXT: func + (_: FunctionsNS1.X, _: FunctionsNS1.X) -> UnsafePointer<CChar>!
2435

2536
// CHECK-NEXT: enum FunctionsNS4 {
2637
// CHECK-NEXT: static func sameNameInSibling() -> UnsafePointer<CChar>!
2738
// CHECK-NEXT: }
39+
40+
// CHECK-NEXT: func == (_: FunctionsNS1.FunctionsNS2.FunctionsNS3.Y, _: FunctionsNS1.FunctionsNS2.FunctionsNS3.Y) -> Bool

0 commit comments

Comments
 (0)