From b6439e8dc1f2db26078a9b38c4760ac427ef5f44 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Wed, 11 Mar 2020 19:42:38 +0900 Subject: [PATCH] [qfcc] Support compound init in assignment and params foo({...}) and bar = {...} --- tools/qfcc/include/expr.h | 30 ++-- tools/qfcc/source/Makefile.am | 3 +- tools/qfcc/source/def.c | 109 +-------------- tools/qfcc/source/expr.c | 63 +++------ tools/qfcc/source/expr_assign.c | 8 +- tools/qfcc/source/expr_compound.c | 209 ++++++++++++++++++++++++++++ tools/qfcc/source/qc-parse.y | 32 +++-- tools/qfcc/test/Makefile.am | 10 ++ tools/qfcc/test/compound.r | 72 ++++++++++ tools/qfcc/test/struct-init-param.r | 19 ++- 10 files changed, 381 insertions(+), 174 deletions(-) create mode 100644 tools/qfcc/source/expr_compound.c create mode 100644 tools/qfcc/test/compound.r diff --git a/tools/qfcc/include/expr.h b/tools/qfcc/include/expr.h index 230ecd241..008313a5a 100644 --- a/tools/qfcc/include/expr.h +++ b/tools/qfcc/include/expr.h @@ -85,16 +85,18 @@ 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 +typedef struct element_s { + struct element_s *next; ///< next in chain + int offset; + struct type_s *type; struct expr_s *expr; ///< initializer expression -} ex_initele_t; + struct symbol_s *symbol; ///< for labeled initializers +} element_t; -typedef struct ex_cmpinit_s { - ex_initele_t *head; - ex_initele_t **tail; -} ex_cmpinit_t; +typedef struct element_chain_s { + element_t *head; + element_t **tail; +} element_chain_t; typedef struct { struct expr_s *head; ///< the first expression in the block @@ -222,7 +224,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 + element_chain_t compound; ///< compound initializer } e; } expr_t; @@ -361,9 +363,15 @@ 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); +element_t *new_element (expr_t *expr, struct symbol_s *symbol); expr_t *new_compound_init (void); -expr_t *append_element (expr_t *compound, ex_initele_t *element); +expr_t *append_element (expr_t *compound, element_t *element); +expr_t *initialized_temp_expr (struct type_s *type, expr_t *compound); +void assign_elements (expr_t *local_expr, expr_t *ptr, + element_chain_t *element_chain); +void build_element_chain (element_chain_t *element_chain, struct type_s *type, + expr_t *eles, int base_offset); +void free_element_chain (element_chain_t *element_chain); /** Create a new binary expression node node. diff --git a/tools/qfcc/source/Makefile.am b/tools/qfcc/source/Makefile.am index d9ceb5ba2..cfd31afbc 100644 --- a/tools/qfcc/source/Makefile.am +++ b/tools/qfcc/source/Makefile.am @@ -41,7 +41,8 @@ bin_SCRIPTS= qfpreqcc common_src=\ class.c codespace.c constfold.c cpp.c dags.c debug.c def.c defspace.c \ diagnostic.c dot.c dot_dag.c dot_expr.c dot_flow.c dot_sblock.c emit.c \ - expr.c expr_assign.c expr_binary.c expr_bool.c flow.c function.c grab.c \ + expr.c expr_assign.c expr_binary.c expr_bool.c expr_compound.c \ + flow.c function.c grab.c \ idstuff.c \ linker.c method.c \ obj_file.c \ diff --git a/tools/qfcc/source/def.c b/tools/qfcc/source/def.c index d600fb0ae..f690f2c3b 100644 --- a/tools/qfcc/source/def.c +++ b/tools/qfcc/source/def.c @@ -62,37 +62,7 @@ #include "type.h" #include "value.h" -typedef struct element_s { - struct element_s *next; - int offset; - type_t *type; - expr_t *expr; -} element_t; - -typedef struct element_chain_s { - element_t *head; - element_t **tail; -} element_chain_t; - static def_t *defs_freelist; -static element_t *elements_freelist; - -static element_t * -new_element (void) -{ - element_t *element; - ALLOC (256, element_t, elements, element); - return element; -} - -static element_t * -append_init_element (element_chain_t *element_chain, element_t *element) -{ - element->next = 0; - *element_chain->tail = element; - element_chain->tail = &element->next; - return element; -} static void set_storage_bits (def_t *def, storage_class_t storage) @@ -367,65 +337,6 @@ init_elements_nil (def_t *def) // it's a global, so already initialized to 0 } -static void -build_element_chain (element_chain_t *element_chain, type_t *type, - expr_t *eles, int base_offset) -{ - ex_initele_t *ele = eles->e.compound.head; - - if (is_array (type)) { - type_t *array_type = type->t.array.type; - int array_size = type->t.array.size; - int i; - - for (i = 0; i < array_size; i++) { - int offset = base_offset + i * type_size (array_type); - 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 = ele->expr; // null will be treated as nil - append_init_element (element_chain, element); - } - if (ele) { - ele = ele->next; - } - } - } else if (is_struct (type) || is_vector (type) || is_quaternion (type)) { - symtab_t *symtab = type->t.symtab; - symbol_t *field; - - 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 (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 = ele->expr; // null will be treated as nil - append_init_element (element_chain, element); - } - if (ele) { - ele = ele->next; - } - } - } else { - error (eles, "invalid initializer"); - } - if (ele && ele->next && options.warnings.initializer) { - warning (eles, "excessive elements in initializer"); - } -} - static void init_elements (struct def_s *def, expr_t *eles) { @@ -444,21 +355,8 @@ init_elements (struct def_s *def, expr_t *eles) build_element_chain (&element_chain, def->type, eles, 0); if (def->local && local_expr) { - for (element = element_chain.head; element; element = element->next) { - int offset = element->offset; - type_t *type = element->type; - expr_t *ptr = new_pointer_expr (offset, type, def); - - if (element->expr) { - c = constant_expr (element->expr); - } else { - c = new_nil_expr (); - } - if (c->type == ex_nil) { - c = convert_nil (c, type); - } - append_expr (local_expr, assign_expr (unary_expr ('.', ptr), c)); - } + expr_t *ptr = new_pointer_expr (0, def->type, def); + assign_elements (local_expr, pointer_expr (ptr), &element_chain); } else { def_t dummy = *def; for (element = element_chain.head; element; element = element->next) { @@ -502,8 +400,7 @@ init_elements (struct def_s *def, expr_t *eles) } } - *element_chain.tail = elements_freelist; - elements_freelist = element_chain.head; + free_element_chain (&element_chain); } static void diff --git a/tools/qfcc/source/expr.c b/tools/qfcc/source/expr.c index b4173f45d..4b0317d53 100644 --- a/tools/qfcc/source/expr.c +++ b/tools/qfcc/source/expr.c @@ -432,8 +432,8 @@ copy_expr (expr_t *e) 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)); + for (element_t *i = e->e.compound.head; i; i = i->next) { + append_element (n, new_element (i->expr, i->symbol)); } return n; } @@ -551,46 +551,6 @@ 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) { @@ -1731,6 +1691,7 @@ expr_t * build_function_call (expr_t *fexpr, const type_t *ftype, expr_t *params) { expr_t *e; + expr_t *p; int arg_count = 0, parm_count = 0; int i; expr_t *args = 0, **a = &args; @@ -1770,8 +1731,18 @@ build_function_call (expr_t *fexpr, const type_t *ftype, expr_t *params) parm_count = ftype->t.func.num_params; } for (i = arg_count - 1, e = params; i >= 0; i--, e = e->next) { - type_t *t = get_type (e); + type_t *t; + if (e->type == ex_compound) { + if (i < parm_count) { + t = ftype->t.func.param_types[i]; + } else { + return error (e, "cannot pass compound initializer " + "through ..."); + } + } else { + t = get_type (e); + } if (!t) { return e; } @@ -1827,7 +1798,11 @@ build_function_call (expr_t *fexpr, const type_t *ftype, expr_t *params) call = expr_file_line (new_block_expr (), fexpr); call->e.block.is_call = 1; - for (e = params, i = 0; e; e = e->next, i++) { + for (p = params, i = 0; p; p = p->next, i++) { + expr_t *e = p; + if (e->type == ex_compound) { + e = expr_file_line (initialized_temp_expr (arg_types[i], e), e); + } if (has_function_call (e)) { expr_t *cast = cast_expr (arg_types[i], convert_vector (e)); expr_t *tmp = new_temp_def_expr (arg_types[i]); diff --git a/tools/qfcc/source/expr_assign.c b/tools/qfcc/source/expr_assign.c index 466ffcf95..edad8eff7 100644 --- a/tools/qfcc/source/expr_assign.c +++ b/tools/qfcc/source/expr_assign.c @@ -313,10 +313,16 @@ assign_expr (expr_t *dst, expr_t *src) } dst_type = get_type (dst); - src_type = get_type (src); if (!dst_type) { internal_error (dst, "dst_type broke in assign_expr"); } + if (src->type == ex_compound) { + src = initialized_temp_expr (dst_type, src); + if (src->type == ex_error) { + return src; + } + } + src_type = get_type (src); if (!src_type) { internal_error (src, "src_type broke in assign_expr"); } diff --git a/tools/qfcc/source/expr_compound.c b/tools/qfcc/source/expr_compound.c new file mode 100644 index 000000000..5ead7de7a --- /dev/null +++ b/tools/qfcc/source/expr_compound.c @@ -0,0 +1,209 @@ +/* + expr_compound.c + + compound intializer expression construction and manipulations + + Copyright (C) 2020 Bill Currie + + Author: Bill Currie + Date: 2020/03/11 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#include + +#include "QF/alloc.h" +#include "QF/dstring.h" +#include "QF/mathlib.h" +#include "QF/sys.h" +#include "QF/va.h" + +#include "diagnostic.h" +#include "expr.h" +#include "options.h" +#include "symtab.h" +#include "type.h" + +static element_t *elements_freelist; + +element_t * +new_element (expr_t *expr, symbol_t *symbol) +{ + element_t *element; + ALLOC (256, element_t, elements, element); + element->expr = expr; + element->symbol = symbol; + return element; +} + +static element_t * +append_init_element (element_chain_t *element_chain, element_t *element) +{ + element->next = 0; + *element_chain->tail = element; + element_chain->tail = &element->next; + return element; +} + +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; +} + +void +build_element_chain (element_chain_t *element_chain, type_t *type, + expr_t *eles, int base_offset) +{ + element_t *ele = eles->e.compound.head; + + if (is_array (type)) { + type_t *array_type = type->t.array.type; + int array_size = type->t.array.size; + int i; + + for (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; + } + } + } else if (is_struct (type) || is_vector (type) || is_quaternion (type)) { + symtab_t *symtab = type->t.symtab; + symbol_t *field; + + 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 (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); + } + if (ele) { + ele = ele->next; + } + } + } else { + error (eles, "invalid initializer"); + } + if (ele && ele->next && options.warnings.initializer) { + warning (eles, "excessive elements in initializer"); + } +} + +void free_element_chain (element_chain_t *element_chain) +{ + *element_chain->tail = elements_freelist; + elements_freelist = element_chain->head; + element_chain->head = 0; + element_chain->tail = &element_chain->head; +} + +expr_t * +append_element (expr_t *compound, element_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"); + } + append_init_element (&compound->e.compound, element); + return compound; +} + +void +assign_elements (expr_t *local_expr, expr_t *init, + element_chain_t *element_chain) +{ + element_t *element; + + for (element = element_chain->head; element; element = element->next) { + int offset = element->offset; + type_t *type = element->type; + expr_t *alias = new_offset_alias_expr (type, init, offset); + + expr_t *c; + + if (element->expr) { + c = constant_expr (element->expr); + } else { + c = new_nil_expr (); + } + if (c->type == ex_nil) { + c = convert_nil (c, type); + } + append_expr (local_expr, assign_expr (alias, c)); + } +} + +expr_t * +initialized_temp_expr (type_t *type, expr_t *compound) +{ + element_chain_t element_chain; + expr_t *temp = new_temp_def_expr (type); + expr_t *block = new_block_expr (); + + element_chain.head = 0; + element_chain.tail = &element_chain.head; + build_element_chain (&element_chain, type, compound, 0); + assign_elements (block, temp, &element_chain); + block->e.block.result = temp; + free_element_chain (&element_chain); + return block; +} diff --git a/tools/qfcc/source/qc-parse.y b/tools/qfcc/source/qc-parse.y index 5b3216d10..a4ce4c433 100644 --- a/tools/qfcc/source/qc-parse.y +++ b/tools/qfcc/source/qc-parse.y @@ -98,7 +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 element_s *element; struct function_s *function; struct switch_block_s *switch_block; struct param_s *param; @@ -188,7 +188,8 @@ int yylex (void); %type optional_state_expr texpr vector_expr %type statement statements compound_statement %type else bool_label break_label continue_label -%type unary_expr ident_expr cast_expr opt_arg_list arg_list +%type unary_expr ident_expr cast_expr expr_list +%type opt_arg_list arg_list arg_expr %type init_var_decl_list init_var_decl %type switch_block %type identifier label @@ -1183,8 +1184,8 @@ element_list ; element - : compound_init { $$ = new_initele ($1, 0); } - | expr { $$ = new_initele ($1, 0); } + : compound_init { $$ = new_element ($1, 0); } + | expr { $$ = new_element ($1, 0); } ; optional_comma @@ -1473,7 +1474,7 @@ ident_expr ; vector_expr - : '[' expr ',' arg_list ']' + : '[' expr ',' expr_list ']' { expr_t *t = $4; while (t->next) @@ -1494,6 +1495,7 @@ cast_expr expr : cast_expr | expr '=' expr { $$ = assign_expr ($1, $3); } + | expr '=' compound_init { $$ = assign_expr ($1, $3); } | expr ASX expr { $$ = asx_expr ($2, $1, $3); } | expr '?' expr ':' expr { $$ = conditional_expr ($1, $3, $5); } | expr AND bool_label expr { $$ = bool_expr (AND, $3, $1, $4); } @@ -1522,7 +1524,7 @@ texpr ; comma_expr - : arg_list + : expr_list { if ($1->next) { expr_t *res = $1; @@ -1533,20 +1535,34 @@ comma_expr } ; +expr_list + : expr + | expr_list ',' expr + { + $3->next = $1; + $$ = $3; + } + ; + opt_arg_list : /* emtpy */ { $$ = 0; } | arg_list { $$ = $1; } ; arg_list - : expr - | arg_list ',' expr + : arg_expr + | arg_list ',' arg_expr { $3->next = $1; $$ = $3; } ; +arg_expr + : expr + | compound_init + ; + const : VALUE | NIL { $$ = new_nil_expr (); } diff --git a/tools/qfcc/test/Makefile.am b/tools/qfcc/test/Makefile.am index 99c24142c..f4d31a2c9 100644 --- a/tools/qfcc/test/Makefile.am +++ b/tools/qfcc/test/Makefile.am @@ -35,6 +35,7 @@ test_progs_dat=\ chewed-alias.dat \ chewed-return.dat \ comma-expr.dat \ + compound.dat \ deadbool.dat \ double.dat \ enum.dat \ @@ -158,6 +159,15 @@ comma-expr.run: Makefile build-run include ./$(DEPDIR)/comma-expr.Qo # am--include-marker r_depfiles_remade += ./$(DEPDIR)/comma-expr.Qo +compound_dat_SOURCES=compound.r +compound_obj=$(compound_dat_SOURCES:.r=.qfo) +compound.dat$(EXEEXT): $(compound_obj) $(QFCC_DEP) + $(QFCC) $(QCFLAGS) -o $@ $(compound_obj) +compound.run: Makefile build-run + @$(srcdir)/build-run $@ +include ./$(DEPDIR)/compound.Qo # am--include-marker +r_depfiles_remade += ./$(DEPDIR)/compound.Qo + deadbool_dat_SOURCES=deadbool.r deadbool_obj=$(deadbool_dat_SOURCES:.r=.qfo) deadbool.dat$(EXEEXT): $(deadbool_obj) $(QFCC_DEP) diff --git a/tools/qfcc/test/compound.r b/tools/qfcc/test/compound.r new file mode 100644 index 000000000..19a72d3fe --- /dev/null +++ b/tools/qfcc/test/compound.r @@ -0,0 +1,72 @@ +#include "test-harness.h" + +typedef struct Point { + int x; + int y; +} Point; + +typedef struct Size { + int width; + int height; +} Size; + +typedef struct Rect { + Point origin; + Size size; +} Rect; + +int test_simple_param (Point p, int x, int y) +{ + int ret = !(p.x == x && p.y == y); + if (ret) { + printf ("simple param: {%d, %d} != {%d, %d}\n", p.x, p.y, x, y); + } + return ret; +} + +int test_nested_param (Rect r, int x, int y, int w, int h) +{ + int ret = !(r.origin.x == x && r.origin.y == y + && r.size.width == w && r.size.height == h); + if (ret) { + printf ("nested param: {{%d, %d}, {%d, %d}} != ", + r.origin.x, r.origin.y, r.size.width, r.size.height); + printf ("{{%d, %d}, {%d, %d}}\n", x, y, w, h); + } + return ret; +} + +int test_simple_assign (int x, int y) +{ + Point p; + p = { x, y }; + int ret = !(p.x == x && p.y == y); + if (ret) { + printf ("simple assign: {%d, %d} != {%d, %d}\n", p.x, p.y, x, y); + } + return ret; +} + +int test_nested_assign (int x, int y, int w, int h) +{ + Rect r; + r = {{x, y}, {w, h}}; + int ret = !(r.origin.x == x && r.origin.y == y + && r.size.width == w && r.size.height == h); + if (ret) { + printf ("nested assign: {{%d, %d}, {%d, %d}} != ", + r.origin.x, r.origin.y, r.size.width, r.size.height); + printf ("{{%d, %d}, {%d, %d}}\n", x, y, w, h); + } + return ret; +} + +int main (void) +{ + int ret = 0; + ret |= test_simple_param ({1, 2}, 1, 2); + ret |= test_nested_param ({{1, 2}, {3, 4}}, 1, 2, 3, 4); + ret |= test_simple_assign (1, 2); + ret |= test_nested_assign (1, 2, 3, 4); + return ret; +} diff --git a/tools/qfcc/test/struct-init-param.r b/tools/qfcc/test/struct-init-param.r index 1b3bc9e7a..f043cfde3 100644 --- a/tools/qfcc/test/struct-init-param.r +++ b/tools/qfcc/test/struct-init-param.r @@ -16,6 +16,12 @@ typedef struct Rect_s { void *foo (Rect *obj, void *cmd, Rect *o, Point pos, Rect r); +void *baz (Rect *obj, void *cmd, Rect *o, Point pos) +{ + Rect rect = { {1, 2}, {3, 4} }; + return foo (obj, cmd, o, pos, rect); +} + void *bar (Rect *obj, void *cmd, Rect *o, Point pos) { Rect rect = { {}, obj.extent }; @@ -33,12 +39,19 @@ Rect o = { { 5, 6}, {7, 8} }; int main (void) { - int ret = 1; + int ret = 0; bar(&obj, nil, &o, obj.offset); printf ("%d %d %d %d\n", o.offset.x, o.offset.y, o.extent.width, o.extent.height); - if (o.offset.x == 0 && o.offset.y == 0 + if not (o.offset.x == 0 && o.offset.y == 0 && o.extent.width == 3 && o.extent.height == 4) - ret = 0; + ret |= 1; + + baz(&obj, nil, &o, obj.offset); + printf ("%d %d %d %d\n", o.offset.x, o.offset.y, + o.extent.width, o.extent.height); + if not (o.offset.x == 1 && o.offset.y == 2 + && o.extent.width == 3 && o.extent.height == 4) + ret |= 1; return ret; }