[qfcc] Parse generic function declarations

It doesn't properly differentiate between (treats genDType as being the
same as genFType):

    @generic(genFType=@vector(float)) genFType radians(genFType degrees);
    @generic(genDType=@vector(double)) genDType radians(genDType degrees);

but this is due to problems with how the type is built from
@vector(float) and @vector(double). However, I thought it was about time
I got some of this into git.

Also, `@generic(...) { ... };` blocks don't work properly (they lose the
generic info): need to get a little smarter about handling generic scope
in `external_def_list`.
This commit is contained in:
Bill Currie 2024-08-10 14:36:52 +09:00
parent 4c424fcb61
commit f1afa1caf0
2 changed files with 130 additions and 14 deletions

View file

@ -173,6 +173,8 @@ typedef struct expr_s expr_t;
typedef struct symbol_s symbol_t; typedef struct symbol_s symbol_t;
typedef struct symtab_s symtab_t; typedef struct symtab_s symtab_t;
void add_generic_function (genfunc_t *genfunc);
param_t *new_param (const char *selector, const type_t *type, const char *name); param_t *new_param (const char *selector, const type_t *type, const char *name);
param_t *new_generic_param (const expr_t *type_expr, const char *name); param_t *new_generic_param (const expr_t *type_expr, const char *name);
param_t *param_append_identifiers (param_t *params, struct symbol_s *idents, param_t *param_append_identifiers (param_t *params, struct symbol_s *idents,

View file

@ -69,6 +69,7 @@
ALLOC_STATE (param_t, params); ALLOC_STATE (param_t, params);
ALLOC_STATE (function_t, functions); ALLOC_STATE (function_t, functions);
ALLOC_STATE (genfunc_t, genfuncs); ALLOC_STATE (genfunc_t, genfuncs);
static hashtab_t *generic_functions;
static hashtab_t *overloaded_functions; static hashtab_t *overloaded_functions;
static hashtab_t *function_map; static hashtab_t *function_map;
@ -79,6 +80,13 @@ static hashtab_t *function_map;
// having to do shenanigans with mixed-alignment stack frames // having to do shenanigans with mixed-alignment stack frames
#define STACK_ALIGN 8 #define STACK_ALIGN 8
static const char *
gen_func_get_key (const void *_f, void *unused)
{
auto f = (genfunc_t *) _f;
return f->name;
}
static const char * static const char *
ol_func_get_key (const void *_f, void *unused) ol_func_get_key (const void *_f, void *unused)
{ {
@ -93,6 +101,95 @@ func_map_get_key (const void *_f, void *unused)
return f->name; return f->name;
} }
static void
check_generic_param (genparam_t *param, genfunc_t *genfunc)
{
if (param->gentype < 0 || param->gentype >= genfunc->num_types) {
internal_error (0, "invalid type index %s for %s",
param->name, genfunc->name);
}
}
static bool __attribute__((pure))
cmp_genparams (genfunc_t *g1, genparam_t *p1, genfunc_t *g2, genparam_t *p2)
{
if (p1->fixed_type || p2->fixed_type) {
return p1->fixed_type == p2->fixed_type;
}
// fixed_type for both p1 and p2 is null
auto t1 = g1->types[p1->gentype];
auto t2 = g2->types[p2->gentype];
if (t1.compute || t2.compute) {
// FIXME probably not right
return t1.compute == t2.compute;
}
auto vt1 = t1.valid_types;
auto vt2 = t2.valid_types;
for (; *vt1 && *vt2 && *vt1 == *vt2; vt1++, vt2++) continue;
return *vt1 == *vt2;
}
void
add_generic_function (genfunc_t *genfunc)
{
for (int i = 0; i < genfunc->num_types; i++) {
auto gentype = &genfunc->types[i];
if (gentype->compute && gentype->valid_types) {
internal_error (0, "both compute and valid_types set in "
"generic type");
}
for (auto type = gentype->valid_types; type && *type; type++) {
if (is_void (*type)) {
internal_error (0, "void in list of valid types");
}
}
}
int gen_params = 0;
for (int i = 0; i < genfunc->num_params; i++) {
auto param = &genfunc->params[i];
if (!param->fixed_type) {
gen_params++;
check_generic_param (param, genfunc);
}
}
if (!gen_params) {
internal_error (0, "%s has no generic parameters", genfunc->name);
}
if (!genfunc->ret_type) {
internal_error (0, "%s has no return type", genfunc->name);
}
check_generic_param (genfunc->ret_type, genfunc);
bool is_new = true;
genfunc_t *old = Hash_Find (generic_functions, genfunc->name);
if (old && old->num_params == genfunc->num_params) {
is_new = false;
for (int i = 0; i < genfunc->num_params; i++) {
if (!cmp_genparams (genfunc, &genfunc->params[i],
old, &old->params[i])) {
is_new = true;
break;
}
}
if (!is_new && !cmp_genparams (genfunc, genfunc->ret_type,
old, old->ret_type)) {
error (0, "can't overload on return types");
return;
}
}
if (is_new) {
Hash_Add (generic_functions, genfunc);
} else {
for (int i = 0; i < genfunc->num_types; i++) {
auto gentype = &genfunc->types[i];
free (gentype->valid_types);
}
free (genfunc->types);
FREE (genfuncs, genfunc);
}
}
static const type_t * static const type_t *
compute_type () compute_type ()
{ {
@ -418,15 +515,30 @@ static overloaded_function_t *
get_function (const char *name, const type_t *type, specifier_t spec) get_function (const char *name, const type_t *type, specifier_t spec)
{ {
auto genfunc = parse_generic_function (name, spec); auto genfunc = parse_generic_function (name, spec);
if (genfunc) {}
//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 (Hash_Find (function_map, name)) {
error (0, "can't mix generic and overload");
return nullptr;
}
add_generic_function (genfunc);
return nullptr;// FIXME
}
if (Hash_Find (generic_functions, name)) {
error (0, "can't mix generic and overload");
return nullptr;
}
bool overload = spec.is_overload; bool overload = spec.is_overload;
const char *full_name; const char *full_name;
overloaded_function_t *func;
name = save_string (name);
full_name = save_string (va (0, "%s|%s", name, encode_params (type))); full_name = save_string (va (0, "%s|%s", name, encode_params (type)));
overloaded_function_t *func;
// check if the exact function signature already exists, in which case
// simply return it.
func = Hash_Find (overloaded_functions, full_name); func = Hash_Find (overloaded_functions, full_name);
if (func) { if (func) {
if (func->type != type) { if (func->type != type) {
@ -439,22 +551,22 @@ get_function (const char *name, const type_t *type, specifier_t spec)
func = Hash_Find (function_map, name); func = Hash_Find (function_map, name);
if (func) { if (func) {
if (!overload && !func->overloaded) { if (!overload && !func->overloaded) {
expr_t e = {
.loc = func->loc,
};
warning (0, "creating overloaded function %s without @overload", warning (0, "creating overloaded function %s without @overload",
full_name); full_name);
warning (&e, "(previous function is %s)", func->full_name); warning (&(expr_t) { .loc = func->loc },
"(previous function is %s)", func->full_name);
} }
overload = true; overload = true;
} }
func = calloc (1, sizeof (overloaded_function_t)); func = malloc (sizeof (overloaded_function_t));
func->name = name; *func = (overloaded_function_t) {
func->full_name = full_name; .name = save_string (name),
func->type = type; .full_name = full_name,
func->overloaded = overload; .type = type,
func->loc = pr.loc; .overloaded = overload,
.loc = pr.loc,
};
Hash_Add (overloaded_functions, func); Hash_Add (overloaded_functions, func);
Hash_Add (function_map, func); Hash_Add (function_map, func);
@ -1099,9 +1211,11 @@ void
clear_functions (void) clear_functions (void)
{ {
if (overloaded_functions) { if (overloaded_functions) {
Hash_FlushTable (generic_functions);
Hash_FlushTable (overloaded_functions); Hash_FlushTable (overloaded_functions);
Hash_FlushTable (function_map); Hash_FlushTable (function_map);
} else { } else {
generic_functions = Hash_NewTable (1021, gen_func_get_key, 0, 0, 0);
overloaded_functions = Hash_NewTable (1021, ol_func_get_key, 0, 0, 0); overloaded_functions = Hash_NewTable (1021, ol_func_get_key, 0, 0, 0);
function_map = Hash_NewTable (1021, func_map_get_key, 0, 0, 0); function_map = Hash_NewTable (1021, func_map_get_key, 0, 0, 0);
} }