Skip to content
Merged
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
16 changes: 16 additions & 0 deletions src/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ pub struct Interpreter {
environment: Rc<RefCell<Environment>>,
functions: HashMap<String, FunctionDef>,
control_flow: ControlFlow,
in_loop_context: bool, // Track whether we're inside a loop
capabilities: crate::security::CapabilityRegistry,
pragmas: PragmaSettings,
worker_defs: HashMap<String, Vec<Statement>>,
Expand All @@ -131,6 +132,7 @@ impl Interpreter {
environment: Rc::new(RefCell::new(Environment::new())),
functions: HashMap::new(),
control_flow: ControlFlow::None,
in_loop_context: false,
capabilities: crate::security::CapabilityRegistry::new(),
pragmas: PragmaSettings::default(),
worker_defs: HashMap::new(),
Expand Down Expand Up @@ -315,6 +317,7 @@ impl Interpreter {
};

let mut result = Value::Unit;
self.in_loop_context = true;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Restore loop context on non-fallthrough loop exits

in_loop_context is set to true before entering the loop, but it is only cleared on the normal fallthrough path; early exits (break, return, or ?-propagated errors) return before cleanup runs. That leaves the interpreter stuck in loop context, so later break/continue statements outside any loop can incorrectly pass validation. This needs guaranteed restoration across all exit paths (e.g., scoped guard or explicit save/restore).

Useful? React with 👍 / 👎.

for _ in 0..iterations {
for stmt in &loop_stmt.body {
result = self.execute_statement(stmt)?;
Expand All @@ -332,11 +335,13 @@ impl Interpreter {
}
}
}
self.in_loop_context = false;
Ok(result)
}

Statement::While(while_loop) => {
let mut result = Value::Unit;
self.in_loop_context = true;
loop {
let condition = self.eval_expr(&while_loop.condition)?;
if !condition.is_truthy() {
Expand All @@ -359,15 +364,26 @@ impl Interpreter {
}
}
}
self.in_loop_context = false;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve outer loop context when exiting nested loops

The new loop-context tracking uses a single boolean and unconditionally writes false when this while finishes, which clobbers an outer loop’s active context. In a nested case like loop { while cond { ... } break; }, the break after the inner while is still inside the outer loop but will now be reported as “outside of loop context.” Please save/restore the previous context (or use a loop-depth counter) instead of forcing false on exit.

Useful? React with 👍 / 👎.

Ok(result)
}

Statement::Break(_) => {
if !self.in_loop_context {
return Err(RuntimeError::new(
"break statement used outside of loop context".to_string()
));
}
self.control_flow = ControlFlow::Break;
Ok(Value::Unit)
}

Statement::Continue(_) => {
if !self.in_loop_context {
return Err(RuntimeError::new(
"continue statement used outside of loop context".to_string()
));
}
self.control_flow = ControlFlow::Continue;
Ok(Value::Unit)
}
Expand Down
Loading