From e15727cf2ad6ebe2503f9ea0e16dccf118bc93ff Mon Sep 17 00:00:00 2001 From: Kaz Kylheku Date: Fri, 26 Oct 2018 07:33:03 -0700 Subject: defvar: warn about prior lexical uses. Now that the compiler has a more efficient treatment of global lexical variables, code which accesses global variables that have not yet been defined will misbehave if the intent is to for those variables to be dynamically scoped. There is such a bug in the op expander, in fact. * eval.c (me_def_variable): When defvar/defparm are expanding, they now check whether there is an outstanding unbound warning against the variable. If so, then a warning is issued that the variable was previously used lexically and is now being marked special. * unwind.c (uw_warning_exists): New function. * unwind.h (uw_warning_exists): Declared. --- eval.c | 6 +++++- unwind.c | 5 +++++ unwind.h | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/eval.c b/eval.c index 50c6fbc7..e28e1164 100644 --- a/eval.c +++ b/eval.c @@ -2929,8 +2929,12 @@ static val me_def_variable(val form, val menv) if (!bindable(sym)) not_bindable_error(form, sym); - if (op == defparm_s || op == defvar_s) + if (op == defparm_s || op == defvar_s) { mark_special(sym); + if (uw_warning_exists(cons(var_s, sym))) + eval_warn(form, lit("~s: global ~s marked special after lexical uses"), + op, sym, nao); + } return apply_frob_args(list(prog1_s, cons(defvarl_s, diff --git a/unwind.c b/unwind.c index b89bf991..36c1a1e0 100644 --- a/unwind.c +++ b/unwind.c @@ -707,6 +707,11 @@ val uw_defer_warning(val args) return nil; } +val uw_warning_exists(val tag) +{ + return member(tag, deferred_warnings, equal_f, cdr_f); +} + val uw_register_tentative_def(val tag) { uw_purge_deferred_warning(tag); diff --git a/unwind.h b/unwind.h index b08f8dc9..33106a0a 100644 --- a/unwind.h +++ b/unwind.h @@ -137,6 +137,7 @@ noreturn val uw_throwfv(val sym, val fmt, struct args *); noreturn val uw_errorf(val fmt, ...); noreturn val uw_errorfv(val fmt, struct args *args); val uw_defer_warning(val args); +val uw_warning_exists(val tag); val uw_dump_deferred_warnings(val stream); val uw_release_deferred_warnings(void); val uw_purge_deferred_warning(val tag); -- cgit v1.2.3