[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;
This commit is contained in:
Bill Currie 2024-04-30 11:32:20 +09:00
parent fed1bce12a
commit aff70aa243
3 changed files with 276 additions and 15 deletions

View file

@ -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); expr_t *new_type_expr (const type_t *type);
const expr_t *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); 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. /** Convert a name to an expression of the appropriate type.

View file

@ -37,9 +37,13 @@
typedef struct { typedef struct {
const char *name; 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; } type_func_t;
static type_func_t type_funcs[];
static bool static bool
check_type (const expr_t *arg) check_type (const expr_t *arg)
{ {
@ -50,23 +54,37 @@ check_type (const expr_t *arg)
return true; return true;
} }
static bool static bool __attribute__((pure))
check_int (const expr_t *arg) 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) if ((arg->type != ex_symbol || arg->symbol->sy_type != sy_type_param)
&& arg->type != ex_type) { && arg->type != ex_type) {
return false; 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; return true;
} }
static const char * 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"; return "too few arguments";
} }
if (count > 1) { if (arg_count > 1) {
return "too many arguments"; return "too many arguments";
} }
if (!check_type (args[0])) { if (!check_type (args[0])) {
@ -76,96 +94,288 @@ single_type (int count, const expr_t **args)
} }
static const char * 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"; return "too few arguments";
} }
if (count > 2) { if (arg_count > 2) {
return "too many arguments"; return "too many arguments";
} }
if (!check_type (args[0])) { if (!check_type (args[0])) {
return "first parameter must be a type"; 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 "second parameter must be int";
} }
return nullptr; return nullptr;
} }
static const char * 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"; return "too few arguments";
} }
if (count > 3) { if (arg_count > 3) {
return "too many arguments"; return "too many arguments";
} }
if (count == 2) { if (arg_count == 2) {
return "must have one or three arguments"; return "must have one or three arguments";
} }
if (!check_type (args[0])) { if (!check_type (args[0])) {
return "first parameter must be a type"; 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 "second and third parameters must be int";
} }
return nullptr; 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[] = { static type_func_t type_funcs[] = {
[QC_AT_FIELD - QC_GENERIC] = { [QC_AT_FIELD - QC_GENERIC] = {
.name = "@field", .name = "@field",
.check_params = single_type, .check_params = single_type,
.resolve = resolve_field,
}, },
[QC_AT_POINTER - QC_GENERIC] = { [QC_AT_POINTER - QC_GENERIC] = {
.name = "@pointer", .name = "@pointer",
.check_params = single_type, .check_params = single_type,
.resolve = resolve_pointer,
}, },
[QC_AT_ARRAY - QC_GENERIC] = { [QC_AT_ARRAY - QC_GENERIC] = {
.name = "@array", .name = "@array",
.check_params = single_type_opt_int, .check_params = single_type_opt_int,
.resolve = resolve_array,
}, },
[QC_AT_BASE - QC_GENERIC] = { [QC_AT_BASE - QC_GENERIC] = {
.name = "@base", .name = "@base",
.check_params = single_type, .check_params = single_type,
.resolve = resolve_base,
}, },
[QC_AT_WIDTH - QC_GENERIC] = { [QC_AT_WIDTH - QC_GENERIC] = {
.name = "@width", .name = "@width",
.check_params = single_type, .check_params = single_type,
.evaluate = evaluate_int_op,
}, },
[QC_AT_VECTOR - QC_GENERIC] = { [QC_AT_VECTOR - QC_GENERIC] = {
.name = "@vector", .name = "@vector",
.check_params = single_type_opt_int, .check_params = single_type_opt_int,
.resolve = resolve_vector,
}, },
[QC_AT_ROWS - QC_GENERIC] = { [QC_AT_ROWS - QC_GENERIC] = {
.name = "@rows", .name = "@rows",
.check_params = single_type, .check_params = single_type,
.evaluate = evaluate_int_op,
}, },
[QC_AT_COLS - QC_GENERIC] = { [QC_AT_COLS - QC_GENERIC] = {
.name = "@cols", .name = "@cols",
.check_params = single_type, .check_params = single_type,
.evaluate = evaluate_int_op,
}, },
[QC_AT_MATRIX - QC_GENERIC] = { [QC_AT_MATRIX - QC_GENERIC] = {
.name = "@matrix", .name = "@matrix",
.check_params = single_type_opt_int_pair, .check_params = single_type_opt_int_pair,
.resolve = resolve_matrix,
}, },
[QC_AT_INT - QC_GENERIC] = { [QC_AT_INT - QC_GENERIC] = {
.name = "@int", .name = "@int",
.check_params = single_type, .check_params = single_type,
.resolve = resolve_int,
}, },
[QC_AT_UINT - QC_GENERIC] = { [QC_AT_UINT - QC_GENERIC] = {
.name = "@uint", .name = "@uint",
.check_params = single_type, .check_params = single_type,
.resolve = resolve_uint,
}, },
[QC_AT_BOOL - QC_GENERIC] = { [QC_AT_BOOL - QC_GENERIC] = {
.name = "@bool", .name = "@bool",
.check_params = single_type, .check_params = single_type,
.resolve = resolve_bool,
}, },
[QC_AT_FLOAT - QC_GENERIC] = { [QC_AT_FLOAT - QC_GENERIC] = {
.name = "@float", .name = "@float",
.check_params = single_type, .check_params = single_type,
.resolve = resolve_float,
}, },
}; };
@ -185,6 +395,7 @@ type_function (int op, const expr_t *params)
} }
auto te = new_expr (); auto te = new_expr ();
te->type = ex_type; te->type = ex_type;
te->nodag = 1;
te->typ = (ex_type_t) { te->typ = (ex_type_t) {
.op = op, .op = op,
.params = params, .params = params,
@ -202,3 +413,43 @@ type_parameter (symbol_t *sym, const expr_t *type)
sym->s.expr = type; sym->s.expr = type;
return sym; 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);
}

View file

@ -418,6 +418,10 @@ make_ellipsis (void)
static param_t * static param_t *
make_param (specifier_t spec) make_param (specifier_t spec)
{ {
if (spec.type_expr) {
if (!(spec.type = resolve_type (spec.type_expr))) {
}
}
spec = default_type (spec, spec.sym); spec = default_type (spec, spec.sym);
spec.type = find_type (append_type (spec.sym->type, spec.type)); spec.type = find_type (append_type (spec.sym->type, spec.type));
param_t *param = new_param (0, spec.type, spec.sym->name); param_t *param = new_param (0, spec.type, spec.sym->name);
@ -916,7 +920,11 @@ typespec
typespec_reserved typespec_reserved
: TYPE_SPEC : 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 %prec LOW
| algebra_specifier '.' attribute | algebra_specifier '.' attribute
{ {