Skip to content

Commit 10b5031

Browse files
authored
Merge pull request #41610 from zoecarver/import-subscript-in-base
[cxx-interop] Add support for subscripts in base classes.
2 parents 067a9d1 + 2e0398f commit 10b5031

File tree

7 files changed

+189
-23
lines changed

7 files changed

+189
-23
lines changed

include/swift/AST/Decl.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5930,6 +5930,14 @@ class SubscriptDecl : public GenericContext, public AbstractStorageDecl {
59305930
DeclContext *Parent,
59315931
GenericParamList *GenericParams);
59325932

5933+
static SubscriptDecl *create(ASTContext &Context, DeclName Name,
5934+
SourceLoc StaticLoc,
5935+
StaticSpellingKind StaticSpelling,
5936+
SourceLoc SubscriptLoc, ParameterList *Indices,
5937+
SourceLoc ArrowLoc, Type ElementTy,
5938+
DeclContext *Parent,
5939+
GenericParamList *GenericParams);
5940+
59335941
static SubscriptDecl *createImported(ASTContext &Context, DeclName Name,
59345942
SourceLoc SubscriptLoc,
59355943
ParameterList *Indices,

lib/AST/Decl.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7357,6 +7357,20 @@ SubscriptDecl *SubscriptDecl::create(ASTContext &Context, DeclName Name,
73577357
return SD;
73587358
}
73597359

7360+
SubscriptDecl *SubscriptDecl::create(ASTContext &Context, DeclName Name,
7361+
SourceLoc StaticLoc,
7362+
StaticSpellingKind StaticSpelling,
7363+
SourceLoc SubscriptLoc,
7364+
ParameterList *Indices, SourceLoc ArrowLoc,
7365+
Type ElementTy, DeclContext *Parent,
7366+
GenericParamList *GenericParams) {
7367+
auto *const SD = new (Context)
7368+
SubscriptDecl(Name, StaticLoc, StaticSpelling, SubscriptLoc, Indices,
7369+
ArrowLoc, nullptr, Parent, GenericParams);
7370+
SD->setElementInterfaceType(ElementTy);
7371+
return SD;
7372+
}
7373+
73607374
SubscriptDecl *SubscriptDecl::createImported(ASTContext &Context, DeclName Name,
73617375
SourceLoc SubscriptLoc,
73627376
ParameterList *Indices,

lib/ClangImporter/ClangImporter.cpp

Lines changed: 96 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4564,7 +4564,7 @@ synthesizeBaseClassFieldGetterBody(AbstractFunctionDecl *afd, void *context) {
45644564
ASTContext &ctx = afd->getASTContext();
45654565

45664566
AccessorDecl *getterDecl = cast<AccessorDecl>(afd);
4567-
VarDecl *baseClassVar = static_cast<VarDecl *>(context);
4567+
AbstractStorageDecl *baseClassVar = static_cast<AbstractStorageDecl *>(context);
45684568
StructDecl *baseStruct =
45694569
cast<StructDecl>(baseClassVar->getDeclContext()->getAsDecl());
45704570
StructDecl *derivedStruct =
@@ -4591,10 +4591,29 @@ synthesizeBaseClassFieldGetterBody(AbstractFunctionDecl *afd, void *context) {
45914591
AccessSemantics accessKind = baseClassVar->getClangDecl()
45924592
? AccessSemantics::DirectToStorage
45934593
: AccessSemantics::DirectToImplementation;
4594-
auto baseMember =
4595-
new (ctx) MemberRefExpr(casted, SourceLoc(), baseClassVar, DeclNameLoc(),
4596-
/*Implicit=*/true, accessKind);
4597-
baseMember->setType(baseClassVar->getType());
4594+
Expr *baseMember = nullptr;
4595+
if (auto subscript = dyn_cast<SubscriptDecl>(baseClassVar)) {
4596+
auto paramDecl = getterDecl->getParameters()->get(0);
4597+
auto paramRefExpr = new (ctx) DeclRefExpr(paramDecl,
4598+
DeclNameLoc(),
4599+
/*Implicit=*/ true);
4600+
paramRefExpr->setType(paramDecl->getType());
4601+
4602+
auto *argList = ArgumentList::forImplicitUnlabeled(ctx, {paramRefExpr});
4603+
baseMember = SubscriptExpr::create(ctx, casted, argList, subscript);
4604+
baseMember->setType(subscript->getElementInterfaceType());
4605+
} else {
4606+
// If the base class var has a clang decl, that means it's an access into a
4607+
// stored field. Otherwise, we're looking into another base class, so it's a
4608+
// another synthesized accessor.
4609+
AccessSemantics accessKind = baseClassVar->getClangDecl()
4610+
? AccessSemantics::DirectToStorage
4611+
: AccessSemantics::DirectToImplementation;
4612+
baseMember =
4613+
new (ctx) MemberRefExpr(casted, SourceLoc(), baseClassVar, DeclNameLoc(),
4614+
/*Implicit=*/true, accessKind);
4615+
baseMember->setType(cast<VarDecl>(baseClassVar)->getType());
4616+
}
45984617

45994618
auto ret = new (ctx) ReturnStmt(SourceLoc(), baseMember);
46004619
auto body = BraceStmt::create(ctx, SourceLoc(), {ret}, SourceLoc(),
@@ -4612,7 +4631,7 @@ synthesizeBaseClassFieldGetterBody(AbstractFunctionDecl *afd, void *context) {
46124631
static std::pair<BraceStmt *, bool>
46134632
synthesizeBaseClassFieldSetterBody(AbstractFunctionDecl *afd, void *context) {
46144633
auto setterDecl = cast<AccessorDecl>(afd);
4615-
VarDecl *baseClassVar = static_cast<VarDecl *>(context);
4634+
AbstractStorageDecl *baseClassVar = static_cast<AbstractStorageDecl *>(context);
46164635
ASTContext &ctx = setterDecl->getASTContext();
46174636

46184637
StructDecl *baseStruct =
@@ -4623,16 +4642,30 @@ synthesizeBaseClassFieldSetterBody(AbstractFunctionDecl *afd, void *context) {
46234642
auto *pointeePropertyRefExpr =
46244643
getInOutSelfInteropStaticCast(setterDecl, baseStruct, derivedStruct);
46254644

4626-
// If the base class var has a clang decl, that means it's an access into a
4627-
// stored field. Otherwise, we're looking into another base class, so it's a
4628-
// another synthesized accessor.
4629-
AccessSemantics accessKind = baseClassVar->getClangDecl()
4630-
? AccessSemantics::DirectToStorage
4631-
: AccessSemantics::DirectToImplementation;
4632-
auto storedRef =
4633-
new (ctx) MemberRefExpr(pointeePropertyRefExpr, SourceLoc(), baseClassVar,
4634-
DeclNameLoc(), /*Implicit=*/true, accessKind);
4635-
storedRef->setType(LValueType::get(baseClassVar->getType()));
4645+
Expr *storedRef = nullptr;
4646+
if (auto subscript = dyn_cast<SubscriptDecl>(baseClassVar)) {
4647+
auto paramDecl = setterDecl->getParameters()->get(1);
4648+
auto paramRefExpr = new (ctx) DeclRefExpr(paramDecl,
4649+
DeclNameLoc(),
4650+
/*Implicit=*/ true);
4651+
paramRefExpr->setType(paramDecl->getType());
4652+
4653+
auto *argList = ArgumentList::forImplicitUnlabeled(ctx, {paramRefExpr});
4654+
storedRef = SubscriptExpr::create(ctx, pointeePropertyRefExpr, argList, subscript);
4655+
storedRef->setType(subscript->getElementInterfaceType());
4656+
} else {
4657+
// If the base class var has a clang decl, that means it's an access into a
4658+
// stored field. Otherwise, we're looking into another base class, so it's a
4659+
// another synthesized accessor.
4660+
AccessSemantics accessKind = baseClassVar->getClangDecl()
4661+
? AccessSemantics::DirectToStorage
4662+
: AccessSemantics::DirectToImplementation;
4663+
4664+
storedRef =
4665+
new (ctx) MemberRefExpr(pointeePropertyRefExpr, SourceLoc(), baseClassVar,
4666+
DeclNameLoc(), /*Implicit=*/true, accessKind);
4667+
storedRef->setType(LValueType::get(cast<VarDecl>(baseClassVar)->getType()));
4668+
}
46364669

46374670
auto newValueParamRefExpr =
46384671
new (ctx) DeclRefExpr(setterDecl->getParameters()->get(0), DeclNameLoc(),
@@ -4649,12 +4682,23 @@ synthesizeBaseClassFieldSetterBody(AbstractFunctionDecl *afd, void *context) {
46494682
return {body, /*isTypeChecked=*/true};
46504683
}
46514684

4652-
static std::array<AccessorDecl *, 2>
4653-
makeBaseClassFieldAccessors(DeclContext *declContext, VarDecl *computedVar,
4654-
VarDecl *baseClassVar) {
4685+
static SmallVector<AccessorDecl *, 2>
4686+
makeBaseClassMemberAccessors(DeclContext *declContext,
4687+
AbstractStorageDecl *computedVar,
4688+
AbstractStorageDecl *baseClassVar) {
46554689
auto &ctx = declContext->getASTContext();
46564690
auto computedType = computedVar->getInterfaceType();
46574691

4692+
ParameterList *bodyParams = nullptr;
4693+
if (auto subscript = dyn_cast<SubscriptDecl>(baseClassVar)) {
4694+
computedType = computedType->getAs<FunctionType>()->getResult();
4695+
4696+
auto idxParam = subscript->getIndices()->get(0);
4697+
bodyParams = ParameterList::create(ctx, { idxParam });
4698+
} else {
4699+
bodyParams = ParameterList::createEmpty(ctx);
4700+
}
4701+
46584702
auto getterDecl = AccessorDecl::create(
46594703
ctx,
46604704
/*FuncLoc=*/SourceLoc(),
@@ -4664,18 +4708,31 @@ makeBaseClassFieldAccessors(DeclContext *declContext, VarDecl *computedVar,
46644708
/*Async=*/false, /*AsyncLoc=*/SourceLoc(),
46654709
/*Throws=*/false,
46664710
/*ThrowsLoc=*/SourceLoc(),
4667-
/*GenericParams=*/nullptr, ParameterList::createEmpty(ctx), computedType,
4711+
/*GenericParams=*/nullptr, bodyParams, computedType,
46684712
declContext);
46694713
getterDecl->setIsTransparent(true);
46704714
getterDecl->setAccess(AccessLevel::Public);
46714715
getterDecl->setBodySynthesizer(synthesizeBaseClassFieldGetterBody,
46724716
baseClassVar);
46734717

4718+
if (baseClassVar->getWriteImpl() == WriteImplKind::Immutable)
4719+
return {getterDecl};
4720+
46744721
auto newValueParam =
46754722
new (ctx) ParamDecl(SourceLoc(), SourceLoc(), Identifier(), SourceLoc(),
46764723
ctx.getIdentifier("newValue"), declContext);
46774724
newValueParam->setSpecifier(ParamSpecifier::Default);
46784725
newValueParam->setInterfaceType(computedType);
4726+
4727+
ParameterList *setterBodyParams = nullptr;
4728+
if (auto subscript = dyn_cast<SubscriptDecl>(baseClassVar)) {
4729+
auto idxParam = subscript->getIndices()->get(0);
4730+
bodyParams = ParameterList::create(ctx, { idxParam });
4731+
setterBodyParams = ParameterList::create(ctx, { newValueParam, idxParam });
4732+
} else {
4733+
setterBodyParams = ParameterList::create(ctx, { newValueParam });
4734+
}
4735+
46794736
auto setterDecl = AccessorDecl::create(
46804737
ctx,
46814738
/*FuncLoc=*/SourceLoc(),
@@ -4685,7 +4742,7 @@ makeBaseClassFieldAccessors(DeclContext *declContext, VarDecl *computedVar,
46854742
/*Async=*/false, /*AsyncLoc=*/SourceLoc(),
46864743
/*Throws=*/false,
46874744
/*ThrowsLoc=*/SourceLoc(),
4688-
/*GenericParams=*/nullptr, ParameterList::create(ctx, {newValueParam}),
4745+
/*GenericParams=*/nullptr, setterBodyParams,
46894746
TupleType::getEmpty(ctx), declContext);
46904747
setterDecl->setIsTransparent(true);
46914748
setterDecl->setAccess(AccessLevel::Public);
@@ -4718,6 +4775,20 @@ ValueDecl *cloneBaseMemberDecl(ValueDecl *decl, DeclContext *newContext) {
47184775
return out;
47194776
}
47204777

4778+
if (auto subscript = dyn_cast<SubscriptDecl>(decl)) {
4779+
auto out = SubscriptDecl::create(
4780+
subscript->getASTContext(), subscript->getName(), subscript->getStaticLoc(),
4781+
subscript->getStaticSpelling(), subscript->getSubscriptLoc(),
4782+
subscript->getIndices(), subscript->getNameLoc(), subscript->getElementInterfaceType(),
4783+
newContext, subscript->getGenericParams());
4784+
out->copyFormalAccessFrom(subscript);
4785+
out->setAccessors(SourceLoc(),
4786+
makeBaseClassMemberAccessors(newContext, out, subscript),
4787+
SourceLoc());
4788+
out->setImplInfo(subscript->getImplInfo());
4789+
return out;
4790+
}
4791+
47214792
if (auto var = dyn_cast<VarDecl>(decl)) {
47224793
auto rawMemory = allocateMemoryForDecl<VarDecl>(var->getASTContext(),
47234794
sizeof(VarDecl), false);
@@ -4729,9 +4800,11 @@ ValueDecl *cloneBaseMemberDecl(ValueDecl *decl, DeclContext *newContext) {
47294800
out->setIsDynamic(var->isDynamic());
47304801
out->copyFormalAccessFrom(var);
47314802
out->setAccessors(SourceLoc(),
4732-
makeBaseClassFieldAccessors(newContext, out, var),
4803+
makeBaseClassMemberAccessors(newContext, out, var),
47334804
SourceLoc());
4734-
out->setImplInfo(StorageImplInfo::getComputed(StorageIsMutable));
4805+
auto isMutable = var->getWriteImpl() == WriteImplKind::Immutable
4806+
? StorageIsNotMutable : StorageIsMutable;
4807+
out->setImplInfo(StorageImplInfo::getComputed(isMutable));
47354808
out->setIsSetterMutating(true);
47364809
return out;
47374810
}

lib/ClangImporter/ImportDecl.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3753,6 +3753,11 @@ namespace {
37533753
// Also add subscripts directly because they won't be found from the
37543754
// clang decl.
37553755
result->addMember(subscript);
3756+
3757+
// Add the subscript as an alternative for the getter so that it gets
3758+
// carried into derived classes.
3759+
auto *subscriptImpl = getterAndSetter.first ? getterAndSetter.first : getterAndSetter.second;
3760+
Impl.addAlternateDecl(subscriptImpl, subscript);
37563761
}
37573762
}
37583763

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
#ifndef TEST_INTEROP_CXX_OPERATORS_INPUTS_MEMBER_INLINE_H
22
#define TEST_INTEROP_CXX_OPERATORS_INPUTS_MEMBER_INLINE_H
33

4+
template <class From, class To>
5+
To __swift_interopStaticCast(From from) { return from; }
6+
47
struct LoadableIntWrapper {
58
int value;
69
LoadableIntWrapper operator-(LoadableIntWrapper rhs) {
@@ -243,4 +246,12 @@ struct ConstPtrByVal {
243246
int a = 64;
244247
};
245248

249+
struct DerivedFromAddressOnlyIntWrapper : AddressOnlyIntWrapper {
250+
DerivedFromAddressOnlyIntWrapper(int value) : AddressOnlyIntWrapper(value) { }
251+
};
252+
253+
struct DerivedFromReadWriteIntArray : ReadWriteIntArray {};
254+
255+
struct DerivedFromNonTrivialArrayByVal : NonTrivialArrayByVal {};
256+
246257
#endif

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,3 +166,20 @@
166166
// CHECK: @available(*, unavailable, message: "use subscript")
167167
// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> UnsafePointer<Int32>!
168168
// CHECK: }
169+
170+
// CHECK: struct DerivedFromAddressOnlyIntWrapper {
171+
// CHECK: mutating func callAsFunction() -> Int32
172+
// CHECK: mutating func callAsFunction(_ x: Int32) -> Int32
173+
// CHECK: mutating func callAsFunction(_ x: Int32, _ y: Int32) -> Int32
174+
// CHECK: }
175+
176+
// CHECK: struct DerivedFromReadWriteIntArray {
177+
// CHECK: subscript(x: Int32) -> Int32
178+
// CHECK: func __operatorSubscriptConst(_ x: Int32) -> UnsafePointer<Int32>
179+
// CHECK: mutating func __operatorSubscript(_ x: Int32) -> UnsafeMutablePointer<Int32>
180+
// CHECK: }
181+
182+
// CHECK: struct DerivedFromNonTrivialArrayByVal {
183+
// CHECK: subscript(x: Int32) -> NonTrivial { get }
184+
// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> NonTrivial
185+
// CHECK: }

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,18 @@ OperatorsTestSuite.test("AddressOnlyIntWrapper.call (inline)") {
4242
expectEqual(57, resultTwoArgs)
4343
}
4444

45+
OperatorsTestSuite.test("DerivedFromAddressOnlyIntWrapper.call (inline, base class)") {
46+
var wrapper = DerivedFromAddressOnlyIntWrapper(42)
47+
48+
let resultNoArgs = wrapper()
49+
let resultOneArg = wrapper(23)
50+
let resultTwoArgs = wrapper(3, 5)
51+
52+
expectEqual(42, resultNoArgs)
53+
expectEqual(65, resultOneArg)
54+
expectEqual(57, resultTwoArgs)
55+
}
56+
4557
OperatorsTestSuite.test("ReadWriteIntArray.subscript (inline)") {
4658
var arr = ReadWriteIntArray()
4759

@@ -54,6 +66,18 @@ OperatorsTestSuite.test("ReadWriteIntArray.subscript (inline)") {
5466
expectEqual(234, resultAfter)
5567
}
5668

69+
OperatorsTestSuite.test("DerivedFromReadWriteIntArray.subscript (inline, base class)") {
70+
var arr = DerivedFromReadWriteIntArray()
71+
72+
let resultBefore = arr[1]
73+
expectEqual(2, resultBefore)
74+
75+
arr[1] = 234
76+
77+
let resultAfter = arr[1]
78+
expectEqual(234, resultAfter)
79+
}
80+
5781
OperatorsTestSuite.test("ReadOnlyIntArray.subscript (inline)") {
5882
let arr = ReadOnlyIntArray(1)
5983

@@ -145,6 +169,20 @@ OperatorsTestSuite.test("NonTrivialArrayByVal.subscript (inline)") {
145169
expectEqual(5, NonTrivialByVal.e)
146170
expectEqual(6, NonTrivialByVal.f)
147171
}
172+
173+
OperatorsTestSuite.test("DerivedFromNonTrivialArrayByVal.subscript (inline, base class)") {
174+
var arr = DerivedFromNonTrivialArrayByVal()
175+
let NonTrivialByVal = arr[0];
176+
let cStr = NonTrivialByVal.Str!
177+
expectEqual("Non-Trivial", String(cString: cStr))
178+
179+
expectEqual(1, NonTrivialByVal.a)
180+
expectEqual(2, NonTrivialByVal.b)
181+
expectEqual(3, NonTrivialByVal.c)
182+
expectEqual(4, NonTrivialByVal.d)
183+
expectEqual(5, NonTrivialByVal.e)
184+
expectEqual(6, NonTrivialByVal.f)
185+
}
148186
#endif
149187

150188
OperatorsTestSuite.test("PtrByVal.subscript (inline)") {

0 commit comments

Comments
 (0)