mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-02-25 13:11:00 +00:00
[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:
parent
4c424fcb61
commit
f1afa1caf0
2 changed files with 130 additions and 14 deletions
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue