Skip to content

Commit 586c675

Browse files
committed
C++ Interop: import subscript operators
This change adds support for calling `operator[]` from Swift code via a synthesized Swift subscript. Fixes SR-12598.
1 parent 2226ea7 commit 586c675

13 files changed

+655
-5
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 286 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3642,9 +3642,21 @@ namespace {
36423642

36433643
result->setHasUnreferenceableStorage(hasUnreferenceableStorage);
36443644

3645-
if (cxxRecordDecl)
3645+
if (cxxRecordDecl) {
36463646
result->setIsCxxNonTrivial(!cxxRecordDecl->isTriviallyCopyable());
36473647

3648+
for (auto &subscriptInfo : Impl.cxxSubscripts) {
3649+
auto declAndParameterType = subscriptInfo.first;
3650+
if (declAndParameterType.first != result)
3651+
continue;
3652+
3653+
auto getterAndSetter = subscriptInfo.second;
3654+
auto subscript = makeSubscript(getterAndSetter.first,
3655+
getterAndSetter.second);
3656+
result->addMember(subscript);
3657+
}
3658+
}
3659+
36483660
return result;
36493661
}
36503662

@@ -3917,12 +3929,10 @@ namespace {
39173929
AbstractStorageDecl *owningStorage;
39183930
switch (importedName.getAccessorKind()) {
39193931
case ImportedAccessorKind::None:
3920-
owningStorage = nullptr;
3921-
break;
3922-
39233932
case ImportedAccessorKind::SubscriptGetter:
39243933
case ImportedAccessorKind::SubscriptSetter:
3925-
llvm_unreachable("Not possible for a function");
3934+
owningStorage = nullptr;
3935+
break;
39263936

39273937
case ImportedAccessorKind::PropertyGetter: {
39283938
auto property = getImplicitProperty(importedName, decl);
@@ -4122,6 +4132,30 @@ namespace {
41224132
func->setImportAsStaticMember();
41234133
}
41244134
}
4135+
4136+
if (importedName.isSubscriptAccessor()) {
4137+
assert(func->getParameters()->size() == 1);
4138+
auto typeDecl = dc->getSelfNominalTypeDecl();
4139+
auto parameterType = func->getParameters()->get(0)->getType();
4140+
if (!typeDecl || !parameterType)
4141+
return nullptr;
4142+
4143+
auto &getterAndSetter = Impl.cxxSubscripts[{ typeDecl,
4144+
parameterType }];
4145+
4146+
switch (importedName.getAccessorKind()) {
4147+
case ImportedAccessorKind::SubscriptGetter:
4148+
getterAndSetter.first = func;
4149+
break;
4150+
case ImportedAccessorKind::SubscriptSetter:
4151+
getterAndSetter.second = func;
4152+
break;
4153+
default:
4154+
llvm_unreachable("invalid subscript kind");
4155+
}
4156+
4157+
Impl.markUnavailable(func, "use subscript");
4158+
}
41254159
// Someday, maybe this will need to be 'open' for C++ virtual methods.
41264160
func->setAccess(AccessLevel::Public);
41274161
}
@@ -4950,6 +4984,15 @@ namespace {
49504984
SubscriptDecl *importSubscript(Decl *decl,
49514985
const clang::ObjCMethodDecl *objcMethod);
49524986

4987+
/// Given either the getter, the setter, or both getter & setter
4988+
/// for a subscript operation, create the Swift subscript declaration.
4989+
///
4990+
/// \param getter function returning `UnsafePointer<T>`
4991+
/// \param setter function returning `UnsafeMutablePointer<T>`
4992+
/// \return subscript declaration
4993+
SubscriptDecl *makeSubscript(FuncDecl *getter,
4994+
FuncDecl *setter);
4995+
49534996
/// Import the accessor and its attributes.
49544997
AccessorDecl *importAccessor(clang::ObjCMethodDecl *clangAccessor,
49554998
AbstractStorageDecl *storage,
@@ -7339,6 +7382,244 @@ SwiftDeclConverter::importAccessor(clang::ObjCMethodDecl *clangAccessor,
73397382
return accessor;
73407383
}
73417384

7385+
static InOutExpr *
7386+
createInOutSelfExpr(AccessorDecl *accessorDecl) {
7387+
ASTContext &ctx = accessorDecl->getASTContext();
7388+
7389+
auto inoutSelfDecl = accessorDecl->getImplicitSelfDecl();
7390+
auto inoutSelfRefExpr =
7391+
new (ctx) DeclRefExpr(inoutSelfDecl, DeclNameLoc(),
7392+
/*implicit=*/ true);
7393+
inoutSelfRefExpr->setType(LValueType::get(inoutSelfDecl->getInterfaceType()));
7394+
7395+
auto inoutSelfExpr =
7396+
new (ctx) InOutExpr(SourceLoc(),
7397+
inoutSelfRefExpr,
7398+
accessorDecl->mapTypeIntoContext(
7399+
inoutSelfDecl->getValueInterfaceType()),
7400+
/*isImplicit=*/ true);
7401+
inoutSelfExpr->setType(InOutType::get(inoutSelfDecl->getInterfaceType()));
7402+
return inoutSelfExpr;
7403+
}
7404+
7405+
static DeclRefExpr *
7406+
createParamRefExpr(AccessorDecl *accessorDecl, unsigned index) {
7407+
ASTContext &ctx = accessorDecl->getASTContext();
7408+
7409+
auto paramDecl = accessorDecl->getParameters()->get(index);
7410+
auto paramRefExpr = new (ctx) DeclRefExpr(paramDecl,
7411+
DeclNameLoc(),
7412+
/*Implicit=*/ true);
7413+
paramRefExpr->setType(paramDecl->getType());
7414+
return paramRefExpr;
7415+
}
7416+
7417+
static CallExpr *
7418+
createAccessorImplCallExpr(FuncDecl *accessorImpl,
7419+
InOutExpr *inoutSelfExpr,
7420+
DeclRefExpr *keyRefExpr) {
7421+
ASTContext &ctx = accessorImpl->getASTContext();
7422+
7423+
auto accessorImplExpr =
7424+
new (ctx) DeclRefExpr(ConcreteDeclRef(accessorImpl),
7425+
DeclNameLoc(),
7426+
/*Implicit=*/ true);
7427+
accessorImplExpr->setType(accessorImpl->getInterfaceType());
7428+
7429+
auto accessorImplDotCallExpr =
7430+
new (ctx) DotSyntaxCallExpr(accessorImplExpr,
7431+
SourceLoc(),
7432+
inoutSelfExpr);
7433+
accessorImplDotCallExpr->setType(accessorImpl->getMethodInterfaceType());
7434+
accessorImplDotCallExpr->setThrows(false);
7435+
7436+
auto *accessorImplCallExpr =
7437+
CallExpr::createImplicit(ctx, accessorImplDotCallExpr,
7438+
{ keyRefExpr }, { Identifier() });
7439+
accessorImplCallExpr->setType(accessorImpl->getResultInterfaceType());
7440+
accessorImplCallExpr->setThrows(false);
7441+
return accessorImplCallExpr;
7442+
}
7443+
7444+
/// Synthesizer callback for a subscript getter.
7445+
static std::pair<BraceStmt *, bool>
7446+
synthesizeSubscriptGetterBody(AbstractFunctionDecl *afd, void *context) {
7447+
auto getterDecl = cast<AccessorDecl>(afd);
7448+
auto getterImpl = static_cast<FuncDecl *>(context);
7449+
7450+
ASTContext &ctx = getterDecl->getASTContext();
7451+
7452+
InOutExpr *inoutSelfExpr = createInOutSelfExpr(getterDecl);
7453+
DeclRefExpr *keyRefExpr = createParamRefExpr(getterDecl, 0);
7454+
7455+
Type elementTy = getterDecl->getResultInterfaceType();
7456+
7457+
auto *getterImplCallExpr = createAccessorImplCallExpr(getterImpl,
7458+
inoutSelfExpr,
7459+
keyRefExpr);
7460+
7461+
// `getterImpl` can return either UnsafePointer or UnsafeMutablePointer.
7462+
// Retrieve the corresponding `.pointee` declaration.
7463+
PointerTypeKind ptrKind;
7464+
getterImpl->getResultInterfaceType()->getAnyPointerElementType(ptrKind);
7465+
VarDecl *pointeePropertyDecl = ctx.getPointerPointeePropertyDecl(ptrKind);
7466+
7467+
SubstitutionMap subMap =
7468+
SubstitutionMap::get(ctx.getUnsafePointerDecl()->getGenericSignature(),
7469+
{ elementTy }, { });
7470+
auto pointeePropertyRefExpr =
7471+
new (ctx) MemberRefExpr(getterImplCallExpr,
7472+
SourceLoc(),
7473+
ConcreteDeclRef(pointeePropertyDecl, subMap),
7474+
DeclNameLoc(),
7475+
/*implicit=*/ true);
7476+
pointeePropertyRefExpr->setType(elementTy);
7477+
7478+
auto returnStmt = new (ctx) ReturnStmt(SourceLoc(),
7479+
pointeePropertyRefExpr,
7480+
/*implicit=*/ true);
7481+
7482+
auto body = BraceStmt::create(ctx, SourceLoc(), { returnStmt }, SourceLoc(),
7483+
/*implicit=*/ true);
7484+
return { body, /*isTypeChecked=*/true };
7485+
}
7486+
7487+
/// Synthesizer callback for a subscript setter.
7488+
static std::pair<BraceStmt *, bool>
7489+
synthesizeSubscriptSetterBody(AbstractFunctionDecl *afd, void *context) {
7490+
auto setterDecl = cast<AccessorDecl>(afd);
7491+
auto setterImpl = static_cast<FuncDecl *>(context);
7492+
7493+
ASTContext &ctx = setterDecl->getASTContext();
7494+
7495+
InOutExpr *inoutSelfExpr = createInOutSelfExpr(setterDecl);
7496+
DeclRefExpr *valueParamRefExpr = createParamRefExpr(setterDecl, 0);
7497+
DeclRefExpr *keyParamRefExpr = createParamRefExpr(setterDecl, 1);
7498+
7499+
Type elementTy = valueParamRefExpr->getDecl()->getInterfaceType();
7500+
7501+
auto *setterImplCallExpr = createAccessorImplCallExpr(setterImpl,
7502+
inoutSelfExpr,
7503+
keyParamRefExpr);
7504+
7505+
VarDecl *pointeePropertyDecl = ctx.getPointerPointeePropertyDecl(PTK_UnsafeMutablePointer);
7506+
7507+
SubstitutionMap subMap =
7508+
SubstitutionMap::get(ctx.getUnsafeMutablePointerDecl()->getGenericSignature(),
7509+
{ elementTy }, { });
7510+
auto pointeePropertyRefExpr =
7511+
new (ctx) MemberRefExpr(setterImplCallExpr,
7512+
SourceLoc(),
7513+
ConcreteDeclRef(pointeePropertyDecl, subMap),
7514+
DeclNameLoc(),
7515+
/*implicit=*/ true);
7516+
pointeePropertyRefExpr->setType(LValueType::get(elementTy));
7517+
7518+
auto assignExpr = new (ctx) AssignExpr(pointeePropertyRefExpr,
7519+
SourceLoc(),
7520+
valueParamRefExpr,
7521+
/*implicit*/ true);
7522+
assignExpr->setType(TupleType::getEmpty(ctx));
7523+
7524+
auto body = BraceStmt::create(ctx, SourceLoc(), { assignExpr, }, SourceLoc());
7525+
return { body, /*isTypeChecked=*/true };
7526+
}
7527+
7528+
SubscriptDecl *
7529+
SwiftDeclConverter::makeSubscript(FuncDecl *getter, FuncDecl *setter) {
7530+
assert((getter || setter) && "getter or setter required to generate subscript");
7531+
7532+
// If only a setter (imported from non-const `operator[]`) is defined,
7533+
// generate both get & set accessors from it.
7534+
FuncDecl *getterImpl = getter ? getter : setter;
7535+
FuncDecl *setterImpl = setter;
7536+
7537+
// Get the return type wrapped in `Unsafe(Mutable)Pointer<T>`.
7538+
const auto rawElementTy = getterImpl->getResultInterfaceType();
7539+
// Unwrap `T`.
7540+
const auto elementTy = rawElementTy->getAnyPointerElementType();
7541+
7542+
auto &ctx = Impl.SwiftContext;
7543+
auto bodyParams = getterImpl->getParameters();
7544+
DeclName name(ctx, DeclBaseName::createSubscript(), bodyParams);
7545+
auto dc = getterImpl->getDeclContext();
7546+
7547+
SubscriptDecl *subscript = SubscriptDecl::createImported(ctx,
7548+
name,
7549+
getterImpl->getLoc(),
7550+
bodyParams,
7551+
getterImpl->getLoc(),
7552+
elementTy,
7553+
dc,
7554+
getterImpl->getClangNode());
7555+
subscript->setAccess(AccessLevel::Public);
7556+
7557+
AccessorDecl *getterDecl = AccessorDecl::create(ctx,
7558+
getterImpl->getLoc(),
7559+
getterImpl->getLoc(),
7560+
AccessorKind::Get,
7561+
subscript,
7562+
SourceLoc(),
7563+
subscript->getStaticSpelling(),
7564+
false,
7565+
SourceLoc(),
7566+
nullptr,
7567+
bodyParams,
7568+
elementTy,
7569+
dc);
7570+
getterDecl->setAccess(AccessLevel::Public);
7571+
getterDecl->setImplicit();
7572+
getterDecl->setIsDynamic(false);
7573+
getterDecl->setIsTransparent(true);
7574+
getterDecl->setBodySynthesizer(synthesizeSubscriptGetterBody, getterImpl);
7575+
7576+
if (getterImpl->isMutating()) {
7577+
getterDecl->setSelfAccessKind(SelfAccessKind::Mutating);
7578+
subscript->setIsGetterMutating(true);
7579+
}
7580+
7581+
AccessorDecl *setterDecl = nullptr;
7582+
if (setterImpl) {
7583+
auto paramVarDecl =
7584+
new (ctx) ParamDecl(SourceLoc(), SourceLoc(),
7585+
Identifier(), SourceLoc(),
7586+
ctx.getIdentifier("newValue"), dc);
7587+
paramVarDecl->setSpecifier(ParamSpecifier::Default);
7588+
paramVarDecl->setInterfaceType(elementTy);
7589+
7590+
auto setterParamList =
7591+
ParameterList::create(ctx, { paramVarDecl, bodyParams->get(0) });
7592+
7593+
setterDecl = AccessorDecl::create(ctx,
7594+
setterImpl->getLoc(),
7595+
setterImpl->getLoc(),
7596+
AccessorKind::Set,
7597+
subscript,
7598+
SourceLoc(),
7599+
subscript->getStaticSpelling(),
7600+
false,
7601+
SourceLoc(),
7602+
nullptr,
7603+
setterParamList,
7604+
TupleType::getEmpty(ctx),
7605+
dc);
7606+
setterDecl->setAccess(AccessLevel::Public);
7607+
setterDecl->setImplicit();
7608+
setterDecl->setIsDynamic(false);
7609+
setterDecl->setIsTransparent(true);
7610+
setterDecl->setBodySynthesizer(synthesizeSubscriptSetterBody, setterImpl);
7611+
7612+
if (setterImpl->isMutating()) {
7613+
setterDecl->setSelfAccessKind(SelfAccessKind::Mutating);
7614+
subscript->setIsSetterMutating(true);
7615+
}
7616+
}
7617+
7618+
makeComputed(subscript, getterDecl, setterDecl);
7619+
7620+
return subscript;
7621+
}
7622+
73427623
void SwiftDeclConverter::addProtocols(
73437624
ProtocolDecl *protocol, SmallVectorImpl<ProtocolDecl *> &protocols,
73447625
llvm::SmallPtrSetImpl<ProtocolDecl *> &known) {

lib/ClangImporter/ImportName.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1797,6 +1797,23 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
17971797
isFunction = true;
17981798
addEmptyArgNamesForClangFunction(functionDecl, argumentNames);
17991799
break;
1800+
case clang::OverloadedOperatorKind::OO_Subscript: {
1801+
auto returnType = functionDecl->getReturnType();
1802+
if (!returnType->isReferenceType()) {
1803+
// TODO: support non-reference return types (SR-14351)
1804+
return ImportedName();
1805+
}
1806+
if (returnType->getPointeeType().isConstQualified()) {
1807+
baseName = "__operatorSubscriptConst";
1808+
result.info.accessorKind = ImportedAccessorKind::SubscriptGetter;
1809+
} else {
1810+
baseName = "__operatorSubscript";
1811+
result.info.accessorKind = ImportedAccessorKind::SubscriptSetter;
1812+
}
1813+
isFunction = true;
1814+
addEmptyArgNamesForClangFunction(functionDecl, argumentNames);
1815+
break;
1816+
}
18001817
default:
18011818
// We don't import these yet.
18021819
return ImportedName();

lib/ClangImporter/ImporterImpl.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,15 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
490490
/// pairs.
491491
llvm::DenseMap<std::pair<FuncDecl *, FuncDecl *>, SubscriptDecl *> Subscripts;
492492

493+
/// Keep track of getter/setter pairs for functions imported from C++
494+
/// subscript operators based on the type in which they are declared and
495+
/// the type of their parameter.
496+
///
497+
/// `.first` corresponds to a getter
498+
/// `.second` corresponds to a setter
499+
llvm::MapVector<std::pair<NominalTypeDecl *, Type>,
500+
std::pair<FuncDecl *, FuncDecl *>> cxxSubscripts;
501+
493502
/// Keeps track of the Clang functions that have been turned into
494503
/// properties.
495504
llvm::DenseMap<const clang::FunctionDecl *, VarDecl *> FunctionsAsProperties;

lib/Sema/TypeCheckStorage.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2311,6 +2311,9 @@ IsAccessorTransparentRequest::evaluate(Evaluator &evaluator,
23112311
break;
23122312
}
23132313
}
2314+
if (auto subscript = dyn_cast<SubscriptDecl>(storage)) {
2315+
break;
2316+
}
23142317

23152318
// Anything else should not have a synthesized setter.
23162319
LLVM_FALLTHROUGH;

0 commit comments

Comments
 (0)