diff --git a/tools/qfcc/include/expr.h b/tools/qfcc/include/expr.h index 134fd149b..0bac39688 100644 --- a/tools/qfcc/include/expr.h +++ b/tools/qfcc/include/expr.h @@ -63,6 +63,7 @@ typedef struct ex_expr_s { bool commutative; ///< e1 and e2 can be swapped bool anticommute; ///< e1 and e2 can be swapped with negation bool associative; ///< a op (b op c) == (a op b) op c + bool constant; ///< constant that has/will not been folded const type_t *type; ///< the type of the result of this expression const expr_t *e1; ///< left side of binary, sole of unary const expr_t *e2; ///< right side of binary, null for unary @@ -758,11 +759,20 @@ bool is_error (const expr_t *e) __attribute__((pure)); /** Check if the expression refers to a constant value. + This does not included computed constants that have not been folded. + \param e The expression to check. \return True if the expression is constant. */ int is_constant (const expr_t *e) __attribute__((pure)); +/** Check if the expression refers to a constant expression or value. + + \param e The expression to check. + \return True if the expression is constant. +*/ +bool is_constexpr (const expr_t *e) __attribute__((pure)); + /** Check if the expression refers to a variable. \param e The expression to check. diff --git a/tools/qfcc/source/expr.c b/tools/qfcc/source/expr.c index 48bdb7809..d82a846f5 100644 --- a/tools/qfcc/source/expr.c +++ b/tools/qfcc/source/expr.c @@ -568,6 +568,7 @@ new_binary_expr (int op, const expr_t *e1, const expr_t *e2) expr_t *e = new_expr (); e->type = ex_expr; e->nodag = e1->nodag | e2->nodag; + e->expr.constant = is_constexpr (e1) && is_constexpr (e2); e->expr.op = op; e->expr.e1 = e1; e->expr.e2 = e2; @@ -596,12 +597,13 @@ new_unary_expr (int op, const expr_t *e1) { if (e1 && e1->type == ex_error) { - internal_error (e1, "error expr in new_binary_expr"); + internal_error (e1, "error expr in new_unary_expr"); } expr_t *e = new_expr (); e->type = ex_uexpr; e->nodag = e1->nodag; + e->expr.constant = is_constexpr (e1); e->expr.op = op; e->expr.e1 = e1; return e; @@ -970,6 +972,18 @@ is_constant (const expr_t *e) return 0; } +bool +is_constexpr (const expr_t *e) +{ + while (e->type == ex_alias) { + e = e->alias.expr; + } + if ((e->type == ex_expr || e->type == ex_uexpr) && e->expr.constant) { + return true; + } + return is_constant (e); +} + int is_variable (const expr_t *e) { diff --git a/tools/qfcc/source/glsl-declaration.c b/tools/qfcc/source/glsl-declaration.c index 3c6c52ecd..a22d85162 100644 --- a/tools/qfcc/source/glsl-declaration.c +++ b/tools/qfcc/source/glsl-declaration.c @@ -45,6 +45,9 @@ void glsl_parse_declaration (specifier_t spec, symbol_t *sym, const type_t *array, const expr_t *init, symtab_t *symtab) { + if (sym->type) { + internal_error (0, "unexected typed symbol"); + } if (array) { spec.type = append_type (array, spec.type); spec.type = find_type (spec.type); @@ -66,7 +69,40 @@ glsl_parse_declaration (specifier_t spec, symbol_t *sym, const type_t *array, } else { spec.sym = sym; if (spec.sym) { - spec.sym = declare_symbol (spec, init, symtab); + if (spec.is_const && !init) { + error (0, "uninitialized const %s", sym->name); + init = new_zero_expr (spec.type); + } + if (spec.is_const && init->type != ex_compound) { + auto type = get_type (init); + if (type != spec.type && type_assignable (spec.type, type)) { + if (!init->implicit && !type_promotes (spec.type, type)) { + warning (init, "initialization of %s with %s" + " (use a cast)\n)", + get_type_string (spec.type), + get_type_string (type)); + } + init = cast_expr (spec.type, init); + } + if (get_type (init) != spec.type) { + error (init, "type mismatch in initializer"); + init = new_zero_expr (spec.type); + } + if (!is_constexpr (init)) { + error (init, "non-constant initializer"); + init = new_zero_expr (spec.type); + } + sym->type = spec.type; + sym->sy_type = sy_expr; + sym->expr = init; + auto s = symtab_lookup (symtab, sym->name); + if (s && s->table == symtab) { + error (0, "%s redeclared", sym->name); + } + symtab_addsymbol (symtab, sym); + } else { + spec.sym = declare_symbol (spec, init, symtab); + } } glsl_apply_attributes (attributes, spec); } diff --git a/tools/qfcc/source/glsl-layout.c b/tools/qfcc/source/glsl-layout.c index f994883af..c48d4a91f 100644 --- a/tools/qfcc/source/glsl-layout.c +++ b/tools/qfcc/source/glsl-layout.c @@ -60,6 +60,14 @@ glsl_layout_location (specifier_t spec, const expr_t *qual_name, get_value_string (val->value)); } +static void +glsl_layout_constant_id (specifier_t spec, const expr_t *qual_name, + const expr_t *val) +{ + notice (qual_name, "%s %s", expr_string (qual_name), + get_value_string (val->value)); +} + static void glsl_layout_binding (specifier_t spec, const expr_t *qual_name, const expr_t *val) @@ -466,7 +474,7 @@ static layout_qual_t layout_qualifiers[] = { }, { .name = "constant_id", - .apply = E(nullptr), + .apply = E(glsl_layout_constant_id), .obj_mask = D(var), .var_type = V(scalar), }, @@ -765,7 +773,7 @@ layout_check_qualifier (const layout_qual_t *qual, specifier_t spec) && qual->var_type != var_type) { return false; } - if (!(qual->if_mask & if_mask)) { + if (qual->if_mask != if_mask && !(qual->if_mask & if_mask)) { return false; } if (qual->stage_filter) {