From 997dbb1284c882730f0a515f49a93babf3fcdcee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mattias=20Engdeg=C3=A5rd?= <mattiase@acm.org>
Date: Mon, 19 Sep 2022 10:55:09 +0200
Subject: [PATCH] Abolish max-specpdl-size (bug#57911)

The max-lisp-eval-depth limit is sufficient to prevent unbounded stack
growth including the specbind stack; simplify matters for the user by
not having them to worry about two different limits.  This change
turns max-specpdl-size into a harmless variable with no effects,
to keep existing code happy.

* lisp/subr.el (max-specpdl-size): Define as an ordinary dynamic variable.
* admin/grammars/Makefile.in:
* doc/lispintro/emacs-lisp-intro.texi (Loops & Recursion):
* doc/lispref/control.texi (Cleanups):
* doc/lispref/edebug.texi (Checking Whether to Stop):
* doc/lispref/eval.texi (Eval):
* doc/lispref/variables.texi (Local Variables):
* doc/misc/calc.texi (Recursion Depth):
Update documentation.
* etc/NEWS: Announce.
* src/eval.c
(FletX): Use safe iteration to guard against circular bindings list.
(syms_of_eval): Remove old max-specpdl-size definition.
(init_eval_once, restore_stack_limits, call_debugger)
(signal_or_quit, grow_specpdl_allocation):
* leim/Makefile.in:
* lisp/Makefile.in:
* lisp/calc/calc-stuff.el (calc-more-recursion-depth)
(calc-less-recursion-depth):
* lisp/calc/calc.el (calc-do):
* lisp/cedet/semantic/ede-grammar.el (ede-proj-makefile-insert-rules):
* lisp/cedet/semantic/grammar.el (semantic-grammar-batch-build-one-package):
* lisp/cus-start.el (standard):
* lisp/emacs-lisp/comp.el (comp--native-compile):
* lisp/emacs-lisp/edebug.el (edebug-max-depth):
(edebug-read-and-maybe-wrap-form, edebug-default-enter):
* lisp/emacs-lisp/regexp-opt.el (regexp-opt):
* lisp/eshell/esh-mode.el (eshell-mode):
* lisp/loadup.el (max-specpdl-size):
* lisp/mh-e/mh-e.el (mh-invisible-headers):
* lisp/net/shr.el (shr-insert-document, shr-descend):
* lisp/play/hanoi.el (hanoi-internal):
* lisp/progmodes/cperl-mode.el:
* src/fileio.c (Fdo_auto_save):
Remove references to and modifications of max-specpdl-size.
---
 admin/grammars/Makefile.in          |  2 +-
 doc/lispintro/emacs-lisp-intro.texi |  5 +--
 doc/lispref/control.texi            |  5 ---
 doc/lispref/edebug.texi             |  5 +--
 doc/lispref/eval.texi               |  5 +--
 doc/lispref/variables.texi          | 21 ----------
 doc/misc/calc.texi                  |  4 --
 etc/NEWS                            |  6 +++
 leim/Makefile.in                    |  2 -
 lisp/Makefile.in                    |  8 ++--
 lisp/calc/calc-stuff.el             |  8 +---
 lisp/calc/calc.el                   |  3 +-
 lisp/cedet/semantic/ede-grammar.el  |  7 ++--
 lisp/cedet/semantic/grammar.el      |  1 -
 lisp/cus-start.el                   |  1 -
 lisp/emacs-lisp/comp.el             |  1 -
 lisp/emacs-lisp/edebug.el           |  6 +--
 lisp/emacs-lisp/regexp-opt.el       |  1 -
 lisp/eshell/esh-mode.el             |  1 -
 lisp/loadup.el                      |  4 +-
 lisp/mh-e/mh-e.el                   |  4 +-
 lisp/net/shr.el                     | 62 +++++++++++++----------------
 lisp/play/hanoi.el                  |  5 +--
 lisp/progmodes/cperl-mode.el        |  1 -
 lisp/subr.el                        |  7 ++++
 src/eval.c                          | 60 +++-------------------------
 src/fileio.c                        |  5 ---
 27 files changed, 67 insertions(+), 173 deletions(-)

diff --git a/admin/grammars/Makefile.in b/admin/grammars/Makefile.in
index 4ca88982cd..178c79b7a0 100644
--- a/admin/grammars/Makefile.in
+++ b/admin/grammars/Makefile.in
@@ -35,7 +35,7 @@ top_builddir =
 
 EMACS = ${top_builddir}/src/emacs
 emacs = "${EMACS}" -batch --no-site-file --no-site-lisp \
-  --eval '(setq max-specpdl-size 5000)' --eval '(setq load-prefer-newer t)'
+  --eval '(setq load-prefer-newer t)'
 
 make_bovine = ${emacs} -l semantic/bovine/grammar -f bovine-batch-make-parser
 make_wisent = ${emacs} -l semantic/wisent/grammar -f wisent-batch-make-parser
diff --git a/doc/lispintro/emacs-lisp-intro.texi b/doc/lispintro/emacs-lisp-intro.texi
index 47a5a870fd..df8fa2f8e7 100644
--- a/doc/lispintro/emacs-lisp-intro.texi
+++ b/doc/lispintro/emacs-lisp-intro.texi
@@ -10100,9 +10100,8 @@ Loops & Recursion
 frugal of mental resources---sometimes use considerable computer
 resources.  Emacs was designed to run on machines that we now consider
 limited and its default settings are conservative.  You may want to
-increase the values of @code{max-specpdl-size} and
-@code{max-lisp-eval-depth}.  In my @file{.emacs} file, I set them to
-15 and 30 times their default value.}.
+increase the value of @code{max-lisp-eval-depth}.  In my @file{.emacs}
+file, I set it to 30 times its default value.}.
 
 @menu
 * while::                       Causing a stretch of code to repeat.
