Skip to content

Commit 37c589e

Browse files
authored
Merge pull request #76619 from compnerd/600-calls
[cxx-interop] import static operator call from C++23 as member callAsFunction functions in Swift to preserve source compatibility
2 parents 8d5ca1e + 9f56551 commit 37c589e

8 files changed

+128
-4
lines changed

lib/ClangImporter/ImportDecl.cpp

+12
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

+39-4
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();
@@ -2140,14 +2149,19 @@ clang::CXXMethodDecl *SwiftDeclSynthesizer::synthesizeCXXForwardingMethod(
21402149
thisExpr = conv.get();
21412150
}
21422151

2152+
auto memberExprTy =
2153+
(method->isStatic() && method->getOverloadedOperator() ==
2154+
clang::OverloadedOperatorKind::OO_Call)
2155+
? method->getType()
2156+
: clangCtx.BoundMemberTy;
21432157
auto memberExpr = clangSema.BuildMemberExpr(
21442158
thisExpr, /*isArrow=*/true, clang::SourceLocation(),
21452159
clang::NestedNameSpecifierLoc(), clang::SourceLocation(),
21462160
const_cast<clang::CXXMethodDecl *>(method),
21472161
clang::DeclAccessPair::make(const_cast<clang::CXXMethodDecl *>(method),
21482162
clang::AS_public),
21492163
/*HadMultipleCandidates=*/false, method->getNameInfo(),
2150-
clangCtx.BoundMemberTy, clang::VK_PRValue, clang::OK_Ordinary);
2164+
memberExprTy, clang::VK_PRValue, clang::OK_Ordinary);
21512165
llvm::SmallVector<clang::Expr *, 4> args;
21522166
for (size_t i = 0; i < newMethod->getNumParams(); ++i) {
21532167
auto *param = newMethod->getParamDecl(i);
@@ -2158,7 +2172,7 @@ clang::CXXMethodDecl *SwiftDeclSynthesizer::synthesizeCXXForwardingMethod(
21582172
clangCtx, param, false, type, clang::ExprValueKind::VK_LValue,
21592173
clang::SourceLocation()));
21602174
}
2161-
auto memberCall = clangSema.BuildCallToMemberFunction(
2175+
auto memberCall = clangSema.BuildCallExpr(
21622176
nullptr, memberExpr, clang::SourceLocation(), args,
21632177
clang::SourceLocation());
21642178
if (!memberCall.isUsable())
@@ -2264,6 +2278,27 @@ FuncDecl *SwiftDeclSynthesizer::makeVirtualMethod(
22642278
return result;
22652279
}
22662280

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

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

lib/ClangImporter/SwiftDeclSynthesizer.h

+3
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

+4
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

+6
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

+24
Original file line numberDiff line numberDiff line change
@@ -477,4 +477,28 @@ 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 HasStaticOperatorCallBaseNonTrivial: public NonTrivial {
486+
public:
487+
HasStaticOperatorCallBaseNonTrivial() {}
488+
HasStaticOperatorCallBaseNonTrivial(const HasStaticOperatorCallBaseNonTrivial &self) : NonTrivial(self) {}
489+
HasStaticOperatorCallBaseNonTrivial(HasStaticOperatorCallBaseNonTrivial &&self) : NonTrivial(self) {}
490+
491+
static int operator()(const NonTrivial &arg) { return arg.f + 42; }
492+
};
493+
494+
class HasStaticOperatorCallDerived : public HasStaticOperatorCallBase {};
495+
496+
class HasStaticOperatorCallWithConstOperator {
497+
public:
498+
inline int operator()(int x, int y) const { return x + y; }
499+
static int operator()(int x) {
500+
return x - 1;
501+
}
502+
};
503+
480504
#endif

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

+13
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

+27
Original file line numberDiff line numberDiff line change
@@ -437,4 +437,31 @@ 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("HasStaticOperatorCallBase2.call") {
447+
let m = NonTrivial()
448+
let h = HasStaticOperatorCallBaseNonTrivial()
449+
let res = h(m)
450+
expectEqual(48, res)
451+
}
452+
453+
OperatorsTestSuite.test("HasStaticOperatorCallDerived.call") {
454+
let h = HasStaticOperatorCallDerived()
455+
let res = h(0)
456+
expectEqual(42, res)
457+
}
458+
459+
OperatorsTestSuite.test("HasStaticOperatorCallWithConstOperator.call") {
460+
let h = HasStaticOperatorCallWithConstOperator()
461+
let res = h(10)
462+
expectEqual(9, res)
463+
let res2 = h(3, 5)
464+
expectEqual(8, res2)
465+
}
466+
440467
runAllTests()

0 commit comments

Comments
 (0)