From aff70aa24370712ac51c55ccc5a2590cb1a0c84c Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Tue, 30 Apr 2024 11:32:20 +0900 Subject: [PATCH] [qfcc] Support static type expressions in declarations This allows types in declarations to be based on other types: int foo[3]; @vector(float,sizeof(foo)) bar; --- tools/qfcc/include/expr.h | 2 + tools/qfcc/source/expr_type.c | 279 ++++++++++++++++++++++++++++++++-- tools/qfcc/source/qc-parse.y | 10 +- 3 files changed, 276 insertions(+), 15 deletions(-) diff --git a/tools/qfcc/include/expr.h b/tools/qfcc/include/expr.h index e2ef252ca..cd2e39a85 100644 --- a/tools/qfcc/include/expr.h +++ b/tools/qfcc/include/expr.h @@ -857,6 +857,8 @@ expr_t *new_memset_expr (const expr_t *dst, const expr_t *val, expr_t *new_type_expr (const type_t *type); 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); +const expr_t *evaluate_type (const expr_t *te); /** Convert a name to an expression of the appropriate type. diff --git a/tools/qfcc/source/expr_type.c b/tools/qfcc/source/expr_type.c index 91369958a..4b95a7b7a 100644 --- a/tools/qfcc/source/expr_type.c +++ b/tools/qfcc/source/expr_type.c @@ -37,9 +37,13 @@ typedef struct { const char *name; - const char *(*check_params) (int count, const expr_t **args); + const char *(*check_params) (int arg_count, const expr_t **args); + const type_t *(*resolve) (int arg_count, const expr_t **args); + const expr_t *(*evaluate) (int arg_count, const expr_t **args); } type_func_t; +static type_func_t type_funcs[]; + static bool check_type (const expr_t *arg) { @@ -50,23 +54,37 @@ check_type (const expr_t *arg) return true; } -static bool +static bool __attribute__((pure)) check_int (const expr_t *arg) { + if (arg->type == ex_expr || arg->type == ex_uexpr) { + if (!is_int (arg->expr.type)) { + return false; + } + return check_int (arg->expr.e1) + && arg->type == ex_expr && check_int (arg->expr.e2); + } + if (is_integral_val (arg)) { + return true; + } if ((arg->type != ex_symbol || arg->symbol->sy_type != sy_type_param) && arg->type != ex_type) { return false; } + int op = arg->symbol->s.expr->typ.op; + if (op != QC_AT_WIDTH && op != QC_AT_ROWS && op != QC_AT_COLS) { + return false; + } return true; } static const char * -single_type (int count, const expr_t **args) +single_type (int arg_count, const expr_t **args) { - if (count < 1) { + if (arg_count < 1) { return "too few arguments"; } - if (count > 1) { + if (arg_count > 1) { return "too many arguments"; } if (!check_type (args[0])) { @@ -76,96 +94,288 @@ single_type (int count, const expr_t **args) } static const char * -single_type_opt_int (int count, const expr_t **args) +single_type_opt_int (int arg_count, const expr_t **args) { - if (count < 1) { + if (arg_count < 1) { return "too few arguments"; } - if (count > 2) { + if (arg_count > 2) { return "too many arguments"; } if (!check_type (args[0])) { return "first parameter must be a type"; } - if (count > 1 && !check_int (args[1])) { + if (arg_count > 1 && !check_int (args[1])) { return "second parameter must be int"; } return nullptr; } static const char * -single_type_opt_int_pair (int count, const expr_t **args) +single_type_opt_int_pair (int arg_count, const expr_t **args) { - if (count < 1) { + if (arg_count < 1) { return "too few arguments"; } - if (count > 3) { + if (arg_count > 3) { return "too many arguments"; } - if (count == 2) { + if (arg_count == 2) { return "must have one or three arguments"; } if (!check_type (args[0])) { return "first parameter must be a type"; } - if (count > 1 && (!check_int (args[1]) || !check_int (args[2]))) { + if (arg_count > 1 && (!check_int (args[1]) || !check_int (args[2]))) { return "second and third parameters must be int"; } return nullptr; } +static const expr_t * +evaluate_int (const expr_t *expr) +{ + if (expr->type == ex_expr || expr->type == ex_uexpr) { + auto e = new_expr (); + *e = *expr; + e->expr.e1 = evaluate_int (expr->expr.e1); + if (expr->type == ex_uexpr) { + e->expr.e2 = evaluate_int (expr->expr.e2); + } + return fold_constants (e); + } + if (is_integral_val (expr)) { + return expr; + } + 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); + } + if (op == QC_AT_WIDTH) { + return new_int_expr (type_width (type), false); + } + if (op == QC_AT_ROWS) { + return new_int_expr (type_rows (type), false); + } + if (op == QC_AT_COLS) { + return new_int_expr (type_cols (type), false); + } + internal_error (expr, "invalid type op"); +} + +static const expr_t * +evaluate_int_op (int arg_count, const expr_t **args) +{ + return evaluate_int (args[0]); +} + +static const type_t * +resolve_field (int arg_count, const expr_t **args) +{ + auto type = resolve_type (args[0]); + if (type) { + type = field_type (type); + type = find_type (type); + } + return type; +} + +static const type_t * +resolve_pointer (int arg_count, const expr_t **args) +{ + auto type = resolve_type (args[0]); + if (type) { + type = pointer_type (type); + type = find_type (type); + } + return type; +} + +static const type_t * +resolve_array (int arg_count, const expr_t **args) +{ + auto type = resolve_type (args[0]); + if (type) { + int count = 0; + if (arg_count > 1) { + auto count_expr = evaluate_int (args[1]); + if (is_error (count_expr)) { + return nullptr; + } + count = expr_int (count_expr); + } + type = array_type (type, count); + } + return type; +} + +static const type_t * +resolve_base (int arg_count, const expr_t **args) +{ + auto type = resolve_type (args[0]); + if (type) { + type = base_type (type); + } + return type; +} + +static const type_t * +resolve_vector (int arg_count, const expr_t **args) +{ + auto type = resolve_type (args[0]); + if (type) { + int width = 0; + if (arg_count > 1) { + auto width_expr = evaluate_int (args[1]); + if (is_error (width_expr)) { + return nullptr; + } + width = expr_integral (width_expr); + } else { + width = type_width (type); + } + type = vector_type (type, width); + if (!type) { + error (args[0], "invalid @vector"); + } + } + return type; +} + +static const type_t * +resolve_matrix (int arg_count, const expr_t **args) +{ + auto type = resolve_type (args[0]); + if (type) { + int rows = 0; + int cols = 0; + if (arg_count > 1) { + auto rows_expr = evaluate_int (args[2]); + auto cols_expr = evaluate_int (args[1]); + if (is_error (rows_expr) || is_error (cols_expr)) { + return nullptr; + } + rows = expr_integral (rows_expr); + cols = expr_integral (cols_expr); + } else { + cols = type_cols (type); + rows = type_rows (type); + } + type = matrix_type (type, cols, rows); + if (!type) { + error (args[0], "invalid @matrix"); + } + } + return type; +} + +static const type_t * +resolve_int (int arg_count, const expr_t **args) +{ + auto type = resolve_type (args[0]); + if (type) { + type = int_type (type); + } + return type; +} + +static const type_t * +resolve_uint (int arg_count, const expr_t **args) +{ + auto type = resolve_type (args[0]); + if (type) { + type = uint_type (type); + } + return type; +} + +static const type_t * +resolve_bool (int arg_count, const expr_t **args) +{ + auto type = resolve_type (args[0]); + if (type) { + type = bool_type (type); + } + return type; +} + +static const type_t * +resolve_float (int arg_count, const expr_t **args) +{ + auto type = resolve_type (args[0]); + if (type) { + type = float_type (type); + } + return type; +} + static type_func_t type_funcs[] = { [QC_AT_FIELD - QC_GENERIC] = { .name = "@field", .check_params = single_type, + .resolve = resolve_field, }, [QC_AT_POINTER - QC_GENERIC] = { .name = "@pointer", .check_params = single_type, + .resolve = resolve_pointer, }, [QC_AT_ARRAY - QC_GENERIC] = { .name = "@array", .check_params = single_type_opt_int, + .resolve = resolve_array, }, [QC_AT_BASE - QC_GENERIC] = { .name = "@base", .check_params = single_type, + .resolve = resolve_base, }, [QC_AT_WIDTH - QC_GENERIC] = { .name = "@width", .check_params = single_type, + .evaluate = evaluate_int_op, }, [QC_AT_VECTOR - QC_GENERIC] = { .name = "@vector", .check_params = single_type_opt_int, + .resolve = resolve_vector, }, [QC_AT_ROWS - QC_GENERIC] = { .name = "@rows", .check_params = single_type, + .evaluate = evaluate_int_op, }, [QC_AT_COLS - QC_GENERIC] = { .name = "@cols", .check_params = single_type, + .evaluate = evaluate_int_op, }, [QC_AT_MATRIX - QC_GENERIC] = { .name = "@matrix", .check_params = single_type_opt_int_pair, + .resolve = resolve_matrix, }, [QC_AT_INT - QC_GENERIC] = { .name = "@int", .check_params = single_type, + .resolve = resolve_int, }, [QC_AT_UINT - QC_GENERIC] = { .name = "@uint", .check_params = single_type, + .resolve = resolve_uint, }, [QC_AT_BOOL - QC_GENERIC] = { .name = "@bool", .check_params = single_type, + .resolve = resolve_bool, }, [QC_AT_FLOAT - QC_GENERIC] = { .name = "@float", .check_params = single_type, + .resolve = resolve_float, }, }; @@ -185,6 +395,7 @@ type_function (int op, const expr_t *params) } auto te = new_expr (); te->type = ex_type; + te->nodag = 1; te->typ = (ex_type_t) { .op = op, .params = params, @@ -202,3 +413,43 @@ type_parameter (symbol_t *sym, const expr_t *type) sym->s.expr = type; return sym; } + +const type_t * +resolve_type (const expr_t *te) +{ + if (te->type != ex_type) { + internal_error (te, "not a type expression"); + } + if (!te->typ.op) { + if (!te->typ.type) { + internal_error (te, "no type in reference"); + } + return te->typ.type; + } + int op = te->typ.op; + unsigned ind = op - QC_GENERIC; + if (ind >= sizeof (type_funcs) / sizeof (type_funcs[0])) { + 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); +} + +const expr_t * +evaluate_type (const expr_t *te) +{ + 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])) { + 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); +} diff --git a/tools/qfcc/source/qc-parse.y b/tools/qfcc/source/qc-parse.y index 5843324e7..0364d3256 100644 --- a/tools/qfcc/source/qc-parse.y +++ b/tools/qfcc/source/qc-parse.y @@ -418,6 +418,10 @@ make_ellipsis (void) static param_t * make_param (specifier_t spec) { + if (spec.type_expr) { + if (!(spec.type = resolve_type (spec.type_expr))) { + } + } 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); @@ -916,7 +920,11 @@ typespec typespec_reserved : TYPE_SPEC - | type_function { $$ = make_spec ($1->typ.type, 0, 0, 0); } + | type_function + { + auto type = resolve_type ($1); + $$ = make_spec (type, 0, 0, 0); + } | algebra_specifier %prec LOW | algebra_specifier '.' attribute {