diff --git a/doc/lispref/control.texi b/doc/lispref/control.texi
index d4520ebdee..ee2acdb002 100644
--- a/doc/lispref/control.texi
+++ b/doc/lispref/control.texi
@@ -2366,11 +2366,6 @@ Cleanups
 guaranteed to evaluate the rest of them.  If the failure of one of the
 @var{cleanup-forms} has the potential to cause trouble, then protect
 it with another @code{unwind-protect} around that form.
-
-The number of currently active @code{unwind-protect} forms counts,
-together with the number of local variable bindings, against the limit
-@code{max-specpdl-size} (@pxref{Definition of max-specpdl-size,, Local
-Variables}).
 @end defspec
 
   For example, here we make an invisible buffer for temporary use, and
diff --git a/doc/lispref/edebug.texi b/doc/lispref/edebug.texi
index 56f7b7bdfa..6a51489d8a 100644
--- a/doc/lispref/edebug.texi
+++ b/doc/lispref/edebug.texi
@@ -1032,9 +1032,8 @@ Checking Whether to Stop
 @itemize @bullet
 @item
 @vindex edebug-max-depth
-@code{max-lisp-eval-depth} (@pxref{Eval}) and @code{max-specpdl-size}
-(@pxref{Local Variables}) are both increased to reduce Edebug's impact
-on the stack.  You could, however, still run out of stack space when
+@code{max-lisp-eval-depth} (@pxref{Eval}) is increased to reduce Edebug's
+impact on the stack.  You could, however, still run out of stack space when
 using Edebug.  You can also enlarge the value of
 @code{edebug-max-depth} if Edebug reaches the limit of recursion depth
 instrumenting code that contains very large quoted lists.
diff --git a/doc/lispref/eval.texi b/doc/lispref/eval.texi
index 6e29a5403f..11c321b32e 100644
--- a/doc/lispref/eval.texi
+++ b/doc/lispref/eval.texi
@@ -830,7 +830,7 @@ Eval
 @code{apply}, and @code{funcall} before an error is signaled (with error
 message @code{"Lisp nesting exceeds max-lisp-eval-depth"}).
 
-This limit, with the associated error when it is exceeded, is one way
+This limit, with the associated error when it is exceeded, is how
 Emacs Lisp avoids infinite recursion on an ill-defined function.  If
 you increase the value of @code{max-lisp-eval-depth} too much, such
 code can cause stack overflow instead.  On some systems, this overflow
@@ -851,9 +851,6 @@ Eval
 reached.  Entry to the Lisp debugger increases the value, if there is
 little room left, to make sure the debugger itself has room to
 execute.
-
-@code{max-specpdl-size} provides another limit on nesting.
-@xref{Definition of max-specpdl-size,, Local Variables}.
 @end defopt
 
 @defvar values
diff --git a/doc/lispref/variables.texi b/doc/lispref/variables.texi
index 975e945b34..ccd19630bf 100644
--- a/doc/lispref/variables.texi
+++ b/doc/lispref/variables.texi
@@ -358,27 +358,6 @@ Local Variables
 like ordinary local bindings, but they are localized depending on
 where you are in Emacs.
 
