(vec-new dyn) is not a (Vec dyn). At milestone 1 the heterogeneous container is the dyn runtime's own object and its type is dyn like everything else the runtime hands back, which is what lets push, at and len on it be the dyn operations instead of a type-erased Vec over eight-byte elements. It takes no allocator, and the refusal says why: the storage has to be storage the collector already knows about, where a Flan Vec's block would hold roots inside memory the collector does not own. len answers an i32 and at answers a dyn. The asymmetry is deliberate -- a length is what an index loop compares against, and handing back a boxed number would make (< i (len xs)) a dyn comparison and two allocations an iteration. The operand-order bug, which the first test could not see because both its operands were dyn: (+ n x) over a typed n and a dyn x threaded i64 into the second check, expect did what an annotation site had asked for and unboxed, and the result was a machine add of a value the runtime was never asked about -- the program trapping on a float instead of promoting it, with nothing in the source to say why. (+ x n) boxed correctly, so it was visible in one operand order only. binary now takes dyn_ok from the operators that have a dyn lowering and checks both operands on their own terms, which is safe exactly when neither needs an expectation to check -- a literal still takes the other's type, and a keyword still gets one, since :lo has no meaning without it. Cast had no bool arms, so the bool boundary failed to emit; reachability hid it, because the program that used it dropped the function. dyn does not cross to C: it is one word and would have passed as an integer, and C has no way to ask what the word means. A condition may not carry one either, nor hold one in a field -- a payload crosses a handler boundary and has to stay rooted across the transfer, which is the collector's question and milestone 2's.
81 KiB
Executable File
81 KiB
Executable File