[llvm-branch-commits] [clang] [analyzer] Turn on CFGLifetimeEnds elements by default (PR #228228)

Benedek Kaibas via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Oct 19 12:06:36 PDT 2026


================
@@ -67,21 +67,22 @@ int main() {
 // FIXME: On steps 8 and 14, a decomposition is referred by name, which they never have.
 
 // CHECK-LABEL: int main()
-// CHECK:      [B3]
+// CHECK:      [B4]
----------------
benedekaibas wrote:

I was not sure where to look for the fix, if it is an actual problem and AI give me answers that were definitely wrong, so I decided to do my own test: I checked whether destructors and lifetime end elements are placed differently. Both a destructor and the lifetime end apply at the scope exit, so a destructor is a good thing to compare the lifetime end against.

For the test case I wrote a simple destructor in the `vector`:
```cpp
struct vector {
  ~vector();
  __wrap_iter begin();
  __wrap_iter end();
};
```

When I constructed the CFG for the example (with `cfg-lifetime=true`) we get the following CFG:
```text
 [B6 (ENTRY)]
   Succs (1): B5

 [B1]
   1: [B5.8] (Lifetime ends)
   2: [B5.12] (Lifetime ends)
   3: [B5.4] (Lifetime ends)
   4: [B5.2].~std::vector() (Implicit destructor)
   5: [B5.2] (Lifetime ends)
   Preds (1): B2
   Succs (1): B0

 [B2]
   1: operator!=
   2: [B2.1] (ImplicitCastExpr, FunctionToPointerDecay, _Bool (*)(__wrap_iter, __wrap_iter))
   3: __begin1
   4: [B2.3] (ImplicitCastExpr, NoOp, const __wrap_iter)
   5: [B2.4] (CXXConstructExpr, [B2.9]+0, __wrap_iter)
   6: __end1
   7: [B2.6] (ImplicitCastExpr, NoOp, const __wrap_iter)
   8: [B2.7] (CXXConstructExpr, [B2.9]+1, __wrap_iter)
   9: [B2.5] != [B2.8] (OperatorCall)
   T: for (auto & [a, b] : [B5.3])
    ;

   Preds (2): B3 B5
   Succs (2): B4 B1

 [B3]
   1: operator++
   2: [B3.1] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(void))
   3: __begin1
   4: ++ [B3.3] (OperatorCall)
   Preds (1): B4
   Succs (1): B2

 [B4]
   1: operator*
   2: [B4.1] (ImplicitCastExpr, FunctionToPointerDecay, iterator_traits<pair<int, int> *>::reference (*)(void))
   3: __begin1
   4: * [B4.3] (OperatorCall)
   5: auto & [a, b];
   6: get<0UL>
   7: [B4.6] (ImplicitCastExpr, FunctionToPointerDecay, tuple_element<0L, pair<int, int> >::type (*)(pair<int, int> &))
   8: decomposition-a-b
   9: [B4.7]([B4.8])
  10: std::tuple_element<0UL, std::pair<int, int>>::type a = get<0UL>(decomposition-a-b);
  11: get<1UL>
  12: [B4.11] (ImplicitCastExpr, FunctionToPointerDecay, tuple_element<1L, pair<int, int> >::type (*)(pair<int, int> &))
  13: decomposition-a-b
  14: [B4.12]([B4.13])                                                                                                                            15: std::tuple_element<1UL, std::pair<int, int>>::type b = get<1UL>(decomposition-a-b);
  16: [B4.5] (Lifetime ends)
   Preds (1): B2
   Succs (1): B3

 [B5]
   1:  (CXXConstructExpr, [B5.2], std::vector)
   2: std::vector v;
   3: v
   4: auto &&__range1 = v;
   5: __range1
   6: [B5.5].end
   7: [B5.6]() (CXXRecordTypedCall, [B5.8])
   8: auto __end1 = __range1.end();
   9: __range1
  10: [B5.9].begin
  11: [B5.10]() (CXXRecordTypedCall, [B5.12])
  12: auto __begin1 = __range1.begin();
   Preds (1): B6
   Succs (1): B2

 [B0 (EXIT)]
   Preds (1): B1
```

If I set `cfg-lifetime=false` then B1 just contains the `[B5.2].~std::vector() (Implicit destructor)`.

> So the lifetime end elements get their own block essentially

Well, I was wrong for this part based on the above example. It is not that lifetime end elements get their own block (because then they would need to be separated from the destructor in the above example as well), but the fact that they are landing in the scope-exti cleanup block just like destructors. IMO this is correct since the lifetime end markers belong wherever the object's scope ends and for `v` that is after the loop.

If I keep the original test case (remove the destructor) then B1 still holds the four lifetime end elements since that is the scope-exit cleanup block and their `DeclStmt`s are all in B5.

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


More information about the llvm-branch-commits mailing list