From 1529b5221389c75371d0f3f181957a77a158a53c Mon Sep 17 00:00:00 2001 From: lemon Date: Mon, 9 Mar 2026 10:24:39 +0100 Subject: c: relax constexpr constraints, fix alignof - Allow short-circuiting of constant logical expressions where the unevaluated operand is not a constant expression (`1 || 0/0`) - Allow constant integer expressions that evaluate to zero to be used as null pointer constants (GNU extension). + According to the standard, `int *x = 5*0;` should be rejected. But compilers evaluate `5*0 -> 0` and allow it as if a null pointer literal. --- c/c.c | 44 ++++++++++++++++++++++++++------------------ c/eval.c | 35 +++++++++++++++++++++++------------ 2 files changed, 49 insertions(+), 30 deletions(-) (limited to 'c') diff --git a/c/c.c b/c/c.c index 87087e7..c7a6eac 100644 --- a/c/c.c +++ b/c/c.c @@ -476,17 +476,23 @@ subscriptcheck(const struct expr *ex, const struct expr *rhs, const struct span return ty; } -static void /* 6.5.3.4 The sizeof and _Alignof operators */ +static uint /* 6.5.3.4 The sizeof and _Alignof operators */ sizeofalignofcheck(const struct span *span, enum toktag tt, union type ty, const struct expr *ex) { - if (isincomplete(ty)) + uint r = (tt == TKWsizeof ? typesize : typealign)(ty); + if (ty.t == TYVOID) { + if (ccopt.pedant) warn(span, "applying %'tt to void type", tt); + r = 1; + } else if (isincomplete(ty)) { error(span, "cannot apply %'tt to incomplete type '%ty'", tt, ty); - else if (ty.t == TYFUNC) + } else if (ty.t == TYFUNC) { error(span, "cannot apply %'tt to function type '%ty'", tt, ty); - else if (tt == TKWsizeof && ex && ex->t == EGETF && ex->fld.bitsiz) + } else if (tt == TKWsizeof && ex && ex->t == EGETF && ex->fld.bitsiz) { error(span, "cannot apply %'tt to bitfield", tt); + } if (tt != TKWsizeof && ex && ccopt.pedant) warn(span, "%'tt applied to an expression is a GNU extension", tt); + return r; } static bool /* 6.5.8 Relational operators */ @@ -506,9 +512,11 @@ static bool isnullpo(const struct expr *ex) /* match '0' or '(void *) 0' */ { static const union type voidptr = {{ TYPTR, .flag = TFCHLDPRIM, .child = TYVOID }}; - if (ex->t == ECAST && ex->ty.bits == voidptr.bits) + while (ex->t == ECAST && ex->ty.bits == voidptr.bits) ex = ex->sub; - return iszero(*ex); + if (iszero(*ex)) return 1; + return eval((struct expr *)ex, EVINTCONST) /* GNU extension. should we warn? */ + && iszero(*ex); } static bool /* 6.5.9 Equality operators */ @@ -531,14 +539,14 @@ condtype(const struct expr *a, const struct expr *b) union type t1 = typedecay(a->ty), t2 = typedecay(b->ty), s1, s2; if (isarith(t1) && isarith(t2)) return cvtarith(t1, t2); if (t1.bits == t2.bits) return t1; + if (t1.t == TYPTR && isnullpo(b)) return t1; + if (isnullpo(a) && t2.t == TYPTR) return t2; if (t1.t == TYPTR && t2.t == TYPTR) { s1 = typechild(t1), s2 = typechild(t2); if (s1.bits == s2.bits || s2.t == TYVOID || s1.t == TYVOID) { return mkptrtype(s1.t == TYVOID ? s1 : s2, (t1.flag | t2.flag) & TFCHLDQUAL); } } - if (t1.t == TYPTR && isnullpo(b)) return t1; - if (isnullpo(a) && t2.t == TYPTR) return t2; return mktype(0); } @@ -1114,30 +1122,30 @@ Unary: ex = mkexpr(ESYM, tk.span, decl->ty, .qual = decl->qual, .decl = decl - declsbuf.p); } break; } - case TKWsizeof: case TKW_Alignof: case TKWalignof: + case TKWsizeof: case TKW_Alignof: case TKWalignof: { + enum toktag tt = tk.t; + uint res; span = tk.span; if (!match(cm, NULL, '(')) /* sizeof/alignof expr */ goto Unops; else if (isdecltok(cm)) { /* sizeof/alignof (type) */ - enum toktag tt = tk.t; struct declstate st = { DCASTEXPR }; ty = pdecl(&st, cm).ty; peek(cm, &tk); if (expect(cm, ')', NULL)) joinspan(&span.ex, tk.span.ex); - sizeofalignofcheck(&span, tt, ty, NULL); + res = sizeofalignofcheck(&span, tt, ty, NULL); } else { /* sizeof/alignof expr */ - enum toktag tt = tk.t; struct expr tmp = commaexpr(cm); peek(cm, &tk); if (expect(cm, ')', NULL)) joinspan(&span.ex, tk.span.ex); ppostfixopers(cm, &tmp); ty = tmp.ty; - sizeofalignofcheck(&span, tt, ty, &tmp); + res = sizeofalignofcheck(&span, tt, ty, &tmp); } - ex = mkexpr(ENUMLIT, span, mktype(targ_sizetype), .u = typesize(ty)); - break; + ex = mkexpr(ENUMLIT, span, mktype(targ_sizetype), .u = res); + break; } case TKW__builtin_va_arg: span = tk.span; ex = vaargexpr(cm, &span); @@ -1187,7 +1195,7 @@ Unary: case '*': if (ex.ty.t == TYPTR || ex.ty.t == TYARRAY) { ty = typechild(ex.ty); - if (isincomplete(typedecay(ty))) { + if (ty.t != TYVOID && isincomplete(typedecay(ty))) { error(&span, "cannot dereference pointer to incomplete type '%ty'", ty); ty = mktype(TYINT); } @@ -1206,8 +1214,8 @@ Unary: ex = mkexpr(EADDROF, span, mkptrtype(ex.ty, ex.qual), .sub = exprdup(cm, &ex)); break; case TKWsizeof: case TKW_Alignof: case TKWalignof: - sizeofalignofcheck(&span, unops[nunop].tt, ex.ty, &ex); - ex = mkexpr(ENUMLIT, span, mktype(targ_sizetype), .u = typesize(ex.ty)); + ex = mkexpr(ENUMLIT, span, mktype(targ_sizetype), + .u = sizeofalignofcheck(&span, unops[nunop].tt, ex.ty, &ex)); break; default: assert(0); } diff --git a/c/eval.c b/c/eval.c index e2d67d7..839ff7a 100644 --- a/c/eval.c +++ b/c/eval.c @@ -215,15 +215,16 @@ binop(struct expr *ex, enum evalmode mode) if (ex->ty.t == TYPTR) mode = EVFOLD; if (!eval(lhs, mode)) return 0; - if (!eval(rhs, mode)) return 0; if (in_range(ex->t, EADD, ESHR)) oty = ex->ty; else oty = cvtarith(lhs->ty, rhs->ty); - if (!numcast(oty, lhs, lhs)) return 0; - if (!numcast(oty, rhs, rhs)) return 0; flt = isflt(oty); sgn = issigned(oty); + if (ex->t != ELOGAND && ex->t != ELOGIOR) { + if (!numcast(oty, lhs, lhs)) return 0; + if (!eval(rhs, mode) || !numcast(oty, rhs, rhs)) return 0; + } switch (ex->t) { #define ef else if case EADD: if (flt) lhs->f += rhs->f; @@ -264,14 +265,6 @@ binop(struct expr *ex, enum evalmode mode) ef (sgn) lhs->i >>= rhs->i; else lhs->u >>= rhs->u; break; - case ELOGAND: if (flt) t = lhs->f && rhs->f; - else t = lhs->u && rhs->u; - lhs->u = t; - break; - case ELOGIOR: if (flt) t = lhs->f || rhs->f; - else t = lhs->u || rhs->u; - lhs->u = t; - break; case EEQU: if (flt) t = lhs->f == rhs->f; else t = lhs->u == rhs->u; lhs->u = t; @@ -300,12 +293,30 @@ binop(struct expr *ex, enum evalmode mode) else t = lhs->u >= rhs->u; lhs->u = t; break; + case ELOGAND: if (flt) t = lhs->f; else t = lhs->u; + if (t) { + if (!eval(rhs, mode) || !numcast(oty, rhs, rhs)) return 0; + if (flt) t = t && lhs->f; + else t = t && lhs->u; + } + lhs->u = t; + break; + case ELOGIOR: if (flt) t = lhs->f; else t = lhs->u; + if (!t) { + if (!eval(rhs, mode) || !numcast(oty, rhs, rhs)) return 0; + if (flt) t = t || lhs->f; + else t = t || lhs->u; + } + lhs->u = t; + break; default: return 0; #undef ef } - if (!in_range(ex->t, EADD, ESHR)) + if (!in_range(ex->t, EADD, ESHR)) { + lhs->t = ENUMLIT; lhs->ty = mktype(TYINT); + } return numcast(ex->ty, ex, lhs); } -- cgit v1.2.3