Subject: Re: bug#36392: (info "(elisp)Writing Emacs Primitives") might need some clarifications

From: "Basil L. Contovounesios" <[email-masked]>
Date: Wed, 26 Jun 2019 14:09:03 +0100

tags 36392 + patch
severity 36392 minor
quit

>From 3d85d73858fe0c126277d04db8b99eeb9f09d672 Mon Sep 17 00:00:00 2001
From: "Basil L. Contovounesios" <[email-masked]>
Date: Wed, 26 Jun 2019 13:05:51 +0100
Subject: [PATCH] Clarify & update (elisp) Writing Emacs Primitives

* doc/lispref/internals.texi (Writing Emacs Primitives): Replace
outdated For listing with current Fwhile, so that the subsequent
paragraph on maybe_quit still applies.  Reconcile other code
listings with their current source.  Fix indentation of sample DEFUN
argument lists.  Replace ... with @dots{}.  Fix argument list of
Ffoo example.  Describe UNEVALLED special forms as taking a single
argument. (bug#36392)
---
 doc/lispref/internals.texi | 112 ++++++++++++++++++-------------------
 1 file changed, 55 insertions(+), 57 deletions(-)

diff --git a/doc/lispref/internals.texi b/doc/lispref/internals.texi
index cfeb492af4..34dfb3924e 100644
--- a/doc/lispref/internals.texi
+++ b/doc/lispref/internals.texi
@@ -695,38 +695,36 @@ Writing Emacs Primitives
 C macros.  The only way to really understand how to write new C code is
 to read the source, but we can explain some things here.
 
-  An example of a special form is the definition of @code{or}, from
+  An example of a special form is the definition of @code{while}, from
 @file{eval.c}.  (An ordinary function would have the same general
 appearance.)
 
 @smallexample
 @group
-DEFUN ("or", For, Sor, 0, UNEVALLED, 0,
-  doc: /* Eval args until one of them yields non-nil, then return
-that value.
-The remaining args are not evalled at all.
-If all args return nil, return nil.
+DEFUN ("while", Fwhile, Swhile, 1, UNEVALLED, 0,
+       doc: /* If TEST yields non-nil, eval BODY... and repeat.
+The order of execution is thus TEST, BODY, TEST, BODY and so on
+until TEST returns nil.
 @end group
 @group
-usage: (or CONDITIONS...)  */)
+usage: (while TEST BODY...)  */)
   (Lisp_Object args)
 @{
-  Lisp_Object val = Qnil;
+  Lisp_Object test, body;
 @end group
 
 @group
-  while (CONSP (args))
+  test = XCAR (args);
+  body = XCDR (args);
+  while (!NILP (eval_sub (test)))
     @{
-      val = eval_sub (XCAR (args));
-      if (!NILP (val))
-        break;
-      args = XCDR (args);
       maybe_quit ();
+      prog_ignore (body);
     @}
 @end group
 
 @group
-  return val;
+  return Qnil;
 @}
 @end group
 @end smallexample
@@ -742,14 +740,14 @@ Writing Emacs Primitives
 @table @var
 @item lname
 This is the name of the Lisp symbol to define as the function name; in
-the example above, it is @code{or}.
+the example above, it is @code{while}.
 
 @item fname
 This is the C function name for this function.  This is the name that
 is used in C code for calling the function.  The name is, by
 convention, @samp{F} prepended to the Lisp name, with all dashes
 (@samp{-}) in the Lisp name changed to underscores.  Thus, to call
-this function from C code, call @code{For}.
+this function from C code, call @code{Fwhile}.
 
 @item sname
 This is a C variable name to use for a structure that holds the data for
@@ -761,7 +759,7 @@ Writing Emacs Primitives
 
 @item min
 This is the minimum number of arguments that the function requires.  The
-function @code{or} allows a minimum of zero arguments.
+function @code{while} allows a minimum of one argument.
 
 @item max
 This is the maximum number of arguments that the function accepts, if
@@ -775,21 +773,20 @@ Writing Emacs Primitives
 @cindex interactive specification in primitives
 @item interactive
 This is an interactive specification, a string such as might be used
