diff --git a/BUILT.md b/BUILT.md index 9a565ef..04f0e4e 100644 --- a/BUILT.md +++ b/BUILT.md @@ -1163,6 +1163,17 @@ unfilled, `invoke-restart` marks it filled, and a clause with parameters checks top of `NEXT.md`: the answer is a Flan expression, and there is already something that compiles one against the live program. +**Lookup stayed by name, and the signature is checked against what it found.** `flan_find_restart` matches the name +hash and nothing else, so an inner `(use-value [s string] ...)` shadows an outer `(use-value [v i32] ...)` and an i32 +is refused there — the outer frame that would have taken it is never consulted. That is §4 read straight ("the first +frame offering the name") and it is the thing a reader will assume works the other way, so `restarts.flan` has a case +for it. Searching outward for a frame whose signature fits would make which restart runs depend on the arguments, +which is overload resolution on a dynamic stack. + +Both of these guards go through `fail_block` unconditionally, unlike the bounds checks: `--no-bounds-checks` does not +remove them. A wrong index is a wrong answer, and a transfer into a clause whose parameters were never written, or +written to a different layout, is memory corruption. + `runtime/flan_rt.c` gained two message functions and nothing else. The restart frame's first four fields are the ones C declares and their offsets do not move; everything §3 needed is appended after them, and C never allocates one. diff --git a/conditions.org b/conditions.org index 823b38f..99d7b6f 100644 --- a/conditions.org +++ b/conditions.org @@ -87,6 +87,10 @@ the agent's socket instead. - *An unknown restart name is a hard stop.* No ~find-restart~ to test with. - *So are the wrong arguments*, and for the same reason: nothing static can know what a name will find. The message names both signatures. +- *Lookup is by name; the signature is checked after it.* Nothing searches for + a frame the arguments would fit. An inner ~(use-value [s string] ...)~ + shadows an outer ~(use-value [v i32] ...)~, so ~(invoke-restart 'use-value + 21)~ stops the program even though the outer clause would have taken it. - *No supertype*, so nothing can say "any condition". - *~signal~ cannot hand a value back.* Deliberate (§1). - A condition must be a *struct*. ~return~ is refused inside either form. diff --git a/test/programs/restarts.flan b/test/programs/restarts.flan index d38baf2..0b6b6ba 100644 --- a/test/programs/restarts.flan +++ b/test/programs/restarts.flan @@ -87,6 +87,20 @@ (handler-bind [(AssetMissing [c] (invoke-restart 'retry 1))] (supplied n))) +;;; §4 meets §3: lookup is by *name*, and the signature is checked against +;;; whatever that finds. An inner frame offering use-value shadows the outer +;;; one, so an i32 is refused here even though the outer clause would have +;;; taken it — a name finds one frame, it does not search for a fitting one. +(defn shadowed [n i32] i32 + (restart-case + (restart-case (middle n) + (use-value [s string] (do (print s) 0))) + (use-value [v i32] v))) + +(defn mislaid [n i32] i32 + (handler-bind [(AssetMissing [c] (invoke-restart 'use-value 21))] + (shadowed n))) + (defn main [args [string]] i32 ;; One argument selects a trap; none runs the table's case. (if (> (len args) 1) @@ -95,6 +109,7 @@ (= k 1) (print (mismatched 90)) (= k 2) (print (mistyped 91)) (= k 3) (print (overfull 92)) + (= k 4) (print (mislaid 93)) :else (println "?")) (return 0))) diff --git a/test/test_acceptance.ml b/test/test_acceptance.ml index eac002a..92e8a0c 100644 --- a/test/test_acceptance.ml +++ b/test/test_acceptance.ml @@ -298,6 +298,12 @@ let () = "restart use-value takes (i32), given (string)"; refuses "arguments given to a restart that takes none" "3" "restart retry takes (), given (i32)"; + (* §4 meets §3. The name finds the innermost frame offering it and the + signature is checked against *that*; nothing searches outward for a + frame the arguments would have fitted, and an outer clause that would + have taken them is not consulted. *) + refuses "a shadowing clause of the same name and a different signature" "4" + "restart use-value takes (string), given (i32)"; (try Sys.remove exe with Sys_error _ -> ()) in restart_mismatch ();