Skip to content

Commit d0879b9

Browse files
authored
Merge pull request #36365 from egorzhdan/cxx-operator-subscript
C++ Interop: import subscript operators
2 parents d997526 + 586c675 commit d0879b9

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
@@ -3651,9 +3651,21 @@ namespace {
36513651

36523652
result->setHasUnreferenceableStorage(hasUnreferenceableStorage);
36533653

3654-
if (cxxRecordDecl)
3654+
if (cxxRecordDecl) {
36553655
result->setIsCxxNonTrivial(!cxxRecordDecl->isTriviallyCopyable());
36563656

3657+
for (auto &subscriptInfo : Impl.cxxSubscripts) {
3658+
auto declAndParameterType = subscriptInfo.first;
3659+
if (declAndParameterType.first != result)
3660+
continue;
3661+
3662+
auto getterAndSetter = subscriptInfo.second;
3663+
auto subscript = makeSubscript(getterAndSetter.first,
3664+
getterAndSetter.second);
3665+
result->addMember(subscript);
3666+
}
3667+
}
3668+
36573669
return result;
36583670
}
36593671

@@ -3945,12 +3957,10 @@ namespace {
39453957
AbstractStorageDecl *owningStorage;
39463958
switch (importedName.getAccessorKind()) {
39473959
case ImportedAccessorKind::None:
3948-
owningStorage = nullptr;
3949-
break;
3950-
39513960
case ImportedAccessorKind::SubscriptGetter:
39523961
case ImportedAccessorKind::SubscriptSetter:
3953-
llvm_unreachable("Not possible for a function");
3962+
owningStorage = nullptr;
3963+
break;
39543964

39553965
case ImportedAccessorKind::PropertyGetter: {
39563966
auto property = getImplicitProperty(importedName, decl);
@@ -4147,6 +4157,30 @@ namespace {
41474157
func->setImportAsStaticMember();
41484158
}
41494159
}
4160+
4161+
if (importedName.isSubscriptAccessor()) {
4162+
assert(func->getParameters()->size() == 1);
4163+
auto typeDecl = dc->getSelfNominalTypeDecl();
4164+
auto parameterType = func->getParameters()->get(0)->getType();
4165+
if (!typeDecl || !parameterType)
4166+
return nullptr;
4167+
4168+
auto &getterAndSetter = Impl.cxxSubscripts[{ typeDecl,
4169+
parameterType }];
4170+
4171+
switch (importedName.getAccessorKind()) {
4172+
case ImportedAccessorKind::SubscriptGetter:
4173+
getterAndSetter.first = func;
4174+
break;
4175+
case ImportedAccessorKind::SubscriptSetter:
4176+
getterAndSetter.second = func;
4177+
break;
4178+
default:
4179+
llvm_unreachable("invalid subscript kind");
4180+
}
4181+
4182+
Impl.markUnavailable(func, "use subscript");
4183+
}
41504184
// Someday, maybe this will need to be 'open' for C++ virtual methods.
41514185
func->setAccess(AccessLevel::Public);
41524186
}
@@ -4976,6 +5010,15 @@ namespace {
49765010
SubscriptDecl *importSubscript(Decl *decl,
49775011
const clang::ObjCMethodDecl *objcMethod);
49785012

5013+
/// Given either the getter, the setter, or both getter & setter
5014+
/// for a subscript operation, create the Swift subscript declaration.
5015+
///
5016+
/// \param getter function returning `UnsafePointer<T>`
5017+
/// \param setter function returning `UnsafeMutablePointer<T>`
5018+
/// \return subscript declaration
5019+
SubscriptDecl *makeSubscript(FuncDecl *getter,
5020+
FuncDecl *setter);
5021+
49795022
/// Import the accessor and its attributes.
49805023
AccessorDecl *importAccessor(clang::ObjCMethodDecl *clangAccessor,
49815024
AbstractStorageDecl *storage,
@@ -7363,6 +7406,244 @@ SwiftDeclConverter::importAccessor(clang::ObjCMethodDecl *clangAccessor,
73637406
return accessor;
73647407
}
73657408

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

lib/ClangImporter/ImportName.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1739,6 +1739,23 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
17391739
isFunction = true;
17401740
addEmptyArgNamesForClangFunction(functionDecl, argumentNames);
17411741
break;
1742+
case clang::OverloadedOperatorKind::OO_Subscript: {
1743+
auto returnType = functionDecl->getReturnType();
1744+
if (!returnType->isReferenceType()) {
1745+
// TODO: support non-reference return types (SR-14351)
1746+
return ImportedName();
1747+
}
1748+
if (returnType->getPointeeType().isConstQualified()) {
1749+
baseName = "__operatorSubscriptConst";
1750+
result.info.accessorKind = ImportedAccessorKind::SubscriptGetter;
1751+
} else {
1752+
baseName = "__operatorSubscript";
1753+
result.info.accessorKind = ImportedAccessorKind::SubscriptSetter;
1754+
}
1755+
isFunction = true;
1756+
addEmptyArgNamesForClangFunction(functionDecl, argumentNames);
1757+
break;
1758+
}
17421759
default:
17431760
// We don't import these yet.
17441761
return ImportedName();

lib/ClangImporter/ImporterImpl.h

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

492+
/// Keep track of getter/setter pairs for functions imported from C++
493+
/// subscript operators based on the type in which they are declared and
494+
/// the type of their parameter.
495+
///
496+
/// `.first` corresponds to a getter
497+
/// `.second` corresponds to a setter
498+
llvm::MapVector<std::pair<NominalTypeDecl *, Type>,
499+
std::pair<FuncDecl *, FuncDecl *>> cxxSubscripts;
500+
492501
/// Keeps track of the Clang functions that have been turned into
493502
/// properties.
494503
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
@@ -2316,6 +2316,9 @@ IsAccessorTransparentRequest::evaluate(Evaluator &evaluator,
23162316
break;
23172317
}
23182318
}
2319+
if (auto subscript = dyn_cast<SubscriptDecl>(storage)) {
2320+
break;
2321+
}
23192322

23202323
// Anything else should not have a synthesized setter.
23212324
LLVM_FALLTHROUGH;

0 commit comments

Comments
 (0)