Subject: Re: bug#45428: 27.1; (quote (quote (quote ...))) unexpectedly works as anonymous face

From: Eli Zaretskii <[email-masked]>
Date: Tue, 29 Dec 2020 16:53:07 +0200

> Date: Mon, 28 Dec 2020 12:44:11 -0800 (PST)
> From: Drew Adams <[email-masked]>
> Cc: [email-masked], [email-masked], [email-masked],
>         [email-masked]
> 
> > > I wouldn't think of this as a doc bug because (1)
> > > this behavior is so unusual
> > 
> > Which behavior are you alluding to here? and what is the "doc bug",
> > exactly?
> 
> I was alluding to the fact that the doc says that
> "Remaining arguments form a sequence of PROPERTY
> VALUE pairs", but the inserted text seems to show
> the effect of the pair `face (:height 10.0)', even
> though that pair isn't present.

You are still not saying where did you find the documentation which
says that.  I will go ahead and guess that you mean the doc string of
'propertize', which says:

  First argument is the string to copy.
  Remaining arguments form a sequence of PROPERTY VALUE pairs for text
  properties to add to the result.

If that is the documentation you are complaining about, then I find
nothing wrong with, because the original recipe, viz.:

  (propertize "hello" 'face (quote (quote '(:height 10.0))))

does indeed call 'propertize' with a pair, as documented: the PROPERTY
is 'face and the VALUE is (quote (quote '(:height 10.0))).  So the doc
string describes this use case accurately, and there's nothing wrong
with the _form_ of the PROPERTIES arguments to 'propertize' in this
case.

The original bug report said something different:

> In 39.12 Faces, the Emacs Lisp documentation states:
> 
>        One way to represent a face is as a property list of attributes,
>     like ‘(:foreground "red" :weight bold)’. Such a list is called an
>     “anonymous face”.
[...]
>     $ emacs -Q --eval '(font-lock-mode 0)' \
>                --eval "(insert (propertize \
>                            \"hello\"       \
>                            'face (quote (quote '(:height 10.0)))))"
> 
> In this case, the anonymous face is not specified as a property list,
> but as a symbolic expression of the form (quote (quote (quote ...))),
> and still works. Is this the intended result, and can we rely on it? If
> so, could you please document it?

This complaint is about a different instance of "pairs": it's about
the form of the _value_ of the 'face' property, not about the form of
the "&rest PROPERTIES" arguments of 'propertize'.

However, that original complaint is incorrectly assuming that a face
can _only_ have the form of an anonymous face, which is a property
list of attribute/value pairs.  The text it quotes from section 39.12
of the ELisp manual is incomplete; here it is in its entirety:

     One way to represent a face is as a property list of attributes, like
  ‘(:foreground "red" :weight bold)’.  Such a list is called an "anonymous
  face".  For example, you can assign an anonymous face as the value of
  the ‘face’ text property, and Emacs will display the underlying text
  with the specified attributes.  *Note Special Properties::.

If you follow the cross-reference to "Special Properties", you will
find that the value of the 'face' property can be one of the
following:

  . a face symbol
  . a property list of attribute/value pairs
  . a cons cell of the form (foreground-color . COLOR)
  . a cons cell of the form (background-color . COLOR)
  . a list of faces, each one given by any of the above forms
  . a cons cell of the form (:filtered FILTER FACE-SPEC), where
    FACE-SPEC is one of the above forms

And now you should recognize that the strange format of the property
value, which prompted the original bug report, fits the "list of
faces" format as described by the 5th item in the above list.

So I still don't see a problem with the documentation in this case.  I
think the only problem/surprise here could be that Emacs acted
according to a single valid part of the face spec and seemingly
silently ignored the invalid ones, logging an error message in
*Messages* instead of perhaps rejecting it wholesale, and the OP
failed to look in *Messages*.  However, that doesn't seem to be a bug:
the face spec is invalid, and so invokes undefined behavior, where we
only have an obligation not to crash nor lock up Emacs (which is why
the error message isn't displayed as such: the face merging happens at
display time, and we cannot usefully signal an error from redisplay).

> Having heard the misunderstanding that we've made
> (still without my understanding, so far), do you
> have a suggestion for how to dispel/prevent it?

Tell users to read the docs?  But we already know that doesn't really
work...