Skip to content
Merged
Show file tree
Hide file tree
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
5 changes: 5 additions & 0 deletions src/eigenscript.h
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,11 @@ void env_dump_unlock(struct Env *e);
* (called from observer_slot_reset so a torn-down env can't be read stale). */
void vm_obs_slot_dropped(struct Env *e);
int observer_slot_converged(const struct ObserverSlot *s);
/* Classify a binding's observer slot as the VM's PREDICATE opcodes do (opaque
* band, query view, kind dispatch). Exported for the AOT runtime, which must
* share this implementation rather than copy it (ouroboros#119/#122).
* require_used: 0 mirrors the bare op, 1 the named ops. */
int observer_predicate_at(struct Env *e, int idx, int kind, int require_used);
int observer_slot_equilibrium(const struct ObserverSlot *s);
int observer_slot_improving(const struct ObserverSlot *s);
int observer_slot_diverging(const struct ObserverSlot *s);
Expand Down
57 changes: 35 additions & 22 deletions src/vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,34 @@ static int vm_slot_value_opaque(Env *e, int idx) {
return r;
}

/* Classify a binding's observer slot exactly as the PREDICATE opcodes do:
* the #708 opaque band, the #711 query view, and the kind dispatch, in one
* place. EXPORTED (eigenscript.h) because the AOT compiler's runtime needs
* the same answer and had been carrying a partial COPY of this logic —
* ouroboros#119/#122, where a bare predicate inside `unobserved:` returned a
* confident `1` from generated code while the VM raised. "The VM is the
* byte-exact oracle" only means something if the compiler CALLS the oracle
* instead of reimplementing a subset of it.
*
* `require_used` is the one place the three opcodes genuinely differ: the
* bare op does not test `used`, the named ops do. That is preserved here
* rather than normalised — this commit is a refactor, and changing which
* slots answer is a separate decision with its own evidence. env_obs_slot
* bounds-checks but does NOT test `used` (its contract covers range only),
* so the distinction is load-bearing on an in-range never-assigned slot.
*
* The #871 unobserved-depth raise stays in the opcode handlers: it needs the
* source line, and a binding assigned inside the block has no `used` slot at
* all, so a classifier-level check is never reached on the path that hangs. */
int observer_predicate_at(Env *e, int idx, int kind, int require_used) {
if (vm_slot_value_opaque(e, idx)) return 0;
const ObserverSlot *s = env_obs_slot(e, idx);
if (!s) return 0;
if (require_used && !s->used) return 0;
ObserverSlot q = vm_slot_query_view(e, idx, s);
return vm_slot_predicate(&q, (uint16_t)kind);
}

/* Phase 5: VM execution state (g_vm), loop-stall accounting
* (g_loop_stall_count, g_loop_iterations, g_loop_exit_reason),
* control-flow / error-state globals (g_return_val, g_returning,
Expand Down Expand Up @@ -5456,16 +5484,11 @@ static Value *vm_run_ex(EigsChunk *chunk, Env *env, Task *resume,
vm_push_slot(slot_null());
DISPATCH();
}
int result = 0;
const ObserverSlot *s =
vm_slot_value_opaque(g_last_obs_slot_env, g_last_obs_slot_idx)
? NULL /* #708: fn/builtin binding — nothing claimable */
: env_obs_slot(g_last_obs_slot_env, g_last_obs_slot_idx);
if (s) {
ObserverSlot q = vm_slot_query_view(g_last_obs_slot_env,
g_last_obs_slot_idx, s); /* #711 */
result = vm_slot_predicate(&q, kind);
}
/* #708 opaque band + #711 query view + kind dispatch, shared with the
* named ops and with the AOT runtime. require_used=0: the bare op has
* never tested `used`, unlike PREDICATE_SLOT/NAME below. */
int result = observer_predicate_at(g_last_obs_slot_env,
g_last_obs_slot_idx, kind, 0);
vm_push(make_num(result ? 1.0 : 0.0));
DISPATCH();
}
Expand All @@ -5481,12 +5504,7 @@ static Value *vm_run_ex(EigsChunk *chunk, Env *env, Task *resume,
DISPATCH();
}
Env *e = frame->fn_env;
int result = 0;
const ObserverSlot *ps_l = env_obs_slot(e, (int)slot);
if (ps_l && ps_l->used && !vm_slot_value_opaque(e, (int)slot)) {
ObserverSlot q = vm_slot_query_view(e, (int)slot, ps_l); /* #711 */
result = vm_slot_predicate(&q, kind); /* #708 */
}
int result = observer_predicate_at(e, (int)slot, kind, 1);
vm_push(make_num(result ? 1.0 : 0.0));
DISPATCH();
}
Expand All @@ -5510,12 +5528,7 @@ static Value *vm_run_ex(EigsChunk *chunk, Env *env, Task *resume,
vm_push_slot(slot_null());
DISPATCH();
}
int result = 0;
const ObserverSlot *ps_n = env_obs_slot(oe, oidx);
if (ps_n && ps_n->used && !vm_slot_value_opaque(oe, oidx)) {
ObserverSlot q = vm_slot_query_view(oe, oidx, ps_n); /* #711 */
result = vm_slot_predicate(&q, kind); /* #708 */
}
int result = observer_predicate_at(oe, oidx, kind, 1);
vm_push(make_num(result ? 1.0 : 0.0));
DISPATCH();
}
Expand Down
Loading