[qfcc] Create a compound initializer expression type

This fixes the problem of using the return value of a function as an
element in a compound initializer. The cause of the problem is that
compound initializers were represented by block expressions, but
function calls are contained within block expressions, so def
initialization saw the block expression and thought it was a nested
compound initializer.

Technically, it was a bug in the nested element parsing code in that it
wasn't checking the result value of the block expression, but using a
whole new expression type makes things much cleaner and the work done
paves the way for labeled initializers and compound assignments.
This commit is contained in:
Bill Currie 2020-03-11 15:46:57 +09:00
parent f10f9e157d
commit d1e83b9d48
5 changed files with 109 additions and 44 deletions

View file

@ -55,6 +55,7 @@ typedef enum {
ex_nil, ///< umm, nil, null. nuff said (0 of any type) ex_nil, ///< umm, nil, null. nuff said (0 of any type)
ex_value, ///< constant value (::ex_value_t) ex_value, ///< constant value (::ex_value_t)
ex_compound, ///< compound initializer
} expr_type; } expr_type;
/** Binary and unary expressions. /** Binary and unary expressions.
@ -84,6 +85,17 @@ typedef struct {
ex_label_t *label; ex_label_t *label;
} ex_labelref_t; } ex_labelref_t;
typedef struct ex_initele_s {
struct ex_initele_s *next; ///< next in chain
struct symbol_s *symbol; ///< for labeled initializers
struct expr_s *expr; ///< initializer expression
} ex_initele_t;
typedef struct ex_cmpinit_s {
ex_initele_t *head;
ex_initele_t **tail;
} ex_cmpinit_t;
typedef struct { typedef struct {
struct expr_s *head; ///< the first expression in the block struct expr_s *head; ///< the first expression in the block
struct expr_s **tail; ///< last expression in the block, for appending struct expr_s **tail; ///< last expression in the block, for appending
@ -210,6 +222,7 @@ typedef struct expr_s {
ex_temp_t temp; ///< temporary variable expression ex_temp_t temp; ///< temporary variable expression
ex_vector_t vector; ///< vector expression list ex_vector_t vector; ///< vector expression list
ex_value_t *value; ///< constant value ex_value_t *value; ///< constant value
ex_cmpinit_t compound; ///< compound initializer
} e; } e;
} expr_t; } expr_t;
@ -348,6 +361,10 @@ expr_t *new_block_expr (void);
*/ */
expr_t *build_block_expr (expr_t *expr_list); expr_t *build_block_expr (expr_t *expr_list);
ex_initele_t *new_initele (expr_t *expr, struct symbol_s *symbol);
expr_t *new_compound_init (void);
expr_t *append_element (expr_t *compound, ex_initele_t *element);
/** Create a new binary expression node node. /** Create a new binary expression node node.
If either \a e1 or \a e2 are error expressions, then that expression will If either \a e1 or \a e2 are error expressions, then that expression will

View file

@ -86,7 +86,7 @@ new_element (void)
} }
static element_t * static element_t *
append_element (element_chain_t *element_chain, element_t *element) append_init_element (element_chain_t *element_chain, element_t *element)
{ {
element->next = 0; element->next = 0;
*element_chain->tail = element; *element_chain->tail = element;
@ -371,7 +371,7 @@ static void
build_element_chain (element_chain_t *element_chain, type_t *type, build_element_chain (element_chain_t *element_chain, type_t *type,
expr_t *eles, int base_offset) expr_t *eles, int base_offset)
{ {
expr_t *e = eles->e.block.head; ex_initele_t *ele = eles->e.compound.head;
if (is_array (type)) { if (is_array (type)) {
type_t *array_type = type->t.array.type; type_t *array_type = type->t.array.type;
@ -380,17 +380,18 @@ build_element_chain (element_chain_t *element_chain, type_t *type,
for (i = 0; i < array_size; i++) { for (i = 0; i < array_size; i++) {
int offset = base_offset + i * type_size (array_type); int offset = base_offset + i * type_size (array_type);
if (e && e->type == ex_block) { if (ele->expr && ele->expr->type == ex_compound) {
build_element_chain (element_chain, array_type, e, offset); build_element_chain (element_chain, array_type,
ele->expr, offset);
} else { } else {
element_t *element = new_element (); element_t *element = new_element ();
element->type = array_type; element->type = array_type;
element->offset = offset; element->offset = offset;
element->expr = e; // null will be treated as nil element->expr = ele->expr; // null will be treated as nil
append_element (element_chain, element); append_init_element (element_chain, element);
} }
if (e) { if (ele) {
e = e->next; ele = ele->next;
} }
} }
} else if (is_struct (type) || is_vector (type) || is_quaternion (type)) { } else if (is_struct (type) || is_vector (type) || is_quaternion (type)) {
@ -403,23 +404,24 @@ build_element_chain (element_chain_t *element_chain, type_t *type,
|| field->visibility == vis_anonymous) { || field->visibility == vis_anonymous) {
continue; continue;
} }
if (e && e->type == ex_block) { if (ele->expr && ele->expr->type == ex_compound) {
build_element_chain (element_chain, field->type, e, offset); build_element_chain (element_chain, field->type,
ele->expr, offset);
} else { } else {
element_t *element = new_element (); element_t *element = new_element ();
element->type = field->type; element->type = field->type;
element->offset = offset; element->offset = offset;
element->expr = e; // null will be treated as nil element->expr = ele->expr; // null will be treated as nil
append_element (element_chain, element); append_init_element (element_chain, element);
} }
if (e) { if (ele) {
e = e->next; ele = ele->next;
} }
} }
} else { } else {
error (eles, "invalid initializer"); error (eles, "invalid initializer");
} }
if (e && e->next && options.warnings.initializer) { if (ele && ele->next && options.warnings.initializer) {
warning (eles, "excessive elements in initializer"); warning (eles, "excessive elements in initializer");
} }
} }
@ -652,7 +654,7 @@ initialize_def (symbol_t *sym, expr_t *init, defspace_t *space,
} }
if (!sym->s.def) { if (!sym->s.def) {
if (is_array (sym->type) && !type_size (sym->type) if (is_array (sym->type) && !type_size (sym->type)
&& init->type == ex_block && !init->e.block.result) { && init->type == ex_compound) {
sym->type = array_type (sym->type->t.array.type, sym->type = array_type (sym->type->t.array.type,
num_elements (init)); num_elements (init));
} }
@ -676,7 +678,7 @@ initialize_def (symbol_t *sym, expr_t *init, defspace_t *space,
return; return;
if ((is_array (sym->type) || is_struct (sym->type) if ((is_array (sym->type) || is_struct (sym->type)
|| sym->type == &type_vector || sym->type == &type_quaternion) || sym->type == &type_vector || sym->type == &type_quaternion)
&& ((init->type == ex_block && !init->e.block.result) && ((init->type == ex_compound)
|| init->type == ex_nil)) { || init->type == ex_nil)) {
init_elements (sym->s.def, init); init_elements (sym->s.def, init);
sym->s.def->initialized = 1; sym->s.def->initialized = 1;

View file

@ -217,6 +217,7 @@ get_type (expr_t *e)
return &type_void; return &type_void;
case ex_label: case ex_label:
case ex_error: case ex_error:
case ex_compound:
return 0; // something went very wrong return 0; // something went very wrong
case ex_bool: case ex_bool:
if (options.code.progsversion == PROG_ID_VERSION) if (options.code.progsversion == PROG_ID_VERSION)
@ -420,14 +421,21 @@ copy_expr (expr_t *e)
n = new_expr (); n = new_expr ();
n->e.vector.type = e->e.vector.type; n->e.vector.type = e->e.vector.type;
n->e.vector.list = copy_expr (e->e.vector.list); n->e.vector.list = copy_expr (e->e.vector.list);
n = n->e.vector.list;
t = e->e.vector.list; t = e->e.vector.list;
e = n->e.vector.list;
while (t->next) { while (t->next) {
n->next = copy_expr (t->next); e->next = copy_expr (t->next);
n = n->next; e = e->next;
t = t->next; t = t->next;
} }
return n; return n;
case ex_compound:
n = new_expr ();
*n = *e;
for (ex_initele_t *i = e->e.compound.head; i; i = i->next) {
append_element (n, new_initele (i->expr, i->symbol));
}
return n;
} }
internal_error (e, "invalid expression"); internal_error (e, "invalid expression");
} }
@ -543,6 +551,46 @@ new_block_expr (void)
return b; return b;
} }
ex_initele_t *
new_initele (expr_t *expr, symbol_t *symbol)
{
ex_initele_t *i = calloc (1, sizeof (*i));
i->expr = expr;
i->symbol = symbol;
return i;
}
expr_t *
new_compound_init (void)
{
expr_t *c = new_expr ();
c->type = ex_compound;
c->e.compound.head = 0;
c->e.compound.tail = &c->e.compound.head;
return c;
}
expr_t *
append_element (expr_t *compound, ex_initele_t *element)
{
if (compound->type != ex_compound) {
internal_error (compound, "not a compound expression");
}
if (!element || (element->expr && element->expr->type == ex_error)) {
return compound;
}
if (element->next) {
internal_error (compound, "append_element: element loop detected");
}
*compound->e.compound.tail = element;
compound->e.compound.tail = &element->next;
return compound;
}
expr_t * expr_t *
new_binary_expr (int op, expr_t *e1, expr_t *e2) new_binary_expr (int op, expr_t *e1, expr_t *e2)
{ {
@ -1499,6 +1547,7 @@ unary_expr (int op, expr_t *e)
case ex_label: case ex_label:
case ex_labelref: case ex_labelref:
case ex_state: case ex_state:
case ex_compound:
internal_error (e, 0); internal_error (e, 0);
case ex_uexpr: case ex_uexpr:
if (e->e.expr.op == '-') if (e->e.expr.op == '-')
@ -1565,6 +1614,7 @@ unary_expr (int op, expr_t *e)
case ex_label: case ex_label:
case ex_labelref: case ex_labelref:
case ex_state: case ex_state:
case ex_compound:
internal_error (e, 0); internal_error (e, 0);
case ex_bool: case ex_bool:
return new_bool_expr (e->e.bool.false_list, return new_bool_expr (e->e.bool.false_list,
@ -1630,6 +1680,7 @@ unary_expr (int op, expr_t *e)
case ex_label: case ex_label:
case ex_labelref: case ex_labelref:
case ex_state: case ex_state:
case ex_compound:
internal_error (e, 0); internal_error (e, 0);
case ex_uexpr: case ex_uexpr:
if (e->e.expr.op == '~') if (e->e.expr.op == '~')

View file

@ -123,6 +123,7 @@ check_valid_lvalue (expr_t *expr)
return check_valid_lvalue (expr->e.expr.e1); return check_valid_lvalue (expr->e.expr.e1);
} }
break; break;
case ex_compound:
case ex_state: case ex_state:
case ex_bool: case ex_bool:
case ex_label: case ex_label:

View file

@ -98,6 +98,7 @@ int yylex (void);
void *pointer; // for ensuring pointer values are null void *pointer; // for ensuring pointer values are null
struct type_s *type; struct type_s *type;
struct expr_s *expr; struct expr_s *expr;
struct ex_initele_s *element;
struct function_s *function; struct function_s *function;
struct switch_block_s *switch_block; struct switch_block_s *switch_block;
struct param_s *param; struct param_s *param;
@ -181,7 +182,9 @@ int yylex (void);
%type <symbol> methoddef %type <symbol> methoddef
%type <expr> opt_initializer var_initializer local_def %type <expr> opt_initializer var_initializer local_def
%type <expr> opt_init opt_expr cexpr expr element_list element %type <expr> opt_init opt_expr cexpr expr
%type <expr> compound_init element_list
%type <element> element
%type <expr> optional_state_expr texpr vector_expr %type <expr> optional_state_expr texpr vector_expr
%type <expr> statement statements compound_statement %type <expr> statement statements compound_statement
%type <expr> else bool_label break_label continue_label %type <expr> else bool_label break_label continue_label
@ -1148,17 +1151,20 @@ opt_initializer
var_initializer var_initializer
: '=' expr { $$ = $2; } : '=' expr { $$ = $2; }
| '=' '{' { $<spec>$ = $<spec>-1; } | '=' compound_init
element_list optional_comma '}' { $$ = $4; }
| '=' '{' '}'
{ {
if (is_scalar ($<spec>-1.type)) { if (!$2 && is_scalar ($<spec>-1.type)) {
error (0, "empty scalar initializer"); error (0, "empty scalar initializer");
} }
$$ = new_nil_expr (); $$ = $2 ? $2 : new_nil_expr ();
} }
; ;
compound_init
: '{' element_list optional_comma '}' { $$ = $2; }
| '{' '}' { $$ = 0; }
;
optional_state_expr optional_state_expr
: /* emtpy */ { $$ = 0; } : /* emtpy */ { $$ = 0; }
| vector_expr { $$ = build_state_expr ($1); } | vector_expr { $$ = build_state_expr ($1); }
@ -1167,30 +1173,18 @@ optional_state_expr
element_list element_list
: element : element
{ {
$$ = new_block_expr (); $$ = new_compound_init ();
append_expr ($$, $1); append_element ($$, $1);
} }
| element_list ',' {$<spec>$ = $<spec>0; } element | element_list ',' element
{ {
append_expr ($$, $4); append_element ($$, $3);
} }
; ;
element element
: '{' { $<spec>$ = $<spec>0; } : compound_init { $$ = new_initele ($1, 0); }
element_list optional_comma '}' { $$ = $3; } | expr { $$ = new_initele ($1, 0); }
| '{' '}'
{
// FIXME doesn't check the right type (does prove the inherited
// attributes have been passed down correctly though). The problem
// is that the type of the sub elements needs to be extracted if
// possible
if (is_scalar ($<spec>0.type)) {
error (0, "empty scalar initializer");
}
$$ = new_nil_expr ();
}
| expr { $$ = $1; }
; ;
optional_comma optional_comma