Skip to content

Commit e4a120b

Browse files
author
Gabor Horvath
committed
[cxx-interop] Support importing subscript operators with multiple params
Previously, the compiler would crash on them. rdar://133541125
1 parent 016e55b commit e4a120b

File tree

6 files changed

+128
-75
lines changed

6 files changed

+128
-75
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2647,22 +2647,23 @@ namespace {
26472647
result->addMember(p);
26482648
}
26492649

2650-
for (auto &subscriptInfo : Impl.cxxSubscripts) {
2651-
auto declAndParameterType = subscriptInfo.first;
2652-
if (declAndParameterType.first != result)
2653-
continue;
2654-
2655-
auto getterAndSetter = subscriptInfo.second;
2656-
auto subscript = synthesizer.makeSubscript(getterAndSetter.first,
2657-
getterAndSetter.second);
2658-
// Also add subscripts directly because they won't be found from the
2659-
// clang decl.
2660-
result->addMember(subscript);
2661-
2662-
// Add the subscript as an alternative for the getter so that it gets
2663-
// carried into derived classes.
2664-
auto *subscriptImpl = getterAndSetter.first ? getterAndSetter.first : getterAndSetter.second;
2665-
Impl.addAlternateDecl(subscriptImpl, subscript);
2650+
auto it = Impl.cxxSubscripts.find(result);
2651+
if (it != Impl.cxxSubscripts.end()) {
2652+
for (auto &subscriptInfo : it->second) {
2653+
auto getterAndSetter = subscriptInfo.second;
2654+
auto subscript = synthesizer.makeSubscript(getterAndSetter.first,
2655+
getterAndSetter.second);
2656+
// Also add subscripts directly because they won't be found from the
2657+
// clang decl.
2658+
result->addMember(subscript);
2659+
2660+
// Add the subscript as an alternative for the getter so that it
2661+
// gets carried into derived classes.
2662+
auto *subscriptImpl = getterAndSetter.first
2663+
? getterAndSetter.first
2664+
: getterAndSetter.second;
2665+
Impl.addAlternateDecl(subscriptImpl, subscript);
2666+
}
26662667
}
26672668

26682669
auto getterAndSetterIt = Impl.cxxDereferenceOperators.find(result);
@@ -3633,14 +3634,16 @@ namespace {
36333634
return true;
36343635

36353636
if (importedName.isSubscriptAccessor()) {
3636-
assert(func->getParameters()->size() == 1);
3637-
auto parameter = func->getParameters()->get(0);
3638-
auto parameterType = parameter->getTypeInContext();
3639-
if (!typeDecl || !parameterType)
3640-
return false;
3641-
if (parameter->isInOut())
3642-
// Subscripts with inout parameters are not allowed in Swift.
3643-
return false;
3637+
SmallVector<TypeBase *> params;
3638+
for (auto parameter : *(func->getParameters())) {
3639+
auto parameterType = parameter->getTypeInContext();
3640+
if (!typeDecl || !parameterType)
3641+
return false;
3642+
if (parameter->isInOut())
3643+
// Subscripts with inout parameters are not allowed in Swift.
3644+
return false;
3645+
params.push_back(parameterType.getPointer());
3646+
}
36443647
// Subscript setter is marked as mutating in Swift even if the
36453648
// C++ `operator []` is `const`.
36463649
if (importedName.getAccessorKind() ==
@@ -3649,7 +3652,8 @@ namespace {
36493652
!typeDecl->getDeclaredType()->isForeignReferenceType())
36503653
func->setSelfAccessKind(SelfAccessKind::Mutating);
36513654

3652-
auto &getterAndSetter = Impl.cxxSubscripts[{typeDecl, parameterType}];
3655+
auto &getterAndSetterMap = Impl.cxxSubscripts[typeDecl];
3656+
auto &getterAndSetter = getterAndSetterMap[params];
36533657

36543658
switch (importedName.getAccessorKind()) {
36553659
case ImportedAccessorKind::SubscriptGetter:

lib/ClangImporter/ImporterImpl.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "swift/AST/ASTContext.h"
2626
#include "swift/AST/Decl.h"
2727
#include "swift/AST/ForeignErrorConvention.h"
28+
#include "swift/AST/Identifier.h"
2829
#include "swift/AST/LazyResolver.h"
2930
#include "swift/AST/Module.h"
3031
#include "swift/AST/RequirementSignature.h"
@@ -683,8 +684,11 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
683684
///
684685
/// `.first` corresponds to a getter
685686
/// `.second` corresponds to a setter
686-
llvm::MapVector<std::pair<NominalTypeDecl *, Type>,
687-
std::pair<FuncDecl *, FuncDecl *>> cxxSubscripts;
687+
llvm::DenseMap<NominalTypeDecl *,
688+
llvm::MapVector<SmallVector<TypeBase *>,
689+
std::pair<FuncDecl *, FuncDecl *>,
690+
std::map<SmallVector<TypeBase *>, unsigned>>>
691+
cxxSubscripts;
688692

689693
llvm::MapVector<NominalTypeDecl *, std::pair<FuncDecl *, FuncDecl *>>
690694
cxxDereferenceOperators;

lib/ClangImporter/SwiftDeclSynthesizer.cpp

Lines changed: 44 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
#include "CXXMethodBridging.h"
1413
#include "SwiftDeclSynthesizer.h"
14+
#include "CXXMethodBridging.h"
1515
#include "swift/AST/ASTMangler.h"
1616
#include "swift/AST/Attr.h"
1717
#include "swift/AST/AttrKind.h"
@@ -47,7 +47,7 @@ static Argument createSelfArg(AccessorDecl *accessorDecl) {
4747

4848
static CallExpr *createAccessorImplCallExpr(FuncDecl *accessorImpl,
4949
Argument selfArg,
50-
DeclRefExpr *keyRefExpr = nullptr) {
50+
ArrayRef<Expr *> keyRefExprs) {
5151
ASTContext &ctx = accessorImpl->getASTContext();
5252

5353
auto accessorImplExpr =
@@ -60,12 +60,8 @@ static CallExpr *createAccessorImplCallExpr(FuncDecl *accessorImpl,
6060
accessorImplDotCallExpr->setType(accessorImpl->getMethodInterfaceType());
6161
accessorImplDotCallExpr->setThrows(nullptr);
6262

63-
ArgumentList *argList;
64-
if (keyRefExpr) {
65-
argList = ArgumentList::forImplicitUnlabeled(ctx, {keyRefExpr});
66-
} else {
67-
argList = ArgumentList::forImplicitUnlabeled(ctx, {});
68-
}
63+
ArgumentList *argList = ArgumentList::forImplicitUnlabeled(ctx, keyRefExprs);
64+
6965
auto *accessorImplCallExpr =
7066
CallExpr::createImplicit(ctx, accessorImplDotCallExpr, argList);
7167
accessorImplCallExpr->setType(accessorImpl->getResultInterfaceType());
@@ -1556,33 +1552,36 @@ synthesizeUnwrappingGetterOrAddressGetterBody(AbstractFunctionDecl *afd,
15561552
ASTContext &ctx = getterDecl->getASTContext();
15571553

15581554
auto selfArg = createSelfArg(getterDecl);
1559-
DeclRefExpr *keyRefExpr = getterDecl->getParameters()->size() == 0
1560-
? nullptr
1561-
: createParamRefExpr(getterDecl, 0);
1555+
SmallVector<Expr *> arguments;
1556+
for (size_t idx = 0, end = getterDecl->getParameters()->size(); idx < end;
1557+
++idx)
1558+
arguments.push_back(createParamRefExpr(getterDecl, idx));
15621559

15631560
Type elementTy = getterDecl->getResultInterfaceType();
15641561

15651562
auto *getterImplCallExpr =
1566-
createAccessorImplCallExpr(getterImpl, selfArg, keyRefExpr);
1563+
createAccessorImplCallExpr(getterImpl, selfArg, arguments);
15671564

15681565
// This default handles C++'s operator[] that returns a value type.
15691566
Expr *propertyExpr = getterImplCallExpr;
15701567
PointerTypeKind ptrKind;
15711568

1572-
// The following check returns true if the subscript operator returns a C++
1573-
// reference type. This check actually checks to see if the type is a pointer
1574-
// type, but this does not apply to C pointers because they are Optional types
1575-
// when imported. TODO: Use a more obvious check here.
1569+
// The following check returns true if the subscript operator returns a
1570+
// C++ reference type. This check actually checks to see if the type is
1571+
// a pointer type, but this does not apply to C pointers because they
1572+
// are Optional types when imported. TODO: Use a more obvious check
1573+
// here.
15761574
if (!isAddress &&
15771575
getterImpl->getResultInterfaceType()->getAnyPointerElementType(ptrKind)) {
1578-
// `getterImpl` can return either UnsafePointer or UnsafeMutablePointer.
1579-
// Retrieve the corresponding `.pointee` declaration.
1576+
// `getterImpl` can return either UnsafePointer or
1577+
// UnsafeMutablePointer. Retrieve the corresponding `.pointee`
1578+
// declaration.
15801579
VarDecl *pointeePropertyDecl = ctx.getPointerPointeePropertyDecl(ptrKind);
15811580

15821581
// Handle operator[] that returns a reference type.
1583-
SubstitutionMap subMap = SubstitutionMap::get(
1584-
ctx.getUnsafePointerDecl()->getGenericSignature(), {elementTy},
1585-
LookUpConformanceInModule());
1582+
SubstitutionMap subMap =
1583+
SubstitutionMap::get(ctx.getUnsafePointerDecl()->getGenericSignature(),
1584+
{elementTy}, LookUpConformanceInModule());
15861585
auto pointeePropertyRefExpr = new (ctx) MemberRefExpr(
15871586
getterImplCallExpr, SourceLoc(),
15881587
ConcreteDeclRef(pointeePropertyDecl, subMap), DeclNameLoc(),
@@ -1627,16 +1626,15 @@ synthesizeUnwrappingSetterBody(AbstractFunctionDecl *afd, void *context) {
16271626

16281627
auto selfArg = createSelfArg(setterDecl);
16291628
DeclRefExpr *valueParamRefExpr = createParamRefExpr(setterDecl, 0);
1630-
// For a subscript this decl will have two parameters, for a pointee property
1631-
// it will only have one.
1632-
DeclRefExpr *keyParamRefExpr = setterDecl->getParameters()->size() == 1
1633-
? nullptr
1634-
: createParamRefExpr(setterDecl, 1);
1629+
SmallVector<Expr *> arguments;
1630+
for (size_t idx = 1, end = setterDecl->getParameters()->size(); idx < end;
1631+
++idx)
1632+
arguments.push_back(createParamRefExpr(setterDecl, idx));
16351633

16361634
Type elementTy = valueParamRefExpr->getDecl()->getInterfaceType();
16371635

16381636
auto *setterImplCallExpr =
1639-
createAccessorImplCallExpr(setterImpl, selfArg, keyParamRefExpr);
1637+
createAccessorImplCallExpr(setterImpl, selfArg, arguments);
16401638

16411639
VarDecl *pointeePropertyDecl =
16421640
ctx.getPointerPointeePropertyDecl(PTK_UnsafeMutablePointer);
@@ -1673,7 +1671,7 @@ synthesizeUnwrappingAddressSetterBody(AbstractFunctionDecl *afd,
16731671

16741672
auto selfArg = createSelfArg(setterDecl);
16751673
auto *setterImplCallExpr =
1676-
createAccessorImplCallExpr(setterImpl, selfArg, nullptr);
1674+
createAccessorImplCallExpr(setterImpl, selfArg, {});
16771675

16781676
auto *returnStmt = ReturnStmt::createImplicit(ctx, setterImplCallExpr);
16791677

@@ -1701,15 +1699,16 @@ SubscriptDecl *SwiftDeclSynthesizer::makeSubscript(FuncDecl *getter,
17011699

17021700
auto &ctx = ImporterImpl.SwiftContext;
17031701

1704-
assert(getterImpl->getParameters()->size() == 1 &&
1705-
"subscript can only have 1 parameter");
1706-
auto bodyParam = ParamDecl::clone(ctx, getterImpl->getParameters()->get(0));
1707-
// If the subscript parameter is unnamed, give it a name to make sure SILGen
1708-
// creates a variable for it.
1709-
if (bodyParam->getName().empty())
1710-
bodyParam->setName(ctx.getIdentifier("__index"));
1711-
1712-
auto bodyParams = ParameterList::create(ctx, bodyParam);
1702+
SmallVector<ParamDecl *> paramVec;
1703+
for (auto [i, param] : llvm::enumerate(*getterImpl->getParameters())) {
1704+
auto clonedParam = ParamDecl::clone(ctx, param);
1705+
// If the subscript parameter is unnamed, give it a name to make sure SILGen
1706+
// creates a variable for it.
1707+
if (clonedParam->getName().empty())
1708+
clonedParam->setName(ctx.getIdentifier("__index" + std::to_string(i)));
1709+
paramVec.push_back(clonedParam);
1710+
}
1711+
auto bodyParams = ParameterList::create(ctx, paramVec);
17131712
DeclName name(ctx, DeclBaseName::createSubscript(), bodyParams);
17141713
auto dc = getterImpl->getDeclContext();
17151714

@@ -1744,8 +1743,11 @@ SubscriptDecl *SwiftDeclSynthesizer::makeSubscript(FuncDecl *getter,
17441743
paramVarDecl->setSpecifier(ParamSpecifier::Default);
17451744
paramVarDecl->setInterfaceType(elementTy);
17461745

1747-
auto setterParamList =
1748-
ParameterList::create(ctx, {paramVarDecl, bodyParams->get(0)});
1746+
SmallVector<ParamDecl *> setterParams;
1747+
setterParams.push_back(paramVarDecl);
1748+
setterParams.append(bodyParams->begin(), bodyParams->end());
1749+
1750+
auto setterParamList = ParameterList::create(ctx, setterParams);
17491751

17501752
setterDecl = AccessorDecl::create(
17511753
ctx, setterImpl->getLoc(), setterImpl->getLoc(), AccessorKind::Set,
@@ -1918,7 +1920,7 @@ synthesizeSuccessorFuncBody(AbstractFunctionDecl *afd, void *context) {
19181920

19191921
// Call `operator++`.
19201922
auto incrementExpr = createAccessorImplCallExpr(
1921-
incrementImpl, Argument::implicitInOut(ctx, copyRefLValueExpr));
1923+
incrementImpl, Argument::implicitInOut(ctx, copyRefLValueExpr), {});
19221924

19231925
auto copyRefRValueExpr = new (ctx) DeclRefExpr(copyDecl, DeclNameLoc(),
19241926
/*implicit*/ true);
@@ -2331,7 +2333,7 @@ synthesizeComputedGetterFromCXXMethod(AbstractFunctionDecl *afd,
23312333

23322334
auto selfArg = createSelfArg(accessor);
23332335

2334-
auto *getterImplCallExpr = createAccessorImplCallExpr(method, selfArg);
2336+
auto *getterImplCallExpr = createAccessorImplCallExpr(method, selfArg, {});
23352337
auto &ctx = method->getASTContext();
23362338
auto *returnStmt = ReturnStmt::createImplicit(ctx, getterImplCallExpr);
23372339
auto *body = BraceStmt::create(ctx, SourceLoc(), {returnStmt}, SourceLoc());
@@ -2349,7 +2351,7 @@ synthesizeComputedSetterFromCXXMethod(AbstractFunctionDecl *afd,
23492351
DeclRefExpr *valueParamRefExpr = createParamRefExpr(setterDecl, 0);
23502352

23512353
auto *getterImplCallExpr =
2352-
createAccessorImplCallExpr(setterImpl, selfArg, valueParamRefExpr);
2354+
createAccessorImplCallExpr(setterImpl, selfArg, {valueParamRefExpr});
23532355

23542356
auto body = BraceStmt::create(setterImpl->getASTContext(), SourceLoc(),
23552357
{getterImplCallExpr}, SourceLoc());

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,20 @@ struct ReadWriteIntArray {
157157
};
158158
};
159159

160+
#if __cplusplus >= 202302L
161+
struct NullarySubscript {
162+
int operator[]() const { return 42; }
163+
int &operator[]() { return field; }
164+
int field;
165+
};
166+
167+
struct BinarySubscript {
168+
int operator[](int x, int y) const { return x + y; }
169+
int &operator[](int, int) { return field; }
170+
int field;
171+
};
172+
#endif
173+
160174
struct __attribute__((swift_attr("import_owned"))) ReadOnlyIntArray {
161175
private:
162176
int values[5] = { 1, 2, 3, 4, 5 };

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

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// RUN: %target-swift-ide-test -print-module -module-to-print=MemberInline -I %S/Inputs -source-filename=x -cxx-interoperability-mode=swift-5.9 | %FileCheck %s
2-
// RUN: %target-swift-ide-test -print-module -module-to-print=MemberInline -I %S/Inputs -source-filename=x -cxx-interoperability-mode=swift-6 | %FileCheck %s
3-
// RUN: %target-swift-ide-test -print-module -module-to-print=MemberInline -I %S/Inputs -source-filename=x -cxx-interoperability-mode=upcoming-swift | %FileCheck %s
1+
// RUN: %target-swift-ide-test -print-module -module-to-print=MemberInline -I %S/Inputs -source-filename=x -cxx-interoperability-mode=swift-5.9 -Xcc -std=c++23 | %FileCheck %s
2+
// RUN: %target-swift-ide-test -print-module -module-to-print=MemberInline -I %S/Inputs -source-filename=x -cxx-interoperability-mode=swift-6 -Xcc -std=c++23 | %FileCheck %s
3+
// RUN: %target-swift-ide-test -print-module -module-to-print=MemberInline -I %S/Inputs -source-filename=x -cxx-interoperability-mode=upcoming-swift -Xcc -std=c++23 | %FileCheck %s
44

55
// CHECK: struct LoadableIntWrapper {
66
// CHECK: func successor() -> LoadableIntWrapper
@@ -58,6 +58,21 @@
5858
// CHECK: }
5959
// CHECK: }
6060

61+
// CHECK: struct NullarySubscript {
62+
// CHECK: subscript() -> Int32
63+
// CHECK: @available(*, unavailable, message: "use subscript")
64+
// CHECK: func __operatorSubscriptConst() -> Int32
65+
// CHECK: @available(*, unavailable, message: "use subscript")
66+
// CHECK: mutating func __operatorSubscript() -> UnsafeMutablePointer<Int32>
67+
// CHECK: }
68+
69+
// CHECK: struct BinarySubscript {
70+
// CHECK: subscript(x: Int32, y: Int32) -> Int32
71+
// CHECK: @available(*, unavailable, message: "use subscript")
72+
// CHECK: func __operatorSubscriptConst(_ x: Int32, _ y: Int32) -> Int32
73+
// CHECK: @available(*, unavailable, message: "use subscript")
74+
// CHECK: mutating func __operatorSubscript(_: Int32, _: Int32) -> UnsafeMutablePointer<Int32>
75+
// CHECK: }
6176

6277
// CHECK: struct ReadOnlyIntArray {
6378
// CHECK: subscript(x: Int32) -> Int32 { get }
@@ -212,10 +227,10 @@
212227
// CHECK: }
213228

214229
// CHECK: struct SubscriptUnnamedParameter {
215-
// CHECK: subscript(__index: Int32) -> Int32 { get }
230+
// CHECK: subscript(__index0: Int32) -> Int32 { get }
216231
// CHECK: }
217232
// CHECK: struct SubscriptUnnamedParameterReadWrite {
218-
// CHECK: subscript(__index: Int32) -> Int32
233+
// CHECK: subscript(__index0: Int32) -> Int32
219234
// CHECK: }
220235

221236
// CHECK: struct Iterator {

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -cxx-interoperability-mode=swift-5.9)
22
// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -cxx-interoperability-mode=swift-6)
3-
// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -cxx-interoperability-mode=upcoming-swift)
3+
// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -cxx-interoperability-mode=upcoming-swift -Xcc -std=c++23 -D CPP23)
44
//
55
// REQUIRES: executable_test
66

@@ -185,6 +185,20 @@ OperatorsTestSuite.test("ReadWriteIntArray.subscript (inline)") {
185185
expectEqual(234, resultAfter)
186186
}
187187

188+
#if CPP23
189+
OperatorsTestSuite.test("Subscript operators with parameter number != 1") {
190+
var ns = NullarySubscript()
191+
expectEqual(42, ns[])
192+
ns[] = 5
193+
expectEqual(5, ns.field)
194+
195+
var bs = BinarySubscript()
196+
expectEqual(10, bs[3, 7])
197+
bs[1, 2] = 6
198+
expectEqual(6, bs.field)
199+
}
200+
#endif
201+
188202
OperatorsTestSuite.test("DerivedFromReadWriteIntArray.subscript (inline, base class)") {
189203
var arr = DerivedFromReadWriteIntArray()
190204

0 commit comments

Comments
 (0)