core: fn*/let*/loop* are plain-symbol primitives, matching Clojure (jolt-f79)
jolt-f79 asked to compile destructuring in fn* rest params. Checking
against Clojure inverts the premise: Clojure's fn* REJECTS destructuring
at compile time ('fn params must be Symbols'; let*/loop* 'Bad binding
form, expected symbol'). So the self-hosted analyzer was already correct
— fn*/let*/loop* are plain-symbol primitives; the fn/let/loop/defn
MACROS desugar destructuring. The real defect was the interpreter
leniently destructuring raw fn*, and defn emitting raw fn* to rely on it.
Changes:
- evaluator: fn*/let*/loop* now reject non-symbol binding forms with
Clojure's exact messages (require-symbol-params/plain-sym?), so the
interpreter agrees with the analyzer + Clojure.
- 00-syntax: defn emits the fn MACRO (not raw fn*) so destructuring
params desugar; unnamed, so self-recursion still resolves via the var.
- 00-syntax: completing that exposed a real gap — the overlay destructure
fn didn't handle kwargs (a map pattern bound against a fn's sequential
rest); it had only worked via the interpreter's destructure-bind. Added
the seq->map coercion to the map? branch (sequential: 1 map elt => that
map, else apply hash-map), matching destructure-bind so interpret ==
compile.
Net: fn*/let*/loop* are plain-symbol primitives across interpreter,
analyzer, and Clojure; all real destructuring (fn/defn/let/loop/macro
params, incl kwargs & {:keys}) compiles through the macros with no
interpreter fallback. Regression spec added.
Gate green: conformance 267x3, fallback-zero 31/5, bootstrap-fixpoint
stage1==2==3, self-host, staged-bootstrap, lazy-infinite 44/44,
clojure-test-suite >=4034/67, sci-bootstrap, features 78/78, all
unit+spec (destructuring 50/50).
This commit is contained in:
parent
20da34a432
commit
d1d3f390ff
3 changed files with 62 additions and 10 deletions
|
|
@ -55,6 +55,20 @@
|
|||
["loop map binding" "4" "(loop [{:keys [v]} {:v 1} n 0] (if (< n 2) (recur {:v (* v 2)} (inc n)) v))"]
|
||||
["loop init sees destr" "[1 2 3]" "(loop [[a b] [1 2] c (+ a b)] [a b c])"])
|
||||
|
||||
# fn*/let*/loop* are PRIMITIVES: their binding forms must be plain symbols, exactly
|
||||
# as in Clojure ("fn params must be Symbols" / "Bad binding form, expected symbol").
|
||||
# Destructuring lives in the fn/let/loop/defn MACROS, which desugar to plain
|
||||
# primitives — so the interpreter and the self-hosted analyzer agree (neither
|
||||
# destructures fn*), and nothing silently falls back to a lenient interpreter path.
|
||||
(defspec "destructure / primitives reject patterns (jolt-f79)"
|
||||
["fn* fixed pattern" :throws "((fn* [[a b]] a) [1 2])"]
|
||||
["fn* rest pattern" :throws "((fn* [a & [b]] b) 1 2 3)"]
|
||||
["let* pattern" :throws "(let* [[a b] [1 2]] a)"]
|
||||
["loop* pattern" :throws "(loop* [[a b] [1 2]] a)"]
|
||||
# the macros still desugar the same patterns to plain primitives:
|
||||
["fn desugars" "[1 2]" "((fn [[a b]] [a b]) [1 2])"]
|
||||
["let desugars" "[1 2]" "(let [[a b] [1 2]] [a b])"])
|
||||
|
||||
(defspec "destructure / macro params"
|
||||
["macro & [a & more :as all]"
|
||||
"[1 [2 3] [1 2 3]]"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue