kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

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:
Mlang/cpp/pp/pp.c | 1+
Mlang/cpp/pp/pp_expand.c | 15++++++++++++++-
Mlang/cpp/pp/pp_priv.h | 1+
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;