-@defopt max-specpdl-size
-@anchor{Definition of max-specpdl-size}
-@cindex variable limit error
-@cindex evaluation error
-@cindex infinite recursion
-This variable defines the limit on the total number of local variable
-bindings and @code{unwind-protect} cleanups (@pxref{Cleanups,,
-Cleaning Up from Nonlocal Exits}) that are allowed before Emacs
-signals an error (with data @code{"Variable binding depth exceeds
-max-specpdl-size"}).
-
-This limit, with the associated error when it is exceeded, is one way
-that Lisp avoids infinite recursion on an ill-defined function.
-@code{max-lisp-eval-depth} provides another limit on depth of nesting.
-@xref{Definition of max-lisp-eval-depth,, Eval}.
-
-The default value is 2500.  Entry to the Lisp debugger increases the
-value, if there is little room left, to make sure the debugger itself
-has room to execute.
-@end defopt
-
 @node Void Variables
 @section When a Variable is Void
 @cindex @code{void-variable} error
diff --git a/doc/misc/calc.texi b/doc/misc/calc.texi
index 98f59b89c0..89a340e734 100644
--- a/doc/misc/calc.texi
+++ b/doc/misc/calc.texi
@@ -10392,7 +10392,6 @@ Recursion Depth
 @cindex Recursion depth
 @cindex ``Computation got stuck'' message
 @cindex @code{max-lisp-eval-depth}
-@cindex @code{max-specpdl-size}
 Calc uses recursion in many of its calculations.  Emacs Lisp keeps a
 variable @code{max-lisp-eval-depth} which limits the amount of recursion
 possible in an attempt to recover from program bugs.  If a calculation
@@ -10406,9 +10405,6 @@ Recursion Depth
 decreases this limit by a factor of two, down to a minimum value of 200.
 The default value is 1000.
 
-These commands also double or halve @code{max-specpdl-size}, another
-internal Lisp recursion limit.  The minimum value for this limit is 600.
-
 @node Caches
 @subsection Caches
 
diff --git a/etc/NEWS b/etc/NEWS
index a739d74b65..63ceb7ba34 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -3806,6 +3806,12 @@ the same but works by modifying LIST destructively.
 ---
 ** 'string-split' is now an alias for 'split-string'.
 
++++
+** 'max-specpdl-size' is no longer used.
+Now 'max-lisp-eval-depth' alone is used for limiting Lisp recursion
+and stack usage.  'max-specpdl-size' is still present as a plain
+variable for compatibility but its limiting powers have been taken away.
+
 
 * Changes in Emacs 29.1 on Non-Free Operating Systems
 
diff --git a/leim/Makefile.in b/leim/Makefile.in
index 29b9f3b2f8..fbd733b7f6 100644
--- a/leim/Makefile.in
+++ b/leim/Makefile.in
@@ -128,7 +128,6 @@ leim-list.el:
 ${leimdir}/leim-list.el: ${srcdir}/leim-ext.el ${TIT_MISC}
 	$(AM_V_GEN)rm -f $@
 	$(AM_V_at)${RUN_EMACS} -l international/quail \
-	  --eval "(setq max-specpdl-size 5000)" \
 	  --eval "(update-leim-list-file (unmsys--file-name \"${leimdir}\"))"
 	$(AM_V_at)sed -n -e '/^[^;]/p' -e 's/^;\(;*\)inc /;\1 /p' < $< >> $@
 
@@ -139,7 +138,6 @@ .PHONY:
 generate-ja-dic: ${leimdir}/ja-dic/ja-dic.el
 ${leimdir}/ja-dic/ja-dic.el: $(srcdir)/SKK-DIC/SKK-JISYO.L
 	$(AM_V_GEN)$(RUN_EMACS) -batch -l ja-dic-cnv \
-	  --eval "(setq max-specpdl-size 5000)" \
 	  -f batch-skkdic-convert -dir "$(leimdir)/ja-dic" $(JA_DIC_NO_REDUCTION_OPTION) "$<"
 
 ${srcdir}/../lisp/language/pinyin.el: ${srcdir}/MISC-DIC/pinyin.map
diff --git a/lisp/Makefile.in b/lisp/Makefile.in
index c73a623cce..bcf4a3146d 100644
--- a/lisp/Makefile.in
+++ b/lisp/Makefile.in
@@ -70,9 +70,7 @@ BYTE_COMPILE_FLAGS =
   --eval "(setq load-prefer-newer t byte-compile-warnings 'all)" \
 	$(BYTE_COMPILE_EXTRA_FLAGS)
 # ... but we must prefer .elc files for those in the early bootstrap.
-# A larger `max-specpdl-size' is needed for emacs-lisp/comp.el.
-compile-first: BYTE_COMPILE_FLAGS = \
-  --eval '(setq max-specpdl-size 5000)' $(BYTE_COMPILE_EXTRA_FLAGS)
+compile-first: BYTE_COMPILE_FLAGS = $(BYTE_COMPILE_EXTRA_FLAGS)
 
 # Files to compile before others during a bootstrap.  This is done to
 # speed up the bootstrap process.  They're ordered by size, so we use
@@ -342,8 +340,8 @@ compile-first:
 
 .PHONY: compile-targets
 # TARGETS is set dynamically in the recursive call from 'compile-main'.
-# Do not build comp.el unless necessary not to exceed max-specpdl-size and
-# max-lisp-eval-depth in normal builds.
+# Do not build comp.el unless necessary not to exceed max-lisp-eval-depth
+# in normal builds.
 ifneq ($(HAVE_NATIVE_COMP),yes)
 compile-targets: $(filter-out ./emacs-lisp/comp-cstr.elc,$(filter-out ./emacs-lisp/comp.elc,$(TARGETS)))
 else
diff --git a/lisp/calc/calc-stuff.el b/lisp/calc/calc-stuff.el
index 0e8ea42bed..758b920184 100644
--- a/lisp/calc/calc-stuff.el
+++ b/lisp/calc/calc-stuff.el
@@ -52,18 +52,14 @@ calc-more-recursion-depth
        (calc-less-recursion-depth n)
      (let ((n (if n (prefix-numeric-value n) 2)))
        (if (> n 1)
-	   (setq max-specpdl-size (* max-specpdl-size n)
-		 max-lisp-eval-depth (* max-lisp-eval-depth n))))
+	   (setq max-lisp-eval-depth (* max-lisp-eval-depth n))))
      (message "max-lisp-eval-depth is now %d" max-lisp-eval-depth))))
 
 (defun calc-less-recursion-depth (n)
   (interactive "P")
   (let ((n (if n (prefix-numeric-value n) 2)))
     (if (> n 1)
-	(setq max-specpdl-size
-	      (max (/ max-specpdl-size n) 600)
-	      max-lisp-eval-depth
-	      (max (/ max-lisp-eval-depth n) 200))))
+	(setq max-lisp-eval-depth (max (/ max-lisp-eval-depth n) 200))))
   (message "max-lisp-eval-depth is now %d" max-lisp-eval-depth))
 
 
