[qfcc] Implement designated initializers

Conforms fairly closely to GCC's C implementation.
This commit is contained in:
Bill Currie 2023-05-27 00:59:43 +09:00
parent 04e26a7b9f
commit 5d9823af30
6 changed files with 201 additions and 63 deletions

View file

@ -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);

View file

@ -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

View file

@ -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:

View file

@ -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 {

View file

@ -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 <expr> opt_init_semi opt_expr comma_expr expr
%type <expr> compound_init element_list
%type <designator> designator designator_spec
%type <element> element
%type <expr> ose optional_state_expr texpr vector_expr
%type <expr> 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 */
| ','

View file

@ -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);
}
}