https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/180446
>From b4de7383fcb75f60b68aaa00dafb69858c0214fc Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena <[email protected]> Date: Sun, 8 Feb 2026 22:29:52 +0000 Subject: [PATCH] Improve liveness to detect more invaldiations --- .../Analyses/LifetimeSafety/FactsGenerator.h | 8 ++--- .../LifetimeSafety/FactsGenerator.cpp | 29 ++++++++++--------- clang/test/Sema/Inputs/lifetime-analysis.h | 16 ++++++---- .../warn-lifetime-safety-invalidations.cpp | 23 +++++++++++++-- 4 files changed, 51 insertions(+), 25 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h index fb7d5ad91db79..43551e48c8ac0 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h @@ -102,10 +102,10 @@ class FactsGenerator : public ConstStmtVisitor<FactsGenerator> { /// If so, creates a `TestPointFact` and returns true. bool handleTestPoint(const CXXFunctionalCastExpr *FCE); - // A DeclRefExpr will be treated as a use of the referenced decl. It will be + // Treats an expression as a use of the referenced object. It will be // checked for use-after-free unless it is later marked as being written to - // (e.g. on the left-hand side of an assignment). - void handleUse(const DeclRefExpr *DRE); + // (e.g. on the left-hand side of an assignment in the case of a DeclRefExpr). + void handleUse(const Expr *E); void markUseAsWrite(const DeclRefExpr *DRE); @@ -122,7 +122,7 @@ class FactsGenerator : public ConstStmtVisitor<FactsGenerator> { // `DeclRefExpr`s as "read" uses. When an assignment is processed, the use // corresponding to the left-hand side is updated to be a "write", thereby // exempting it from the check. - llvm::DenseMap<const DeclRefExpr *, UseFact *> UseFacts; + llvm::DenseMap<const Expr *, UseFact *> UseFacts; }; } // namespace clang::lifetimes::internal diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index b69f69ddbae34..be62acf475fd1 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -366,6 +366,7 @@ void FactsGenerator::VisitBinaryOperator(const BinaryOperator *BO) { // result should have the same loans as the pointer operand. if (BO->isCompoundAssignmentOp()) return; + handleUse(BO->getRHS()); if (BO->isAssignmentOp()) handleAssignment(BO->getLHS(), BO->getRHS()); // TODO: Handle assignments involving dereference like `*p = q`. @@ -582,7 +583,9 @@ void FactsGenerator::handleFunctionCall(const Expr *Call, FD = getDeclWithMergedLifetimeBoundAttrs(FD); if (!FD) return; - + // All arguments to a function are a use of the corresponding expressions. + for (const Expr *Arg : Args) + handleUse(Arg); handleInvalidatingCall(Call, FD, Args); handleMovedArgsInCall(FD, Args); if (!CallList) @@ -677,24 +680,24 @@ bool FactsGenerator::handleTestPoint(const CXXFunctionalCastExpr *FCE) { return false; } -// A DeclRefExpr will be treated as a use of the referenced decl. It will be -// checked for use-after-free unless it is later marked as being written to -// (e.g. on the left-hand side of an assignment). -void FactsGenerator::handleUse(const DeclRefExpr *DRE) { - OriginList *List = getOriginsList(*DRE); +void FactsGenerator::handleUse(const Expr *E) { + OriginList *List = getOriginsList(*E); if (!List) return; - // Remove the outer layer of origin which borrows from the decl directly - // (e.g., when this is not a reference). This is a use of the underlying decl. - if (!DRE->getDecl()->getType()->isReferenceType()) + // For DeclRefExpr: Remove the outer layer of origin which borrows from the + // decl directly (e.g., when this is not a reference). This is a use of the + // underlying decl. + if (auto *DRE = dyn_cast<DeclRefExpr>(E); + DRE && !DRE->getDecl()->getType()->isReferenceType()) List = getRValueOrigins(DRE, List); // Skip if there is no inner origin (e.g., when it is not a pointer type). if (!List) return; - UseFact *UF = FactMgr.createFact<UseFact>(DRE, List); - CurrentBlockFacts.push_back(UF); - assert(!UseFacts.contains(DRE)); - UseFacts[DRE] = UF; + if (!UseFacts.contains(E)) { + UseFact *UF = FactMgr.createFact<UseFact>(E, List); + CurrentBlockFacts.push_back(UF); + UseFacts[E] = UF; + } } void FactsGenerator::markUseAsWrite(const DeclRefExpr *DRE) { diff --git a/clang/test/Sema/Inputs/lifetime-analysis.h b/clang/test/Sema/Inputs/lifetime-analysis.h index f30db1a29b149..880e4650f21d0 100644 --- a/clang/test/Sema/Inputs/lifetime-analysis.h +++ b/clang/test/Sema/Inputs/lifetime-analysis.h @@ -75,11 +75,6 @@ struct vector { void clear(); }; -template<class Key,class T> -struct unordered_map { - T& operator[](const Key& key); -}; - template<class T> void swap( T& a, T& b ); @@ -89,6 +84,17 @@ struct pair { B second; }; +template<class Key,class T> +struct unordered_map { + using iterator = __gnu_cxx::basic_iterator<std::pair<const Key, T>>; + T& operator[](const Key& key); + iterator begin(); + iterator end(); + iterator find(const Key& key); + void erase(iterator); +}; + + template<typename T> struct basic_string_view { basic_string_view(); diff --git a/clang/test/Sema/warn-lifetime-safety-invalidations.cpp b/clang/test/Sema/warn-lifetime-safety-invalidations.cpp index c9ce0c35c53d2..dc924eb1f599b 100644 --- a/clang/test/Sema/warn-lifetime-safety-invalidations.cpp +++ b/clang/test/Sema/warn-lifetime-safety-invalidations.cpp @@ -260,9 +260,26 @@ void PointerToVectorElement() { } void SelfInvalidatingMap() { - std::unordered_map<int, int> mp; - mp[1] = 1; - mp[2] = mp[1]; // FIXME: Detect this. We are mising a UseFact for the assignment params. + std::unordered_map<int, std::string> mp; + // TODO: We do not have a way to differentiate between pointer stability and iterator stability! + // + // std::unordered_map and other containers provide pointer/reference stability. Therefore the + // following is safe in practice. + // On the other hand, std::flat_hash_map (since C++23) does not provide pointer stability on + // insertion and following is unsafe for this container. + // + // FIXME(https://github.com/llvm/llvm-project/issues/180521): RHS should be evaluated before the LHS. + // RHS is the reference which is invaldiated by the LHS (and not the opposite which is the case now). + mp[1] = "42"; + mp[2] // expected-warning {{object whose reference is captured is later invalidated}} expected-note {{later used here}} + = + mp[1]; // expected-note {{invalidated here}} + + // None of these containers provide iterator stability. So following is unsafe: + auto it = mp.find(3); // expected-warning {{object whose reference is captured is later invalidated}} + mp.erase(mp.find(4)); // expected-note {{invalidated here}} + if (it != mp.end()) // expected-note {{later used here}} + *it; } } // namespace ElementReferences _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