-as the argument of @code{interactive} in a Lisp function
-(@pxref{Using Interactive}).  In the case
-of @code{or}, it is 0 (a null pointer), indicating that @code{or}
-cannot be called interactively.  A value of @code{""} indicates a
-function that should receive no arguments when called interactively.
-If the value begins with a @samp{"(}, the string is evaluated as a
-Lisp form.  For example:
+as the argument of @code{interactive} in a Lisp function (@pxref{Using
+Interactive}).  In the case of @code{while}, it is @code{0} (a null
+pointer), indicating that @code{while} cannot be called interactively.
+A value of @code{""} indicates a function that should receive no
+arguments when called interactively.  If the value begins with a
+@samp{"(}, the string is evaluated as a Lisp form.  For example:
 
 @example
 @group
-DEFUN ("foo", Ffoo, Sfoo, 0, UNEVALLED, 0
+DEFUN ("foo", Ffoo, Sfoo, 0, 3,
        "(list (read-char-by-name \"Insert character: \")\
               (prefix-numeric-value current-prefix-arg)\
-              t))",
-  doc: /* @dots{} */)
+              t)",
+       doc: /* @dots{} */)
 @end group
 @end example
 
@@ -826,8 +823,8 @@ Writing Emacs Primitives
 @example
 @group
 DEFUN ("bar", Fbar, Sbar, 0, UNEVALLED, 0
-  doc: /* @dots{} */
-  attributes: @var{attr1} @var{attr2} @dots{})
+       doc: /* @dots{} */
+       attributes: @var{attr1} @var{attr2} @dots{})
 @end group
 @end example
 
@@ -863,20 +860,23 @@ Writing Emacs Primitives
 arguments, there must be one C argument for each Lisp argument, and
 each argument must be of type @code{Lisp_Object}.  (Various macros and
 functions for creating values of type @code{Lisp_Object} are declared
-in the file @file{lisp.h}.)  If the primitive has no upper limit on
-the number of Lisp arguments, it must have exactly two C arguments:
-the first is the number of Lisp arguments, and the second is the
-address of a block containing their values.  These have types
-@code{int} and @w{@code{Lisp_Object *}} respectively.  Since
-@code{Lisp_Object} can hold any Lisp object of any data type, you
-can determine the actual data type only at run time; so if you want
-a primitive to accept only a certain type of argument, you must check
-the type explicitly using a suitable predicate (@pxref{Type Predicates}).
+in the file @file{lisp.h}.)  If the primitive is a special form, it
+must accept a Lisp list containing its unevaluated Lisp arguments as a
+single argument of type @code{Lisp_Object}.  If the primitive has no
+upper limit on the number of evaluated Lisp arguments, it must have
+exactly two C arguments: the first is the number of Lisp arguments,
+and the second is the address of a block containing their values.
+These have types @code{ptrdiff_t} and @w{@code{Lisp_Object *}},
+respectively.  Since @code{Lisp_Object} can hold any Lisp object of
+any data type, you can determine the actual data type only at run
+time; so if you want a primitive to accept only a certain type of
+argument, you must check the type explicitly using a suitable
+predicate (@pxref{Type Predicates}).
 @cindex type checking internals
 
 @cindex garbage collection protection
 @cindex protect C variables from garbage collection
-  Within the function @code{For} itself, the local variable
+  Within the function @code{Fwhile} itself, the local variable
 @code{args} refers to objects controlled by Emacs's stack-marking
 garbage collector.  Although the garbage collector does not reclaim
 objects reachable from C @code{Lisp_Object} stack variables, it may
@@ -890,9 +890,8 @@ Writing Emacs Primitives
   Note the call to @code{maybe_quit} inside the loop: this function
 checks whether the user pressed @kbd{C-g}, and if so, aborts the
 processing.  You should do that in any loop that can potentially
-require a large number of iterations; in this case, the list of
-arguments could be very long.  This increases Emacs responsiveness and
-improves user experience.
+require a large number of iterations.  This increases Emacs
+responsiveness and improves user experience.
 
   You must not use C initializers for static or global variables unless
 the variables are never written once Emacs is dumped.  These variables
