[qfcc] Implement anonymous structs and unions

For struct/union scope
This commit is contained in:
Bill Currie 2020-03-04 16:31:28 +09:00
parent 4fa203852a
commit e298904dc0
6 changed files with 128 additions and 4 deletions

View file

@ -45,6 +45,7 @@ typedef enum vis_e {
vis_public, vis_public,
vis_protected, vis_protected,
vis_private, vis_private,
vis_anonymous,
} vis_t; } vis_t;
typedef enum { typedef enum {

View file

@ -42,6 +42,7 @@
#include <QF/hash.h> #include <QF/hash.h>
#include <QF/sys.h> #include <QF/sys.h>
#include <QF/va.h>
#include "class.h" #include "class.h"
#include "debug.h" #include "debug.h"
@ -264,6 +265,26 @@ default_type (specifier_t spec, symbol_t *sym)
return spec; return spec;
} }
static int
is_anonymous_struct (specifier_t spec)
{
if (spec.sym) {
return 0;
}
if (!is_struct (spec.type)) {
return 0;
}
if (spec.type->t.symtab->parent) {
return 0;
}
// struct and union type names always begin with "tag ". Untagged s/u
// are "tag .<filename>.<id>".
if (spec.type->name[4] != '.') {
return 0;
}
return 1;
}
%} %}
%expect 0 %expect 0
@ -675,15 +696,27 @@ struct_def
: type struct_decl_list : type struct_decl_list
| type | type
{ {
if ($1.sym) { if ($1.sym && $1.sym->type != $1.type) {
// a type name (id, typedef, etc) was used as a field name. // a type name (id, typedef, etc) was used as a field name.
// this is allowed in C // this is allowed in C
print_type ($1.type);
printf ("%s\n", $1.sym->name);
$1.sym = new_symbol ($1.sym->name); $1.sym = new_symbol ($1.sym->name);
$1.sym->type = $1.type; $1.sym->type = $1.type;
$1.sym->sy_type = sy_var; $1.sym->sy_type = sy_var;
symtab_addsymbol (current_symtab, $1.sym); symtab_addsymbol (current_symtab, $1.sym);
if (!$1.sym->table) {
error (0, "duplicate field `%s'", $1.sym->name);
}
} else if (is_anonymous_struct ($1)) {
// anonymous struct/union
// type->name always begins with "tag "
$1.sym = new_symbol (va (".anonymous.%s", $1.type->name + 4));
$1.sym->type = $1.type;
$1.sym->sy_type = sy_var;
$1.sym->visibility = vis_anonymous;
symtab_addsymbol (current_symtab, $1.sym);
if (!$1.sym->table) {
error (0, "duplicate field `%s'", $1.sym->name);
}
} else { } else {
// bare type // bare type
warning (0, "declaration does not declare anything"); warning (0, "declaration does not declare anything");

View file

@ -114,6 +114,7 @@ build_struct (int su, symbol_t *tag, symtab_t *symtab, type_t *type)
symbol_t *sym = find_struct (su, tag, type); symbol_t *sym = find_struct (su, tag, type);
symbol_t *s; symbol_t *s;
int alignment = 1; int alignment = 1;
symbol_t *as;
symtab->parent = 0; // disconnect struct's symtab from parent scope symtab->parent = 0; // disconnect struct's symtab from parent scope
@ -140,6 +141,28 @@ build_struct (int su, symbol_t *tag, symtab_t *symtab, type_t *type)
if (s->type->alignment > alignment) { if (s->type->alignment > alignment) {
alignment = s->type->alignment; alignment = s->type->alignment;
} }
if (s->visibility == vis_anonymous) {
symtab_t *anonymous;
symbol_t *t = s->next;
int offset = s->s.offset;
if (!is_struct (s->type)) {
internal_error (0, "non-struct/union anonymous field");
}
anonymous = s->type->t.symtab;
for (as = anonymous->symbols; as; as = as->next) {
if (Hash_Find (symtab->tab, as->name)) {
error (0, "ambiguous field `%s' in anonymous %s",
as->name, su == 's' ? "struct" : "union");
} else {
s->next = copy_symbol (as);
s = s->next;
s->s.offset += offset;
Hash_Add (symtab->tab, s);
}
}
s->next = t;
}
} }
if (!type) if (!type)
sym->type = find_type (sym->type); // checks the tag, not the symtab sym->type = find_type (sym->type); // checks the tag, not the symtab

View file

@ -152,7 +152,7 @@ symtab_removesymbol (symtab_t *symtab, symbol_t *symbol)
for (s = &symtab->symbols; *s && *s != symbol; s = & (*s)->next) for (s = &symtab->symbols; *s && *s != symbol; s = & (*s)->next)
; ;
if (!*s) if (!*s)
internal_error (0, "symtab_removesymbol"); internal_error (0, "attempt to remove symbol not in symtab");
*s = (*s)->next; *s = (*s)->next;
if (symtab->symtail == &symbol->next) if (symtab->symtail == &symbol->next)
symtab->symtail = s; symtab->symtail = s;

View file

@ -31,6 +31,7 @@ fail_bins=
test_progs_dat=\ test_progs_dat=\
address-cast.dat \ address-cast.dat \
alignment.dat \ alignment.dat \
anonstruct.dat \
chewed-alias.dat \ chewed-alias.dat \
chewed-return.dat \ chewed-return.dat \
comma-expr.dat \ comma-expr.dat \
@ -119,6 +120,15 @@ alignment.run: Makefile build-run
include ./$(DEPDIR)/alignment.Qo # am--include-marker include ./$(DEPDIR)/alignment.Qo # am--include-marker
r_depfiles_remade += ./$(DEPDIR)/alignment.Qo r_depfiles_remade += ./$(DEPDIR)/alignment.Qo
anonstruct_dat_SOURCES=anonstruct.r
anonstruct_obj=$(anonstruct_dat_SOURCES:.r=.qfo)
anonstruct.dat$(EXEEXT): $(anonstruct_obj) $(QFCC_DEP)
$(QFCC) $(QCFLAGS) -o $@ $(anonstruct_obj)
anonstruct.run: Makefile build-run
@$(srcdir)/build-run $@
include ./$(DEPDIR)/anonstruct.Qo # am--include-marker
r_depfiles_remade += ./$(DEPDIR)/anonstruct.Qo
chewed_alias_dat_SOURCES=chewed-alias.r chewed_alias_dat_SOURCES=chewed-alias.r
chewed_alias_obj=$(chewed_alias_dat_SOURCES:.r=.qfo) chewed_alias_obj=$(chewed_alias_dat_SOURCES:.r=.qfo)
chewed-alias.dat$(EXEEXT): $(chewed_alias_obj) $(QFCC_DEP) chewed-alias.dat$(EXEEXT): $(chewed_alias_obj) $(QFCC_DEP)

View file

@ -0,0 +1,57 @@
void printf (string fmt, ...) = #0;
typedef struct xyzzy_s {
int magic;
} xyzzy_t;
typedef struct anon_s {
int foo;
int id;
struct {
int bar;
int baz;
};
union {
int snafu;
float fizzle;
};
} anon_t;
union {
int xsnafu;
float xfizzle;
};
int foo (float f)
{
anon_t anon;
anon.fizzle = f;
return anon.snafu;
}
int main()
{
anon_t anon;
int ret = 0;
if (&anon.snafu != &anon.fizzle) {
printf ("anon union broken: %p %p\n",
&anon.snafu, &anon.fizzle);
ret |= 1;
}
if (&anon.snafu - &anon.baz != 1) {
printf ("snafu and baz not adjacant: snafu:%p baz:%p\n",
&anon.snafu, &anon.baz);
ret |= 1;
}
if (&anon.baz - &anon.bar != 1) {
printf ("baz and bar not adjacant: baz:%p bar:%p\n",
&anon.baz, &anon.bar);
ret |= 1;
}
if (&anon.bar - &anon.id != 1) {
printf ("bar not after id: bar:%p id:%p\n",
&anon.bar, &anon.id);
ret |= 1;
}
return ret;
}