From d1e83b9d4887eba79236b5531dc8e0a42063d702 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Wed, 11 Mar 2020 15:46:57 +0900 Subject: [PATCH] [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. --- tools/qfcc/include/expr.h | 17 ++++++++++ tools/qfcc/source/def.c | 36 +++++++++++---------- tools/qfcc/source/expr.c | 57 +++++++++++++++++++++++++++++++-- tools/qfcc/source/expr_assign.c | 1 + tools/qfcc/source/qc-parse.y | 42 +++++++++++------------- 5 files changed, 109 insertions(+), 44 deletions(-) diff --git a/tools/qfcc/include/expr.h b/tools/qfcc/include/expr.h index fa88dc80d..230ecd241 100644 --- a/tools/qfcc/include/expr.h +++ b/tools/qfcc/include/expr.h @@ -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 diff --git a/tools/qfcc/source/def.c b/tools/qfcc/source/def.c index 865ef4066..d600fb0ae 100644 --- a/tools/qfcc/source/def.c +++ b/tools/qfcc/source/def.c @@ -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; diff --git a/tools/qfcc/source/expr.c b/tools/qfcc/source/expr.c index e9967dbc7..b4173f45d 100644 --- a/tools/qfcc/source/expr.c +++ b/tools/qfcc/source/expr.c @@ -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 == '~') diff --git a/tools/qfcc/source/expr_assign.c b/tools/qfcc/source/expr_assign.c index f0d7daf4b..466ffcf95 100644 --- a/tools/qfcc/source/expr_assign.c +++ b/tools/qfcc/source/expr_assign.c @@ -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: diff --git a/tools/qfcc/source/qc-parse.y b/tools/qfcc/source/qc-parse.y index 27423d4ef..83ac9ca8a 100644 --- a/tools/qfcc/source/qc-parse.y +++ b/tools/qfcc/source/qc-parse.y @@ -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 methoddef %type opt_initializer var_initializer local_def -%type opt_init opt_expr cexpr expr element_list element +%type opt_init opt_expr cexpr expr +%type compound_init element_list +%type element %type optional_state_expr texpr vector_expr %type statement statements compound_statement %type else bool_label break_label continue_label @@ -1148,17 +1151,20 @@ opt_initializer var_initializer : '=' expr { $$ = $2; } - | '=' '{' { $$ = $-1; } - element_list optional_comma '}' { $$ = $4; } - | '=' '{' '}' + | '=' compound_init { - if (is_scalar ($-1.type)) { + if (!$2 && is_scalar ($-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 ',' {$$ = $0; } element + | element_list ',' element { - append_expr ($$, $4); + append_element ($$, $3); } ; element - : '{' { $$ = $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 ($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