Skip to content

Commit 2b1e38e

Browse files
committed
Add backend parity verifier
1 parent 452d176 commit 2b1e38e

20 files changed

Lines changed: 424 additions & 42 deletions

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ jobs:
5757
- name: Verify examples through C
5858
run: uv run python scripts/verify_examples_e2e_c.py
5959

60+
- name: Verify backend parity
61+
run: uv run python scripts/verify_backend_parity.py
62+
6063
- name: Build debug artifacts
6164
run: uv run python scripts/build_examples.py --profile debug --backend both --clean
6265

CHANGELOG.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2727
returns, linked-list traversal, iterative binary-tree traversal, and string
2828
utilities; tightened example catalog copy where language support is still
2929
status-only.
30+
- Added a selected Zig/C backend parity verifier that compiles, builds, runs,
31+
and compares non-example smoke programs across both native backends.
3032

3133
## [0.3.0] - 2026-05-07
3234

@@ -48,7 +50,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4850
- Expanded `scripts/verify_error_stages.py` and `test/test_error_stage_matrix.py` so deferred semantic errors are checked across all semantic-capable modes and human/JSON output.
4951

5052
- **Labeled Loops** (both backends)
51-
- `outer: while`, `outer: for`, `outer: for-in` with `break outer` / `continue outer`.
53+
- `@outer while`, `@outer for`, `@outer for-in` with `break outer` / `continue outer`.
54+
- The old `outer: for` spelling is rejected so `name:` remains reserved for typed bindings, fields, and case-like syntax.
5255
- Zig backend emits native labeled loops; C backend lowers to goto-based control flow.
5356

5457
- **Slice Expressions in C Backend**
@@ -357,7 +360,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
357360
- Inline type sets: `fn($T: @type_set(i32, i64), x: T) T`
358361
- AST stores constraint on GENERIC_PARAM nodes
359362
- **Labeled Loops**: Implemented for all loop forms (while, for, for-in, indexed for-in)
360-
- Syntax: `outer: for i := 0; i < 10; i += 1 { break outer }`
363+
- Syntax: `@outer for i := 0; i < 10; i += 1 { break outer }`
361364
- Zig backend emits native labeled loops (`label: while ...`, `break :label`)
362365
- C backend lowers to goto-based control flow with unique labels
363366
- **Builtin Intrinsics**: All `@function` intrinsics now parse correctly

MISSING_FEATURES.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
## Recently Completed (Language Core)
1010

11-
1. **Labeled loops**`outer: while`, `outer: for`, `outer: for-in` with `break outer` / `continue outer` in both Zig and C backends.
11+
1. **Labeled loops**`@outer while`, `@outer for`, `@outer for-in` with `break outer` / `continue outer` in both Zig and C backends.
1212
2. **Slice expressions in C backend**`arr[1..4]` on arrays and slices, including indexing and `for-in` over slices.
1313
3. **Type checker: slice and index validation**`visit_slice_expr` returns `SliceType`; `visit_index_expr` rejects non-integer indices.
1414
4. `match` expressions are type-checked and participate in expression typing.
@@ -27,7 +27,7 @@
2727
13. Constant folding covers arithmetic, boolean logic, literal comparisons, and integer bitwise expressions.
2828
14. Installed CLI entrypoint (`a7`) is wired through `pyproject.toml`.
2929
15. Debug/release example artifact verification is available through `scripts/build_examples.py`.
30-
16. `run_all_tests.sh` includes C backend verification, both example E2E verifiers, debug/release artifact builds, the error-stage matrix, docs style checks, and full pytest.
30+
16. `run_all_tests.sh` includes C backend verification, both example E2E verifiers, selected Zig/C parity smoke checks, debug/release artifact builds, the error-stage matrix, docs style checks, and full pytest.
3131
17. Local file-based imports now fail closed during semantic analysis instead of swallowing module loading failures.
3232
18. Zig unsupported expression fallbacks now fail as compiler-side codegen errors instead of generated `@compileError` expressions.
3333
19. `fall` now fails closed with a semantic error instead of being ignored or reaching backend output.

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ PYTHONPATH=. uv run pytest test/test_tokenizer.py # Specific test file
7474
PYTHONPATH=. uv run pytest -k "generic" -v # Targeted tests
7575
uv run python scripts/verify_examples_e2e.py # Compile/build/run + output checks for all examples
7676
uv run python scripts/verify_examples_e2e_c.py # Same flow via C backend + zig cc
77+
uv run python scripts/verify_backend_parity.py # Selected Zig/C differential smoke checks
7778
uv run python scripts/verify_error_stages.py # Error-stage audit across modes and formats
7879
uv run python scripts/build_examples.py --profile debug --backend both --clean
7980
uv run python scripts/build_examples.py --profile release --backend both --clean
@@ -162,6 +163,8 @@ Use fixed-width integers such as `i32`, `i64`, `u32`, or `u64` when the data its
162163
- Example end-to-end verification is available for both backends:
163164
- `uv run python scripts/verify_examples_e2e.py`
164165
- `uv run python scripts/verify_examples_e2e_c.py`
166+
- Selected Zig/C differential smoke checks are available through
167+
`scripts/verify_backend_parity.py`.
165168
- Debug/release artifact verification is available through `scripts/build_examples.py`.
166169
- Tag releases publish package artifacts through PyPI Trusted Publishing once the PyPI project trusted publisher is configured.
167170
- Parser covers the implemented language surface, but spec/implementation gaps remain tracked in `MISSING_FEATURES.md`.

