Skip to content

Commit

Permalink
fix: correct order of basic block successors (#110)
Browse files Browse the repository at this point in the history
Co-authored-by: Mark Koch <[email protected]>
Co-authored-by: Mark Koch <[email protected]>
  • Loading branch information
3 people authored Jan 18, 2024
1 parent e0761ff commit a42db7d
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 4 deletions.
2 changes: 1 addition & 1 deletion guppylang/cfg/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,8 +459,8 @@ def generic_visit(self, node: ast.expr, bb: BB, true_bb: BB, false_bb: BB) -> No
# the result as a branch predicate
pred, bb = ExprBuilder.build(node, self.cfg, bb)
bb.branch_pred = pred
self.cfg.link(bb, true_bb)
self.cfg.link(bb, false_bb)
self.cfg.link(bb, true_bb)


def is_functional_annotation(stmt: ast.stmt) -> bool:
Expand Down
27 changes: 24 additions & 3 deletions guppylang/checker/cfg_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
"""

import collections
from collections.abc import Sequence
from collections.abc import Iterator, Sequence
from dataclasses import dataclass
from typing import TypeVar

from guppylang.ast_util import line_col
from guppylang.cfg.bb import BB
Expand Down Expand Up @@ -76,7 +77,9 @@ def check_cfg(
# We do BFS instead of DFS to get a better error ordering.
queue = collections.deque(
(checked_cfg.entry_bb, i, succ)
for i, succ in enumerate(cfg.entry_bb.successors)
# We enumerate the successor starting from the back, so we start with the `True`
# branch. This way, we find errors in a more natural order
for i, succ in reverse_enumerate(cfg.entry_bb.successors)
)
while len(queue) > 0:
pred, num_output, bb = queue.popleft()
Expand All @@ -92,7 +95,12 @@ def check_cfg(
else:
# Otherwise, check the BB and enqueue its successors
checked_bb = check_bb(bb, checked_cfg, input_row, return_ty, globals)
queue += [(checked_bb, i, succ) for i, succ in enumerate(bb.successors)]
queue += [
# We enumerate the successor starting from the back, so we start with
# the `True` branch. This way, we find errors in a more natural order
(checked_bb, i, succ)
for i, succ in reverse_enumerate(bb.successors)
]
compiled[bb] = checked_bb

# Link up BBs in the checked CFG
Expand Down Expand Up @@ -218,3 +226,16 @@ def check_rows_match(row1: VarRow, row2: VarRow, bb: BB) -> None:
bb.containing_cfg.live_before[bb][v1.name].vars.used[v1.name],
[v1.defined_at, v2.defined_at],
)


T = TypeVar("T")


def reverse_enumerate(xs: list[T]) -> Iterator[tuple[int, T]]:
"""Enumerates a list in reverse order.
Equivalent to `reversed(list(enumerate(data)))` without creating an intermediate
list.
"""
for i in range(len(xs) - 1, -1, -1):
yield i, xs[i]
2 changes: 2 additions & 0 deletions tests/error/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import pytest
pytest.register_assert_rewrite("tests.error.util")

0 comments on commit a42db7d

Please sign in to comment.