https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/180446
>From cb33c1b2ccc91107359b7d5803651bb2e2ba424d 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 | 4 +-- .../LifetimeSafety/FactsGenerator.cpp | 29 +++++++++++-------- clang/test/Sema/Inputs/lifetime-analysis.h | 16 ++++++---- .../warn-lifetime-safety-invalidations.cpp | 19 ++++++++++-- 4 files changed, 46 insertions(+), 22 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h index fb7d5ad91db79..315f396fc986d 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h @@ -105,7 +105,7 @@ class FactsGenerator : public ConstStmtVisitor<FactsGenerator> { // 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 handleUse(const DeclRefExpr *DRE); + 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..805809cc593bb 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,26 @@ 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; + // 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). // 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()) - List = getRValueOrigins(DRE, List); + if (auto *DRE = dyn_cast<DeclRefExpr>(E)) + if (!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..414541eb031c5 100644 --- a/clang/test/Sema/warn-lifetime-safety-invalidations.cpp +++ b/clang/test/Sema/warn-lifetime-safety-invalidations.cpp @@ -260,9 +260,22 @@ 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. + mp[1] = "42"; + mp[2] = mp[1]; // expected-warning {{object whose reference is captured is later invalidated}} \ + // expected-note {{invalidated here}} \ + // expected-note {{later used 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
