mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
fix(semantic/cfg): connect while branches from condition exit (#23790)
Correct the CFG generated for `while` statements whose test expression creates multiple basic blocks, such as logical or conditional expressions.
Previously, the loop body and exit edges originated from the test expression's entry block. They now originate from its exit block after the complete expression has been evaluated.
## Before
The body and exit edges could bypass the test expression, leaving its exit block disconnected.
```mermaid
flowchart LR
Entry["Test entry"] --> Test["Test expression"]
Test --> Exit["Test exit"]
Entry -->|true| Body["Loop body"]
Entry -->|false| After["After while"]
Body -->|backedge| Entry
```
## After
The complete test expression is evaluated before control branches to the loop body or exit.
```mermaid
flowchart LR
Entry["Test entry"] --> Test["Test expression"]
Test --> Exit["Test exit"]
Exit -->|true| Body["Loop body"]
Exit -->|false| After["After while"]
Body -->|backedge| Entry
```
The implementation records cfg.current_node_ix after visiting the test expression as after_test_graph_ix, then uses that block for the body and exit edges. The loop backedge and continue target remain connected to the test entry so each iteration evaluates the expression again.
This commit is contained in:
1 parent
222e9f6b5f
commit
d3e92d5258
2 files changed
+7
-6
No files matched your search
@@ -1911,12 +1911,13 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
|
||||
|
||||
/* cfg - body basic block */
|
||||
#[cfg(feature = "cfg")]
|
||||
let body_graph_ix = control_flow!(self, |cfg| {
|
||||
let (after_test_graph_ix, body_graph_ix) = control_flow!(self, |cfg| {
|
||||
cfg.append_condition_to(condition_graph_ix, test_node_id);
|
||||
let after_test_graph_ix = cfg.current_node_ix;
|
||||
let body_graph_ix = cfg.new_basic_block_normal();
|
||||
|
||||
cfg.ctx(None).default().allow_break().allow_continue();
|
||||
body_graph_ix
|
||||
(after_test_graph_ix, body_graph_ix)
|
||||
});
|
||||
/* cfg */
|
||||
|
||||
@@ -1928,9 +1929,9 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
|
||||
let after_while_graph_ix = cfg.new_basic_block_normal();
|
||||
|
||||
cfg.add_edge(before_while_stmt_graph_ix, condition_graph_ix, EdgeType::Normal);
|
||||
cfg.add_edge(condition_graph_ix, body_graph_ix, EdgeType::Jump);
|
||||
cfg.add_edge(after_test_graph_ix, body_graph_ix, EdgeType::Jump);
|
||||
cfg.add_edge(after_body_graph_ix, condition_graph_ix, EdgeType::Backedge);
|
||||
cfg.add_edge(condition_graph_ix, after_while_graph_ix, EdgeType::Normal);
|
||||
cfg.add_edge(after_test_graph_ix, after_while_graph_ix, EdgeType::Normal);
|
||||
|
||||
cfg.ctx(None)
|
||||
.mark_break(after_while_graph_ix)
|
||||
|
||||
@@ -94,9 +94,9 @@ unreachable" shape = box]
|
||||
8 -> 9 [ label="Unreachable", style="dotted"]
|
||||
10 -> 2 [ label="Error(Implicit)", color=red, style=dashed]
|
||||
3 -> 4 [ label="Normal"]
|
||||
4 -> 8 [ label="Jump", color=green]
|
||||
7 -> 8 [ label="Jump", color=green]
|
||||
9 -> 4 [ label="Backedge", style="dotted", color=grey]
|
||||
4 -> 10 [ label="Normal"]
|
||||
7 -> 10 [ label="Normal"]
|
||||
11 -> 2 [ label="Error(Implicit)", style=dashed, color=red]
|
||||
10 -> 11 [ label="Unreachable", style="dotted"]
|
||||
12 -> 0 [ label="Error(Implicit)", color=red, style=dashed]
|
||||
|
||||
Reference in new issue
Block a user