mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-04-15 22:01:01 +00:00
[qfcc] Partially handle implementing generic functions
Implemented via specific overloads of the function. It's not quite working correctly in that parameter names are taken from the declaration instead of definition. However, this seems to be an old bug that went unnoticed due to me almost always using the same parameter names in declaration and definition. Also, the code in get_function() is a horrible mess. However, the basic idea turned out to be simpler than I though (though details of the implementation are indeed a little trickier): generic functions are essentially prototype generators when implemented using non-generic specialized overloads.
This commit is contained in:
parent
980dd913b5
commit
ffcee99835
1 changed files with 185 additions and 166 deletions
|
@ -532,58 +532,8 @@ new_metafunc (void)
|
|||
return metafunc;
|
||||
}
|
||||
|
||||
static metafunc_t *
|
||||
get_function (const char *name, const type_t *type, specifier_t spec)
|
||||
{
|
||||
metafunc_t *func = Hash_Find (function_map, name);
|
||||
if (func && func->meta_type == mf_generic) {
|
||||
error (0, "can't mix generic and simple or overload");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool overload = spec.is_overload | current_language.always_override;
|
||||
const char *full_name;
|
||||
|
||||
full_name = save_string (va (0, "%s|%s", name, encode_params (type)));
|
||||
|
||||
// check if the exact function signature already exists, in which case
|
||||
// simply return it.
|
||||
func = Hash_Find (metafuncs, full_name);
|
||||
if (func) {
|
||||
if (func->type != type) {
|
||||
error (0, "can't overload on return types");
|
||||
return func;
|
||||
}
|
||||
return func;
|
||||
}
|
||||
|
||||
func = Hash_Find (function_map, name);
|
||||
if (func) {
|
||||
if (!overload && func->meta_type != mf_overload) {
|
||||
warning (0, "creating overloaded function %s without @overload",
|
||||
full_name);
|
||||
warning (&(expr_t) { .loc = func->loc },
|
||||
"(previous function is %s)", func->full_name);
|
||||
}
|
||||
overload = true;
|
||||
}
|
||||
|
||||
func = new_metafunc ();
|
||||
*func = (metafunc_t) {
|
||||
.name = save_string (name),
|
||||
.full_name = full_name,
|
||||
.type = type,
|
||||
.loc = pr.loc,
|
||||
.meta_type = overload ? mf_overload : mf_simple,
|
||||
};
|
||||
|
||||
Hash_Add (metafuncs, func);
|
||||
Hash_Add (function_map, func);
|
||||
return func;
|
||||
}
|
||||
|
||||
static void
|
||||
set_func_type_attrs (const type_t *func_type, attribute_t *attr_list)
|
||||
set_func_attrs (const type_t *func_type, attribute_t *attr_list)
|
||||
{
|
||||
auto func = &((type_t *) func_type)->func;//FIXME
|
||||
for (auto attr = attr_list; attr; attr = attr->next) {
|
||||
|
@ -597,97 +547,33 @@ set_func_type_attrs (const type_t *func_type, attribute_t *attr_list)
|
|||
}
|
||||
}
|
||||
|
||||
symbol_t *
|
||||
function_symbol (specifier_t spec)
|
||||
static bool
|
||||
check_type (const type_t *type, const type_t *param_type, unsigned *cost,
|
||||
bool promote)
|
||||
{
|
||||
symbol_t *sym = spec.sym;
|
||||
const char *name = sym->name;
|
||||
metafunc_t *func = Hash_Find (function_map, name);
|
||||
|
||||
auto genfunc = parse_generic_function (name, spec);
|
||||
//FIXME want to be able to provide specific overloads for generic functions
|
||||
//but need to figure out details, so disallow for now.
|
||||
if (genfunc) {
|
||||
if (func && func->meta_type != mf_generic) {
|
||||
error (0, "can't mix generic and simple or overload");
|
||||
return nullptr;
|
||||
}
|
||||
add_generic_function (genfunc);
|
||||
|
||||
ALLOC (1024, metafunc_t, metafuncs, func);
|
||||
*func = (metafunc_t) {
|
||||
.name = save_string (name),
|
||||
.full_name = name,
|
||||
.loc = pr.loc,
|
||||
.meta_type = mf_generic,
|
||||
};
|
||||
Hash_Add (metafuncs, func);
|
||||
Hash_Add (function_map, func);
|
||||
} else {
|
||||
if (!spec.sym->type || !spec.sym->type->encoding) {
|
||||
spec = default_type (spec, spec.sym);
|
||||
spec.sym->type = append_type (spec.sym->type, spec.type);
|
||||
set_func_type_attrs (spec.sym->type, spec.attributes);
|
||||
spec.sym->type = find_type (spec.sym->type);
|
||||
}
|
||||
func = get_function (name, unalias_type (sym->type), spec);
|
||||
if (!type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (func && func->meta_type == mf_overload)
|
||||
name = func->full_name;
|
||||
symbol_t *s = symtab_lookup (current_symtab, name);
|
||||
if (!s || s->table != current_symtab) {
|
||||
s = new_symbol (name);
|
||||
s->sy_type = sy_func;
|
||||
s->type = unalias_type (sym->type);
|
||||
s->params = sym->params;
|
||||
s->metafunc = func;
|
||||
symtab_addsymbol (current_symtab, s);
|
||||
if (type == param_type) {
|
||||
return true;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// NOTE sorts the list in /reverse/ order
|
||||
static int
|
||||
func_compare (const void *a, const void *b)
|
||||
{
|
||||
metafunc_t *fa = *(metafunc_t **) a;
|
||||
metafunc_t *fb = *(metafunc_t **) b;
|
||||
const type_t *ta = fa->type;
|
||||
const type_t *tb = fb->type;
|
||||
int na = ta->func.num_params;
|
||||
int nb = tb->func.num_params;
|
||||
int ret, i;
|
||||
|
||||
if (na < 0)
|
||||
na = ~na;
|
||||
if (nb < 0)
|
||||
nb = ~nb;
|
||||
if (na != nb)
|
||||
return nb - na;
|
||||
if ((ret = (tb->func.num_params - ta->func.num_params)))
|
||||
return ret;
|
||||
for (i = 0; i < na && i < nb; i++) {
|
||||
auto diff = tb->func.param_types[i] - ta->func.param_types[i];
|
||||
if (diff) {
|
||||
return diff < 0 ? -1 : 1;
|
||||
}
|
||||
if (is_reference (type)) {
|
||||
// pass by references is a free conversion, but no promotion
|
||||
return dereference_type (type) == param_type;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const expr_t *
|
||||
set_func_symbol (const expr_t *fexpr, metafunc_t *f)
|
||||
{
|
||||
auto sym = symtab_lookup (current_symtab, f->full_name);
|
||||
if (!sym) {
|
||||
internal_error (fexpr, "overloaded function %s not found",
|
||||
f->full_name);
|
||||
if (is_reference (param_type)) {
|
||||
// dereferencing a reference is free so long as there's no
|
||||
// promotion, otherwise there's the promotion cost
|
||||
param_type = dereference_type (param_type);
|
||||
}
|
||||
auto nf = new_expr ();
|
||||
*nf = *fexpr;
|
||||
nf->symbol = sym;
|
||||
return nf;
|
||||
if (type == param_type) {
|
||||
return true;
|
||||
}
|
||||
if (!promote || !type_promotes (type, param_type)) {
|
||||
return false;
|
||||
}
|
||||
*cost += 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
static const type_t * __attribute__((pure))
|
||||
|
@ -716,34 +602,6 @@ select_type (gentype_t *gentype, const type_t *param_type)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
static bool
|
||||
check_type (const type_t *type, const type_t *param_type, unsigned *cost, bool promote)
|
||||
{
|
||||
if (!type) {
|
||||
return false;
|
||||
}
|
||||
if (type == param_type) {
|
||||
return true;
|
||||
}
|
||||
if (is_reference (type)) {
|
||||
// pass by references is a free conversion, but no promotion
|
||||
return dereference_type (type) == param_type;
|
||||
}
|
||||
if (is_reference (param_type)) {
|
||||
// dereferencing a reference is free so long as there's no
|
||||
// promotion, otherwise there's the promotion cost
|
||||
param_type = dereference_type (param_type);
|
||||
}
|
||||
if (type == param_type) {
|
||||
return true;
|
||||
}
|
||||
if (!promote || !type_promotes (type, param_type)) {
|
||||
return false;
|
||||
}
|
||||
*cost += 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
static genfunc_t *
|
||||
find_generic_function (genfunc_t **genfuncs, const expr_t *fexpr,
|
||||
const type_t *call_type, bool promote)
|
||||
|
@ -804,7 +662,6 @@ find_generic_function (genfunc_t **genfuncs, const expr_t *fexpr,
|
|||
static symbol_t *
|
||||
create_generic_sym (genfunc_t *g, const expr_t *fexpr, const type_t *call_type)
|
||||
{
|
||||
auto fsym = fexpr->symbol;
|
||||
int num_params = call_type->func.num_params;
|
||||
auto call_params = call_type->func.param_types;
|
||||
const type_t *types[g->num_types] = {};
|
||||
|
@ -857,6 +714,7 @@ create_generic_sym (genfunc_t *g, const expr_t *fexpr, const type_t *call_type)
|
|||
auto name = g->name;
|
||||
auto full_name = save_string (va (0, "%s|%s", name, encode_params (type)));
|
||||
|
||||
auto fsym = fexpr->symbol;
|
||||
auto sym = symtab_lookup (fsym->table, full_name);
|
||||
if (!sym || sym->table != fsym->table) {
|
||||
sym = new_symbol (full_name);
|
||||
|
@ -870,6 +728,167 @@ create_generic_sym (genfunc_t *g, const expr_t *fexpr, const type_t *call_type)
|
|||
return sym;
|
||||
}
|
||||
|
||||
static metafunc_t *
|
||||
get_function (const char *name, specifier_t spec)
|
||||
{
|
||||
if (!spec.sym->type || !spec.sym->type->encoding) {
|
||||
spec = default_type (spec, spec.sym);
|
||||
spec.sym->type = append_type (spec.sym->type, spec.type);
|
||||
set_func_attrs (spec.sym->type, spec.attributes);
|
||||
spec.sym->type = find_type (spec.sym->type);
|
||||
}
|
||||
auto type = unalias_type (spec.sym->type);
|
||||
|
||||
bool overload = spec.is_overload | current_language.always_override;
|
||||
metafunc_t *func = Hash_Find (function_map, name);
|
||||
if (func && func->meta_type == mf_generic) {
|
||||
auto genfuncs = (genfunc_t **) Hash_FindList (generic_functions, name);
|
||||
auto type = spec.sym->type;
|
||||
expr_t fexpr = {
|
||||
.loc = pr.loc,
|
||||
.type = ex_symbol,
|
||||
.symbol = symtab_lookup (current_symtab, name),
|
||||
};
|
||||
if (!fexpr.symbol || fexpr.symbol->sy_type != sy_func
|
||||
|| !fexpr.symbol->metafunc) {
|
||||
internal_error (0, "genfunc oops");
|
||||
}
|
||||
auto gen = find_generic_function (genfuncs, &fexpr, type, false);
|
||||
if (gen) {
|
||||
auto sym = create_generic_sym (gen, &fexpr, type);
|
||||
if (sym == fexpr.symbol
|
||||
|| sym->metafunc == fexpr.symbol->metafunc) {
|
||||
internal_error (0, "genfunc oops");
|
||||
}
|
||||
func = sym->metafunc;
|
||||
overload = true;
|
||||
} else {
|
||||
func = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const char *full_name;
|
||||
full_name = save_string (va (0, "%s|%s", name, encode_params (type)));
|
||||
|
||||
if (!func || func->meta_type != mf_generic) {
|
||||
// check if the exact function signature already exists, in which case
|
||||
// simply return it.
|
||||
func = Hash_Find (metafuncs, full_name);
|
||||
if (func) {
|
||||
if (func->type != type) {
|
||||
error (0, "can't overload on return types");
|
||||
return func;
|
||||
}
|
||||
return func;
|
||||
}
|
||||
|
||||
func = Hash_Find (function_map, name);
|
||||
if (func) {
|
||||
if (!overload && func->meta_type != mf_overload) {
|
||||
warning (0, "creating overloaded function %s without @overload",
|
||||
full_name);
|
||||
warning (&(expr_t) { .loc = func->loc },
|
||||
"(previous function is %s)", func->full_name);
|
||||
}
|
||||
overload = true;
|
||||
}
|
||||
|
||||
func = new_metafunc ();
|
||||
}
|
||||
*func = (metafunc_t) {
|
||||
.name = save_string (name),
|
||||
.full_name = full_name,
|
||||
.type = type,
|
||||
.loc = pr.loc,
|
||||
.meta_type = overload ? mf_overload : mf_simple,
|
||||
};
|
||||
|
||||
Hash_Add (metafuncs, func);
|
||||
Hash_Add (function_map, func);
|
||||
return func;
|
||||
}
|
||||
|
||||
symbol_t *
|
||||
function_symbol (specifier_t spec)
|
||||
{
|
||||
symbol_t *sym = spec.sym;
|
||||
const char *name = sym->name;
|
||||
metafunc_t *func = Hash_Find (function_map, name);
|
||||
|
||||
auto genfunc = parse_generic_function (name, spec);
|
||||
if (genfunc) {
|
||||
add_generic_function (genfunc);
|
||||
|
||||
ALLOC (1024, metafunc_t, metafuncs, func);
|
||||
*func = (metafunc_t) {
|
||||
.name = save_string (name),
|
||||
.full_name = name,
|
||||
.loc = pr.loc,
|
||||
.meta_type = mf_generic,
|
||||
};
|
||||
Hash_Add (metafuncs, func);
|
||||
Hash_Add (function_map, func);
|
||||
} else {
|
||||
func = get_function (name, spec);
|
||||
}
|
||||
|
||||
if (func && func->meta_type == mf_overload)
|
||||
name = func->full_name;
|
||||
symbol_t *s = symtab_lookup (current_symtab, name);
|
||||
if (!s || s->table != current_symtab) {
|
||||
s = new_symbol (name);
|
||||
s->sy_type = sy_func;
|
||||
s->type = unalias_type (sym->type);
|
||||
s->params = sym->params;
|
||||
s->metafunc = func;
|
||||
symtab_addsymbol (current_symtab, s);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// NOTE sorts the list in /reverse/ order
|
||||
static int
|
||||
func_compare (const void *a, const void *b)
|
||||
{
|
||||
metafunc_t *fa = *(metafunc_t **) a;
|
||||
metafunc_t *fb = *(metafunc_t **) b;
|
||||
const type_t *ta = fa->type;
|
||||
const type_t *tb = fb->type;
|
||||
int na = ta->func.num_params;
|
||||
int nb = tb->func.num_params;
|
||||
int ret, i;
|
||||
|
||||
if (na < 0)
|
||||
na = ~na;
|
||||
if (nb < 0)
|
||||
nb = ~nb;
|
||||
if (na != nb)
|
||||
return nb - na;
|
||||
if ((ret = (tb->func.num_params - ta->func.num_params)))
|
||||
return ret;
|
||||
for (i = 0; i < na && i < nb; i++) {
|
||||
auto diff = tb->func.param_types[i] - ta->func.param_types[i];
|
||||
if (diff) {
|
||||
return diff < 0 ? -1 : 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const expr_t *
|
||||
set_func_symbol (const expr_t *fexpr, metafunc_t *f)
|
||||
{
|
||||
auto sym = symtab_lookup (current_symtab, f->full_name);
|
||||
if (!sym) {
|
||||
internal_error (fexpr, "overloaded function %s not found",
|
||||
f->full_name);
|
||||
}
|
||||
auto nf = new_expr ();
|
||||
*nf = *fexpr;
|
||||
nf->symbol = sym;
|
||||
return nf;
|
||||
}
|
||||
|
||||
const expr_t *
|
||||
find_function (const expr_t *fexpr, const expr_t *params)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue