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