Skip to content

Commit 1feef92

Browse files
authored
Fix lifetimebound for field access (#100197)
Fixes: #81589 There is no way to switch this off without `-Wno-dangling`.
1 parent 557a7b8 commit 1feef92

File tree

3 files changed

+37
-0
lines changed

3 files changed

+37
-0
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ Improvements to Clang's diagnostics
124124
template <typename> int i; // error: non-static data member 'i' cannot be declared as a template
125125
};
126126

127+
- Clang now diagnoses dangling references to fields of temporary objects. Fixes #GH81589.
128+
127129
Improvements to Clang's time-trace
128130
----------------------------------
129131

clang/lib/Sema/CheckExprLifetime.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "CheckExprLifetime.h"
10+
#include "clang/AST/Decl.h"
1011
#include "clang/AST/Expr.h"
1112
#include "clang/Basic/DiagnosticSema.h"
1213
#include "clang/Sema/Initialization.h"
@@ -548,6 +549,14 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path,
548549
EnableLifetimeWarnings);
549550
}
550551

552+
if (auto *M = dyn_cast<MemberExpr>(Init)) {
553+
// Lifetime of a non-reference type field is same as base object.
554+
if (auto *F = dyn_cast<FieldDecl>(M->getMemberDecl());
555+
F && !F->getType()->isReferenceType())
556+
visitLocalsRetainedByInitializer(Path, M->getBase(), Visit, true,
557+
EnableLifetimeWarnings);
558+
}
559+
551560
if (isa<CallExpr>(Init)) {
552561
if (EnableLifetimeWarnings)
553562
handleGslAnnotatedTypes(Path, Init, Visit);

clang/test/SemaCXX/attr-lifetimebound.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,31 @@ namespace usage_ok {
4747
q = A(); // expected-warning {{object backing the pointer q will be destroyed at the end of the full-expression}}
4848
r = A(1); // expected-warning {{object backing the pointer r will be destroyed at the end of the full-expression}}
4949
}
50+
51+
struct FieldCheck {
52+
struct Set {
53+
int a;
54+
};
55+
struct Pair {
56+
const int& a;
57+
int b;
58+
Set c;
59+
int * d;
60+
};
61+
Pair p;
62+
FieldCheck(const int& a): p(a){}
63+
Pair& getR() [[clang::lifetimebound]] { return p; }
64+
Pair* getP() [[clang::lifetimebound]] { return &p; }
65+
Pair* getNoLB() { return &p; }
66+
};
67+
void test_field_access() {
68+
int x = 0;
69+
const int& a = FieldCheck{x}.getR().a;
70+
const int& b = FieldCheck{x}.getP()->b; // expected-warning {{temporary bound to local reference 'b' will be destroyed at the end of the full-expression}}
71+
const int& c = FieldCheck{x}.getP()->c.a; // expected-warning {{temporary bound to local reference 'c' will be destroyed at the end of the full-expression}}
72+
const int& d = FieldCheck{x}.getNoLB()->c.a;
73+
const int* e = FieldCheck{x}.getR().d;
74+
}
5075
}
5176

5277
# 1 "<std>" 1 3
@@ -239,3 +264,4 @@ namespace move_forward_et_al_examples {
239264
S X;
240265
S *AddressOfOk = std::addressof(X);
241266
} // namespace move_forward_et_al_examples
267+

0 commit comments

Comments
 (0)