scc

simple c99 compiler
git clone git://git.simple-cc.org/scc
Log | Files | Refs | Submodules | README | LICENSE

commit b04e1786f2fbbf3976b6d0aba4cc122aefd23bc7
parent 740fd29faa97cc8a39c7e48c368191bc2df9cc99
Author: Roberto E. Vargas Caballero <k0ga@shike2.net>
Date:   Thu, 29 Jan 2026 12:36:50 +0100

cc1: Fix elif handling

The code was assuming that an #else was always inverting the state of the
preprocessor output, and elif was handled like a #else plus a #if, but
that was not the actual case. The code tried to manage this situation
counting the number of nested enabling/disabling of cppoff, but it was
a non sense because we already had a stack of cpp contexts that could do
the work much better. Also, we needed a sticky variable (called "done"
in this commit) to track that one case of the if-else chain was true
before, because the "enabled" field changes and then we forget the history
of the chain.

Diffstat:
Msrc/cmd/scc-cc/cc1/cpp.c | 74+++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Atests/cc/execute/0233-ifelif.c | 15+++++++++++++++
Mtests/cc/execute/scc-tests.lst | 1+
3 files changed, 63 insertions(+), 27 deletions(-)

diff --git a/src/cmd/scc-cc/cc1/cpp.c b/src/cmd/scc-cc/cc1/cpp.c @@ -11,6 +11,7 @@ #include "cc1.h" struct ifstate { + unsigned char done; unsigned char enabled; unsigned char iselse; }; @@ -867,15 +868,16 @@ ifclause(int negate, int isifdef) { Symbol *sym; unsigned n; - int status; + int enabled, done; Node *expr; if (cppctx == NR_COND-1) error("too many nesting levels of conditional inclusion"); n = cppctx++; - if (cppoff) { - status = 0; + if (n > 0 && !ifstate[n-1].enabled) { + done = 1; + enabled = 0; goto disabled; } @@ -890,8 +892,8 @@ ifclause(int negate, int isifdef) } sym = yylval.sym; next(); - status = (sym->flags & SDECLARED) != 0; - if (!status) + enabled = (sym->flags & SDECLARED) != 0; + if (!enabled) killsym(sym); } else { /* TODO: catch recovery here */ @@ -900,18 +902,19 @@ ifclause(int negate, int isifdef) return; } DBG("CPP if expr=%d", expr->sym->u.i); - status = expr->sym->u.i != 0; + enabled = expr->sym->u.i != 0; freetree(expr); } if (negate) - status = !status; + enabled = !enabled; + done = enabled; disabled: - if (status == 0) - ++cppoff; - DBG("CPP if result=%d", status); - ifstate[n].enabled = status; + cppoff = !enabled; + DBG("CPP if result=%d", enabled); + ifstate[n].done = done; + ifstate[n].enabled = enabled; ifstate[n].iselse = 0; } @@ -935,16 +938,6 @@ ifndef(void) } static void -elseclause(void) -{ - int status; - - status = ifstate[cppctx-1].enabled; - ifstate[cppctx-1].enabled = !status; - cppoff += (status) ? 1 : -1; -} - -static void cppelse(void) { if (cppctx == 0 || ifstate[cppctx-1].iselse) { @@ -952,8 +945,21 @@ cppelse(void) return; } - ifstate[cppctx-1].iselse = 1; - elseclause(); + /* + * If we are disabled by a upper ifdef then ifclause() already + * marked us as disabled and done. So if we are done then we + * disable cpp because or ifclause was true, or it was disabled + * by the upper. If we are not done, then it is our turn. + */ + if (ifstate[cppctx-1].done) { + ifstate[cppctx-1].enabled = 0; + cppoff = 1; + } else { + ifstate[cppctx-1].done = 1; + ifstate[cppctx-1].enabled = 1; + cppoff = 0; + } + next(); } @@ -965,8 +971,17 @@ elif(void) return; } - elseclause(); - if (ifstate[cppctx-1].enabled) { + /* + * If we are disabled by a upper ifdef then ifclause() already + * marked us as disabled and done. So if we are done then we + * disable cpp because or ifclause was true, or it was disabled + * by the upper. If we are not done, then we have to evaluate + * the if condition. + */ + if (ifstate[cppctx-1].done) { + ifstate[cppctx-1].enabled = 0; + cppoff = 1; + } else { --cppctx; cppif(); } @@ -977,8 +992,13 @@ endif(void) { if (cppctx == 0) error("#endif without #if"); - if (!ifstate[--cppctx].enabled) - --cppoff; + + if (cppctx > 1) + cppoff = !ifstate[cppctx - 2].enabled; + else + cppoff = 0; + + --cppctx; next(); } diff --git a/tests/cc/execute/0233-ifelif.c b/tests/cc/execute/0233-ifelif.c @@ -0,0 +1,15 @@ +#define FT_UINT_MAX 0xFFFFFFFFUL + +#if FT_UINT_MAX == 0xFFFFFFFFUL +#define FT_SIZEOF_INT ( 32 / FT_CHAR_BIT ) +#elif FT_UINT_MAX > 0xFFFFFFFFUL && FT_UINT_MAX == 0xFFFFFFFFFFFFFFFFUL +#define FT_SIZEOF_INT ( 64 / FT_CHAR_BIT ) +#else +#error "Unsupported size of `int' type!" +#endif + +int +main(void) +{ + return 0; +} diff --git a/tests/cc/execute/scc-tests.lst b/tests/cc/execute/scc-tests.lst @@ -223,3 +223,4 @@ 0230-init.c 0231-init.c 0232-cppmacro.c +0233-ifelif.c