From e1d05010c3277ffb1f71fbb7bfad66704dcfc4cf Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Wed, 24 Jul 2024 15:58:52 +0200 Subject: [PATCH] Fix lifetimebound for field access (#100197) Fixes: https://github.com/llvm/llvm-project/issues/81589 There is no way to switch this off without `-Wno-dangling`. --- clang/docs/ReleaseNotes.rst | 3 +++ clang/lib/Sema/CheckExprLifetime.cpp | 9 ++++++++ clang/test/SemaCXX/attr-lifetimebound.cpp | 26 +++++++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 549da6812740f4..71d615553c6131 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -767,6 +767,9 @@ Improvements to Clang's diagnostics - Clang now diagnoses undefined behavior in constant expressions more consistently. This includes invalid shifts, and signed overflow in arithmetic. +- Clang now diagnoses dangling references to fields of temporary objects. Fixes #GH81589. + + Improvements to Clang's time-trace ---------------------------------- diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp index 5c8ef564f30aaf..112cf3d0818227 100644 --- a/clang/lib/Sema/CheckExprLifetime.cpp +++ b/clang/lib/Sema/CheckExprLifetime.cpp @@ -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" @@ -548,6 +549,14 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, EnableLifetimeWarnings); } + if (auto *M = dyn_cast(Init)) { + // Lifetime of a non-reference type field is same as base object. + if (auto *F = dyn_cast(M->getMemberDecl()); + F && !F->getType()->isReferenceType()) + visitLocalsRetainedByInitializer(Path, M->getBase(), Visit, true, + EnableLifetimeWarnings); + } + if (isa(Init)) { if (EnableLifetimeWarnings) handleGslAnnotatedTypes(Path, Init, Visit); diff --git a/clang/test/SemaCXX/attr-lifetimebound.cpp b/clang/test/SemaCXX/attr-lifetimebound.cpp index 70bc545c07bd9f..7db0a4d64d2596 100644 --- a/clang/test/SemaCXX/attr-lifetimebound.cpp +++ b/clang/test/SemaCXX/attr-lifetimebound.cpp @@ -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 "" 1 3 @@ -239,3 +264,4 @@ namespace move_forward_et_al_examples { S X; S *AddressOfOk = std::addressof(X); } // namespace move_forward_et_al_examples +