[qfcc] Improve handling of type names as variables

This is allowed in C so long as the scopes are different.
This commit is contained in:
Bill Currie 2022-01-19 21:32:01 +09:00
parent 4ae9474396
commit 105d57f5e7
3 changed files with 48 additions and 11 deletions

View file

@ -422,6 +422,18 @@ process_keyword (keyword_t *keyword, const char *token)
sym = symtab_lookup (current_symtab, token);
qc_yylval.symbol = sym;
// the global id symbol is always just a name so attempts to redefine
// it globally can be caught and treated as an error, but it needs to
// be redefinable when in an enclosing scope.
if (sym->sy_type == sy_name) {
// this is the global id (object)
return OBJECT;
} else if (sym->sy_type == sy_type) {
// id has been redeclared via a typedef
return TYPE_NAME;
}
// id has been redelcared as a variable (hopefully)
return NAME;
} else {
qc_yylval.type = keyword->type;
}

View file

@ -267,6 +267,7 @@ spec_merge (specifier_t spec, specifier_t new)
spec.multi_store = 1;
}
}
spec.sym = new.sym;
spec.is_signed |= new.is_signed;
spec.is_unsigned |= new.is_unsigned;
spec.is_short |= new.is_short;
@ -367,6 +368,16 @@ is_null_spec (specifier_t spec)
return memcmp (&spec, &null_spec, sizeof (spec)) == 0;
}
static int
use_type_name (specifier_t spec)
{
spec.sym = new_symbol (spec.sym->name);
spec.sym->type = spec.type;
spec.sym->sy_type = sy_var;
symtab_addsymbol (current_symtab, spec.sym);
return !!spec.sym->table;
}
static void
check_specifiers (specifier_t spec)
{
@ -383,7 +394,12 @@ check_specifiers (specifier_t spec)
} else if (!spec.type && spec.sym) {
bug (0, "wha? %p %p", spec.type, spec.sym);
} else {
bug (0, "wha? %p %p", spec.type, spec.sym);
// a type name (id, typedef, etc) was used as a variable name.
// this is allowed in C, so long as it's in a different scope
if (!use_type_name (spec)) {
error (0, "%s redeclared as different kind of symbol",
spec.sym->name);
}
}
}
}
@ -614,7 +630,9 @@ type
// deal with eg "int id"
$1.sym = $2.sym;
if (!$1.sym) {
if (!$1.sym && !$1.type) {
$1 = spec_merge ($1, $2);
} else if (!$1.sym) {
error (0, "two or more data types in declaration specifiers");
}
$$ = $1;
@ -827,11 +845,7 @@ struct_def
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
$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) {
if (!use_type_name ($1)) {
error (0, "duplicate field `%s'", $1.sym->name);
}
} else if (is_anonymous_struct ($1)) {
@ -997,18 +1011,27 @@ param_declaration
$2->type = find_type (append_type ($2->type, $1.type));
$$ = new_param (0, $2->type, $2->name);
}
| abstract_decl { $$ = new_param (0, $1->type, 0); }
| abstract_decl { $$ = new_param (0, $1->type, $1->name); }
| ELLIPSIS { $$ = new_param (0, 0, 0); }
;
abstract_decl
: type abs_decl
{
// abs_decl's symbol is just a carrier for the type
if ($2->name) {
bug (0, "unexpected name in abs_decl");
}
if ($1.sym) {
$1.sym = new_symbol ($1.sym->name);
$1.sym->type = $2->type;
$2 = $1.sym;
}
$$ = $2;
$1 = default_type ($1, $2);
$$->type = find_type (append_type ($$->type, $1.type));
}
| error { $$ = new_symbol (""); }
| error { $$ = new_symbol (0); }
;
qc_param_decl
@ -1035,7 +1058,7 @@ qc_param_decl
;
abs_decl
: /* empty */ { $$ = new_symbol (""); }
: /* empty */ { $$ = new_symbol (0); }
| '(' abs_decl ')' function_params
{
$$ = $2;

View file

@ -75,7 +75,9 @@ new_symbol (const char *name)
{
symbol_t *symbol;
ALLOC (256, symbol_t, symbols, symbol);
symbol->name = save_string (name);
if (name) {
symbol->name = save_string (name);
}
return symbol;
}