================
@@ -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
_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits