From 9d7587940d3d43d0d88bb12b26a24ce688ec7a71 Mon Sep 17 00:00:00 2001 From: Kaz Kylheku Date: Wed, 28 Apr 2021 20:41:22 -0700 Subject: macros: lexical-var-p: don't report t for specials. The lexical-var-p function wrongly reports true for locally rebound special variables, which are not lexical. * eval.c (special_var_p): Static function moved to avoid forward declaration. (lexical_var_p): Bail if sym satisfies special_var_p. (old_lexical_var_p): New function, copy of old lexical_var_p before this bugfix. (eval_init): Conditionally register lexical-var-p as either lexical_var_p or old_lexical_var_p depending on the compat value. * txr.1: Update documentation for lexical-var-p to clarify that it doesn't report true for specials. Also make note that it doesn't report true for global lexicals, and likewise that lexical-fun-p doesn't report global functions. Added compat note. --- eval.c | 38 ++++++++++++++++++++++++++++++-------- txr.1 | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 82 insertions(+), 12 deletions(-) diff --git a/eval.c b/eval.c index 5983f2cd..b2e0605a 100644 --- a/eval.c +++ b/eval.c @@ -715,10 +715,19 @@ static val reparent_env(val child, val parent) return child; } +static val special_var_p(val sym) +{ + uses_or2; + return or2(gethash(special, sym), + if2(lisplib_try_load(sym), gethash(special, sym))); +} + static val lexical_var_p(val menv, val sym) { if (nilp(menv)) { return nil; + } else if (special_var_p(sym)) { + return nil; } else { type_check(lit("lexical-var-p"), menv, ENV); @@ -732,6 +741,23 @@ static val lexical_var_p(val menv, val sym) } } +static val old_lexical_var_p(val menv, val sym) +{ + if (nilp(menv)) { + return nil; + } else { + type_check(lit("lexical-var-p"), menv, ENV); + + { + val binding = assoc(sym, menv->e.vbindings); + + if (binding) + return tnil(cdr(binding) == special_s); + return lexical_var_p(menv->e.up_env, sym); + } + } +} + static val lexical_fun_p(val menv, val sym) { if (nilp(menv)) { @@ -780,13 +806,6 @@ static val mark_special(val sym) return sethash(special, sym, t); } -static val special_var_p(val sym) -{ - uses_or2; - return or2(gethash(special, sym), - if2(lisplib_try_load(sym), gethash(special, sym))); -} - static void copy_env_handler(mem_t *ptr) { val *penv = coerce(val *, ptr); @@ -6793,7 +6812,10 @@ void eval_init(void) reg_fun(intern(lit("env-vbindings"), user_package), func_n1(env_vbindings)); reg_fun(intern(lit("env-fbindings"), user_package), func_n1(env_fbindings)); reg_fun(intern(lit("env-next"), user_package), func_n1(env_next)); - reg_fun(intern(lit("lexical-var-p"), user_package), func_n2(lexical_var_p)); + reg_fun(intern(lit("lexical-var-p"), user_package), + func_n2(if3(opt_compat && opt_compat <= 257, + old_lexical_var_p, + lexical_var_p))); reg_fun(intern(lit("lexical-fun-p"), user_package), func_n2(lexical_fun_p)); reg_fun(intern(lit("lexical-lisp1-binding"), user_package), func_n2(lexical_lisp1_binding)); diff --git a/txr.1 b/txr.1 index c3524bfa..ddf1f105 100644 --- a/txr.1 +++ b/txr.1 @@ -36785,11 +36785,45 @@ parameter. Using these functions, a macro can enquire whether a given .meta form is a symbol which has a variable binding or a function binding -in the lexical environment. +in the local lexical environment. This information is known during macro expansion. The macro expander recognizes lexical function and variable bindings, because these bindings can shadow macros. +Special variables are not lexical. The function +.code lexical-var-p +returns +.code nil +if +.meta form +satisfies +.code special-var-p +function, indicating that it is the name of a special variable. + +The +.code lexical-var-p +function also returns +.code nil +for global lexical variables. If +.meta form +is a symbol for which only a global lexical variable binding is apparent, +.code lexical-var-p +returns +.codn nil . +Testing for the existence for a global variable can be done using +.codn boundp ; +if a symbol is +.code boundp +but not +.codn special-var-p , +then it is a global lexical variable. + +Similarly, +.code lexical-fun-p +returns +.code nil +for global functions. + .TP* Example: .verb @@ -36806,14 +36840,20 @@ bindings can shadow macros. (t :not-lex-fun-var))) ;; - ;; This returns: - ;; - ;; (:lexical-var :not-lex-fun-var :lexical-fun) + ;; Use classify macro above to report classification + ;; of the x, y and f symbols in the given scope ;; (let ((x 1) (y 2)) (symacrolet ((y x)) (flet ((f () (+ 2 2))) (list (classify x) (classify y) (classify f))))) + --> (:lexical-var :not-lex-fun-var :lexical-fun) + + ;; Locally bound specials are not lexical + + (let ((*stdout* *stdnull*)) + (classify *stdout*)) + --> :not-lex-fun-var .brev .TP* Note: @@ -79273,6 +79313,14 @@ A compatibility value of 248 or lower restores the above old behaviors of .code @ and .codn hash-revget . +.IP 257 +Until \*(TX 257, the function +.code lexical-var-p +returned +.code t +for not only lexical variables, but also for locally bound special variables, +which are not lexical. The behavior is restored if 257 or older compatibility +is selected. .IP 244 Until \*(TX 244, the .code env-hash -- cgit v1.2.3