https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/229784
>From 8930fd631ea89da84d257e7ca1900e35991213d3 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena <[email protected]> Date: Wed, 7 Oct 2026 14:10:53 +0000 Subject: [PATCH] field-origin-escape --- .../Analyses/LifetimeSafety/LifetimeSafety.h | 6 + .../Analysis/Analyses/LifetimeSafety/Loans.h | 43 ++++-- clang/include/clang/Basic/DiagnosticGroups.td | 7 + .../clang/Basic/DiagnosticSemaKinds.td | 8 ++ clang/lib/Analysis/LifetimeSafety/Checker.cpp | 34 +++++ .../LifetimeSafety/FactsGenerator.cpp | 20 ++- clang/lib/Analysis/LifetimeSafety/Loans.cpp | 16 +++ clang/lib/Sema/SemaLifetimeSafety.h | 23 ++++ .../LifetimeSafety/annotation-suggestions.cpp | 125 +++++++++++++++++- 9 files changed, 262 insertions(+), 20 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index 6854902059e50..e6fbc2a5caee7 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -46,6 +46,7 @@ struct LifetimeSafetyOpts { bool CheckLifetimeboundViolations; bool CheckMisplacedLifetimebound; bool CheckInapplicableLifetimebound; + bool CheckFieldOriginEscape; }; /// Enum to track functions visible across or within TU. @@ -164,6 +165,11 @@ class LifetimeSafetySemaHelper { const CXXMethodDecl *MD, const Expr *EscapeExpr) {} + // Reports when a member function returns an origin of a field. + virtual void reportFieldOriginEscape(const CXXMethodDecl *MD, + const FieldDecl *FD, + const Expr *EscapeExpr) {} + // Adds inferred lifetime bound attribute for implicit this to its // TypeSourceInfo. virtual void addLifetimeBoundToImplicitThis(const CXXMethodDecl *MD) {} diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h index 5643f0fc87314..8f8356444e4b8 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Loans.h @@ -70,32 +70,42 @@ class PathElement { const FieldDecl *FD; }; -/// Represents the base of a placeholder access path, which is either a -/// function parameter or the implicit 'this' object of an instance method. -/// Placeholder paths never expire within the function scope, as they represent -/// storage from the caller's scope. +/// Represents the base of a placeholder access path, which is a function +/// parameter, the implicit 'this' object of an instance method, or a field of +/// the enclosing class. Placeholder paths never expire within the function +/// scope, as they represent storage from the caller's or object's scope. class PlaceholderBase : public llvm::FoldingSetNode { - llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *> ParamOrMethod; + llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *, + const FieldDecl *> + ParamMethodOrField; public: - PlaceholderBase(const ParmVarDecl *PVD) : ParamOrMethod(PVD) {} - PlaceholderBase(const CXXMethodDecl *MD) : ParamOrMethod(MD) {} + PlaceholderBase(const ParmVarDecl *PVD) : ParamMethodOrField(PVD) {} + PlaceholderBase(const CXXMethodDecl *MD) : ParamMethodOrField(MD) {} + PlaceholderBase(const FieldDecl *FD) : ParamMethodOrField(FD) {} const ParmVarDecl *getParmVarDecl() const { - return dyn_cast<const ParmVarDecl *>(ParamOrMethod); + return dyn_cast<const ParmVarDecl *>(ParamMethodOrField); } const CXXMethodDecl *getImplicitThisParent() const { - return dyn_cast<const CXXMethodDecl *>(ParamOrMethod); + return dyn_cast<const CXXMethodDecl *>(ParamMethodOrField); } - using KeyTy = llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *>; + const FieldDecl *getFieldDecl() const { + return dyn_cast<const FieldDecl *>(ParamMethodOrField); + } + + using KeyTy = llvm::PointerUnion<const ParmVarDecl *, const CXXMethodDecl *, + const FieldDecl *>; - static void Profile(llvm::FoldingSetNodeID &ID, KeyTy ParamOrMethod) { - ID.AddPointer(ParamOrMethod.getOpaqueValue()); + static void Profile(llvm::FoldingSetNodeID &ID, KeyTy ParamMethodOrField) { + ID.AddPointer(ParamMethodOrField.getOpaqueValue()); } - void Profile(llvm::FoldingSetNodeID &ID) const { Profile(ID, ParamOrMethod); } + void Profile(llvm::FoldingSetNodeID &ID) const { + Profile(ID, ParamMethodOrField); + } }; /// Represents the storage location being borrowed, e.g., a specific stack @@ -229,6 +239,9 @@ class LoanManager { Loan *createPlaceholderLoan(const CXXMethodDecl *MD) { return createLoan(AccessPath(getOrCreatePlaceholderBase(MD))); } + Loan *createPlaceholderLoan(const FieldDecl *FD) { + return createLoan(AccessPath(getOrCreatePlaceholderBase(FD))); + } const Loan *getLoan(LoanID ID) const { assert(ID.Value < AllLoans.size()); @@ -240,9 +253,11 @@ class LoanManager { private: LoanID getNextLoanID() { return NextLoanID++; } - /// Gets or creates a placeholder base for a given parameter or method. + /// Gets or creates a placeholder base for a given parameter, method, or + /// field. const PlaceholderBase *getOrCreatePlaceholderBase(const ParmVarDecl *PVD); const PlaceholderBase *getOrCreatePlaceholderBase(const CXXMethodDecl *MD); + const PlaceholderBase *getOrCreatePlaceholderBase(const FieldDecl *FD); LoanID NextLoanID{0}; diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 7779a6989cb7a..ae9d437e3a54c 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -744,6 +744,13 @@ Validates correct usage and adherence to lifetime safety annotations like [[clan }]; } +def LifetimeSafetyFieldOriginEscape + : DiagGroup<"lifetime-safety-field-origin-escape"> { + code Documentation = [{ +Detects member functions returning an origin of a field (such as a pointer, reference, or view field). + }]; +} + def LifetimeSafetyAll : DiagGroup<"lifetime-safety-all", [LifetimeSafety, LifetimeSafetySuggestions, diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index cc87f556d50dc..fcdaf2180f7c9 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11148,6 +11148,14 @@ def warn_lifetime_safety_noescape_escapes InGroup<LifetimeSafetyNoescape>, DefaultIgnore; +def warn_lifetime_safety_field_origin_escape + : Warning<"return value depends on the lifetime of the object referenced by %0, which cannot be expressed with [[clang::lifetimebound]]">, + InGroup<LifetimeSafetyFieldOriginEscape>, + DefaultIgnore; + +def note_lifetime_safety_owned_field_suggestion + : Note<"if %0 is owned by the class, mark the method [[clang::lifetimebound]]">; + // For non-floating point, expressions of the form x == x or x != x // should result in a warning, since these always evaluate to a constant. // Array comparisons have similar warnings diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index fbf345460b6d8..00e241d583387 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -60,6 +60,7 @@ class LifetimeChecker { llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap; llvm::DenseMap<AnnotationTarget, EscapingTarget> AnnotationWarningsMap; llvm::DenseMap<const ParmVarDecl *, EscapingTarget> NoescapeWarningsMap; + llvm::DenseMap<const FieldDecl *, const Expr *> FieldOriginEscapesMap; llvm::DenseSet<const Decl *> VerifiedLiftimeboundEscapes; const LoanPropagationAnalysis &LoanPropagation; const MovedLoansAnalysis &MovedLoans; @@ -105,6 +106,8 @@ class LifetimeChecker { checkAnnotations(OEF); issuePendingWarnings(); suggestAnnotations(); + if (LSOpts.CheckFieldOriginEscape) + reportFieldOriginEscapes(); if (LSOpts.CheckNoescapeViolations) reportNoescapeViolations(); if (LSOpts.CheckLifetimeboundViolations) @@ -171,6 +174,24 @@ class LifetimeChecker { AnnotationWarningsMap.try_emplace(MD, ReturnEsc->getReturnExpr()); } }; + // The return value derives from a field's origin, not *this (e.g., + // Span::data()), so [[clang::lifetimebound]] cannot express its lifetime. + auto CheckField = [&](const FieldDecl *Field) { + if (!LSOpts.CheckFieldOriginEscape) + return; + // Lifetimebound marks the field as owned by *this (e.g., Vector::data()). + const auto *MD = dyn_cast<CXXMethodDecl>(FD); + if (MD && implicitObjectParamIsLifetimeBound(MD)) + return; + if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF)) { + // Ignore field loans in inner origins, e.g., returning a reference to + // a view field borrows *this. + if (OriginList *RetList = FactMgr.getOriginMgr().getOrCreateList( + ReturnEsc->getReturnExpr()); + RetList && RetList->getOuterOriginID() == EscapedOID) + FieldOriginEscapesMap.try_emplace(Field, ReturnEsc->getReturnExpr()); + } + }; auto MovedAtEscape = MovedLoans.getMovedLoans(OEF); for (LoanID LID : EscapedLoans) { const Loan *L = FactMgr.getLoanMgr().getLoan(LID); @@ -181,6 +202,8 @@ class LifetimeChecker { CheckParam(PVD, /*IsMoved=*/MovedAtEscape.lookup(LID)); else if (const auto *MD = PB->getImplicitThisParent()) CheckImplicitThis(MD); + else if (const auto *Field = PB->getFieldDecl()) + CheckField(Field); } } @@ -446,6 +469,17 @@ class LifetimeChecker { } } + void reportFieldOriginEscapes() { + if (!SemaHelper) + return; + const auto *MD = dyn_cast<CXXMethodDecl>(FD); + if (!MD) + return; + llvm::TimeTraceScope TimeTrace("ReportFieldOriginEscapes"); + for (auto [Field, EscapeExpr] : FieldOriginEscapesMap) + SemaHelper->reportFieldOriginEscape(MD, Field, EscapeExpr); + } + void reportNoescapeViolations() { llvm::TimeTraceScope TimeTrace("ReportNoescapeViolations"); for (auto [PVD, EscapeTarget] : NoescapeWarningsMap) { diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index d1113783ab882..6d3750eeeceaf 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -1205,7 +1205,8 @@ void FactsGenerator::handleUse(const Expr *E) { } // Creates an IssueFact for a new placeholder loan for each pointer or reference -// parameter at the function's entry. +// parameter, the implicit 'this', and each origin-carrying field at the +// function's entry. llvm::SmallVector<Fact *> FactsGenerator::issuePlaceholderLoans() { const auto *FD = dyn_cast<FunctionDecl>(AC.getDecl()); if (!FD) @@ -1214,10 +1215,23 @@ llvm::SmallVector<Fact *> FactsGenerator::issuePlaceholderLoans() { llvm::SmallVector<Fact *> PlaceholderLoanFacts; if (auto ThisOrigins = FactMgr.getOriginMgr().getThisOrigins()) { OriginList *List = *ThisOrigins; - const Loan *L = - FactMgr.getLoanMgr().createPlaceholderLoan(cast<CXXMethodDecl>(FD)); + const auto *MD = cast<CXXMethodDecl>(FD); + const Loan *L = FactMgr.getLoanMgr().createPlaceholderLoan(MD); PlaceholderLoanFacts.push_back( FactMgr.createFact<IssueFact>(L->getID(), List->getOuterOriginID())); + // $this.field represents the storage a field refers to, as opposed to the + // field itself (covered by $this). + if (!isa<CXXConstructorDecl>(MD)) + if (const CXXRecordDecl *ClassDecl = MD->getParent()->getDefinition()) + for (const FieldDecl *Field : ClassDecl->fields()) { + OriginList *FieldList = getOriginsList(*Field); + if (!FieldList) + continue; + const Loan *FieldLoan = + FactMgr.getLoanMgr().createPlaceholderLoan(Field); + PlaceholderLoanFacts.push_back(FactMgr.createFact<IssueFact>( + FieldLoan->getID(), FieldList->getOuterOriginID())); + } } for (const ParmVarDecl *PVD : FD->parameters()) { OriginList *List = getOriginsList(*PVD); diff --git a/clang/lib/Analysis/LifetimeSafety/Loans.cpp b/clang/lib/Analysis/LifetimeSafety/Loans.cpp index e6cbd922d20c7..c15f9b499b35b 100644 --- a/clang/lib/Analysis/LifetimeSafety/Loans.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Loans.cpp @@ -21,6 +21,8 @@ void AccessPath::dump(llvm::raw_ostream &OS) const { OS << "$" << PVD->getNameAsString(); else if (PB->getImplicitThisParent()) OS << "$this"; + else if (const auto *FD = PB->getFieldDecl()) + OS << "$this." << FD->getNameAsString(); } else if (const auto *E = getAsNewAllocation()) OS << "NewAllocation at " << E; else @@ -62,4 +64,18 @@ LoanManager::getOrCreatePlaceholderBase(const CXXMethodDecl *MD) { PlaceholderBases.insert(NewPB, InsertToken); return NewPB; } + +const PlaceholderBase * +LoanManager::getOrCreatePlaceholderBase(const FieldDecl *FD) { + llvm::FoldingSetNodeID ID; + PlaceholderBase::Profile(ID, FD); + llvm::FoldingSetInsertToken InsertToken; + if (PlaceholderBase *Existing = PlaceholderBases.lookup(ID, InsertToken)) + return Existing; + + void *Mem = LoanAllocator.Allocate<PlaceholderBase>(); + PlaceholderBase *NewPB = new (Mem) PlaceholderBase(FD); + PlaceholderBases.insert(NewPB, InsertToken); + return NewPB; +} } // namespace clang::lifetimes::internal diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h index 52ee8c0037b8d..3db298b6fec85 100644 --- a/clang/lib/Sema/SemaLifetimeSafety.h +++ b/clang/lib/Sema/SemaLifetimeSafety.h @@ -72,6 +72,11 @@ inline bool ShouldCheckInapplicableLifetimebound(Sema &S, const Decl *D) { diag::warn_lifetime_safety_inapplicable_lifetimebound, D->getBeginLoc()); } +inline bool ShouldCheckFieldOriginEscape(Sema &S, const Decl *D) { + return !S.getDiagnostics().isIgnored( + diag::warn_lifetime_safety_field_origin_escape, D->getBeginLoc()); +} + inline bool ShouldSuggestLifetimeAnnotations(Sema &S, const Decl *D) { DiagnosticsEngine &Diags = S.getDiagnostics(); constexpr unsigned DiagIDs[] = { @@ -114,6 +119,7 @@ inline bool IsLifetimeSafetyEnabled(Sema &S, const Decl *D) { ShouldCheckLifetimeboundViolations(S, D) || ShouldCheckMisplacedLifetimebound(S, D) || ShouldCheckInapplicableLifetimebound(S, D) || + ShouldCheckFieldOriginEscape(S, D) || ShouldSuggestLifetimeAnnotations(S, D); } @@ -127,6 +133,7 @@ inline LifetimeSafetyOpts GetLifetimeSafetyOpts(Sema &S, const Decl *D) { LSOpts.CheckMisplacedLifetimebound = ShouldCheckMisplacedLifetimebound(S, D); LSOpts.CheckInapplicableLifetimebound = ShouldCheckInapplicableLifetimebound(S, D); + LSOpts.CheckFieldOriginEscape = ShouldCheckFieldOriginEscape(S, D); return LSOpts; } @@ -458,6 +465,22 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { << EscapeExpr->getSourceRange(); } + void reportFieldOriginEscape(const CXXMethodDecl *MD, const FieldDecl *FD, + const Expr *EscapeExpr) override { + std::string FieldDesc = getDiagSubjectDescription(FD); + S.Diag(MD->getLocation(), diag::warn_lifetime_safety_field_origin_escape) + << FieldDesc << MD->getNameInfo().getSourceRange(); + + S.Diag(EscapeExpr->getBeginLoc(), diag::note_lifetime_safety_returned_here) + << EscapeExpr->getSourceRange(); + + // A raw pointer field may own its storage (e.g., Vector::data()). + if (FD->getType()->isPointerType()) + S.Diag(FD->getLocation(), + diag::note_lifetime_safety_owned_field_suggestion) + << FieldDesc << FD->getSourceRange(); + } + void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape, const Expr *EscapeExpr) override { S.Diag(ParmWithNoescape->getBeginLoc(), diff --git a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp index 4deb6d1f8688d..72d4959a69807 100644 --- a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp +++ b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp @@ -1,8 +1,8 @@ // RUN: rm -rf %t // RUN: split-file %s %t -// RUN: %clang_cc1 -fsyntax-only -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wlifetime-safety -Wlifetime-safety-annotation-placement -Wno-dangling -I%t -I%S -verify %t/test_source.cpp -// RUN: %clang_cc1 -fsyntax-only -std=c++23 -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wlifetime-safety -Wno-dangling -I%t -I%S -verify %t/test_source.cpp -// RUN: %clang_cc1 -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wlifetime-safety -Wno-dangling -I%t -I%S -fixit %t/test_source.cpp +// RUN: %clang_cc1 -fsyntax-only -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wlifetime-safety -Wlifetime-safety-annotation-placement -Wlifetime-safety-field-origin-escape -Wno-dangling -I%t -I%S -verify %t/test_source.cpp +// RUN: %clang_cc1 -fsyntax-only -std=c++23 -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wlifetime-safety -Wlifetime-safety-field-origin-escape -Wno-dangling -I%t -I%S -verify %t/test_source.cpp +// RUN: %clang_cc1 -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wlifetime-safety -Wlifetime-safety-field-origin-escape -Wno-dangling -I%t -I%S -fixit %t/test_source.cpp // RUN: %clang_cc1 -fsyntax-only -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wno-dangling -I%t -I%S -Werror=lifetime-safety-suggestions %t/test_source.cpp View definition_before_header(View a); @@ -700,3 +700,122 @@ S foo() { return S(create_up()); } } // namespace GH193747 + +namespace field_origin_escape { + +class Context { +public: + // This method should be annotated with lifetimebound. + std::string_view method() const { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}} + return method_; // expected-note {{param returned here}} + } + +private: + std::string method_ = "42"; +}; + +class ContextAnnotated { +public: + std::string_view method() const [[clang::lifetimebound]] { + return method_; + } + +private: + std::string method_ = "42"; +}; + +class ContextWrapper { +public: + explicit ContextWrapper(Context &inner, // expected-warning {{parameter in intra-TU constructor should be marked [[clang::lifetimebound]]}} + ContextAnnotated &annot) // expected-warning {{parameter in intra-TU constructor should be marked [[clang::lifetimebound]]}} + : inner_(inner), annot_(annot) {} + + Context &inner() const { // expected-warning {{return value depends on the lifetime of the object referenced by field 'inner_', which cannot be expressed with [[clang::lifetimebound]]}} + return inner_; // expected-note {{returned here}} + } + + // This method should NOT be annotated with lifetimebound. + std::string_view method() const { // expected-warning {{return value depends on the lifetime of the object referenced by field 'inner_', which cannot be expressed with [[clang::lifetimebound]]}} + return inner_.method(); // expected-note {{returned here}} + } + + std::string_view annot_method() const { // expected-warning {{return value depends on the lifetime of the object referenced by field 'annot_', which cannot be expressed with [[clang::lifetimebound]]}} + return annot_.method(); // expected-note {{returned here}} + } + +private: + Context &inner_; // expected-note {{escapes to this field}} + ContextAnnotated &annot_; // expected-note {{escapes to this field}} +}; + +struct FieldTypes { + int val_; + int *ptr_; // expected-note 2 {{if field 'ptr_' is owned by the class, mark the method [[clang::lifetimebound]]}} + int &ref_; + std::string str_; + std::string &str_ref_; + std::string *str_ptr_; // expected-note 2 {{if field 'str_ptr_' is owned by the class, mark the method [[clang::lifetimebound]]}} + std::string_view view_; + + // Loans to fields (depend on enclosing object, not field origin). + int &get_val_ref() { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}} + return val_; // expected-note {{param returned here}} + } + int *get_val_ptr() { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}} + return &val_; // expected-note {{param returned here}} + } + std::string_view get_str_view() const { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}} + return str_; // expected-note {{param returned here}} + } + const char *get_str_data() const { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}} + return str_.data(); // expected-note {{param returned here}} + } + const char *get_str_c_str() const { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}} + return str_.c_str(); // expected-note {{param returned here}} + } + const std::string &get_str_ref() const { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}} + return str_; // expected-note {{param returned here}} + } + int *&get_ptr_ref() { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}} + return ptr_; // expected-note {{param returned here}} + } + + // Owned pointer field annotated with [[clang::lifetimebound]] -> no warning. + int *get_owned_ptr() const [[clang::lifetimebound]] { + return ptr_; + } + + // Origins of fields -> field-origin-escape warning, no lifetimebound suggestion on 'this'. + int *get_ptr() const { // expected-warning {{return value depends on the lifetime of the object referenced by field 'ptr_', which cannot be expressed with [[clang::lifetimebound]]}} + return ptr_; // expected-note {{returned here}} + } + int &get_ptr_deref() const { // expected-warning {{return value depends on the lifetime of the object referenced by field 'ptr_', which cannot be expressed with [[clang::lifetimebound]]}} + return *ptr_; // expected-note {{returned here}} + } + int &get_ref() const { // expected-warning {{return value depends on the lifetime of the object referenced by field 'ref_', which cannot be expressed with [[clang::lifetimebound]]}} + return ref_; // expected-note {{returned here}} + } + int *get_ref_addr() const { // expected-warning {{return value depends on the lifetime of the object referenced by field 'ref_', which cannot be expressed with [[clang::lifetimebound]]}} + return &ref_; // expected-note {{returned here}} + } + std::string_view get_view() const { // expected-warning {{return value depends on the lifetime of the object referenced by field 'view_', which cannot be expressed with [[clang::lifetimebound]]}} + return view_; // expected-note {{returned here}} + } + const char *get_view_data() const { // expected-warning {{return value depends on the lifetime of the object referenced by field 'view_', which cannot be expressed with [[clang::lifetimebound]]}} + return view_.data(); // expected-note {{returned here}} + } + const char *get_str_ref_data() const { // expected-warning {{return value depends on the lifetime of the object referenced by field 'str_ref_', which cannot be expressed with [[clang::lifetimebound]]}} + return str_ref_.data(); // expected-note {{returned here}} + } + std::string_view get_str_ref_view() const { // expected-warning {{return value depends on the lifetime of the object referenced by field 'str_ref_', which cannot be expressed with [[clang::lifetimebound]]}} + return str_ref_; // expected-note {{returned here}} + } + const char *get_str_ptr_data() const { // expected-warning {{return value depends on the lifetime of the object referenced by field 'str_ptr_', which cannot be expressed with [[clang::lifetimebound]]}} + return str_ptr_->data(); // expected-note {{returned here}} + } + std::string_view get_str_ptr_view() const { // expected-warning {{return value depends on the lifetime of the object referenced by field 'str_ptr_', which cannot be expressed with [[clang::lifetimebound]]}} + return *str_ptr_; // expected-note {{returned here}} + } +}; + +} // namespace field_origin_escape _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
