✓
Passing This code compiles and runs correctly.
Code
// IMPLEMENTED — the continuation must not depend on pointer identity.
//
// `advance` allocates a NEW handle and destroys the old, yet the caller here is
// identical to 336_007: `h` continues, no rebind. The caller observes only the
// base type (still a Handle) and the phantom state (now <active!>); whether the
// implementation re-tagged the old pointer or minted a fresh one is invisible,
// and therefore not something the caller should have to encode.
//
// Green: the desugar binds the freshly allocated handle to `__cont0`, and
// `close` destroys THAT pointer — not the freed one (output_emitted.zig).
import app/held
import std/io
app/held:open()
| opened h |> app/held:advance(h) |> app/held:close(h) |> std/io:print.ln("advanced (new allocation), in place")
| err e |> std/io:print.ln("err: {{ e:s }}")
Supporting Files
// The identity-agnostic fixture: same transition as 336_007, but `advance`
// MINTS A NEW HANDLE and destroys the old one. The caller cannot tell, and the
// rule says they must not have to — the continuation is keyed on the base type,
// not the pointer.
const std = @import("std");
pub const Handle = struct { _token: u8 };
~pub tor open { }
| opened *Handle<open!>
| err string
~proc open|zig {
const h = std.heap.page_allocator.create(Handle) catch unreachable;
h.* = Handle{ ._token = 0 };
return .{ .opened = h };
}
~pub tor advance { h: *Handle<!open|!active> } -> *Handle<active!>
~proc advance|zig {
const n = std.heap.page_allocator.create(Handle) catch unreachable;
n.* = Handle{ ._token = h._token };
std.heap.page_allocator.destroy(h);
return n;
}
~pub tor close { h: *Handle<!active> }
~proc close|zig {
std.heap.page_allocator.destroy(h);
}
Actual
advanced (new allocation), in place
Expected output
advanced (new allocation), in place
Flows
flow ~open click a branch to expand · @labels scroll to their anchor
open
Test Configuration
MUST_RUN