mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-26 22:31:05 +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_public,
|
||||||
vis_protected,
|
vis_protected,
|
||||||
vis_private,
|
vis_private,
|
||||||
|
vis_anonymous,
|
||||||
} vis_t;
|
} vis_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
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