diff --git a/.pylintrc b/.pylintrc index 26998d8..44744d8 100644 --- a/.pylintrc +++ b/.pylintrc @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 75ddc05..3a646b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/pyqasm/scope.py b/src/pyqasm/scope.py index 12484ee..e804942 100644 --- a/src/pyqasm/scope.py +++ b/src/pyqasm/scope.py @@ -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 ( @@ -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: diff --git a/tests/qasm3/test_box.py b/tests/qasm3/test_box.py index d8ef0d1..90a8de9 100644 --- a/tests/qasm3/test_box.py +++ b/tests/qasm3/test_box.py @@ -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()