diff --git a/tools/qfcc/include/function.h b/tools/qfcc/include/function.h index 814fcf5b4..3c9750308 100644 --- a/tools/qfcc/include/function.h +++ b/tools/qfcc/include/function.h @@ -136,16 +136,18 @@ extern function_t *current_func; typedef struct param_s { struct param_s *next; const char *selector; - const struct type_s *type; + const type_t *type; + const expr_t *type_expr; const char *name; } param_t; -struct expr_s; -struct symbol_s; -struct symtab_s; +typedef struct expr_s expr_t; +typedef struct symbol_s symbol_t; +typedef struct symtab_s symtab_t; param_t *new_param (const char *selector, const struct type_s *type, 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, const struct type_s *type); param_t *reverse_params (param_t *params); diff --git a/tools/qfcc/source/function.c b/tools/qfcc/source/function.c index 76d61c8d9..a594dd3a8 100644 --- a/tools/qfcc/source/function.c +++ b/tools/qfcc/source/function.c @@ -98,11 +98,24 @@ new_param (const char *selector, const type_t *type, const char *name) param_t *param; ALLOC (4096, param_t, params, param); - param->next = 0; - param->selector = selector; - param->type = find_type (type); - param->name = name; + *param = (param_t) { + .selector = selector, + .type = find_type (type), + .name = name, + }; + return param; +} +param_t * +new_generic_param (const expr_t *type_expr, const char *name) +{ + param_t *param; + + ALLOC (4096, param_t, params, param); + *param = (param_t) { + .type_expr = type_expr, + .name = name, + }; return param; } diff --git a/tools/qfcc/source/qc-parse.y b/tools/qfcc/source/qc-parse.y index 0364d3256..a1ddd59d7 100644 --- a/tools/qfcc/source/qc-parse.y +++ b/tools/qfcc/source/qc-parse.y @@ -285,15 +285,22 @@ storage_auto (specifier_t spec) return spec.storage == sc_global || spec.storage == sc_local; } +static bool +spec_type (specifier_t spec) +{ + return spec.type || spec.type_expr; +} + static specifier_t spec_merge (specifier_t spec, specifier_t new) { - if (new.type) { + if (spec_type (new)) { // deal with "type " - if (!spec.type || new.sym) { + if (!spec_type (spec) || new.sym) { spec.sym = new.sym; - if (!spec.type) { + if (!spec_type (spec)) { spec.type = new.type; + spec.type_expr = new.type_expr; } } else if (!spec.multi_type) { error (0, "two or more data types in declaration specifiers"); @@ -419,13 +426,14 @@ static param_t * make_param (specifier_t spec) { if (spec.type_expr) { - if (!(spec.type = resolve_type (spec.type_expr))) { - } + auto param = new_generic_param (spec.type_expr, spec.sym->name); + return param; + } else { + spec = default_type (spec, spec.sym); + spec.type = find_type (append_type (spec.sym->type, spec.type)); + auto param = new_param (0, spec.type, spec.sym->name); + return param; } - spec = default_type (spec, spec.sym); - spec.type = find_type (append_type (spec.sym->type, spec.type)); - param_t *param = new_param (0, spec.type, spec.sym->name); - return param; } static param_t * @@ -922,8 +930,14 @@ typespec_reserved : TYPE_SPEC | type_function { - auto type = resolve_type ($1); - $$ = make_spec (type, 0, 0, 0); + if (generic_scope) { + $$ = (specifier_t) { + .type_expr = $1, + }; + } else { + auto type = resolve_type ($1); + $$ = make_spec (type, 0, 0, 0); + } } | algebra_specifier %prec LOW | algebra_specifier '.' attribute