Skip to content

Commit e570222

Browse files
authored
Break on fail during debugging (#2400)
This change updates the behavior of `fail` expressions during debugging such that they trigger a new kind of `StepResultId` that propagates up into the extension and will cause a debug session to stop just before the failure is processed. https://github.com/user-attachments/assets/30d11954-d9b4-460d-8a7a-ef5dfdb1b2f6
1 parent 9791237 commit e570222

File tree

5 files changed

+34
-2
lines changed

5 files changed

+34
-2
lines changed

compiler/qsc_eval/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,7 @@ pub enum StepResult {
344344
StepIn,
345345
StepOut,
346346
Return(Value),
347+
Fail,
347348
}
348349

349350
trait AsIndex {
@@ -692,6 +693,10 @@ impl State {
692693
None => continue,
693694
}
694695
}
696+
Some(ExecGraphNode::Fail) => {
697+
self.idx += 1;
698+
return Ok(StepResult::Fail);
699+
}
695700
Some(ExecGraphNode::Jump(idx)) => {
696701
self.idx = *idx;
697702
continue;

compiler/qsc_fir/src/fir.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,8 @@ pub enum ExecGraphNode {
929929
PushScope,
930930
/// A pop of the current scope, used when tracking variables for debugging.
931931
PopScope,
932+
/// A failure node, inserted just before a `fail` expression to halt execution for debugging.
933+
Fail,
932934
}
933935

934936
/// A sequenced block of statements.

compiler/qsc_lowerer/src/lib.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,15 @@ impl Lowerer {
557557
let arg = self.lower_expr(arg);
558558
fir::ExprKind::Call(call, arg)
559559
}
560-
hir::ExprKind::Fail(message) => fir::ExprKind::Fail(self.lower_expr(message)),
560+
hir::ExprKind::Fail(message) => {
561+
// Ensure the right-hand side expression is lowered first so that it
562+
// is executed before the fail node, if any.
563+
let fail = fir::ExprKind::Fail(self.lower_expr(message));
564+
if self.enable_debug {
565+
self.exec_graph.push(ExecGraphNode::Fail);
566+
}
567+
fail
568+
}
561569
hir::ExprKind::Field(container, field) => {
562570
let container = self.lower_expr(container);
563571
let field = lower_field(field);

vscode/src/debugger/session.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,9 @@ export class QscDebugSession extends LoggingDebugSession {
336336
this.sendEvent(evt);
337337
} else if (result.id == StepResultId.Return) {
338338
await this.endSession(`ending session`, 0);
339+
} else if (result.id == StepResultId.Fail) {
340+
log.trace(`step result: ${result.id} ${result.value}`);
341+
this.sendEvent(new StoppedEvent("exception", QscDebugSession.threadID));
339342
} else {
340343
log.trace(`step result: ${result.id} ${result.value}`);
341344
this.sendEvent(new StoppedEvent("step", QscDebugSession.threadID));
@@ -387,13 +390,22 @@ export class QscDebugSession extends LoggingDebugSession {
387390
// supports shots.
388391
for (let i = 0; i < args.shots; i++) {
389392
try {
390-
const result = await this.debugService.evalContinue(
393+
let result = await this.debugService.evalContinue(
391394
bps,
392395
this.eventTarget,
393396
);
394397

398+
// Ensure the circuit is updated before checking for fail, since the
399+
// right-hand side of the fail expression may itself be a block that includes
400+
// gate calls.
395401
await this.updateCircuit();
396402

403+
if (result.id == StepResultId.Fail) {
404+
// This was a runtime failure in the program, so we need to continue to
405+
// trigger the actual failure event.
406+
result = await this.debugService.evalContinue(bps, this.eventTarget);
407+
}
408+
397409
if (result.id != StepResultId.Return) {
398410
await this.endSession(`execution didn't run to completion`, -1);
399411
return;

wasm/src/debug_service.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,10 @@ impl From<StepResult> for StructStepResult {
281281
id: StepResultId::Return.into(),
282282
value: 0,
283283
},
284+
StepResult::Fail => StructStepResult {
285+
id: StepResultId::Fail.into(),
286+
value: 0,
287+
},
284288
}
285289
}
286290
}
@@ -293,6 +297,7 @@ pub enum StepResultId {
293297
StepIn = 2,
294298
StepOut = 3,
295299
Return = 4,
300+
Fail = 5,
296301
}
297302

298303
impl From<StepResultId> for usize {

0 commit comments

Comments
 (0)