commit b30bada9e8811cac46685ca575d8d1acd6f69479
parent bbe0c3e30b210857cf082638b7cc4ecfa2b3e022
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Tue, 19 May 2026 11:30:51 -0700
Fix parser constraint diagnostics
Diffstat:
7 files changed, 242 insertions(+), 5 deletions(-)
diff --git a/lang/c/parse/cg_adapter.c b/lang/c/parse/cg_adapter.c
@@ -16,6 +16,7 @@ static u32 pcg_alignof(Parser* p, const Type* ty) {
#define PCG_VALUE_MODIFIABLE 2u
#define PCG_VALUE_BITFIELD 4u
#define PCG_VALUE_NULL_PTR_CONST 8u
+#define PCG_VALUE_REGISTER 16u
static u8 pcg_lvalue_flags_for_type(const Type* ty) {
u8 flags = PCG_VALUE_LVALUE;
@@ -126,6 +127,16 @@ void pcg_set_top_bitfield(Parser* p) {
if (p->cg_type_sp) p->cg_value_flags[p->cg_type_sp - 1u] |= PCG_VALUE_BITFIELD;
}
+int pcg_top_is_register(Parser* p) {
+ return p->cg_type_sp &&
+ (p->cg_value_flags[p->cg_type_sp - 1u] & PCG_VALUE_REGISTER) != 0;
+}
+
+void pcg_set_top_register(Parser* p) {
+ if (p->cg_type_sp)
+ p->cg_value_flags[p->cg_type_sp - 1u] |= PCG_VALUE_REGISTER;
+}
+
int pcg_top_is_lvalue(Parser* p) {
return p->cg_type_sp &&
(p->cg_value_flags[p->cg_type_sp - 1u] & PCG_VALUE_LVALUE) != 0;
diff --git a/lang/c/parse/cg_public_compat.h b/lang/c/parse/cg_public_compat.h
@@ -178,6 +178,8 @@ void pcg_dup_type(Parser*);
void pcg_swap_type(Parser*);
int pcg_top_is_bitfield(Parser*);
void pcg_set_top_bitfield(Parser*);
+int pcg_top_is_register(Parser*);
+void pcg_set_top_register(Parser*);
int pcg_top_is_lvalue(Parser*);
int pcg_top_is_modifiable_lvalue(Parser*);
int pcg_top_is_null_ptr_const(Parser*);
diff --git a/lang/c/parse/parse.c b/lang/c/parse/parse.c
@@ -291,13 +291,17 @@ Scope* scope_new(Parser* p, Scope* parent) {
s->entries = NULL;
s->tags = NULL;
s->parent = parent;
+ s->saved_vla_mark = p->vla_mark;
return s;
}
void scope_push(Parser* p) { p->scope = scope_new(p, p->scope); }
void scope_pop(Parser* p) {
- if (p->scope) p->scope = p->scope->parent;
+ if (p->scope) {
+ p->vla_mark = p->scope->saved_vla_mark;
+ p->scope = p->scope->parent;
+ }
}
SymEntry* scope_define(Parser* p, Sym name, SymEntryKind kind,
@@ -469,6 +473,13 @@ static void parse_init_declarator(Parser* p, const DeclSpecs* specs) {
SrcLoc loc;
Sym name;
const Type* var_ty = parse_declarator(p, specs->type, &name, &loc);
+ if ((specs->flags & DF_THREAD) &&
+ specs->storage != DS_STATIC && specs->storage != DS_EXTERN) {
+ perr(p, "block-scope _Thread_local requires static or extern");
+ }
+ validate_decl_type_constraints(p, specs, var_ty,
+ var_ty && var_ty->kind == TY_FUNC,
+ /*is_member=*/0);
if (specs->storage == DS_TYPEDEF) {
if (is_punct(&p->cur, '=')) {
@@ -613,6 +624,7 @@ static void parse_init_declarator(Parser* p, const DeclSpecs* specs) {
bsd.align = c_abi_alignof(p->abi, bsd.type);
bsd.kind = FS_LOCAL;
byte_slot = cg_local(p->cg, &bsd);
+ ++p->vla_mark;
p->vla_pending = 0;
p->vla_pending_count_slot = FRAME_SLOT_NONE;
cg_set_loc(p->cg, loc);
@@ -656,11 +668,19 @@ static void parse_init_declarator(Parser* p, const DeclSpecs* specs) {
advance(p); /* '=' */
var_ty = complete_incomplete_array(p, var_ty);
s = make_local_aligned(p, name, var_ty, loc, specs->align);
+ if (specs->storage == DS_REGISTER) {
+ SymEntry* e = scope_lookup_current(p, name);
+ if (e && e->kind == SEK_LOCAL) e->storage = DS_REGISTER;
+ }
cg_set_loc(p->cg, loc);
init_at(p, s, var_ty, 0, var_ty);
return;
}
s = make_local_aligned(p, name, var_ty, loc, specs->align);
+ if (specs->storage == DS_REGISTER) {
+ SymEntry* e = scope_lookup_current(p, name);
+ if (e && e->kind == SEK_LOCAL) e->storage = DS_REGISTER;
+ }
if (accept_punct(p, '=')) {
cg_set_loc(p->cg, loc);
if ((var_ty->kind == TY_STRUCT || var_ty->kind == TY_UNION) &&
@@ -735,6 +755,10 @@ void parse_param_list(Parser* p, ParamInfo** infos_out, u16* nparams_out,
if (!parse_decl_specs(p, &specs)) {
perr(p, "expected parameter type");
}
+ if ((specs.storage_explicit && specs.storage != DS_REGISTER) ||
+ specs.storage == DS_TYPEDEF || (specs.flags & DF_THREAD)) {
+ perr(p, "invalid storage-class specifier in parameter declaration");
+ }
p->in_param_decl++;
pty = parse_declarator_full(p, specs.type, /*allow_abstract=*/1, &pname,
&ploc);
@@ -747,6 +771,8 @@ void parse_param_list(Parser* p, ParamInfo** infos_out, u16* nparams_out,
if (pty && pty->kind == TY_VOID) {
perr(p, "'void' must be the only parameter");
}
+ validate_decl_type_constraints(p, &specs, pty, /*is_function=*/0,
+ /*is_member=*/0);
if (pname) {
for (u32 pi = 0; pi < n; ++pi) {
if (infos[pi].name == pname) perr(p, "redefinition of parameter");
@@ -918,6 +944,10 @@ static void parse_external_decl(Parser* p) {
if (!parse_decl_specs(p, &specs)) {
perr(p, "expected declaration");
}
+ if (specs.storage == DS_REGISTER ||
+ (specs.storage == DS_AUTO && specs.storage_explicit)) {
+ perr(p, "invalid storage-class specifier at file scope");
+ }
if (accept_punct(p, ';')) return;
@@ -928,6 +958,9 @@ static void parse_external_decl(Parser* p) {
const Type* tty =
parse_declarator_full(p, specs.type,
/*allow_abstract=*/0, &tname, &tloc);
+ validate_decl_type_constraints(p, &specs, tty,
+ tty && tty->kind == TY_FUNC,
+ /*is_member=*/0);
if (is_punct(&p->cur, '=')) {
perr(p, "typedef declarator cannot have initializer");
}
@@ -983,6 +1016,8 @@ static void parse_external_decl(Parser* p) {
for (u16 i = 0; i < nparams; ++i) ptypes[i] = infos[i].type;
}
fn_ty = type_func(p->pool, base_ty, ptypes, nparams, (int)variadic);
+ validate_decl_type_constraints(p, &specs, fn_ty, /*is_function=*/1,
+ /*is_member=*/0);
abi = c_abi_func_info(p->abi, p->pool, fn_ty);
ObjSecId fn_section_id;
@@ -1047,6 +1082,8 @@ static void parse_external_decl(Parser* p) {
if (existing && existing->kind != SEK_GLOBAL) {
perr(p, "redefinition of identifier");
}
+ validate_decl_type_constraints(p, &specs, base_ty, /*is_function=*/0,
+ /*is_member=*/0);
if (existing && existing->kind == SEK_GLOBAL) {
const Type* composite = NULL;
diff --git a/lang/c/parse/parse_expr.c b/lang/c/parse/parse_expr.c
@@ -1825,6 +1825,7 @@ static void parse_primary(Parser* p) {
switch (e->kind) {
case SEK_LOCAL:
cg_push_local_typed(p->cg, e->v.slot, e->type);
+ if (e->storage == DS_REGISTER) pcg_set_top_register(p);
if (e->vla_byte_slot != FRAME_SLOT_NONE) {
p->last_pushed_vla_slot = e->vla_byte_slot;
}
@@ -2172,6 +2173,8 @@ void parse_unary(Parser* p) {
perr(p, "address-of requires lvalue operand");
}
if (pcg_top_is_bitfield(p)) perr(p, "cannot take address of bit-field");
+ if (pcg_top_is_register(p))
+ perr(p, "cannot take address of register object");
cg_addr(p->cg);
return;
}
@@ -2542,6 +2545,8 @@ static void emit_add_or_sub(Parser* p, BinOp bop) {
perr(p, "invalid operands to binary +");
}
if (l_is_ptr && type_is_int(rt)) {
+ if (lt->ptr.pointee && lt->ptr.pointee->kind == TY_VOID)
+ perr(p, "pointer arithmetic on void pointer");
u32 esz = c_abi_sizeof(p->abi, lt->ptr.pointee);
if (esz != 1) {
cg_push_int(p->cg, (i64)esz, ty_size_t(p));
@@ -2551,6 +2556,8 @@ static void emit_add_or_sub(Parser* p, BinOp bop) {
return;
}
if (r_is_ptr && type_is_int(lt)) {
+ if (rt->ptr.pointee && rt->ptr.pointee->kind == TY_VOID)
+ perr(p, "pointer arithmetic on void pointer");
cg_swap(p->cg);
u32 esz = c_abi_sizeof(p->abi, rt->ptr.pointee);
if (esz != 1) {
@@ -2562,6 +2569,8 @@ static void emit_add_or_sub(Parser* p, BinOp bop) {
}
} else { /* BO_ISUB */
if (l_is_ptr && type_is_int(rt)) {
+ if (lt->ptr.pointee && lt->ptr.pointee->kind == TY_VOID)
+ perr(p, "pointer arithmetic on void pointer");
u32 esz = c_abi_sizeof(p->abi, lt->ptr.pointee);
if (esz != 1) {
cg_push_int(p->cg, (i64)esz, ty_size_t(p));
diff --git a/lang/c/parse/parse_priv.h b/lang/c/parse/parse_priv.h
@@ -123,6 +123,7 @@ struct Scope {
SymEntry* entries; /* LIFO */
TagEntry* tags; /* LIFO */
Scope* parent;
+ u32 saved_vla_mark;
};
/* ============================================================
@@ -152,6 +153,8 @@ struct GotoLabel {
u8 placed;
u8 pad[3];
SrcLoc first_use;
+ u32 min_forward_vla_mark;
+ u32 label_vla_mark;
GotoLabel* next;
};
@@ -240,6 +243,7 @@ typedef struct Parser {
u8 vla_pending;
FrameSlot vla_pending_count_slot;
+ u32 vla_mark;
FrameSlot last_pushed_vla_slot;
@@ -275,6 +279,8 @@ typedef struct DeclSpecs {
DeclStorage storage;
u32 flags; /* DeclFlag */
u16 quals;
+ u8 storage_explicit;
+ u8 pad;
u32 align;
FrameSlot vla_byte_slot;
Attr* attrs;
@@ -411,6 +417,9 @@ void attr_list_append(Attr** head, Attr* add);
void parse_attrs_into(Parser* p, Attr** sink);
int parse_decl_suffix(Parser* p, DeclSuffix* out);
const Type* apply_decl_suffix(Parser* p, const Type* base, const DeclSuffix* s);
+void validate_decl_type_constraints(Parser* p, const DeclSpecs* specs,
+ const Type* ty, int is_function,
+ int is_member);
/* parse_expr.c */
void parse_expr(Parser* p);
diff --git a/lang/c/parse/parse_stmt.c b/lang/c/parse/parse_stmt.c
@@ -223,6 +223,8 @@ static GotoLabel* label_get_or_create(Parser* p, Sym name, SrcLoc loc) {
gl->label = cg_label_new(p->cg);
gl->placed = 0;
gl->first_use = loc;
+ gl->min_forward_vla_mark = p->vla_mark;
+ gl->label_vla_mark = 0;
gl->next = p->goto_labels;
p->goto_labels = gl;
return gl;
@@ -240,6 +242,13 @@ static void parse_goto_stmt(Parser* p) {
advance(p);
expect_punct(p, ';', "';' after goto");
gl = label_get_or_create(p, name, loc);
+ if (gl->placed) {
+ if (p->vla_mark < gl->label_vla_mark) {
+ perr(p, "goto into scope of variably modified object");
+ }
+ } else if (p->vla_mark < gl->min_forward_vla_mark) {
+ gl->min_forward_vla_mark = p->vla_mark;
+ }
cg_jump(p->cg, gl->label);
}
@@ -251,7 +260,11 @@ static void parse_label_stmt(Parser* p) {
advance(p); /* ':' */
gl = label_get_or_create(p, name, loc);
if (gl->placed) perr(p, "duplicate label");
+ if (gl->min_forward_vla_mark < p->vla_mark) {
+ perr(p, "goto into scope of variably modified object");
+ }
gl->placed = 1;
+ gl->label_vla_mark = p->vla_mark;
cg_label_place(p->cg, gl->label);
parse_stmt(p);
}
@@ -306,6 +319,7 @@ static void parse_switch_stmt(Parser* p) {
to_rvalue(p);
vty = cg_top_type(p->cg);
if (!vty) vty = type_prim(p->pool, TY_INT);
+ if (!type_is_int(vty)) perr(p, "switch expression requires integer type");
expect_punct(p, ')', "')' after switch expression");
memset(&ctx, 0, sizeof ctx);
diff --git a/lang/c/parse/parse_type.c b/lang/c/parse/parse_type.c
@@ -366,12 +366,78 @@ u32 attrs_pick_aligned(const Attr* a) {
return best;
}
+static int is_power_of_two_u32(u32 v) {
+ return v != 0 && (v & (v - 1u)) == 0;
+}
+
+static void validate_atomic_operand(Parser* p, const Type* ty) {
+ if (!ty) perr(p, "_Atomic requires an object type");
+ if (ty->qual) perr(p, "_Atomic operand must not be qualified");
+ if (ty->kind == TY_ARRAY || ty->kind == TY_FUNC || ty->kind == TY_VOID) {
+ perr(p, "_Atomic operand must be an object type");
+ }
+}
+
+void validate_decl_type_constraints(Parser* p, const DeclSpecs* specs,
+ const Type* ty, int is_function,
+ int is_member) {
+ const Type* u = ty ? type_unqual(p->pool, ty) : NULL;
+ if (!u) return;
+ if (specs->quals & Q_RESTRICT) {
+ perr(p, "restrict requires pointer type");
+ }
+ if ((ty->qual & Q_RESTRICT) && ty->kind != TY_PTR) {
+ perr(p, "restrict requires pointer type");
+ }
+ if (is_member) {
+ if (specs->storage != DS_AUTO || specs->storage_explicit) {
+ perr(p, "storage-class specifier is invalid for struct member");
+ }
+ if (specs->flags & DF_INLINE) perr(p, "inline is invalid for struct member");
+ if (specs->flags & DF_NORETURN)
+ perr(p, "_Noreturn is invalid for struct member");
+ if (specs->flags & DF_THREAD)
+ perr(p, "_Thread_local is invalid for struct member");
+ }
+ if (u->kind == TY_VOID && !is_function) {
+ perr(p, "object may not have void type");
+ }
+ if ((specs->flags & DF_INLINE) && !is_function) {
+ perr(p, "inline may only appear on a function declaration");
+ }
+ if ((specs->flags & DF_NORETURN) && !is_function) {
+ perr(p, "_Noreturn may only appear on a function declaration");
+ }
+ if ((specs->flags & DF_THREAD) && (is_function || specs->storage == DS_TYPEDEF)) {
+ perr(p, "_Thread_local may only appear on object declarations");
+ }
+ if (specs->align) {
+ u32 natural = 0;
+ if (!is_power_of_two_u32(specs->align)) {
+ perr(p, "_Alignas requires a power-of-two alignment");
+ }
+ if (is_function || specs->storage == DS_TYPEDEF) {
+ perr(p, "_Alignas is invalid on this declaration");
+ }
+ if (u->kind == TY_VOID || u->kind == TY_FUNC) {
+ perr(p, "_Alignas requires an object type");
+ }
+ natural = c_abi_alignof(p->abi, ty);
+ if (specs->align < natural) {
+ perr(p, "_Alignas cannot weaken natural alignment");
+ }
+ }
+}
+
/* ============================================================
* resolve_type_specs
* ============================================================ */
const Type* resolve_type_specs(Parser* p, const TypeSpecAccum* a, SrcLoc loc) {
if (!a->saw_explicit_type) return NULL;
+ if (a->long_count > 2) {
+ compiler_panic(p->c, loc, "too many long type specifiers");
+ }
if (a->saw_void) {
if (a->saw_char || a->saw_int || a->saw_short || a->long_count ||
a->saw_signed || a->saw_unsigned || a->saw_bool || a->saw_float ||
@@ -381,18 +447,39 @@ const Type* resolve_type_specs(Parser* p, const TypeSpecAccum* a, SrcLoc loc) {
return type_void(p->pool);
}
if (a->saw_bool) {
+ if (a->saw_char || a->saw_int || a->saw_short || a->long_count ||
+ a->saw_signed || a->saw_unsigned || a->saw_float || a->saw_double) {
+ compiler_panic(p->c, loc, "conflicting type specifiers (_Bool mixed)");
+ }
return type_prim(p->pool, TY_BOOL);
}
if (a->saw_char) {
+ if (a->saw_int || a->saw_short || a->long_count || a->saw_float ||
+ a->saw_double) {
+ compiler_panic(p->c, loc, "conflicting type specifiers (char mixed)");
+ }
if (a->saw_unsigned) return type_prim(p->pool, TY_UCHAR);
if (a->saw_signed) return type_prim(p->pool, TY_SCHAR);
return type_prim(p->pool, TY_CHAR);
}
- if (a->saw_float) return type_prim(p->pool, TY_FLOAT);
+ if (a->saw_float) {
+ if (a->saw_int || a->saw_short || a->long_count || a->saw_signed ||
+ a->saw_unsigned || a->saw_double) {
+ compiler_panic(p->c, loc, "conflicting type specifiers (float mixed)");
+ }
+ return type_prim(p->pool, TY_FLOAT);
+ }
if (a->saw_double) {
+ if (a->saw_int || a->saw_short || a->saw_signed || a->saw_unsigned ||
+ a->long_count > 1) {
+ compiler_panic(p->c, loc, "conflicting type specifiers (double mixed)");
+ }
return type_prim(p->pool, a->long_count ? TY_LDOUBLE : TY_DOUBLE);
}
if (a->saw_short) {
+ if (a->long_count) {
+ compiler_panic(p->c, loc, "conflicting type specifiers (short long)");
+ }
return type_prim(p->pool, a->saw_unsigned ? TY_USHORT : TY_SHORT);
}
if (a->saw_int128) {
@@ -424,6 +511,8 @@ int parse_decl_specs(Parser* p, DeclSpecs* out) {
out->storage = DS_AUTO;
out->flags = DF_NONE;
out->quals = 0;
+ out->storage_explicit = 0;
+ out->pad = 0;
out->align = 0;
out->vla_byte_slot = FRAME_SLOT_NONE;
out->attrs = NULL;
@@ -536,12 +625,14 @@ int parse_decl_specs(Parser* p, DeclSpecs* out) {
} else if (is_kw(p, &t, KW_STATIC)) {
if (storage_seen) perr(p, "multiple storage-class specifiers");
storage_seen = 1;
+ out->storage_explicit = 1;
out->storage = DS_STATIC;
advance(p);
seen = 1;
} else if (is_kw(p, &t, KW_EXTERN)) {
if (storage_seen) perr(p, "multiple storage-class specifiers");
storage_seen = 1;
+ out->storage_explicit = 1;
out->storage = DS_EXTERN;
advance(p);
seen = 1;
@@ -568,6 +659,7 @@ int parse_decl_specs(Parser* p, DeclSpecs* out) {
advance(p); /* `(` */
inner = parse_type_name(p);
expect_punct(p, ')', "')' after _Atomic type");
+ validate_atomic_operand(p, inner);
tagged_ty = type_qualified(p->pool, inner, Q_ATOMIC);
acc.saw_explicit_type = 1;
seen = 1;
@@ -579,6 +671,7 @@ int parse_decl_specs(Parser* p, DeclSpecs* out) {
} else if (is_kw(p, &t, KW_TYPEDEF)) {
if (storage_seen) perr(p, "multiple storage-class specifiers");
storage_seen = 1;
+ out->storage_explicit = 1;
out->storage = DS_TYPEDEF;
advance(p);
seen = 1;
@@ -594,6 +687,9 @@ int parse_decl_specs(Parser* p, DeclSpecs* out) {
if (v < 0) perr(p, "_Alignas requires a non-negative alignment");
a = (u32)v;
}
+ if (a != 0 && !is_power_of_two_u32(a)) {
+ perr(p, "_Alignas requires a power-of-two alignment");
+ }
expect_punct(p, ')', "')' after _Alignas argument");
if (a > out->align) out->align = a;
seen = 1;
@@ -606,17 +702,20 @@ int parse_decl_specs(Parser* p, DeclSpecs* out) {
advance(p);
seen = 1;
} else if (is_kw(p, &t, KW_NORETURN)) {
+ out->flags |= DF_NORETURN;
advance(p);
seen = 1;
} else if (is_kw(p, &t, KW_REGISTER)) {
if (storage_seen) perr(p, "multiple storage-class specifiers");
storage_seen = 1;
+ out->storage_explicit = 1;
out->storage = DS_REGISTER;
advance(p);
seen = 1;
} else if (is_kw(p, &t, KW_AUTO)) {
if (storage_seen) perr(p, "multiple storage-class specifiers");
storage_seen = 1;
+ out->storage_explicit = 1;
out->storage = DS_AUTO;
advance(p);
seen = 1;
@@ -698,7 +797,56 @@ int find_field(TargetABI* abi, Pool* pool, const Type* rec, Sym name,
return 0;
}
+typedef struct MemberNameSeen {
+ Sym name;
+ struct MemberNameSeen* next;
+} MemberNameSeen;
+
+static int member_name_seen(MemberNameSeen* names, Sym name) {
+ for (; names; names = names->next) {
+ if (names->name == name) return 1;
+ }
+ return 0;
+}
+
+static void member_name_add(Parser* p, MemberNameSeen** names, Sym name) {
+ MemberNameSeen* n;
+ if (!name) return;
+ n = arena_new(p->pool->arena, MemberNameSeen);
+ if (!n) perr(p, "out of memory tracking struct members");
+ n->name = name;
+ n->next = *names;
+ *names = n;
+}
+
+static void validate_and_add_field(Parser* p, TypeRecordBuilder* b,
+ const DeclSpecs* specs, Field* f,
+ MemberNameSeen** names, u32* field_count,
+ int* saw_flexible) {
+ int is_flexible = f->type && f->type->kind == TY_ARRAY && f->type->arr.incomplete;
+ validate_decl_type_constraints(p, specs, f->type, /*is_function=*/0,
+ /*is_member=*/1);
+ if ((f->flags & FIELD_BITFIELD) && specs->align) {
+ perr(p, "_Alignas is invalid on bit-field");
+ }
+ if (f->name && member_name_seen(*names, f->name)) {
+ perr(p, "duplicate member name");
+ }
+ if (*saw_flexible) perr(p, "flexible array member must be last");
+ if (is_flexible) {
+ if (*field_count == 0) perr(p, "flexible array member cannot be only member");
+ f->flags |= FIELD_FLEXIBLE_ARRAY;
+ *saw_flexible = 1;
+ }
+ member_name_add(p, names, f->name);
+ type_record_field(b, *f);
+ ++*field_count;
+}
+
static void parse_member_decls(Parser* p, TypeRecordBuilder* b) {
+ MemberNameSeen* names = NULL;
+ u32 field_count = 0;
+ int saw_flexible = 0;
while (!is_punct(&p->cur, '}') && p->cur.kind != TOK_EOF) {
DeclSpecs specs;
if (!parse_decl_specs(p, &specs)) {
@@ -712,7 +860,8 @@ static void parse_member_decls(Parser* p, TypeRecordBuilder* b) {
f.name = 0;
f.type = specs.type;
f.flags = FIELD_ANON;
- type_record_field(b, f);
+ validate_and_add_field(p, b, &specs, &f, &names, &field_count,
+ &saw_flexible);
advance(p);
continue;
}
@@ -739,7 +888,8 @@ static void parse_member_decls(Parser* p, TypeRecordBuilder* b) {
if (w == 0) f.flags |= FIELD_ZERO_WIDTH;
attrs_to_field(specs.attrs, &f);
if (specs.align > f.align_override) f.align_override = (u16)specs.align;
- type_record_field(b, f);
+ validate_and_add_field(p, b, &specs, &f, &names, &field_count,
+ &saw_flexible);
if (!accept_punct(p, ',')) break;
continue;
}
@@ -773,7 +923,8 @@ static void parse_member_decls(Parser* p, TypeRecordBuilder* b) {
attrs_to_field(trailing, &f);
}
if (specs.align > f.align_override) f.align_override = (u16)specs.align;
- type_record_field(b, f);
+ validate_and_add_field(p, b, &specs, &f, &names, &field_count,
+ &saw_flexible);
if (!accept_punct(p, ',')) break;
}
expect_punct(p, ';', "';' after struct member declaration");
@@ -1069,6 +1220,9 @@ int parse_decl_suffix(Parser* p, DeclSuffix* out) {
{
Tok t = p->cur;
int is_const_start = (t.kind == TOK_NUM || t.kind == TOK_CHR);
+ if (t.kind == TOK_FLT) {
+ perr(p, "array bound requires integer type");
+ }
if (!is_const_start && t.kind == TOK_IDENT) {
SymEntry* e = scope_lookup(p, t.v.ident);
if (e && e->kind == SEK_ENUM_CST) is_const_start = 1;
@@ -1102,6 +1256,7 @@ int parse_decl_suffix(Parser* p, DeclSuffix* out) {
cg_store(p->cg);
cg_drop(p->cg);
p->vla_pending = 1;
+ ++p->vla_mark;
p->vla_pending_count_slot = out->vla_count_slot;
}
}