Subject: bug#78591: 31.0.50; recursive print invocations broken

From: Pip Cet <[email-masked]>
Date: Mon, 26 May 2025 13:56:02 +0000

There are some comments in print.c about supporting recursive
invocations of print.

However, the function 'print' sets print_depth to 0, and 'print_object'
asserts that print_depth reassumes its original base_depth value when
the function is done running, so virtually all attempts to call the
printing machinery from either printcharfun or print-unreadable-function
will crash.

;; -*- lexical-binding: t; -*-
(prin1
 "hi"
 (lambda (x) (princ (string (capitalize x)))))

;; -*- lexical-binding: t; -*-
(let ((print-unreadable-function
       (lambda (&rest args)
         (prin1-to-string "unreadable"))))
  (prin1 (make-finalizer (lambda nil nil))))

The fix isn't as simple as removing the print_depth = 0 line (a
non-local exit would leave print_depth set to a nonzero value for the
next invocation of print) or removing the base_depth assertion
(print_depth might then become negative while within print_object, with
disastrous results).

I think the best approach is to expand print_context to capture all of
the printing context, including print_depth, being_printed, and
print_buffer, but there are some questions:

1. Should circle detection work for print-within-print?  If so, we need
to "carry over" being_printed and print_depth to the inner invocation.
If we don't, we lose the ability to detect circular structures when
print-within-print is in use.

2. Should we really assume print-unreadable-function is non-recursive?
This code:

      /* Bind `print-unreadable-function' to nil to avoid accidental
	 infinite recursion in the function called.  */
      Lisp_Object func = Vprint_unreadable_function;
      specbind (Qprint_unreadable_function, Qnil);

makes it so, but as the point of print-unreadable-function is presumably
(sometimes) to produce a readable syntax for an unreadable object which
describes it, I don't see why sub-objects of that description cannot
similarly be made readable.

3. Is there, maybe, a simpler solution?  We could arrange to call
print-unreadable-function at print_preprocess time, then print to a
buffer, and finally call printcharfun on each character.  This would
mean that print_object itself would never need to call Lisp.