✓
Passing This code compiles and runs correctly.
Code
// PINS: a phantom-only transition is SPELLED, not inferred.
//
// `transition { r: *Resource<!state_a> } -> *Resource<state_b!>` consumes one
// phantom and issues another on the same base type. Its implementation is one
// line — `transition -> r` — and that line is the point: it is where the author
// says identity is what they meant.
//
// RULED (Lars, 2026-07-31): auto-proc synthesis is NOT extended to bare
// returns. The existing exemption (350_001, green) fires only when input and
// output fields match by NAME and type — the author naming the output after the
// input is the evidence that passthrough was intended. A bare return has no
// field name, so that evidence cannot exist, and the rule would collapse to
// "one input whose base type equals the return's".
//
// The phantom pair does not rescue it. `validate { t: *Token<!raw> } ->
// *Token<checked!>` has the identical signature shape — consumes one phantom,
// issues another, same base type. Synthesizing identity there hands back a token
// marked `checked!` that nothing checked. A phantom claiming a property no code
// established is the guarantee lying, which is the worst failure this language
// has.
//
// Contrast the auto-discharge ruling made the same day (330_097/330_120), which
// went the other way for a reason that holds in both directions: auto-discharge
// synthesizes a NAME for a value that already exists, inventing no information.
// Auto-proc here would synthesize an IMPLEMENTATION, inventing all of it.
//
// 350_004 pins the other narrowing: an UNCALLED tor needs no proc at all.
const std = @import("std");
const Resource = struct { value: i32 };
// Create resource - has a proc (does real work)
~pub tor create { value: i32 } -> *Resource<state_a!>
~proc create|zig {
const r = std.heap.page_allocator.create(Resource) catch unreachable;
r.* = Resource{ .value = value };
return r;
}
~pub tor transition { r: *Resource<!state_a> } -> *Resource<state_b!>
~transition -> r
// Cleanup state_b - has a proc (does real work)
~pub tor cleanup { r: *Resource<!state_b> }
~proc cleanup|zig {
std.debug.print("cleaned up resource with value {d}\n", .{r.value});
std.heap.page_allocator.destroy(r);
}
// Test it
~create(value: 99): c |> transition(r: c): t |> cleanup(r: t)
Actual
cleaned up resource with value 99
Expected output
cleaned up resource with value 99
Flows
flow ~create click a branch to expand · @labels scroll to their anchor
create (value: 99)
Test Configuration
MUST_RUN