Skip to content

Commit 770adc5

Browse files
authored
[clang] Infer lifetime_capture_by for map's subscript operator. (#118078)
1 parent 7bbc049 commit 770adc5

File tree

2 files changed

+52
-22
lines changed

2 files changed

+52
-22
lines changed

clang/lib/Sema/SemaAttr.cpp

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -270,34 +270,48 @@ void Sema::inferLifetimeBoundAttribute(FunctionDecl *FD) {
270270
}
271271

272272
void Sema::inferLifetimeCaptureByAttribute(FunctionDecl *FD) {
273-
if (!FD)
273+
auto *MD = dyn_cast_if_present<CXXMethodDecl>(FD);
274+
if (!MD || !MD->getParent()->isInStdNamespace())
274275
return;
275-
auto *MD = dyn_cast<CXXMethodDecl>(FD);
276-
if (!MD || !MD->getIdentifier() || !MD->getParent()->isInStdNamespace())
276+
auto Annotate = [this](const FunctionDecl *MD) {
277+
// Do not infer if any parameter is explicitly annotated.
278+
for (ParmVarDecl *PVD : MD->parameters())
279+
if (PVD->hasAttr<LifetimeCaptureByAttr>())
280+
return;
281+
for (ParmVarDecl *PVD : MD->parameters()) {
282+
// Methods in standard containers that capture values typically accept
283+
// reference-type parameters, e.g., `void push_back(const T& value)`.
284+
// We only apply the lifetime_capture_by attribute to parameters of
285+
// pointer-like reference types (`const T&`, `T&&`).
286+
if (PVD->getType()->isReferenceType() &&
287+
sema::isPointerLikeType(PVD->getType().getNonReferenceType())) {
288+
int CaptureByThis[] = {LifetimeCaptureByAttr::THIS};
289+
PVD->addAttr(
290+
LifetimeCaptureByAttr::CreateImplicit(Context, CaptureByThis, 1));
291+
}
292+
}
293+
};
294+
295+
if (!MD->getIdentifier()) {
296+
static const llvm::StringSet<> MapLikeContainer{
297+
"map",
298+
"multimap",
299+
"unordered_map",
300+
"unordered_multimap",
301+
};
302+
// Infer for the map's operator []:
303+
// std::map<string_view, ...> m;
304+
// m[ReturnString(..)] = ...; // !dangling references in m.
305+
if (MD->getOverloadedOperator() == OO_Subscript &&
306+
MapLikeContainer.contains(MD->getParent()->getName()))
307+
Annotate(MD);
277308
return;
278-
// FIXME: Infer for operator[] for map-like containers. For example:
279-
// std::map<string_view, ...> m;
280-
// m[ReturnString(..)] = ...;
309+
}
281310
static const llvm::StringSet<> CapturingMethods{"insert", "push",
282311
"push_front", "push_back"};
283312
if (!CapturingMethods.contains(MD->getName()))
284313
return;
285-
// Do not infer if any parameter is explicitly annotated.
286-
for (ParmVarDecl *PVD : MD->parameters())
287-
if (PVD->hasAttr<LifetimeCaptureByAttr>())
288-
return;
289-
for (ParmVarDecl *PVD : MD->parameters()) {
290-
// Methods in standard containers that capture values typically accept
291-
// reference-type parameters, e.g., `void push_back(const T& value)`.
292-
// We only apply the lifetime_capture_by attribute to parameters of
293-
// pointer-like reference types (`const T&`, `T&&`).
294-
if (PVD->getType()->isReferenceType() &&
295-
sema::isPointerLikeType(PVD->getType().getNonReferenceType())) {
296-
int CaptureByThis[] = {LifetimeCaptureByAttr::THIS};
297-
PVD->addAttr(
298-
LifetimeCaptureByAttr::CreateImplicit(Context, CaptureByThis, 1));
299-
}
300-
}
314+
Annotate(MD);
301315
}
302316

303317
void Sema::inferNullableClassAttribute(CXXRecordDecl *CRD) {

clang/test/AST/attr-lifetime-capture-by.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ struct vector {
3030

3131
void insert(iterator, T&&);
3232
};
33+
34+
template <typename Key, typename Value>
35+
struct map {
36+
Value& operator[](Key&& p);
37+
Value& operator[](const Key& p);
38+
};
3339
} // namespace std
3440

3541
// CHECK-NOT: LifetimeCaptureByAttr
@@ -99,3 +105,13 @@ std::vector<int> ints;
99105
// CHECK-NEXT: ParmVarDecl {{.*}} 'iterator'
100106
// CHECK-NEXT: ParmVarDecl {{.*}} 'int &&'
101107
// CHECK-NOT: LifetimeCaptureByAttr
108+
109+
std::map<View, int> map;
110+
// CHECK: ClassTemplateSpecializationDecl {{.*}} struct map definition implicit_instantiation
111+
112+
// CHECK: CXXMethodDecl {{.*}} operator[] 'int &(View &&)' implicit_instantiation
113+
// CHECK-NEXT: ParmVarDecl {{.*}} p 'View &&'
114+
// CHECK-NEXT: LifetimeCaptureByAttr {{.*}} Implicit
115+
// CHECK: CXXMethodDecl {{.*}} operator[] 'int &(const View &)' implicit_instantiation
116+
// CHECK-NEXT: ParmVarDecl {{.*}} p 'const View &'
117+
// CHECK-NEXT: LifetimeCaptureByAttr {{.*}} Implicit

0 commit comments

Comments
 (0)