Subject: bug#4835: 23.1; Improper `Invalid face reference' messages. Performance degraded.

From: "Drew Adams" <[email-masked]>
Date: Fri, 30 Oct 2009 15:48:22 -0700

This sure seems like a bug to me. If not, please tell me what the
problem is.
 
Load the attached file, move point to, say, column 15 in some buffer
(e.g. *scratch*), then do `M-x column-marker-1'. Move point a bit, to
be sure to see the column highlighting.
 
Check buffer `*Messages*'. You will see zillions of messages `Invalid
face reference', logged by the C function `merge_face_ref' (in
`xfaces.c').  If you click the mouse in `*Messages*', zillions more
such messages will be displayed. This message logging degrades Emacs
performance considerably.
 
Also, binding `message-log-max' to nil has no effect - it does not
inhibit the message logging. That seems like a bug in itself.  But
perhaps you will say that this message is coming from the display
engine, and that it is appropriate for the display code to ignore
`message-log-max' (but why, especially if it impacts performance so
much?).  At the very least, there is a doc bug, since neither the
manual nor the doc string say anything about the display code ignoring
this variable.
 
Anyway, displaying the message at all seems like a bug in this case.
But if not, I would appreciate understanding what is wrong with the
code I'm using. In the attached code, `M-x column-marker-1' calls:
 
(column-marker-internal
  'column-marker-1
  (1+ (current-column)) 'column-marker-1)
 
That sets the value of symbol `column-marker-1' to the following list
- call it KEYWORDS, where COL is the column that point was in when you
did `M-x column-marker-1' (e.g. 15).
 
KEYWORDS:
 
(((lambda (end)
    (let ((start (point))
          (message-log-max nil)) ; (Has no effect.)
      (when (> end (point-max))
        (setq end (point-max)))
      (unless (< (current-column) COL)
        (forward-line 1))
      (when (< (current-column) COL)
        (move-to-column COL))
      (while (and (< (current-column) COL)
                  (< (point) end)
                  (= 0 (+ (forward-line 1)
                          (current-column))))
        (move-to-column COL))
      (if (and (= COL (current-column))
               (<= (point) end)
               (> (point) start))
          (progn (set-match-data
                  (list (1- (point)) (point)))
                 t)
        (goto-char start)
        nil)))
  (0 column-marker-1 prepend t)))
 
And then `column-marker-internal' calls this, where KEYWORDS is the
list shown above:
 
(font-lock-add-keywords nil KEYWORDS t)
(font-lock-fontify-buffer)
 
The effect is to highlight the column where you issued the command. It
works well, apart from the message logging and its performance impact.
 
The `font-lock-keywords' entry used has a lambda form as car and this
as cadr: (0 column-marker-1 prepend t). Face `column-marker-1' is
defined as follows:
 
(defface column-marker-1 '((t (:background "gray")))
  "Face used for a column marker.  Usually a background color."
  :group 'faces)
 
The lambda form is thus, AFAICT, just the kind of function that
`font-lock(-add)-keywords' expects. Please let me know if I'm missing
something.
 
I think I am correctly using the form (MATCHER HIGHLIGHTER...), from
node `Search-based Fontification' of the Elisp manual.
 
MATCHER here is the lambda form shown above. There is only one
HIGHLIGHTER, with form SUBEXP-HIGHLIGHTER. The SUBEXP-HIGHLIGHTER here
is of form (SUBEXP FACESPEC OVERRIDE LAXMATCH), with SUBEXP=0,
FACESPEC=the symbol (face name) `column-marker-1', OVERRIDE=`prepend',
and LAXMATCH=t.
 
I also tried removing the parens around (0 column-marker-1 prepend t),
making it the cdr instead of the cadr, so it looked like this:
(MATCHER 0 column-marker-1 prepend t), where MATCHER is the same
lambda form. That should match the also-allowed form (MATCHER
. SUBEXP-HIGHLIGHTER). But that didn't change anything.
 
What's odd is that the message complains about a _face reference_, but
the text included with the error message is code from the lambda form
(i.e. from the MATCHER part, not from the HIGHLIGHTER part that
specifies the face).  The display code seems to be walking down the
lambda form, spitting it out bit by bit progressively in the `Invalid
face reference' messages. E.g.:
 
Invalid face reference: end
Invalid face reference: point
Invalid face reference: <=
Invalid face reference: current-column
Invalid face reference: 37
Invalid face reference: =
Invalid face reference: and
Invalid face reference: if
Invalid face reference: 37
Invalid face reference: move-to-column
Invalid face reference: current-column
Invalid face reference: 1
Invalid face reference: forward-line
Invalid face reference: +
Invalid face reference: 0
Invalid face reference: =
Invalid face reference: end
Invalid face reference: point
Invalid face reference: <
Invalid face reference: 37
Invalid face reference: current-column
Invalid face reference: <
Invalid face reference: and
Invalid face reference: while
Invalid face reference: 37
Invalid face reference: move-to-column
Invalid face reference: 37
Invalid face reference: current-column
Invalid face reference: <
Invalid face reference: when
Invalid face reference: 1
Invalid face reference: forward-line
Invalid face reference: 37
Invalid face reference: current-column
Invalid face reference: <
Invalid face reference: unless
Invalid face reference: point-max
Invalid face reference: end
Invalid face reference: setq
Invalid face reference: point-max
Invalid face reference: end
Invalid face reference: >
Invalid face reference: when
Invalid face reference: nil
Invalid face reference: message-log-max
Invalid face reference: point
Invalid face reference: start
Invalid face reference: let
Invalid face reference: end
Invalid face reference: lambda
Invalid face reference: t
Invalid face reference: prepend
Invalid face reference: 0
Invalid face reference: nil
Invalid face reference: start
Invalid face reference: goto-char
Invalid face reference: t
Invalid face reference: point [2 times]
Invalid face reference: 1-
Invalid face reference: list
Invalid face reference: set-match-data
Invalid face reference: progn
Invalid face reference: start
Invalid face reference: point
Invalid face reference: >
Invalid face...
(repeated)
 
It's as if the code were trying to get face specs from the lambda
form, in order to merge those faces. Just a guess. At any rate, it
seems bugged to me, but if not, please let me know what I'm doing
wrong. Thx.

In GNU Emacs 23.1.1 (i386-mingw-nt5.1.2600)
 of 2009-07-29 on SOFT-MJASON
Windowing system distributor `Microsoft Corp.', version 5.1.2600
configured using `configure --with-gcc (4.4)'