RELEASE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ uvx pip-audit --strict
7373
- Zig and C backend tests
7474
- Zig example compile/build/run/output verification
7575
- C example compile/build/run/output verification
76+
- selected Zig/C backend parity verification beyond examples
7677
- debug artifact build verification for Zig and C
7778
- release artifact build verification for Zig and C
7879
- CLI error-stage matrix verification

RELEASE_READINESS_REVIEW.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ not factually provable from local tests alone.
3232
- `PYTHONPATH=. uv run pytest test/test_release_tooling.py -q`
3333
- `uv run python scripts/build_examples.py --profile debug --backend both --clean`
3434
- `uv run python scripts/build_examples.py --profile release --backend both --clean`
35+
- `uv run python scripts/verify_backend_parity.py`
3536
- `./run_all_tests.sh`
3637
- `uv build`
3738
- `uvx pip-audit --strict`
@@ -41,10 +42,11 @@ not factually provable from local tests alone.
4142
- `cd site && npm run build`
4243
- built wheel installed into a temporary virtualenv and invoked as `a7`
4344
- `git diff --check`
44-
- hosted CI run `25519819833` passed for commit `13bf369`:
45+
- hosted CI run `25520030936` passed for commit `452d176` before the backend
46+
parity verifier was added:
4547
docs, pytest, dependency audits, error-stage verification, Zig and C example
4648
verification, debug artifacts, release artifacts, and package build
47-
- hosted Deploy Docs run `25519819834` passed for commit `13bf369`
49+
- hosted Deploy Docs run `25520030885` passed for commit `452d176`
4850
- manual release workflow dispatch passed on `master` after the PyPI publish
4951
dependency update; run `25517785179` uploaded
5052
`python-package-distributions` artifact `6864726540` and `release-bundles`
@@ -60,7 +62,8 @@ not factually provable from local tests alone.
6062
- `scripts/build_examples.py` builds and verifies debug/release artifacts for
6163
both backends.
6264
- `run_all_tests.sh` now covers C backend tests, C E2E, error-stage audit,
63-
debug/release artifact builds, docs style, and full pytest.
65+
selected Zig/C backend parity checks, debug/release artifact builds, docs
66+
style, and full pytest.
6467
- GitHub CI now runs Python tests, backend verifiers, artifact builds, package
6568
build, dependency audits, secret scanning, docs style, docs lint, and docs
6669
build.
@@ -102,6 +105,8 @@ not factually provable from local tests alone.
102105
- Semantic validation now rejects direct and mutual recursion and avoids false
103106
recursion reports when a local function-pointer variable shadows a top-level
104107
function name.
108+
- Selected non-example programs now run through both Zig and C backends and
109+
compare runtime output.
105110

106111
## Residual Risks
107112

@@ -111,7 +116,8 @@ not factually provable from local tests alone.
111116
- Full ownership/lifetime safety is not implemented.
112117
- Built-in stdlib imports are virtual and still need unification with file-based
113118
module semantics.
114-
- Backend parity is verified for examples, not all possible source programs.
119+
- Backend parity is verified for examples and selected differential smoke
120+
programs, not all possible source programs.
115121
- Tag-based PyPI publishing is wired, but `a7-py` is not yet a public PyPI
116122
project and still needs matching trusted-publisher configuration before the
117123
first real publish.

