Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,6 @@ recursive=no
# source root.
source-roots=

# When enabled, pylint would attempt to guess common misconfiguration and emit
# user-friendly hints instead of false-positive error messages.
suggestion-mode=yes

# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
unsafe-load-any-extension=no
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Types of changes:
### Removed

### Fixed
- Fixed classical register declarations not being visible inside `box` scope, causing "Missing clbit register declaration" errors for measurement statements inside box blocks. ([#306](https://github.com/qBraid/pyqasm/pull/306))

### Dependencies

Expand Down
9 changes: 2 additions & 7 deletions src/pyqasm/scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,12 +193,7 @@ def get_from_visible_scope(self, var_name: str) -> Variable | None:
curr_scope = self.get_curr_scope()
if self.in_global_scope():
return global_scope.get(var_name, None)
if (
self.in_function_scope()
or self.in_gate_scope()
or self.in_box_scope()
or self.in_pulse_scope()
):
if self.in_function_scope() or self.in_gate_scope() or self.in_pulse_scope():
if var_name in curr_scope:
return curr_scope[var_name]
if var_name in global_scope and (
Expand All @@ -207,7 +202,7 @@ def get_from_visible_scope(self, var_name: str) -> Variable | None:
# we also need to return the variable if it is a constant or qubit
# in the global scope, as it can be used in the function or gate
return global_scope[var_name]
if self.in_block_scope():
if self.in_block_scope() or self.in_box_scope():
var_found = None
for scope, context in zip(reversed(self._scope), reversed(self._context)):
if context != Context.BLOCK:
Expand Down
50 changes: 50 additions & 0 deletions tests/qasm3/test_box.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,53 @@ def test_box_statement_error(qasm_code, error_message, error_span, caplog):
loads(qasm_code).unroll()
assert error_message in str(err.value)
assert error_span in caplog.text


def test_box_measurement_with_classical_register():
"""Measurement inside a box should have access to classical registers
declared in the enclosing (global) scope.

Regression: box scope was treated like function/gate scope, restricting
access to only constants and qubits from global scope. Classical registers
were invisible, causing 'Missing clbit register declaration' errors.
See: https://github.com/qBraid/pyqasm/issues/302
"""
qasm_str = """
OPENQASM 3.0;
include "stdgates.inc";
qubit[4] q;
bit[3] c;

box [100ns] {
h q[0];
c[1] = measure q[1];
}
"""
result = loads(qasm_str)
result.validate()


def test_box_measurement_with_enclosing_block_scope():
"""Measurement inside a box nested within a non-global scope (e.g. an
if-block) should still have access to classical registers declared in
the enclosing global scope.

Complements test_box_measurement_with_classical_register by ensuring the
scope walker traverses intermediate BLOCK contexts to reach the global
scope where the classical register is declared.
"""
qasm_str = """
OPENQASM 3.0;
include "stdgates.inc";
qubit[4] q;
bit[3] c;

if (true) {
box {
h q[0];
c[1] = measure q[1];
}
}
"""
result = loads(qasm_str)
result.validate()
Loading