On Wed, May 14, 2025 at 11:57:08AM -0400, Douglas McIlroy wrote: > This program > define(`A', x define('A',y)A z) > A > produces this answer > > y
Not as written, it doesn't. Because of your mismatched quoting (you used ' unpaired to a leading `), your inner call to define is defining macro "'A'", so the last A in the first line is not yet a macro at that point in time, and then the outer define creates macro "A" whose expansion is "x A z". So attempting to expand A in the second line causes an infinite loop of printing "x x x ..." (at least until the machine runs out of memory to store the corresponding tail of "z z z ..." that it is batching up in memory while expanding that loop on macro A). But if I understand what you meant, define(`A', x define(`A', y)A z) should indeed define macro "A" first to "y", then replace that definition to "x y z". And that's the behavior I get in both m4 1.4.19 and 1.4.20: $ m4 -daeqt define(`A', x define(`A', y)A z) m4trace: -2- define(`A', `y') m4trace: -2- A -> `y' m4trace: -1- define(`A', `x y z') A m4trace: -1- A -> `x y z' x y z You're going to have to be more specific about what version of m4 you are using that sees a bug here, or give me a formula that actually reproduces the problem. > > It appears that the inner definition stamps on the > outer definition in progress. I imagine that each > allocates and fills a string that it assigns as the > stored definition for A, The inner one, being > assigned later, wins. The outer one likely > becomes either a memory leak or a use of > freed storage. I would not be surprised if there are older versions of GNU m4 that had a bug in this area; 1.4.20 explicitly documents: ** Fix regression introduced in 1.4.19 where trace output (such as with `debugmode(t)') could read invalid memory when tracing a series of pushed macros that are popped during argument collection. but trace output is different than define definitions, and your example didn't include trace output. > > I claim the output should be > x y z Yes, that matches my reading of the POSIX requirements on the corrected spelling of the example you gave. > > I also claim this is not a pathological example > that should be declared illegal. I ran into it in a > subtle guise that was completely reasonable. > > If my guess about the nature of the collision is > correct, the fix would be to make the assignments > referred to above at the end of the definition > process, not the beginning. I also ran your (corrected) example under valgrind, and it did not detect any obvious use-after-free violations. I'm not ruling out a bug, just that I'm not seeing what you are claiming. -- Eric Blake, Principal Software Engineer Red Hat, Inc. Virtualization: qemu.org | libguestfs.org