Skip to content

Commit 2e0398f

Browse files
committed
[cxx-interop] Add support for subscripts in base classes.
1 parent 7462169 commit 2e0398f

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
@@ -4563,7 +4563,7 @@ synthesizeBaseClassFieldGetterBody(AbstractFunctionDecl *afd, void *context) {
45634563
ASTContext &ctx = afd->getASTContext();
45644564

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

45984617
auto ret = new (ctx) ReturnStmt(SourceLoc(), baseMember);
45994618
auto body = BraceStmt::create(ctx, SourceLoc(), {ret}, SourceLoc(),
@@ -4611,7 +4630,7 @@ synthesizeBaseClassFieldGetterBody(AbstractFunctionDecl *afd, void *context) {
46114630
static std::pair<BraceStmt *, bool>
46124631
synthesizeBaseClassFieldSetterBody(AbstractFunctionDecl *afd, void *context) {
46134632
auto setterDecl = cast<AccessorDecl>(afd);
4614-
VarDecl *baseClassVar = static_cast<VarDecl *>(context);
4633+
AbstractStorageDecl *baseClassVar = static_cast<AbstractStorageDecl *>(context);
46154634
ASTContext &ctx = setterDecl->getASTContext();
46164635

46174636
StructDecl *baseStruct =
@@ -4622,16 +4641,30 @@ synthesizeBaseClassFieldSetterBody(AbstractFunctionDecl *afd, void *context) {
46224641
auto *pointeePropertyRefExpr =
46234642
getInOutSelfInteropStaticCast(setterDecl, baseStruct, derivedStruct);
46244643

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

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

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

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

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

4777+
if (auto subscript = dyn_cast<SubscriptDecl>(decl)) {
4778+
auto out = SubscriptDecl::create(
4779+
subscript->getASTContext(), subscript->getName(), subscript->getStaticLoc(),
4780+
subscript->getStaticSpelling(), subscript->getSubscriptLoc(),
4781+
subscript->getIndices(), subscript->getNameLoc(), subscript->getElementInterfaceType(),
4782+
newContext, subscript->getGenericParams());
4783+
out->copyFormalAccessFrom(subscript);
4784+
out->setAccessors(SourceLoc(),
4785+
makeBaseClassMemberAccessors(newContext, out, subscript),
4786+
SourceLoc());
4787+
out->setImplInfo(subscript->getImplInfo());
4788+
return out;
4789+
}
4790+
47204791
if (auto var = dyn_cast<VarDecl>(decl)) {
47214792
auto rawMemory = allocateMemoryForDecl<VarDecl>(var->getASTContext(),
47224793
sizeof(VarDecl), false);
@@ -4728,9 +4799,11 @@ ValueDecl *cloneBaseMemberDecl(ValueDecl *decl, DeclContext *newContext) {
47284799
out->setIsDynamic(var->isDynamic());
47294800
out->copyFormalAccessFrom(var);
47304801
out->setAccessors(SourceLoc(),
4731-
makeBaseClassFieldAccessors(newContext, out, var),
4802+
makeBaseClassMemberAccessors(newContext, out, var),
47324803
SourceLoc());
4733-
out->setImplInfo(StorageImplInfo::getComputed(StorageIsMutable));
4804+
auto isMutable = var->getWriteImpl() == WriteImplKind::Immutable
4805+
? StorageIsNotMutable : StorageIsMutable;
4806+
out->setImplInfo(StorageImplInfo::getComputed(isMutable));
47344807
out->setIsSetterMutating(true);
47354808
return out;
47364809
}

lib/ClangImporter/ImportDecl.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3641,6 +3641,11 @@ namespace {
36413641
// Also add subscripts directly because they won't be found from the
36423642
// clang decl.
36433643
result->addMember(subscript);
3644+
3645+
// Add the subscript as an alternative for the getter so that it gets
3646+
// carried into derived classes.
3647+
auto *subscriptImpl = getterAndSetter.first ? getterAndSetter.first : getterAndSetter.second;
3648+
Impl.addAlternateDecl(subscriptImpl, subscript);
36443649
}
36453650
}
36463651

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)