Subject: bug#70368: [PATCH] Use a dedicated type to represent interpreted-function values

From: Stefan Monnier <[email-masked]>
Date: Sun, 14 Apr 2024 12:18:24 -0400

Here's the corresponding additional patch (not yet merged into the main patch).

        Stefan
diff --git a/doc/lispref/compile.texi b/doc/lispref/compile.texi
index 00602198da5..b5f3b07f083 100644
--- a/doc/lispref/compile.texi
+++ b/doc/lispref/compile.texi
@@ -37,7 +37,7 @@ Byte Compilation
 * Docs and Compilation::        Dynamic loading of documentation strings.
 * Eval During Compile::         Code to be evaluated when you compile.
 * Compiler Errors::             Handling compiler error messages.
-* Byte-Code Objects::           The data type used for byte-compiled functions.
+* Closure Objects::             The data type used for byte-compiled functions.
 * Disassembly::                 Disassembling byte-code; how to read byte-code.
 @end menu
 
@@ -120,7 +120,7 @@ Compilation Functions
 definition of @var{symbol} must be the actual code for the function;
 @code{byte-compile} does not handle function indirection.  The return
 value is the byte-code function object which is the compiled
-definition of @var{symbol} (@pxref{Byte-Code Objects}).
+definition of @var{symbol} (@pxref{Closure Objects}).
 
 @example
 @group
@@ -487,21 +487,22 @@ Compiler Errors
 using @code{error}.  If so, set @code{byte-compile-error-on-warn} to a
 non-@code{nil} value.
 
-@node Byte-Code Objects
-@section Byte-Code Function Objects
+@node Closure Objects
+@section Closure Function Objects
 @cindex compiled function
 @cindex byte-code function
 @cindex byte-code object
 
-  Byte-compiled functions have a special data type: they are
-@dfn{byte-code function objects}.  Whenever such an object appears as
-a function to be called, Emacs uses the byte-code interpreter to
-execute the byte-code.
+  Byte-compiled functions use a special data type: they are closures.
+Closures are used both for byte-compiled Lisp functions as well as for
+interpreted Lisp functions.  Whenever such an object appears as
+a function to be called, Emacs uses the appropriate interpreter to
+execute either the byte-code or the non-compiled Lisp code.
 
