[qfcc] Support compound init in assignment and params

foo({...}) and bar = {...}
This commit is contained in:
Bill Currie 2020-03-11 19:42:38 +09:00
parent 023a920a51
commit b6439e8dc1
10 changed files with 381 additions and 174 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,209 @@
/*
expr_compound.c
compound intializer expression construction and manipulations
Copyright (C) 2020 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
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 <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdlib.h>
#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;
}

View file

@ -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 <expr> optional_state_expr texpr vector_expr
%type <expr> statement statements compound_statement
%type <expr> else bool_label break_label continue_label
%type <expr> unary_expr ident_expr cast_expr opt_arg_list arg_list
%type <expr> unary_expr ident_expr cast_expr expr_list
%type <expr> opt_arg_list arg_list arg_expr
%type <expr> init_var_decl_list init_var_decl
%type <switch_block> switch_block
%type <symbol> 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 (); }

View file

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

View file

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

View file

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