commit 64e882134660367b2546da279ad4d2adbab6158f
parent 24d477f4ce2d0de89590909ce1fdbfb2c84f0ffa
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Mon, 18 May 2026 15:51:25 -0700
Implement typed C constant expression evaluation
Diffstat:
7 files changed, 591 insertions(+), 112 deletions(-)
diff --git a/doc/C11_CONFORMANCE_CHECKLIST.md b/doc/C11_CONFORMANCE_CHECKLIST.md
@@ -11,7 +11,7 @@ make the implementation pass it.
- [x] `make test-lex` passes: 16/16.
- [x] `make test-pp test-pp-err` passes: 82/82 and 15/15.
- [x] `make test-parse-err` passes with expanded C11 constraint coverage:
- currently 56/56 pass.
+ currently 57/57 pass.
- [ ] `make test-parse` passes without skips: currently 2528 pass, 0 fail,
4 skip. Skips are `long double` and file-scope `asm`.
- [x] `make test-cg-api test-opt test-dwarf test-debug` passes.
@@ -171,10 +171,22 @@ CFREE_TEST_FILTER=asm_02_file_scope make test-parse
## Constant expressions and initializers
-- [ ] Replace the current narrow integer evaluator with a C11-aware constant
- expression evaluator that tracks type, value category, and allowed forms.
-- [ ] Accept `_Alignof` in integer constant expressions.
+- [x] Replace the previous narrow `i64` integer evaluator with a typed,
+ target-width integer constant-expression evaluator.
+ Covered cases include integer literal type selection by suffix/base/value,
+ integer promotions, usual arithmetic conversions, logical operators,
+ conditional expressions, integer casts, immediate floating-constant casts,
+ and shift-count diagnostics.
+ Tests: `6_6_10_logical_cond_const`,
+ `6_6_11_unsigned_const_expr`, and
+ `6_6_shift_count_out_of_range`.
+ Code: `eval_const_int_typed`, `CConstInt`, and `eval_const_int` in
+ `parse_expr.c`.
+- [x] Accept `_Alignof` in integer constant expressions.
Positive array-bound coverage passes.
+- [ ] Generalize constant-expression classification beyond integer ICE call
+ sites so arithmetic constants, address constants, null pointer constants,
+ and static initializer validation share one semantic evaluator.
- [ ] Complete static initializer address constants:
object address, function address, array plus/minus integer constant,
and null pointer constants.
@@ -250,7 +262,8 @@ conformance needs a mode story.
Ordinary identifier redeclarations, tentative/defined state, and composite
object/function types are covered; tag completion remains pending.
4. Finish bit-fields: diagnostics first, then layout/codegen.
-5. Finish `sizeof`/constant-expression/static-initializer semantics.
+5. Finish `sizeof` edge cases and unify static-initializer/address-constant
+ semantics with the typed constant-expression evaluator.
6. Unskip `long double` or explicitly narrow the supported C profile until
runtime/CG support exists.
7. Bring `rt` and freestanding header tests into the default conformance gate.
diff --git a/lang/c/parse/parse_expr.c b/lang/c/parse/parse_expr.c
@@ -82,21 +82,28 @@ static const Type* conditional_pointer_type(Parser* p, const Type* then_ty,
* Literal parsing
* ============================================================ */
-i64 parse_int_literal(Parser* p, const Tok* t) {
+static u32 cint_bits(Parser* p, const Type* ty);
+static int cint_signed(Parser* p, const Type* ty);
+
+static u64 parse_int_literal_u64(Parser* p, const Tok* t, int* decimal_out) {
size_t len = 0;
const char* s = pool_str(p->pool, t->spelling, &len);
size_t i = 0;
- i64 base = 10;
- i64 acc = 0;
+ u64 base = 10;
+ u64 acc = 0;
+ int decimal = 1;
if (!s) perr(p, "bad numeric literal");
if (len >= 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
base = 16;
+ decimal = 0;
i = 2;
} else if (len >= 2 && s[0] == '0' && (s[1] == 'b' || s[1] == 'B')) {
base = 2;
+ decimal = 0;
i = 2;
} else if (len >= 1 && s[0] == '0') {
base = 8;
+ decimal = 0;
i = 1;
}
for (; i < len; ++i) {
@@ -111,26 +118,80 @@ i64 parse_int_literal(Parser* p, const Tok* t) {
dv = c - 'A' + 10;
else
perr(p, "bad digit in numeric literal");
- if (dv >= base) perr(p, "digit out of range for base");
+ if ((u64)dv >= base) perr(p, "digit out of range for base");
+ if (acc > (~0ull - (u64)dv) / base)
+ perr(p, "integer literal too large");
acc = acc * base + dv;
}
+ if (decimal_out) *decimal_out = decimal;
return acc;
}
+static int uint_fits_type(Parser* p, u64 v, const Type* ty) {
+ u32 nb = cint_bits(p, ty);
+ if (cint_signed(p, ty)) {
+ if (nb >= 64) return v <= 0x7fffffffffffffffull;
+ return v <= ((1ull << (nb - 1u)) - 1ull);
+ }
+ if (nb >= 64) return 1;
+ return v <= ((1ull << nb) - 1ull);
+}
+
+static const Type* first_fitting_type(Parser* p, u64 v, const TypeKind* kinds,
+ u32 nkinds) {
+ u32 i;
+ for (i = 0; i < nkinds; ++i) {
+ const Type* ty = type_prim(p->pool, kinds[i]);
+ if (uint_fits_type(p, v, ty)) return ty;
+ }
+ perr(p, "integer literal too large for supported integer types");
+}
+
+i64 parse_int_literal(Parser* p, const Tok* t) {
+ return (i64)parse_int_literal_u64(p, t, NULL);
+}
+
static const Type* int_literal_type(Parser* p, const Tok* t) {
int u = (t->flags & TF_INT_U) != 0;
int l = (t->flags & TF_INT_L) != 0;
int ll = (t->flags & TF_INT_LL) != 0;
- TypeKind k;
- if (ll)
- k = u ? TY_ULLONG : TY_LLONG;
- else if (l)
- k = u ? TY_ULONG : TY_LONG;
- else if (u)
- k = TY_UINT;
- else
- k = TY_INT;
- return type_prim(p->pool, k);
+ int decimal = 1;
+ u64 v = parse_int_literal_u64(p, t, &decimal);
+ if (u && ll) {
+ static const TypeKind order[] = {TY_ULLONG};
+ return first_fitting_type(p, v, order, (u32)(sizeof order / sizeof order[0]));
+ }
+ if (!u && ll) {
+ static const TypeKind order[] = {TY_LLONG};
+ return first_fitting_type(p, v, order, (u32)(sizeof order / sizeof order[0]));
+ }
+ if (u && l) {
+ static const TypeKind order[] = {TY_ULONG, TY_ULLONG};
+ return first_fitting_type(p, v, order, (u32)(sizeof order / sizeof order[0]));
+ }
+ if (!u && l) {
+ static const TypeKind dec_order[] = {TY_LONG, TY_LLONG};
+ static const TypeKind other_order[] = {TY_LONG, TY_ULONG, TY_LLONG,
+ TY_ULLONG};
+ return decimal ? first_fitting_type(p, v, dec_order,
+ (u32)(sizeof dec_order /
+ sizeof dec_order[0]))
+ : first_fitting_type(p, v, other_order,
+ (u32)(sizeof other_order /
+ sizeof other_order[0]));
+ }
+ if (u) {
+ static const TypeKind order[] = {TY_UINT, TY_ULONG, TY_ULLONG};
+ return first_fitting_type(p, v, order, (u32)(sizeof order / sizeof order[0]));
+ }
+ if (decimal) {
+ static const TypeKind order[] = {TY_INT, TY_LONG, TY_LLONG};
+ return first_fitting_type(p, v, order, (u32)(sizeof order / sizeof order[0]));
+ } else {
+ static const TypeKind order[] = {TY_INT, TY_UINT, TY_LONG,
+ TY_ULONG, TY_LLONG, TY_ULLONG};
+ return first_fitting_type(p, v, order, (u32)(sizeof order / sizeof order[0]));
+ }
}
static double parse_float_literal(Parser* p, const Tok* t) {
@@ -388,136 +449,385 @@ CfreeCgSym emit_string_to_rodata(Parser* p, const u8* bytes, size_t n) {
* Constant expression evaluator (cexpr_*)
* ============================================================ */
-static i64 cexpr_unary(Parser* p, SrcLoc loc);
-static i64 cexpr_cond(Parser* p, SrcLoc loc);
+static CConstInt cexpr_unary(Parser* p, SrcLoc loc);
+static CConstInt cexpr_cond(Parser* p, SrcLoc loc);
static const Type* offsetof_designator(Parser* p, const Type* base, u32* off);
-static i64 cexpr_mul(Parser* p, SrcLoc loc) {
- i64 v = cexpr_unary(p, loc);
+static u32 cint_bits(Parser* p, const Type* ty) {
+ u32 sz = ty ? c_abi_sizeof(p->abi, ty) : 8u;
+ if (sz >= 8) return 64;
+ return sz * 8u;
+}
+
+static u64 cint_mask_for_bits(u32 bits) {
+ if (bits >= 64) return ~0ull;
+ return (1ull << bits) - 1ull;
+}
+
+static int cint_signed(Parser* p, const Type* ty) {
+ if (!ty) return 1;
+ return c_abi_type_info(p->abi, ty).signed_ != 0;
+}
+
+static CConstInt cint_make(Parser* p, const Type* ty, u64 bits) {
+ CConstInt v;
+ u32 nb;
+ if (!ty) ty = ty_int(p);
+ nb = cint_bits(p, ty);
+ v.type = ty;
+ v.bits = bits & cint_mask_for_bits(nb);
+ if (ty->kind == TY_BOOL) v.bits = v.bits ? 1u : 0u;
+ return v;
+}
+
+i64 const_int_as_i64(Parser* p, CConstInt v) {
+ u32 nb = cint_bits(p, v.type);
+ u64 mask = cint_mask_for_bits(nb);
+ u64 u = v.bits & mask;
+ if (cint_signed(p, v.type) && nb < 64) {
+ u64 sign = 1ull << (nb - 1u);
+ if (u & sign) u |= ~mask;
+ }
+ return (i64)u;
+}
+
+static CConstInt cint_cast(Parser* p, CConstInt v, const Type* ty) {
+ const Type* dst = type_unqual(p->pool, ty);
+ if (!dst || !type_is_int(dst)) {
+ perr(p, "integer constant expression cast requires integer type");
+ }
+ return cint_make(p, dst, v.bits);
+}
+
+static u32 cint_rank(const Type* ty) {
+ if (!ty) return 0;
+ switch ((TypeKind)ty->kind) {
+ case TY_BOOL:
+ return 1;
+ case TY_CHAR:
+ case TY_SCHAR:
+ case TY_UCHAR:
+ return 2;
+ case TY_SHORT:
+ case TY_USHORT:
+ return 3;
+ case TY_INT:
+ case TY_UINT:
+ case TY_ENUM:
+ return 4;
+ case TY_LONG:
+ case TY_ULONG:
+ return 5;
+ case TY_LLONG:
+ case TY_ULLONG:
+ return 6;
+ case TY_INT128:
+ case TY_UINT128:
+ return 7;
+ default:
+ return 0;
+ }
+}
+
+static const Type* cint_unsigned_variant(Parser* p, const Type* ty) {
+ if (!ty) return type_prim(p->pool, TY_UINT);
+ switch ((TypeKind)ty->kind) {
+ case TY_BOOL:
+ case TY_CHAR:
+ case TY_SCHAR:
+ case TY_UCHAR:
+ case TY_SHORT:
+ case TY_USHORT:
+ case TY_INT:
+ case TY_UINT:
+ case TY_ENUM:
+ return type_prim(p->pool, TY_UINT);
+ case TY_LONG:
+ case TY_ULONG:
+ return type_prim(p->pool, TY_ULONG);
+ case TY_LLONG:
+ case TY_ULLONG:
+ return type_prim(p->pool, TY_ULLONG);
+ case TY_INT128:
+ case TY_UINT128:
+ return type_prim(p->pool, TY_UINT128);
+ default:
+ return type_prim(p->pool, TY_UINT);
+ }
+}
+
+static const Type* cint_promote_type(Parser* p, const Type* ty) {
+ const Type* u = type_unqual(p->pool, ty);
+ if (u && u->kind == TY_ENUM) return type_prim(p->pool, TY_INT);
+ return type_promoted(p->pool, u);
+}
+
+static const Type* cint_common_type(Parser* p, const Type* a, const Type* b) {
+ const Type* ap = cint_promote_type(p, a);
+ const Type* bp = cint_promote_type(p, b);
+ int as = cint_signed(p, ap);
+ int bs = cint_signed(p, bp);
+ u32 ar = cint_rank(ap);
+ u32 br = cint_rank(bp);
+ if (type_compatible(ap, bp)) return ap;
+ if (as == bs) return ar >= br ? ap : bp;
+ if (!as && ar >= br) return ap;
+ if (!bs && br >= ar) return bp;
+ if (as && cint_bits(p, ap) > cint_bits(p, bp)) return ap;
+ if (bs && cint_bits(p, bp) > cint_bits(p, ap)) return bp;
+ return cint_unsigned_variant(p, as ? ap : bp);
+}
+
+static CConstInt cint_convert(Parser* p, CConstInt v, const Type* ty) {
+ return cint_make(p, ty, v.bits);
+}
+
+static int cint_truth(Parser* p, CConstInt v) {
+ (void)p;
+ return v.bits != 0;
+}
+
+static CConstInt cint_bool(Parser* p, int truth) {
+ return cint_make(p, ty_int(p), truth ? 1u : 0u);
+}
+
+static CConstInt cexpr_mul(Parser* p, SrcLoc loc) {
+ CConstInt v = cexpr_unary(p, loc);
for (;;) {
- if (accept_punct(p, '*'))
- v = v * cexpr_unary(p, loc);
- else if (accept_punct(p, '/')) {
- i64 r = cexpr_unary(p, loc);
- if (r == 0) compiler_panic(p->c, loc, "division by zero in constant");
- v = v / r;
+ int op = 0;
+ CConstInt r;
+ const Type* ct;
+ if (accept_punct(p, '*')) {
+ op = '*';
+ } else if (accept_punct(p, '/')) {
+ op = '/';
} else if (accept_punct(p, '%')) {
- i64 r = cexpr_unary(p, loc);
- if (r == 0) compiler_panic(p->c, loc, "modulo by zero in constant");
- v = v % r;
- } else
+ op = '%';
+ } else {
break;
+ }
+ r = cexpr_unary(p, loc);
+ ct = cint_common_type(p, v.type, r.type);
+ v = cint_convert(p, v, ct);
+ r = cint_convert(p, r, ct);
+ if (op == '*') {
+ v = cint_make(p, ct, v.bits * r.bits);
+ } else {
+ if (r.bits == 0)
+ compiler_panic(p->c, loc, op == '/' ? "division by zero in constant"
+ : "modulo by zero in constant");
+ if (cint_signed(p, ct)) {
+ i64 lv = const_int_as_i64(p, v);
+ i64 rv = const_int_as_i64(p, r);
+ v = cint_make(p, ct, op == '/' ? (u64)(lv / rv) : (u64)(lv % rv));
+ } else {
+ v = cint_make(p, ct, op == '/' ? v.bits / r.bits : v.bits % r.bits);
+ }
+ }
}
return v;
}
-static i64 cexpr_add(Parser* p, SrcLoc loc) {
- i64 v = cexpr_mul(p, loc);
+static CConstInt cexpr_add(Parser* p, SrcLoc loc) {
+ CConstInt v = cexpr_mul(p, loc);
for (;;) {
- if (accept_punct(p, '+'))
- v = v + cexpr_mul(p, loc);
- else if (accept_punct(p, '-'))
- v = v - cexpr_mul(p, loc);
- else
+ int sub = 0;
+ CConstInt r;
+ const Type* ct;
+ if (accept_punct(p, '+')) {
+ sub = 0;
+ } else if (accept_punct(p, '-')) {
+ sub = 1;
+ } else {
break;
+ }
+ r = cexpr_mul(p, loc);
+ ct = cint_common_type(p, v.type, r.type);
+ v = cint_convert(p, v, ct);
+ r = cint_convert(p, r, ct);
+ v = cint_make(p, ct, sub ? v.bits - r.bits : v.bits + r.bits);
}
return v;
}
-static i64 cexpr_shift(Parser* p, SrcLoc loc) {
- i64 v = cexpr_add(p, loc);
+static CConstInt cexpr_shift(Parser* p, SrcLoc loc) {
+ CConstInt v = cexpr_add(p, loc);
for (;;) {
- if (accept_punct(p, P_SHL))
- v = v << cexpr_add(p, loc);
- else if (accept_punct(p, P_SHR))
- v = v >> cexpr_add(p, loc);
- else
+ int left = 0;
+ CConstInt r;
+ i64 sh;
+ const Type* vt;
+ if (accept_punct(p, P_SHL)) {
+ left = 1;
+ } else if (accept_punct(p, P_SHR)) {
+ left = 0;
+ } else {
break;
+ }
+ r = cexpr_add(p, loc);
+ vt = cint_promote_type(p, v.type);
+ v = cint_convert(p, v, vt);
+ sh = const_int_as_i64(p, r);
+ if (sh < 0 || sh >= (i64)cint_bits(p, vt))
+ perr(p, "shift count out of range in constant expression");
+ if (left) {
+ if (cint_signed(p, vt) && const_int_as_i64(p, v) < 0)
+ perr(p, "left shift of negative value in constant expression");
+ v = cint_make(p, vt, v.bits << (u32)sh);
+ } else if (cint_signed(p, vt)) {
+ v = cint_make(p, vt, (u64)(const_int_as_i64(p, v) >> (u32)sh));
+ } else {
+ v = cint_make(p, vt, v.bits >> (u32)sh);
+ }
}
return v;
}
-static i64 cexpr_rel(Parser* p, SrcLoc loc) {
- i64 v = cexpr_shift(p, loc);
+static CConstInt cexpr_rel(Parser* p, SrcLoc loc) {
+ CConstInt v = cexpr_shift(p, loc);
for (;;) {
- if (accept_punct(p, P_LE))
- v = v <= cexpr_shift(p, loc);
- else if (accept_punct(p, P_GE))
- v = v >= cexpr_shift(p, loc);
- else if (is_punct(&p->cur, '<')) {
+ int op = 0;
+ CConstInt r;
+ const Type* ct;
+ int res;
+ if (accept_punct(p, P_LE)) {
+ op = P_LE;
+ } else if (accept_punct(p, P_GE)) {
+ op = P_GE;
+ } else if (is_punct(&p->cur, '<')) {
advance(p);
- v = v < cexpr_shift(p, loc);
+ op = '<';
} else if (is_punct(&p->cur, '>')) {
advance(p);
- v = v > cexpr_shift(p, loc);
- } else
+ op = '>';
+ } else {
break;
+ }
+ r = cexpr_shift(p, loc);
+ ct = cint_common_type(p, v.type, r.type);
+ v = cint_convert(p, v, ct);
+ r = cint_convert(p, r, ct);
+ if (cint_signed(p, ct)) {
+ i64 lv = const_int_as_i64(p, v);
+ i64 rv = const_int_as_i64(p, r);
+ res = op == P_LE ? lv <= rv
+ : op == P_GE ? lv >= rv
+ : op == '<' ? lv < rv
+ : lv > rv;
+ } else {
+ res = op == P_LE ? v.bits <= r.bits
+ : op == P_GE ? v.bits >= r.bits
+ : op == '<' ? v.bits < r.bits
+ : v.bits > r.bits;
+ }
+ v = cint_bool(p, res);
}
return v;
}
-static i64 cexpr_eq(Parser* p, SrcLoc loc) {
- i64 v = cexpr_rel(p, loc);
+static CConstInt cexpr_eq(Parser* p, SrcLoc loc) {
+ CConstInt v = cexpr_rel(p, loc);
for (;;) {
- if (accept_punct(p, P_EQ))
- v = (v == cexpr_rel(p, loc));
- else if (accept_punct(p, P_NE))
- v = (v != cexpr_rel(p, loc));
- else
+ int ne = 0;
+ CConstInt r;
+ const Type* ct;
+ if (accept_punct(p, P_EQ)) {
+ ne = 0;
+ } else if (accept_punct(p, P_NE)) {
+ ne = 1;
+ } else {
break;
+ }
+ r = cexpr_rel(p, loc);
+ ct = cint_common_type(p, v.type, r.type);
+ v = cint_convert(p, v, ct);
+ r = cint_convert(p, r, ct);
+ v = cint_bool(p, ne ? v.bits != r.bits : v.bits == r.bits);
}
return v;
}
-static i64 cexpr_band(Parser* p, SrcLoc loc) {
- i64 v = cexpr_eq(p, loc);
+static CConstInt cexpr_band(Parser* p, SrcLoc loc) {
+ CConstInt v = cexpr_eq(p, loc);
while (is_punct(&p->cur, '&') && !is_punct(&p->cur, P_AND)) {
+ CConstInt r;
+ const Type* ct;
advance(p);
- v = v & cexpr_eq(p, loc);
+ r = cexpr_eq(p, loc);
+ ct = cint_common_type(p, v.type, r.type);
+ v = cint_convert(p, v, ct);
+ r = cint_convert(p, r, ct);
+ v = cint_make(p, ct, v.bits & r.bits);
}
return v;
}
-static i64 cexpr_bxor(Parser* p, SrcLoc loc) {
- i64 v = cexpr_band(p, loc);
- while (accept_punct(p, '^')) v = v ^ cexpr_band(p, loc);
+static CConstInt cexpr_bxor(Parser* p, SrcLoc loc) {
+ CConstInt v = cexpr_band(p, loc);
+ while (accept_punct(p, '^')) {
+ CConstInt r = cexpr_band(p, loc);
+ const Type* ct = cint_common_type(p, v.type, r.type);
+ v = cint_convert(p, v, ct);
+ r = cint_convert(p, r, ct);
+ v = cint_make(p, ct, v.bits ^ r.bits);
+ }
return v;
}
-static i64 cexpr_bor(Parser* p, SrcLoc loc) {
- i64 v = cexpr_bxor(p, loc);
+static CConstInt cexpr_bor(Parser* p, SrcLoc loc) {
+ CConstInt v = cexpr_bxor(p, loc);
while (is_punct(&p->cur, '|') && !is_punct(&p->cur, P_OR)) {
+ CConstInt r;
+ const Type* ct;
advance(p);
- v = v | cexpr_bxor(p, loc);
+ r = cexpr_bxor(p, loc);
+ ct = cint_common_type(p, v.type, r.type);
+ v = cint_convert(p, v, ct);
+ r = cint_convert(p, r, ct);
+ v = cint_make(p, ct, v.bits | r.bits);
}
return v;
}
-static i64 cexpr_land(Parser* p, SrcLoc loc) {
- i64 v = cexpr_bor(p, loc);
+static CConstInt cexpr_land(Parser* p, SrcLoc loc) {
+ CConstInt v = cexpr_bor(p, loc);
while (accept_punct(p, P_AND)) {
- i64 r = cexpr_bor(p, loc);
- v = (v != 0 && r != 0) ? 1 : 0;
+ CConstInt r = cexpr_bor(p, loc);
+ v = cint_bool(p, cint_truth(p, v) && cint_truth(p, r));
}
return v;
}
-static i64 cexpr_lor(Parser* p, SrcLoc loc) {
- i64 v = cexpr_land(p, loc);
+static CConstInt cexpr_lor(Parser* p, SrcLoc loc) {
+ CConstInt v = cexpr_land(p, loc);
while (accept_punct(p, P_OR)) {
- i64 r = cexpr_land(p, loc);
- v = (v != 0 || r != 0) ? 1 : 0;
+ CConstInt r = cexpr_land(p, loc);
+ v = cint_bool(p, cint_truth(p, v) || cint_truth(p, r));
}
return v;
}
-static i64 cexpr_cond(Parser* p, SrcLoc loc) {
- i64 v = cexpr_lor(p, loc);
+static CConstInt cexpr_cond(Parser* p, SrcLoc loc) {
+ CConstInt v = cexpr_lor(p, loc);
if (accept_punct(p, '?')) {
- i64 then_v = cexpr_cond(p, loc);
- i64 else_v;
+ CConstInt then_v = cexpr_cond(p, loc);
+ CConstInt else_v;
+ const Type* ct;
expect_punct(p, ':', "':' in constant conditional expression");
else_v = cexpr_cond(p, loc);
- v = v ? then_v : else_v;
+ ct = cint_common_type(p, then_v.type, else_v.type);
+ then_v = cint_convert(p, then_v, ct);
+ else_v = cint_convert(p, else_v, ct);
+ v = cint_truth(p, v) ? then_v : else_v;
}
return v;
}
-static i64 cexpr_unary(Parser* p, SrcLoc loc) {
+static CConstInt cexpr_unary(Parser* p, SrcLoc loc) {
if (accept_punct(p, '+')) return cexpr_unary(p, loc);
- if (accept_punct(p, '-')) return -cexpr_unary(p, loc);
- if (accept_punct(p, '~')) return ~cexpr_unary(p, loc);
- if (accept_punct(p, '!')) return cexpr_unary(p, loc) ? 0 : 1;
+ if (accept_punct(p, '-')) {
+ CConstInt v = cexpr_unary(p, loc);
+ const Type* pt = cint_promote_type(p, v.type);
+ v = cint_convert(p, v, pt);
+ return cint_make(p, pt, (u64)(-const_int_as_i64(p, v)));
+ }
+ if (accept_punct(p, '~')) {
+ CConstInt v = cexpr_unary(p, loc);
+ const Type* pt = cint_promote_type(p, v.type);
+ v = cint_convert(p, v, pt);
+ return cint_make(p, pt, ~v.bits);
+ }
+ if (accept_punct(p, '!')) return cint_bool(p, !cint_truth(p, cexpr_unary(p, loc)));
if (accept_kw(p, KW_SIZEOF)) {
if (is_punct(&p->cur, '(')) {
Tok n = peek1(p);
@@ -527,7 +837,7 @@ static i64 cexpr_unary(Parser* p, SrcLoc loc) {
const Type* t = parse_type_name(p);
expect_punct(p, ')', "')' after sizeof type-name");
require_sizeof_type(p, t);
- return (i64)c_abi_sizeof(p->abi, t);
+ return cint_make(p, ty_size_t(p), c_abi_sizeof(p->abi, t));
}
}
}
@@ -538,7 +848,7 @@ static i64 cexpr_unary(Parser* p, SrcLoc loc) {
require_sizeof_type(p, ty);
i64 sz = (i64)c_abi_sizeof(p->abi, ty);
cg_drop(p->cg);
- return sz;
+ return cint_make(p, ty_size_t(p), (u64)sz);
}
}
if (accept_kw(p, KW_ALIGNOF)) {
@@ -549,7 +859,7 @@ static i64 cexpr_unary(Parser* p, SrcLoc loc) {
{
const Type* t = parse_type_name(p);
expect_punct(p, ')', "')' after _Alignof type-name");
- return (i64)c_abi_alignof(p->abi, t);
+ return cint_make(p, ty_size_t(p), c_abi_alignof(p->abi, t));
}
}
}
@@ -558,7 +868,7 @@ static i64 cexpr_unary(Parser* p, SrcLoc loc) {
const Type* ty = cg_top_type(p->cg);
i64 al = (i64)c_abi_alignof(p->abi, ty);
cg_drop(p->cg);
- return al;
+ return cint_make(p, ty_size_t(p), (u64)al);
}
}
if (accept_punct(p, '(')) {
@@ -566,37 +876,36 @@ static i64 cexpr_unary(Parser* p, SrcLoc loc) {
const Type* t = parse_type_name(p);
expect_punct(p, ')', "')' after cast type-name");
{
- i64 v = cexpr_unary(p, loc);
- u32 sz = c_abi_sizeof(p->abi, t);
- int is_signed = c_abi_type_info(p->abi, t).signed_;
- if (sz < 8) {
- u64 mask = (1ull << (sz * 8u)) - 1ull;
- u64 uv = (u64)v & mask;
- if (is_signed) {
- u64 sign = 1ull << (sz * 8u - 1u);
- v = (i64)((uv ^ sign) - sign);
- } else {
- v = (i64)uv;
+ const Type* tu = type_unqual(p->pool, t);
+ if (p->cur.kind == TOK_FLT) {
+ double fv;
+ if (!tu || !type_is_int(tu)) {
+ perr(p, "integer constant expression cast requires integer type");
}
+ fv = parse_float_literal(p, &p->cur);
+ advance(p);
+ return cint_make(p, tu, (u64)(i64)fv);
}
- return v;
+ CConstInt v = cexpr_unary(p, loc);
+ return cint_cast(p, v, t);
}
}
{
- i64 v = cexpr_cond(p, loc);
+ CConstInt v = cexpr_cond(p, loc);
expect_punct(p, ')', "')' in constant expression");
return v;
}
}
if (p->cur.kind == TOK_NUM) {
i64 v = parse_int_literal(p, &p->cur);
+ const Type* ty = int_literal_type(p, &p->cur);
advance(p);
- return v;
+ return cint_make(p, ty, (u64)v);
}
if (p->cur.kind == TOK_CHR) {
i64 v = decode_char_literal(p, &p->cur);
advance(p);
- return v;
+ return cint_make(p, ty_int(p), (u64)v);
}
if (p->cur.kind == TOK_IDENT) {
Sym name = p->cur.v.ident;
@@ -609,13 +918,13 @@ static i64 cexpr_unary(Parser* p, SrcLoc loc) {
expect_punct(p, ',', "',' in __builtin_offsetof");
(void)offsetof_designator(p, root, &off);
expect_punct(p, ')', "')' after __builtin_offsetof");
- return (i64)off;
+ return cint_make(p, ty_size_t(p), off);
}
{
SymEntry* e = scope_lookup(p, name);
if (e && e->kind == SEK_ENUM_CST) {
advance(p);
- return e->v.enum_value;
+ return cint_make(p, e->type ? e->type : ty_int(p), (u64)e->v.enum_value);
}
}
compiler_panic(p->c, loc, "non-constant identifier in constant expression");
@@ -623,7 +932,15 @@ static i64 cexpr_unary(Parser* p, SrcLoc loc) {
compiler_panic(p->c, loc, "expected constant expression");
}
-i64 eval_const_int(Parser* p, SrcLoc loc) { return cexpr_cond(p, loc); }
+CConstInt eval_const_int_typed(Parser* p, SrcLoc loc) {
+ CConstInt v = cexpr_cond(p, loc);
+ if (!type_is_int(v.type)) perr(p, "integer constant expression required");
+ return v;
+}
+
+i64 eval_const_int(Parser* p, SrcLoc loc) {
+ return const_int_as_i64(p, eval_const_int_typed(p, loc));
+}
/* ============================================================
* to_rvalue
@@ -883,6 +1200,129 @@ static const Type* offsetof_designator(Parser* p, const Type* base, u32* off) {
return cur;
}
+typedef struct BuiltinOverflowInfo {
+ CfreeCgIntrinsic intrin;
+ TypeKind type_kind;
+ const char* name;
+} BuiltinOverflowInfo;
+
+static int sym_eq_cstr(Parser* p, Sym sym, const char* want) {
+ size_t got_len = 0;
+ size_t want_len = strlen(want);
+ const char* got = pool_str(p->pool, sym, &got_len);
+ return got && got_len == want_len && memcmp(got, want, want_len) == 0;
+}
+
+static int builtin_overflow_info(Parser* p, Sym name,
+ BuiltinOverflowInfo* out) {
+ static const BuiltinOverflowInfo infos[] = {
+ {CFREE_CG_INTRIN_SADD_OVERFLOW, TY_INT, "__builtin_sadd_overflow"},
+ {CFREE_CG_INTRIN_SADD_OVERFLOW, TY_LONG, "__builtin_saddl_overflow"},
+ {CFREE_CG_INTRIN_SADD_OVERFLOW, TY_LLONG, "__builtin_saddll_overflow"},
+ {CFREE_CG_INTRIN_UADD_OVERFLOW, TY_UINT, "__builtin_uadd_overflow"},
+ {CFREE_CG_INTRIN_UADD_OVERFLOW, TY_ULONG, "__builtin_uaddl_overflow"},
+ {CFREE_CG_INTRIN_UADD_OVERFLOW, TY_ULLONG, "__builtin_uaddll_overflow"},
+ {CFREE_CG_INTRIN_SSUB_OVERFLOW, TY_INT, "__builtin_ssub_overflow"},
+ {CFREE_CG_INTRIN_SSUB_OVERFLOW, TY_LONG, "__builtin_ssubl_overflow"},
+ {CFREE_CG_INTRIN_SSUB_OVERFLOW, TY_LLONG, "__builtin_ssubll_overflow"},
+ {CFREE_CG_INTRIN_USUB_OVERFLOW, TY_UINT, "__builtin_usub_overflow"},
+ {CFREE_CG_INTRIN_USUB_OVERFLOW, TY_ULONG, "__builtin_usubl_overflow"},
+ {CFREE_CG_INTRIN_USUB_OVERFLOW, TY_ULLONG, "__builtin_usubll_overflow"},
+ {CFREE_CG_INTRIN_SMUL_OVERFLOW, TY_INT, "__builtin_smul_overflow"},
+ {CFREE_CG_INTRIN_SMUL_OVERFLOW, TY_LONG, "__builtin_smull_overflow"},
+ {CFREE_CG_INTRIN_SMUL_OVERFLOW, TY_LLONG, "__builtin_smulll_overflow"},
+ {CFREE_CG_INTRIN_UMUL_OVERFLOW, TY_UINT, "__builtin_umul_overflow"},
+ {CFREE_CG_INTRIN_UMUL_OVERFLOW, TY_ULONG, "__builtin_umull_overflow"},
+ {CFREE_CG_INTRIN_UMUL_OVERFLOW, TY_ULLONG, "__builtin_umulll_overflow"},
+ };
+ size_t i;
+ for (i = 0; i < sizeof(infos) / sizeof(infos[0]); ++i) {
+ if (sym_eq_cstr(p, name, infos[i].name)) {
+ if (out) *out = infos[i];
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static FrameSlot builtin_tmp_slot(Parser* p, const Type* ty) {
+ FrameSlotDesc fsd;
+ memset(&fsd, 0, sizeof fsd);
+ fsd.type = ty;
+ fsd.size = c_abi_sizeof(p->abi, ty);
+ fsd.align = c_abi_alignof(p->abi, ty);
+ fsd.kind = FS_LOCAL;
+ return cg_local(p->cg, &fsd);
+}
+
+static int parse_builtin_overflow_call(Parser* p, Sym name, SrcLoc loc) {
+ BuiltinOverflowInfo info;
+ const Type* op_ty;
+ const Type* ptr_ty;
+ const Type* out_ty;
+ const Type* bool_ty;
+ FrameSlot ptr_slot;
+ FrameSlot ov_slot;
+ if (!builtin_overflow_info(p, name, &info)) return 0;
+
+ op_ty = type_prim(p->pool, info.type_kind);
+ bool_ty = type_prim(p->pool, TY_BOOL);
+
+ advance(p); /* IDENT */
+ expect_punct(p, '(', "'(' after overflow builtin");
+ parse_assign_expr(p);
+ to_rvalue(p);
+ coerce_top_to_type(p, op_ty);
+ expect_punct(p, ',', "',' in overflow builtin");
+ parse_assign_expr(p);
+ to_rvalue(p);
+ coerce_top_to_type(p, op_ty);
+ expect_punct(p, ',', "',' in overflow builtin");
+ parse_assign_expr(p);
+ to_rvalue(p);
+ ptr_ty = cg_top_type(p->cg);
+ if (!ptr_ty || ptr_ty->kind != TY_PTR) {
+ perr(p, "overflow builtin result argument must be a pointer");
+ }
+ out_ty = ptr_ty->ptr.pointee;
+ if (!type_compatible(type_unqual(p->pool, out_ty), op_ty)) {
+ perr(p, "overflow builtin result pointer type mismatch");
+ }
+ expect_punct(p, ')', "')' after overflow builtin");
+
+ ptr_slot = builtin_tmp_slot(p, ptr_ty);
+ cg_push_local_typed(p->cg, ptr_slot, ptr_ty);
+ cg_swap(p->cg);
+ cg_store(p->cg);
+ cg_drop(p->cg);
+
+ cg_set_loc(p->cg, loc);
+ if (pcg_emit_enabled(p)) {
+ cfree_cg_intrinsic(p->cg, info.intrin, 2, pcg_tid(p->c, op_ty));
+ }
+ pcg_drop_type(p);
+ pcg_drop_type(p);
+ pcg_push_type(p, op_ty);
+ pcg_push_type(p, bool_ty);
+
+ ov_slot = builtin_tmp_slot(p, bool_ty);
+ cg_push_local_typed(p->cg, ov_slot, bool_ty);
+ cg_swap(p->cg);
+ cg_store(p->cg);
+ cg_drop(p->cg);
+
+ cg_push_local_typed(p->cg, ptr_slot, ptr_ty);
+ cg_load(p->cg);
+ cg_deref(p->cg, out_ty);
+ cg_swap(p->cg);
+ cg_store(p->cg);
+ cg_drop(p->cg);
+
+ cg_push_local_typed(p->cg, ov_slot, bool_ty);
+ cg_load(p->cg);
+ return 1;
+}
+
static int try_parse_builtin_call(Parser* p) {
Sym name = p->cur.v.ident;
SrcLoc loc = p->cur.loc;
@@ -892,6 +1332,8 @@ static int try_parse_builtin_call(Parser* p) {
return parse_builtin_mem_call(p, name, loc);
}
+ if (parse_builtin_overflow_call(p, name, loc)) return 1;
+
if (name != p->sym_b_alloca && name != p->sym_b_ctz &&
name != p->sym_b_ctzl && name != p->sym_b_ctzll && name != p->sym_b_clz &&
name != p->sym_b_clzl && name != p->sym_b_clzll &&
diff --git a/lang/c/parse/parse_priv.h b/lang/c/parse/parse_priv.h
@@ -416,7 +416,13 @@ void parse_expr(Parser* p);
void parse_assign_expr(Parser* p);
void parse_cond_expr(Parser* p);
void parse_unary(Parser* p);
+typedef struct CConstInt {
+ const Type* type;
+ u64 bits;
+} CConstInt;
+CConstInt eval_const_int_typed(Parser* p, SrcLoc loc);
i64 eval_const_int(Parser* p, SrcLoc loc);
+i64 const_int_as_i64(Parser* p, CConstInt v);
i64 parse_int_literal(Parser* p, const Tok* t);
i64 decode_char_literal(Parser* p, const Tok* t);
u8* decode_string_literal(Parser* p, const Tok* t, size_t* nlen_out);
diff --git a/test/parse/cases/6_6_11_unsigned_const_expr.c b/test/parse/cases/6_6_11_unsigned_const_expr.c
@@ -0,0 +1,11 @@
+_Static_assert(0u - 1u > 1u, "unsigned constants wrap modulo width");
+_Static_assert(!(-1 < 1u), "usual arithmetic conversions choose unsigned");
+_Static_assert(2147483648 > 0, "decimal constants choose a wider signed type");
+_Static_assert(0xffffffff > 0, "hex constants may choose unsigned int");
+_Static_assert((int)1.75 == 1, "floating constant may be cast in an ICE");
+
+enum { K = (unsigned char)260 + (1 ? 38 : 0) };
+
+int test_main(void) {
+ return K;
+}
diff --git a/test/parse/cases/6_6_11_unsigned_const_expr.expected b/test/parse/cases/6_6_11_unsigned_const_expr.expected
@@ -0,0 +1 @@
+42
diff --git a/test/parse/cases_err/6_6_shift_count_out_of_range.c b/test/parse/cases_err/6_6_shift_count_out_of_range.c
@@ -0,0 +1,5 @@
+enum { X = 1 << 32 };
+
+int test_main(void) {
+ return X;
+}
diff --git a/test/parse/cases_err/6_6_shift_count_out_of_range.errpat b/test/parse/cases_err/6_6_shift_count_out_of_range.errpat
@@ -0,0 +1 @@
+shift count out of range in constant expression