This patch reworks generation for the CFGScopeBegin, CFGScopeEnd,
and CFGLiftimeEnd, in a way that they are now compatible with each
other and CFGAutomaticObjDtor. All of the above elements are now
generated by a single code path, that conditionally inserts elements if
they are requested.
In addition, the handling of goto statements is improved.
The goto statement may leave multiple scopes (and trigger destruction
and lifetime end for the affected variables) and enter multiple scopes,
for example:
{ int s1; { int s2; goto label; // leaves s1, s2, and enters t1 t1 } } { int t1; { int t2; label: } }
This is performed by first determining the shared parent scope of the
source and destination. And then emitting elements for exiting each
scope between the source and the parent, and entering each scope
between the parent and destination. All such elements are appended
to the source block, as one label may be reached from multiple scopes.
Finally, the approach for handling backward jumps is changed. When
connecting a source block to a destination block that requires the
insertion of additional elements, we put this element into a new block,
which is then linked between the source and the destination block.
For example:
{ int t; label: // Destination block referred to as 'DB' } { // Source block referred to as 'SB' Obj s; goto label; }
The jump between SB with terminator T: goto and DB should be
coupled with the following CFG elements:
CFGAutomaticObjDtor(s) CFGLifetimeEnd(s) CFGScopeEnd(s) CFGScopeBegin(t)
To handle such situations, we create a new link (LB) that is linked as
the predecessor of DB, to which we transfer the terminator (goto
statement) of SB. Then LB is handled in the same manner as the
source block in the case of forward jumps.
This produces CFG that looks like this:
SB -> LB (T: goto) -> DB
Finally, the resulting block is linked as the successor of SB. Such an
approach uses existing handling of the noreturn destructors.
As a reminder, for each destructor of an automatic object that is
marked as noreturn, a new noreturn block (marked NBn) is
created, at the destructor is inserted at the end of it.
To illustrate, given two noreturn destructors, we will have:
SB -> NB1 (noreturn) NB2 (noreturn) LB (T:goto) -> DB
Consider adding comments describing these methods, either here or at the function definitions.