diff options
| author | Stefan Monnier | 2023-12-27 15:06:32 -0500 |
|---|---|---|
| committer | Stefan Monnier | 2024-01-04 16:37:01 -0500 |
| commit | 2ef6e40da88d5b4f070e339a2210f5751ab6a7cb (patch) | |
| tree | c5669492d32dba2bffe119cc0c2c72b327d994ee /src/eval.c | |
| parent | 02edbc88a1210b8d5a3e62ca4f03ffd17b23cbf7 (diff) | |
| download | emacs-2ef6e40da88d5b4f070e339a2210f5751ab6a7cb.tar.gz emacs-2ef6e40da88d5b4f070e339a2210f5751ab6a7cb.zip | |
(signal_or_quit): Preserve error object identity
Make sure we build the (ERROR-SYMBOL . ERROR-DATA) object only once
when signaling an error, so that its `eq` identity can be used.
It also gets us a tiny bit closer to having real "error objects"
like in most other current programming languages.
* src/eval.c (maybe_call_debugger): Change arglist to receive the error
object instead of receiving the signal and the data separately.
(signal_or_quit): Build the error object right at the beginning so it
stays `eq` to itself.
Rename the `keyboard_quit` arg to `continuable` so say what it does
rather than what it's used for.
(signal_quit_p): Change arg to be the error object rather than just the
error-symbol.
* src/keyboard.c (cmd_error_internal, menu_item_eval_property_1):
Adjust calls to `signal_quit_p` accordingly.
* test/src/eval-tests.el (eval-tests--error-id): New test.
Diffstat (limited to 'src/eval.c')
| -rw-r--r-- | src/eval.c | 66 |
1 files changed, 30 insertions, 36 deletions
diff --git a/src/eval.c b/src/eval.c index b982c124184..1dd797063eb 100644 --- a/src/eval.c +++ b/src/eval.c | |||
| @@ -1706,8 +1706,7 @@ push_handler_nosignal (Lisp_Object tag_ch_val, enum handlertype handlertype) | |||
| 1706 | 1706 | ||
| 1707 | static Lisp_Object signal_or_quit (Lisp_Object, Lisp_Object, bool); | 1707 | static Lisp_Object signal_or_quit (Lisp_Object, Lisp_Object, bool); |
| 1708 | static Lisp_Object find_handler_clause (Lisp_Object, Lisp_Object); | 1708 | static Lisp_Object find_handler_clause (Lisp_Object, Lisp_Object); |
| 1709 | static bool maybe_call_debugger (Lisp_Object conditions, Lisp_Object sig, | 1709 | static bool maybe_call_debugger (Lisp_Object conditions, Lisp_Object error); |
| 1710 | Lisp_Object data); | ||
| 1711 | 1710 | ||
| 1712 | static void | 1711 | static void |
| 1713 | process_quit_flag (void) | 1712 | process_quit_flag (void) |
| @@ -1773,20 +1772,25 @@ quit (void) | |||
| 1773 | bool backtrace_yet = false; | 1772 | bool backtrace_yet = false; |
| 1774 | 1773 | ||
| 1775 | /* Signal an error, or quit. ERROR_SYMBOL and DATA are as with Fsignal. | 1774 | /* Signal an error, or quit. ERROR_SYMBOL and DATA are as with Fsignal. |
| 1776 | If KEYBOARD_QUIT, this is a quit; ERROR_SYMBOL should be | 1775 | If CONTINUABLE, the caller allows this function to return |
| 1777 | Qquit and DATA should be Qnil, and this function may return. | 1776 | (presumably after calling the debugger); |
| 1778 | Otherwise this function is like Fsignal and does not return. */ | 1777 | Otherwise this function is like Fsignal and does not return. */ |
| 1779 | 1778 | ||
| 1780 | static Lisp_Object | 1779 | static Lisp_Object |
| 1781 | signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) | 1780 | signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool continuable) |
| 1782 | { | 1781 | { |
| 1783 | /* When memory is full, ERROR-SYMBOL is nil, | 1782 | /* When memory is full, ERROR-SYMBOL is nil, |
| 1784 | and DATA is (REAL-ERROR-SYMBOL . REAL-DATA). | 1783 | and DATA is (REAL-ERROR-SYMBOL . REAL-DATA). |
| 1785 | That is a special case--don't do this in other situations. */ | 1784 | That is a special case--don't do this in other situations. */ |
| 1785 | bool oom = NILP (error_symbol); | ||
| 1786 | Lisp_Object error /* The error object. */ | ||
| 1787 | = oom ? data | ||
| 1788 | : (!SYMBOLP (error_symbol) && NILP (data)) ? error_symbol | ||
| 1789 | : Fcons (error_symbol, data); | ||
| 1786 | Lisp_Object conditions; | 1790 | Lisp_Object conditions; |
| 1787 | Lisp_Object string; | 1791 | Lisp_Object string; |
| 1788 | Lisp_Object real_error_symbol | 1792 | Lisp_Object real_error_symbol |
| 1789 | = (NILP (error_symbol) ? Fcar (data) : error_symbol); | 1793 | = CONSP (error) ? XCAR (error) : error_symbol; |
| 1790 | Lisp_Object clause = Qnil; | 1794 | Lisp_Object clause = Qnil; |
| 1791 | struct handler *h; | 1795 | struct handler *h; |
| 1792 | int skip; | 1796 | int skip; |
| @@ -1804,11 +1808,13 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) | |||
| 1804 | 1808 | ||
| 1805 | /* This hook is used by edebug. */ | 1809 | /* This hook is used by edebug. */ |
| 1806 | if (! NILP (Vsignal_hook_function) | 1810 | if (! NILP (Vsignal_hook_function) |
| 1807 | && ! NILP (error_symbol)) | 1811 | && !oom) |
| 1808 | { | 1812 | { |
| 1809 | specpdl_ref count = SPECPDL_INDEX (); | 1813 | specpdl_ref count = SPECPDL_INDEX (); |
| 1810 | max_ensure_room (20); | 1814 | max_ensure_room (20); |
| 1811 | /* FIXME: 'handler-bind' makes `signal-hook-function' obsolete? */ | 1815 | /* FIXME: 'handler-bind' makes `signal-hook-function' obsolete? */ |
| 1816 | /* FIXME: Here we still "split" the error object | ||
| 1817 | into its error-symbol and its error-data? */ | ||
| 1812 | call2 (Vsignal_hook_function, error_symbol, data); | 1818 | call2 (Vsignal_hook_function, error_symbol, data); |
| 1813 | unbind_to (count, Qnil); | 1819 | unbind_to (count, Qnil); |
| 1814 | } | 1820 | } |
| @@ -1820,7 +1826,7 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) | |||
| 1820 | too. Don't do this when ERROR_SYMBOL is nil, because that | 1826 | too. Don't do this when ERROR_SYMBOL is nil, because that |
| 1821 | is a memory-full error. */ | 1827 | is a memory-full error. */ |
| 1822 | Vsignaling_function = Qnil; | 1828 | Vsignaling_function = Qnil; |
| 1823 | if (!NILP (error_symbol)) | 1829 | if (!oom) |
| 1824 | { | 1830 | { |
| 1825 | union specbinding *pdl = backtrace_next (backtrace_top ()); | 1831 | union specbinding *pdl = backtrace_next (backtrace_top ()); |
| 1826 | if (backtrace_p (pdl) && EQ (backtrace_function (pdl), Qerror)) | 1832 | if (backtrace_p (pdl) && EQ (backtrace_function (pdl), Qerror)) |
| @@ -1845,14 +1851,11 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) | |||
| 1845 | { | 1851 | { |
| 1846 | if (!NILP (find_handler_clause (h->tag_or_ch, conditions))) | 1852 | if (!NILP (find_handler_clause (h->tag_or_ch, conditions))) |
| 1847 | { | 1853 | { |
| 1848 | Lisp_Object error_data | ||
| 1849 | = (NILP (error_symbol) | ||
| 1850 | ? data : Fcons (error_symbol, data)); | ||
| 1851 | specpdl_ref count = SPECPDL_INDEX (); | 1854 | specpdl_ref count = SPECPDL_INDEX (); |
| 1852 | max_ensure_room (20); | 1855 | max_ensure_room (20); |
| 1853 | push_handler (make_fixnum (skip + h->bytecode_dest), | 1856 | push_handler (make_fixnum (skip + h->bytecode_dest), |
| 1854 | SKIP_CONDITIONS); | 1857 | SKIP_CONDITIONS); |
| 1855 | call1 (h->val, error_data); | 1858 | call1 (h->val, error); |
| 1856 | unbind_to (count, Qnil); | 1859 | unbind_to (count, Qnil); |
| 1857 | pop_handler (); | 1860 | pop_handler (); |
| 1858 | } | 1861 | } |
| @@ -1875,7 +1878,7 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) | |||
| 1875 | bool debugger_called = false; | 1878 | bool debugger_called = false; |
| 1876 | if (/* Don't run the debugger for a memory-full error. | 1879 | if (/* Don't run the debugger for a memory-full error. |
| 1877 | (There is no room in memory to do that!) */ | 1880 | (There is no room in memory to do that!) */ |
| 1878 | !NILP (error_symbol) | 1881 | !oom |
| 1879 | && (!NILP (Vdebug_on_signal) | 1882 | && (!NILP (Vdebug_on_signal) |
| 1880 | /* If no handler is present now, try to run the debugger. */ | 1883 | /* If no handler is present now, try to run the debugger. */ |
| 1881 | || NILP (clause) | 1884 | || NILP (clause) |
| @@ -1887,17 +1890,17 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) | |||
| 1887 | || EQ (clause, Qerror))) | 1890 | || EQ (clause, Qerror))) |
| 1888 | { | 1891 | { |
| 1889 | debugger_called | 1892 | debugger_called |
| 1890 | = maybe_call_debugger (conditions, error_symbol, data); | 1893 | = maybe_call_debugger (conditions, error); |
| 1891 | /* We can't return values to code which signaled an error, but we | 1894 | /* We can't return values to code which signaled an error, but we |
| 1892 | can continue code which has signaled a quit. */ | 1895 | can continue code which has signaled a quit. */ |
| 1893 | if (keyboard_quit && debugger_called && EQ (real_error_symbol, Qquit)) | 1896 | if (continuable && debugger_called) |
| 1894 | return Qnil; | 1897 | return Qnil; |
| 1895 | } | 1898 | } |
| 1896 | 1899 | ||
| 1897 | /* If an error is signaled during a Lisp hook in redisplay, write a | 1900 | /* If an error is signaled during a Lisp hook in redisplay, write a |
| 1898 | backtrace into the buffer *Redisplay-trace*. */ | 1901 | backtrace into the buffer *Redisplay-trace*. */ |
| 1899 | /* FIXME: Turn this into a `handler-bind` installed during redisplay? */ | 1902 | /* FIXME: Turn this into a `handler-bind` installed during redisplay? */ |
| 1900 | if (!debugger_called && !NILP (error_symbol) | 1903 | if (!debugger_called && !oom |
| 1901 | && backtrace_on_redisplay_error | 1904 | && backtrace_on_redisplay_error |
| 1902 | && (NILP (clause) || h == redisplay_deep_handler) | 1905 | && (NILP (clause) || h == redisplay_deep_handler) |
| 1903 | && NILP (Vinhibit_debugger) | 1906 | && NILP (Vinhibit_debugger) |
| @@ -1918,7 +1921,7 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) | |||
| 1918 | backtrace_yet = true; | 1921 | backtrace_yet = true; |
| 1919 | specbind (Qstandard_output, redisplay_trace_buffer); | 1922 | specbind (Qstandard_output, redisplay_trace_buffer); |
| 1920 | specbind (Qdebugger, Qdebug_early); | 1923 | specbind (Qdebugger, Qdebug_early); |
| 1921 | call_debugger (list2 (Qerror, Fcons (error_symbol, data))); | 1924 | call_debugger (list2 (Qerror, error)); |
| 1922 | unbind_to (count, Qnil); | 1925 | unbind_to (count, Qnil); |
| 1923 | delayed_warning = make_string | 1926 | delayed_warning = make_string |
| 1924 | ("Error in a redisplay Lisp hook. See buffer *Redisplay-trace*", 61); | 1927 | ("Error in a redisplay Lisp hook. See buffer *Redisplay-trace*", 61); |
| @@ -1929,10 +1932,7 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) | |||
| 1929 | 1932 | ||
| 1930 | if (!NILP (clause)) | 1933 | if (!NILP (clause)) |
| 1931 | { | 1934 | { |
| 1932 | Lisp_Object unwind_data | 1935 | unwind_to_catch (h, NONLOCAL_EXIT_SIGNAL, error); |
| 1933 | = (NILP (error_symbol) ? data : Fcons (error_symbol, data)); | ||
| 1934 | |||
| 1935 | unwind_to_catch (h, NONLOCAL_EXIT_SIGNAL, unwind_data); | ||
| 1936 | } | 1936 | } |
| 1937 | else | 1937 | else |
| 1938 | { | 1938 | { |
| @@ -1943,10 +1943,7 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) | |||
| 1943 | Fthrow (Qtop_level, Qt); | 1943 | Fthrow (Qtop_level, Qt); |
| 1944 | } | 1944 | } |
| 1945 | 1945 | ||
| 1946 | if (! NILP (error_symbol)) | 1946 | string = Ferror_message_string (error); |
| 1947 | data = Fcons (error_symbol, data); | ||
| 1948 | |||
| 1949 | string = Ferror_message_string (data); | ||
| 1950 | fatal ("%s", SDATA (string)); | 1947 | fatal ("%s", SDATA (string)); |
| 1951 | } | 1948 | } |
| 1952 | 1949 | ||
| @@ -2071,14 +2068,15 @@ skip_debugger (Lisp_Object conditions, Lisp_Object data) | |||
| 2071 | return 0; | 2068 | return 0; |
| 2072 | } | 2069 | } |
| 2073 | 2070 | ||
| 2074 | /* Say whether SIGNAL is a `quit' symbol (or inherits from it). */ | 2071 | /* Say whether SIGNAL is a `quit' error (or inherits from it). */ |
| 2075 | bool | 2072 | bool |
| 2076 | signal_quit_p (Lisp_Object signal) | 2073 | signal_quit_p (Lisp_Object error) |
| 2077 | { | 2074 | { |
| 2075 | Lisp_Object signal = CONSP (error) ? XCAR (error) : Qnil; | ||
| 2078 | Lisp_Object list; | 2076 | Lisp_Object list; |
| 2079 | 2077 | ||
| 2080 | return EQ (signal, Qquit) | 2078 | return EQ (signal, Qquit) |
| 2081 | || (!NILP (Fsymbolp (signal)) | 2079 | || (SYMBOLP (signal) |
| 2082 | && CONSP (list = Fget (signal, Qerror_conditions)) | 2080 | && CONSP (list = Fget (signal, Qerror_conditions)) |
| 2083 | && !NILP (Fmemq (Qquit, list))); | 2081 | && !NILP (Fmemq (Qquit, list))); |
| 2084 | } | 2082 | } |
| @@ -2089,27 +2087,23 @@ signal_quit_p (Lisp_Object signal) | |||
| 2089 | = SIG is nil, and DATA is (SYMBOL . REST-OF-DATA). | 2087 | = SIG is nil, and DATA is (SYMBOL . REST-OF-DATA). |
| 2090 | This is for memory-full errors only. */ | 2088 | This is for memory-full errors only. */ |
| 2091 | static bool | 2089 | static bool |
| 2092 | maybe_call_debugger (Lisp_Object conditions, Lisp_Object sig, Lisp_Object data) | 2090 | maybe_call_debugger (Lisp_Object conditions, Lisp_Object error) |
| 2093 | { | 2091 | { |
| 2094 | Lisp_Object combined_data; | ||
| 2095 | |||
| 2096 | combined_data = Fcons (sig, data); | ||
| 2097 | |||
| 2098 | if ( | 2092 | if ( |
| 2099 | /* Don't try to run the debugger with interrupts blocked. | 2093 | /* Don't try to run the debugger with interrupts blocked. |
| 2100 | The editing loop would return anyway. */ | 2094 | The editing loop would return anyway. */ |
| 2101 | ! input_blocked_p () | 2095 | ! input_blocked_p () |
| 2102 | && NILP (Vinhibit_debugger) | 2096 | && NILP (Vinhibit_debugger) |
| 2103 | /* Does user want to enter debugger for this kind of error? */ | 2097 | /* Does user want to enter debugger for this kind of error? */ |
| 2104 | && (signal_quit_p (sig) | 2098 | && (signal_quit_p (error) |
| 2105 | ? debug_on_quit | 2099 | ? debug_on_quit |
| 2106 | : wants_debugger (Vdebug_on_error, conditions)) | 2100 | : wants_debugger (Vdebug_on_error, conditions)) |
| 2107 | && ! skip_debugger (conditions, combined_data) | 2101 | && ! skip_debugger (conditions, error) |
| 2108 | /* See commentary on definition of | 2102 | /* See commentary on definition of |
| 2109 | `internal-when-entered-debugger'. */ | 2103 | `internal-when-entered-debugger'. */ |
| 2110 | && when_entered_debugger < num_nonmacro_input_events) | 2104 | && when_entered_debugger < num_nonmacro_input_events) |
| 2111 | { | 2105 | { |
| 2112 | call_debugger (list2 (Qerror, combined_data)); | 2106 | call_debugger (list2 (Qerror, error)); |
| 2113 | return 1; | 2107 | return 1; |
| 2114 | } | 2108 | } |
| 2115 | 2109 | ||