SECURITY.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ uv run python scripts/check_no_secrets.py
2727
```
2828

2929
The full gate includes parser/tokenizer tests, semantic tests, Zig and C backend
30-
tests, example runtime verification, debug/release artifact builds, error-stage
31-
checks, docs style checks, and full pytest.
30+
tests, example runtime verification, selected Zig/C backend parity checks,
31+
debug/release artifact builds, error-stage checks, docs style checks, and full
32+
pytest.
3233

3334
## Known Security-Relevant Limitations
3435

TODO.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ These are entire subsystems. Each needs a design decision before implementation
214214

215215
- [x] Add parser coverage for labeled `continue`, nested labeled loops, and malformed labels.
216216
Files: `test/test_parser_combinatorial.py`, `test/test_parser_integration.py`
217-
Notes: parser combinatorial tests now assert labeled `continue`, nested `for`/`while` labels, labeled `for-in` forms, and malformed non-loop labels.
217+
Notes: parser combinatorial tests now assert `@label` loop prefixes, labeled `continue`, nested `for`/`while` labels, labeled `for-in` forms, and malformed or old `label:` labels.
218218

219219
- [x] Add Zig regression coverage for labeled `for-in` and indexed `for-in`.
220220
Files: `test/test_codegen_zig.py`
@@ -245,7 +245,7 @@ These are entire subsystems. Each needs a design decision before implementation
245245

246246
- [x] Add CI coverage for release-oriented gates.
247247
Files: `.github/workflows/ci.yml`, `.github/workflows/deploy-docs.yml`, `site/package-lock.json`
248-
Notes: CI now installs Zig, runs pytest, dependency audits, error-stage checks, both backend E2E verifiers, debug/release artifact builds, package build, docs style, docs lint, and docs build. Pages deploy now uses `npm ci`.
248+
Notes: CI now installs Zig, runs pytest, dependency audits, error-stage checks, both backend E2E verifiers, selected backend parity checks, debug/release artifact builds, package build, docs style, docs lint, and docs build. Pages deploy now uses `npm ci`.
249249

250250
- [x] Add dependency-audit checks for release readiness.
251251
Files: `.github/workflows/ci.yml`, `RELEASE.md`, `SECURITY.md`

docs/SPEC.md

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -617,8 +617,8 @@ break
617617
// Continue to next iteration
618618
continue
619619
620-
// Break/continue with label
621-
outer: for i := 0; i < 10; i += 1 {
620+
// Break/continue with loop label
621+
@outer for i := 0; i < 10; i += 1 {
622622
for j := 0; j < 10; j += 1 {
623623
if condition {
624624
break outer
@@ -627,6 +627,10 @@ outer: for i := 0; i < 10; i += 1 {
627627
}
628628
```
629629

630+
Loop labels use `@name` directly before a loop statement. The old
631+
`name: for ...` spelling is rejected because `name:` is reserved for typed
632+
bindings, fields, and case-like syntax.
633+
630634
Semantic validation reports unreachable statements that appear later in the same block after `ret`, a valid `break` or `continue`, `fall`, or an `if`/`match` statement whose branches all terminate.
631635

632636
---
@@ -1905,8 +1909,8 @@ break
19051909
// Continue to next iteration
19061910
continue
19071911
1908-
// Break/continue with label
1909-
outer: for i := 0; i < 10; i += 1 {
1912+
// Break/continue with loop label
1913+
@outer for i := 0; i < 10; i += 1 {
19101914
for j := 0; j < 10; j += 1 {
19111915
if condition {
19121916
break outer
@@ -1915,6 +1919,10 @@ outer: for i := 0; i < 10; i += 1 {
19151919
}
19161920
```
19171921

1922+
Loop labels use `@name` directly before a loop statement. The old
1923+
`name: for ...` spelling is rejected because `name:` is reserved for typed
1924+
bindings, fields, and case-like syntax.
1925+
19181926
Semantic validation reports unreachable statements that appear later in the same block after `ret`, a valid `break` or `continue`, `fall`, or an `if`/`match` statement whose branches all terminate.
19191927

19201928
---
@@ -2801,12 +2809,16 @@ statement =
28012809
| block_stmt
28022810
| if_stmt
28032811
| match_stmt
2812+
| loop_label loop_stmt
28042813
| for_stmt
28052814
| while_stmt
28062815
| jump_stmt
28072816
| defer_stmt
28082817
| var_decl
28092818
2819+
loop_label = "@" identifier
2820+
loop_stmt = for_stmt | while_stmt
2821+
28102822
jump_stmt =
28112823
| "ret" expr?
28122824
| "break" identifier?

examples/036_control_flow_edges.a7

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ main :: fn() {
1515
io.println("slice total = {}", slice_total)
1616

1717
break_total := 0
18-
outer_break: for value in numbers {
18+
@outer_break for value in numbers {
1919
if value == 7 {
2020
break outer_break
2121
}
2222
break_total += value
2323
}
2424

2525
continue_total := 0
26-
outer_continue: for value in numbers {
26+
@outer_continue for value in numbers {
2727
if value == 7 {
2828
continue outer_continue
2929
}

0 commit comments

Comments
 (0)