Package: emacs;
Reported by: Alan Mackenzie <acm <at> muc.de>
Date: Wed, 15 Nov 2023 17:03:02 UTC
Severity: normal
Done: Alan Mackenzie <acm <at> muc.de>
Bug is archived. No further changes may be made.
Message #53 received at 67196 <at> debbugs.gnu.org (full text, mbox):
From: Alan Mackenzie <acm <at> muc.de> To: Eli Zaretskii <eliz <at> gnu.org> Cc: 67196 <at> debbugs.gnu.org, acm <at> muc.de, monnier <at> iro.umontreal.ca Subject: Re: bug#67196: M-: uses a wrong value of debug-on-error when it is nil. Date: Sat, 25 Nov 2023 12:40:03 +0000
Hello, Eli. On Sat, Nov 25, 2023 at 13:15:15 +0200, Eli Zaretskii wrote: > > Date: Sat, 25 Nov 2023 10:32:23 +0000 > > Cc: monnier <at> iro.umontreal.ca, 67196 <at> debbugs.gnu.org > > From: Alan Mackenzie <acm <at> muc.de> > > > How about not exposing the internal variable to Lisp at all? > > That's a very good idea. It would need little more than a new C function > > which would bind that variable then call eval. Maybe move > > eval-expression-debug-on-error into eval.c, too. > What I had in mind was a function exposed to Lisp that would set an > internal variable not exposed to Lisp. The would still require an unwind-protect somewhere. I've implemented sub-eval-expression in eval.c. debug-from--eval-expression is no longer visible from Lisp. Perhaps this is good enough. > > > > For what it's worth, I lost about 10 hours of time trying to debug > > > > a situation where I wasn't getting a backtrace, despite debug-on-error > > > > being t. The problem was that d-o-e wasn't t at all, it was nil. M-: > > > > had been lying. > > > You never described that situation, AFAICT. I think you should, so > > > that we could assess how grave the problem is, which is an important > > > part of deciding whether the solution you propose is useful. I don't > > > understand how could you NOT get a backtrace when debug-on-error is > > > non-nil. > > Sorry, I wasn't clear enough. During those 10 hours, I was under the > > impression that debug-on-error was t, because M-: debug-on-error said so. > > It actually was nil. That's why I submitted this bug report. > So maybe instead of changing how this stuff works we should improve > how debug-on-error's value is reported by M-: and other eval commands? Yes, but that might be complicated, and won't help the user trying to debug something which depends on debug-on-error, who is using M-: to try to test it. I still say the bug is trying to make debug-on-error do too much, more than it's capable of. > Note that (AFAIU) your change doesn't just solve the problem you > bumped into, it also changes the value of debug-on-error inside > eval-expression etc., when eval-expression-debug-on-error's value is > non-nil, but not t. I wonder what is the reason for that? I don't see that in my current version of the patch (below). To test this, I used the following: (defun foo () (interactive) (message "debug-on-error is %s" debug-on-error) (message "eval-expression-debug-on-error is %s" eval-expression-debug-on-error) (car 'foo)) , and called it with various settings of debug-on-error and eval-expression-debug-on-error. In particular, with (setq eval-expression-debug-on-error '(wrong-type-argument)) , I still see debug-on-error reported as nil. diff --git a/lisp/cus-start.el b/lisp/cus-start.el index 6d83aaf4d14..9d176c6c599 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el @@ -262,6 +262,13 @@ minibuffer-prompt-properties--setter :value (nil) (symbol :format "%v")) (const :tag "always" t))) + (eval-expression-debug-on-error debug + (choice (const :tag "off") + (repeat :menu-tag "When" + :value (nil) + (symbol :format "%v")) + (const :tag "always" t)) + "30.1") (debug-ignored-errors debug (repeat (choice symbol regexp))) (debug-on-quit debug boolean) (debug-on-signal debug boolean) diff --git a/lisp/simple.el b/lisp/simple.el index 02c68912dba..f4c9873ceed 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -1980,13 +1980,6 @@ eval-expression-print-length :type '(choice (const :tag "No Limit" nil) integer) :version "21.1") -(defcustom eval-expression-debug-on-error t - "If non-nil set `debug-on-error' to t in `eval-expression'. -If nil, don't change the value of `debug-on-error'." - :group 'lisp - :type 'boolean - :version "21.1") - (defcustom eval-expression-print-maximum-character 127 "The largest integer that will be displayed as a character. This affects printing by `eval-expression' (via @@ -2120,34 +2113,18 @@ eval-expression (cons (read--expression "Eval: ") (eval-expression-get-print-arguments current-prefix-arg))) - (let (result) - (if (null eval-expression-debug-on-error) - (setq result - (values--store-value - (eval (let ((lexical-binding t)) (macroexpand-all exp)) t))) - (let ((old-value (make-symbol "t")) new-value) - ;; Bind debug-on-error to something unique so that we can - ;; detect when evalled code changes it. - (let ((debug-on-error old-value)) - (setq result - (values--store-value - (eval (let ((lexical-binding t)) (macroexpand-all exp)) t))) - (setq new-value debug-on-error)) - ;; If evalled code has changed the value of debug-on-error, - ;; propagate that change to the global binding. - (unless (eq old-value new-value) - (setq debug-on-error new-value)))) - - (let ((print-length (unless no-truncate eval-expression-print-length)) - (print-level (unless no-truncate eval-expression-print-level)) - (eval-expression-print-maximum-character char-print-limit) - (deactivate-mark)) - (let ((out (if insert-value (current-buffer) t))) - (prog1 - (prin1 result out) - (let ((str (and char-print-limit - (eval-expression-print-format result)))) - (when str (princ str out)))))))) + (let* ((result (values--store-value + (sub-eval-expression (macroexpand-all exp)))) + (print-length (unless no-truncate eval-expression-print-length)) + (print-level (unless no-truncate eval-expression-print-level)) + (eval-expression-print-maximum-character char-print-limit) + (deactivate-mark) + (out (if insert-value (current-buffer) t))) + (prog1 + (prin1 result out) + (let ((str (and char-print-limit + (eval-expression-print-format result)))) + (when str (princ str out)))))) (defun edit-and-eval-command (prompt command) "Prompting with PROMPT, let user edit COMMAND and eval result. diff --git a/src/eval.c b/src/eval.c index 12e811ce264..eccabf3a091 100644 --- a/src/eval.c +++ b/src/eval.c @@ -2033,7 +2033,8 @@ maybe_call_debugger (Lisp_Object conditions, Lisp_Object sig, Lisp_Object data) /* Does user want to enter debugger for this kind of error? */ && (signal_quit_p (sig) ? debug_on_quit - : wants_debugger (Vdebug_on_error, conditions)) + : (wants_debugger (Vdebug_from__eval_expression, conditions) + || wants_debugger (Vdebug_on_error, conditions))) && ! skip_debugger (conditions, combined_data) /* See commentary on definition of `internal-when-entered-debugger'. */ @@ -2383,6 +2384,19 @@ DEFUN ("eval", Feval, Seval, 1, 2, 0, return unbind_to (count, eval_sub (form)); } +DEFUN ("sub-eval-expression", Fsub_eval_expression, Ssub_eval_expression, + 1, 1, 0, + doc: /* Evaluate FORM and return its value. +This function should be called only from `eval-expression'. +It evaluates with `lexical-binding' non-nil, and handles +`eval-expression-debug-on-error'. */) + (Lisp_Object form) +{ + specpdl_ref count = SPECPDL_INDEX (); + specbind (Qdebug_from__eval_expression, Veval_expression_debug_on_error); + return unbind_to (count, Feval (form, Qt)); +} + void grow_specpdl_allocation (void) { @@ -4299,6 +4313,29 @@ syms_of_eval (void) See also the variable `debug-on-quit' and `inhibit-debugger'. */); Vdebug_on_error = Qnil; + DEFSYM (Qeval_expression_debug_on_error, "eval-expression-debug-on-error"); + DEFVAR_LISP ("eval-expression-debug-on-error", + Veval_expression_debug_on_error, + doc: /* Non-nil means enter debugger on error on a call from `eval-expression'. +Does not apply to errors handled by `condition-case' or those +matched by `debug-ignored-errors'. +Like `debug-on-error', this variable's value can also be a list, +with the same meaning as for `debug-on-error'. + +A nil value for this variable will not prevent an entry to +the debugger caused by other variables such as `debug-on-error'. */); + Veval_expression_debug_on_error = Qt; + + DEFSYM (Qdebug_from__eval_expression, "debug-from--eval-expression"); + DEFVAR_LISP ("debug-from--eval-expression", Vdebug_from__eval_expression, + doc: /* Non-nil means enter debugger if an error is signaled. +This only applies in forms called by `eval-expression'. This variable +has the same semantics as `debug-on-error'. It is an internal variable +only. */); + Vdebug_from__eval_expression = Qnil; + /* debug-from--eval-expression should not be visible from Lisp. */ + Funintern (Qdebug_from__eval_expression, Qnil); + DEFVAR_LISP ("debug-ignored-errors", Vdebug_ignored_errors, doc: /* List of errors for which the debugger should not be called. Each element may be a condition-name or a regexp that matches error messages. @@ -4455,6 +4492,7 @@ syms_of_eval (void) defsubr (&Sautoload); defsubr (&Sautoload_do_load); defsubr (&Seval); + defsubr (&Ssub_eval_expression); defsubr (&Sapply); defsubr (&Sfuncall); defsubr (&Sfunc_arity); -- Alan Mackenzie (Nuremberg, Germany).
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.