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:
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