commit fd743afc8033264bba9f456a36810a97df568326
parent a093cd3a04ccea2aff751e618e07ff77befa396c
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Wed, 17 Jun 2026 01:04:39 -0700
cpp: under __ASSEMBLER__, pass a non-stringize '#' through literally
A '#' not followed by a macro parameter inside a function-like macro is a C
constraint violation (stringize needs a parameter). But assembler-with-cpp uses
'#' as the immediate prefix, so `#define M(c) ... cmp r0, #0 ...` is idiomatic
hand-written-asm (e.g. compiler-rt's DEFINE_AEABI_DCMP). When __ASSEMBLER__ is
defined, emit the lone '#' as a literal token (it is mid-line, never a directive
introducer) instead of erroring — matching gcc/clang. C-mode behavior is
unchanged (test-pp green).
Diffstat:
3 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/lang/cpp/pp/pp.c b/lang/cpp/pp/pp.c
@@ -548,6 +548,7 @@ static void pp_intern_keywords(Pp* pp) {
pp->sym_undef = kit_sym_intern(p->c, KIT_SLICE_LIT("undef"));
pp->sym_include = kit_sym_intern(p->c, KIT_SLICE_LIT("include"));
pp->sym_include_next = kit_sym_intern(p->c, KIT_SLICE_LIT("include_next"));
+ pp->sym_assembler = kit_sym_intern(p->c, KIT_SLICE_LIT("__ASSEMBLER__"));
pp->sym_has_include = kit_sym_intern(p->c, KIT_SLICE_LIT("__has_include"));
pp->sym_has_include_next =
kit_sym_intern(p->c, KIT_SLICE_LIT("__has_include_next"));
diff --git a/lang/cpp/pp/pp_expand.c b/lang/cpp/pp/pp_expand.c
@@ -698,8 +698,21 @@ static void subst_phase1_impl(Pp* pp, const Macro* m, ArgList* a,
for (j = 0; j < m->body_len; ++j) {
const Tok* bt = &m->body[j];
if (bt->kind == TOK_PP_HASH) {
- /* §6.10.3.2: # must be followed by a parameter. */
+ /* §6.10.3.2: # must be followed by a parameter. EXCEPT under
+ * assembler-with-cpp (__ASSEMBLER__ defined), where a `#` not before a
+ * parameter is the assembler's immediate prefix (e.g. `cmp r0, #0` inside
+ * a function-like macro) — pass it through literally, matching gcc/clang.
+ * It is emitted mid-line, so it never starts a directive downstream. */
if (j + 1 >= m->body_len || m->body[j + 1].kind != TOK_PP_PARAM) {
+ if (mt_get(pp, pp->sym_assembler)) {
+ Tok lit = *bt;
+ if (carry) {
+ lit.flags |= carry;
+ carry = 0;
+ }
+ tv_push(pp, out, lit);
+ continue;
+ }
compiler_panic(pp->c, pp_materialize_loc(pp, bt->loc),
"'#' is not followed by a macro parameter");
}
diff --git a/lang/cpp/pp/pp_priv.h b/lang/cpp/pp/pp_priv.h
@@ -285,6 +285,7 @@ struct Pp {
Sym sym_undef;
Sym sym_include;
Sym sym_include_next; /* GCC/clang #include_next extension */
+ Sym sym_assembler; /* __ASSEMBLER__ — asm-with-cpp `#` leniency */
Sym sym_has_include; /* __has_include() #if operator */
Sym sym_has_include_next; /* __has_include_next() #if operator */
Sym sym_if;