Subject: Re: bug#78042: 31.0.50; Improper sequence of `before/after-change-functions` calls

From: Eli Zaretskii <[email-masked]>
Date: Fri, 25 Apr 2025 09:08:53 +0300

> Cc: [email-masked]
> Date: Thu, 24 Apr 2025 11:38:59 -0400
> From:  Stefan Monnier via "Bug reports for GNU Emacs,
>  the Swiss army knife of text editors" <[email-masked]>
> 
> Package: Emacs
> Version: 31.0.50
> 
> 
> I just found another case where we break our promises w.r.t
> `before/after-change-functions`:
> 
>     src/emacs -Q src/regexp-emacs.c
>     M-: (decode-coding-region 123 (point-max) 'windows-1252) RET
> 
> [ The details of the file and buffer positions don't really
>   matter, AFAIK.  ]
> 
> I first get a run of `before-change-functions` for 123...point-max, as
> expected at the beginning and a run of `after-change-functions` for
> 123...point-max, as expected at the end, but between the two I get
> another run of `before-change-functions` for 123..16497:
> 
>     #0  signal_before_change
>         (start_int=start_int@entry=123, end_int=end_int@entry=16497, preserve_ptr=preserve_ptr@entry=0x0) at insdel.c:2157
>     #1  0x0000555555702c3a in prepare_to_modify_buffer_1
>         (start=start@entry=123, end=end@entry=16497, preserve_ptr=preserve_ptr@entry=0x0) at insdel.c:2024
>     #2  0x00005555557c9f36 in modify_text_properties (buffer=MISSING, 
>         buffer@entry=XIL(0x555555dfac0d), start=MISSING, end=MISSING) at textprop.c:85
>     #3  0x00005555557cb5cc in add_text_properties_1
>         (start=make_fixnum(123), end=make_fixnum(16497), properties=XIL(0x7fffffffce33), object=XIL(0x555555dfac0d), set_type=set_type@entry=TEXT_PROPERTY_REPLACE, destructive=destructive@entry=true) at textprop.c:1236
>     #4  0x00005555557cb9b8 in Fadd_text_properties (start=MISSING, 
>         start@entry=make_fixnum(123), end=MISSING, 
>         end@entry=make_fixnum(16497), properties=MISSING, object=MISSING, 
>         object@entry=XIL(0x555555dfac0d)) at textprop.c:1308
>     #5  0x00005555557cba12 in Fput_text_property
>         (start=make_fixnum(123), end=end@entry=make_fixnum(16497), property=MISSING, 
>         property@entry=XIL(0x55b0), value=MISSING, 
>         value@entry=XIL(0x2aaa9984e428), object=XIL(0x555555dfac0d))
>         at textprop.c:1326
>     #6  0x0000555555644852 in produce_charset
>         (coding=coding@entry=0x7fffffffd080, charbuf=charbuf@entry=0x555556a7ef88, pos=pos@entry=16497) at coding.c:7277
>     #7  0x00005555556448e8 in produce_annotation
>         (coding=coding@entry=0x7fffffffd080, pos=16497, pos@entry=123)
>         at coding.c:7320
>     #8  0x000055555564578f in decode_coding (coding=coding@entry=0x7fffffffd080)
>         at coding.c:7421
>     #9  0x000055555564cc32 in decode_coding_object
>         (coding=coding@entry=0x7fffffffd080, src_object=src_object@entry=XIL(0x555555dfac0d), from=from@entry=123, from_byte=from_byte@entry=123, to=to@entry=161558, to_byte=to_byte@entry=161558, dst_object=XIL(0x555555dfac0d))
>         at coding.c:8183
>     #10 0x000055555564e1c7 in code_convert_region
>         (start=make_fixnum(123), end=make_fixnum(161558), coding_system=XIL(0x2aaa9984e428), dst_object=XIL(0x555555dfac0d), encodep=encodep@entry=false, norecord=norecord@entry=false) at coding.c:9510
> 
> There's probably also a corresponding run of `after-change-functions`,
> I haven't checked, but the problem is that the "final run of
> `after-change-functions` for 123...point-max is now "invalid" in the
> sense that it modifies a range of the buffer beyond the last run of
> `before-change-functions`, contrary to what we promise.

Sorry, I don't think I understand the problem.  Is the problem that we
have "nested" notifications, whereby you have

  before-change-functions A1 B1
  before-change-functions A2 B2
  after-change-functions  A2 B2
  after-change-functions  A1 B1

IOW, _any_ situation where we have nested pairs of notifications is
"breaking our promise"?  If so, how can that be avoided in principle
in Emacs, given its recursive nature, except by some mechanism which
collects all the notifications and rearranges them to "keep our
promise"?

Or what am I missing?