Skip to content

Commit e1d0501

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

File tree

3 files changed

+38
-0
lines changed

3 files changed

+38
-0
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,9 @@ Improvements to Clang's diagnostics
767767

768768
- Clang now diagnoses undefined behavior in constant expressions more consistently. This includes invalid shifts, and signed overflow in arithmetic.
769769

770+
- Clang now diagnoses dangling references to fields of temporary objects. Fixes #GH81589.
771+
772+
770773
Improvements to Clang's time-trace
771774
----------------------------------
772775

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)