[qfcc] Add bypass scopes

Because the symbol tables for generic functions are ephemeral (as such),
they need to be easily removed from the scope chain, it's easiest if
definitions are never added to them (instead, they get added to the
parent symbol table). This keeps handling of function declarations or
definitions and their parameter scopes simple as the function gets put
in the global scope still, and the parameter scope simply gets
reconnected to the global scope (really, the generic scope's parent)
when the parameter scope is popped within a generic scope.
This commit is contained in:
Bill Currie 2024-12-11 03:06:50 +09:00
parent c8158f9ebe
commit 36cf1f948e
10 changed files with 179 additions and 119 deletions

View file

@ -212,14 +212,12 @@ function_t *make_function (symbol_t *sym, const char *nice_name,
enum storage_class_e storage);
symbol_t *function_symbol (specifier_t spec);
const expr_t *find_function (const expr_t *fexpr, const expr_t *params);
function_t *begin_function (symbol_t *sym, const char *nicename,
symtab_t *parent, int far,
enum storage_class_e storage);
function_t *build_code_function (symbol_t *fsym, const expr_t *state_expr,
function_t *begin_function (specifier_t spec, const char *nicename,
symtab_t *parent);
void build_code_function (specifier_t spec, const expr_t *state_expr,
expr_t *statements, rua_ctx_t *ctx);
function_t *build_builtin_function (symbol_t *sym, const char *ext_name,
const expr_t *bi_val, int far,
enum storage_class_e storage);
void build_builtin_function (specifier_t spec, const char *ext_name,
const expr_t *bi_val);
void build_intrinsic_function (specifier_t spec, const expr_t *intrinsic);
void emit_function (function_t *f, expr_t *e);
void clear_functions (void);

View file

@ -84,6 +84,7 @@ typedef struct rua_tok_s {
typedef struct {
symtab_t *symtab;
function_t *function;
specifier_t spec;
} funcstate_t;
typedef union rua_val_s {

View file

@ -81,6 +81,7 @@ typedef struct specifier_s {
bool is_generic:1;
bool is_generic_block:1;
bool is_function:1;//FIXME do proper void(*)() -> ev_func
bool is_far:1;
};
unsigned spec_bits;
};

View file

@ -107,6 +107,7 @@ typedef enum {
stab_enum,
stab_label,
stab_block,
stab_bypass, ///< symbols are added to parent
} stab_type_e;
typedef struct symtab_s {

View file

@ -1010,11 +1010,19 @@ value_too_large (const type_t *val_type)
static void
check_function (symbol_t *fsym)
{
function_t *func = fsym->metafunc->func;
param_t *params = fsym->params;
param_t *p;
int i;
auto ret_type = fsym->type->func.ret_type;
if (!func) {
internal_error (0, "function %s not defined", fsym->name);
}
if (!is_func (func->type)) {
internal_error (0, "function type %s not a funciton", fsym->name);
}
if (!ret_type || !type_size (ret_type)) {
error (0, "return type is an incomplete type");
return;
@ -1048,15 +1056,6 @@ build_scope (symbol_t *fsym, symtab_t *parent)
symtab_t *parameters;
symtab_t *locals;
if (!func) {
internal_error (0, "function %s not defined", fsym->name);
}
if (!is_func (func->type)) {
internal_error (0, "function type %s not a funciton", fsym->name);
}
check_function (fsym);
func->label_scope = new_symtab (0, stab_label);
parameters = new_symtab (parent, stab_param);
@ -1122,9 +1121,9 @@ add_function (function_t *f)
}
function_t *
begin_function (symbol_t *sym, const char *nicename, symtab_t *parent,
int far, storage_class_t storage)
begin_function (specifier_t spec, const char *nicename, symtab_t *parent)
{
auto sym = spec.sym;
if (sym->sy_type != sy_func) {
error (0, "%s is not a function", sym->name);
sym = new_symbol_type (sym->name, &type_func);
@ -1143,9 +1142,9 @@ begin_function (symbol_t *sym, const char *nicename, symtab_t *parent,
});
}
defspace_t *space = far ? pr.far_data : sym->table->space;
defspace_t *space = spec.is_far ? pr.far_data : sym->table->space;
func = make_function (sym, nicename, space, storage);
func = make_function (sym, nicename, space, spec.storage);
if (!func->def->external) {
func->def->initialized = 1;
func->def->constant = 1;
@ -1163,6 +1162,8 @@ begin_function (symbol_t *sym, const char *nicename, symtab_t *parent,
func->line_info = lineno - pr.linenos;
}
check_function (sym);
build_scope (sym, parent);
return func;
}
@ -1177,58 +1178,60 @@ build_function (symbol_t *fsym)
}
}
function_t *
build_code_function (symbol_t *fsym, const expr_t *state_expr,
void
build_code_function (specifier_t spec, const expr_t *state_expr,
expr_t *statements, rua_ctx_t *ctx)
{
auto fsym = spec.sym;
if (ctx) {
statements = (expr_t *) expr_process (statements, ctx);
}
if (fsym->sy_type != sy_func) // probably in error recovery
return 0;
if (fsym->sy_type != sy_func) { // probably in error recovery
return;
}
build_function (fsym);
if (state_expr) {
prepend_expr (statements, state_expr);
}
function_t *func = fsym->metafunc->func;
current_target.build_code (func, statements);
return fsym->metafunc->func;
}
function_t *
build_builtin_function (symbol_t *sym, const char *ext_name,
const expr_t *bi_val, int far, storage_class_t storage)
void
build_builtin_function (specifier_t spec, const char *ext_name,
const expr_t *bi_val)
{
auto sym = spec.sym;
int bi;
if (sym->sy_type != sy_func) {
error (bi_val, "%s is not a function", sym->name);
return 0;
return;
}
if (!is_int_val (bi_val)
&& !(type_default != &type_int && is_float_val (bi_val))) {
error (bi_val, "invalid constant for = #");
return 0;
return;
}
if (sym->metafunc->meta_type == mf_generic) {
return 0;
return;
}
function_t *func = sym->metafunc->func;
if (func && func->def && func->def->initialized) {
error (bi_val, "%s redefined", sym->name);
return 0;
return;
}
defspace_t *space = far ? pr.far_data : sym->table->space;
func = make_function (sym, nullptr, space, storage);
defspace_t *space = spec.is_far ? pr.far_data : sym->table->space;
func = make_function (sym, nullptr, space, spec.storage);
if (ext_name) {
func->s_name = ReuseString (ext_name);
}
if (func->def->external)
return 0;
if (func->def->external) {
return;
}
func->def->initialized = 1;
func->def->constant = 1;
@ -1245,17 +1248,18 @@ build_builtin_function (symbol_t *sym, const char *ext_name,
}
if (bi < 0) {
error (bi_val, "builtin functions must be positive or 0");
return 0;
return;
}
func->builtin = bi;
reloc_def_func (func, func->def);
build_function (sym);
check_function (sym);
// for debug info
build_scope (sym, current_symtab);
func->parameters->space->size = 0;
func->locals->space = func->parameters->space;
return func;
}
void
@ -1315,8 +1319,12 @@ emit_ctor (void)
return;
}
auto ctor_sym = new_symbol_type (".ctor", &type_func);
ctor_sym = function_symbol ((specifier_t) { .sym = ctor_sym });
current_func = begin_function (ctor_sym, 0, current_symtab, 1, sc_static);
build_code_function (ctor_sym, 0, pr.ctor_exprs, nullptr);
auto spec = (specifier_t) {
.sym = new_symbol_type (".ctor", &type_func),
.storage = sc_static,
.is_far = true,
};
spec.sym = function_symbol (spec);
current_func = begin_function (spec, nullptr, current_symtab);
build_code_function (spec, 0, pr.ctor_exprs, nullptr);
}

View file

@ -437,9 +437,11 @@ SRC_LINE
"genUType clamp(genUType x, genUType minVal, genUType maxVal);" "\n"
"genUType clamp(genUType x, uint minVal, uint maxVal);" "\n"
"genFType mix(genFType x, genFType y, genFType a) = " GLSL(46) ";" "\n"
"genFType mix(genFType x, genFType y, float a) = " GLSL(46) ";" "\n"
"genFType mix(genFType x, genFType y, float a)" "\n"
"{ return mix (x, y, (genFType) a); }" "\n"
"genDType mix(genDType x, genDType y, genDType a) = " GLSL(46) ";" "\n"
"genDType mix(genDType x, genDType y, double a) = " GLSL(46) ";" "\n"
"genDType mix(genDType x, genDType y, double a)" "\n"
"{ return mix (x, y, (genDType) a); }" "\n"
"genFType mix(genFType x, genFType y, genBType a) = " SPV(169) ";" "\n"
"genDType mix(genDType x, genDType y, genBType a) = " SPV(169) ";" "\n"
"genIType mix(genIType x, genIType y, genBType a) = " SPV(169) ";" "\n"

View file

@ -332,19 +332,16 @@ function_definition
auto spec = $1;
spec.sym->params = spec.params;
spec.is_overload = true;
auto sym = function_symbol (spec);
current_func = begin_function (sym, nullptr, current_symtab,
false, spec.storage);
spec.sym = function_symbol (spec);
current_func = begin_function (spec, nullptr, current_symtab);
current_symtab = current_func->locals;
current_storage = sc_local;
spec.sym = sym;
$1 = spec;
}
compound_statement_no_new_scope
{
auto spec = $1;
auto sym = spec.sym;
build_code_function (sym, nullptr, (expr_t *) $3, ctx);
build_code_function (spec, nullptr, (expr_t *) $3, ctx);
current_symtab = $<symtab>2;
current_storage = sc_global;//FIXME
current_func = nullptr;

View file

@ -257,8 +257,12 @@ static expr_t *local_expr;
static void
end_generic_scope (void)
{
generic_scope = false;
if (generic_symtab != current_symtab || !generic_symtab->parent) {
internal_error (0, "scope stack tangled?");
}
current_symtab = generic_symtab->parent;
generic_symtab = nullptr;
generic_scope = false;
}
static void
@ -662,6 +666,23 @@ decl_expr (specifier_t spec, const expr_t *init, rua_ctx_t *ctx)
return append_decl (decl, sym, init);
}
static symtab_t *
pop_scope (symtab_t *current)
{
auto parent = current->parent;
if (!parent) {
internal_error (0, "scope stack underflow");
}
if (parent->type == stab_bypass) {
if (!parent->parent) {
internal_error (0, "bypass scope with no parent");
}
// reconnect the current scope to the parent of the bypassed scope
current->parent = parent->parent;
}
return parent;
}
%}
%expect 2
@ -681,7 +702,7 @@ program
external_def_list
: /* empty */
{
if (!generic_block) {
if (!current_symtab) {
current_symtab = pr.symtab;
}
}
@ -832,8 +853,8 @@ qc_nocode_func
const expr_t *bi_val = expr_process ($4, ctx);
spec.is_overload |= ctx->language->always_overload;
symbol_t *sym = function_symbol (spec);
build_builtin_function (sym, nullptr, bi_val, 0, spec.storage);
spec.sym = function_symbol (spec);
build_builtin_function (spec, nullptr, bi_val);
}
| identifier '=' intrinsic
{
@ -858,24 +879,24 @@ qc_code_func
: identifier '=' optional_state_expr
save_storage
{
$<funcstate>$ = (funcstate_t) {
.symtab = current_symtab,
specifier_t spec = qc_set_symbol ($<spec>0, $1);
auto fs = (funcstate_t) {
.function = current_func,
};
specifier_t spec = qc_set_symbol ($<spec>0, $1);
spec.is_overload |= ctx->language->always_overload;
symbol_t *sym = function_symbol (spec);
current_func = begin_function (sym, 0, current_symtab, 0,
spec.storage);
spec.sym = function_symbol (spec);
current_func = begin_function (spec, nullptr, current_symtab);
current_symtab = current_func->locals;
current_storage = sc_local;
$1 = sym;
fs.spec = spec;
$<funcstate>$ = fs;
}
compound_statement_ns
{
build_code_function ($1, $3, $6, ctx);
current_symtab = $<funcstate>5.symtab;
current_func = $<funcstate>5.function;
auto fs = $<funcstate>5;
build_code_function (fs.spec, $3, $6, ctx);
current_symtab = pop_scope (current_func->parameters);
current_func = fs.function;
restore_storage ($4);
}
;
@ -1171,29 +1192,34 @@ save_storage
;
function_body
: method_optional_state_expr
: method_optional_state_expr[state]
{
specifier_t spec = default_type ($<spec>0, $<spec>0.sym);
spec.is_overload |= ctx->language->always_overload;
$<symbol>$ = function_symbol (spec);
specifier_t spec = $<spec>0;
if (!spec.is_generic) {
spec = default_type (spec, spec.sym);
}
save_storage
spec.is_overload |= ctx->language->always_overload;
spec.sym = function_symbol (spec);
$<spec>$ = spec;
}
save_storage[storage]
{
$<funcstate>$ = (funcstate_t) {
.symtab = current_symtab,
.function = current_func,
};
current_func = begin_function ($<symbol>2, 0, current_symtab, 0,
$<spec>-1.storage);
auto spec = $<spec>2;
current_func = begin_function (spec, nullptr, current_symtab);
current_symtab = current_func->locals;
current_storage = sc_local;
}
compound_statement_ns
compound_statement_ns[body]
{
build_code_function ($<symbol>2, $1, $5, ctx);
current_symtab = $<funcstate>4.symtab;
current_func = $<funcstate>4.function;
restore_storage ($3);
auto spec = $<spec>2;
auto funcstate = $<funcstate>4;
build_code_function (spec, $state, $body, ctx);
current_symtab = pop_scope (current_func->parameters);
current_func = funcstate.function;
restore_storage ($storage);
}
| '=' '#' expr ';'
{
@ -1201,8 +1227,8 @@ function_body
const expr_t *bi_val = expr_process ($3, ctx);
spec.is_overload |= ctx->language->always_overload;
symbol_t *sym = function_symbol (spec);
build_builtin_function (sym, nullptr, bi_val, 0, spec.storage);
spec.sym = function_symbol (spec);
build_builtin_function (spec, nullptr, bi_val);
}
| '=' intrinsic
{
@ -1232,6 +1258,9 @@ storage_class
} else {
generic_scope = true;
}
generic_symtab->type = stab_bypass;
generic_symtab->parent = current_symtab;
current_symtab = generic_symtab;
}
| ATTRIBUTE '(' attribute_list ')'
{
@ -1380,7 +1409,7 @@ optional_enum_list
enum_list
: '{' enum_init enumerator_list optional_comma '}'
{
current_symtab = current_symtab->parent;
current_symtab = pop_scope (current_symtab);
$$ = finish_enum ($3);
}
;
@ -1467,7 +1496,7 @@ struct_list
{
symbol_t *sym;
symtab_t *symtab = current_symtab;
current_symtab = symtab->parent;
current_symtab = pop_scope (symtab);
if ($<op>1) {
sym = $<symbol>2;
@ -1841,7 +1870,7 @@ end_scope
: /* empty */
{
if (!options.traditional) {
current_symtab = current_symtab->parent;
current_symtab = pop_scope (current_symtab);
}
}
;
@ -1980,7 +2009,7 @@ statement
}
compound_statement
{
current_symtab = current_symtab->parent;
current_symtab = pop_scope (current_symtab);
$$ = $9;
}
| ALGEBRA '(' TYPE_SPEC ')'
@ -1990,7 +2019,7 @@ statement
}
compound_statement
{
current_symtab = current_symtab->parent;
current_symtab = pop_scope (current_symtab);
$$ = $6;
}
| ALGEBRA '(' TYPE_NAME ')'
@ -2000,7 +2029,7 @@ statement
}
compound_statement
{
current_symtab = current_symtab->parent;
current_symtab = pop_scope (current_symtab);
$$ = $6;
}
| comma_expr ';'
@ -2523,7 +2552,7 @@ ivar_decl_list
{
symtab_t *tab = $<symtab>1;
$$ = current_symtab;
current_symtab = tab->parent;
current_symtab = pop_scope (tab);
tab->parent = 0;
tab = $$->parent; // preserve the ivars inheritance chain
@ -2615,14 +2644,18 @@ methoddef
symbol_t *sym = $<symbol>4;
symtab_t *ivar_scope;
$<funcstate>$ = (funcstate_t) {
.symtab = current_symtab,
.function = current_func,
auto spec = (specifier_t) {
.sym = sym,
.storage = sc_static,
.is_far = true,
};
ivar_scope = class_ivar_scope (current_class, current_symtab);
current_func = begin_function (sym, nicename, ivar_scope, 1,
sc_static);
$<funcstate>$ = (funcstate_t) {
.symtab = ivar_scope,
.function = current_func,
};
current_func = begin_function (spec, nicename, ivar_scope);
class_finish_ivar_scope (current_class, ivar_scope,
current_func->locals);
method->func = sym->metafunc->func;
@ -2632,22 +2665,29 @@ methoddef
}
compound_statement_ns
{
build_code_function ($<symbol>4, $3, $7, ctx);
current_symtab = $<funcstate>6.symtab;
current_func = $<funcstate>6.function;
auto fs = $<funcstate>6;
auto fsym = $<symbol>4;
fs.spec.sym = fsym;
build_code_function (fs.spec, $3, $7, ctx);
current_symtab = pop_scope (fs.symtab);
current_func = fs.function;
restore_storage ($5);
}
| ci methoddecl '=' '#' const ';'
{
symbol_t *sym;
method_t *method = $2;
const expr_t *bi_val = expr_process ($5, ctx);
method->instance = $1;
method = class_find_method (current_class, method);
sym = method_symbol (current_class, method);
build_builtin_function (sym, nullptr, bi_val, 1, sc_static);
method->func = sym->metafunc->func;
auto spec = (specifier_t) {
.sym = method_symbol (current_class, method),
.storage = sc_static,
.is_far = true,
};
build_builtin_function (spec, nullptr, bi_val);
method->func = spec.sym->metafunc->func;
method->def = method->func->def;
}
;
@ -3127,9 +3167,6 @@ qc_keyword_or_id (QC_YYSTYPE *lval, const char *token, rua_ctx_t *ctx)
}
symbol_t *sym = nullptr;
if (generic_symtab) {
sym = symtab_lookup (generic_symtab, token);
}
if (!sym) {
sym = symtab_lookup (current_symtab, token);
}

View file

@ -161,14 +161,17 @@ build_dotmain (symbol_t *program, rua_ctx_t *ctx)
exitcode = new_symbol_expr (symtab_lookup (current_symtab, "ExitCode"));
current_func = begin_function (dotmain, 0, current_symtab, 0,
current_storage);
auto spec = (specifier_t) {
.sym = dotmain,
.storage = current_storage,
};
current_func = begin_function (spec, nullptr, current_symtab);
code = new_block_expr (0);
code->block.scope = current_func->locals;
auto call = new_call_expr (new_symbol_expr (program), nullptr, nullptr);
append_expr (code, call);
append_expr (code, new_return_expr (exitcode));
build_code_function (dotmain, 0, code, ctx);
build_code_function (spec, 0, code, ctx);
}
static const expr_t *
@ -281,10 +284,13 @@ program
symtab_removesymbol (current_symtab, $1);
symtab_addsymbol (current_symtab, $1);
current_func = begin_function ($1, 0, current_symtab, 0,
current_storage);
auto spec = (specifier_t) {
.sym = $1,
.storage = current_storage,
};
current_func = begin_function (spec, nullptr, current_symtab);
current_symtab = current_func->locals;
build_code_function ($1, 0, $4, ctx);
build_code_function (spec, 0, $4, ctx);
current_symtab = st;
build_dotmain ($1, ctx);
@ -367,25 +373,25 @@ subprogram_declarations
subprogram_declaration
: subprogram_head ';'
{
$<spec>$.storage = current_storage;
auto sym = $1;
// always an sy_xvalue with callable symbol in lvalue and
// actual function symbol in rvalue
auto csym = (symbol_t *) sym->xvalue.lvalue;
auto fsym = (symbol_t *) sym->xvalue.rvalue;
current_func = begin_function (fsym, sym->name, current_symtab, 0,
current_storage);
auto spec = (specifier_t) {
.sym = fsym,
.storage = current_storage,
};
current_func = begin_function (spec, sym->name, current_symtab);
current_symtab = current_func->locals;
current_storage = sc_local;
// null for procedures, valid symbol expression for functions
csym->xvalue.lvalue = function_return (current_func);
$<spec>$ = spec;
}
declarations compound_statement ';'
{
auto sym = $1;
// always an sy_xvalue with callable symbol in lvalue and
// actual function symbol in rvalue
auto fsym = (symbol_t *) sym->xvalue.rvalue;
auto spec = $<spec>3;
auto statements = $5;
if (!statements) {
statements = new_block_expr (0);
@ -393,17 +399,20 @@ subprogram_declaration
}
auto ret_expr = new_return_expr (function_return (current_func));
append_expr (statements, ret_expr);
build_code_function (fsym, 0, statements, ctx);
build_code_function (spec, 0, statements, ctx);
current_symtab = current_func->parameters->parent;
current_storage = $<spec>3.storage;
current_storage = spec.storage;
}
| subprogram_head ASSIGNOP '#' VALUE ';'
{
auto sym = $1;
// always an sy_xvalue with callable symbol in lvalue and
// actual function symbol in rvalue
auto fsym = (symbol_t *) sym->xvalue.rvalue;
build_builtin_function (fsym, sym->name, $4, 0, current_storage);
auto spec = (specifier_t) {
.sym = (symbol_t *) sym->xvalue.rvalue,
.storage = current_storage,
};
build_builtin_function (spec, sym->name, $4);
}
;

View file

@ -152,6 +152,12 @@ symtab_addsymbol (symtab_t *symtab, symbol_t *symbol)
if (symbol->table)
internal_error (0, "symbol '%s' is already in another symbol table",
symbol->name);
if (symtab->type == stab_bypass) {
if (!symtab->parent) {
internal_error (0, "bypass symbol table with no parent");
}
symtab = symtab->parent;
}
if ((s = Hash_Find (symtab->tab, symbol->name)))
return s;
Hash_Add (symtab->tab, symbol);