[clang] [LifetimeSafety] Detect expiry of loans to trivially destructed types (PR #168855)

Kashika Akhouri via cfe-commits cfe-commits at lists.llvm.org
Fri Nov 21 01:07:19 PST 2025


================
@@ -66,6 +66,9 @@ void FactsGenerator::run() {
       else if (std::optional<CFGAutomaticObjDtor> DtorOpt =
                    Element.getAs<CFGAutomaticObjDtor>())
         handleDestructor(*DtorOpt);
+      else if (std::optional<CFGLifetimeEnds> LifetimeEnds =
+                   Element.getAs<CFGLifetimeEnds>())
+        handleTrivialDestructors(*LifetimeEnds);
----------------
kashika0112 wrote:

The test cases are passing now (after removing the destructor handling). The issue was that in handleLifetimeEnds I was using a break statement after it found the first expireFact. After removing that break, all tests are passing. Just to give an example.

For the first one:
Test:
```
void definite_multiple_pointers() {
  std::string *p, *q, *r;
  {
    std::string s;
    p = &s;     
    q = &s;     
    r = &s;     
  }             
  std::cout<< *p;
  std::cout<< *q;
  std::cout<< *r;
}
```

CFG with handleDestructor:
```
Function: definite_multiple_pointers
  Block B2:
  End of Block
  Block B1:
    Issue (0 (Path: s), ToOrigin: 0 (Expr: DeclRefExpr))
    OriginFlow (Dest: 1 (Expr: UnaryOperator), Src: 0 (Expr: DeclRefExpr))
    Use (2 (Decl: p), Write)
    OriginFlow (Dest: 2 (Decl: p), Src: 1 (Expr: UnaryOperator))
    Issue (1 (Path: s), ToOrigin: 3 (Expr: DeclRefExpr))
    OriginFlow (Dest: 4 (Expr: UnaryOperator), Src: 3 (Expr: DeclRefExpr))
    Use (5 (Decl: q), Write)
    OriginFlow (Dest: 5 (Decl: q), Src: 4 (Expr: UnaryOperator))
    Issue (2 (Path: s), ToOrigin: 6 (Expr: DeclRefExpr))
    OriginFlow (Dest: 7 (Expr: UnaryOperator), Src: 6 (Expr: DeclRefExpr))
    Use (8 (Decl: r), Write)
    OriginFlow (Dest: 8 (Decl: r), Src: 7 (Expr: UnaryOperator))
    Expire (0 (Path: s))
    Expire (1 (Path: s))
    Expire (2 (Path: s))
    Expire (0 (Path: s))
    Issue (3 (Path: operator<<), ToOrigin: 9 (Expr: DeclRefExpr))
    OriginFlow (Dest: 10 (Expr: ImplicitCastExpr), Src: 9 (Expr: DeclRefExpr))
    Issue (4 (Path: cout), ToOrigin: 11 (Expr: DeclRefExpr))
    Use (2 (Decl: p), Read)
    OriginFlow (Dest: 12 (Expr: ImplicitCastExpr), Src: 2 (Decl: p))
    OriginFlow (Dest: 13 (Expr: ImplicitCastExpr), Src: 14 (Expr: UnaryOperator))
    Issue (5 (Path: operator<<), ToOrigin: 15 (Expr: DeclRefExpr))
    OriginFlow (Dest: 16 (Expr: ImplicitCastExpr), Src: 15 (Expr: DeclRefExpr))
    Issue (6 (Path: cout), ToOrigin: 17 (Expr: DeclRefExpr))
    Use (5 (Decl: q), Read)
    OriginFlow (Dest: 18 (Expr: ImplicitCastExpr), Src: 5 (Decl: q))
    OriginFlow (Dest: 19 (Expr: ImplicitCastExpr), Src: 20 (Expr: UnaryOperator))
    Issue (7 (Path: operator<<), ToOrigin: 21 (Expr: DeclRefExpr))
    OriginFlow (Dest: 22 (Expr: ImplicitCastExpr), Src: 21 (Expr: DeclRefExpr))
    Issue (8 (Path: cout), ToOrigin: 23 (Expr: DeclRefExpr))
    Use (8 (Decl: r), Read)
    OriginFlow (Dest: 24 (Expr: ImplicitCastExpr), Src: 8 (Decl: r))
    OriginFlow (Dest: 25 (Expr: ImplicitCastExpr), Src: 26 (Expr: UnaryOperator))
  End of Block
  Block B0:
  End of Block
```

CFG without handleDestructor (only LifetimeEnds)
```
Function: definite_multiple_pointers
  Block B2:
  End of Block
  Block B1:
    Issue (0 (Path: s), ToOrigin: 0 (Expr: DeclRefExpr))
    OriginFlow (Dest: 1 (Expr: UnaryOperator), Src: 0 (Expr: DeclRefExpr))
    Use (2 (Decl: p), Write)
    OriginFlow (Dest: 2 (Decl: p), Src: 1 (Expr: UnaryOperator))
    Issue (1 (Path: s), ToOrigin: 3 (Expr: DeclRefExpr))
    OriginFlow (Dest: 4 (Expr: UnaryOperator), Src: 3 (Expr: DeclRefExpr))
    Use (5 (Decl: q), Write)
    OriginFlow (Dest: 5 (Decl: q), Src: 4 (Expr: UnaryOperator))
    Issue (2 (Path: s), ToOrigin: 6 (Expr: DeclRefExpr))
    OriginFlow (Dest: 7 (Expr: UnaryOperator), Src: 6 (Expr: DeclRefExpr))
    Use (8 (Decl: r), Write)
    OriginFlow (Dest: 8 (Decl: r), Src: 7 (Expr: UnaryOperator))
    Expire (0 (Path: s))
    Expire (1 (Path: s))
    Expire (2 (Path: s))
    Issue (3 (Path: operator<<), ToOrigin: 9 (Expr: DeclRefExpr))
    OriginFlow (Dest: 10 (Expr: ImplicitCastExpr), Src: 9 (Expr: DeclRefExpr))
    Issue (4 (Path: cout), ToOrigin: 11 (Expr: DeclRefExpr))
    Use (2 (Decl: p), Read)
    OriginFlow (Dest: 12 (Expr: ImplicitCastExpr), Src: 2 (Decl: p))
    OriginFlow (Dest: 13 (Expr: ImplicitCastExpr), Src: 14 (Expr: UnaryOperator))
    Issue (5 (Path: operator<<), ToOrigin: 15 (Expr: DeclRefExpr))
    OriginFlow (Dest: 16 (Expr: ImplicitCastExpr), Src: 15 (Expr: DeclRefExpr))
    Issue (6 (Path: cout), ToOrigin: 17 (Expr: DeclRefExpr))
    Use (5 (Decl: q), Read)
    OriginFlow (Dest: 18 (Expr: ImplicitCastExpr), Src: 5 (Decl: q))
    OriginFlow (Dest: 19 (Expr: ImplicitCastExpr), Src: 20 (Expr: UnaryOperator))
    Issue (7 (Path: operator<<), ToOrigin: 21 (Expr: DeclRefExpr))
    OriginFlow (Dest: 22 (Expr: ImplicitCastExpr), Src: 21 (Expr: DeclRefExpr))
    Issue (8 (Path: cout), ToOrigin: 23 (Expr: DeclRefExpr))
    Use (8 (Decl: r), Read)
    OriginFlow (Dest: 24 (Expr: ImplicitCastExpr), Src: 8 (Decl: r))
    OriginFlow (Dest: 25 (Expr: ImplicitCastExpr), Src: 26 (Expr: UnaryOperator))
  End of Block
  Block B0:
  End of Block
  ```
  
Given this, should I remove handleDestructor now?

https://github.com/llvm/llvm-project/pull/168855


More information about the cfe-commits mailing list