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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Types of changes:
### Removed

### Fixed
- Fixed a `duration` initialized from arithmetic on a `stretch` operand, such as `duration b = a + s;` or `2 * s`, raising a bare `TypeError: unsupported operand type(s) for +: 'float' and 'NoneType'`. A `stretch` has no value until scheduling, so it now raises a `ValidationError` naming the variable ([#417](https://github.com/qBraid/pyqasm/pull/417), [#389](https://github.com/qBraid/pyqasm/issues/389)).
- Decompose `p`, `phaseshift`, and `u1` (`U1`) gates into a single `rz(theta)` rotation instead of `h; rx(theta); h` ([#470](https://github.com/qBraid/pyqasm/pull/470), [#469](https://github.com/qBraid/pyqasm/issues/469)).
- Fixed `rebase` failing on `ccx`, `id`, and the `p` gates produced by unrolling `c3x` for the `ROTATIONAL_CX` / `CLIFFORD_T` basis sets ([#468](https://github.com/qBraid/pyqasm/pull/468), [#467](https://github.com/qBraid/pyqasm/issues/467)).
- `loads()` syntax errors now name the offending token, line and column, e.g. `Failed to parse OpenQASM string: syntax error near ';' at line 3, column 13`. Before, the reason after the colon was empty ([#460](https://github.com/qBraid/pyqasm/pull/460)).
Expand Down
13 changes: 13 additions & 0 deletions src/pyqasm/expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,19 @@ def _get_external_function_return_type(expression):
return (None, [])

statements.extend(rhs_statements)
for operand in (expression.lhs, expression.rhs):
if not isinstance(operand, Identifier):
continue
assert cls.visitor_obj
var = cls.visitor_obj._scope_manager.get_from_visible_scope(operand.name)
# const stretch declarations carry a value, so only reject unresolved ones
if Qasm3Validator.validate_variable_type(var, StretchType) and var.value is None:

@TheGupta2012 TheGupta2012 Nov 3, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should've caught this already but I'm only realizing it now - can we rather just update the _check_var_initialized method?

It already has a branch for the stretch type, so we can add this change there. I think this will be even cleaner than adding this single stretch type handling in the BinaryExpression branch

raise_qasm3_error(
f"Arithmetic on 'stretch' operand '{operand.name}' is not yet supported",
err_type=ValidationError,
error_node=expression,
span=expression.span,
)
return _check_and_return_value(
qasm3_expression_op_map(expression.op.name, lhs_value, rhs_value)
)
Expand Down
58 changes: 58 additions & 0 deletions tests/qasm3/declarations/test_classical.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,64 @@ def test_stretch_type_error(qasm_code, error_message, error_span, caplog):
assert error_span in caplog.text


@pytest.mark.parametrize(
"qasm_code,stretch_var,error_span",
[
(
"""
OPENQASM 3.0;
include "stdgates.inc";
stretch s;
duration a = 300ns;
duration b = a + s;
""",
"s",
r"Error at line 6, column 25",
),
(
"""
OPENQASM 3.0;
include "stdgates.inc";
stretch s;
duration b = 2 * s;
""",
"s",
r"Error at line 5, column 25",
),
(
"""
OPENQASM 3.0;
include "stdgates.inc";
stretch s;
duration a = 300ns;
duration b = a + 2 * s;
""",
"s",
r"Error at line 6, column 29",
),
],
)
def test_stretch_arithmetic_not_supported(qasm_code, stretch_var, error_span, caplog):
with pytest.raises(ValidationError) as err:
with caplog.at_level("ERROR"):
loads(qasm_code).validate()
cause = err.value.__cause__
assert cause is not None
assert f"Arithmetic on 'stretch' operand '{stretch_var}' is not yet supported" in str(cause)
assert error_span in caplog.text


def test_const_stretch_arithmetic():
qasm3_string = """
OPENQASM 3.0;
include "stdgates.inc";
const stretch s = 100ns;
duration a = 300ns;
duration b = a + 2 * s;
"""
loads(qasm3_string).validate()


@pytest.mark.parametrize(
"qasm_code,error_message,error_span",
[
Expand Down
Loading