Support classical value bit slicing on int / uint - #408
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 |
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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 |
Implements the OpenQASM 3 classical value bit slicing rules for `int[n]` and `uint[n]`, as reads and as assignment targets, including elements of arrays of integers. Index 0 of an integer is its least-significant bit, the opposite of the `bit[n]` register convention, where bit 0 is the most-significant bit of the stored value. The two conventions are converted at the boundary: `read_int_bits` / `write_int_bits` work in LSB-first integer space and hand back a width-carrying `BitValue`. Three sites changed: - `Qasm3Analyzer.resolve_index` is extracted from `analyze_classical_indices`, so a single subscript can be resolved against a bit width rather than an array dimension. The extraction is behaviour-preserving for the container path; `normalize_index` gains an optional `type_name` so an out-of-range bit index names the declared width: `Index 32 out of range for 'int[32]' variable 'myInt'`. - `Qasm3ExprEvaluator._get_var_value` takes an integer bit-view path before the container path. - `_visit_classical_assignment` detects a bit-slice lvalue up front and bypasses the scalar cast, which is what produced `Cannot cast 'str' to 'IntType'`. A chained subscript is split as element indices plus one trailing bit subscript rather than flattened, so `intArr[0][0] = 1` and `a[0][0][3] = 1` resolve instead of failing with `Invalid index for variable`. A descending range needs an explicit negative step. `myInt[-1:-16]` is an empty index set under the spec's normative range definition, even though the spec's own example assigns it to a `bit[16]`; the ambiguous form is rejected and `myInt[-1:-1:-16]` gives the reversed slice. The `indexing_non_array` fixture moves from `int` to `float`, because `int x; x[0] = 4;` is now a valid lvalue with an out-of-range value. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
f373eac to
aba4c79
Compare
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Closes #395
Problem
Indexing an
int[n]/uint[n]at the bit level was unsupported. Reads failed withInvalid initialization value for variable ..., slice assignment failed with the misleadingCannot cast 'str' to 'IntType', andintArr[0][0]failed withInvalid index for variable.Approach
Qasm3Analyzer.resolve_indexis extracted fromanalyze_classical_indices, so one subscript can be resolved against a bit width instead of an array dimension.normalize_indexgains an optionaltype_name, so an out-of-range bit index reports the declared width:Index 32 out of range for 'int[32]' variable 'myInt'.read_int_bits/write_int_bitswork in LSB-first integer space, sincemyInt[0]is the least-significant bit — the opposite of thebit[n]convention, where bit 0 is the most-significant bit of the stored value. Conversion happens at the boundary, and a slice read returns a width-carryingBitValue.intArr[0][0]anda[0][0][3]resolve._visit_classical_assignmentdetects a bit-slice lvalue up front and bypasses the scalar cast that produced thestr-to-IntTypeerror.int[n]re-reads as negative (int[4] x = 0; x[3] = 1;gives-8).uint[n]stays unsigned.Reversed slice:
myInt[-1:-16]is rejectedThe spec is self-inconsistent here. Its normative range rule says
a:bimplies step 1 and the set is{a, a+c, ...}witha+mc <= b, so31:16is empty — and "a register cannot be indexed by an empty index set". Yet the same section's example assignsmyInt[-1:-16]to abit[16].pyqasm follows the normative rule and rejects the ambiguous form, which also keeps descending ranges consistent with every other slice in the codebase.
myInt[-1:-1:-16]is well-defined by the same formula and gives the reversed slice. Both are covered by a test.Tested
tests/qasm3/test_expressions.py: the spec example end to end (1, 0, 0, 3, 0xAF), single-bit read, stepped slice read, slice assignment, signed wrap on a top-bit write, 1-D and 2-D array-element bit access,uintvariants, both reversed-slice forms, out-of-range index naming the width, and RHS width mismatch. Also a regression guard for a descendingbit[n]slice, which dropped its final position before this change.840 passed, 3 skipped.pylint10.00/10;isort,black, andmypyclean apart from the pre-existingtabulatestub error.The
indexing_non_arrayfixture moves frominttofloat:int x; x[0] = 4;is now a valid lvalue with an out-of-range value, so it no longer exercises that path.Out of scope
angle[n]; not implemented here.myInt[{0,2}](aDiscreteSetsubscript) still raisesTypeError: 'DiscreteSet' object is not subscriptable, unchanged frommain.qasm_variable_type_castkeeps the RHS width when aBitValueinitializes abit[n], sobit[8] w = <16-bit slice>is accepted silently. Left for a reviewer call on where that belongs.Stacked on #404 (
feature-385-391-bit-repr-negative-indices); rebases ontomainonce #404 merges.