[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_value, ///< constant value (::ex_value_t)
ex_compound, ///< compound initializer
} expr_type;
/** Binary and unary expressions.
@ -84,6 +85,17 @@ typedef struct {
ex_label_t *label;
} 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 {
struct expr_s *head; ///< the first expression in the block
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_vector_t vector; ///< vector expression list
ex_value_t *value; ///< constant value
ex_cmpinit_t compound; ///< compound initializer
} e;
} expr_t;
@ -348,6 +361,10 @@ expr_t *new_block_expr (void);
*/
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.
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 *
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_chain->tail = element;
@ -371,7 +371,7 @@ static void
build_element_chain (element_chain_t *element_chain, type_t *type,
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)) {
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++) {
int offset = base_offset + i * type_size (array_type);
if (e && e->type == ex_block) {
build_element_chain (element_chain, array_type, e, offset);
if (ele->expr && ele->expr->type == ex_compound) {
build_element_chain (element_chain, array_type,
ele->expr, offset);
} else {
element_t *element = new_element ();
element->type = array_type;
element->offset = offset;
element->expr = e; // null will be treated as nil
append_element (element_chain, element);
element->expr = ele->expr; // null will be treated as nil
append_init_element (element_chain, element);
}
if (e) {
e = e->next;
if (ele) {
ele = ele->next;
}
}
} 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) {
continue;
}
if (e && e->type == ex_block) {
build_element_chain (element_chain, field->type, e, offset);
if (ele->expr && ele->expr->type == ex_compound) {
build_element_chain (element_chain, field->type,
ele->expr, offset);
} else {
element_t *element = new_element ();
element->type = field->type;
element->offset = offset;
element->expr = e; // null will be treated as nil
append_element (element_chain, element);
element->expr = ele->expr; // null will be treated as nil
append_init_element (element_chain, element);
}
if (e) {
e = e->next;
if (ele) {
ele = ele->next;
}
}
} else {
error (eles, "invalid initializer");
}
if (e && e->next && options.warnings.initializer) {
if (ele && ele->next && options.warnings.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 (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,
num_elements (init));
}
@ -676,7 +678,7 @@ initialize_def (symbol_t *sym, expr_t *init, defspace_t *space,
return;
if ((is_array (sym->type) || is_struct (sym->type)
|| 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_elements (sym->s.def, init);
sym->s.def->initialized = 1;

View file

@ -217,6 +217,7 @@ get_type (expr_t *e)
return &type_void;
case ex_label:
case ex_error:
case ex_compound:
return 0; // something went very wrong
case ex_bool:
if (options.code.progsversion == PROG_ID_VERSION)
@ -420,14 +421,21 @@ copy_expr (expr_t *e)
n = new_expr ();
n->e.vector.type = e->e.vector.type;
n->e.vector.list = copy_expr (e->e.vector.list);
n = n->e.vector.list;
t = e->e.vector.list;
e = n->e.vector.list;
while (t->next) {
n->next = copy_expr (t->next);
n = n->next;
e->next = copy_expr (t->next);
e = e->next;
t = t->next;
}
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");
}
@ -543,6 +551,46 @@ new_block_expr (void)
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 *
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_labelref:
case ex_state:
case ex_compound:
internal_error (e, 0);
case ex_uexpr:
if (e->e.expr.op == '-')
@ -1565,6 +1614,7 @@ unary_expr (int op, expr_t *e)
case ex_label:
case ex_labelref:
case ex_state:
case ex_compound:
internal_error (e, 0);
case ex_bool:
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_labelref:
case ex_state:
case ex_compound:
internal_error (e, 0);
case ex_uexpr:
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);
}
break;
case ex_compound:
case ex_state:
case ex_bool:
case ex_label:

View file

@ -98,6 +98,7 @@ int yylex (void);
void *pointer; // for ensuring pointer values are null
struct type_s *type;
struct expr_s *expr;
struct ex_initele_s *element;
struct function_s *function;
struct switch_block_s *switch_block;
struct param_s *param;
@ -181,7 +182,9 @@ int yylex (void);
%type <symbol> methoddef
%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> statement statements compound_statement
%type <expr> else bool_label break_label continue_label
@ -1148,17 +1151,20 @@ opt_initializer
var_initializer
: '=' expr { $$ = $2; }
| '=' '{' { $<spec>$ = $<spec>-1; }
element_list optional_comma '}' { $$ = $4; }
| '=' '{' '}'
| '=' compound_init
{
if (is_scalar ($<spec>-1.type)) {
if (!$2 && is_scalar ($<spec>-1.type)) {
error (0, "empty scalar initializer");
}
$$ = new_nil_expr ();
$$ = $2 ? $2 : new_nil_expr ();
}
;
compound_init
: '{' element_list optional_comma '}' { $$ = $2; }
| '{' '}' { $$ = 0; }
;
optional_state_expr
: /* emtpy */ { $$ = 0; }
| vector_expr { $$ = build_state_expr ($1); }
@ -1167,30 +1173,18 @@ optional_state_expr
element_list
: element
{
$$ = new_block_expr ();
append_expr ($$, $1);
$$ = new_compound_init ();
append_element ($$, $1);
}
| element_list ',' {$<spec>$ = $<spec>0; } element
| element_list ',' element
{
append_expr ($$, $4);
append_element ($$, $3);
}
;
element
: '{' { $<spec>$ = $<spec>0; }
element_list optional_comma '}' { $$ = $3; }
| '{' '}'
{
// 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; }
: compound_init { $$ = new_initele ($1, 0); }
| expr { $$ = new_initele ($1, 0); }
;
optional_comma