boot2

Playing with the boostrap
git clone https://git.ryansepassi.com/git/boot2.git
Log | Files | Refs | README

commit 84ebbc029a7f0918783284562c38a0b8fa276f1a
parent 4db54671fd6800c54f8b16e9f6d78a60a83af578
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Fri,  1 May 2026 15:18:21 -0700

tests/cc: pin macro arg prescan (M(M(1)))

C11 §6.10.3.1 requires function-like macro arguments to be
macro-expanded before substitution into the body, except when the
parameter is the operand of `#` or `##`. The recursive case
M(M(1)) — for `#define M(x) ((x) + (x))` — only works under
prescan: without it, the outer M's name is in every substituted
token's hide-set, so the inner M never re-expands during rescan
and the parser sees a call to undeclared M.

Test pins the prescan path that landed in cc/cc.scm via
%pp-substitute's normal-substitution branch.

Diffstat:
Atests/cc/210-pp-arg-prescan.c | 18++++++++++++++++++
Atests/cc/210-pp-arg-prescan.expected-exit | 1+
2 files changed, 19 insertions(+), 0 deletions(-)

diff --git a/tests/cc/210-pp-arg-prescan.c b/tests/cc/210-pp-arg-prescan.c @@ -0,0 +1,18 @@ +/* Per C11 §6.10.3.1: function-like macro arguments must be + * macro-expanded BEFORE substitution into the body, except when + * the parameter is the operand of `#` or `##`. + * + * `M(M(1))` for `#define M(x) ((x) + (x))`: + * With prescan: inner M(1) -> ((1) + (1)); outer expands to + * ((((1) + (1))) + (((1) + (1)))) = 4. + * Without prescan: outer body becomes ((M(1)) + (M(1))) where M + * is in every substituted token's hide-set, so the inner Ms + * never expand and the parser sees calls to undeclared `M`. + */ + +#define M(x) ((x) + (x)) + +int main(void) { + int v = M(M(1)); + return v == 4 ? 0 : 1; +} diff --git a/tests/cc/210-pp-arg-prescan.expected-exit b/tests/cc/210-pp-arg-prescan.expected-exit @@ -0,0 +1 @@ +0