diff --git a/tools/qfcc/include/algebra.h b/tools/qfcc/include/algebra.h index 884956a5d..7b4562c5c 100644 --- a/tools/qfcc/include/algebra.h +++ b/tools/qfcc/include/algebra.h @@ -86,10 +86,10 @@ typedef struct multivector_s { struct symbol_s *mvec_sym; ///< null if single group } multivector_t; -struct attribute_s; +typedef struct attribute_s attribute_t; bool is_algebra (const type_t *type) __attribute__((pure)); const type_t *algebra_type (const type_t *type, const expr_t *params); -const type_t *algebra_subtype (const type_t *type, struct attribute_s *attr); +const type_t *algebra_subtype (const type_t *type, const attribute_t *attr); const type_t *algebra_mvec_type (algebra_t *algebra, pr_uint_t group_mask); int algebra_count_flips (const algebra_t *alg, pr_uint_t a, pr_uint_t b) __attribute__((pure)); struct ex_value_s *algebra_blade_value (algebra_t *alg, const char *name); diff --git a/tools/qfcc/include/evaluate_type.h b/tools/qfcc/include/evaluate_type.h index 8ec4200a0..cdf51fb94 100644 --- a/tools/qfcc/include/evaluate_type.h +++ b/tools/qfcc/include/evaluate_type.h @@ -36,6 +36,7 @@ enum { tf_null, tf_eval, + tf_attribute, tf_function, tf_field, tf_pointer, @@ -59,8 +60,10 @@ typedef struct typeeval_s { struct typeeval_s *next; struct dstatement_s *code; struct pr_type_s *data; + const char *strings; int code_size; int data_size; + int string_size; } typeeval_t; void setup_type_progs (void); diff --git a/tools/qfcc/include/expr.h b/tools/qfcc/include/expr.h index dd39cd3e9..4e3702916 100644 --- a/tools/qfcc/include/expr.h +++ b/tools/qfcc/include/expr.h @@ -331,6 +331,7 @@ typedef struct { typedef struct { int op; ///< type "function" const expr_t *params; ///< if dynamic + const attribute_t *attrib; const type_t *type; const symbol_t *sym; } ex_type_t; diff --git a/tools/qfcc/include/type.h b/tools/qfcc/include/type.h index f7c5a0302..01b0f6d64 100644 --- a/tools/qfcc/include/type.h +++ b/tools/qfcc/include/type.h @@ -103,6 +103,7 @@ typedef struct type_s { int allocated; struct protocollist_s *protos; const char *encoding; ///< Objective-QC encoding + const expr_t *(*attrib) (const type_t *type, const attribute_t *attr); } type_t; #define EV_TYPE(type) extern type_t type_##type; diff --git a/tools/qfcc/source/algebra.c b/tools/qfcc/source/algebra.c index 1b4055020..782ab95cc 100644 --- a/tools/qfcc/source/algebra.c +++ b/tools/qfcc/source/algebra.c @@ -342,6 +342,13 @@ is_algebra (const type_t *type) return type->meta == ty_algebra; } +static const expr_t * +algebra_attrib (const type_t *type, const attribute_t *attr) +{ + type = algebra_subtype (type, attr); + return new_type_expr (type); +} + const type_t * algebra_type (const type_t *type, const expr_t *params) { @@ -412,12 +419,13 @@ algebra_type (const type_t *type, const expr_t *params) t->type = ev_invalid; t->alignment = (dim > 1 ? 4 : 2) * type->alignment; t->algebra = algebra; + t->attrib = algebra_attrib; algebra->algebra_type = t; return find_type (t); } const type_t * -algebra_subtype (const type_t *type, attribute_t *attr) +algebra_subtype (const type_t *type, const attribute_t *attr) { if (!is_algebra (type)) { internal_error (0, "unexpected type"); @@ -532,6 +540,7 @@ algebra_mvec_type (algebra_t *algebra, pr_uint_t group_mask) .algebra = (algebra_t *) mvec, .freeable = true, .allocated = true, + .attrib = algebra_attrib, }; chain_type (type); if (!(group_mask & (group_mask - 1))) { diff --git a/tools/qfcc/source/dot_expr.c b/tools/qfcc/source/dot_expr.c index 5bb7bdedb..79defeaf1 100644 --- a/tools/qfcc/source/dot_expr.c +++ b/tools/qfcc/source/dot_expr.c @@ -48,6 +48,7 @@ #include "qfalloca.h" #include "tools/qfcc/include/algebra.h" +#include "tools/qfcc/include/attribute.h" #include "tools/qfcc/include/expr.h" #include "tools/qfcc/include/method.h" #include "tools/qfcc/include/rua-lang.h" @@ -106,6 +107,7 @@ get_op_string (int op) case QC_REVERSE: return "@reverse"; case QC_DUAL: return "@dual"; case QC_UNDUAL: return "@undual"; + case QC_ATTRIBUTE: return ".attribute"; case QC_AT_FUNCTION:return "@function"; case QC_AT_FIELD: return "@field"; case QC_AT_POINTER: return "@pointer"; @@ -347,6 +349,11 @@ print_type_expr (dstring_t *dstr, const expr_t *e, int level, int id, if (!str) { str = type_get_encoding (e->typ.type); } + } else if (e->typ.attrib) { + auto attrib = e->typ.attrib; + _print_expr (dstr, attrib->params, level + 1, id, nullptr); + dasprintf (dstr, "%*se_%p -> e_%p;\n", indent, "", e, attrib->params); + str = attrib->name; } else if (e->typ.sym) { str = e->typ.sym->name; } diff --git a/tools/qfcc/source/expr_process.c b/tools/qfcc/source/expr_process.c index 0b418b672..b41e53126 100644 --- a/tools/qfcc/source/expr_process.c +++ b/tools/qfcc/source/expr_process.c @@ -680,6 +680,10 @@ spec_process (specifier_t spec, rua_ctx_t *ctx) spec = default_type (spec, spec.sym); } if (!spec.type_list) { + if (spec.type_expr) { + spec.type = resolve_type (spec.type_expr, ctx); + spec.type_list = nullptr; + } return spec; } if (spec.type_list->type != ex_list) { diff --git a/tools/qfcc/source/expr_type.c b/tools/qfcc/source/expr_type.c index 0e8726698..b6870e663 100644 --- a/tools/qfcc/source/expr_type.c +++ b/tools/qfcc/source/expr_type.c @@ -32,6 +32,7 @@ #include "QF/progs.h" #include "QF/sys.h" +#include "tools/qfcc/include/attribute.h" #include "tools/qfcc/include/def.h" #include "tools/qfcc/include/defspace.h" #include "tools/qfcc/include/codespace.h" @@ -43,6 +44,7 @@ #include "tools/qfcc/include/rua-lang.h" #include "tools/qfcc/include/shared.h" #include "tools/qfcc/include/statements.h" +#include "tools/qfcc/include/strpool.h" #include "tools/qfcc/include/symtab.h" #include "tools/qfcc/include/type.h" @@ -51,6 +53,7 @@ typedef struct { int num_types; codespace_t *code; defspace_t *data; + strpool_t *strings; sys_jmpbuf jmpbuf; def_t *args[3]; def_t *funcs[tf_num_functions]; @@ -80,6 +83,15 @@ check_type (const expr_t *arg) return true; } +static bool +check_attribute (const expr_t *arg) +{ + if (arg->type != ex_type && !arg->typ.attrib) { + return false; + } + return true; +} + static bool __attribute__((pure)) check_int (const expr_t *arg) { @@ -119,6 +131,24 @@ single_type (int arg_count, const expr_t **args) return nullptr; } +static const char * +single_type_attribute (int arg_count, const expr_t **args) +{ + if (arg_count < 1) { + return "too few arguments"; + } + if (arg_count > 2) { + return "too many arguments"; + } + if (!check_type (args[0])) { + return "first parameter must be a type"; + } + if (!check_attribute (args[1])) { + return "second parameter must be a type attribute"; + } + return nullptr; +} + static const char * single_type_opt_int (int arg_count, const expr_t **args) { @@ -158,6 +188,24 @@ single_type_opt_int_pair (int arg_count, const expr_t **args) return nullptr; } +static const expr_t * +evaluate_attribute (int arg_count, const expr_t **args, rua_ctx_t *ctx) +{ + auto type = resolve_type (args[0], ctx); + if (!type) { + return error (args[0], "could not resolve type"); + } + if (!type->attrib) { + return error (args[0], "type doesn't support attributes"); + } else { + auto e = type->attrib (type, args[1]->typ.attrib); + if (!is_error (e)) { + e = eval_type (e, ctx); + } + return e; + } +} + static const expr_t * evaluate_int (const expr_t *expr, rua_ctx_t *ctx) { @@ -178,10 +226,9 @@ evaluate_int (const expr_t *expr, rua_ctx_t *ctx) internal_error (expr, "invalid type op"); } int op = expr->typ.op; - int ind = op - QC_GENERIC; auto type = expr->typ.type; if (!type) { - return error (expr, "invalid type passed to %s", type_funcs[ind].name); + return error (expr, "invalid type passed to %s", type_funcs[op].name); } if (op == QC_AT_WIDTH) { return new_int_expr (type_width (type), false); @@ -201,6 +248,25 @@ evaluate_int_op (int arg_count, const expr_t **args, rua_ctx_t *ctx) return evaluate_int (args[0], ctx); } +static const type_t * +resolve_attribute (int arg_count, const expr_t **args, rua_ctx_t *ctx) +{ + auto type = resolve_type (args[0], ctx); + if (type) { + if (!type->attrib) { + error (args[0], "type doesn't support attributes"); + } else { + auto e = type->attrib (type, args[1]->typ.attrib); + if (is_error (e)) { + type = nullptr; + } else { + type = resolve_type (e, ctx); + } + } + } + return type; +} + static const type_t * resolve_function (int arg_count, const expr_t **args, rua_ctx_t *ctx) { @@ -418,6 +484,8 @@ expand_matrix (int arg_count, const expr_t **args, rua_ctx_t *ctx) } static def_t *compute_tmp (comp_ctx_t *ctx); +static def_t *compute_sized_tmp (int size, comp_ctx_t *ctx); +static pr_string_t compute_str (const char *, comp_ctx_t *ctx); static def_t *compute_type (const expr_t *arg, comp_ctx_t *ctx); static def_t *compute_val (const expr_t *arg, comp_ctx_t *ctx); @@ -433,6 +501,39 @@ static def_t *compute_val (const expr_t *arg, comp_ctx_t *ctx); } #define C(op, a, b, c) codespace_addcode (ctx->code, I(op, a, b, c), 1) +static def_t * +compute_attribute (int arg_count, const expr_t **args, comp_ctx_t *ctx) +{ + auto type = compute_type (args[0], ctx); + auto res = compute_tmp (ctx); + auto attrib = args[1]->typ.attrib; + int count = attrib->params ? list_count (&attrib->params->list) : 0; + // FIXME assumes simple 1-component types + auto attr_params = compute_sized_tmp (2 + 2 * count, ctx); + def_t ndef = { .offset = attr_params->offset + 0, .space = ctx->data }; + def_t cdef = { .offset = attr_params->offset + 1, .space = ctx->data }; + D_STRING (&ndef) = compute_str (attrib->name, ctx); + D_INT (&cdef) = count; + if (count) { + const expr_t *params[count]; + list_scatter (&attrib->params->list, params); + for (int i = 0; i < count; i++) { + auto p = compute_type (params[i], ctx); + auto s = codespace_newstatement (ctx->code); + *s = (dstatement_t) { + .op = OP_STORE_A_1, + .a = attr_params->offset + 2 + i, + .b = 0, + .c = DOFFSET(p), + }; + } + } + C (OP_STORE_A_1, ctx->args[0], nullptr, type); + C (OP_STORE_A_1, ctx->args[1], nullptr, attr_params); + C (OP_CALL_B, ctx->funcs[tf_attribute], nullptr, res); + return res; +} + static def_t * compute_function (int arg_count, const expr_t **args, comp_ctx_t *ctx) { @@ -582,87 +683,94 @@ compute_cols (int arg_count, const expr_t **args, comp_ctx_t *ctx) } static type_func_t type_funcs[] = { - [QC_AT_FUNCTION - QC_GENERIC] = { + [QC_ATTRIBUTE] = { + .name = ".attribute", + .check_params = single_type_attribute, + .resolve = resolve_attribute, + .evaluate = evaluate_attribute, + .compute = compute_attribute, + }, + [QC_AT_FUNCTION] = { .name = "@function", .check_params = single_type, .resolve = resolve_function, .compute = compute_function, }, - [QC_AT_FIELD - QC_GENERIC] = { + [QC_AT_FIELD] = { .name = "@field", .check_params = single_type, .resolve = resolve_field, .compute = compute_field, }, - [QC_AT_POINTER - QC_GENERIC] = { + [QC_AT_POINTER] = { .name = "@pointer", .check_params = single_type, .resolve = resolve_pointer, .compute = compute_pointer, }, - [QC_AT_ARRAY - QC_GENERIC] = { + [QC_AT_ARRAY] = { .name = "@array", .check_params = single_type_opt_int, .resolve = resolve_array, .compute = compute_type_array, }, - [QC_AT_BASE - QC_GENERIC] = { + [QC_AT_BASE] = { .name = "@base", .check_params = single_type, .resolve = resolve_base, .compute = compute_base, }, - [QC_AT_WIDTH - QC_GENERIC] = { + [QC_AT_WIDTH] = { .name = "@width", .check_params = single_type, .evaluate = evaluate_int_op, .compute = compute_width, }, - [QC_AT_VECTOR - QC_GENERIC] = { + [QC_AT_VECTOR] = { .name = "@vector", .check_params = single_type_opt_int, .resolve = resolve_vector, .expand = expand_vector, .compute = compute_type_vector, }, - [QC_AT_ROWS - QC_GENERIC] = { + [QC_AT_ROWS] = { .name = "@rows", .check_params = single_type, .evaluate = evaluate_int_op, .compute = compute_rows, }, - [QC_AT_COLS - QC_GENERIC] = { + [QC_AT_COLS] = { .name = "@cols", .check_params = single_type, .evaluate = evaluate_int_op, .compute = compute_cols, }, - [QC_AT_MATRIX - QC_GENERIC] = { + [QC_AT_MATRIX] = { .name = "@matrix", .check_params = single_type_opt_int_pair, .resolve = resolve_matrix, .expand = expand_matrix, .compute = compute_matrix, }, - [QC_AT_INT - QC_GENERIC] = { + [QC_AT_INT] = { .name = "@int", .check_params = single_type, .resolve = resolve_int, .compute = compute_int, }, - [QC_AT_UINT - QC_GENERIC] = { + [QC_AT_UINT] = { .name = "@uint", .check_params = single_type, .resolve = resolve_uint, .compute = compute_uint, }, - [QC_AT_BOOL - QC_GENERIC] = { + [QC_AT_BOOL] = { .name = "@bool", .check_params = single_type, .resolve = resolve_bool, .compute = compute_bool, }, - [QC_AT_FLOAT - QC_GENERIC] = { + [QC_AT_FLOAT] = { .name = "@float", .check_params = single_type, .resolve = resolve_float, @@ -689,14 +797,13 @@ type_function (int op, const expr_t *params) int arg_count = list_count (¶ms->list); const expr_t *args[arg_count]; list_scatter (¶ms->list, args); - unsigned ind = op - QC_GENERIC; - if (ind >= sizeof (type_funcs) / sizeof (type_funcs[0]) - || !type_funcs[ind].name) { + if ((unsigned) op >= sizeof (type_funcs) / sizeof (type_funcs[0]) + || !type_funcs[op].name) { internal_error (params, "invalid type op: %d", op); } - const char *msg = type_funcs[ind].check_params (arg_count, args); + const char *msg = type_funcs[op].check_params (arg_count, args); if (msg) { - return error (params, "%s for %s", msg, type_funcs[ind].name); + return error (params, "%s for %s", msg, type_funcs[op].name); } return new_type_function (op, params); } @@ -735,16 +842,15 @@ resolve_type (const expr_t *te, rua_ctx_t *ctx) //} return te->typ.type; } - int op = te->typ.op; - unsigned ind = op - QC_GENERIC; - if (ind >= sizeof (type_funcs) / sizeof (type_funcs[0]) - || !type_funcs[ind].name) { + unsigned op = te->typ.op; + if (op >= sizeof (type_funcs) / sizeof (type_funcs[0]) + || !type_funcs[op].name) { internal_error (te, "invalid type op: %d", op); } int arg_count = list_count (&te->typ.params->list); const expr_t *args[arg_count]; list_scatter (&te->typ.params->list, args); - return type_funcs[ind].resolve (arg_count, args, ctx); + return type_funcs[op].resolve (arg_count, args, ctx); } const expr_t * @@ -759,22 +865,21 @@ process_type (const expr_t *te, rua_ctx_t *ctx) } return new_type_expr (te->typ.type); } - int op = te->typ.op; - unsigned ind = op - QC_GENERIC; - if (ind >= sizeof (type_funcs) / sizeof (type_funcs[0]) - || !type_funcs[ind].name) { + unsigned op = te->typ.op; + if (op >= sizeof (type_funcs) / sizeof (type_funcs[0]) + || !type_funcs[op].name) { internal_error (te, "invalid type op: %d", op); } int arg_count = list_count (&te->typ.params->list); const expr_t *args[arg_count]; list_scatter (&te->typ.params->list, args); - if (type_funcs[ind].resolve) { - auto type = type_funcs[ind].resolve (arg_count, args, ctx); + if (type_funcs[op].resolve) { + auto type = type_funcs[op].resolve (arg_count, args, ctx); return new_type_expr (type); - } else if (type_funcs[ind].evaluate) { - return type_funcs[ind].evaluate (arg_count, args, ctx); + } else if (type_funcs[op].evaluate) { + return type_funcs[op].evaluate (arg_count, args, ctx); } else { - internal_error (te, "invalid type op: %s", type_funcs[ind].name); + internal_error (te, "invalid type op: %s", type_funcs[op].name); } } @@ -794,19 +899,18 @@ expand_type (const expr_t *te, rua_ctx_t *ctx) types[1] = nullptr; return types; } - int op = te->typ.op; - unsigned ind = op - QC_GENERIC; - if (ind >= sizeof (type_funcs) / sizeof (type_funcs[0]) - || !type_funcs[ind].name) { + unsigned op = te->typ.op; + if (op >= sizeof (type_funcs) / sizeof (type_funcs[0]) + || !type_funcs[op].name) { internal_error (te, "invalid type op: %d", op); } - if (!type_funcs[ind].expand) { - error (te, "cannot expand %s", type_funcs[ind].name); + if (!type_funcs[op].expand) { + error (te, "cannot expand %s", type_funcs[op].name); } int arg_count = list_count (&te->typ.params->list); const expr_t *args[arg_count]; list_scatter (&te->typ.params->list, args); - return type_funcs[ind].expand (arg_count, args, ctx); + return type_funcs[op].expand (arg_count, args, ctx); } const expr_t * @@ -815,22 +919,35 @@ eval_type (const expr_t *te, rua_ctx_t *ctx) if (te->type != ex_type) { internal_error (te, "not a type expression"); } - int op = te->typ.op; - unsigned ind = op - QC_GENERIC; - if (ind >= sizeof (type_funcs) / sizeof (type_funcs[0]) - || !type_funcs[ind].name) { + unsigned op = te->typ.op; + if (op >= sizeof (type_funcs) / sizeof (type_funcs[0]) + || !type_funcs[op].name) { internal_error (te, "invalid type op: %d", op); } int arg_count = list_count (&te->typ.params->list); const expr_t *args[arg_count]; list_scatter (&te->typ.params->list, args); - return type_funcs[ind].evaluate (arg_count, args, ctx); + return type_funcs[op].evaluate (arg_count, args, ctx); } static def_t * compute_tmp (comp_ctx_t *ctx) { - return new_def (0, &type_int, ctx->data, sc_static); + return new_def (nullptr, &type_int, ctx->data, sc_static); +} + +static def_t * +compute_sized_tmp (int size, comp_ctx_t *ctx) +{ + auto def = new_def (nullptr, nullptr, ctx->data, sc_static); + def->offset = defspace_alloc_loc (ctx->data, size); + return def; +} + +static pr_string_t +compute_str (const char *str, comp_ctx_t *ctx) +{ + return strpool_addstr (ctx->strings, str); } static def_t * @@ -859,7 +976,6 @@ compute_val (const expr_t *arg, comp_ctx_t *ctx) D_INT (val) = expr_integral (arg); } else if (arg->type == ex_type) { int op = arg->typ.op; - int ind = op - QC_GENERIC; if (arg->typ.type) { auto type = arg->typ.type; if (op == QC_AT_WIDTH) { @@ -890,7 +1006,7 @@ compute_val (const expr_t *arg, comp_ctx_t *ctx) C (OP_STORE_A_1, ctx->args[0], nullptr, type); C (OP_CALL_B, ctx->funcs[op], nullptr, val); } else { - error (arg, "invalid type passed to %s", type_funcs[ind].name); + error (arg, "invalid type passed to %s", type_funcs[op].name); } } return val; @@ -930,16 +1046,15 @@ compute_type (const expr_t *arg, comp_ctx_t *ctx) D_INT (val) = arg->typ.type->id; return val; } - int op = arg->typ.op; - unsigned ind = op - QC_GENERIC; - if (ind >= sizeof (type_funcs) / sizeof (type_funcs[0]) - || !type_funcs[ind].name) { + unsigned op = arg->typ.op; + if (op >= sizeof (type_funcs) / sizeof (type_funcs[0]) + || !type_funcs[op].name) { internal_error (arg, "invalid type op: %d", op); } int arg_count = list_count (&arg->typ.params->list); const expr_t *args[arg_count]; list_scatter (&arg->typ.params->list, args); - return type_funcs[ind].compute (arg_count, args, ctx); + return type_funcs[op].compute (arg_count, args, ctx); } typeeval_t * @@ -947,11 +1062,13 @@ build_type_function (const expr_t *te, int num_types, gentype_t *types) { auto code = codespace_new (); auto data = defspace_new (ds_backed); + auto strings = strpool_new (); comp_ctx_t *ctx = &(comp_ctx_t) { .types = types, .num_types = num_types, .code = code, .data = data, + .strings = strings, }; compute_tmp (ctx); for (int i = 0; i < 3; i++) { @@ -971,13 +1088,17 @@ build_type_function (const expr_t *te, int num_types, gentype_t *types) *func = (typeeval_t) { .code = code->code, .data = data->data, + .strings = strings->strings, .code_size = code->size, .data_size = data->size, + .string_size = strings->size, }; } code->code = nullptr; data->data = nullptr; + strings->strings = nullptr; codespace_delete (code); defspace_delete (data); + strpool_delete (strings); return func; } diff --git a/tools/qfcc/source/qc-parse.y b/tools/qfcc/source/qc-parse.y index 177a18acc..f48735309 100644 --- a/tools/qfcc/source/qc-parse.y +++ b/tools/qfcc/source/qc-parse.y @@ -284,6 +284,20 @@ type_spec (const type_t *type) return spec; } +static const expr_t * +type_attribute (specifier_t spec, const attribute_t *attrib) +{ + auto type_expr = spec.type_expr; + if (!type_expr) { + type_expr = new_type_expr (spec.type); + } + auto params = new_list_expr (type_expr); + auto attr = new_type_expr (nullptr); + attr->typ.attrib = attrib; + expr_append_expr (params, attr); + return new_type_function (QC_ATTRIBUTE, params); +} + static specifier_t storage_spec (storage_class_t storage) { @@ -524,17 +538,15 @@ make_param (specifier_t spec, rua_ctx_t *ctx) 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 if (spec.sym) { - spec = default_type (spec, spec.sym); + spec = spec_process (spec, ctx); spec.type = find_type (append_type (spec.sym->type, spec.type)); param = new_param (nullptr, spec.type, spec.sym->name); } else { - spec = default_type (spec, spec.sym); + spec = spec_process (spec, ctx); param = new_param (nullptr, spec.type, nullptr); } if (spec.is_const) { @@ -1146,15 +1158,10 @@ typespec_reserved typespec_nonreserved : TYPE_NAME %prec LOW - | TYPE_NAME '.' attribute + | TYPE_NAME[spec] '.' attribute { - if (!is_algebra ($1.type)) { - error (0, "%s does not have any subtypes", - get_type_string ($1.type)); - $$ = $1; - } else { - $$ = type_spec (algebra_subtype ($1.type, $3)); - } + auto te = type_attribute ($spec, $attribute); + $$ = (specifier_t) { .type_expr = te }; } | OBJECT_NAME protocolrefs { @@ -1356,6 +1363,10 @@ type_ref $$ = new_type_expr ($1.type); } } + | TYPE_NAME[spec] '.' attribute + { + $$ = type_attribute ($spec, $attribute); + } ; type_ref_spec @@ -2886,6 +2897,12 @@ obj_string %% +static void __attribute__((used)) +qc_dump_stack(yypstate *yyps) +{ + yy_stack_print (yyps->yyss, yyps->yyssp); +} + // preprocessor directives in ruamoko and quakec static directive_t rua_directives[] = { {"include", PRE_INCLUDE}, diff --git a/tools/qfcc/source/type.c b/tools/qfcc/source/type.c index 5e58e0c88..40b59e9c9 100644 --- a/tools/qfcc/source/type.c +++ b/tools/qfcc/source/type.c @@ -1040,6 +1040,7 @@ alias_type (const type_t *type, const type_t *alias_chain, const char *name) if (name) { alias->name = save_string (name); } + alias->attrib = type->attrib; return alias; }