Skip to content

Commit 9af8376

Browse files
authored
Merge pull request #59551 from apple/egorzhdan/cxx-inc-operator
[cxx-interop] Import increment operators
2 parents d76e743 + d29b78e commit 9af8376

File tree

8 files changed

+184
-11
lines changed

8 files changed

+184
-11
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2189,12 +2189,31 @@ namespace {
21892189

21902190
Impl.markUnavailable(MD, "use .pointee property");
21912191
MD->overwriteAccess(AccessLevel::Private);
2192+
} else if (cxxOperatorKind ==
2193+
clang::OverloadedOperatorKind::OO_PlusPlus) {
2194+
auto selfTy = result->getSelfInterfaceType();
2195+
auto returnTy =
2196+
MD->getResultInterfaceType()->getAnyPointerElementType();
2197+
if (cxxMethod->param_empty() &&
2198+
selfTy->getCanonicalType() == returnTy->getCanonicalType()) {
2199+
// This is a pre-increment operator. We synthesize a
2200+
// non-mutating function called `successor() -> Self`.
2201+
FuncDecl *successorFunc = synthesizer.makeSuccessorFunc(MD);
2202+
result->addMember(successorFunc);
2203+
2204+
Impl.markUnavailable(MD, "use .successor()");
2205+
} else {
2206+
Impl.markUnavailable(MD, "unable to create .successor() func");
2207+
}
2208+
MD->overwriteAccess(AccessLevel::Private);
21922209
}
21932210
// Check if this method _is_ an overloaded operator but is not a
2194-
// call / subscript / dereference. Those 3 operators do not need
2195-
// static versions.
2211+
// call / subscript / dereference / increment. Those
2212+
// operators do not need static versions.
21962213
else if (cxxOperatorKind !=
21972214
clang::OverloadedOperatorKind::OO_None &&
2215+
cxxOperatorKind !=
2216+
clang::OverloadedOperatorKind::OO_PlusPlus &&
21982217
cxxOperatorKind !=
21992218
clang::OverloadedOperatorKind::OO_Call &&
22002219
cxxOperatorKind !=

lib/ClangImporter/ImportName.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1854,6 +1854,7 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
18541854
case clang::OverloadedOperatorKind::OO_LessLess:
18551855
case clang::OverloadedOperatorKind::OO_GreaterGreater:
18561856
case clang::OverloadedOperatorKind::OO_EqualEqual:
1857+
case clang::OverloadedOperatorKind::OO_PlusPlus:
18571858
case clang::OverloadedOperatorKind::OO_ExclaimEqual:
18581859
case clang::OverloadedOperatorKind::OO_LessEqual:
18591860
case clang::OverloadedOperatorKind::OO_GreaterEqual:

lib/ClangImporter/SwiftDeclSynthesizer.cpp

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ SwiftDeclSynthesizer::createVarWithPattern(DeclContext *dc, Identifier name,
144144
VarDecl::Introducer introducer,
145145
bool isImplicit, AccessLevel access,
146146
AccessLevel setterAccess) {
147-
ASTContext &ctx = ImporterImpl.SwiftContext;
147+
ASTContext &ctx = dc->getASTContext();
148148

149149
// Create a variable to store the underlying value.
150150
auto var = new (ctx) VarDecl(
@@ -1720,6 +1720,97 @@ VarDecl *SwiftDeclSynthesizer::makeDereferencedPointeeProperty(
17201720
return result;
17211721
}
17221722

1723+
// MARK: C++ increment operator
1724+
1725+
/// Synthesizer callback for a successor function.
1726+
///
1727+
/// \code
1728+
/// var __copy: Self
1729+
/// __copy = self
1730+
/// __copy.__operatorPlusPlus()
1731+
/// return __copy
1732+
/// \endcode
1733+
static std::pair<BraceStmt *, bool>
1734+
synthesizeSuccessorFuncBody(AbstractFunctionDecl *afd, void *context) {
1735+
auto successorDecl = cast<FuncDecl>(afd);
1736+
auto incrementImpl = static_cast<FuncDecl *>(context);
1737+
1738+
ASTContext &ctx = successorDecl->getASTContext();
1739+
auto emptyTupleTy = TupleType::getEmpty(ctx);
1740+
auto returnTy = successorDecl->getResultInterfaceType();
1741+
1742+
auto selfDecl = successorDecl->getImplicitSelfDecl();
1743+
auto selfRefExpr = new (ctx) DeclRefExpr(selfDecl, DeclNameLoc(),
1744+
/*implicit*/ true);
1745+
selfRefExpr->setType(selfDecl->getInterfaceType());
1746+
1747+
// Create a `__copy` variable.
1748+
VarDecl *copyDecl = nullptr;
1749+
PatternBindingDecl *patternDecl = nullptr;
1750+
std::tie(copyDecl, patternDecl) = SwiftDeclSynthesizer::createVarWithPattern(
1751+
successorDecl, ctx.getIdentifier("__copy"), returnTy,
1752+
VarDecl::Introducer::Var,
1753+
/*isImplicit*/ true, AccessLevel::Public, AccessLevel::Public);
1754+
1755+
auto copyRefLValueExpr = new (ctx) DeclRefExpr(copyDecl, DeclNameLoc(),
1756+
/*implicit*/ true);
1757+
copyRefLValueExpr->setType(LValueType::get(copyDecl->getInterfaceType()));
1758+
1759+
auto inoutCopyExpr = new (ctx) InOutExpr(
1760+
SourceLoc(), copyRefLValueExpr,
1761+
successorDecl->mapTypeIntoContext(copyDecl->getValueInterfaceType()),
1762+
/*isImplicit*/ true);
1763+
inoutCopyExpr->setType(InOutType::get(copyDecl->getInterfaceType()));
1764+
1765+
// Copy `self` to `__copy`.
1766+
auto copyAssignExpr = new (ctx) AssignExpr(copyRefLValueExpr, SourceLoc(),
1767+
selfRefExpr, /*implicit*/ true);
1768+
copyAssignExpr->setType(emptyTupleTy);
1769+
1770+
// Call `operator++`.
1771+
auto incrementExpr = createAccessorImplCallExpr(incrementImpl, inoutCopyExpr);
1772+
1773+
auto copyRefRValueExpr = new (ctx) DeclRefExpr(copyDecl, DeclNameLoc(),
1774+
/*implicit*/ true);
1775+
copyRefRValueExpr->setType(copyDecl->getInterfaceType());
1776+
1777+
auto returnStmt = new (ctx) ReturnStmt(SourceLoc(), copyRefRValueExpr,
1778+
/*implicit*/ true);
1779+
1780+
auto body = BraceStmt::create(ctx, SourceLoc(),
1781+
{
1782+
copyDecl,
1783+
patternDecl,
1784+
copyAssignExpr,
1785+
incrementExpr,
1786+
returnStmt,
1787+
},
1788+
SourceLoc());
1789+
return {body, /*isTypeChecked*/ true};
1790+
}
1791+
1792+
FuncDecl *SwiftDeclSynthesizer::makeSuccessorFunc(FuncDecl *incrementFunc) {
1793+
auto &ctx = ImporterImpl.SwiftContext;
1794+
auto dc = incrementFunc->getDeclContext();
1795+
1796+
auto returnTy = incrementFunc->getImplicitSelfDecl()->getInterfaceType();
1797+
1798+
auto nameId = ctx.getIdentifier("successor");
1799+
auto *params = ParameterList::createEmpty(ctx);
1800+
DeclName name(ctx, DeclBaseName(nameId), params);
1801+
1802+
auto result = FuncDecl::createImplicit(
1803+
ctx, StaticSpellingKind::None, name, SourceLoc(),
1804+
/*Async*/ false, /*Throws*/ false,
1805+
/*GenericParams*/ nullptr, params, returnTy, dc);
1806+
1807+
result->setAccess(AccessLevel::Public);
1808+
result->setIsDynamic(false);
1809+
result->setBodySynthesizer(synthesizeSuccessorFuncBody, incrementFunc);
1810+
1811+
return result;
1812+
}
1813+
17231814
// MARK: C++ arithmetic operators
17241815

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

lib/ClangImporter/SwiftDeclSynthesizer.h

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,14 @@ class SwiftDeclSynthesizer {
5151
: ImporterImpl(Impl) {}
5252

5353
/// Create a typedpattern(namedpattern(decl))
54-
Pattern *createTypedNamedPattern(VarDecl *decl);
54+
static Pattern *createTypedNamedPattern(VarDecl *decl);
55+
56+
/// Create a var member for this struct, along with its pattern binding, and
57+
/// add it as a member.
58+
static std::pair<VarDecl *, PatternBindingDecl *>
59+
createVarWithPattern(DeclContext *dc, Identifier name, Type ty,
60+
VarDecl::Introducer introducer, bool isImplicit,
61+
AccessLevel access, AccessLevel setterAccess);
5562

5663
/// Create a new named constant with the given value.
5764
///
@@ -272,6 +279,10 @@ class SwiftDeclSynthesizer {
272279
/// \return computed property declaration
273280
VarDecl *makeDereferencedPointeeProperty(FuncDecl *dereferenceFunc);
274281

282+
/// Given a C++ pre-increment operator (`operator++()`). create a non-mutating
283+
/// function `successor() -> Self`.
284+
FuncDecl *makeSuccessorFunc(FuncDecl *incrementFunc);
285+
275286
FuncDecl *makeOperator(FuncDecl *operatorMethod,
276287
clang::CXXMethodDecl *clangOperator);
277288

@@ -280,13 +291,6 @@ class SwiftDeclSynthesizer {
280291

281292
private:
282293
Type getConstantLiteralType(Type type, ConstantConvertKind convertKind);
283-
284-
/// Create a var member for this struct, along with its pattern binding, and
285-
/// add it as a member.
286-
std::pair<VarDecl *, PatternBindingDecl *>
287-
createVarWithPattern(DeclContext *dc, Identifier name, Type ty,
288-
VarDecl::Introducer introducer, bool isImplicit,
289-
AccessLevel access, AccessLevel setterAccess);
290294
};
291295

292296
} // namespace swift

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ struct LoadableIntWrapper {
1919
int operator()(int x, int y) {
2020
return value + x * y;
2121
}
22+
23+
LoadableIntWrapper &operator++() {
24+
value++;
25+
return *this;
26+
}
2227
};
2328

2429
struct LoadableBoolWrapper {
@@ -43,6 +48,26 @@ struct AddressOnlyIntWrapper {
4348
int operator()(int x, int y) {
4449
return value + x * y;
4550
}
51+
52+
AddressOnlyIntWrapper &operator++() {
53+
value++;
54+
return *this;
55+
}
56+
AddressOnlyIntWrapper operator++(int) {
57+
// This shouldn't be called, since we only support pre-increment operators.
58+
return AddressOnlyIntWrapper(-777);
59+
}
60+
};
61+
62+
struct HasPostIncrementOperator {
63+
HasPostIncrementOperator operator++(int) {
64+
return HasPostIncrementOperator();
65+
}
66+
};
67+
68+
struct HasPreIncrementOperatorWithAnotherReturnType {
69+
int value = 0;
70+
const int &operator++() { return value; }
4671
};
4772

4873
struct HasDeletedOperator {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// RUN: %target-swift-ide-test -print-module -module-to-print=MemberInline -I %S/Inputs -source-filename=x -enable-experimental-cxx-interop | %FileCheck %s
22

33
// CHECK: struct LoadableIntWrapper {
4+
// CHECK: func successor() -> LoadableIntWrapper
45
// CHECK: static func - (lhs: inout LoadableIntWrapper, rhs: LoadableIntWrapper) -> LoadableIntWrapper
56
// CHECK: mutating func callAsFunction() -> Int32
67
// CHECK: mutating func callAsFunction(_ x: Int32) -> Int32

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,9 @@ let nonTrivialValueByVal = nonTrivialIntArrayByVal[1]
4040
var diffTypesArrayByVal = DifferentTypesArrayByVal()
4141
let diffTypesResultIntByVal: Int32 = diffTypesArrayByVal[0]
4242
let diffTypesResultDoubleByVal: Double = diffTypesArrayByVal[0.5]
43+
44+
let postIncrement = HasPostIncrementOperator()
45+
postIncrement.successor() // expected-error {{value of type 'HasPostIncrementOperator' has no member 'successor'}}
46+
47+
let anotherReturnType = HasPreIncrementOperatorWithAnotherReturnType()
48+
anotherReturnType.successor() // expected-error {{value of type 'HasPreIncrementOperatorWithAnotherReturnType' has no member 'successor'}}

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,19 @@ OperatorsTestSuite.test("LoadableIntWrapper.call (inline)") {
3030
expectEqual(57, resultTwoArgs)
3131
}
3232

33+
OperatorsTestSuite.test("LoadableIntWrapper.successor() (inline)") {
34+
var wrapper = LoadableIntWrapper(value: 42)
35+
36+
let result1 = wrapper.successor()
37+
expectEqual(43, result1.value)
38+
expectEqual(42, wrapper.value) // Calling `successor()` should not mutate `wrapper`.
39+
40+
let result2 = result1.successor()
41+
expectEqual(44, result2.value)
42+
expectEqual(43, result1.value)
43+
expectEqual(42, wrapper.value)
44+
}
45+
3346
OperatorsTestSuite.test("LoadableBoolWrapper.exclaim (inline)") {
3447
var wrapper = LoadableBoolWrapper(value: true)
3548

@@ -49,6 +62,19 @@ OperatorsTestSuite.test("AddressOnlyIntWrapper.call (inline)") {
4962
expectEqual(57, resultTwoArgs)
5063
}
5164

65+
OperatorsTestSuite.test("AddressOnlyIntWrapper.successor() (inline)") {
66+
var wrapper = AddressOnlyIntWrapper(0)
67+
68+
let result1 = wrapper.successor()
69+
expectEqual(1, result1.value)
70+
expectEqual(0, wrapper.value) // Calling `successor()` should not mutate `wrapper`.
71+
72+
let result2 = result1.successor()
73+
expectEqual(2, result2.value)
74+
expectEqual(1, result1.value)
75+
expectEqual(0, wrapper.value)
76+
}
77+
5278
OperatorsTestSuite.test("DerivedFromAddressOnlyIntWrapper.call (inline, base class)") {
5379
var wrapper = DerivedFromAddressOnlyIntWrapper(42)
5480

0 commit comments

Comments
 (0)