Skip to content

Commit

Permalink
Fix lifetimebound for field access (#100197)
Browse files Browse the repository at this point in the history
Fixes: #81589

There is no way to switch this off without  `-Wno-dangling`.
  • Loading branch information
usx95 authored Jul 24, 2024
1 parent 557a7b8 commit 1feef92
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 0 deletions.
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ Improvements to Clang's diagnostics
template <typename> int i; // error: non-static data member 'i' cannot be declared as a template
};

- Clang now diagnoses dangling references to fields of temporary objects. Fixes #GH81589.

Improvements to Clang's time-trace
----------------------------------

Expand Down
9 changes: 9 additions & 0 deletions clang/lib/Sema/CheckExprLifetime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "CheckExprLifetime.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Sema/Initialization.h"
Expand Down Expand Up @@ -548,6 +549,14 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path,
EnableLifetimeWarnings);
}

if (auto *M = dyn_cast<MemberExpr>(Init)) {
// Lifetime of a non-reference type field is same as base object.
if (auto *F = dyn_cast<FieldDecl>(M->getMemberDecl());
F && !F->getType()->isReferenceType())
visitLocalsRetainedByInitializer(Path, M->getBase(), Visit, true,
EnableLifetimeWarnings);
}

if (isa<CallExpr>(Init)) {
if (EnableLifetimeWarnings)
handleGslAnnotatedTypes(Path, Init, Visit);
Expand Down
26 changes: 26 additions & 0 deletions clang/test/SemaCXX/attr-lifetimebound.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,31 @@ namespace usage_ok {
q = A(); // expected-warning {{object backing the pointer q will be destroyed at the end of the full-expression}}
r = A(1); // expected-warning {{object backing the pointer r will be destroyed at the end of the full-expression}}
}

struct FieldCheck {
struct Set {
int a;
};
struct Pair {
const int& a;
int b;
Set c;
int * d;
};
Pair p;
FieldCheck(const int& a): p(a){}
Pair& getR() [[clang::lifetimebound]] { return p; }
Pair* getP() [[clang::lifetimebound]] { return &p; }
Pair* getNoLB() { return &p; }
};
void test_field_access() {
int x = 0;
const int& a = FieldCheck{x}.getR().a;
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}}
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}}
const int& d = FieldCheck{x}.getNoLB()->c.a;
const int* e = FieldCheck{x}.getR().d;
}
}

# 1 "<std>" 1 3
Expand Down Expand Up @@ -239,3 +264,4 @@ namespace move_forward_et_al_examples {
S X;
S *AddressOfOk = std::addressof(X);
} // namespace move_forward_et_al_examples

0 comments on commit 1feef92

Please sign in to comment.