From 5d9823af30703f5b965a7739205d719e8bf05ce3 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Sat, 27 May 2023 00:59:43 +0900 Subject: [PATCH] [qfcc] Implement designated initializers Conforms fairly closely to GCC's C implementation. --- tools/qfcc/include/expr.h | 11 +- tools/qfcc/include/symtab.h | 1 + tools/qfcc/source/expr.c | 2 +- tools/qfcc/source/expr_compound.c | 221 ++++++++++++++++++++++-------- tools/qfcc/source/qc-parse.y | 28 +++- tools/qfcc/source/struct.c | 1 + 6 files changed, 201 insertions(+), 63 deletions(-) diff --git a/tools/qfcc/include/expr.h b/tools/qfcc/include/expr.h index b2b7631f3..c6f45c3dc 100644 --- a/tools/qfcc/include/expr.h +++ b/tools/qfcc/include/expr.h @@ -74,12 +74,18 @@ typedef struct { ex_label_t *label; } ex_labelref_t; +typedef struct designator_s { + struct designator_s *next; + struct expr_s *field; + struct expr_s *index; +} designator_t; + typedef struct element_s { struct element_s *next; ///< next in chain int offset; struct type_s *type; struct expr_s *expr; ///< initializer expression - struct symbol_s *symbol; ///< for labeled initializers + designator_t *designator; ///< for labeled initializers } element_t; typedef struct element_chain_s { @@ -448,7 +454,8 @@ expr_t *new_block_expr (void); */ expr_t *build_block_expr (expr_t *expr_list); -element_t *new_element (expr_t *expr, struct symbol_s *symbol); +designator_t *new_designator (expr_t *field, expr_t *index); +element_t *new_element (expr_t *expr, designator_t *designator); expr_t *new_compound_init (void); expr_t *append_element (expr_t *compound, element_t *element); expr_t *initialized_temp_expr (const struct type_s *type, expr_t *compound); diff --git a/tools/qfcc/include/symtab.h b/tools/qfcc/include/symtab.h index ad6204f8d..8d8943ef7 100644 --- a/tools/qfcc/include/symtab.h +++ b/tools/qfcc/include/symtab.h @@ -72,6 +72,7 @@ typedef struct symbol_s { sy_type_e sy_type; ///< symbol type struct type_s *type; ///< type of object to which symbol refers struct param_s *params; ///< the parameters if a function + unsigned no_auto_init; ///< skip for non-designated initializers union { int offset; ///< sy_var (in a struct/union) struct def_s *def; ///< sy_var diff --git a/tools/qfcc/source/expr.c b/tools/qfcc/source/expr.c index 3e25ee5b1..6f84bfdea 100644 --- a/tools/qfcc/source/expr.c +++ b/tools/qfcc/source/expr.c @@ -370,7 +370,7 @@ copy_expr (expr_t *e) n = new_expr (); *n = *e; for (element_t *i = e->e.compound.head; i; i = i->next) { - append_element (n, new_element (i->expr, i->symbol)); + append_element (n, new_element (i->expr, i->designator)); } return n; case ex_memset: diff --git a/tools/qfcc/source/expr_compound.c b/tools/qfcc/source/expr_compound.c index eb5dafae8..df382809f 100644 --- a/tools/qfcc/source/expr_compound.c +++ b/tools/qfcc/source/expr_compound.c @@ -53,14 +53,31 @@ #include "tools/qfcc/include/type.h" ALLOC_STATE (element_t, elements); +ALLOC_STATE (designator_t, designators); + +designator_t * +new_designator (expr_t *field, expr_t *index) +{ + if ((!field && !index) || (field && index)) { + internal_error (0, "exactly one of field or index is required"); + } + if (field && field->type != ex_symbol) { + internal_error (field, "invalid field designator"); + } + designator_t *des; + ALLOC (256, designator_t, designators, des); + des->field = field; + des->index = index; + return des; +} element_t * -new_element (expr_t *expr, symbol_t *symbol) +new_element (expr_t *expr, designator_t *designator) { element_t *element; ALLOC (256, element_t, elements, element); element->expr = expr; - element->symbol = symbol; + element->designator = designator; return element; } @@ -83,29 +100,99 @@ new_compound_init (void) return c; } -static element_t * -build_array_element_chain(element_chain_t *element_chain, - int array_size, type_t *array_type, - element_t *ele, - int base_offset) +static symbol_t * +designator_field (const designator_t *des, const type_t *type) { - for (int i = 0; i < array_size; i++) { - int offset = base_offset + i * type_size (array_type); - if (ele && ele->expr && ele->expr->type == ex_compound) { - build_element_chain (element_chain, array_type, - ele->expr, offset); - } else { - element_t *element = new_element (0, 0); - element->type = array_type; - element->offset = offset; - element->expr = ele ? ele->expr : 0; // null -> nil - append_init_element (element_chain, element); - } - if (ele) { - ele = ele->next; - } + if (des->index) { + error (des->index, "designator index in non-array"); + return 0; } - return ele; + symtab_t *symtab = type->t.symtab; + symbol_t *sym = des->field->e.symbol; + symbol_t *field = symtab_lookup (symtab, sym->name);; + if (!field) { + const char *name = type->name; + if (!strncmp (name, "tag ", 4)) { + name += 4; + } + error (des->field, "'%s' has no member named '%s'", name, sym->name); + return 0; + } + return field; +} + +static int +designator_index (const designator_t *des, int ele_size, int array_size) +{ + if (des->field) { + error (des->field, "field designator in array initializer"); + return -1; + } else if (!is_constant (des->index)) { + error (des->index, "non-constant designator index"); + return -1; + } else if (!is_integral (get_type (des->index))) { + error (des->index, "invalid designator index type"); + return -1; + } + int index = expr_integral (des->index); + if (index <= 0 || index >= array_size) { + error (des->index, "designator index out of bounds"); + return -1; + } + return index * ele_size; +} + +typedef struct { + type_t *type; + symbol_t *field; + int offset; +} initstate_t; + +static initstate_t +get_designated_offset (const type_t *type, const designator_t *des) +{ + int offset = -1; + type_t *ele_type = 0; + symbol_t *field = 0; + + if (is_struct (type) || is_union (type)) { + field = designator_field (des, type); + offset = field->s.offset; + ele_type = field->type; + } else if (is_array (type)) { + int array_size = type->t.array.size; + ele_type = type->t.array.type; + offset = designator_index (des, type_size (ele_type), array_size); + } else if (is_nonscalar (type)) { + ele_type = ev_types[type->type]; + if (type->t.symtab && des->field) { + field = designator_field (des, type); + offset = field->s.offset; + } else { + int vec_width = type_width (type); + offset = designator_index (des, type_size (ele_type), vec_width); + } + } else { + error (0, "invalid initializer"); + } + if (ele_type && des->next) { + __auto_type state = get_designated_offset (ele_type, des->next); + ele_type = state.type; + offset += state.offset; + } + return (initstate_t) { .type = ele_type, .field = field, .offset = offset}; +} + +static int +skip_field (symbol_t *field) +{ + if (field->sy_type != sy_var) { + return 1; + } + if (field->no_auto_init) { + return 1; + } + return 0; } void @@ -116,50 +203,64 @@ build_element_chain (element_chain_t *element_chain, const type_t *type, type = unalias_type (type); - if (is_array (type)) { - type_t *array_type = type->t.array.type; - int array_size = type->t.array.size; - ele = build_array_element_chain (element_chain, array_size, array_type, - ele, base_offset); - } else if (is_struct (type) || (is_nonscalar (type) && type->t.symtab)) { - symtab_t *symtab = type->t.symtab; - symbol_t *field; + initstate_t state = {}; + if (is_struct (type) || is_union (type) + || (is_nonscalar (type) && type->t.symtab)) { + state.field = type->t.symtab->symbols; + while (skip_field (state.field)) { + state.field = state.field->next; + } + state.type = state.field->type; + state.offset = state.field->s.offset; + } else if (is_array (type)) { + state.type = type->t.array.type; + } else { + internal_error (eles, "invalid initialization"); + } + while (ele) { + if (ele->designator) { + state = get_designated_offset (type, ele->designator); + } + if (!state.type) { + break; + } - for (field = symtab->symbols; field; field = field->next) { - int offset = base_offset + field->s.offset; - if (field->sy_type != sy_var - || field->visibility == vis_anonymous) { - continue; + if (state.offset >= type_size (type)) { + if (options.warnings.initializer) { + warning (eles, "excessive elements in initializer"); } - if (ele && ele->expr && ele->expr->type == ex_compound) { - build_element_chain (element_chain, field->type, - ele->expr, offset); - } else { - element_t *element = new_element (0, 0); - element->type = field->type; - element->offset = offset; - element->expr = ele ? ele->expr : 0; // null -> nil - append_init_element (element_chain, element); + break; + } + + if (ele->expr && ele->expr->type == ex_compound) { + build_element_chain (element_chain, state.type, ele->expr, + state.offset); + } else { + element_t *element = new_element (0, 0); + element->type = state.type; + element->offset = base_offset + state.offset; + element->expr = ele->expr; // null -> nil + append_init_element (element_chain, element); + } + + state.offset += type_size (state.type); + if (state.field) { + state.field = state.field->next; + while (state.field && skip_field (state.field)) { + state.field = state.field->next; } - if (ele) { - ele = ele->next; + if (state.field) { + state.type = state.field->type; + state.offset = state.field->s.offset; } } - } else if (is_nonscalar (type)) { - // vector type with unnamed components - int vec_width = type_width (type); - type_t *vec_type = ev_types[type->type]; - ele = build_array_element_chain (element_chain, vec_width, vec_type, - ele, base_offset); - } else { - error (eles, "invalid initializer"); - } - if (ele && ele->next && options.warnings.initializer) { - warning (eles, "excessive elements in initializer"); + + ele = ele->next; } } -void free_element_chain (element_chain_t *element_chain) +void +free_element_chain (element_chain_t *element_chain) { *element_chain->tail = elements_freelist; elements_freelist = element_chain->head; @@ -200,6 +301,8 @@ assign_elements (expr_t *local_expr, expr_t *init, expr_t *c; + if (type_size (type) == 0) + internal_error (init, "wtf"); if (element->expr) { c = constant_expr (element->expr); } else { diff --git a/tools/qfcc/source/qc-parse.y b/tools/qfcc/source/qc-parse.y index 75df40e0f..26e053730 100644 --- a/tools/qfcc/source/qc-parse.y +++ b/tools/qfcc/source/qc-parse.y @@ -114,6 +114,7 @@ int yylex (void); struct symbol_s *symbol; struct symtab_s *symtab; struct attribute_s *attribute; + struct designator_s *designator; } // these tokens are common between qc and qp @@ -194,6 +195,7 @@ int yylex (void); %type opt_init_semi opt_expr comma_expr expr %type compound_init element_list +%type designator designator_spec %type element %type ose optional_state_expr texpr vector_expr %type statement statements compound_statement @@ -1146,6 +1148,9 @@ struct_defs component_decl_list : component_decl_list2 | component_decl_list2 component_decl + { + warning (0, "no semicolon at end of struct or union"); + } ; component_decl_list2 @@ -1405,10 +1410,31 @@ element_list ; element - : compound_init { $$ = new_element ($1, 0); } + : designator '=' compound_init { $$ = new_element ($3, $1); } + | designator '=' expr { $$ = new_element ($3, $1); } + | compound_init { $$ = new_element ($1, 0); } | expr { $$ = new_element ($1, 0); } ; +designator + : designator_spec + | designator designator_spec + { + designator_t *des = $1; + while (des->next) { + des = des->next; + } + des->next = $2; + $$ = $1; + } + ; + +designator_spec + : '.' ident_expr { $$ = new_designator ($2, 0); } + | '.' NAME { $$ = new_designator (new_symbol_expr ($2), 0); } + | '[' expr ']' { $$ = new_designator (0, $2); } + ; + optional_comma : /* empty */ | ',' diff --git a/tools/qfcc/source/struct.c b/tools/qfcc/source/struct.c index eda541cb1..80c14fb01 100644 --- a/tools/qfcc/source/struct.c +++ b/tools/qfcc/source/struct.c @@ -202,6 +202,7 @@ build_struct (int su, symbol_t *tag, symtab_t *symtab, type_t *type, int base) s = s->next; s->s.offset += offset; s->table = symtab; + s->no_auto_init = 1; Hash_Add (symtab->tab, s); } }