@@ -957,9 +956,9 @@ Writing Emacs Primitives
 @smallexample
 @group
 DEFUN ("coordinates-in-window-p", Fcoordinates_in_window_p,
-  Scoordinates_in_window_p, 2, 2, 0,
-  doc: /* Return non-nil if COORDINATES are in WINDOW.
-  ...
+       Scoordinates_in_window_p, 2, 2, 0,
+       doc: /* Return non-nil if COORDINATES are in WINDOW.
+  @dots{}
 @end group
 @group
   or `right-margin' is returned.  */)
@@ -972,43 +971,42 @@ Writing Emacs Primitives
 @end group
 
 @group
-  CHECK_LIVE_WINDOW (window);
-  w = XWINDOW (window);
+  w = decode_live_window (window);
   f = XFRAME (w->frame);
   CHECK_CONS (coordinates);
   lx = Fcar (coordinates);
   ly = Fcdr (coordinates);
-  CHECK_NUMBER_OR_FLOAT (lx);
-  CHECK_NUMBER_OR_FLOAT (ly);
-  x = FRAME_PIXEL_X_FROM_CANON_X (f, lx) + FRAME_INTERNAL_BORDER_WIDTH(f);
-  y = FRAME_PIXEL_Y_FROM_CANON_Y (f, ly) + FRAME_INTERNAL_BORDER_WIDTH(f);
+  CHECK_NUMBER (lx);
+  CHECK_NUMBER (ly);
+  x = FRAME_PIXEL_X_FROM_CANON_X (f, lx) + FRAME_INTERNAL_BORDER_WIDTH (f);
+  y = FRAME_PIXEL_Y_FROM_CANON_Y (f, ly) + FRAME_INTERNAL_BORDER_WIDTH (f);
 @end group
 
 @group
   switch (coordinates_in_window (w, x, y))
     @{
-    case ON_NOTHING:            /* NOT in window at all.  */
+    case ON_NOTHING:
       return Qnil;
 @end group
 
-    ...
+    @dots{}
 
 @group
-    case ON_MODE_LINE:          /* In mode line of window.  */
+    case ON_MODE_LINE:
       return Qmode_line;
 @end group
 
-    ...
+    @dots{}
 
 @group
-    case ON_SCROLL_BAR:         /* On scroll-bar of window.  */
+    case ON_VERTICAL_SCROLL_BAR:
       /* Historically we are supposed to return nil in this case.  */
       return Qnil;
 @end group
 
 @group
     default:
-      abort ();
+      emacs_abort ();
     @}
 @}
 @end group
-- 
2.20.1

Stefan Kangas <[email-masked]> writes:

> As a beginner on Emacs Internals, I've been reading:
>
> (info "(elisp)Writing Emacs Primitives")
>
> I hope that you find the following observations useful:
>
> 1.  "If the primitive accepts a fixed maximum number of Lisp
>     arguments, there must be one C argument for each Lisp argument,
>     and each argument must be of type ‘Lisp_Object’.  ...  If the
>     primitive has no upper limit on the number of Lisp arguments, it
>     must have exactly two C arguments: the first is the number of Lisp
>     arguments, and the second is the address of a block containing
>     their values."
>
> The example given is DEFUN( "or", ...)
>
> This function has no upper limit on the number of Lisp arguments, but
> still has only ONE argument.  Unless I'm missing something, this
> contradicts the statement above that "it must have exactly TWO C
> arguments" (my emphasis).

Indeed, this is because For is a special form (with UNEVALLED args).
Compare it with the definition for Fapply (with MANY args) in the same
file, and see the macros DEFUN_ARGS_UNEVALLED and DEFUN_ARGS_MANY in
lisp.h.

Does the attached patch, suitable for either master or emacs-26, help
clarify this?

> 2.  "Although the garbage collector does not reclaim objects reachable
>     from C ‘Lisp_Object’ stack variables, it may move non-object
>     components of an object, such as string contents; so functions
>     that access non-object components must take care to refetch their
>     addresses after performing Lisp evaluation."
>
> I don't think this is very clear.  What is non-object components?  How
> would I refetch their addresses?  How is this relevant to the topic at
> hand?

I don't know about this.

Thanks,

-- 
Basil