fix(events): a listener's return ends that listener, not the dispatch

Listeners are compiled into one @ev_<E> function and return branched to its exit, so every
listener declared after one that returned early - and every foreign listener - never heard
the event. The per-kind listener shape (return unless it is my kind) answered only the
first-declared kind. examples/events/answer.ludic holds it; seed regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-23 17:42:18 +03:00
parent 45e14dbb99
commit f5ab5cf88a
8 changed files with 11240 additions and 11128 deletions

View file

@ -441,6 +441,7 @@ function ondisable_body(prop: pointer) -> Node {
# Gated on `len(g_events) > 0`, so a program with no events is byte-identical.
var g_events: []Node # each: an N_EVENT node (s = name, kids = payload fields, ival=1 if cancellable)
var g_onlisten: []Node # each: N_BLOCK, s = event name, a = listener body block
var g_ret_label: pointer = "ret" # where `return` goes: the function's exit, or inside an event dispatch the end of the one listener
var g_cancel_addr: pointer = null # EV3: address of the current cancellable dispatch's flag (null outside one)
function register_event(n: Node) -> void { push(g_events, n) }

View file

@ -121,7 +121,7 @@ function emit_return(st: Node) -> void {
let v = emit_expr(st.a)
store_at(llty(ret_ty), coerce_code(v, ret_ty), "%retval")
}
emit(" br label %ret\n")
emit(` br label %{g_ret_label}\n`)
g_term = true
}

View file

@ -652,7 +652,18 @@ function emit_event_fns() -> void {
let base = nloc # listeners share the params but not each other's locals
var i = 0
while i < len(g_onlisten) {
if (g_onlisten[i].s == en) { nloc = base; g_term = false; emit_block(g_onlisten[i].a) }
if (g_onlisten[i].s == en) {
# a `return` ends this listener, not the dispatch: the next one and the foreign ones still run
nloc = base
g_term = false
let next = lbl("evnext")
g_ret_label = next
emit_block(g_onlisten[i].a)
g_ret_label = "ret"
if not g_term { emit(` br label %{next}\n`) }
emit(`{next}:\n`)
g_term = false
}
i += 1
}
# the open half: walk the foreign callback array in registration order

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff