Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

`break` / `continue`: escapes as `BreakSignal` from a `for` loop, silently drops the iteration body in a `while` loop

Đang mở
#386 0 bình luận 0 reaction 1 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

@TheGupta2012 đang làm issue này rồi.

Từ ngày 21/8/2026.

  • #402 của @TheGupta2012 — đang mở

Đánh giá

Issue này chưa được đánh giá.

Mô tả

bug error-handling llm-assisted qasm3

Limitation

Breaking and continuing loops
defines break and continue inside both for and while bodies. pyqasm implements them
as internal control-flow exceptions, and both loop visitors mishandle them — in two different
ways, from one shared root cause.

  • for loops: no handler at all. The signal propagates out of validate() / unroll()
    to the caller as BreakSignal / ContinueSignal — internal types that are not
    ValidationError and are not part of the public API.
  • while loops: a handler exists, but it discards every statement of the iteration in
    which the break occurred. The gates executed before the break vanish from the unrolled
    program with no error raised.

The second is the more dangerous of the two: it is silent wrong output.

Example QASM failure

for — internal signal escapes
import pyqasm

m = pyqasm.loads('''
OPENQASM 3.0;
for int i in [0:3] {
    break;
}
''')
m.validate()
pyqasm.exceptions.BreakSignal: None

Same for continue, and for the common nested-in-a-branch form:

OPENQASM 3.0;
for int i in [0:3] { continue; }              // ContinueSignal: None
OPENQASM 3.0;
for int i in [0:3] { if (i == 1) { break; } }   // BreakSignal: None
while — silent loss of the iteration body
OPENQASM 3.0;
include "stdgates.inc";
qubit[1] q;
int[8] i = 0;
while (i < 3) { h q[0]; i += 1; break; }
>>> m = pyqasm.loads(qasm); m.unroll(); print(pyqasm.dumps(m))
OPENQASM 3.0;
include "stdgates.inc";
qubit[1] q;

The h q[0] executed before the break is gone, and depth() reports 0. Expected output is
a single h q[0].

The same while loop without the break unrolls correctly to three h q[0], which
confirms the loss is caused by the signal handling and not by while-loop unrolling in general.

Change Requested

  1. for loop unrolling must catch both signals. continue abandons the rest of the current
    iteration and proceeds to the next value; break abandons the rest of the current
    iteration and stops iterating.
  2. Both loops must retain the statements already emitted in the interrupted iteration.
    Only the statements after the break / continue are skipped.
  3. Neither signal type may be observable from validate(), unroll(), or any other public
    entry point.

Implementation Details

  • _visit_while_loop (src/pyqasm/visitor.py:2700) has the handler at visitor.py:2739:

    try:
        result.extend(self.visit_basic_block(statement.block))
    except LoopControlSignal as lcs:
        ...
    

    visit_basic_block raises from inside the call, so it never returns and result.extend
    never runs — the whole iteration's output is discarded. That is the silent-drop bug.

  • _visit_forin_loop (src/pyqasm/visitor.py:2432) has no try/except around its
    result.extend(self.visit_basic_block(...)) calls at visitor.py:2500, so the signal
    escapes.

  • The shared fix is in visit_basic_block, not in either loop. It must be able to hand
    back the statements it produced before the signal was raised. Two workable shapes:
    attach the partial result to the signal object (lcs.partial_result), or have
    visit_basic_block return (statements, signal_or_None) instead of raising. The second is
    cleaner but touches every caller.

  • LoopControlSignal already exists as a common base in src/pyqasm/exceptions.py:59, so a
    single except clause covers both signals — no new exception types are needed.

  • Minor, in the same file: ContinueSignal.__init__ ignores its msg argument and always
    passes the literal "continue" to super(), and neither signal calls Exception.__init__
    with a message — which is why the escaping error prints as BreakSignal: None. Worth
    tidying while here.

  • Check the same handling inside a switch case body, which is another block-scoped
    statement list that can legally contain break.

  • Tests: tests/qasm3/test_loop.py — break/continue directly in a for body, nested one
    and two if levels deep, inside a nested for, the while+break case above asserting the
    pre-break gate survives, and an assertion that the unrolled gate sequence matches the
    truncated iteration count.

Ngôn ngữ chính
Python
Star
29
Fork
33
Merge trung bình
2 ngày 4 giờ
Pull request đã merge (30 ngày)
15

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của qBraid/pyqasm

Tất cả issue của qBraid/pyqasm

Issue tương tự

Thêm issue về Python

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.