The dyn if truthiness review turned up that or's answer position, unlike
and's, still traps on a non-bool dyn value: or's short-circuit sentinel
sat in the then arm of its own if, the one check_if types first, so that
sentinel decided the whole expression's type and a later non-bool dyn
answer hit the strict bool boundary and unboxed itself into a trap
rather than surviving as itself. (or nil "x") — the canonical Clojure
(or x default) idiom — crashed instead of answering "x", identical on
all three backends.
or now binds its test to a temp and answers the temp itself, exactly the
way Clojure's own or macro expands: (or a b) becomes (let [t a] (if t t
b)), not (if a true b). The temp evaluates a once and lets the answer be
a without writing it a second time as the then arm; it is the temp's own
type check_if sees first, so or hands back the actual truthy operand the
same way and always has. Verified real output, unchanged, on LLVM, -O0
and --x86, and the survey program now exercises the case its own header
used to exclude for being unsafe: a non-bool value stopping or and being
handed back as-is.
check_truthy also gets three corrections a closer look found. Its own
[loc] used to come from the enclosing if/while/not rather than from the
condition itself, so the rt call and cast it builds carried the wrong
column in an --x86 disassembly or the dev inspector whenever the
condition was not the form's first token; it now takes loc from the
scrutinee's own AST node, confirmed against a real --x86 dump. A comment
now names the precondition its exception-swallowing retry rests on: none
of check.ml's save-restore sites (barrier, in_frames, in_defer, loops,
scope) are exception-safe, which is harmless only because the retry
always either succeeds cleanly or re-raises and aborts the compile
before ctx is read again — and would stop being harmless the day some
want-sensitive elaboration on this path could succeed differently on
retry. And a bare keyword condition, which used to be checked with
want:Bool from the start and refused by the keyword arm's enum-or-refuse
case, now resolves as the dyn keyword instead and is unconditionally
truthy — a deliberate loss of that diagnostic, the author's call, pinned
in test_flan.ml so it does not regress by accident.
The two typed-refusal messages captured before this pass (a float
literal condition, an i32 while condition) are unchanged, checked again
against the same baseline. test_flan.ml's parser test for or's shape is
updated to match the new let-bound desugaring.