From 5980b5b60f6de449a37d255d39a5761620892d84 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Fri, 11 Sep 2026 12:23:36 +0700 Subject: [PATCH] The transfer channel is the ABI, not a stopgap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit plan.org now says every Flan function carries the transfer channel, that uniformity is what keeps indirect calls and hot reload ABI-safe, and that a later optimisation cannot change the ABI. spec-conditions.md §6 still read the other way round - escape analysis deciding which functions are transfer-transparent, with the rest paying nothing - which describes a signature that depends on an analysis, and a cell cannot hold one of those. So the analysis is demoted to what it can still honestly do: a function that provably cannot transfer need not check the channel after a call and can pass the pointer straight through. It may not drop the parameter. NEXT.md said the same thing as a for-later note and now says it is settled. --- NEXT.md | 12 +++++++----- spec-conditions.md | 12 +++++++++--- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/NEXT.md b/NEXT.md index 3de934a..b20e314 100644 --- a/NEXT.md +++ b/NEXT.md @@ -955,11 +955,13 @@ check and return early, which reuses the existing `return` path and with it §5's defers. `Emit.signature` was already the one place a signature is spelled, which is what made this a three-line change rather than a hunt. -**Every function is transfer-transparent**, release included. §6's escape -analysis is an optimisation, and in a dev build a cell can hold anything, so -the honest answer to "what can this call?" is "anything" — the same bargain as -the indirect call. Uniform also means redefinition acquires no new refusal -class. +**Every function is transfer-transparent**, release included — and that is the +ABI, not a stopgap. A cell holds a bare pointer, so the honest answer to "what +can this call?" is "anything"; the same bargain as the indirect call. §6 and +plan.org both now say the later optimisation may stop a function *checking* the +channel, or pass the pointer straight through, but may not drop the parameter — +a signature that depended on an analysis could not be reloaded into. Uniform +also means redefinition acquires no new refusal class. **The transfer target is the restart frame's own address, not a clause id.** This is a correction to what the previous note settled. A static id has to be diff --git a/spec-conditions.md b/spec-conditions.md index d0bcc3a..18c3d9b 100644 --- a/spec-conditions.md +++ b/spec-conditions.md @@ -123,9 +123,15 @@ but it is not re-entrant: §5 runs defers *during* a transfer, so a defer that signals and invokes a restart would start a second transfer over the first. A per-frame slot nests correctly with no threads involved. -- The compiler marks a function transfer-transparent if it can call, directly or - indirectly, anything that may invoke a restart. Escape analysis narrows this - set; functions outside it pay nothing. +- **Every function carries the channel, and that is the ABI.** Uniformity is + what keeps an indirect call and a hot-reload cell safe: a cell holds a bare + pointer, so the honest answer to "what can this call?" is "anything", and a + signature that depended on the answer could not be reloaded into. An earlier + draft had escape analysis decide which functions are transfer-transparent; + that is now an *optimisation over what a function does with the channel* — + a function that provably cannot transfer need not check it after a call, and + can pass the pointer straight through. It may not drop the parameter. See + plan.org, Hot reload. - **Foreign frames cannot be crossed.** A restart transfer whose path passes through a C frame (a raylib callback, an `extern` function calling back into Flan) is a runtime error, not undefined behaviour. Handlers installed across an