-  Internally, a byte-code function object is much like a vector; its
+  Internally, a closure is much like a vector; its
 elements can be accessed using @code{aref}.  Its printed
 representation is like that for a vector, with an additional @samp{#}
-before the opening @samp{[}.  It must have at least four elements;
+before the opening @samp{[}.  It must have at least three elements;
 there is no maximum number, but only the first six elements have any
 normal use.  They are:
 
@@ -515,20 +516,28 @@ Byte-Code Objects
 the argument list uses @code{&rest}, then bit 7 is set; otherwise it's
 cleared.
 
-If @var{argdesc} is a list, the arguments will be dynamically bound
+When the closure is a byte-code function,
+if @var{argdesc} is a list, the arguments will be dynamically bound
 before executing the byte code.  If @var{argdesc} is an integer, the
 arguments will be instead pushed onto the stack of the byte-code
 interpreter, before executing the code.
 
-@item byte-code
-The string containing the byte-code instructions.
+@item code
+For interpreted functions, this element is the (non-empty) list of Lisp
+forms that make up the function's body.  For byte-compiled functions, it
+is the string containing the byte-code instructions.
 
 @item constants
-The vector of Lisp objects referenced by the byte code.  These include
-symbols used as function names and variable names.
+For byte-compiled functions, this holds the vector of Lisp objects
+referenced by the byte code.  These include symbols used as function
+names and variable names.
+For interpreted functions, this is nil if the function is using the old
+dynamically scoped dialect of Emacs Lisp, and otherwise it holds the
+function's lexical environment.
 
 @item stacksize
-The maximum stack size this function needs.
+The maximum stack size this function needs.  This element is left unused
+for interpreted functions.
 
 @item docstring
 The documentation string (if any); otherwise, @code{nil}.  The value may
@@ -558,8 +567,8 @@ Byte-Code Objects
 @code{make-byte-code}:
 
 @defun make-byte-code &rest elements
-This function constructs and returns a byte-code function object
-with @var{elements} as its elements.
+This function constructs and returns a closure which represents the
+byte-code function object with @var{elements} as its elements.
 @end defun
 
   You should not try to come up with the elements for a byte-code
@@ -567,6 +576,20 @@ Byte-Code Objects
 when you call the function.  Always leave it to the byte compiler to
 create these objects; it makes the elements consistent (we hope).
 
+The primitive way to create an interpreted function is with
+@code{make-interpreted-closure}:
+
+@defun make-interpreted-closure args body env &optional docstring iform
+This function constructs and returns a closure representing the
+interpreted function with arguments @var{args} and whose body is made of
+@var{body} which must be a non-nil list of Lisp forms.  @var{env} is the
+lexical environment in the same form as used with @code{eval}
+(@pxref{Eval}).  The documentation @var{docstring} if non-nil should be
+a string, and the interactive form @var{iform} if non-nil should be of
+the form @code{(interactive @var{arg-descriptor})} (@pxref{Using
+Interactive}).
+@end defun
+
 @node Disassembly
 @section Disassembled Byte-Code
 @cindex disassembled byte-code
@@ -595,7 +618,7 @@ Disassembly
 point is left before the output.
 
 The argument @var{object} can be a function name, a lambda expression
-(@pxref{Lambda Expressions}), or a byte-code object (@pxref{Byte-Code
+(@pxref{Lambda Expressions}), or a byte-code object (@pxref{Closure
 Objects}).  If it is a lambda expression, @code{disassemble} compiles
 it and disassembles the resulting compiled code.
 @end deffn
diff --git a/doc/lispref/elisp.texi b/doc/lispref/elisp.texi
index ec93a0b9c8a..339272d1f05 100644
--- a/doc/lispref/elisp.texi
+++ b/doc/lispref/elisp.texi
@@ -323,7 +323,7 @@ Top
 * Macro Type::          A method of expanding an expression into another
                           expression, more fundamental but less pretty.
 * Primitive Function Type::     A function written in C, callable from Lisp.
-* Byte-Code Type::      A function written in Lisp, then compiled.
+* Closure Type::        A function written in Lisp, then compiled.
 * Record Type::         Compound objects with programmer-defined types.
 * Type Descriptors::    Objects holding information about types.
 * Autoload Type::       A type used for automatically loading seldom-used
@@ -657,7 +657,7 @@ Top
 * Docs and Compilation::    Dynamic loading of documentation strings.
 * Eval During Compile::     Code to be evaluated when you compile.
 * Compiler Errors::         Handling compiler error messages.
-* Byte-Code Objects::       The data type used for byte-compiled functions.
+* Closure Objects::         The data type used for byte-compiled functions.
 * Disassembly::             Disassembling byte-code; how to read byte-code.
 
 Native Compilation
diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi
index 0062de91354..c57de08460f 100644
--- a/doc/lispref/functions.texi
+++ b/doc/lispref/functions.texi
@@ -130,7 +130,7 @@ What Is a Function
 
 @item byte-code function
 A function that has been compiled by the byte compiler.
-@xref{Byte-Code Type}.
+@xref{Closure Type}.
 
 @item autoload object
 @cindex autoload object
@@ -227,6 +227,16 @@ What Is a Function
 a function loaded from a dynamic module (@pxref{Dynamic Modules}).
 @end defun
 
+@defun interpreted-function-p object
+This function returns @code{t} if @var{object} is an interpreted function.
+@end defun
+
+@defun closurep object
+This function returns @code{t} if @var{object} is a closure, which is
+a particular kind of function object.  Currently closures are used
+for all byte-code functions and all interpreted functions.
+@end defun
+
 @defun subr-arity subr
 This works like @code{func-arity}, but only for built-in functions and
 without symbol indirection.  It signals an error for non-built-in
diff --git a/doc/lispref/objects.texi b/doc/lispref/objects.texi
index aa1e073042f..cf703aba9c8 100644
--- a/doc/lispref/objects.texi
+++ b/doc/lispref/objects.texi
@@ -244,7 +244,7 @@ Programming Types
 * Macro Type::          A method of expanding an expression into another
                           expression, more fundamental but less pretty.
 * Primitive Function Type::     A function written in C, callable from Lisp.
-* Byte-Code Type::      A function written in Lisp, then compiled.
+* Closure Type::        A function written in Lisp.
 * Record Type::         Compound objects with programmer-defined types.
 * Type Descriptors::    Objects holding information about types.
 * Autoload Type::       A type used for automatically loading seldom-used
@@ -1458,18 +1458,24 @@ Primitive Function Type
 @end group
 @end example
 
-@node Byte-Code Type
-@subsection Byte-Code Function Type
+@node Closure Type
+@subsection Closure Function Type
 
-@dfn{Byte-code function objects} are produced by byte-compiling Lisp
-code (@pxref{Byte Compilation}).  Internally, a byte-code function
-object is much like a vector; however, the evaluator handles this data
-type specially when it appears in a function call.  @xref{Byte-Code
-Objects}.
+@dfn{Closures} are function objects produced when turning a function
+definition into a function value.  Closures are used both for
+byte-compiled Lisp functions as well as for interpreted Lisp functions.
+Closures can be produced by byte-compiling Lisp code (@pxref{Byte
+Compilation}) or simply by evaluating a lambda expression without
+compiling it, resulting in an interpreted function.  Internally,
+a closure is much like a vector; however, the evaluator
+handles this data type specially when it appears in a function call.
+@xref{Closure Objects}.
 
 The printed representation and read syntax for a byte-code function
 object is like that for a vector, with an additional @samp{#} before the
-opening @samp{[}.
+opening @samp{[}.  When printed for human consumption, it is printed as
+a special kind of list with an additional @samp{#f} before the opening
+@samp{(}.
 
 @node Record Type
 @subsection Record Type
@@ -2042,10 +2048,7 @@ Type Predicates
 @xref{Buffer Basics, bufferp}.
 
 @item byte-code-function-p
-@xref{Byte-Code Type, byte-code-function-p}.
-
-@item compiled-function-p
-@xref{Byte-Code Type, compiled-function-p}.
+@xref{Closure Type, byte-code-function-p}.
 
 @item case-table-p
 @xref{Case Tables, case-table-p}.
@@ -2056,9 +2059,15 @@ Type Predicates
 @item char-table-p
 @xref{Char-Tables, char-table-p}.
 
+@item closurep
+@xref{What Is a Function, closurep}.
+
 @item commandp
 @xref{Interactive Call, commandp}.
 
+@item compiled-function-p
+@xref{Closure Type, compiled-function-p}.
+
 @item condition-variable-p
 @xref{Condition Variables, condition-variable-p}.
 
@@ -2098,6 +2107,9 @@ Type Predicates
 @item integerp
 @xref{Predicates on Numbers, integerp}.
 
+@item interpreted-function-p
+@xref{What Is a Function, interpreted-function-p}.
+
 @item keymapp
 @xref{Creating Keymaps, keymapp}.
 
diff --git a/doc/lispref/sequences.texi b/doc/lispref/sequences.texi
index c9e47624878..4c5525f10c5 100644
--- a/doc/lispref/sequences.texi
+++ b/doc/lispref/sequences.texi
@@ -1583,7 +1583,7 @@ Vector Functions
 
 The @code{vconcat} function also allows byte-code function objects as
 arguments.  This is a special feature to make it easy to access the entire
-contents of a byte-code function object.  @xref{Byte-Code Objects}.
+contents of a byte-code function object.  @xref{Closure Objects}.
 
 For other concatenation functions, see @code{mapconcat} in @ref{Mapping
 Functions}, @code{concat} in @ref{Creating Strings}, and @code{append}
diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi
index 0cd4e2c614e..07fb730f0f1 100644
--- a/doc/lispref/text.texi
+++ b/doc/lispref/text.texi
@@ -6386,8 +6386,12 @@ Change Hooks
 instead.
 @end defvar
 
+@menu
+* Tracking changes::       Keeping track of buffer modifications.
+@end menu
+
 @node Tracking changes
-@subsection Tracking changes
+@subsection Keeping track of buffer modifications
 @cindex track-changes
 @cindex change tracker
 
diff --git a/src/.gdbinit b/src/.gdbinit
index 6c4dda67f06..7645d466a5e 100644
--- a/src/.gdbinit
+++ b/src/.gdbinit
@@ -822,15 +822,22 @@ Print $ as a frame pointer.
 This command assumes $ is an Emacs Lisp frame value.
 end
 
-define xcompiled
+define xclosure
   xgetptr $
   print (struct Lisp_Vector *) $ptr
   output ($->contents[0])@($->header.size & 0xff)
   echo \n
 end
+document xclosure
+Print $ as a function pointer.
+This command assumes that $ is an Emacs Lisp byte-code or interpreted function value.
+end
+
+define xcompiled
+  xclosure
+end
 document xcompiled
-Print $ as a compiled function pointer.
-This command assumes that $ is an Emacs Lisp compiled value.
+Obsolete alias for "xclosure".
 end
 
 define xwindow
@@ -1038,8 +1045,8 @@ define xpr
       if $vec == PVEC_FRAME
 	xframe
       end
-      if $vec == PVEC_COMPILED
-	xcompiled
+      if $vec == PVEC_CLOSURE
+	xclosure
       end
       if $vec == PVEC_WINDOW
 	xwindow
diff --git a/src/eval.c b/src/eval.c
index 2dfa869b03b..aa8ac4f10af 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -521,6 +521,9 @@ DEFUN ("make-interpreted-closure", Fmake_interpreted_closure,
    Lisp_Object docstring, Lisp_Object iform)
 {
   CHECK_CONS (body);          /* Make sure it's not confused with byte-code! */
+  /* Despite its name 'Fmake_byte_code' works to build all kinds of 'closure'
+     objects, including interpreted functions, rather than only byte-code
+     functions.  */
   if (!NILP (iform))
     {
       iform = Fcdr (iform);