Skip to content

Commit 0ee89fc

Browse files
hypcompnerd
authored andcommitted
[cxx-interop] import static operator call from C++23 as member callAsFunction functions in Swift to preserve source compatibility
1 parent 8d5ca1e commit 0ee89fc

File tree

8 files changed

+105
-2
lines changed

8 files changed

+105
-2
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3735,6 +3735,18 @@ namespace {
37353735
}
37363736

37373737
Decl *VisitCXXMethodDecl(const clang::CXXMethodDecl *decl) {
3738+
// The static `operator ()` introduced in C++ 23 is still callable as an
3739+
// instance operator in C++, and we want to preserve the ability to call
3740+
// it as an instance method in Swift as well for source compatibility.
3741+
// Therefore, we synthesize a C++ instance member that invokes the
3742+
// operator and import it instead.
3743+
if (decl->getOverloadedOperator() ==
3744+
clang::OverloadedOperatorKind::OO_Call &&
3745+
decl->isStatic()) {
3746+
auto result = synthesizer.makeInstanceToStaticOperatorCallMethod(decl);
3747+
if (result)
3748+
return result;
3749+
}
37383750
auto method = VisitFunctionDecl(decl);
37393751

37403752
// Do not expose constructors of abstract C++ classes.

lib/ClangImporter/SwiftDeclSynthesizer.cpp

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2021,9 +2021,12 @@ clang::CXXMethodDecl *SwiftDeclSynthesizer::synthesizeCXXForwardingMethod(
20212021

20222022
auto &clangCtx = ImporterImpl.getClangASTContext();
20232023
auto &clangSema = ImporterImpl.getClangSema();
2024+
assert(!method->isStatic() ||
2025+
method->getNameInfo().getName().getCXXOverloadedOperator() ==
2026+
clang::OO_Call);
20242027
// When emitting symbolic decls, the method might not have a concrete
20252028
// record type as this type.
2026-
if (ImporterImpl.importSymbolicCXXDecls &&
2029+
if (ImporterImpl.importSymbolicCXXDecls && !method->isStatic() &&
20272030
!method->getThisType()->getPointeeCXXRecordDecl())
20282031
return nullptr;
20292032

@@ -2051,6 +2054,11 @@ clang::CXXMethodDecl *SwiftDeclSynthesizer::synthesizeCXXForwardingMethod(
20512054
(forwardingMethodKind == ForwardingMethodKind::Virtual
20522055
? "__synthesizedVirtualCall_operatorStar"
20532056
: "__synthesizedBaseCall_operatorStar")));
2057+
} else if (name.getCXXOverloadedOperator() == clang::OO_Call) {
2058+
assert(forwardingMethodKind != ForwardingMethodKind::Virtual);
2059+
name = clang::DeclarationName(
2060+
&ImporterImpl.getClangPreprocessor().getIdentifierTable().get(
2061+
"__synthesizedBaseCall_operatorCall"));
20542062
}
20552063
auto methodType = method->getType();
20562064
// Check if we need to drop the reference from the return type
@@ -2093,7 +2101,8 @@ clang::CXXMethodDecl *SwiftDeclSynthesizer::synthesizeCXXForwardingMethod(
20932101
clangCtx, const_cast<clang::CXXRecordDecl *>(derivedClass),
20942102
method->getSourceRange().getBegin(),
20952103
clang::DeclarationNameInfo(name, clang::SourceLocation()), methodType,
2096-
method->getTypeSourceInfo(), method->getStorageClass(),
2104+
method->getTypeSourceInfo(),
2105+
method->isStatic() ? clang::SC_None : method->getStorageClass(),
20972106
method->UsesFPIntrin(), /*isInline=*/true, method->getConstexprKind(),
20982107
method->getSourceRange().getEnd());
20992108
newMethod->setImplicit();
@@ -2264,6 +2273,27 @@ FuncDecl *SwiftDeclSynthesizer::makeVirtualMethod(
22642273
return result;
22652274
}
22662275

2276+
// MARK: C++ operators
2277+
2278+
FuncDecl *SwiftDeclSynthesizer::makeInstanceToStaticOperatorCallMethod(
2279+
const clang::CXXMethodDecl *clangMethodDecl) {
2280+
auto clangDC = clangMethodDecl->getParent();
2281+
auto &ctx = ImporterImpl.SwiftContext;
2282+
2283+
assert(clangMethodDecl->isStatic() && "Expected a static operator");
2284+
2285+
auto newMethod = synthesizeCXXForwardingMethod(
2286+
clangDC, clangDC, clangMethodDecl, ForwardingMethodKind::Base,
2287+
ReferenceReturnTypeBehaviorForBaseMethodSynthesis::KeepReference,
2288+
/*forceConstQualifier*/ true);
2289+
newMethod->addAttr(clang::SwiftNameAttr::CreateImplicit(
2290+
clangMethodDecl->getASTContext(), "callAsFunction"));
2291+
2292+
auto result = dyn_cast_or_null<FuncDecl>(
2293+
ctx.getClangModuleLoader()->importDeclDirectly(newMethod));
2294+
return result;
2295+
}
2296+
22672297
// MARK: C++ properties
22682298

22692299
static std::pair<BraceStmt *, bool>

lib/ClangImporter/SwiftDeclSynthesizer.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,9 @@ class SwiftDeclSynthesizer {
319319
/// method that dispatches the call dynamically.
320320
FuncDecl *makeVirtualMethod(const clang::CXXMethodDecl *clangMethodDecl);
321321

322+
FuncDecl *makeInstanceToStaticOperatorCallMethod(
323+
const clang::CXXMethodDecl *clangMethodDecl);
324+
322325
VarDecl *makeComputedPropertyFromCXXMethods(FuncDecl *getter,
323326
FuncDecl *setter);
324327

test/Interop/Cxx/class/Inputs/protocol-conformance.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,8 @@ struct HasVirtualMethod {
7272
virtual int return42() { return 42; }
7373
};
7474

75+
struct HasStaticOperatorCall {
76+
static int operator()(int x) { return x * 2; }
77+
};
78+
7579
#endif // TEST_INTEROP_CXX_CLASS_INPUTS_PROTOCOL_CONFORMANCE_H

test/Interop/Cxx/class/protocol-conformance-typechecker.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,9 @@ protocol HasOperatorPlusEqualProtocol {
4949
}
5050

5151
extension HasOperatorPlusEqualInt : HasOperatorPlusEqualProtocol {}
52+
53+
protocol HasOperatorCall {
54+
func callAsFunction(_ x: Int32) -> Int32
55+
}
56+
57+
extension HasStaticOperatorCall : HasOperatorCall {}

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,4 +477,19 @@ struct HasOperatorCallWithDefaultArg {
477477
int operator()(int x = 0) const { return value + x; }
478478
};
479479

480+
class HasStaticOperatorCallBase {
481+
public:
482+
static int operator()(int x) { return x + 42; }
483+
};
484+
485+
class HasStaticOperatorCallDerived : public HasStaticOperatorCallBase {};
486+
487+
class HasStaticOperatorCallWithConstOperator {
488+
public:
489+
inline int operator()(int x, int y) const { return x + y; }
490+
static int operator()(int x) {
491+
return x - 1;
492+
}
493+
};
494+
480495
#endif

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,3 +294,16 @@
294294
// CHECK: struct HasOperatorCallWithDefaultArg {
295295
// CHECK: func callAsFunction(_ x: Int32 = cxxDefaultArg) -> Int32
296296
// CHECK: }
297+
298+
// CHECK: struct HasStaticOperatorCallBase {
299+
// CHECK: func callAsFunction(_ x: Int32) -> Int32
300+
// CHECK: }
301+
302+
// CHECK: struct HasStaticOperatorCallDerived {
303+
// CHECK: func callAsFunction(_ x: Int32) -> Int32
304+
// CHECK: }
305+
306+
// CHECK: struct HasStaticOperatorCallWithConstOperator {
307+
// CHECK: func callAsFunction(_ x: Int32, _ y: Int32) -> Int32
308+
// CHECK: func callAsFunction(_ x: Int32) -> Int32
309+
// CHECK: }

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,4 +437,24 @@ OperatorsTestSuite.test("HasOperatorCallWithDefaultArg.call") {
437437
expectEqual(444, res)
438438
}
439439

440+
OperatorsTestSuite.test("HasStaticOperatorCallBase.call") {
441+
let h = HasStaticOperatorCallBase()
442+
let res = h(1)
443+
expectEqual(43, res)
444+
}
445+
446+
OperatorsTestSuite.test("HasStaticOperatorCallDerived.call") {
447+
let h = HasStaticOperatorCallDerived()
448+
let res = h(0)
449+
expectEqual(42, res)
450+
}
451+
452+
OperatorsTestSuite.test("HasStaticOperatorCallWithConstOperator.call") {
453+
let h = HasStaticOperatorCallWithConstOperator()
454+
let res = h(10)
455+
expectEqual(9, res)
456+
let res2 = h(3, 5)
457+
expectEqual(8, res2)
458+
}
459+
440460
runAllTests()

0 commit comments

Comments
 (0)