Skip to content

Commit 1374aa3

Browse files
authored
[clang] Don't infer lifetime_capture-by for reference of raw pointer types. (#122240)
When a vector is instantiated with a pointer type (`T` being `const Foo*`), the inferred annotation becomes `push_back(const Foo*& value [[clang::lifetime_capture_by(this)]])`. For reference parameters, the `lifetime_capture_by` attribute treats the lifetime as referring to the referenced object -- in this case, the **pointer** itself, not the pointee object. In the `push_back`, we copy the pointer's value, which does not establish a reference to the pointer. This behavior is safe and does not capture the pointer's lifetime. The annotation should not be inferred for cases where `T` is a pointer type, as the intended semantics do not align with the annotation. Fixes #121391
1 parent c01ffab commit 1374aa3

File tree

5 files changed

+33
-11
lines changed

5 files changed

+33
-11
lines changed

clang/lib/Sema/CheckExprLifetime.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -281,9 +281,11 @@ template <typename T> static bool isRecordWithAttr(QualType Type) {
281281
return Result;
282282
}
283283

284-
bool isPointerLikeType(QualType QT) {
285-
return isRecordWithAttr<PointerAttr>(QT) || QT->isPointerType() ||
286-
QT->isNullPtrType();
284+
// Tells whether the type is annotated with [[gsl::Pointer]].
285+
bool isGLSPointerType(QualType QT) { return isRecordWithAttr<PointerAttr>(QT); }
286+
287+
static bool isPointerLikeType(QualType QT) {
288+
return isGLSPointerType(QT) || QT->isPointerType() || QT->isNullPtrType();
287289
}
288290

289291
// Decl::isInStdNamespace will return false for iterators in some STL

clang/lib/Sema/CheckExprLifetime.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,8 @@
1818

1919
namespace clang::sema {
2020

21-
// Tells whether the type is annotated with [[gsl::Pointer]] or is a pointer
22-
// type.
23-
bool isPointerLikeType(QualType QT);
21+
// Tells whether the type is annotated with [[gsl::Pointer]].
22+
bool isGLSPointerType(QualType QT);
2423

2524
/// Describes an entity that is being assigned.
2625
struct AssignedEntity {

clang/lib/Sema/SemaAttr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ void Sema::inferLifetimeCaptureByAttribute(FunctionDecl *FD) {
284284
// We only apply the lifetime_capture_by attribute to parameters of
285285
// pointer-like reference types (`const T&`, `T&&`).
286286
if (PVD->getType()->isReferenceType() &&
287-
sema::isPointerLikeType(PVD->getType().getNonReferenceType())) {
287+
sema::isGLSPointerType(PVD->getType().getNonReferenceType())) {
288288
int CaptureByThis[] = {LifetimeCaptureByAttr::THIS};
289289
PVD->addAttr(
290290
LifetimeCaptureByAttr::CreateImplicit(Context, CaptureByThis, 1));

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,16 +80,16 @@ std::vector<int*> pointers;
8080

8181
// CHECK: CXXMethodDecl {{.*}} push_back 'void (int *const &)'
8282
// CHECK-NEXT: ParmVarDecl {{.*}} 'int *const &'
83-
// CHECK-NEXT: LifetimeCaptureByAttr {{.*}} Implicit
83+
// CHECK-NOT: LifetimeCaptureByAttr
8484

8585
// CHECK: CXXMethodDecl {{.*}} push_back 'void (int *&&)'
8686
// CHECK-NEXT: ParmVarDecl {{.*}} 'int *&&'
87-
// CHECK-NEXT: LifetimeCaptureByAttr {{.*}} Implicit
87+
// CHECK-NOT: LifetimeCaptureByAttr
8888

8989
// CHECK: CXXMethodDecl {{.*}} insert 'void (iterator, int *&&)'
9090
// CHECK-NEXT: ParmVarDecl {{.*}} 'iterator'
9191
// CHECK-NEXT: ParmVarDecl {{.*}} 'int *&&'
92-
// CHECK-NEXT: LifetimeCaptureByAttr {{.*}} Implicit
92+
// CHECK-NOT: LifetimeCaptureByAttr
9393

9494
std::vector<int> ints;
9595
// CHECK: ClassTemplateSpecializationDecl {{.*}} struct vector definition implicit_instantiation

clang/test/Sema/warn-lifetime-analysis-capture-by.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ void use() {
406406
strings.insert(strings.begin(), std::string());
407407

408408
std::vector<const std::string*> pointers;
409-
pointers.push_back(getLifetimeBoundPointer(std::string())); // expected-warning {{object whose reference is captured by 'pointers' will be destroyed at the end of the full-expression}}
409+
pointers.push_back(getLifetimeBoundPointer(std::string()));
410410
pointers.push_back(&local);
411411
}
412412

@@ -451,3 +451,24 @@ void test() {
451451
T2(1, a); // expected-warning {{object whose reference is captured by}}
452452
}
453453
} // namespace on_constructor
454+
455+
namespace GH121391 {
456+
457+
struct Foo {};
458+
459+
template <typename T>
460+
struct Container {
461+
const T& tt() [[clang::lifetimebound]];
462+
};
463+
template<typename T>
464+
struct StatusOr {
465+
T* get() [[clang::lifetimebound]];
466+
};
467+
StatusOr<Container<const Foo*>> getContainer();
468+
469+
void test() {
470+
std::vector<const Foo*> vv;
471+
vv.push_back(getContainer().get()->tt()); // OK
472+
}
473+
474+
} // namespace GH121391

0 commit comments

Comments
 (0)