From 99632ddb03310a12fba8a3088f1c45c2ed26dfd5 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Fri, 3 Jan 2025 02:54:30 +0900 Subject: [PATCH] [qfcc] Rework function/pointer/array declarations They should now work in generic contexts, but the pressing need to work on arrays was due to constant expressions for element counts breaking. As a side effect, function pointers are now a thing (and seem to work like they do in C) --- ruamoko/lib/Entity.r | 6 +- ruamoko/lib/Object.r | 2 +- tools/qfcc/include/expr.h | 3 + tools/qfcc/include/specifier.h | 2 +- tools/qfcc/include/symtab.h | 3 +- tools/qfcc/source/class.c | 17 +- tools/qfcc/source/dot_expr.c | 6 +- tools/qfcc/source/expr_call.c | 5 +- tools/qfcc/source/expr_cast.c | 6 +- tools/qfcc/source/expr_process.c | 144 ++++++++++++++- tools/qfcc/source/expr_type.c | 52 +++--- tools/qfcc/source/function.c | 9 +- tools/qfcc/source/glsl-declaration.c | 2 +- tools/qfcc/source/method.c | 2 +- tools/qfcc/source/qc-parse.y | 263 +++++++++++++-------------- tools/qfcc/source/qp-parse.y | 3 +- tools/qfcc/source/symtab.c | 14 +- tools/qfcc/source/type.c | 18 +- 18 files changed, 358 insertions(+), 199 deletions(-) diff --git a/ruamoko/lib/Entity.r b/ruamoko/lib/Entity.r index 1d24aed1a..56c205fc0 100644 --- a/ruamoko/lib/Entity.r +++ b/ruamoko/lib/Entity.r @@ -6,10 +6,10 @@ #include #include -typedef void () void_function; +typedef void (*void_function)(); int PR_SetField (entity ent, string field, string value) = #0; -@function(void) PR_FindFunction (string func) = #0; +void_function PR_FindFunction (string func) = #0; @static void ParseEntities (string ent_data); @@ -80,7 +80,7 @@ int PR_SetField (entity ent, string field, string value) = #0; local int count; local string field, value; local plitem_t *keys; - local @function(void) func; + local void_function func; local Entity *e; classname = PL_String (PL_ObjectForKey (dict, "classname")); diff --git a/ruamoko/lib/Object.r b/ruamoko/lib/Object.r index 83ed07755..0c21cc116 100644 --- a/ruamoko/lib/Object.r +++ b/ruamoko/lib/Object.r @@ -78,7 +78,7 @@ BOOL (id object) object_is_meta_class = #0; @end -typedef void arIMP(id, SEL, id); +typedef void (*arIMP)(id, SEL, id); @static BOOL allocDebug; @static Class autoreleaseClass; diff --git a/tools/qfcc/include/expr.h b/tools/qfcc/include/expr.h index 39ab1e1f9..a6f89588f 100644 --- a/tools/qfcc/include/expr.h +++ b/tools/qfcc/include/expr.h @@ -108,6 +108,7 @@ typedef struct element_chain_s { element_t *head; element_t **tail; const type_t *type; ///< inferred if null + const expr_t *type_expr; } element_chain_t; typedef struct ex_listitem_s { @@ -1045,6 +1046,7 @@ expr_t *new_memset_expr (const expr_t *dst, const expr_t *val, const expr_t *count); expr_t *new_type_expr (const type_t *type); +expr_t *new_type_function (int op, const expr_t *params); const expr_t *type_function (int op, const expr_t *params); symbol_t *type_parameter (symbol_t *sym, const expr_t *type); const type_t *resolve_type (const expr_t *te, rua_ctx_t *ctx); @@ -1156,6 +1158,7 @@ const expr_t *gather_factors (const type_t *type, int op, typedef struct rua_ctx_s rua_ctx_t; const expr_t *expr_process (const expr_t *expr, rua_ctx_t *ctx); +specifier_t spec_process (specifier_t spec, rua_ctx_t *ctx); bool can_inline (const expr_t *expr, symbol_t *fsym); ///@} diff --git a/tools/qfcc/include/specifier.h b/tools/qfcc/include/specifier.h index 95f99f047..4082eed80 100644 --- a/tools/qfcc/include/specifier.h +++ b/tools/qfcc/include/specifier.h @@ -61,6 +61,7 @@ extern const char *storage_class_names[sc_count]; typedef struct specifier_s { const type_t *type; const expr_t *type_expr; + expr_t *type_list; attribute_t *attributes; param_t *params; symbol_t *sym; @@ -80,7 +81,6 @@ typedef struct specifier_s { bool is_overload:1; 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; diff --git a/tools/qfcc/include/symtab.h b/tools/qfcc/include/symtab.h index 9d009cfce..5c7909911 100644 --- a/tools/qfcc/include/symtab.h +++ b/tools/qfcc/include/symtab.h @@ -285,7 +285,8 @@ symbol_t *make_symbol (const char *name, const struct type_s *type, struct specifier_s; symbol_t *declare_symbol (struct specifier_s spec, const expr_t *init, symtab_t *symtab, expr_t *block, rua_ctx_t *ctx); -symbol_t *declare_field (struct specifier_s spec, symtab_t *symtab); +symbol_t *declare_field (struct specifier_s spec, symtab_t *symtab, + rua_ctx_t *ctx); ///@} diff --git a/tools/qfcc/source/class.c b/tools/qfcc/source/class.c index 40035edc3..704efbdd8 100644 --- a/tools/qfcc/source/class.c +++ b/tools/qfcc/source/class.c @@ -86,9 +86,8 @@ type_t type_SEL = { }; const type_t *IMP_params[] = { &type_id, &type_SEL }; param_qual_t IMP_quals[] = { pq_in, pq_in }; -type_t type_IMP = { +type_t type_IMP_func = { .type = ev_func, - .name = "IMP", .alignment = 1, .width = 1, .columns = 1, @@ -101,6 +100,17 @@ type_t type_IMP = { .no_va_list = 1, }, }; +type_t type_IMP = { + .type = ev_ptr, + .name = "IMP", + .alignment = 1, + .width = 1, + .columns = 1, + .meta = ty_basic, + .fldptr = { + .type = &type_IMP_func, + }, +}; type_t type_super = { .type = ev_invalid, }; @@ -1983,6 +1993,7 @@ init_objective_structs (void) make_structure ("obj_selector", 's', sel_struct, &type_selector); chain_type (&type_selector); chain_type (&type_SEL); + chain_type (&type_IMP_func); chain_type (&type_IMP); make_structure ("obj_method", 's', method_struct, &type_method); @@ -2009,8 +2020,8 @@ init_objective_structs (void) static void init_classes (void) { - make_structure ("obj_class", 's', class_struct, &type_class); chain_type (&type_class); + make_structure ("obj_class", 's', class_struct, &type_class); chain_type (&type_Class); make_structure ("obj_object", 's', object_struct, &type_object); chain_type (&type_object); diff --git a/tools/qfcc/source/dot_expr.c b/tools/qfcc/source/dot_expr.c index 201922ea0..5bb7bdedb 100644 --- a/tools/qfcc/source/dot_expr.c +++ b/tools/qfcc/source/dot_expr.c @@ -106,6 +106,7 @@ get_op_string (int op) case QC_REVERSE: return "@reverse"; case QC_DUAL: return "@dual"; case QC_UNDUAL: return "@undual"; + case QC_AT_FUNCTION:return "@function"; case QC_AT_FIELD: return "@field"; case QC_AT_POINTER: return "@pointer"; case QC_AT_ARRAY: return "@array"; @@ -342,7 +343,10 @@ print_type_expr (dstring_t *dstr, const expr_t *e, int level, int id, dasprintf (dstr, "%*se_%p -> e_%p;\n", indent, "", e, e->typ.params); str = get_op_string (e->typ.op); } else if (e->typ.type) { - str = e->typ.type->encoding; + str = e->typ.type->encoding; + if (!str) { + str = type_get_encoding (e->typ.type); + } } else if (e->typ.sym) { str = e->typ.sym->name; } diff --git a/tools/qfcc/source/expr_call.c b/tools/qfcc/source/expr_call.c index c9fdf8193..39f1a9d3f 100644 --- a/tools/qfcc/source/expr_call.c +++ b/tools/qfcc/source/expr_call.c @@ -459,7 +459,10 @@ function_expr (const expr_t *fexpr, const expr_t *args) } auto ftype = get_type (fexpr); - if (ftype->type != ev_func) { + if (is_ptr (ftype) && is_func (dereference_type (ftype))) { + ftype = dereference_type (ftype); + } + if (!is_func (ftype)) { if (fexpr->type == ex_symbol) return error (fexpr, "Called object \"%s\" is not a function", fexpr->symbol->name); diff --git a/tools/qfcc/source/expr_cast.c b/tools/qfcc/source/expr_cast.c index 02a51f85f..ce19754c4 100644 --- a/tools/qfcc/source/expr_cast.c +++ b/tools/qfcc/source/expr_cast.c @@ -108,8 +108,12 @@ cast_expr (const type_t *dstType, const expr_t *e) } if ((dstType == type_default && is_enum (srcType)) - || (is_enum (dstType) && srcType == type_default)) + || (is_enum (dstType) && srcType == type_default)) { return e; + } + if ((is_pointer (dstType) && is_func (srcType))) { + return new_alias_expr (dstType, e); + } if ((is_pointer (dstType) && is_string (srcType)) || (is_string (dstType) && is_pointer (srcType))) { return new_alias_expr (dstType, e); diff --git a/tools/qfcc/source/expr_process.c b/tools/qfcc/source/expr_process.c index 2a145b614..2be78e295 100644 --- a/tools/qfcc/source/expr_process.c +++ b/tools/qfcc/source/expr_process.c @@ -50,14 +50,27 @@ typedef const expr_t *(*process_f) (const expr_t *expr, rua_ctx_t *ctx); +static const type_t * +proc_decl_type (const expr_t *decl, rua_ctx_t *ctx) +{ + if (decl->type != ex_decl) { + internal_error (decl, "not a decl expression"); + } + if (decl->decl.list.head) { + internal_error (decl, "non-empty cast decl"); + } + auto spec = spec_process (decl->decl.spec, ctx); + return spec.type; +} + static const expr_t * proc_expr (const expr_t *expr, rua_ctx_t *ctx) { scoped_src_loc (expr); if (expr->expr.op == 'C') { - auto type = resolve_type (expr->expr.e1, ctx); - expr = expr_process (expr->expr.e2, ctx); - return cast_expr (type, expr); + auto type = proc_decl_type (expr->expr.e1, ctx); + auto e = expr_process (expr->expr.e2, ctx); + return cast_expr (type, e); } auto e1 = expr_process (expr->expr.e1, ctx); auto e2 = expr_process (expr->expr.e2, ctx); @@ -447,6 +460,9 @@ proc_compound (const expr_t *expr, rua_ctx_t *ctx) { scoped_src_loc (expr); auto comp = new_compound_init (); + if (expr->compound.type_expr) { + comp->compound.type = proc_decl_type (expr->compound.type_expr, ctx); + } for (auto ele = expr->compound.head; ele; ele = ele->next) { append_element (comp, new_element (expr_process (ele->expr, ctx), ele->designator)); @@ -593,18 +609,132 @@ proc_cond (const expr_t *expr, rua_ctx_t *ctx) return new_cond_expr (test, true_expr, false_expr); } +static const type_t * +decl_function (const type_t *type, const expr_t *t, const symbol_t *sym, + rua_ctx_t *ctx) +{ + scoped_src_loc (t); + if (type && is_func (type)) { + error (t, "'%s' declared as a function returning a function", + sym->name); + } else if (type && is_array (type)) { + error (t, "'%s' declared as function returning an array", sym->name); + } + // FIXME not sure I like this setup for @function + auto params = t->typ.params; + auto ftype = params->typ.type; + if (type) { + ftype = append_type (ftype, type); + ftype = find_type (ftype); + } + return ftype; +} + +static const type_t * +decl_array (const type_t *type, const expr_t *t, const symbol_t *sym, + rua_ctx_t *ctx) +{ + scoped_src_loc (t); + if (type && is_func (type)) { + error (t, "declaration of '%s' as array of functions", sym->name); + } + auto params = new_list_expr (nullptr); + if (!proc_do_list (¶ms->list, &t->typ.params->list, ctx)) { + return type; + } + expr_prepend_expr (params, new_type_expr (type)); + auto type_expr = type_function (QC_AT_ARRAY, params); + return resolve_type (type_expr, ctx); +} + +static const type_t * +decl_pointer (const type_t *type, const expr_t *t, const symbol_t *sym, + rua_ctx_t *ctx) +{ + scoped_src_loc (t); + auto params = new_list_expr (new_type_expr (type)); + auto type_expr = type_function (QC_AT_POINTER, params); + return resolve_type (type_expr, ctx); +} + +static const type_t * +decl_field (const type_t *type, const expr_t *t, const symbol_t *sym, + rua_ctx_t *ctx) +{ + scoped_src_loc (t); + auto params = new_list_expr (new_type_expr (type)); + auto type_expr = type_function (QC_AT_FIELD, params); + return resolve_type (type_expr, ctx); +} + +specifier_t +spec_process (specifier_t spec, rua_ctx_t *ctx) +{ + if (spec.type && spec.type_expr) { + internal_error (0, "both type and type_expr set"); + } + if (!spec.type_expr) { + spec = default_type (spec, spec.sym); + } + if (!spec.type_list) { + return spec; + } + if (spec.type_list->type != ex_list) { + internal_error (spec.type_list, "not a list"); + } + int num_types = list_count (&spec.type_list->list); + const expr_t *type_list[num_types]; + auto type = spec.type; // core type (int etc) + if (spec.type_expr) { + type = resolve_type (spec.type_expr, ctx); + } + //const type_t *type = nullptr; + // other than fields, the type list is built up by appending types + // to the list, but it's the final type in the list that takes the + // core type, so extract them in reverse for a forward loop + list_scatter_rev (&spec.type_list->list, type_list); + for (int i = 0; i < num_types; i++) { + auto t = type_list[i]; + if (t->type != ex_type) { + internal_error (t, "not a type expr"); + } + switch (t->typ.op) { + case QC_AT_FUNCTION: + type = decl_function (type, t, spec.sym, ctx); + break; + case QC_AT_ARRAY: + type = decl_array (type, t, spec.sym, ctx); + break; + case QC_AT_POINTER: + type = decl_pointer (type, t, spec.sym, ctx); + break; + case QC_AT_FIELD: + type = decl_field (type, t, spec.sym, ctx); + break; + default: + internal_error (t, "unexpected type op: %d", t->typ.op); + } + } + spec.type_list = nullptr; + spec.type = type; + return spec; +} + static const expr_t * proc_decl (const expr_t *expr, rua_ctx_t *ctx) { scoped_src_loc (expr); expr_t *block = nullptr; auto decl_spec = expr->decl.spec; + if (decl_spec.type && decl_spec.type_expr) { + internal_error (0, "both type and type_expr set"); + } if (decl_spec.storage == sc_local) { scoped_src_loc (expr); block = new_block_expr (nullptr); } int count = list_count (&expr->decl.list); - const expr_t *decls[count + 1]; + const expr_t *decls[count]; list_scatter (&expr->decl.list, decls); for (int i = 0; i < count; i++) { auto decl = decls[i]; @@ -628,6 +758,12 @@ proc_decl (const expr_t *expr, rua_ctx_t *ctx) internal_error (decl, "not a symbol"); } auto spec = decl_spec; + spec.sym = sym; + if (spec.type_list) { + // to get here, a concrete declaration is being made + spec.is_generic = false; + spec = spec_process (spec, ctx); + } if (sym && !spec.type_expr) { spec.type = append_type (sym->type, spec.type); spec.type = find_type (spec.type); diff --git a/tools/qfcc/source/expr_type.c b/tools/qfcc/source/expr_type.c index bbc12234e..72e65b9da 100644 --- a/tools/qfcc/source/expr_type.c +++ b/tools/qfcc/source/expr_type.c @@ -161,6 +161,7 @@ single_type_opt_int_pair (int arg_count, const expr_t **args) static const expr_t * evaluate_int (const expr_t *expr, rua_ctx_t *ctx) { + expr = expr_process (expr, ctx); if (expr->type == ex_expr || expr->type == ex_uexpr) { auto e = new_expr (); *e = *expr; @@ -203,7 +204,7 @@ resolve_function (int arg_count, const expr_t **args, rua_ctx_t *ctx) return &type_func;//FIXME auto type = resolve_type (args[0], ctx); if (type) { - type = field_type (type); + type = field_type (type);//FIXME wrong type type = find_type (type); } return type; @@ -224,10 +225,7 @@ static const type_t * resolve_pointer (int arg_count, const expr_t **args, rua_ctx_t *ctx) { auto type = resolve_type (args[0], ctx); - if (type) { - type = pointer_type (type); - type = find_type (type); - } + type = pointer_type (type); return type; } @@ -235,17 +233,15 @@ static const type_t * resolve_array (int arg_count, const expr_t **args, rua_ctx_t *ctx) { auto type = resolve_type (args[0], ctx); - if (type) { - int count = 0; - if (arg_count > 1) { - auto count_expr = evaluate_int (args[1], ctx); - if (is_error (count_expr)) { - return nullptr; - } - count = expr_int (count_expr); + int count = 0; + if (arg_count > 1) { + auto count_expr = evaluate_int (args[1], ctx); + if (is_error (count_expr)) { + return nullptr; } - type = array_type (type, count); + count = expr_integral (count_expr); } + type = array_type (type, count); return type; } @@ -671,6 +667,19 @@ static type_func_t type_funcs[] = { }, }; +expr_t * +new_type_function (int op, const expr_t *params) +{ + auto te = new_expr (); + te->type = ex_type; + te->nodag = true; + te->typ = (ex_type_t) { + .op = op, + .params = params, + }; + return te; +} + const expr_t * type_function (int op, const expr_t *params) { @@ -686,14 +695,7 @@ type_function (int op, const expr_t *params) if (msg) { return error (params, "%s for %s", msg, type_funcs[ind].name); } - auto te = new_expr (); - te->type = ex_type; - te->nodag = true; - te->typ = (ex_type_t) { - .op = op, - .params = params, - }; - return te; + return new_type_function (op, params); } symbol_t * @@ -725,9 +727,9 @@ resolve_type (const expr_t *te, rua_ctx_t *ctx) internal_error (te, "not a type expression"); } if (!te->typ.op) { - if (!te->typ.type) { - internal_error (te, "no type in reference"); - } + //if (!te->typ.type) { + // internal_error (te, "no type in reference"); + //} return te->typ.type; } int op = te->typ.op; diff --git a/tools/qfcc/source/function.c b/tools/qfcc/source/function.c index 8745d6a3f..91f2d77bc 100644 --- a/tools/qfcc/source/function.c +++ b/tools/qfcc/source/function.c @@ -755,11 +755,11 @@ create_generic_sym (genfunc_t *g, const expr_t *fexpr, calltype_t *calltype, } static metafunc_t * -get_function (const char *name, specifier_t spec) +get_function (const char *name, specifier_t spec, rua_ctx_t *ctx) { if (!spec.sym->type || !spec.sym->type->encoding) { - spec = default_type (spec, spec.sym); - spec.sym->type = append_type (spec.sym->type, spec.type); + spec = spec_process (spec, ctx); + spec.sym->type = spec.type; set_func_attrs (spec.sym->type, spec.attributes); spec.sym->type = find_type (spec.sym->type); } @@ -850,6 +850,7 @@ symbol_t * function_symbol (specifier_t spec, rua_ctx_t *ctx) { symbol_t *sym = spec.sym; + sym->params = spec.params; const char *name = sym->name; metafunc_t *func = Hash_Find (function_map, name); @@ -879,7 +880,7 @@ function_symbol (specifier_t spec, rua_ctx_t *ctx) Hash_Add (metafuncs, func); Hash_Add (function_map, func); } else { - func = get_function (name, spec); + func = get_function (name, spec, ctx); } if (func && func->meta_type == mf_overload) diff --git a/tools/qfcc/source/glsl-declaration.c b/tools/qfcc/source/glsl-declaration.c index c5a724a4f..d256f1792 100644 --- a/tools/qfcc/source/glsl-declaration.c +++ b/tools/qfcc/source/glsl-declaration.c @@ -108,6 +108,6 @@ void glsl_declare_field (specifier_t spec, symtab_t *symtab, rua_ctx_t *ctx) { auto attributes = glsl_optimize_attributes (spec.attributes); - spec.sym = declare_field (spec, symtab); + spec.sym = declare_field (spec, symtab, ctx); glsl_apply_attributes (attributes, spec); } diff --git a/tools/qfcc/source/method.c b/tools/qfcc/source/method.c index 8fd2ab04e..5a1c5bb0c 100644 --- a/tools/qfcc/source/method.c +++ b/tools/qfcc/source/method.c @@ -359,7 +359,7 @@ send_message (int super, rua_ctx_t *ctx) { symbol_t *sym; const char *sm_name = "obj_msgSend"; - type_t *sm_type = &type_IMP; + const type_t *sm_type = type_IMP.fldptr.type; if (super) { sm_name = "obj_msgSend_super"; diff --git a/tools/qfcc/source/qc-parse.y b/tools/qfcc/source/qc-parse.y index edca24968..c2853908c 100644 --- a/tools/qfcc/source/qc-parse.y +++ b/tools/qfcc/source/qc-parse.y @@ -199,7 +199,7 @@ int yylex (YYSTYPE *yylval, YYLTYPE *yylloc); %type enum_specifier algebra_specifier %type optional_enum_list enum_list enumerator_list enumerator %type enum_init -%type array_decl +%type array_decl %type const string @@ -213,7 +213,7 @@ int yylex (YYSTYPE *yylval, YYLTYPE *yylloc); %type opt_init_semi opt_expr comma_expr %type expr %type compound_init -%type opt_cast +%type opt_cast %type element_list expr_list %type designator designator_spec %type element @@ -353,6 +353,7 @@ spec_merge (specifier_t spec, specifier_t new) spec.multi_type = true; } } + spec.type_list = new.type_list; if (new.is_typedef || !storage_auto (new)) { if ((spec.is_typedef || !storage_auto (spec)) && !spec.multi_store) { error (0, "multiple storage classes in declaration specifiers"); @@ -391,50 +392,40 @@ spec_merge (specifier_t spec, specifier_t new) return spec; } -static const type_t * +static specifier_t resolve_type_spec (specifier_t spec, rua_ctx_t *ctx) { + spec = spec_process (spec, ctx); auto type = spec.type; if (spec.type_expr) { type = resolve_type (spec.type_expr, ctx); + spec.type_expr = nullptr; } - return find_type (type); -} - -static specifier_t -typename_spec (specifier_t spec) -{ - spec = default_type (spec, 0); - spec.sym->type = find_type (append_type (spec.sym->type, spec.type)); - spec.type = spec.sym->type; + spec.type = find_type (type); return spec; } static specifier_t -function_spec (specifier_t spec, param_t *params) +typename_spec (specifier_t spec, rua_ctx_t *ctx) { - // empty param list in an abstract decl does not create a symbol - if (!spec.sym) { - spec.sym = new_symbol (0); + spec = default_type (spec, 0); + return spec; +} + +static specifier_t +function_spec (specifier_t spec, param_t *parameters) +{ + // return type will be filled in when building the final type + // FIXME not sure I like this setup for @function + auto params = new_type_expr (parse_params (0, parameters)); + auto type_expr = new_type_function (QC_AT_FUNCTION, params); + if (spec.type_list) { + expr_append_expr (spec.type_list, type_expr); + } else { + spec.type_list = new_list_expr (type_expr); } - if (!spec.type_expr) { - spec = default_type (spec, spec.sym); - } - spec.sym->params = params; - if (spec.sym->type) { - if (is_func (spec.sym->type)) { - error (0, "'%s' declared as a function returning a function", - spec.sym->name); - } else if (is_array (spec.sym->type)) { - error (0, "declaration of '%s' as array of functions", - spec.sym->name); - } else if (!is_pointer (spec.sym->type) - && !is_field (spec.sym->type)) { - internal_error (0, "unexpected type"); - } - } - spec.is_function = !spec.sym->type; //FIXME do proper void(*)() -> ev_func - spec.sym->type = append_type (spec.sym->type, parse_params (0, params)); + + spec.params = parameters; spec.is_generic = generic_scope; spec.is_generic_block = generic_block; spec.symtab = generic_symtab; @@ -442,41 +433,50 @@ function_spec (specifier_t spec, param_t *params) } static specifier_t -array_spec (specifier_t spec, unsigned size) +array_spec (specifier_t spec, const expr_t *size) { - spec = default_type (spec, spec.sym); - if (spec.sym->type) { - if (is_func (spec.sym->type)) { - error (0, "'%s' declared as function returning an array", - spec.sym->name); - } else if (!is_pointer (spec.sym->type) - && !is_array (spec.sym->type) - && !is_field (spec.sym->type)) { - internal_error (0, "unexpected type"); - } + // element type will be filled in when building the final type + auto params = new_list_expr (size); + auto type_expr = new_type_function (QC_AT_ARRAY, params); + if (spec.type_list) { + expr_append_expr (spec.type_list, type_expr); + } else { + spec.type_list = new_list_expr (type_expr); } - spec.sym->type = append_type (spec.sym->type, array_type (0, size)); return spec; } static specifier_t pointer_spec (specifier_t quals, specifier_t spec) { - if (spec.sym->type) { - if (!is_func (spec.sym->type) - && !is_pointer (spec.sym->type) - && !is_array (spec.sym->type) - && !is_field (spec.sym->type)) { - internal_error (0, "unexpected type"); - } + // referenced type will be filled in when building the final type + auto type_expr = new_type_function (QC_AT_POINTER, nullptr); + if (spec.type_list) { + expr_append_expr (spec.type_list, type_expr); + } else { + spec.type_list = new_list_expr (type_expr); + } + return spec; +} + +static specifier_t +field_spec (specifier_t spec) +{ + // referenced type will be filled in when building the final type + auto type_expr = new_type_function (QC_AT_FIELD, nullptr); + if (spec.type_list) { + expr_prepend_expr (spec.type_list, type_expr); + } else { + spec.type_list = new_list_expr (type_expr); } - spec.sym->type = append_type (spec.sym->type, pointer_type (0)); return spec; } static specifier_t qc_function_spec (specifier_t spec, param_t *params) { +#if 0 + //FIXME this is borked // .float () foo; is a field holding a function variable rather // than a function that returns a float field. // FIXME I think this breaks fields holding functions that return fields @@ -494,19 +494,17 @@ qc_function_spec (specifier_t spec, param_t *params) spec.sym = new_symbol (0); spec.sym->type = field_chain; spec.type = ret_type; +#endif spec = function_spec (spec, params); return spec; } static specifier_t -qc_set_symbol (specifier_t spec, symbol_t *sym) +qc_set_symbol (specifier_t spec, symbol_t *sym, rua_ctx_t *ctx) { - // qc-style function declarations don't know the symbol name until the - // declaration is fully parsed, so spec.sym's name is null but its type - // carries any extra type information (field, pointer, array) - sym->params = spec.sym->params; - sym->type = spec.sym->type; + spec = spec_process (spec, ctx); + sym->type = spec.type; spec.sym = sym; return spec; } @@ -518,20 +516,25 @@ make_ellipsis (void) } static param_t * -make_param (specifier_t spec) +make_param (specifier_t spec, rua_ctx_t *ctx) { //FIXME should not be sc_global if (spec.storage == sc_global) { spec.storage = sc_param; } + spec = spec_process (spec, ctx); + param_t *param; if (spec.type_expr) { param = new_generic_param (spec.type_expr, spec.sym->name); - } else { + } else if (spec.sym) { spec = default_type (spec, spec.sym); spec.type = find_type (append_type (spec.sym->type, spec.type)); - param = new_param (0, spec.type, spec.sym->name); + param = new_param (nullptr, spec.type, spec.sym->name); + } else { + spec = default_type (spec, spec.sym); + param = new_param (nullptr, spec.type, nullptr); } if (spec.is_const) { if (spec.storage == sc_out) { @@ -569,19 +572,20 @@ make_selector (const char *selector, const type_t *type, const char *name) } static param_t * -make_qc_param (specifier_t spec, symbol_t *sym) +make_qc_param (specifier_t spec, symbol_t *sym, rua_ctx_t *ctx) { sym->type = nullptr; spec.sym = sym; - return make_param (spec); + return make_param (spec, ctx); } static param_t * -make_qc_func_param (specifier_t spec, param_t *params, symbol_t *sym) +make_qc_func_param (specifier_t spec, param_t *params, symbol_t *sym, + rua_ctx_t *ctx) { spec = qc_function_spec (spec, params); - sym->type = append_type (spec.sym->type, spec.type); - param_t *param = new_param (0, sym->type, sym->name); + spec = spec_process (spec, ctx); + param_t *param = new_param (0, spec.type, sym->name); return param; } @@ -795,11 +799,11 @@ qc_param_list qc_first_param : typespec identifier { - $$ = make_qc_param ($1, $2); + $$ = make_qc_param ($1, $2, ctx); } | typespec_reserved qc_func_params identifier { - $$ = make_qc_func_param ($1, $2, $3); + $$ = make_qc_func_param ($1, $2, $3, ctx); } | ELLIPSIS { @@ -813,11 +817,11 @@ qc_first_param qc_param : typespec identifier { - $$ = make_qc_param ($1, $2); + $$ = make_qc_param ($1, $2, ctx); } | typespec qc_func_params identifier { - $$ = make_qc_func_param ($1, $2, $3); + $$ = make_qc_func_param ($1, $2, $3, ctx); } | ELLIPSIS { @@ -859,7 +863,7 @@ qc_func_decl qc_nocode_func : identifier '=' '#' expr { - specifier_t spec = qc_set_symbol ($0, $1); + specifier_t spec = qc_set_symbol ($0, $1, ctx); const expr_t *bi_val = expr_process ($4, ctx); spec.is_overload |= ctx->language->always_overload; @@ -868,19 +872,19 @@ qc_nocode_func } | identifier '=' intrinsic { - specifier_t spec = qc_set_symbol ($0, $1); + specifier_t spec = qc_set_symbol ($0, $1, ctx); build_intrinsic_function (spec, $3, ctx); } | identifier '=' expr { - specifier_t spec = qc_set_symbol ($0, $1); + specifier_t spec = qc_set_symbol ($0, $1, ctx); const expr_t *expr = $3; declare_symbol (spec, expr, current_symtab, local_expr, ctx); } | identifier { - specifier_t spec = qc_set_symbol ($0, $1); + specifier_t spec = qc_set_symbol ($0, $1, ctx); declare_symbol (spec, nullptr, current_symtab, local_expr, ctx); } ; @@ -889,7 +893,7 @@ qc_code_func : identifier '=' optional_state_expr save_storage { - specifier_t spec = qc_set_symbol ($0, $1); + specifier_t spec = qc_set_symbol ($0, $1, ctx); auto fs = (funcstate_t) { .function = current_func, }; @@ -971,8 +975,9 @@ notype_declarator } | NAME { - $$ = $0; - $$.sym = new_symbol ($1->name); + auto spec = $0; + spec.sym = new_symbol ($1->name); + $$ = spec; } | NOT { @@ -1134,9 +1139,7 @@ typespec_reserved // for basic types, but functions need special treatment | '.' typespec_reserved { - // avoid find_type() - $$ = type_spec (field_type (0)); - $$.type = append_type ($$.type, $2.type); + $$ = field_spec ($2); } ; @@ -1178,9 +1181,7 @@ typespec_nonreserved // for basic types, but functions need special treatment | '.' typespec_nonreserved { - // avoid find_type() - $$ = type_spec (field_type (0)); - $$.type = append_type ($$.type, $2.type); + $$ = field_spec ($2); } ; @@ -1202,10 +1203,7 @@ save_storage function_body : method_optional_state_expr[state] { - specifier_t spec = $0; - if (!spec.is_generic) { - spec = default_type (spec, spec.sym); - } + specifier_t spec = spec_process ($0, ctx); spec.is_overload |= ctx->language->always_overload; spec.sym = function_symbol (spec, ctx); $$ = spec; @@ -1231,7 +1229,7 @@ function_body } | '=' '#' expr ';' { - specifier_t spec = $0; + specifier_t spec = spec_process ($0, ctx); const expr_t *bi_val = expr_process ($3, ctx); spec.is_overload |= ctx->language->always_overload; @@ -1240,7 +1238,7 @@ function_body } | '=' intrinsic { - specifier_t spec = $0; + specifier_t spec = spec_process ($0, ctx); build_intrinsic_function (spec, $2, ctx); } ; @@ -1300,7 +1298,7 @@ generic_type ; type_function - : type_func '(' type_param_list ')' { $$ = type_function ($1, $3); } + : type_func '(' type_param_list ')' { $$ = new_type_function ($1, $3); } ; type_func @@ -1579,7 +1577,7 @@ components component_declarator : declarator { - declare_field ($1, current_symtab); + declare_field ($1, current_symtab, ctx); } | declarator ':' expr | ':' expr @@ -1630,23 +1628,23 @@ parameter_list parameter : declspecs_ts param_declarator { - $$ = make_param ($2); + $$ = make_param ($2, ctx); } | declspecs_ts notype_declarator { - $$ = make_param ($2); + $$ = make_param ($2, ctx); } | declspecs_ts absdecl { - $$ = make_param ($2); + $$ = make_param ($2, ctx); } | declspecs_nosc_nots notype_declarator { - $$ = make_param ($2); + $$ = make_param ($2, ctx); } | declspecs_nosc_nots absdecl { - $$ = make_param ($2); + $$ = make_param ($2, ctx); } ; @@ -1654,7 +1652,6 @@ absdecl : /* empty */ { $$ = $0; - $$.sym = new_symbol (0); } | absdecl1 ; @@ -1716,24 +1713,11 @@ param_declarator_nostarttypename ; typename - : declspecs_nosc absdecl - { - $$ = typename_spec ($2); - } + : declspecs_nosc absdecl { $$ = typename_spec ($2, ctx); } ; array_decl - : '[' expr ']' - { - if (is_int_val ($2) && expr_int ($2) > 0) { - $$ = expr_int ($2); - } else if (is_uint_val ($2) && expr_uint ($2) > 0) { - $$ = expr_uint ($2); - } else { - error (0, "invalid array size"); - $$ = 0; - } - } + : '[' expr ']' { $$ = $expr; } | '[' ']' { $$ = 0; } ; @@ -1780,16 +1764,16 @@ var_initializer compound_init : opt_cast '{' element_list optional_comma '}' { - auto type = resolve_type_spec ($1, ctx); - $3->compound.type = type; + auto cast = $1; + $3->compound.type_expr = cast; $$ = $3; } | opt_cast '{' '}' { - auto type = resolve_type_spec ($1, ctx); - if (type) { + auto cast = $1; + if (cast) { auto elements = new_compound_init (); - elements->compound.type = type; + elements->compound.type_expr = cast; $$ = elements; } else { $$ = nullptr; @@ -1798,8 +1782,8 @@ compound_init ; opt_cast - : '(' typename ')' { $$ = $2; } - | /*empty*/ { $$ = (specifier_t) {}; } + : '(' typename ')' { $$ = new_decl_expr ($2, nullptr); } + | /*empty*/ { $$ = nullptr; } ; method_optional_state_expr @@ -2156,11 +2140,8 @@ cast_expr : '(' typename ')' cast_expr { auto spec = $2; - auto type_expr = spec.type_expr; - if (!type_expr) { - type_expr = new_type_expr (spec.type); - } - $$ = new_binary_expr ('C', type_expr, $4); + auto decl = new_decl_expr (spec, nullptr); + $$ = new_binary_expr ('C', decl, $4); } | CONSTRUCT '(' typename ',' expr_list[args] ')' //FIXME arg_expr instead? { @@ -2635,7 +2616,7 @@ notype_ivars ivar_declarator : declarator { - declare_field ($1, current_symtab); + declare_field ($1, current_symtab, ctx); } | declarator ':' expr | ':' expr @@ -2747,15 +2728,15 @@ methodproto methoddecl : '(' typename ')' unaryselector { - auto type = resolve_type_spec ($2, ctx); - $$ = new_method (type, $4, 0); + auto spec = resolve_type_spec ($2, ctx); + $$ = new_method (spec.type, $4, 0); } | unaryselector { $$ = new_method (&type_id, $1, 0); } | '(' typename ')' keywordselector optional_param_list { - auto type = resolve_type_spec ($2, ctx); - $$ = new_method (type, $4, $5); + auto spec = resolve_type_spec ($2, ctx); + $$ = new_method (spec.type, $4, $5); } | keywordselector optional_param_list { $$ = new_method (&type_id, $1, $2); } @@ -2810,18 +2791,22 @@ reserved_word keyworddecl : selector ':' '(' typename ')' identifier { - auto type = resolve_type_spec ($4, ctx); - $$ = make_selector ($1->name, type, $6->name); + auto spec = resolve_type_spec ($4, ctx); + $$ = make_selector ($1->name, spec.type, $6->name); } | selector ':' identifier - { $$ = make_selector ($1->name, &type_id, $3->name); } + { + $$ = make_selector ($1->name, &type_id, $3->name); + } | ':' '(' typename ')' identifier { - auto type = resolve_type_spec ($3, ctx); - $$ = make_selector ("", type, $5->name); + auto spec = resolve_type_spec ($3, ctx); + $$ = make_selector ("", spec.type, $5->name); } | ':' identifier - { $$ = make_selector ("", &type_id, $2->name); } + { + $$ = make_selector ("", &type_id, $2->name); + } ; obj_expr @@ -2830,8 +2815,8 @@ obj_expr | PROTOCOL '(' identifier ')' { $$ = protocol_expr ($3->name); } | ENCODE '(' typename ')' { - auto type = resolve_type_spec ($3, ctx); - $$ = encode_expr (type); + auto spec = resolve_type_spec ($3, ctx); + $$ = encode_expr (spec.type); } | obj_string /* FIXME string object? */ ; diff --git a/tools/qfcc/source/qp-parse.y b/tools/qfcc/source/qp-parse.y index 1f3629cd9..f476cc5e3 100644 --- a/tools/qfcc/source/qp-parse.y +++ b/tools/qfcc/source/qp-parse.y @@ -214,10 +214,9 @@ function_decl (symbol_t *sym, param_t *params, const type_t *ret_type, // use `@name` so `main` can be used (`.main` is reserved for the entry // point) auto fsym = new_symbol (va (0, "@%s", sym->name)); - fsym->params = params; fsym->type = parse_params (ret_type, params); fsym->type = find_type (fsym->type); - fsym = function_symbol ((specifier_t) { .sym = fsym, }, ctx); + fsym = function_symbol ((specifier_t) {.sym = fsym, .params = params}, ctx); auto fsym_expr = new_symbol_expr (fsym); if (!params) { fsym_expr = new_call_expr (fsym_expr, nullptr, nullptr); diff --git a/tools/qfcc/source/symtab.c b/tools/qfcc/source/symtab.c index b9ebb5047..ad8894b1f 100644 --- a/tools/qfcc/source/symtab.c +++ b/tools/qfcc/source/symtab.c @@ -291,16 +291,12 @@ declare_symbol (specifier_t spec, const expr_t *init, symtab_t *symtab, sym->table = nullptr; } - if (!spec.type_expr && !spec.is_function) { - spec = default_type (spec, sym); - } + spec = spec_process (spec, ctx); if (!spec.storage) { spec.storage = current_storage; } - if (spec.type && (spec.is_typedef || !sym->type || !is_func (sym->type))) { - sym->type = append_type (spec.sym->type, spec.type); - } + sym->type = spec.type; if (spec.is_typedef) { if (init) { @@ -311,7 +307,7 @@ declare_symbol (specifier_t spec, const expr_t *init, symtab_t *symtab, sym->type = find_type (alias_type (sym->type, sym->type, sym->name)); symtab_addsymbol (symtab, sym); } else { - if (spec.is_function && is_func (sym->type)) { + if (is_func (sym->type)) { if (init) { error (0, "function %s is initialized", sym->name); } @@ -328,10 +324,10 @@ declare_symbol (specifier_t spec, const expr_t *init, symtab_t *symtab, } symbol_t * -declare_field (specifier_t spec, symtab_t *symtab) +declare_field (specifier_t spec, symtab_t *symtab, rua_ctx_t *ctx) { symbol_t *s = spec.sym; - spec = default_type (spec, s); + spec = spec_process (spec, ctx); s->type = find_type (append_type (s->type, spec.type)); s->sy_type = sy_offset; s->visibility = current_visibility; diff --git a/tools/qfcc/source/type.c b/tools/qfcc/source/type.c index 31af66395..78ce69bda 100644 --- a/tools/qfcc/source/type.c +++ b/tools/qfcc/source/type.c @@ -202,6 +202,14 @@ type_t type_param = { .type = ev_invalid, .meta = ty_struct, }; +static type_t type_param_pointer = { + .type = ev_ptr, + .alignment = 1, + .width = 1, + .columns = 1, + .meta = ty_basic, + .fldptr.type = &type_param, +}; type_t type_zero = { .type = ev_invalid, .meta = ty_struct, @@ -222,7 +230,7 @@ type_t type_xdef_pointer = { .width = 1, .columns = 1, .meta = ty_basic, - {{&type_xdef}}, + .fldptr.type = &type_xdef, }; type_t type_xdefs = { .type = ev_invalid, @@ -1668,6 +1676,10 @@ type_assignable (const type_t *dst, const type_t *src) // any field = any field if (dst->type == ev_field && src->type == ev_field) return true; + if (is_pointer (dst) && is_func (src)) { + auto type = dereference_type (dst); + return type == src; + } // pointer = array if (is_pointer (dst) && is_array (src)) { if (is_void (dst->fldptr.type) @@ -2063,6 +2075,7 @@ static void chain_structural_types (void) { chain_type (&type_param); + chain_type (&type_param_pointer); chain_type (&type_zero); chain_type (&type_type_encodings); chain_type (&type_xdef); @@ -2203,6 +2216,7 @@ init_types (void) make_structure ("@zero", 'u', zero_struct, &type_zero); make_structure ("@param", 'u', param_struct, &type_param); + build_vector_struct (&type_vector, false); make_structure ("@type_encodings", 's', type_encoding_struct, @@ -2210,7 +2224,7 @@ init_types (void) make_structure ("@xdef", 's', xdef_struct, &type_xdef); make_structure ("@xdefs", 's', xdefs_struct, &type_xdefs); - va_list_struct[1].type = pointer_type (&type_param); + va_list_struct[1].type = &type_param_pointer; make_structure ("@va_list", 's', va_list_struct, &type_va_list); build_vector_struct (&type_quaternion, false);