Give bit[n] a width-carrying int representation; normalize negative indices - #404
Give bit[n] a width-carrying int representation; normalize negative indices#404TheGupta2012 wants to merge 1 commit into
Conversation
Argus reviewAuto-review is off for this repo. Tick the box below to run a review on this PR.
Estimated cost
Tip: you can also comment |
|
Warning Review limit reachedNext included review available in 59 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (12)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
11f8de3 to
f65532d
Compare
…e indices Fixes #385 and #391 in one branch because both rewrite the same index-resolution path. #385: `bit[n]` values were stored inconsistently as `str` (from bitstring literals) or `np.ndarray` (uninitialized), so every bitwise, shift, or index op reached a Python operator that `str` cannot handle and escaped the public API as a raw `TypeError`. The internal representation is now a `BitValue` — an `int` subclass carrying the register width — with shared `bits_to_int` / `int_to_bits` helpers in `pyqasm.analyzer`. `qasm3_expression_op_map` recognizes `BitValue` operands, enforces equal-width for `|`, `&`, `^`, re-masks `~` / shift / binary results to the declared width, and raises `ValidationError` for width-mismatched bitwise ops (the evaluator attaches the source span so the error is properly located). `b[i]` returns a single-bit `int`; `b[a:c]` returns a `BitValue` of the sliced width. Indexed writes (`b[i] = ...`, `b[-1] = ...`) rebuild the integer via a shared `_write_bit_slice` helper. The serialized AST is unchanged: `bit[4] a = "1010";` still round-trips through `dumps()`. #391: Added `Qasm3Analyzer.normalize_index`, applied at every index-resolution site (arrays incl. multi-dim and assignment targets, qubit registers, classical registers, `bit[n]`, `let` aliases, branch conditions, and the transformer's range-expansion helpers). `validate_register_index` now returns the normalized index so callers rewrite the emitted `IntegerLiteral`; downstream passes (`remove_idle_qubits`, `reverse_qubit_order`, and the register consolidator) only see concrete non-negative indices. An index still outside `[-size, size)` after normalization raises the existing out-of-range error and reports the index **as written in the source**. Range endpoints normalize per-endpoint and keep each function's existing convention: qubit ranges stay end-exclusive (matching Python slice semantics), classical array ranges stay end-inclusive. Two deliberate behavior changes fall out of the new representation: - `test_extern_function_call` expected output changed. A `bit[2] b1 = true` extern arg now serializes as `"01"` (the canonical bitstring for the register's value) rather than leaking Python `True`. The test's expected output was the pre-existing bug. - Oversized int inits to a `bit[n]` (e.g. `bit[4] c = 999`) now mask to width. Previously the raw value was stored uncapped; casts through `qasm_variable_type_cast` now go through `BitValue`. Test suite: 795 passed, 3 skipped (all pre-existing). Two CLI tests (`test_validate_qasm_with_invalid_file`, `test_validate_command_with_invalid_file`) fail on this branch and equally on `main` — pre-existing terminal-width truncation in Rich console output, unrelated to this change. Ran `black`, `isort`, `pylint`, `mypy` directly rather than through `tox` because `tox` would `pip install` into the shared environment. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
f65532d to
cc80ec7
Compare
Fixes #385
Fixes #391
Combined because both rewrite the same index-resolution path.
#385 —
bit[n]operatorsA
bit[n]value was stored as a Pythonstr, so every bitwise, shift and index operation reached an operatorstrdoes not implement and escaped a rawTypeErrorthrough the public API.BitValue(anintsubclass carrying an explicitwidth) is now the internal representation; the"1010"string form is produced only at serialisation. Bit 0 is the most-significant bit, matching the existingangle_bit_stringconvention.|,&,^,~,<<,>>evaluate and re-mask tonbits,b[i]andb[a:c]read, and operands of unequal width raise aValidationError.Shared helpers
bits_to_int/int_to_bitskeep the conversion in one place.#391 — negative indices
Qasm3Analyzer.normalize_indexresolves-1to the last element and is applied at every index-resolution site, so behaviour cannot drift between paths.Qasm3Validator.validate_register_indexnow returns the normalized index, which callers write back into the AST — passes downstream of unroll expect concrete non-negative integers.Covers arrays (including multi-dimensional and assignment targets),
bit[n],qubit[n],letaliases, and both endpoints of a range. An index still out of bounds after normalization raises, reporting the index as written.Tests
tests/qasm3/test_expressions.py— each operator with its expected value and width, width-mismatch errors, single-bit and ranged reads, and adumps()round-trip. Newtests/qasm3/test_negative_indices.py— negative reads, writes, ranges, stepped ranges, and an out-of-range negative that must still error.One pre-existing test changed:
test_extern_function_callasserted a bool-initializedbit[2]leaking PythonTrueinto the serialized extern-arg AST; it now emits the correct"01".828 passed, 3 skipped.
pylint10.00/10,black+isortclean.🤖 Generated with Claude Code