Skip to content

Commit 55e5d69

Browse files
authored
Merge pull request #36075 from egorzhdan/cxx-operator-call
C++ Interop: import call operators
2 parents c7f2130 + 7141ae2 commit 55e5d69

16 files changed

+251
-44
lines changed

include/swift/SIL/AbstractionPattern.h

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,12 @@ class AbstractionPattern {
509509
case Kind::CFunctionAsMethodType:
510510
case Kind::CurriedCFunctionAsMethodType:
511511
case Kind::PartialCurriedCFunctionAsMethodType:
512+
case Kind::CXXMethodType:
513+
case Kind::CurriedCXXMethodType:
514+
case Kind::PartialCurriedCXXMethodType:
515+
case Kind::CXXOperatorMethodType:
516+
case Kind::CurriedCXXOperatorMethodType:
517+
case Kind::PartialCurriedCXXOperatorMethodType:
512518
return true;
513519

514520
default:
@@ -552,9 +558,11 @@ class AbstractionPattern {
552558
}
553559

554560
void initCXXMethod(CanGenericSignature signature, CanType origType,
555-
const clang::CXXMethodDecl *method, Kind kind) {
561+
const clang::CXXMethodDecl *method, Kind kind,
562+
ImportAsMemberStatus memberStatus) {
556563
initSwiftType(signature, origType, kind);
557564
CXXMethod = method;
565+
OtherData = memberStatus.getRawValue();
558566
}
559567

560568
AbstractionPattern() {}
@@ -715,19 +723,22 @@ class AbstractionPattern {
715723
/// then the uncurried type is:
716724
/// ((RefrigeratorCompartment, Temperature), Refrigerator) -> ()
717725
static AbstractionPattern getCXXMethod(CanType origType,
718-
const clang::CXXMethodDecl *method) {
726+
const clang::CXXMethodDecl *method,
727+
ImportAsMemberStatus memberStatus) {
719728
assert(isa<AnyFunctionType>(origType));
720729
AbstractionPattern pattern;
721-
pattern.initCXXMethod(nullptr, origType, method, Kind::CXXMethodType);
730+
pattern.initCXXMethod(nullptr, origType, method,
731+
Kind::CXXMethodType, memberStatus);
722732
return pattern;
723733
}
724734

725735
static AbstractionPattern
726-
getCXXOperatorMethod(CanType origType, const clang::CXXMethodDecl *method) {
736+
getCXXOperatorMethod(CanType origType, const clang::CXXMethodDecl *method,
737+
ImportAsMemberStatus memberStatus) {
727738
assert(isa<AnyFunctionType>(origType));
728739
AbstractionPattern pattern;
729740
pattern.initCXXMethod(nullptr, origType, method,
730-
Kind::CXXOperatorMethodType);
741+
Kind::CXXOperatorMethodType, memberStatus);
731742
return pattern;
732743
}
733744

@@ -739,21 +750,23 @@ class AbstractionPattern {
739750
/// then the curried type:
740751
/// (Refrigerator) -> (Compartment, Temperature) -> ()
741752
static AbstractionPattern
742-
getCurriedCXXMethod(CanType origType, const clang::CXXMethodDecl *method) {
753+
getCurriedCXXMethod(CanType origType, const clang::CXXMethodDecl *method,
754+
ImportAsMemberStatus memberStatus) {
743755
assert(isa<AnyFunctionType>(origType));
744756
AbstractionPattern pattern;
745757
pattern.initCXXMethod(nullptr, origType, method,
746-
Kind::CurriedCXXMethodType);
758+
Kind::CurriedCXXMethodType, memberStatus);
747759
return pattern;
748760
}
749761

750762
static AbstractionPattern
751763
getCurriedCXXOperatorMethod(CanType origType,
752-
const clang::CXXMethodDecl *method) {
764+
const clang::CXXMethodDecl *method,
765+
ImportAsMemberStatus memberStatus) {
753766
assert(isa<AnyFunctionType>(origType));
754767
AbstractionPattern pattern;
755768
pattern.initCXXMethod(nullptr, origType, method,
756-
Kind::CurriedCXXOperatorMethodType);
769+
Kind::CurriedCXXOperatorMethodType, memberStatus);
757770
return pattern;
758771
}
759772

@@ -855,22 +868,24 @@ class AbstractionPattern {
855868
/// (Compartment, Temperature) -> ()
856869
static AbstractionPattern
857870
getPartialCurriedCXXMethod(CanGenericSignature signature, CanType origType,
858-
const clang::CXXMethodDecl *method) {
871+
const clang::CXXMethodDecl *method,
872+
ImportAsMemberStatus memberStatus) {
859873
assert(isa<AnyFunctionType>(origType));
860874
AbstractionPattern pattern;
861875
pattern.initCXXMethod(signature, origType, method,
862-
Kind::PartialCurriedCXXMethodType);
876+
Kind::PartialCurriedCXXMethodType, memberStatus);
863877
return pattern;
864878
}
865879

866880
static AbstractionPattern
867881
getPartialCurriedCXXOperatorMethod(CanGenericSignature signature,
868882
CanType origType,
869-
const clang::CXXMethodDecl *method) {
883+
const clang::CXXMethodDecl *method,
884+
ImportAsMemberStatus memberStatus) {
870885
assert(isa<AnyFunctionType>(origType));
871886
AbstractionPattern pattern;
872887
pattern.initCXXMethod(signature, origType, method,
873-
Kind::PartialCurriedCXXOperatorMethodType);
888+
Kind::PartialCurriedCXXOperatorMethodType, memberStatus);
874889
return pattern;
875890
}
876891

lib/ClangImporter/ImportDecl.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4045,8 +4045,9 @@ namespace {
40454045
// functions get imported into Swift as static member functions
40464046
// that use an additional parameter for the left-hand side operand
40474047
// instead of the receiver object.
4048-
mdecl->getDeclName().getNameKind() ==
4049-
clang::DeclarationName::CXXOperatorName) {
4048+
(mdecl->getDeclName().getNameKind() ==
4049+
clang::DeclarationName::CXXOperatorName &&
4050+
isImportedAsStatic(mdecl->getOverloadedOperator()))) {
40504051
selfIdx = None;
40514052
} else {
40524053
selfIdx = 0;
@@ -7927,6 +7928,16 @@ bool importer::isSpecialUIKitStructZeroProperty(const clang::NamedDecl *decl) {
79277928
return ident->isStr("UIEdgeInsetsZero") || ident->isStr("UIOffsetZero");
79287929
}
79297930

7931+
bool importer::isImportedAsStatic(const clang::OverloadedOperatorKind op) {
7932+
switch (op) {
7933+
#define OVERLOADED_OPERATOR(Name,Spelling,Token,Unary,Binary,MemberOnly) \
7934+
case clang::OO_##Name: return !MemberOnly;
7935+
#include "clang/Basic/OperatorKinds.def"
7936+
default:
7937+
llvm_unreachable("not an operator");
7938+
}
7939+
}
7940+
79307941
Type ClangImporter::Implementation::getMainActorType() {
79317942
if (MainActorType)
79327943
return *MainActorType;

lib/ClangImporter/ImportName.cpp

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1757,6 +1757,13 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
17571757

17581758
case clang::DeclarationName::CXXOperatorName: {
17591759
auto op = D->getDeclName().getCXXOverloadedOperator();
1760+
auto functionDecl = dyn_cast<clang::FunctionDecl>(D);
1761+
if (!functionDecl) {
1762+
// This can happen for example for templated operators functions.
1763+
// We don't support those, yet.
1764+
return ImportedName();
1765+
}
1766+
17601767
switch (op) {
17611768
case clang::OverloadedOperatorKind::OO_Plus:
17621769
case clang::OverloadedOperatorKind::OO_Minus:
@@ -1775,21 +1782,20 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
17751782
case clang::OverloadedOperatorKind::OO_GreaterEqual:
17761783
case clang::OverloadedOperatorKind::OO_AmpAmp:
17771784
case clang::OverloadedOperatorKind::OO_PipePipe:
1778-
if (auto FD = dyn_cast<clang::FunctionDecl>(D)) {
1779-
baseName = clang::getOperatorSpelling(op);
1780-
isFunction = true;
1781-
argumentNames.resize(
1782-
FD->param_size() +
1783-
// C++ operators that are implemented as non-static member functions
1784-
// get imported into Swift as static member functions that use an
1785-
// additional parameter for the left-hand side operand instead of
1786-
// the receiver object.
1787-
(isa<clang::CXXMethodDecl>(D) ? 1 : 0));
1788-
} else {
1789-
// This can happen for example for templated operators functions.
1790-
// We don't support those, yet.
1791-
return ImportedName();
1792-
}
1785+
baseName = clang::getOperatorSpelling(op);
1786+
isFunction = true;
1787+
argumentNames.resize(
1788+
functionDecl->param_size() +
1789+
// C++ operators that are implemented as non-static member functions
1790+
// get imported into Swift as static member functions that use an
1791+
// additional parameter for the left-hand side operand instead of
1792+
// the receiver object.
1793+
(isa<clang::CXXMethodDecl>(D) ? 1 : 0));
1794+
break;
1795+
case clang::OverloadedOperatorKind::OO_Call:
1796+
baseName = "callAsFunction";
1797+
isFunction = true;
1798+
addEmptyArgNamesForClangFunction(functionDecl, argumentNames);
17931799
break;
17941800
default:
17951801
// We don't import these yet.

lib/ClangImporter/ImportType.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1815,7 +1815,7 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList(
18151815
// imported into Swift as static methods that have an additional
18161816
// parameter for the left-hand side operand instead of the receiver object.
18171817
if (auto CMD = dyn_cast<clang::CXXMethodDecl>(clangDecl)) {
1818-
if (clangDecl->isOverloadedOperator()) {
1818+
if (clangDecl->isOverloadedOperator() && isImportedAsStatic(clangDecl->getOverloadedOperator())) {
18191819
auto param = new (SwiftContext)
18201820
ParamDecl(SourceLoc(), SourceLoc(), Identifier(), SourceLoc(),
18211821
SwiftContext.getIdentifier("lhs"), dc);

lib/ClangImporter/ImporterImpl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1473,6 +1473,10 @@ bool shouldSuppressDeclImport(const clang::Decl *decl);
14731473
/// but are now renamed using the swift_name attribute.
14741474
bool isSpecialUIKitStructZeroProperty(const clang::NamedDecl *decl);
14751475

1476+
/// \returns true if this operator should be made a static function
1477+
/// even if imported as a non-static member function.
1478+
bool isImportedAsStatic(clang::OverloadedOperatorKind op);
1479+
14761480
/// Add command-line arguments for a normal import of Clang code.
14771481
void getNormalInvocationArguments(std::vector<std::string> &invocationArgStrs,
14781482
ASTContext &ctx);

lib/SIL/IR/AbstractionPattern.cpp

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -176,13 +176,13 @@ AbstractionPattern
176176
AbstractionPattern::getCurriedCXXMethod(CanType origType,
177177
const AbstractFunctionDecl *function) {
178178
auto clangMethod = cast<clang::CXXMethodDecl>(function->getClangDecl());
179-
return getCurriedCXXMethod(origType, clangMethod);
179+
return getCurriedCXXMethod(origType, clangMethod, function->getImportAsMemberStatus());
180180
}
181181

182182
AbstractionPattern AbstractionPattern::getCurriedCXXOperatorMethod(
183183
CanType origType, const AbstractFunctionDecl *function) {
184184
auto clangMethod = cast<clang::CXXMethodDecl>(function->getClangDecl());
185-
return getCurriedCXXOperatorMethod(origType, clangMethod);
185+
return getCurriedCXXOperatorMethod(origType, clangMethod, function->getImportAsMemberStatus());
186186
}
187187

188188
AbstractionPattern
@@ -529,11 +529,12 @@ AbstractionPattern AbstractionPattern::getFunctionResultType() const {
529529
getImportAsMemberStatus());
530530
case Kind::CurriedCXXMethodType:
531531
return getPartialCurriedCXXMethod(getGenericSignatureForFunctionComponent(),
532-
getResultType(getType()), getCXXMethod());
532+
getResultType(getType()), getCXXMethod(),
533+
getImportAsMemberStatus());
533534
case Kind::CurriedCXXOperatorMethodType:
534535
return getPartialCurriedCXXOperatorMethod(
535536
getGenericSignatureForFunctionComponent(), getResultType(getType()),
536-
getCXXMethod());
537+
getCXXMethod(), getImportAsMemberStatus());
537538
case Kind::PartialCurriedObjCMethodType:
538539
case Kind::ObjCMethodType: {
539540
// If this is a foreign async function, the result type comes from the
@@ -753,10 +754,19 @@ AbstractionPattern::getFunctionParamType(unsigned index) const {
753754
auto params = cast<AnyFunctionType>(getType()).getParams();
754755
auto paramType = params[index].getParameterType();
755756

756-
// The first parameter holds the left-hand-side operand, which gets passed
757-
// to the C++ function as the this pointer.
758-
if (index == 0)
759-
return getCXXMethodSelfPattern(paramType);
757+
// See importer::isImportedAsStatic
758+
bool isStatic = getImportAsMemberStatus().isStatic();
759+
if (isStatic) {
760+
// The first parameter holds the left-hand-side operand, which gets passed
761+
// to the C++ function as the this pointer.
762+
if (index == 0)
763+
return getCXXMethodSelfPattern(paramType);
764+
} else {
765+
// The last parameter is 'self'.
766+
if (getKind() == Kind::CXXOperatorMethodType &&
767+
index == params.size() - 1)
768+
return getCXXMethodSelfPattern(params.back().getParameterType());
769+
}
760770

761771
// A parameter of type () does not correspond to a Clang parameter.
762772
if (paramType->isVoid())
@@ -766,7 +776,7 @@ AbstractionPattern::getFunctionParamType(unsigned index) const {
766776
auto methodType = getCXXMethod()->getType().getTypePtr();
767777
return AbstractionPattern(
768778
getGenericSignatureForFunctionComponent(), paramType,
769-
getClangFunctionParameterType(methodType, index - 1));
779+
getClangFunctionParameterType(methodType, index - (isStatic ? 1 : 0)));
770780
}
771781
case Kind::CurriedObjCMethodType: {
772782
auto params = cast<AnyFunctionType>(getType()).getParams();

lib/SIL/IR/SILFunctionType.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2903,8 +2903,8 @@ static CanSILFunctionType getSILFunctionTypeForClangDecl(
29032903

29042904
if (auto method = dyn_cast<clang::CXXMethodDecl>(clangDecl)) {
29052905
AbstractionPattern origPattern = method->isOverloadedOperator() ?
2906-
AbstractionPattern::getCXXOperatorMethod(origType, method):
2907-
AbstractionPattern::getCXXMethod(origType, method);
2906+
AbstractionPattern::getCXXOperatorMethod(origType, method, foreignInfo.Self):
2907+
AbstractionPattern::getCXXMethod(origType, method, foreignInfo.Self);
29082908
auto conventions = CXXMethodConventions(method);
29092909
return getSILFunctionType(TC, TypeExpansionContext::minimal(), origPattern,
29102910
substInterfaceType, extInfoBuilder, conventions,

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,33 @@ struct LoadableIntWrapper {
66
LoadableIntWrapper operator-(LoadableIntWrapper rhs) {
77
return LoadableIntWrapper{.value = value - rhs.value};
88
}
9+
10+
int operator()() {
11+
return value;
12+
}
13+
int operator()(int x) {
14+
return value + x;
15+
}
16+
int operator()(int x, int y) {
17+
return value + x * y;
18+
}
19+
};
20+
21+
struct AddressOnlyIntWrapper {
22+
int value;
23+
24+
AddressOnlyIntWrapper(int value) : value(value) {}
25+
AddressOnlyIntWrapper(const AddressOnlyIntWrapper &other) : value(other.value) {}
26+
27+
int operator()() {
28+
return value;
29+
}
30+
int operator()(int x) {
31+
return value + x;
32+
}
33+
int operator()(int x, int y) {
34+
return value + x * y;
35+
}
936
};
1037

1138
struct HasDeletedOperator {

test/Interop/Cxx/operators/Inputs/member-out-of-line.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,27 @@
33
LoadableIntWrapper LoadableIntWrapper::operator+(LoadableIntWrapper rhs) const {
44
return LoadableIntWrapper{.value = value + rhs.value};
55
}
6+
7+
int LoadableIntWrapper::operator()() const {
8+
return value;
9+
}
10+
11+
int LoadableIntWrapper::operator()(int x) const {
12+
return value + x;
13+
}
14+
15+
int LoadableIntWrapper::operator()(int x, int y) const {
16+
return value + x * y;
17+
}
18+
19+
int AddressOnlyIntWrapper::operator()() const {
20+
return value;
21+
}
22+
23+
int AddressOnlyIntWrapper::operator()(int x) const {
24+
return value + x;
25+
}
26+
27+
int AddressOnlyIntWrapper::operator()(int x, int y) const {
28+
return value + x * y;
29+
}

test/Interop/Cxx/operators/Inputs/member-out-of-line.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@
44
struct LoadableIntWrapper {
55
int value;
66
LoadableIntWrapper operator+(LoadableIntWrapper rhs) const;
7+
int operator()() const;
8+
int operator()(int x) const;
9+
int operator()(int x, int y) const;
10+
};
11+
12+
struct AddressOnlyIntWrapper {
13+
int value;
14+
15+
AddressOnlyIntWrapper(int value) : value(value) {}
16+
AddressOnlyIntWrapper(const AddressOnlyIntWrapper &other) : value(other.value) {}
17+
18+
int operator()() const;
19+
int operator()(int x) const;
20+
int operator()(int x, int y) const;
721
};
822

923
#endif

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,13 @@ public func sub(_ lhs: inout LoadableIntWrapper, _ rhs: LoadableIntWrapper) -> L
99

1010
// CHECK: call [[RES:i32|i64]] [[NAME:@(_ZN18LoadableIntWrappermiES_|"\?\?GLoadableIntWrapper@@QEAA\?AU0@U0@@Z")]](%struct.LoadableIntWrapper* {{%[0-9]+}}, {{i32|\[1 x i32\]|i64|%struct.LoadableIntWrapper\* byval\(.*\) align 4}} {{%[0-9]+}})
1111
// CHECK: define linkonce_odr [[RES]] [[NAME]](%struct.LoadableIntWrapper* nonnull dereferenceable(4) {{.*}}, {{i32 %rhs.coerce|\[1 x i32\] %rhs.coerce|i64 %rhs.coerce|%struct.LoadableIntWrapper\* byval\(%struct.LoadableIntWrapper\) align 4 %rhs}})
12+
13+
public func call(_ wrapper: inout LoadableIntWrapper, _ arg: Int32) -> Int32 { wrapper(arg) }
14+
15+
// CHECK: call [[RES:i32|i64]] [[NAME:@(_ZN18LoadableIntWrapperclEi|"\?\?GLoadableIntWrapper@@QEAAHH@Z")]](%struct.LoadableIntWrapper* {{%[0-9]+}}, {{i32|\[1 x i32\]|i64|%struct.LoadableIntWrapper\* byval\(.*\) align 4}} {{%[0-9]+}})
16+
// CHECK: define linkonce_odr [[RES]] [[NAME]](%struct.LoadableIntWrapper* nonnull dereferenceable(4) {{.*}}, {{i32 %x|\[1 x i32\] %x|i64 %x|%struct.LoadableIntWrapper\* byval\(%struct.LoadableIntWrapper\) align 4 %rhs}})
17+
18+
public func call(_ wrapper: inout AddressOnlyIntWrapper) -> Int32 { wrapper() }
19+
20+
// CHECK: call [[RES:i32|i64]] [[NAME:@(_ZN21AddressOnlyIntWrapperclEv|"\?\?GAddressOnlyIntWrapper@@QEAAHXZ")]](%struct.AddressOnlyIntWrapper* {{%[0-9]+}})
21+
// CHECK: define linkonce_odr [[RES]] [[NAME]](%struct.AddressOnlyIntWrapper* nonnull dereferenceable(4) {{.*}})

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22

33
// CHECK: struct LoadableIntWrapper {
44
// CHECK: static func - (lhs: inout LoadableIntWrapper, rhs: LoadableIntWrapper) -> LoadableIntWrapper
5+
// CHECK: mutating func callAsFunction() -> Int32
6+
// CHECK: mutating func callAsFunction(_ x: Int32) -> Int32
7+
// CHECK: mutating func callAsFunction(_ x: Int32, _ y: Int32) -> Int32
8+
// CHECK: }
9+
10+
// CHECK: struct AddressOnlyIntWrapper {
11+
// CHECK: mutating func callAsFunction() -> Int32
12+
// CHECK: mutating func callAsFunction(_ x: Int32) -> Int32
13+
// CHECK: mutating func callAsFunction(_ x: Int32, _ y: Int32) -> Int32
514
// CHECK: }
615

716
// CHECK: struct HasDeletedOperator {

0 commit comments

Comments
 (0)