special-form heads are not shadowable
Found in a read/eval review: a local named like a special form wrongly took over operator position. (let [if (fn ...)] (if true 1 2)) returned the fn, but per spec section 3 (and the reference) special-form heads are not shadowable; only macros are. Two fixes: drop the (not shadowed) guard on the special-form branch of analyze-list (so an (if ...) head is always the special), and prefix a local whose name is a Scheme keyword when emitting (so a value local legally named if does not shadow the (if ...) the back end emits). Value-position locals named if/or/case still work.
This commit is contained in:
parent
f18ae3bd46
commit
212cd0399a
5 changed files with 106 additions and 81 deletions
|
|
@ -465,7 +465,10 @@
|
|||
;; read -> macroexpand -> analyze. A local shadows both.
|
||||
(and (form-sym? head) (not shadowed) (form-macro? ctx head))
|
||||
(analyze ctx (form-expand-1 ctx form) env)
|
||||
(and hname (not shadowed) (contains? handled hname))
|
||||
;; special-form heads are NOT shadowable (unlike macros): a local named
|
||||
;; `if` does not change the meaning of (if …) in operator position, per
|
||||
;; spec §3 and the reference. No (not shadowed) guard here.
|
||||
(and hname (contains? handled hname))
|
||||
(analyze-special ctx hname items env)
|
||||
(and hname (not shadowed) (method-head? hname))
|
||||
(analyze-host-call ctx hname items env)
|
||||
|
|
|
|||
|
|
@ -105,10 +105,23 @@
|
|||
(def ^:private gensym-counter (atom 0))
|
||||
(defn- fresh-label [prefix] (str prefix (swap! gensym-counter inc)))
|
||||
|
||||
;; Scheme syntactic keywords. A jolt local with one of these names would, when
|
||||
;; emitted verbatim, shadow the Scheme form in operator position (a local named
|
||||
;; `if` would turn the special form (if …) the back end emits into a call), so
|
||||
;; such locals are prefixed. Matches the spec: special-form heads are not
|
||||
;; shadowable, but a value local may legally be named `if`.
|
||||
(def ^:private scheme-reserved
|
||||
#{"if" "begin" "lambda" "let" "let*" "letrec" "letrec*" "quote" "quasiquote"
|
||||
"unquote" "set!" "define" "define-syntax" "cond" "case" "when" "unless"
|
||||
"and" "or" "do" "else" "guard" "parameterize" "delay" "values"})
|
||||
|
||||
;; Most jolt names are already valid Scheme identifiers. The one that isn't is
|
||||
;; `#`, which jolt auto-gensyms use as a suffix (p1__0000X4# from #(...)) — `#`
|
||||
;; starts a datum in Scheme, so replace it with `_`.
|
||||
(defn- munge-name [s] (str/replace s "#" "_"))
|
||||
;; starts a datum in Scheme, so replace it with `_`. A name that collides with a
|
||||
;; Scheme keyword is prefixed with `_` so it can never shadow the emitted form.
|
||||
(defn- munge-name [s]
|
||||
(let [s (str/replace s "#" "_")]
|
||||
(if (contains? scheme-reserved s) (str "_" s) s)))
|
||||
|
||||
(declare emit)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue