commit 0e80961769b914c539ffb502ea6be3cb3130fc8c
parent 1b5a72b7bfb642b0a17e7e5cb8236930bd3a6fab
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Tue, 19 May 2026 16:40:17 -0700
Fix parse edge cases
Diffstat:
5 files changed, 111 insertions(+), 17 deletions(-)
diff --git a/doc/BUGS.md b/doc/BUGS.md
@@ -12,10 +12,10 @@ Format as:
- [x] function declarator with an inline function-pointer return type (no typedef): `6_7_6_20_func_returning_funcptr_no_typedef`
- [x] static initializer accepts unary `-` on a floating constant: `6_7_9_30_static_init_neg_float`
- [x] `#warning` preprocessing directive (non-fatal, parsing continues): `6_10_warning_directive`
-- [ ] static initializer accepts a binary constant expression on floating constants (`1.0f/2.2f`): `6_7_9_31_static_init_const_float_expr`
-- [ ] conditional operator allows a comma expression in its middle operand (`a ? b, c : d`): `6_5_15_01_conditional_comma_in_middle`
-- [ ] subscript accepts a conditional whose constant arm is `0` without treating it as a null pointer: `6_5_2_1_01_subscript_conditional_zero_branch`
-- [ ] struct field declarator `RETTY (*(*name)(P))(IP)` (pointer-to-function-returning-function-pointer; sqlite VFS `xDlSym`): `6_7_6_21_field_ptr_to_func_returning_funcptr`
+- [x] static initializer accepts a binary constant expression on floating constants (`1.0f/2.2f`): `6_7_9_31_static_init_const_float_expr`
+- [x] conditional operator allows a comma expression in its middle operand (`a ? b, c : d`): `6_5_15_01_conditional_comma_in_middle`
+- [x] subscript accepts a conditional whose constant arm is `0` without treating it as a null pointer: `6_5_2_1_01_subscript_conditional_zero_branch`
+- [x] struct field declarator `RETTY (*(*name)(P))(IP)` (pointer-to-function-returning-function-pointer; sqlite VFS `xDlSym`): `6_7_6_21_field_ptr_to_func_returning_funcptr`
Known bugs caught by other harnesses
diff --git a/lang/c/parse/cg_adapter.c b/lang/c/parse/cg_adapter.c
@@ -396,7 +396,6 @@ void pcg_store(Parser* p) {
if (emit) cfree_cg_store(p->cg, pcg_mem(p, mem_ty ? mem_ty : rv_ty));
pcg_drop_type(p);
pcg_drop_type(p);
- pcg_push_type(p, rv_ty);
}
void pcg_deref(Parser* p, const Type* pointee) {
diff --git a/lang/c/parse/parse_expr.c b/lang/c/parse/parse_expr.c
@@ -2942,7 +2942,7 @@ static void parse_ternary(Parser* p) {
to_rvalue(p);
require_scalar(p, cg_top_type(p->cg), "conditional operator");
cg_branch_false(p->cg, L_else);
- parse_assign_expr(p);
+ parse_expr(p);
to_rvalue(p);
result_ty = cg_top_type(p->cg);
then_null = null_pointer_constant(p, result_ty);
diff --git a/lang/c/parse/parse_init.c b/lang/c/parse/parse_init.c
@@ -710,26 +710,67 @@ static void encode_binary128_from_double_le(u8 out[16], double value) {
encode_uint128_le(out, 16, lo, hi);
}
+static double parse_static_float_add(Parser* p);
+
+static double parse_static_float_primary(Parser* p) {
+ double v;
+ if (accept_punct(p, '+')) return parse_static_float_primary(p);
+ if (accept_punct(p, '-')) return -parse_static_float_primary(p);
+ if (accept_punct(p, '(')) {
+ v = parse_static_float_add(p);
+ expect_punct(p, ')', "')' in floating constant expression");
+ return v;
+ }
+ if (p->cur.kind == TOK_FLT) {
+ v = parse_float_literal(p, &p->cur);
+ advance(p);
+ return v;
+ }
+ if (p->cur.kind == TOK_NUM) {
+ v = (double)parse_int_literal(p, &p->cur);
+ advance(p);
+ return v;
+ }
+ perr(p, "expected floating constant expression");
+ return 0.0;
+}
+
+static double parse_static_float_mul(Parser* p) {
+ double v = parse_static_float_primary(p);
+ for (;;) {
+ if (accept_punct(p, '*')) {
+ v *= parse_static_float_primary(p);
+ } else if (accept_punct(p, '/')) {
+ v /= parse_static_float_primary(p);
+ } else {
+ return v;
+ }
+ }
+}
+
+static double parse_static_float_add(Parser* p) {
+ double v = parse_static_float_mul(p);
+ for (;;) {
+ if (accept_punct(p, '+')) {
+ v += parse_static_float_mul(p);
+ } else if (accept_punct(p, '-')) {
+ v -= parse_static_float_mul(p);
+ } else {
+ return v;
+ }
+ }
+}
+
static int try_parse_static_float(Parser* p, u8* dst, u32 size,
const Type* ty) {
const Type* uty = type_unqual(p->pool, ty);
double value;
- int neg = 0;
if (!uty ||
(uty->kind != TY_FLOAT && uty->kind != TY_DOUBLE &&
uty->kind != TY_LDOUBLE)) {
return 0;
}
- if (is_punct(&p->cur, '-') || is_punct(&p->cur, '+')) {
- neg = is_punct(&p->cur, '-');
- advance(p);
- }
- if (p->cur.kind != TOK_FLT && p->cur.kind != TOK_NUM)
- perr(p, "expected floating constant expression");
- value = p->cur.kind == TOK_FLT ? parse_float_literal(p, &p->cur)
- : (double)parse_int_literal(p, &p->cur);
- if (neg) value = -value;
- advance(p);
+ value = parse_static_float_add(p);
if (uty->kind == TY_FLOAT && size == 4u) {
union {
float f;
diff --git a/lang/c/parse/parse_type.c b/lang/c/parse/parse_type.c
@@ -1380,6 +1380,8 @@ const Type* parse_declarator_full_info(Parser* p, const Type* base,
SrcLoc nloc = {0, 0, 0};
u8 nptrs_inner = 0;
u16 inner_quals[8];
+ u8 nptrs_nested = 0;
+ u16 nested_quals[8];
int has_inner_parens = 0;
DeclSuffix inner_suffs[8];
int n_inner_suffs = 0;
@@ -1428,9 +1430,54 @@ const Type* parse_declarator_full_info(Parser* p, const Type* base,
name = p->cur.v.ident;
nloc = tok_loc(&p->cur);
advance(p);
+ } else if (is_punct(&p->cur, '(')) {
+ Tok nn = peek1(p);
+ if (!is_punct(&nn, '*')) {
+ if (!allow_abstract) perr(p, "expected declarator name");
+ goto after_inner_name;
+ }
+ advance(p); /* nested '(' */
+ while (accept_punct(p, '*')) {
+ u16 q = 0;
+ if (nptrs_nested >= 8) perr(p, "too many pointer levels");
+ for (;;) {
+ if (accept_kw(p, KW_CONST)) {
+ q |= Q_CONST;
+ continue;
+ }
+ if (accept_kw(p, KW_VOLATILE)) {
+ q |= Q_VOLATILE;
+ continue;
+ }
+ if (accept_kw(p, KW_RESTRICT)) {
+ q |= Q_RESTRICT;
+ continue;
+ }
+ if (accept_kw(p, KW_ATOMIC)) {
+ q |= Q_ATOMIC;
+ continue;
+ }
+ if (starts_attr(p)) {
+ parse_and_discard_attributes(p);
+ continue;
+ }
+ break;
+ }
+ nested_quals[nptrs_nested++] = q;
+ }
+ if (p->cur.kind == TOK_IDENT && ident_kw(p, p->cur.v.ident) == KW_NONE) {
+ name = p->cur.v.ident;
+ nloc = tok_loc(&p->cur);
+ advance(p);
+ } else if (!allow_abstract) {
+ perr(p, "expected declarator name");
+ }
+ if (starts_attr(p)) parse_and_discard_attributes(p);
+ expect_punct(p, ')', "')' after nested declarator");
} else if (!allow_abstract) {
perr(p, "expected declarator name");
}
+after_inner_name:
if (starts_attr(p)) parse_and_discard_attributes(p);
while (n_inner_suffs < 8) {
if (!parse_decl_suffix(p, &inner_suffs[n_inner_suffs])) break;
@@ -1497,6 +1544,13 @@ const Type* parse_declarator_full_info(Parser* p, const Type* base,
base = apply_decl_suffix(p, base, &inner_suffs[i]);
}
+ for (int i = (int)nptrs_nested - 1; i >= 0; --i) {
+ base = type_ptr(p->pool, base);
+ if (nested_quals[i]) {
+ base = type_qualified(p->pool, base, nested_quals[i]);
+ }
+ }
+
if (info_out && base && base->kind == TY_FUNC && final_fn_suff) {
info_out->fn_params = final_fn_suff->params;
info_out->fn_nparams = final_fn_suff->nparams;