diff --git a/lisp/calc/calc.el b/lisp/calc/calc.el
index 5077c8c852..c0f87ad3d4 100644
--- a/lisp/calc/calc.el
+++ b/lisp/calc/calc.el
@@ -1625,8 +1625,7 @@ calc-do
 	  (error
 	   (if (and (eq (car err) 'error)
 		    (stringp (nth 1 err))
-		    (string-match "max-specpdl-size\\|max-lisp-eval-depth"
-				  (nth 1 err)))
+		    (string-search "max-lisp-eval-depth" (nth 1 err)))
                (error (substitute-command-keys
                        "Computation got stuck or ran too long.  Type \\`M' to increase the limit"))
 	     (setq calc-aborted-prefix nil)
diff --git a/lisp/cedet/semantic/ede-grammar.el b/lisp/cedet/semantic/ede-grammar.el
index ff9f991ff4..40ff8fc86d 100644
--- a/lisp/cedet/semantic/ede-grammar.el
+++ b/lisp/cedet/semantic/ede-grammar.el
@@ -177,10 +177,9 @@ ede-proj-makefile-insert-variables
 
 (cl-defmethod ede-proj-makefile-insert-rules :after ((this semantic-ede-proj-target-grammar))
     "Insert rules needed by THIS target.
-This raises `max-specpdl-size' and `max-lisp-eval-depth', which can be
-needed for the compilation of the resulting parsers."
-    (insert (format "%s: EMACSFLAGS+= --eval '(setq max-specpdl-size 1500 \
-max-lisp-eval-depth 700)'\n"
+This raises `max-lisp-eval-depth', which can be needed for the compilation
+of the resulting parsers."
+    (insert (format "%s: EMACSFLAGS+= --eval '(setq max-lisp-eval-depth 700)'\n"
 		    (oref this name))))
 
 (cl-defmethod ede-proj-makefile-insert-dist-dependencies ((this semantic-ede-proj-target-grammar))
diff --git a/lisp/cedet/semantic/grammar.el b/lisp/cedet/semantic/grammar.el
index 72037f4710..8ba0e346ff 100644
--- a/lisp/cedet/semantic/grammar.el
+++ b/lisp/cedet/semantic/grammar.el
@@ -1009,7 +1009,6 @@ semantic-grammar-batch-build-one-package
              packagename (byte-compile-dest-file packagename))
             (let (;; Some complex grammar table expressions need a few
                   ;; more resources than the default.
-                  (max-specpdl-size    (max 3000 max-specpdl-size))
                   (max-lisp-eval-depth (max 1000 max-lisp-eval-depth))
                   )
               ;; byte compile the resultant file
diff --git a/lisp/cus-start.el b/lisp/cus-start.el
index 0e1cb4589d..d7fb56c985 100644
--- a/lisp/cus-start.el
+++ b/lisp/cus-start.el
@@ -251,7 +251,6 @@ minibuffer-prompt-properties--setter
 	     ;; emacs.c
 	     (report-emacs-bug-address emacsbug string)
 	     ;; eval.c
-	     (max-specpdl-size limits integer)
 	     (max-lisp-eval-depth limits integer)
 	     (max-mini-window-height limits
 				     (choice (const :tag "quarter screen" nil)
diff --git a/lisp/emacs-lisp/comp.el b/lisp/emacs-lisp/comp.el
index a9087313b1..35acbff9b1 100644
--- a/lisp/emacs-lisp/comp.el
+++ b/lisp/emacs-lisp/comp.el
@@ -4044,7 +4044,6 @@ comp--native-compile
             (list "Not a function symbol or file" function-or-file)))
   (catch 'no-native-compile
     (let* ((print-symbols-bare t)
-           (max-specpdl-size (max max-specpdl-size 5000))
            (data function-or-file)
            (comp-native-compiling t)
            (byte-native-qualities nil)
diff --git a/lisp/emacs-lisp/edebug.el b/lisp/emacs-lisp/edebug.el
index 31c05057bf..67704bdb51 100644
--- a/lisp/emacs-lisp/edebug.el
+++ b/lisp/emacs-lisp/edebug.el
@@ -129,7 +129,7 @@ edebug-max-depth
 containing very large quoted lists, it may reach this limit and give
 the error message \"Too deep - perhaps infinite loop in spec?\".
 Make this limit larger to countermand that, but you may also need to
-increase `max-lisp-eval-depth' and `max-specpdl-size'."
+increase `max-lisp-eval-depth'."
   :type 'integer
   :version "26.1")
 
@@ -1107,8 +1107,7 @@ edebug-read-and-maybe-wrap-form
 	edebug-best-error
 	edebug-error-point
 	;; Do this once here instead of several times.
-	(max-lisp-eval-depth (+ 800 max-lisp-eval-depth))
-	(max-specpdl-size (+ 2000 max-specpdl-size)))
+	(max-lisp-eval-depth (+ 800 max-lisp-eval-depth)))
     (let ((no-match
            (catch 'no-match
              (setq result (edebug-read-and-maybe-wrap-form1))
@@ -2317,7 +2316,6 @@ edebug-default-enter
               ;; but not inside an unwind-protect.
               ;; Doing it here also keeps it from growing too large.
               (max-lisp-eval-depth (+ 100 max-lisp-eval-depth)) ; too much??
-              (max-specpdl-size (+ 200 max-specpdl-size))
 
               (debugger edebug-debugger) ; only while edebug is active.
               (edebug-outside-debug-on-error debug-on-error)
diff --git a/lisp/emacs-lisp/regexp-opt.el b/lisp/emacs-lisp/regexp-opt.el
index cae5dd00d1..4d5a39458d 100644
--- a/lisp/emacs-lisp/regexp-opt.el
+++ b/lisp/emacs-lisp/regexp-opt.el
@@ -133,7 +133,6 @@ regexp-opt
   (save-match-data
     ;; Recurse on the sorted list.
     (let* ((max-lisp-eval-depth 10000)
-	   (max-specpdl-size 10000)
 	   (completion-ignore-case nil)
 	   (completion-regexp-list nil)
 	   (open (cond ((stringp paren) paren) (paren "\\(")))
diff --git a/lisp/eshell/esh-mode.el b/lisp/eshell/esh-mode.el
index 69069183a3..8f11e6f04a 100644
--- a/lisp/eshell/esh-mode.el
+++ b/lisp/eshell/esh-mode.el
@@ -331,7 +331,6 @@ eshell-mode
   (setq-local require-final-newline nil)
 
   (setq-local max-lisp-eval-depth (max 3000 max-lisp-eval-depth))
-  (setq-local max-specpdl-size (max 6000 max-lisp-eval-depth))
 
   (setq-local eshell-last-input-start (point-marker))
   (setq-local eshell-last-input-end (point-marker))
diff --git a/lisp/loadup.el b/lisp/loadup.el
index 634a331436..c01c827a75 100644
--- a/lisp/loadup.el
+++ b/lisp/loadup.el
@@ -244,9 +244,7 @@
 (load "language/indonesian")
 
 (load "indent")
-(let ((max-specpdl-size (max max-specpdl-size 1800)))
-  ;; A particularly demanding file to load; 1600 does not seem to be enough.
-  (load "emacs-lisp/cl-generic"))
+(load "emacs-lisp/cl-generic")
 (load "simple")
 (load "emacs-lisp/seq")
 (load "emacs-lisp/nadvice")
diff --git a/lisp/mh-e/mh-e.el b/lisp/mh-e/mh-e.el
index 0ad934107d..9a04d89097 100644
--- a/lisp/mh-e/mh-e.el
+++ b/lisp/mh-e/mh-e.el
@@ -2831,9 +2831,7 @@ mh-invisible-headers
         (setq mh-invisible-header-fields-compiled
               (concat
                "^"
-               ;; workaround for insufficient default
-               (let ((max-specpdl-size 1000))
-                 (regexp-opt fields t))))
+               (regexp-opt fields t)))
       (setq mh-invisible-header-fields-compiled nil))))
 
 ;; Compile invisible header fields.
diff --git a/lisp/net/shr.el b/lisp/net/shr.el
index 54ce9b1a41..d56420eb02 100644
--- a/lisp/net/shr.el
+++ b/lisp/net/shr.el
@@ -373,7 +373,6 @@ shr-insert-document
 		 shr-width
 	       (* shr-width (frame-char-width)))
            (shr--window-width)))
-        (max-specpdl-size max-specpdl-size)
         (shr--link-targets nil)
         (hscroll (window-hscroll))
         ;; `bidi-display-reordering' is supposed to be only used for
@@ -625,41 +624,34 @@ shr-descend
 	(shr-stylesheet shr-stylesheet)
 	(shr-depth (1+ shr-depth))
 	(start (point)))
-    ;; shr uses many frames per nested node.
-    (if (and (> shr-depth (/ max-specpdl-size 15))
-             (not (and shr-offer-extend-specpdl
-                       (y-or-n-p "Too deeply nested to render properly; increase `max-specpdl-size'?")
-                       (setq max-specpdl-size (* max-specpdl-size 2)))))
-        (setq shr-warning
-              "Not rendering the complete page because of too-deep nesting")
+    (when style
+      (if (string-match-p "color\\|display\\|border-collapse" style)
+	  (setq shr-stylesheet (nconc (shr-parse-style style)
+				      shr-stylesheet))
+	(setq style nil)))
+    ;; If we have a display:none, then just ignore this part of the DOM.
+    (unless (or (equal (cdr (assq 'display shr-stylesheet)) "none")
+                (and shr-discard-aria-hidden
+                     (equal (dom-attr dom 'aria-hidden) "true")))
+      ;; We don't use shr-indirect-call here, since shr-descend is
+      ;; the central bit of shr.el, and should be as fast as
+      ;; possible.  Having one more level of indirection with its
+      ;; negative effect on performance is deemed unjustified in
+      ;; this case.
+      (cond (external
+             (funcall external dom))
+            ((fboundp function)
+             (funcall function dom))
+            (t
+             (shr-generic dom)))
+      (when-let ((id (dom-attr dom 'id)))
+        (push (cons id (set-marker (make-marker) start)) shr--link-targets))
+      ;; If style is set, then this node has set the color.
       (when style
-	(if (string-match-p "color\\|display\\|border-collapse" style)
-	    (setq shr-stylesheet (nconc (shr-parse-style style)
-					shr-stylesheet))
-	  (setq style nil)))
-      ;; If we have a display:none, then just ignore this part of the DOM.
-      (unless (or (equal (cdr (assq 'display shr-stylesheet)) "none")
-                  (and shr-discard-aria-hidden
-                       (equal (dom-attr dom 'aria-hidden) "true")))
-        ;; We don't use shr-indirect-call here, since shr-descend is
-        ;; the central bit of shr.el, and should be as fast as
-        ;; possible.  Having one more level of indirection with its
-        ;; negative effect on performance is deemed unjustified in
-        ;; this case.
-        (cond (external
-               (funcall external dom))
-              ((fboundp function)
-               (funcall function dom))
-              (t
-               (shr-generic dom)))
-        (when-let ((id (dom-attr dom 'id)))
-          (push (cons id (set-marker (make-marker) start)) shr--link-targets))
-	;; If style is set, then this node has set the color.
-	(when style
-	  (shr-colorize-region
-	   start (point)
-	   (cdr (assq 'color shr-stylesheet))
-	   (cdr (assq 'background-color shr-stylesheet))))))))
+	(shr-colorize-region
+	 start (point)
+	 (cdr (assq 'color shr-stylesheet))
+	 (cdr (assq 'background-color shr-stylesheet)))))))
 
 (defun shr-fill-text (text)
   (if (zerop (length text))
diff --git a/lisp/play/hanoi.el b/lisp/play/hanoi.el
index 58fb82b6ed..1a4b6dbeb1 100644
--- a/lisp/play/hanoi.el
+++ b/lisp/play/hanoi.el
@@ -149,10 +149,9 @@ hanoi-internal
   (setq show-trailing-whitespace nil)
   (unwind-protect
       (let*
-	  (;; These lines can cause Emacs to crash if you ask for too
-	   ;; many rings.  If you uncomment them, on most systems you
+	  (;; This line can cause Emacs to crash if you ask for too
+	   ;; many rings.  If you uncomment it, on most systems you
 	   ;; can get 10,000+ rings.
-	   ;;(max-specpdl-size (max max-specpdl-size (* nrings 15)))
 	   ;;(max-lisp-eval-depth (max max-lisp-eval-depth (+ nrings 20)))
 	   (vert (not hanoi-horizontal-flag))
 	   (pole-width (length (format "%d" (max 0 (1- nrings)))))
diff --git a/lisp/progmodes/cperl-mode.el b/lisp/progmodes/cperl-mode.el
index 614ee60fa0..c3704a05db 100644
--- a/lisp/progmodes/cperl-mode.el
+++ b/lisp/progmodes/cperl-mode.el
@@ -3718,7 +3718,6 @@ cperl-process-here-doc
           overshoot
           warning-message)))
 
-;; Debugging this may require (setq max-specpdl-size 2000)...
 (defun cperl-find-pods-heres (&optional min max non-inter end ignore-max end-of-here-doc)
   "Scan the buffer for hard-to-parse Perl constructions.
 If `cperl-pod-here-fontify' is non-nil after evaluation,
diff --git a/lisp/subr.el b/lisp/subr.el
index d7cdc28abb..0e8cb2fcd6 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -376,6 +376,13 @@ ignore-error
 
 ;;;; Basic Lisp functions.
 
+(defvar max-specpdl-size 2500
+  "Former limit on specbindings, now without effect.
+This variable used to limit the size of the specpdl stack which,
+among other things, held dynamic variable bindings and `unwind-protect'
+activations.  To prevent runaway recursion, use `max-lisp-eval-lisp-depth'
+instead; it will indirectly limit the specpdl stack size as well.")
+
 (defvar gensym-counter 0
   "Number used to construct the name of the next symbol created by `gensym'.")
 
diff --git a/src/eval.c b/src/eval.c
index bd414fb868..7da1d8fb98 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -211,9 +211,7 @@ backtrace_thread_next (struct thread_state *tstate, union specbinding *pdl)
 void
 init_eval_once (void)
 {
-  /* Don't forget to update docs
-     (lispref nodes "Local Variables" and "Eval").  */
-  max_specpdl_size = 2500;
+  /* Don't forget to update docs (lispref node "Eval").  */
   max_lisp_eval_depth = 1600;
   Vrun_hooks = Qnil;
   pdumper_do_now_and_after_load (init_eval_once_for_pdumper);
@@ -265,8 +263,7 @@ max_ensure_room (intmax_t *m, intmax_t a, intmax_t b)
 static void
 restore_stack_limits (Lisp_Object data)
 {
-  integer_to_intmax (XCAR (data), &max_specpdl_size);
-  integer_to_intmax (XCDR (data), &max_lisp_eval_depth);
+  integer_to_intmax (data, &max_lisp_eval_depth);
 }
 
 /* Call the Lisp debugger, giving it argument ARG.  */
@@ -278,9 +275,6 @@ call_debugger (Lisp_Object arg)
   specpdl_ref count = SPECPDL_INDEX ();
   Lisp_Object val;
   intmax_t old_depth = max_lisp_eval_depth;
-  /* Do not allow max_specpdl_size less than actual depth (Bug#16603).  */
-  ptrdiff_t counti = specpdl_ref_to_count (count);
-  intmax_t old_max = max (max_specpdl_size, counti);
 
   /* The previous value of 40 is too small now that the debugger
      prints using cl-prin1 instead of prin1.  Printing lists nested 8
@@ -288,20 +282,8 @@ call_debugger (Lisp_Object arg)
      currently requires 77 additional frames.  See bug#31919.  */
   max_ensure_room (&max_lisp_eval_depth, lisp_eval_depth, 100);
 
-  /* While debugging Bug#16603, previous value of 100 was found
-     too small to avoid specpdl overflow in the debugger itself.  */
-  max_ensure_room (&max_specpdl_size, counti, 200);
-
-  if (old_max == counti)
-    {
-      /* We can enter the debugger due to specpdl overflow (Bug#16603).  */
-      specpdl_ptr--;
-      grow_specpdl ();
-    }
-
   /* Restore limits after leaving the debugger.  */
-  record_unwind_protect (restore_stack_limits,
-			 Fcons (make_int (old_max), make_int (old_depth)));
+  record_unwind_protect (restore_stack_limits, make_int (old_depth));
 
 #ifdef HAVE_WINDOW_SYSTEM
   if (display_hourglass_p)
@@ -933,12 +915,9 @@ DEFUN ("let*", FletX, SletX, 1, UNEVALLED, 0,
   lexenv = Vinternal_interpreter_environment;
 
   Lisp_Object varlist = XCAR (args);
-  while (CONSP (varlist))
+  FOR_EACH_TAIL (varlist)
     {
-      maybe_quit ();
-
       elt = XCAR (varlist);
-      varlist = XCDR (varlist);
       if (SYMBOLP (elt))
 	{
 	  var = elt;
@@ -1752,8 +1731,6 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit)
     {
       /* Edebug takes care of restoring these variables when it exits.  */
       max_ensure_room (&max_lisp_eval_depth, lisp_eval_depth, 20);
-      ptrdiff_t counti = specpdl_ref_to_count (SPECPDL_INDEX ());
-      max_ensure_room (&max_specpdl_size, counti, 40);
 
       call2 (Vsignal_hook_function, error_symbol, data);
     }
@@ -1822,8 +1799,6 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit)
     {
       max_ensure_room (&max_lisp_eval_depth, lisp_eval_depth, 100);
       specpdl_ref count = SPECPDL_INDEX ();
-      ptrdiff_t counti = specpdl_ref_to_count (count);
-      max_ensure_room (&max_specpdl_size, counti, 200);
       specbind (Qdebugger, Qdebug_early);
       call_debugger (list2 (Qerror, Fcons (error_symbol, data)));
       unbind_to (count, Qnil);
@@ -1839,12 +1814,10 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit)
     {
       max_ensure_room (&max_lisp_eval_depth, lisp_eval_depth, 100);
       specpdl_ref count = SPECPDL_INDEX ();
-      ptrdiff_t counti = specpdl_ref_to_count (count);
       AUTO_STRING (redisplay_trace, "*Redisplay_trace*");
       Lisp_Object redisplay_trace_buffer;
       AUTO_STRING (gap, "\n\n\n\n"); /* Separates things in *Redisplay-trace* */
       Lisp_Object delayed_warning;
-      max_ensure_room (&max_specpdl_size, counti, 200);
       redisplay_trace_buffer = Fget_buffer_create (redisplay_trace, Qnil);
       current_buffer = XBUFFER (redisplay_trace_buffer);
       if (!backtrace_yet) /* Are we on the first backtrace of the command?  */
@@ -2376,17 +2349,12 @@ grow_specpdl_allocation (void)
   eassert (specpdl_ptr == specpdl_end);
 
   specpdl_ref count = SPECPDL_INDEX ();
-  ptrdiff_t max_size = min (max_specpdl_size, PTRDIFF_MAX - 1000);
+  ptrdiff_t max_size = PTRDIFF_MAX - 1000;
   union specbinding *pdlvec = specpdl - 1;
   ptrdiff_t size = specpdl_end - specpdl;
   ptrdiff_t pdlvecsize = size + 1;
   if (max_size <= size)
-    {
-      if (max_specpdl_size < 400)
-	max_size = max_specpdl_size = 400;
-      if (max_size <= size)
-	xsignal0 (Qexcessive_variable_binding);
-    }
+    xsignal0 (Qexcessive_variable_binding);  /* Can't happen, essentially.  */
   pdlvec = xpalloc (pdlvec, &pdlvecsize, 1, max_size + 1, sizeof *specpdl);
   specpdl = pdlvec + 1;
   specpdl_end = specpdl + pdlvecsize - 1;
@@ -4229,22 +4197,6 @@ get_backtrace (Lisp_Object array)
 void
 syms_of_eval (void)
 {
-  DEFVAR_INT ("max-specpdl-size", max_specpdl_size,
-	      doc: /* Limit on number of Lisp variable bindings and `unwind-protect's.
-
-If Lisp code tries to use more bindings than this amount, an error is
-signaled.
-
-You can safely increase this variable substantially if the default
-value proves inconveniently small.  However, if you increase it too
-much, Emacs could run out of memory trying to make the stack bigger.
-Note that this limit may be silently increased by the debugger if
-`debug-on-error' or `debug-on-quit' is set.
-
-\"spec\" is short for \"special variables\", i.e., dynamically bound
-variables.  \"PDL\" is short for \"push-down list\", which is an old
-term for \"stack\".  */);
-
   DEFVAR_INT ("max-lisp-eval-depth", max_lisp_eval_depth,
 	      doc: /* Limit on depth in `eval', `apply' and `funcall' before error.
 
diff --git a/src/fileio.c b/src/fileio.c
index 6efea8ac36..dd7f85ec97 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -6019,11 +6019,6 @@ DEFUN ("do-auto-save", Fdo_auto_save, Sdo_auto_save, 0, 2, "",
   bool old_message_p = 0;
   struct auto_save_unwind auto_save_unwind;
 
-  intmax_t sum = INT_ADD_WRAPV (specpdl_end - specpdl, 40, &sum)
-                 ? INTMAX_MAX : sum;
-  if (max_specpdl_size < sum)
-    max_specpdl_size = sum;
-
   if (minibuf_level)
     no_message = Qt;
 
-- 
2.32.0 (Apple Git-132)

