Review found it, and it is a silent miscompile on both backends rather than a refusal anywhere. mangle_ty flattens a whole signature into one hyphen-joined string, so (CFn [(Ptr i32)] i32) and (CFn [ptr i32] i32) -- the second over a struct someone called ptr -- flatten alike; keyed on that, the second widening reused the first's thunk at the wrong arity. The key is the types now, compared with Types.equal, and the symbol is a counter over the thunks already minted, so nothing is derived from a spelling. fn-thunk-share.flan is the pair, and it prints 5 and 17. And a regression beside it: a generic whose function parameter binds the type variable. (defn apply2 [f (Fn [$t] $t) x $t] ...) called as (apply2 bump 1) compiled before this lane and stopped, because bind_ty had no arm admitting a CFn argument at an Fn pattern -- and once it had one, the call still handed one word to an instance declaring two, because a parameter that still mentions a variable is checked with no expectation and expect never sees the pair. Both halves: the arm, and the widening in generic_call's catch-up pass beside the numeric one. The same gap hid new functionality -- a CFn argument at a (CFn [$t] $t) parameter had no arm either -- and mentions had no CFn case, so bound_exactly answered wrong for a variable living only inside one. The corpus missed all of it because the prelude binds $t from an earlier argument, so the parameter is concrete before bind_ty sees it. While here: escaping's enumeration is the *clean* set now rather than the suspect set. It had a hole where a list like that cannot -- an (at s 0) over a slice of Fn read as clean while the Vec, struct and pointer spellings were refused. Unreachable today, and the header claims the list is closed. The same inversion fixes which refusal message an index read gets. Three minors: Anull was defined and never constructed; the capture-dyn message substituted a descriptor into a noun slot; and session.ml rendered a defn's changed signature as (Fn [...] ...), which is now a real type and not the same as (CFn [...] ...) -- it writes the parameters and the return the way a defn writes them. And one rounding corrected in the docs: handler-bind is not free for a program that captures nothing. %handler grew from 24 bytes to 32, every push writes a null into the new field, every clause gains ptr %env with an alloca and a store, and flan_signal passes one more argument per dispatch -- twenty changed x86 lines on loops.flan. Small, real, and paid by every conditions program.
34 lines
1.5 KiB
Plaintext
34 lines
1.5 KiB
Plaintext
;; A generic whose function parameter binds the type variable, which is the
|
|
;; shape a name now has to reach: a defn's address carries (CFn [i32] i32),
|
|
;; and (apply2 bump 1) has to bind $t from it and then widen the argument.
|
|
;;
|
|
;; Both halves are here because they fail apart. The binding is Check's
|
|
;; bind_ty, which runs inside generic_call and decides the instantiation; the
|
|
;; widening is the catch-up pass at the end of the same function, because a
|
|
;; parameter that still mentioned a variable was checked with no expectation
|
|
;; at all and expect never saw the pair. Get the first without the second and
|
|
;; the call site hands one word to an instance that declares two.
|
|
;;
|
|
;; The prelude does not cover this and that is worth saying: its higher-order
|
|
;; functions bind $t from an *earlier* argument, so the parameter is already
|
|
;; concrete by the time the function value is reached, and (map-in-place s
|
|
;; double) never walks this path at all.
|
|
;;
|
|
;; The CFn half is new rather than restored: a matching bare address against
|
|
;; a (CFn [$t] $t) parameter had no arm either, and fell through to plain
|
|
;; equality.
|
|
|
|
(defn apply2 [f (Fn [$t] $t) x $t] $t (f (f x)))
|
|
(defn applyc [f (CFn [$t] $t) x $t] $t (f (f x)))
|
|
|
|
(defn bump [n i32] i32 (+ n 1))
|
|
(defn twice [x f64] f64 (* x 2.0))
|
|
|
|
(defn main [] i32
|
|
(println (apply2 bump 1))
|
|
(println (applyc bump 1))
|
|
;; A second instantiation, so the two copies are really two and the thunk
|
|
;; the first one minted is not reused at the wrong signature.
|
|
(println (apply2 twice 1.5))
|
|
0)
|