// Licensing scope: see ../../LICENSE and this directory's LICENSE and NOTICE.
// Potentially incorporated Binaryen expression retains Apache-2.0 terms.
// Original Starshine contributions use the scoped MIT grant in ../../LICENSE.
// Modified for Starshine through 2026: implementation and validation use
// Starshine's representations and APIs; this is not an unchanged upstream file.
// Conservative notice retention does not establish copying or independence.
///|
// Expr typechecking is a left-to-right fold over Instruction. Once a prefix
// fails, extending it cannot repair that earlier error. Keep the state only
// within this scan; no cached result survives an instruction or environment edit.
// Return an absolute end, or start when no complete statement is admitted.
fn run_hot_pipeline_raw_simplify_locals_statement_prefix_end(
instrs : Array[@lib.Instruction],
env : @validate.Env,
allow_escape : Bool,
start~ : Int32 = 0,
typed_instructions~ : Array[Int32]? = None,
typed_seed~ : SlStatementTypeSeed? = None,
initialization_slots~ : Array[Int32]? = None,
flat_only~ : Bool = false,
) -> Int32 {
// Balanced local forwarding rejects any completed statement containing
// structured control or an early terminator. Reject that same candidate
// before recursively typechecking it, including unused error rendering.
// Other prefix clients retain the complete structured typecheck path.
if flat_only &&
start >= 0 &&
start < instrs.length() &&
sl_statement_prefix_rejects_control(instrs[start]) {
return start
}
let mut state = match typed_seed {
Some(seed) => seed.fresh_state()
None => {
match initialization_slots {
Some(slots) => slots[0] += env.locals.length()
None => ()
}
@validate.make_state_owned(env, [])
}
}
for index = start; index < instrs.length(); index = index + 1 {
if flat_only && sl_statement_prefix_rejects_control(instrs[index]) {
return start
}
match typed_instructions {
Some(count) => count[0] += 1
None => ()
}
state = match @validate.Typecheck::typecheck(instrs[index], state) {
Ok(next) => next
Err(_) => return start
}
let end = index + 1
let reaches = state.reachable && state.escape == @validate.tc_escape_none()
let boundary = if allow_escape {
state.stack.length() == 0 ||
(end == instrs.length() && reaches && state.stack.length() > 0)
} else {
reaches && state.stack.length() == 0
}
if boundary {
return end
}
}
start
}
///|
fn sl_statement_prefix_rejects_control(instr : @lib.Instruction) -> Bool {
run_hot_pipeline_raw_simplify_locals_instr_is_structured(instr) ||
run_hot_pipeline_raw_simplify_locals_instr_is_early_terminator(instr)
}
///|
// Own only the complete statement; callers do not need a copied remaining tail.
fn sl_statement_at(
instrs : Array[@lib.Instruction],
start : Int32,
env : @validate.Env,
allow_escape~ : Bool = false,
typed_seed~ : SlStatementTypeSeed? = None,
initialization_slots~ : Array[Int32]? = None,
) -> Array[@lib.Instruction] {
let end = run_hot_pipeline_raw_simplify_locals_statement_prefix_end(
instrs,
env,
allow_escape,
start~,
typed_seed~,
initialization_slots~,
)
Array::makei(end - start, fn(index) { instrs[start + index] })
}