Subject: Re: bug#31586: 27.0.50; `frame-title-format' doesn't save match data

From: Stefan Monnier <[email-masked]>
Date: Sun, 27 May 2018 15:32:44 -0400

>> FWIW, I think this qualifies as a bug in query-replace: Elisp code
>> should never presume that the match-data is preserved across something
>> like sit-for, read-char, or any other function which can run process
>> filters, redisplay, timers, or contains a yield-point.
> Is this practical?  We have any number of hooks, advices, and other
> means to make arbitrary Lisp run almost off any function call.  Given
> that redisplay can be entered by such Lisp by calling 'redisplay' or
> 'message' or one of the other functions you mentioned, your suggestion
> would mean we need to save-match-data around any call to any
> function.  That would make our code very cluttered, indeed.

That's how we've lived so far, except that the need for save-match-data
is not around "any" call, but only around "any call except for <...>"
where <...> is the set of "primitive enough" functions.  The main
problem so far is that this set is not formally defined (and also that
the byte-compiler doesn't warn you if you use a function outside of this
set without wrapping with save-match-data), but other than that it works
well in practice, because in 99% there is *very* little code executed
between a regexp match and the use of the match-data.

[ Side question: while `message` does cause a form of redisplay, IIUC it
  doesn't cause a *real* redisplay in the sense that it won't recompute
  mode-lines, frame-titles, nor will it run jit-lock, IOW it won't run
  elisp code.  ]

> My POV is that using :eval is intrinsically tricky, and whoever does
> that should take the necessary precautions.

I think it would be preferable to save the match-data around the whole
redisplay than have each :eval do it.

More to the point: AFAICT in the problem at hand, between the
regexp-match and the call to buffer-substring-no-properties, process
filters can be executed, so it's not just the match data which could be
changed, but the whole buffer's contents, so save-match-data around
the :eval call will just patch over one particular instance of a more
general problem, I think.

This said, having looked at the code this time, the bug is not quite as
clear as I thought: perform-replace does already save&restore the match
data, as evidenced by:

		  (setq key (read-event))
		  ;; Necessary in case something happens during
		  ;; read-event that clobbers the match data.
		  (set-match-data real-match-data)

But it does it in a fairly complex way, so the exact problem is hard
to pinpoint.  If someone can understand what replace-match-data really
does, maybe they can figure out the origin of the problem.

        Stefan