Skip to content

Commit 2f9f722

Browse files
committed
[cxx-interop] import static operator call from C++23 as member callAsFunction functions in Swift to preserve source compatibility
1 parent 7743741 commit 2f9f722

File tree

6 files changed

+73
-2
lines changed

6 files changed

+73
-2
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3734,6 +3734,17 @@ namespace {
37343734
}
37353735

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

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

lib/ClangImporter/SwiftDeclSynthesizer.cpp

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

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

@@ -2051,6 +2052,11 @@ clang::CXXMethodDecl *SwiftDeclSynthesizer::synthesizeCXXForwardingMethod(
20512052
(forwardingMethodKind == ForwardingMethodKind::Virtual
20522053
? "__synthesizedVirtualCall_operatorStar"
20532054
: "__synthesizedBaseCall_operatorStar")));
2055+
} else if (name.getCXXOverloadedOperator() == clang::OO_Call) {
2056+
assert(forwardingMethodKind != ForwardingMethodKind::Virtual);
2057+
name = clang::DeclarationName(
2058+
&ImporterImpl.getClangPreprocessor().getIdentifierTable().get(
2059+
"__synthesizedBaseCall_operatorCall"));
20542060
}
20552061
auto methodType = method->getType();
20562062
// Check if we need to drop the reference from the return type
@@ -2093,7 +2099,7 @@ clang::CXXMethodDecl *SwiftDeclSynthesizer::synthesizeCXXForwardingMethod(
20932099
clangCtx, const_cast<clang::CXXRecordDecl *>(derivedClass),
20942100
method->getSourceRange().getBegin(),
20952101
clang::DeclarationNameInfo(name, clang::SourceLocation()), methodType,
2096-
method->getTypeSourceInfo(), method->getStorageClass(),
2102+
method->getTypeSourceInfo(), method->isStatic() ? clang::SC_None : method->getStorageClass(),
20972103
method->UsesFPIntrin(), /*isInline=*/true, method->getConstexprKind(),
20982104
method->getSourceRange().getEnd());
20992105
newMethod->setImplicit();
@@ -2264,6 +2270,28 @@ FuncDecl *SwiftDeclSynthesizer::makeVirtualMethod(
22642270
return result;
22652271
}
22662272

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

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

lib/ClangImporter/SwiftDeclSynthesizer.h

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

322+
FuncDecl *makeInstanceToStaticOperatorCallMethod(const clang::CXXMethodDecl *clangMethodDecl);
323+
322324
VarDecl *makeComputedPropertyFromCXXMethods(FuncDecl *getter,
323325
FuncDecl *setter);
324326

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,4 +477,14 @@ 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) {
483+
return x + 42;
484+
}
485+
};
486+
487+
class HasStaticOperatorCallDerived: public HasStaticOperatorCallBase {
488+
};
489+
480490
#endif

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,3 +294,11 @@
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: }

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,4 +437,16 @@ 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+
440452
runAllTests()

0 commit comments

Comments
 (0)