spec-memory.md says ownership is structural: a struct containing a Vec is itself move-only, free recurses into owning fields, and a field cannot be freed on its own. None of that machinery exists — it is the recursive teardown drop brings — and the move rule as written covered only the types Vec appears in directly. Three ways past it, each of which hands out a second owner of one buffer: A struct field of Vec type. The struct copies its header on assignment and nothing records a move. A global of Vec type. The dead set is per function, so two functions each freeing it is a double free nothing could see, and a global read does not go through the move path at all — even the one-function case was accepted. Half a rule is worse than none, so the type is refused where it is declared. A global Allocator is not this and stays legal: an allocator is a copyable handle, and it is what makes a handler that owns the arena expressible. A Vec of a Vec. The runtime is type-erased and copies elements bytewise, so clone would duplicate inner headers rather than copying what they own and free would drop their buffers. Shipping the shallow answer under the deep name was the alternative. All three name drop as what they wait on. Also: match arms shared one dead set, so `(match o (Some k) (free v) None (free v))` reported the second arm as a use after the first arm's move — a legal program refused, the same case that was already fixed for `if`. Arms are alternatives, so each starts from the state before the match and the union survives the join. And a Vec reaching declare-c now says what to pass instead. It was already refused, by the shim generator's catch-all for a type it does not know; the reason it is refused is that handing a header that owns storage to C hands out an owner, and that is worth saying at the declaration.
9 lines
444 B
Plaintext
9 lines
444 B
Plaintext
;;;; A Vec cannot cross to C. The shim flattens a struct that crosses, and a
|
|
;;;; Vec is not a struct anyone should flatten: it owns storage, and handing
|
|
;;;; its header to C hands out an owner. It falls to the same aggregate
|
|
;;;; refusal every other non-scalar declare-c parameter gets, which is the
|
|
;;;; point — nothing special was needed and nothing special was added.
|
|
(declare-c vec-sum [v (Vec i32)] i32 "vec_sum")
|
|
|
|
(defn main [] i32 0)
|