mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-26 14:20:59 +00:00
[qfcc] Implement anonymous structs and unions
For struct/union scope
This commit is contained in:
parent
4fa203852a
commit
e298904dc0
6 changed files with 128 additions and 4 deletions
|
@ -45,6 +45,7 @@ typedef enum vis_e {
|
|||
vis_public,
|
||||
vis_protected,
|
||||
vis_private,
|
||||
vis_anonymous,
|
||||
} vis_t;
|
||||
|
||||
typedef enum {
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
|
||||
#include <QF/hash.h>
|
||||
#include <QF/sys.h>
|
||||
#include <QF/va.h>
|
||||
|
||||
#include "class.h"
|
||||
#include "debug.h"
|
||||
|
@ -264,6 +265,26 @@ default_type (specifier_t spec, symbol_t *sym)
|
|||
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
|
||||
|
@ -675,15 +696,27 @@ struct_def
|
|||
: type struct_decl_list
|
||||
| type
|
||||
{
|
||||
if ($1.sym) {
|
||||
if ($1.sym && $1.sym->type != $1.type) {
|
||||
// a type name (id, typedef, etc) was used as a field name.
|
||||
// this is allowed in C
|
||||
print_type ($1.type);
|
||||
printf ("%s\n", $1.sym->name);
|
||||
$1.sym = new_symbol ($1.sym->name);
|
||||
$1.sym->type = $1.type;
|
||||
$1.sym->sy_type = sy_var;
|
||||
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 {
|
||||
// bare type
|
||||
warning (0, "declaration does not declare anything");
|
||||
|
|
|
@ -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 *s;
|
||||
int alignment = 1;
|
||||
symbol_t *as;
|
||||
|
||||
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) {
|
||||
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)
|
||||
sym->type = find_type (sym->type); // checks the tag, not the symtab
|
||||
|
|
|
@ -152,7 +152,7 @@ symtab_removesymbol (symtab_t *symtab, symbol_t *symbol)
|
|||
for (s = &symtab->symbols; *s && *s != symbol; s = & (*s)->next)
|
||||
;
|
||||
if (!*s)
|
||||
internal_error (0, "symtab_removesymbol");
|
||||
internal_error (0, "attempt to remove symbol not in symtab");
|
||||
*s = (*s)->next;
|
||||
if (symtab->symtail == &symbol->next)
|
||||
symtab->symtail = s;
|
||||
|
|
|
@ -31,6 +31,7 @@ fail_bins=
|
|||
test_progs_dat=\
|
||||
address-cast.dat \
|
||||
alignment.dat \
|
||||
anonstruct.dat \
|
||||
chewed-alias.dat \
|
||||
chewed-return.dat \
|
||||
comma-expr.dat \
|
||||
|
@ -119,6 +120,15 @@ alignment.run: Makefile build-run
|
|||
include ./$(DEPDIR)/alignment.Qo # am--include-marker
|
||||
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_obj=$(chewed_alias_dat_SOURCES:.r=.qfo)
|
||||
chewed-alias.dat$(EXEEXT): $(chewed_alias_obj) $(QFCC_DEP)
|
||||
|
|
57
tools/qfcc/test/anonstruct.r
Normal file
57
tools/qfcc/test/anonstruct.r
Normal 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;
|
||||
}
|
Loading…
Reference in a new issue