From aa9093a1441bcf2bd6b5781f39aa382385c410b5 Mon Sep 17 00:00:00 2001 From: Michael Matz Date: Tue, 16 Aug 2022 16:58:00 +0200 Subject: [PATCH] Fix null pointer constants an expression like 'i*0', even though it's value is constant and can be evaluated at compile time is not an integer constant expression, and hence no null pointer constant, and therefore the conditional operator doesn't select the other type. --- tcc.h | 2 ++ tccgen.c | 6 +++++- tests/tests2/94_generic.c | 3 +++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/tcc.h b/tcc.h index bfac0638..8369c9ff 100644 --- a/tcc.h +++ b/tcc.h @@ -1029,6 +1029,8 @@ struct filespec { #define VT_SYM 0x0200 /* a symbol value is added */ #define VT_MUSTCAST 0x0C00 /* value must be casted to be correct (used for char/short stored in integer registers) */ +#define VT_NONCONST 0x1000 /* VT_CONST, but not an (C standard) integer + constant expression */ #define VT_MUSTBOUND 0x4000 /* bound checking must be done before dereferencing value */ #define VT_BOUNDED 0x8000 /* value is bounded. The address of the diff --git a/tccgen.c b/tccgen.c index 54fb38ad..4b2c4912 100644 --- a/tccgen.c +++ b/tccgen.c @@ -2187,6 +2187,7 @@ static void gen_opic(int op) int t2 = v2->type.t & VT_BTYPE; int c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; int c2 = (v2->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; + int nonconst = (v1->r | v2->r) & VT_NONCONST; uint64_t l1 = c1 ? v1->c.i : 0; uint64_t l2 = c2 ? v2->c.i : 0; int shm = (t1 == VT_LLONG) ? 63 : 31; @@ -2253,6 +2254,7 @@ static void gen_opic(int op) v1->c.i = l1; vtop--; } else { + nonconst = VT_NONCONST; /* if commutative ops, put c2 as constant */ if (c1 && (op == '+' || op == '&' || op == '^' || op == '|' || op == '*' || op == TOK_EQ || op == TOK_NE)) { @@ -2326,6 +2328,8 @@ static void gen_opic(int op) gen_opi(op); } } + if (vtop->r == VT_CONST) + vtop->r |= nonconst; } #if defined TCC_TARGET_X86_64 || defined TCC_TARGET_I386 @@ -2609,7 +2613,7 @@ static int pointed_size(CType *type) static inline int is_null_pointer(SValue *p) { - if ((p->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST) + if ((p->r & (VT_VALMASK | VT_LVAL | VT_SYM | VT_NONCONST)) != VT_CONST) return 0; return ((p->type.t & VT_BTYPE) == VT_INT && (uint32_t)p->c.i == 0) || ((p->type.t & VT_BTYPE) == VT_LLONG && p->c.i == 0) || diff --git a/tests/tests2/94_generic.c b/tests/tests2/94_generic.c index 4a68bcdd..1cb01641 100644 --- a/tests/tests2/94_generic.c +++ b/tests/tests2/94_generic.c @@ -92,6 +92,9 @@ int main() //void ptrs get chosen preferentially; qualifs still combine _Generic( 0?(int volatile*)0: (void const*)1, void volatile const*: (void)0); + //but this is no null-ptr constant, so fallback to void-choice + i = 0; + _Generic( 1?(void*)(i*0LL):&i, void*:0); //like gcc but not clang, don't treat (void* const as the null-ptr constant) _Generic( 0?(int volatile*)0: (void const*)0, void volatile const*: (void)0);