[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);
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.

View file

@ -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);
}

View file

@ -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
{