]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
Merge branch 'master' of github.com:graphitemaster/gmqcc
[xonotic/gmqcc.git] / parser.c
index c771dd6d6fb3d5223b663c3a854cd28d38771db4..128fa4f79c2d2210f47a1e88eb5fb95b2be97788 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -75,6 +75,7 @@ typedef struct {
     size_t          *_blocklocals;
     ast_value      **_typedefs;
     size_t          *_blocktypedefs;
+    lex_ctx         *_block_ctx;
 
     size_t errors;
 
@@ -86,20 +87,27 @@ typedef struct {
      * anything else: type error
      */
     qcint  memberof;
+
+    /* Keep track of our ternary vs parenthesis nesting state.
+     * If we reach a 'comma' operator in a ternary without a paren,
+     * we shall trigger -Wternary-precedence.
+     */
+    enum { POT_PAREN, POT_TERNARY1, POT_TERNARY2 } *pot;
+
+    /* pragma flags */
+    bool noref;
 } parser_t;
 
-#define CV_NONE 0
-#define CV_CONST 1
-#define CV_VAR -1
+static const ast_expression *intrinsic_debug_typestring = (ast_expression*)0x10;
 
 static void parser_enterblock(parser_t *parser);
 static bool parser_leaveblock(parser_t *parser);
 static void parser_addlocal(parser_t *parser, const char *name, ast_expression *e);
 static bool parse_typedef(parser_t *parser);
-static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int is_const_var, ast_value *cached_typedef);
-static ast_block* parse_block(parser_t *parser, bool warnreturn);
-static bool parse_block_into(parser_t *parser, ast_block *block, bool warnreturn);
-static ast_expression* parse_statement_or_block(parser_t *parser);
+static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool noreturn);
+static ast_block* parse_block(parser_t *parser);
+static bool parse_block_into(parser_t *parser, ast_block *block);
+static bool parse_statement_or_block(parser_t *parser, ast_expression **out);
 static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases);
 static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma);
 static ast_expression* parse_expression(parser_t *parser, bool stopatcomma);
@@ -118,40 +126,22 @@ static void parseerror(parser_t *parser, const char *fmt, ...)
 /* returns true if it counts as an error */
 static bool GMQCC_WARN parsewarning(parser_t *parser, int warntype, const char *fmt, ...)
 {
+    bool    r;
        va_list ap;
-       int lvl = LVL_WARNING;
-
-    if (!OPTS_WARN(warntype))
-        return false;
-
-    if (opts_werror) {
-           parser->errors++;
-           lvl = LVL_ERROR;
-       }
-
        va_start(ap, fmt);
-    con_vprintmsg(lvl, parser->lex->tok.ctx.file, parser->lex->tok.ctx.line, "warning", fmt, ap);
+       r = vcompile_warning(parser->lex->tok.ctx, warntype, fmt, ap);
        va_end(ap);
-
-       return opts_werror;
+       return r;
 }
 
 static bool GMQCC_WARN genwarning(lex_ctx ctx, int warntype, const char *fmt, ...)
 {
+    bool    r;
        va_list ap;
-       int lvl = LVL_WARNING;
-
-    if (!OPTS_WARN(warntype))
-        return false;
-
-    if (opts_werror)
-           lvl = LVL_ERROR;
-
        va_start(ap, fmt);
-    con_vprintmsg(lvl, ctx.file, ctx.line, "warning", fmt, ap);
+       r = vcompile_warning(ctx, warntype, fmt, ap);
        va_end(ap);
-
-       return opts_werror;
+       return r;
 }
 
 /**********************************************************************
@@ -221,7 +211,7 @@ static ast_value* parser_const_float(parser_t *parser, double d)
             return parser->imm_float[i];
     }
     out = ast_value_new(parser_ctx(parser), "#IMMEDIATE", TYPE_FLOAT);
-    out->constant = true;
+    out->cvq      = CV_CONST;
     out->hasvalue = true;
     out->constval.vfloat = d;
     vec_push(parser->imm_float, out);
@@ -267,7 +257,7 @@ static ast_value* parser_const_string(parser_t *parser, const char *str, bool do
         out = ast_value_new(parser_ctx(parser), name, TYPE_STRING);
     } else
         out = ast_value_new(parser_ctx(parser), "#IMMEDIATE", TYPE_STRING);
-    out->constant = true;
+    out->cvq      = CV_CONST;
     out->hasvalue = true;
     out->constval.vstring = parser_strdup(str);
     vec_push(parser->imm_string, out);
@@ -283,7 +273,7 @@ static ast_value* parser_const_vector(parser_t *parser, vector v)
             return parser->imm_vector[i];
     }
     out = ast_value_new(parser_ctx(parser), "#IMMEDIATE", TYPE_VECTOR);
-    out->constant = true;
+    out->cvq      = CV_CONST;
     out->hasvalue = true;
     out->constval.vvec = v;
     vec_push(parser->imm_vector, out);
@@ -388,6 +378,7 @@ typedef struct
 #define SY_PAREN_EXPR '('
 #define SY_PAREN_FUNC 'f'
 #define SY_PAREN_INDEX '['
+#define SY_PAREN_TERNARY '?'
 
 static sy_elem syexp(lex_ctx ctx, ast_expression *v) {
     sy_elem e;
@@ -477,7 +468,33 @@ static bool rotate_entfield_array_index_nodes(ast_expression **out)
     return true;
 }
 
-static bool parser_sy_pop(parser_t *parser, shunt *sy)
+static bool immediate_is_true(lex_ctx ctx, ast_value *v)
+{
+    switch (v->expression.vtype) {
+        case TYPE_FLOAT:
+            return !!v->constval.vfloat;
+        case TYPE_INTEGER:
+            return !!v->constval.vint;
+        case TYPE_VECTOR:
+            if (OPTS_FLAG(CORRECT_LOGIC))
+                return v->constval.vvec.x &&
+                       v->constval.vvec.y &&
+                       v->constval.vvec.z;
+            else
+                return !!(v->constval.vvec.x);
+        case TYPE_STRING:
+            if (!v->constval.vstring)
+                return false;
+            if (v->constval.vstring && OPTS_FLAG(TRUE_EMPTY_STRINGS))
+                return true;
+            return !!v->constval.vstring[0];
+        default:
+            compile_error(ctx, "internal error: immediate_is_true on invalid type");
+            return !!v->constval.vfunc;
+    }
+}
+
+static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
 {
     const oper_info *op;
     lex_ctx ctx;
@@ -515,6 +532,10 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
 
     vec_shrinkby(sy->ops, 1);
 
+    /* op(:?) has no input and no output */
+    if (!op->operands)
+        return true;
+
     vec_shrinkby(sy->out, op->operands);
     for (i = 0; i < op->operands; ++i) {
         exprs[i]  = sy->out[vec_size(sy->out)+i].out;
@@ -531,7 +552,8 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
              (exprs[0]->expression.vtype != exprs[1]->expression.vtype || \
               exprs[0]->expression.vtype != T)
 #define CanConstFold1(A) \
-             (ast_istype((A), ast_value) && ((ast_value*)(A))->hasvalue && ((ast_value*)(A))->constant)
+             (ast_istype((A), ast_value) && ((ast_value*)(A))->hasvalue && (((ast_value*)(A))->cvq == CV_CONST) &&\
+              (A)->expression.vtype != TYPE_FUNCTION)
 #define CanConstFold(A, B) \
              (CanConstFold1(A) && CanConstFold1(B))
 #define ConstV(i) (asvalue[(i)]->constval.vvec)
@@ -578,22 +600,29 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
             out = (ast_expression*)ast_array_index_new(ctx, exprs[0], exprs[1]);
             if (rotate_entfield_array_index_nodes(&out))
             {
-                if (opts_standard != COMPILER_GMQCC) {
+#if 0
+                /* This is not broken in fteqcc anymore */
+                if (opts.standard != COMPILER_GMQCC) {
                     /* this error doesn't need to make us bail out */
                     (void)!parsewarning(parser, WARN_EXTENSIONS,
                                         "accessing array-field members of an entity without parenthesis\n"
                                         " -> this is an extension from -std=gmqcc");
                 }
+#endif
             }
             break;
 
         case opid1(','):
             if (blocks[0]) {
-                ast_block_add_expr(blocks[0], exprs[1]);
+                if (!ast_block_add_expr(blocks[0], exprs[1]))
+                    return false;
             } else {
                 blocks[0] = ast_block_new(ctx);
-                ast_block_add_expr(blocks[0], exprs[0]);
-                ast_block_add_expr(blocks[0], exprs[1]);
+                if (!ast_block_add_expr(blocks[0], exprs[0]) ||
+                    !ast_block_add_expr(blocks[0], exprs[1]))
+                {
+                    return false;
+                }
             }
             if (!ast_block_set_type(blocks[0], exprs[1]))
                 return false;
@@ -646,10 +675,17 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
                         out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_V, exprs[0]);
                     break;
                 case TYPE_STRING:
-                    if (CanConstFold1(exprs[0]))
-                        out = (ast_expression*)parser_const_float(parser, !ConstS(0) || !*ConstS(0));
-                    else
-                        out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_S, exprs[0]);
+                    if (CanConstFold1(exprs[0])) {
+                        if (OPTS_FLAG(TRUE_EMPTY_STRINGS))
+                            out = (ast_expression*)parser_const_float(parser, !ConstS(0));
+                        else
+                            out = (ast_expression*)parser_const_float(parser, !ConstS(0) || !*ConstS(0));
+                    } else {
+                        if (OPTS_FLAG(TRUE_EMPTY_STRINGS))
+                            out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, exprs[0]);
+                        else
+                            out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_S, exprs[0]);
+                    }
                     break;
                 /* we don't constant-fold NOT for these types */
                 case TYPE_ENTITY:
@@ -835,16 +871,57 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
                 return false;
             }
 #endif
-            if (opts_standard == COMPILER_GMQCC)
-                con_out("TODO: early out logic\n");
             if (CanConstFold(exprs[0], exprs[1]))
-                out = (ast_expression*)parser_const_float(parser,
-                    (generated_op == INSTR_OR ? (ConstF(0) || ConstF(1)) : (ConstF(0) && ConstF(1))));
+            {
+                if (OPTS_FLAG(PERL_LOGIC)) {
+                    if (immediate_is_true(ctx, asvalue[0]))
+                        out = exprs[1];
+                }
+                else
+                    out = (ast_expression*)parser_const_float(parser,
+                          ( (generated_op == INSTR_OR)
+                            ? (immediate_is_true(ctx, asvalue[0]) || immediate_is_true(ctx, asvalue[1]))
+                            : (immediate_is_true(ctx, asvalue[0]) && immediate_is_true(ctx, asvalue[1])) )
+                          ? 0 : 1);
+            }
             else
+            {
+                if (OPTS_FLAG(PERL_LOGIC) && !ast_compare_type(exprs[0], exprs[1])) {
+                    ast_type_to_string(exprs[0], ty1, sizeof(ty1));
+                    ast_type_to_string(exprs[1], ty2, sizeof(ty2));
+                    parseerror(parser, "invalid types for logical operation with -fperl-logic: %s and %s", ty1, ty2);
+                    return false;
+                }
+                if (OPTS_FLAG(CORRECT_LOGIC)) {
+                    /* non-floats need to be NOTed */
+                    for (i = 0; i < 2; ++i) {
+                        if (exprs[i]->expression.vtype != TYPE_FLOAT) {
+                            if (type_not_instr[exprs[i]->expression.vtype] == AINSTR_END) {
+                                ast_type_to_string(exprs[0], ty1, sizeof(ty1));
+                                ast_type_to_string(exprs[1], ty2, sizeof(ty2));
+                                parseerror(parser, "invalid types for logical operation with -fcorrect-logic: %s and %s", ty1, ty2);
+                                return false;
+                            }
+                            out = (ast_expression*)ast_unary_new(ctx, type_not_instr[exprs[i]->expression.vtype], exprs[i]);
+                            if (!out) break;
+                            out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, out);
+                            if (!out) break;
+                            exprs[i] = out; out = NULL;
+                            if (OPTS_FLAG(PERL_LOGIC)) {
+                            }
+                        }
+                    }
+                }
                 out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]);
+            }
             break;
 
         case opid2('?',':'):
+            if (vec_last(parser->pot) != POT_TERNARY2) {
+                parseerror(parser, "mismatched parenthesis/ternary");
+                return false;
+            }
+            vec_pop(parser->pot);
             if (exprs[1]->expression.vtype != exprs[2]->expression.vtype) {
                 ast_type_to_string(exprs[1], ty1, sizeof(ty1));
                 ast_type_to_string(exprs[2], ty2, sizeof(ty2));
@@ -903,7 +980,9 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
                 }
                 else
                     assignop = type_storep_instr[exprs[0]->expression.vtype];
-                if (!ast_compare_type(field->expression.next, exprs[1])) {
+                if (assignop == AINSTR_END ||
+                    !ast_compare_type(field->expression.next, exprs[1]))
+                {
                     ast_type_to_string(field->expression.next, ty1, sizeof(ty1));
                     ast_type_to_string(exprs[1], ty2, sizeof(ty2));
                     if (OPTS_FLAG(ASSIGN_FUNCTION_TYPES) &&
@@ -954,7 +1033,7 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
                         parseerror(parser, "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
                 }
             }
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->constant) {
+            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
                 parseerror(parser, "assignment to constant `%s`", asvalue[0]->name);
             }
             out = (ast_expression*)ast_store_new(ctx, assignop, exprs[0], exprs[1]);
@@ -971,7 +1050,7 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
                 addop = INSTR_ADD_F;
             else
                 addop = INSTR_SUB_F;
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->constant) {
+            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
                 parseerror(parser, "assignment to constant `%s`", asvalue[0]->name);
             }
             if (ast_istype(exprs[0], ast_entfield)) {
@@ -999,7 +1078,7 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
                 addop = INSTR_SUB_F;
                 subop = INSTR_ADD_F;
             }
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->constant) {
+            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
                 parseerror(parser, "assignment to constant `%s`", asvalue[0]->name);
             }
             if (ast_istype(exprs[0], ast_entfield)) {
@@ -1028,7 +1107,7 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
                            ty1, ty2);
                 return false;
             }
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->constant) {
+            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
                 parseerror(parser, "assignment to constant `%s`", asvalue[0]->name);
             }
             if (ast_istype(exprs[0], ast_entfield))
@@ -1065,7 +1144,7 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
                            ty1, ty2);
                 return false;
             }
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->constant) {
+            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
                 parseerror(parser, "assignment to constant `%s`", asvalue[0]->name);
             }
             if (ast_istype(exprs[0], ast_entfield))
@@ -1109,7 +1188,7 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
                            ty1, ty2);
                 return false;
             }
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->constant) {
+            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
                 parseerror(parser, "assignment to constant `%s`", asvalue[0]->name);
             }
             if (ast_istype(exprs[0], ast_entfield))
@@ -1139,7 +1218,7 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
             out = (ast_expression*)ast_binary_new(ctx, INSTR_BITAND, exprs[0], exprs[1]);
             if (!out)
                 return false;
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->constant) {
+            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
                 parseerror(parser, "assignment to constant `%s`", asvalue[0]->name);
             }
             asbinstore = ast_binstore_new(ctx, assignop, INSTR_SUB_F, exprs[0], out);
@@ -1185,9 +1264,25 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
 
     fun = sy->out[fid].out;
 
+    if (fun == intrinsic_debug_typestring) {
+        char ty[1024];
+        if (fid+2 != vec_size(sy->out) ||
+            vec_last(sy->out).block)
+        {
+            parseerror(parser, "intrinsic __builtin_debug_typestring requires exactly 1 parameter");
+            return false;
+        }
+        ast_type_to_string(vec_last(sy->out).out, ty, sizeof(ty));
+        ast_unref(vec_last(sy->out).out);
+        sy->out[fid] = syexp(ast_ctx(vec_last(sy->out).out),
+                             (ast_expression*)parser_const_string(parser, ty, false));
+        vec_shrinkby(sy->out, 1);
+        return true;
+    }
+
     call = ast_call_new(sy->ops[vec_size(sy->ops)].ctx, fun);
     if (!call) {
-        parseerror(parser, "out of memory");
+        parseerror(parser, "internal error: failed to create ast_call node");
         return false;
     }
 
@@ -1228,14 +1323,14 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
         return false;
     } else {
         if (vec_size(fun->expression.params) != paramcount &&
-            !(fun->expression.variadic &&
+            !((fun->expression.flags & AST_FLAG_VARIADIC) &&
               vec_size(fun->expression.params) < paramcount))
         {
             ast_value *fval;
             const char *fewmany = (vec_size(fun->expression.params) > paramcount) ? "few" : "many";
 
             fval = (ast_istype(fun, ast_value) ? ((ast_value*)fun) : NULL);
-            if (opts_standard == COMPILER_GMQCC)
+            if (opts.standard == COMPILER_GMQCC)
             {
                 if (fval)
                     parseerror(parser, "too %s parameters for call to %s: expected %i, got %i\n"
@@ -1244,9 +1339,9 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
                                fval->name, ast_ctx(fun).file, (int)ast_ctx(fun).line);
                 else
                     parseerror(parser, "too %s parameters for function call: expected %i, got %i\n"
-                               " -> `%s` has been declared here: %s:%i",
-                               fewmany, fval->name, (int)vec_size(fun->expression.params), (int)paramcount,
-                               fval->name, ast_ctx(fun).file, (int)ast_ctx(fun).line);
+                               " -> it has been declared here: %s:%i",
+                               fewmany, (int)vec_size(fun->expression.params), (int)paramcount,
+                               ast_ctx(fun).file, (int)ast_ctx(fun).line);
                 return false;
             }
             else
@@ -1260,9 +1355,9 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
                 else
                     return !parsewarning(parser, WARN_TOO_FEW_PARAMETERS,
                                          "too %s parameters for function call: expected %i, got %i\n"
-                                         " -> `%s` has been declared here: %s:%i",
-                                         fewmany, fval->name, (int)vec_size(fun->expression.params), (int)paramcount,
-                                         fval->name, ast_ctx(fun).file, (int)ast_ctx(fun).line);
+                                         " -> it has been declared here: %s:%i",
+                                         fewmany, (int)vec_size(fun->expression.params), (int)paramcount,
+                                         ast_ctx(fun).file, (int)ast_ctx(fun).line);
             }
         }
     }
@@ -1298,11 +1393,23 @@ static bool parser_close_paren(parser_t *parser, shunt *sy, bool functions_only)
             /* pop off the parenthesis */
             vec_shrinkby(sy->ops, 1);
             /* then apply the index operator */
-            if (!parser_sy_pop(parser, sy))
+            if (!parser_sy_apply_operator(parser, sy))
                 return false;
             return true;
         }
-        if (!parser_sy_pop(parser, sy))
+        if (sy->ops[vec_size(sy->ops)-1].paren == SY_PAREN_TERNARY) {
+            if (functions_only)
+                return false;
+            if (vec_last(parser->pot) != POT_TERNARY1) {
+                parseerror(parser, "mismatched colon in ternary expression (missing closing paren?)");
+                return false;
+            }
+            vec_last(parser->pot) = POT_TERNARY2;
+            /* pop off the parenthesis */
+            vec_shrinkby(sy->ops, 1);
+            return true;
+        }
+        if (!parser_sy_apply_operator(parser, sy))
             return false;
     }
     return true;
@@ -1388,7 +1495,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
             }
             wantop = true;
             /* variable */
-            if (opts_standard == COMPILER_GMQCC)
+            if (opts.standard == COMPILER_GMQCC)
             {
                 if (parser->memberof == TYPE_ENTITY) {
                     /* still get vars first since there could be a fieldpointer */
@@ -1413,11 +1520,28 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                     var = parser_find_field(parser, parser_tokval(parser));
             }
             if (!var) {
-                parseerror(parser, "unexpected ident: %s", parser_tokval(parser));
-                goto onerr;
+                /* intrinsics */
+                if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) {
+                    var = (ast_expression*)intrinsic_debug_typestring;
+
+                }
+                else
+                {
+                    parseerror(parser, "unexpected ident: %s", parser_tokval(parser));
+                    goto onerr;
+                }
+            }
+            else
+            {
+                if (ast_istype(var, ast_value)) {
+                    ((ast_value*)var)->uses++;
+                }
+                else if (ast_istype(var, ast_member)) {
+                    ast_member *mem = (ast_member*)var;
+                    if (ast_istype(mem->owner, ast_value))
+                        ((ast_value*)(mem->owner))->uses++;
+                }
             }
-            if (ast_istype(var, ast_value))
-                ((ast_value*)var)->uses++;
             vec_push(sy.out, syexp(parser_ctx(parser), var));
             DEBUGSHUNTDO(con_out("push %s\n", parser_tokval(parser)));
         }
@@ -1494,6 +1618,11 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 /* closing an opening paren */
                 if (!parser_close_paren(parser, &sy, false))
                     goto onerr;
+                if (vec_last(parser->pot) != POT_PAREN) {
+                    parseerror(parser, "mismatched parentheses (closing paren during ternary expression?)");
+                    goto onerr;
+                }
+                vec_pop(parser->pot);
             } else {
                 DEBUGSHUNTDO(con_out("do[nop] )\n"));
                 --parens;
@@ -1502,6 +1631,11 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 /* allowed for function calls */
                 if (!parser_close_paren(parser, &sy, true))
                     goto onerr;
+                if (vec_last(parser->pot) != POT_PAREN) {
+                    parseerror(parser, "mismatched parentheses (closing paren during ternary expression?)");
+                    goto onerr;
+                }
+                vec_pop(parser->pot);
             }
             wantop = true;
         }
@@ -1513,8 +1647,17 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 break;
             if (!parser_close_paren(parser, &sy, false))
                 goto onerr;
+            if (vec_last(parser->pot) != POT_PAREN) {
+                parseerror(parser, "mismatched parentheses (closing paren during ternary expression?)");
+                goto onerr;
+            }
+            vec_pop(parser->pot);
             wantop = true;
         }
+        else if (parser->tok == TOKEN_TYPENAME) {
+            parseerror(parser, "unexpected typename");
+            goto onerr;
+        }
         else if (parser->tok != TOKEN_OPERATOR) {
             if (wantop) {
                 parseerror(parser, "expected operator or end of statement");
@@ -1556,6 +1699,12 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 break;
             }
 
+            if (op->id == opid1(',')) {
+                if (vec_size(parser->pot) && vec_last(parser->pot) == POT_TERNARY2) {
+                    (void)!parsewarning(parser, WARN_TERNARY_PRECEDENCE, "suggesting parenthesis around ternary expression");
+                }
+            }
+
             if (vec_size(sy.ops) && !vec_last(sy.ops).paren)
                 olast = &operators[vec_last(sy.ops).etype-1];
 
@@ -1563,7 +1712,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                     (op->prec < olast->prec) ||
                     (op->assoc == ASSOC_LEFT && op->prec <= olast->prec) ) )
             {
-                if (!parser_sy_pop(parser, &sy))
+                if (!parser_sy_apply_operator(parser, &sy))
                     goto onerr;
                 if (vec_size(sy.ops) && !vec_last(sy.ops).paren)
                     olast = &operators[vec_last(sy.ops).etype-1];
@@ -1571,7 +1720,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                     olast = NULL;
             }
 
-            if (op->id == opid1('.') && opts_standard == COMPILER_GMQCC) {
+            if (op->id == opid1('.') && opts.standard == COMPILER_GMQCC) {
                 /* for gmqcc standard: open up the namespace of the previous type */
                 ast_expression *prevex = vec_last(sy.out).out;
                 if (!prevex) {
@@ -1593,11 +1742,11 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 if (wantop) {
                     size_t sycount = vec_size(sy.out);
                     DEBUGSHUNTDO(con_out("push [op] (\n"));
-                    ++parens;
+                    ++parens; vec_push(parser->pot, POT_PAREN);
                     /* we expected an operator, this is the function-call operator */
                     vec_push(sy.ops, syparen(parser_ctx(parser), SY_PAREN_FUNC, sycount-1));
                 } else {
-                    ++parens;
+                    ++parens; vec_push(parser->pot, POT_PAREN);
                     vec_push(sy.ops, syparen(parser_ctx(parser), SY_PAREN_EXPR, 0));
                     DEBUGSHUNTDO(con_out("push [nop] (\n"));
                 }
@@ -1607,20 +1756,31 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                     parseerror(parser, "unexpected array subscript");
                     goto onerr;
                 }
-                ++parens;
+                ++parens; vec_push(parser->pot, POT_PAREN);
                 /* push both the operator and the paren, this makes life easier */
                 vec_push(sy.ops, syop(parser_ctx(parser), op));
                 vec_push(sy.ops, syparen(parser_ctx(parser), SY_PAREN_INDEX, 0));
                 wantop = false;
             } else if (op->id == opid2('?',':')) {
-                wantop = false;
                 vec_push(sy.ops, syop(parser_ctx(parser), op));
+                vec_push(sy.ops, syparen(parser_ctx(parser), SY_PAREN_TERNARY, 0));
                 wantop = false;
-                --ternaries;
+                ++ternaries;
+                vec_push(parser->pot, POT_TERNARY1);
             } else if (op->id == opid2(':','?')) {
-                /* we don't push this operator */
+                if (!vec_size(parser->pot)) {
+                    parseerror(parser, "unexpected colon outside ternary expression (missing parenthesis?)");
+                    goto onerr;
+                }
+                if (vec_last(parser->pot) != POT_TERNARY1) {
+                    parseerror(parser, "unexpected colon outside ternary expression (missing parenthesis?)");
+                    goto onerr;
+                }
+                if (!parser_close_paren(parser, &sy, false))
+                    goto onerr;
+                vec_push(sy.ops, syop(parser_ctx(parser), op));
                 wantop = false;
-                ++ternaries;
+                --ternaries;
             } else {
                 DEBUGSHUNTDO(con_out("push operator %s\n", op->op));
                 vec_push(sy.ops, syop(parser_ctx(parser), op));
@@ -1638,7 +1798,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
     }
 
     while (vec_size(sy.ops)) {
-        if (!parser_sy_pop(parser, &sy))
+        if (!parser_sy_apply_operator(parser, &sy))
             goto onerr;
     }
 
@@ -1651,6 +1811,11 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
     vec_free(sy.out);
     vec_free(sy.ops);
     DEBUGSHUNTDO(con_out("shunt done\n"));
+    if (vec_size(parser->pot)) {
+        parseerror(parser, "internal error: vec_size(parser->pot) = %lu", (unsigned long)vec_size(parser->pot));
+        return NULL;
+    }
+    vec_free(parser->pot);
     return expr;
 
 onerr:
@@ -1678,6 +1843,7 @@ static void parser_enterblock(parser_t *parser)
     vec_push(parser->_blocklocals, vec_size(parser->_locals));
     vec_push(parser->typedefs, util_htnew(TYPEDEF_HT_SIZE));
     vec_push(parser->_blocktypedefs, vec_size(parser->_typedefs));
+    vec_push(parser->_block_ctx, parser_ctx(parser));
 }
 
 static bool parser_leaveblock(parser_t *parser)
@@ -1704,8 +1870,10 @@ static bool parser_leaveblock(parser_t *parser)
         ast_value      *v = (ast_value*)e;
         vec_pop(parser->_locals);
         if (ast_istype(e, ast_value) && !v->uses) {
-            if (parsewarning(parser, WARN_UNUSED_VARIABLE, "unused variable: `%s`", v->name))
+            if (compile_warning(ast_ctx(v), WARN_UNUSED_VARIABLE, "unused variable: `%s`", v->name)) {
+                parser->errors++;
                 rv = false;
+            }
         }
     }
 
@@ -1717,6 +1885,7 @@ static bool parser_leaveblock(parser_t *parser)
     util_htdel(vec_last(parser->typedefs));
     vec_pop(parser->typedefs);
 
+    vec_pop(parser->_block_ctx);
     return rv;
 }
 
@@ -1726,10 +1895,63 @@ static void parser_addlocal(parser_t *parser, const char *name, ast_expression *
     util_htset(vec_last(parser->variables), name, (void*)e);
 }
 
+static ast_expression* process_condition(parser_t *parser, ast_expression *cond, bool *_ifnot)
+{
+    bool       ifnot = false;
+    ast_unary *unary;
+    ast_expression *prev;
+
+    if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && cond->expression.vtype == TYPE_STRING) {
+        prev = cond;
+        cond = (ast_expression*)ast_unary_new(ast_ctx(cond), INSTR_NOT_S, cond);
+        if (!cond) {
+            ast_unref(prev);
+            parseerror(parser, "internal error: failed to process condition");
+            return NULL;
+        }
+        ifnot = !ifnot;
+    }
+    if (OPTS_FLAG(CORRECT_LOGIC) &&
+        !(cond->expression.vtype == TYPE_STRING && OPTS_FLAG(TRUE_EMPTY_STRINGS)))
+    {
+        /* non-floats need to use NOT; except for strings on -ftrue-empty-strings */
+        unary = (ast_unary*)cond;
+        if (!ast_istype(cond, ast_unary) || unary->op < INSTR_NOT_F || unary->op > INSTR_NOT_FNC)
+        {
+            /* use the right NOT_ */
+            prev = cond;
+            cond = (ast_expression*)ast_unary_new(ast_ctx(cond), type_not_instr[cond->expression.vtype], cond);
+            if (!cond) {
+                ast_unref(prev);
+                parseerror(parser, "internal error: failed to process condition");
+                return NULL;
+            }
+            ifnot = !ifnot;
+        }
+    }
+
+    unary = (ast_unary*)cond;
+    while (ast_istype(cond, ast_unary) && unary->op == INSTR_NOT_F)
+        /*&& unary->operand->expression.vtype != TYPE_STRING) */
+    {
+        cond = unary->operand;
+        unary->operand = NULL;
+        ast_delete(unary);
+        ifnot = !ifnot;
+        unary = (ast_unary*)cond;
+    }
+
+    if (!cond)
+        parseerror(parser, "internal error: failed to process condition");
+
+    if (ifnot) *_ifnot = !*_ifnot;
+    return cond;
+}
+
 static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out)
 {
     ast_ifthen *ifthen;
-    ast_expression *cond, *ontrue, *onfalse = NULL;
+    ast_expression *cond, *ontrue = NULL, *onfalse = NULL;
     bool ifnot = false;
 
     lex_ctx ctx = parser_ctx(parser);
@@ -1773,8 +1995,7 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out)
         ast_delete(cond);
         return false;
     }
-    ontrue = parse_statement_or_block(parser);
-    if (!ontrue) {
+    if (!parse_statement_or_block(parser, &ontrue)) {
         ast_delete(cond);
         return false;
     }
@@ -1787,14 +2008,20 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out)
             ast_delete(cond);
             return false;
         }
-        onfalse = parse_statement_or_block(parser);
-        if (!onfalse) {
+        if (!parse_statement_or_block(parser, &onfalse)) {
             ast_delete(ontrue);
             ast_delete(cond);
             return false;
         }
     }
 
+    cond = process_condition(parser, cond, &ifnot);
+    if (!cond) {
+        if (ontrue)  ast_delete(ontrue);
+        if (onfalse) ast_delete(onfalse);
+        return false;
+    }
+
     if (ifnot)
         ifthen = ast_ifthen_new(ctx, cond, onfalse, ontrue);
     else
@@ -1808,6 +2035,8 @@ static bool parse_while(parser_t *parser, ast_block *block, ast_expression **out
     ast_loop *aloop;
     ast_expression *cond, *ontrue;
 
+    bool ifnot = false;
+
     lex_ctx ctx = parser_ctx(parser);
 
     (void)block; /* not touching */
@@ -1838,13 +2067,17 @@ static bool parse_while(parser_t *parser, ast_block *block, ast_expression **out
         ast_delete(cond);
         return false;
     }
-    ontrue = parse_statement_or_block(parser);
-    if (!ontrue) {
+    if (!parse_statement_or_block(parser, &ontrue)) {
         ast_delete(cond);
         return false;
     }
 
-    aloop = ast_loop_new(ctx, NULL, cond, NULL, NULL, ontrue);
+    cond = process_condition(parser, cond, &ifnot);
+    if (!cond) {
+        ast_delete(ontrue);
+        return false;
+    }
+    aloop = ast_loop_new(ctx, NULL, cond, ifnot, NULL, false, NULL, ontrue);
     *out = (ast_expression*)aloop;
     return true;
 }
@@ -1854,6 +2087,8 @@ static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **o
     ast_loop *aloop;
     ast_expression *cond, *ontrue;
 
+    bool ifnot = false;
+
     lex_ctx ctx = parser_ctx(parser);
 
     (void)block; /* not touching */
@@ -1863,8 +2098,7 @@ static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **o
         parseerror(parser, "expected loop body");
         return false;
     }
-    ontrue = parse_statement_or_block(parser);
-    if (!ontrue)
+    if (!parse_statement_or_block(parser, &ontrue))
         return false;
 
     /* expect the "while" */
@@ -1914,7 +2148,12 @@ static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **o
         return false;
     }
 
-    aloop = ast_loop_new(ctx, NULL, NULL, cond, NULL, ontrue);
+    cond = process_condition(parser, cond, &ifnot);
+    if (!cond) {
+        ast_delete(ontrue);
+        return false;
+    }
+    aloop = ast_loop_new(ctx, NULL, NULL, false, cond, ifnot, NULL, ontrue);
     *out = (ast_expression*)aloop;
     return true;
 }
@@ -1924,7 +2163,9 @@ static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out)
     ast_loop       *aloop;
     ast_expression *initexpr, *cond, *increment, *ontrue;
     ast_value      *typevar;
-    bool   retval = true;
+
+    bool retval = true;
+    bool ifnot  = false;
 
     lex_ctx ctx = parser_ctx(parser);
 
@@ -1951,12 +2192,12 @@ static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out)
         typevar = parser_find_typedef(parser, parser_tokval(parser), 0);
 
     if (typevar || parser->tok == TOKEN_TYPENAME) {
-        if (opts_standard != COMPILER_GMQCC) {
+        if (opts.standard != COMPILER_GMQCC) {
             if (parsewarning(parser, WARN_EXTENSIONS,
                              "current standard does not allow variable declarations in for-loop initializers"))
                 goto onerr;
         }
-        if (!parse_variable(parser, block, true, CV_VAR, typevar))
+        if (!parse_variable(parser, block, true, CV_VAR, typevar, false, false))
             goto onerr;
     }
     else if (parser->tok != ';')
@@ -2014,12 +2255,15 @@ static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out)
         parseerror(parser, "expected for-loop body");
         goto onerr;
     }
-    ontrue = parse_statement_or_block(parser);
-    if (!ontrue) {
+    if (!parse_statement_or_block(parser, &ontrue))
         goto onerr;
-    }
 
-    aloop = ast_loop_new(ctx, initexpr, cond, NULL, increment, ontrue);
+    if (cond) {
+        cond = process_condition(parser, cond, &ifnot);
+        if (!cond)
+            goto onerr;
+    }
+    aloop = ast_loop_new(ctx, initexpr, cond, ifnot, NULL, false, increment, ontrue);
     *out = (ast_expression*)aloop;
 
     if (!parser_leaveblock(parser))
@@ -2039,6 +2283,8 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou
     ast_return     *ret = NULL;
     ast_value      *expected = parser->function->vtype;
 
+    lex_ctx ctx = parser_ctx(parser);
+
     (void)block; /* not touching */
 
     if (!parser_next(parser)) {
@@ -2055,7 +2301,7 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou
             parseerror(parser, "return with invalid expression");
         }
 
-        ret = ast_return_new(exp->expression.node.context, exp);
+        ret = ast_return_new(ctx, exp);
         if (!ret) {
             ast_delete(exp);
             return false;
@@ -2064,12 +2310,12 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou
         if (!parser_next(parser))
             parseerror(parser, "parse error");
         if (expected->expression.next->expression.vtype != TYPE_VOID) {
-            if (opts_standard != COMPILER_GMQCC)
+            if (opts.standard != COMPILER_GMQCC)
                 (void)!parsewarning(parser, WARN_MISSING_RETURN_VALUES, "return without value");
             else
                 parseerror(parser, "return without value");
         }
-        ret = ast_return_new(parser_ctx(parser), NULL);
+        ret = ast_return_new(ctx, NULL);
     }
     *out = (ast_expression*)ret;
     return true;
@@ -2093,13 +2339,61 @@ static bool parse_break_continue(parser_t *parser, ast_block *block, ast_express
     return true;
 }
 
+/* returns true when it was a variable qualifier, false otherwise!
+ * on error, cvq is set to CV_WRONG
+ */
+static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *noref, bool *noreturn)
+{
+    bool had_const = false;
+    bool had_var   = false;
+    bool had_noref = false;
+    bool had_noreturn = false;
+
+    for (;;) {
+        if (!strcmp(parser_tokval(parser), "const"))
+            had_const = true;
+        else if (!strcmp(parser_tokval(parser), "var"))
+            had_var = true;
+        else if (with_local && !strcmp(parser_tokval(parser), "local"))
+            had_var = true;
+        else if (!strcmp(parser_tokval(parser), "noref"))
+            had_noref = true;
+        else if (!strcmp(parser_tokval(parser), "noreturn"))
+            had_noreturn = true;
+        else if (!had_const && !had_var && !had_noref && !had_noreturn) {
+            return false;
+        }
+        else
+            break;
+        if (!parser_next(parser))
+            goto onerr;
+    }
+    if (had_const)
+        *cvq = CV_CONST;
+    else if (had_var)
+        *cvq = CV_VAR;
+    else
+        *cvq = CV_NONE;
+    *noref    = had_noref;
+    *noreturn = had_noreturn;
+    return true;
+onerr:
+    parseerror(parser, "parse error after variable qualifier");
+    *cvq = CV_WRONG;
+    return true;
+}
+
 static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **out)
 {
     ast_expression *operand;
     ast_value      *opval;
+    ast_value      *typevar;
     ast_switch     *switchnode;
     ast_switch_case swcase;
 
+    int  cvq;
+    bool noref, noreturn;
+
     lex_ctx ctx = parser_ctx(parser);
 
     (void)block; /* not touching */
@@ -2143,6 +2437,34 @@ static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **ou
         return false;
     }
 
+    /* new block; allow some variables to be declared here */
+    parser_enterblock(parser);
+    while (true) {
+        typevar = NULL;
+        if (parser->tok == TOKEN_IDENT)
+            typevar = parser_find_typedef(parser, parser_tokval(parser), 0);
+        if (typevar || parser->tok == TOKEN_TYPENAME) {
+            if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false)) {
+                ast_delete(switchnode);
+                return false;
+            }
+            continue;
+        }
+        if (parse_var_qualifiers(parser, true, &cvq, &noref, &noreturn))
+        {
+            if (cvq == CV_WRONG) {
+                ast_delete(switchnode);
+                return false;
+            }
+            if (!parse_variable(parser, block, false, cvq, NULL, noref, noreturn)) {
+                ast_delete(switchnode);
+                return false;
+            }
+            continue;
+        }
+        break;
+    }
+
     /* case list! */
     while (parser->tok != '}') {
         ast_block *caseblock;
@@ -2165,8 +2487,7 @@ static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **ou
                 return false;
             }
             if (!OPTS_FLAG(RELAXED_SWITCH)) {
-                opval = (ast_value*)swcase.value;
-                if (!ast_istype(swcase.value, ast_value)) { /* || !opval->constant) { */
+                if (!ast_istype(swcase.value, ast_value)) { /* || ((ast_value*)swcase.value)->cvq != CV_CONST) { */
                     parseerror(parser, "case on non-constant values need to be explicitly enabled via -frelaxed-switch");
                     ast_unref(operand);
                     return false;
@@ -2221,10 +2542,15 @@ static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **ou
             }
             if (!expr)
                 continue;
-            ast_block_add_expr(caseblock, expr);
+            if (!ast_block_add_expr(caseblock, expr)) {
+                ast_delete(switchnode);
+                return false;
+            }
         }
     }
 
+    parser_leaveblock(parser);
+
     /* closing paren */
     if (parser->tok != '}') {
         ast_delete(switchnode);
@@ -2274,10 +2600,81 @@ static bool parse_goto(parser_t *parser, ast_expression **out)
     return true;
 }
 
+static bool parse_skipwhite(parser_t *parser)
+{
+    do {
+        if (!parser_next(parser))
+            return false;
+    } while (parser->tok == TOKEN_WHITE && parser->tok < TOKEN_ERROR);
+    return parser->tok < TOKEN_ERROR;
+}
+
+static bool parse_eol(parser_t *parser)
+{
+    if (!parse_skipwhite(parser))
+        return false;
+    return parser->tok == TOKEN_EOL;
+}
+
+static bool parse_pragma_do(parser_t *parser)
+{
+    if (!parser_next(parser) ||
+        parser->tok != TOKEN_IDENT ||
+        strcmp(parser_tokval(parser), "pragma"))
+    {
+        parseerror(parser, "expected `pragma` keyword after `#`, got `%s`", parser_tokval(parser));
+        return false;
+    }
+    if (!parse_skipwhite(parser) || parser->tok != TOKEN_IDENT) {
+        parseerror(parser, "expected pragma, got `%s`", parser_tokval(parser));
+        return false;
+    }
+
+    if (!strcmp(parser_tokval(parser), "noref")) {
+        if (!parse_skipwhite(parser) || parser->tok != TOKEN_INTCONST) {
+            parseerror(parser, "`noref` pragma requires an argument: 0 or 1");
+            return false;
+        }
+        parser->noref = !!parser_token(parser)->constval.i;
+        if (!parse_eol(parser)) {
+            parseerror(parser, "parse error after `noref` pragma");
+            return false;
+        }
+    }
+    else
+    {
+        (void)!parsewarning(parser, WARN_UNKNOWN_PRAGMAS, "ignoring #pragma %s", parser_tokval(parser));
+        return false;
+    }
+
+    return true;
+}
+
+static bool parse_pragma(parser_t *parser)
+{
+    bool rv;
+    parser->lex->flags.preprocessing = true;
+    parser->lex->flags.mergelines = true;
+    rv = parse_pragma_do(parser);
+    if (parser->tok != TOKEN_EOL) {
+        parseerror(parser, "junk after pragma");
+        rv = false;
+    }
+    parser->lex->flags.preprocessing = false;
+    parser->lex->flags.mergelines = false;
+    if (!parser_next(parser)) {
+        parseerror(parser, "parse error after pragma");
+        rv = false;
+    }
+    return rv;
+}
+
 static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases)
 {
-    int cvq;
+    bool       noref, noreturn;
+    int        cvq = CV_NONE;
     ast_value *typevar = NULL;
+
     *out = NULL;
 
     if (parser->tok == TOKEN_IDENT)
@@ -2290,47 +2687,23 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
             parseerror(parser, "cannot declare a variable from here");
             return false;
         }
-        if (opts_standard == COMPILER_QCC) {
+        if (opts.standard == COMPILER_QCC) {
             if (parsewarning(parser, WARN_EXTENSIONS, "missing 'local' keyword when declaring a local variable"))
                 return false;
         }
-        if (!parse_variable(parser, block, false, CV_NONE, typevar))
+        if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false))
             return false;
-        *out = NULL;
         return true;
     }
-    else if (parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "var"))
+    else if (parse_var_qualifiers(parser, !!block, &cvq, &noref, &noreturn))
     {
-        goto ident_var;
+        if (cvq == CV_WRONG)
+            return false;
+        return parse_variable(parser, block, true, cvq, NULL, noref, noreturn);
     }
     else if (parser->tok == TOKEN_KEYWORD)
     {
-        if (!strcmp(parser_tokval(parser), "local") ||
-            !strcmp(parser_tokval(parser), "const") ||
-            !strcmp(parser_tokval(parser), "var"))
-        {
-ident_var:
-            if (parser_tokval(parser)[0] == 'c')
-                cvq = CV_CONST;
-            else if (parser_tokval(parser)[0] == 'v')
-                cvq = CV_VAR;
-            else
-                cvq = CV_NONE;
-
-            if (!block) {
-                parseerror(parser, "cannot declare a local variable here");
-                return false;
-            }
-            if (!parser_next(parser)) {
-                parseerror(parser, "expected variable declaration");
-                return false;
-            }
-            if (!parse_variable(parser, block, true, cvq, NULL))
-                return false;
-            *out = NULL;
-            return true;
-        }
-        else if (!strcmp(parser_tokval(parser), "__builtin_debug_printtype"))
+        if (!strcmp(parser_tokval(parser), "__builtin_debug_printtype"))
         {
             char ty[1024];
             ast_value *tdef;
@@ -2381,7 +2754,7 @@ ident_var:
         }
         else if (!strcmp(parser_tokval(parser), "for"))
         {
-            if (opts_standard == COMPILER_QCC) {
+            if (opts.standard == COMPILER_QCC) {
                 if (parsewarning(parser, WARN_EXTENSIONS, "for loops are not recognized in the original Quake C standard, to enable try an alternate standard --std=?"))
                     return false;
             }
@@ -2426,7 +2799,7 @@ ident_var:
     else if (parser->tok == '{')
     {
         ast_block *inner;
-        inner = parse_block(parser, false);
+        inner = parse_block(parser);
         if (!inner)
             return false;
         *out = (ast_expression*)inner;
@@ -2484,7 +2857,7 @@ ident_var:
     }
 }
 
-static bool parse_block_into(parser_t *parser, ast_block *block, bool warnreturn)
+static bool parse_block_into(parser_t *parser, ast_block *block)
 {
     bool   retval = true;
 
@@ -2508,23 +2881,16 @@ static bool parse_block_into(parser_t *parser, ast_block *block, bool warnreturn
         }
         if (!expr)
             continue;
-        ast_block_add_expr(block, expr);
+        if (!ast_block_add_expr(block, expr)) {
+            ast_delete(block);
+            block = NULL;
+            goto cleanup;
+        }
     }
 
     if (parser->tok != '}') {
         block = NULL;
     } else {
-        if (warnreturn && parser->function->vtype->expression.next->expression.vtype != TYPE_VOID)
-        {
-            if (!vec_size(block->exprs) ||
-                !ast_istype(vec_last(block->exprs), ast_return))
-            {
-                if (parsewarning(parser, WARN_MISSING_RETURN_VALUES, "control reaches end of non-void function")) {
-                    block = NULL;
-                    goto cleanup;
-                }
-            }
-        }
         (void)parser_next(parser);
     }
 
@@ -2534,27 +2900,26 @@ cleanup:
     return retval && !!block;
 }
 
-static ast_block* parse_block(parser_t *parser, bool warnreturn)
+static ast_block* parse_block(parser_t *parser)
 {
     ast_block *block;
     block = ast_block_new(parser_ctx(parser));
     if (!block)
         return NULL;
-    if (!parse_block_into(parser, block, warnreturn)) {
+    if (!parse_block_into(parser, block)) {
         ast_block_delete(block);
         return NULL;
     }
     return block;
 }
 
-static ast_expression* parse_statement_or_block(parser_t *parser)
+static bool parse_statement_or_block(parser_t *parser, ast_expression **out)
 {
-    ast_expression *expr = NULL;
-    if (parser->tok == '{')
-        return (ast_expression*)parse_block(parser, false);
-    if (!parse_statement(parser, NULL, &expr, false))
-        return NULL;
-    return expr;
+    if (parser->tok == '{') {
+        *out = (ast_expression*)parse_block(parser);
+        return !!*out;
+    }
+    return parse_statement(parser, NULL, out, false);
 }
 
 static bool create_vector_members(ast_value *var, ast_member **me)
@@ -2605,7 +2970,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
         return false;
     }
 
-    if (var->expression.variadic) {
+    if (var->expression.flags & AST_FLAG_VARIADIC) {
         if (parsewarning(parser, WARN_VARIADIC_FUNCTION,
                          "variadic function with implementation will not be able to access additional parameters"))
         {
@@ -2785,9 +3150,12 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
                 if (store_think)     ast_delete(store_think);
                 retval = false;
             }
-            ast_block_add_expr(block, (ast_expression*)store_frame);
-            ast_block_add_expr(block, (ast_expression*)store_nextthink);
-            ast_block_add_expr(block, (ast_expression*)store_think);
+            if (!ast_block_add_expr(block, (ast_expression*)store_frame) ||
+                !ast_block_add_expr(block, (ast_expression*)store_nextthink) ||
+                !ast_block_add_expr(block, (ast_expression*)store_think))
+            {
+                retval = false;
+            }
         }
 
         if (!retval) {
@@ -2833,7 +3201,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
     vec_push(parser->functions, func);
 
     parser->function = func;
-    if (!parse_block_into(parser, block, true)) {
+    if (!parse_block_into(parser, block)) {
         ast_block_delete(block);
         goto enderrfn;
     }
@@ -2850,7 +3218,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
 
     if (parser->tok == ';')
         return parser_next(parser);
-    else if (opts_standard == COMPILER_QCC)
+    else if (opts.standard == COMPILER_QCC)
         parseerror(parser, "missing semicolon after function body (mandatory with -std=qcc)");
     return retval;
 
@@ -2936,7 +3304,10 @@ static ast_expression *array_setter_node(parser_t *parser, ast_value *array, ast
             return NULL;
         }
 
-        ast_block_add_expr(block, (ast_expression*)st);
+        if (!ast_block_add_expr(block, (ast_expression*)st)) {
+            ast_delete(block);
+            return NULL;
+        }
 
         ret = ast_return_new(ctx, NULL);
         if (!ret) {
@@ -2944,7 +3315,10 @@ static ast_expression *array_setter_node(parser_t *parser, ast_value *array, ast
             return NULL;
         }
 
-        ast_block_add_expr(block, (ast_expression*)ret);
+        if (!ast_block_add_expr(block, (ast_expression*)ret)) {
+            ast_delete(block);
+            return NULL;
+        }
 
         return (ast_expression*)block;
     } else {
@@ -3005,7 +3379,10 @@ static ast_expression *array_field_setter_node(
             return NULL;
         }
 
-        ast_block_add_expr(block, (ast_expression*)st);
+        if (!ast_block_add_expr(block, (ast_expression*)st)) {
+            ast_delete(block);
+            return NULL;
+        }
 
         ret = ast_return_new(ctx, NULL);
         if (!ret) {
@@ -3013,7 +3390,10 @@ static ast_expression *array_field_setter_node(
             return NULL;
         }
 
-        ast_block_add_expr(block, (ast_expression*)ret);
+        if (!ast_block_add_expr(block, (ast_expression*)ret)) {
+            ast_delete(block);
+            return NULL;
+        }
 
         return (ast_expression*)block;
     } else {
@@ -3125,9 +3505,8 @@ static bool parser_create_array_setter(parser_t *parser, ast_value *array, const
         goto cleanup;
     }
 
-    ast_block_add_expr(func->blocks[0], root);
     array->setter = fval;
-    return true;
+    return ast_block_add_expr(func->blocks[0], root);
 cleanup:
     if (index) ast_delete(index);
     if (value) ast_delete(value);
@@ -3174,9 +3553,8 @@ static bool parser_create_array_field_setter(parser_t *parser, ast_value *array,
         goto cleanup;
     }
 
-    ast_block_add_expr(func->blocks[0], root);
     array->setter = fval;
-    return true;
+    return ast_block_add_expr(func->blocks[0], root);
 cleanup:
     if (entity) ast_delete(entity);
     if (index)  ast_delete(index);
@@ -3221,9 +3599,8 @@ static bool parser_create_array_getter(parser_t *parser, ast_value *array, const
         goto cleanup;
     }
 
-    ast_block_add_expr(func->blocks[0], root);
     array->getter = fval;
-    return true;
+    return ast_block_add_expr(func->blocks[0], root);
 cleanup:
     if (index) ast_delete(index);
     if (root)  ast_delete(root);
@@ -3300,7 +3677,7 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
         vec_free(params);
 
     /* sanity check */
-    if (vec_size(params) > 8 && opts_standard == COMPILER_QCC)
+    if (vec_size(params) > 8 && opts.standard == COMPILER_QCC)
         (void)!parsewarning(parser, WARN_EXTENSIONS, "more than 8 parameters are not supported by this standard");
 
     /* parse-out */
@@ -3312,7 +3689,8 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
     /* now turn 'var' into a function type */
     fval = ast_value_new(ctx, "<type()>", TYPE_FUNCTION);
     fval->expression.next     = (ast_expression*)var;
-    fval->expression.variadic = variadic;
+    if (variadic)
+        fval->expression.flags |= AST_FLAG_VARIADIC;
     var = fval;
 
     var->expression.params = params;
@@ -3509,7 +3887,7 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va
     }
 
     /* now there may be function parens again */
-    if (parser->tok == '(' && opts_standard == COMPILER_QCC)
+    if (parser->tok == '(' && opts.standard == COMPILER_QCC)
         parseerror(parser, "C-style function syntax is not allowed in -std=qcc");
     if (parser->tok == '(' && wasarray)
         parseerror(parser, "arrays as part of a return type is not supported");
@@ -3577,7 +3955,7 @@ static bool parse_typedef(parser_t *parser)
     return true;
 }
 
-static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int is_const_var, ast_value *cached_typedef)
+static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool noreturn)
 {
     ast_value *var;
     ast_value *proto;
@@ -3608,7 +3986,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
 
         /* Part 0: finish the type */
         if (parser->tok == '(') {
-            if (opts_standard == COMPILER_QCC)
+            if (opts.standard == COMPILER_QCC)
                 parseerror(parser, "C-style function syntax is not allowed in -std=qcc");
             var = parse_parameter_list(parser, var);
             if (!var) {
@@ -3631,7 +4009,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
         }
         /* for functions returning functions */
         while (parser->tok == '(') {
-            if (opts_standard == COMPILER_QCC)
+            if (opts.standard == COMPILER_QCC)
                 parseerror(parser, "C-style function syntax is not allowed in -std=qcc");
             var = parse_parameter_list(parser, var);
             if (!var) {
@@ -3640,8 +4018,12 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
             }
         }
 
-        if (is_const_var == CV_CONST)
-            var->constant = true;
+        var->cvq = qualifier;
+        /* in a noref section we simply bump the usecount */
+        if (noref || parser->noref)
+            var->uses++;
+        if (noreturn)
+            var->expression.flags |= AST_FLAG_NORETURN;
 
         /* Part 1:
          * check for validity: (end_sys_..., multiple-definitions, prototypes, ...)
@@ -3652,10 +4034,12 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
             /* Deal with end_sys_ vars */
             was_end = false;
             if (!strcmp(var->name, "end_sys_globals")) {
+                var->uses++;
                 parser->crc_globals = vec_size(parser->globals);
                 was_end = true;
             }
             else if (!strcmp(var->name, "end_sys_fields")) {
+                var->uses++;
                 parser->crc_fields = vec_size(parser->fields);
                 was_end = true;
             }
@@ -3690,7 +4074,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                     goto cleanup;
                     */
                 }
-                if (opts_standard == COMPILER_QCC &&
+                if (opts.standard == COMPILER_QCC &&
                     (old = parser_find_global(parser, var->name)))
                 {
                     parseerror(parser, "cannot declare a field and a global of the same name with -std=qcc");
@@ -3730,7 +4114,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                 {
                     /* other globals */
                     if (old) {
-                        if (opts_standard == COMPILER_GMQCC) {
+                        if (opts.standard == COMPILER_GMQCC) {
                             parseerror(parser, "global `%s` already declared here: %s:%i",
                                        var->name, ast_ctx(old).file, ast_ctx(old).line);
                             retval = false;
@@ -3754,7 +4138,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                             var = proto;
                         }
                     }
-                    if (opts_standard == COMPILER_QCC &&
+                    if (opts.standard == COMPILER_QCC &&
                         (old = parser_find_field(parser, var->name)))
                     {
                         parseerror(parser, "cannot declare a field and a global of the same name with -std=qcc");
@@ -3785,7 +4169,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                     retval = false;
                     goto cleanup;
                 }
-                if (opts_standard != COMPILER_GMQCC) {
+                if (opts.standard != COMPILER_GMQCC) {
                     ast_delete(var);
                     var = NULL;
                     goto skipvar;
@@ -3812,7 +4196,8 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
 
             if (!localblock) {
                 /* deal with global variables, fields, functions */
-                if (!nofields && var->expression.vtype == TYPE_FIELD) {
+                if (!nofields && var->expression.vtype == TYPE_FIELD && parser->tok != '=') {
+                    var->isfield = true;
                     vec_push(parser->fields, (ast_expression*)var);
                     util_htset(parser->htfields, var->name, var);
                     if (isvector) {
@@ -3900,12 +4285,15 @@ skipvar:
         if (parser->tok == ',')
             goto another;
 
+        /*
         if (!var || (!localblock && !nofields && basetype->expression.vtype == TYPE_FIELD)) {
+        */
+        if (!var) {
             parseerror(parser, "missing comma or semicolon while parsing variables");
             break;
         }
 
-        if (localblock && opts_standard == COMPILER_QCC) {
+        if (localblock && opts.standard == COMPILER_QCC) {
             if (parsewarning(parser, WARN_LOCAL_CONSTANTS,
                              "initializing expression turns variable `%s` into a constant in this standard",
                              var->name) )
@@ -3925,7 +4313,7 @@ skipvar:
                 break;
             }
         }
-        else if (opts_standard == COMPILER_QCC) {
+        else if (opts.standard == COMPILER_QCC) {
             parseerror(parser, "expected '=' before function body in this standard");
         }
 
@@ -3948,7 +4336,7 @@ skipvar:
                 parseerror(parser, "builtin number must be an integer constant");
                 break;
             }
-            if (parser_token(parser)->constval.i <= 0) {
+            if (parser_token(parser)->constval.i < 0) {
                 parseerror(parser, "builtin number must be an integer greater than zero");
                 break;
             }
@@ -3968,7 +4356,7 @@ skipvar:
                 }
                 vec_push(parser->functions, func);
 
-                func->builtin = -parser_token(parser)->constval.i;
+                func->builtin = -parser_token(parser)->constval.i-1;
             }
 
             if (!parser_next(parser)) {
@@ -3987,6 +4375,9 @@ skipvar:
                 break;
             }
 
+            if (proto)
+                ast_ctx(proto) = parser_ctx(parser);
+
             if (!parse_function_body(parser, var))
                 break;
             ast_delete(basetype);
@@ -4005,19 +4396,21 @@ skipvar:
 
             if (!localblock) {
                 cval = (ast_value*)cexp;
-                if (!ast_istype(cval, ast_value) || !cval->hasvalue || !cval->constant)
+                if (!ast_istype(cval, ast_value) || ((!cval->hasvalue || cval->cvq != CV_CONST) && !cval->isfield))
                     parseerror(parser, "cannot initialize a global constant variable with a non-constant expression");
                 else
                 {
-                    if (opts_standard != COMPILER_GMQCC &&
+                    if (opts.standard != COMPILER_GMQCC &&
                         !OPTS_FLAG(INITIALIZED_NONCONSTANTS) &&
-                        is_const_var != CV_VAR)
+                        qualifier != CV_VAR)
                     {
-                        var->constant = true;
+                        var->cvq = CV_CONST;
                     }
                     var->hasvalue = true;
                     if (cval->expression.vtype == TYPE_STRING)
                         var->constval.vstring = parser_strdup(cval->constval.vstring);
+                    else if (cval->expression.vtype == TYPE_FIELD)
+                        var->constval.vfield = cval;
                     else
                         memcpy(&var->constval, &cval->constval, sizeof(var->constval));
                     ast_unref(cval);
@@ -4025,21 +4418,22 @@ skipvar:
             } else {
                 bool cvq;
                 shunt sy = { NULL, NULL };
-                cvq = var->constant;
-                var->constant = false;
+                cvq = var->cvq;
+                var->cvq = CV_NONE;
                 vec_push(sy.out, syexp(ast_ctx(var), (ast_expression*)var));
                 vec_push(sy.out, syexp(ast_ctx(cexp), (ast_expression*)cexp));
                 vec_push(sy.ops, syop(ast_ctx(var), parser->assign_op));
-                if (!parser_sy_pop(parser, &sy))
+                if (!parser_sy_apply_operator(parser, &sy))
                     ast_unref(cexp);
                 else {
                     if (vec_size(sy.out) != 1 && vec_size(sy.ops) != 0)
                         parseerror(parser, "internal error: leaked operands");
-                    ast_block_add_expr(localblock, (ast_expression*)sy.out[0].out);
+                    if (!ast_block_add_expr(localblock, (ast_expression*)sy.out[0].out))
+                        break;
                 }
                 vec_free(sy.out);
                 vec_free(sy.ops);
-                var->constant = cvq;
+                var->cvq = cvq;
             }
         }
 
@@ -4095,41 +4489,27 @@ cleanup:
 
 static bool parser_global_statement(parser_t *parser)
 {
-    ast_value *istype = NULL;
+    int        cvq      = CV_WRONG;
+    bool       noref    = false;
+    bool       noreturn = false;
+    ast_value *istype   = NULL;
+
     if (parser->tok == TOKEN_IDENT)
         istype = parser_find_typedef(parser, parser_tokval(parser), 0);
 
     if (istype || parser->tok == TOKEN_TYPENAME || parser->tok == '.')
     {
-        return parse_variable(parser, NULL, false, CV_NONE, istype);
+        return parse_variable(parser, NULL, false, CV_NONE, istype, false, false);
     }
-    else if (parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "var"))
+    else if (parse_var_qualifiers(parser, false, &cvq, &noref, &noreturn))
     {
-        if (!strcmp(parser_tokval(parser), "var")) {
-            if (!parser_next(parser)) {
-                parseerror(parser, "expected variable declaration after 'var'");
-                return false;
-            }
-            return parse_variable(parser, NULL, true, CV_VAR, NULL);
-        }
+        if (cvq == CV_WRONG)
+            return false;
+        return parse_variable(parser, NULL, true, cvq, NULL, noref, noreturn);
     }
     else if (parser->tok == TOKEN_KEYWORD)
     {
-        if (!strcmp(parser_tokval(parser), "const")) {
-            if (!parser_next(parser)) {
-                parseerror(parser, "expected variable declaration after 'const'");
-                return false;
-            }
-            if (parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "var")) {
-                (void)!parsewarning(parser, WARN_CONST_VAR, "ignoring `var` after const qualifier");
-                if (!parser_next(parser)) {
-                    parseerror(parser, "expected variable declaration after 'const var'");
-                    return false;
-                }
-            }
-            return parse_variable(parser, NULL, true, CV_CONST, NULL);
-        }
-        else if (!strcmp(parser_tokval(parser), "typedef")) {
+        if (!strcmp(parser_tokval(parser), "typedef")) {
             if (!parser_next(parser)) {
                 parseerror(parser, "expected type definition after 'typedef'");
                 return false;
@@ -4139,6 +4519,10 @@ static bool parser_global_statement(parser_t *parser)
         parseerror(parser, "unrecognized keyword `%s`", parser_tokval(parser));
         return false;
     }
+    else if (parser->tok == '#')
+    {
+        return parse_pragma(parser);
+    }
     else if (parser->tok == '$')
     {
         if (!parser_next(parser)) {
@@ -4371,6 +4755,8 @@ void parser_cleanup()
     vec_free(parser->typedefs);
     vec_free(parser->_blocktypedefs);
 
+    vec_free(parser->_block_ctx);
+
     vec_free(parser->labels);
     vec_free(parser->gotos);
 
@@ -4423,12 +4809,8 @@ bool parser_finish(const char *output)
                 continue;
             asvalue = (ast_value*)(parser->globals[i]);
             if (!asvalue->uses && !asvalue->hasvalue && asvalue->expression.vtype != TYPE_FUNCTION) {
-                if (strcmp(asvalue->name, "end_sys_globals") &&
-                    strcmp(asvalue->name, "end_sys_fields"))
-                {
-                    retval = retval && !genwarning(ast_ctx(asvalue), WARN_UNUSED_VARIABLE,
-                                                   "unused global: `%s`", asvalue->name);
-                }
+                retval = retval && !genwarning(ast_ctx(asvalue), WARN_UNUSED_VARIABLE,
+                                               "unused global: `%s`", asvalue->name);
             }
             if (!ast_global_codegen(asvalue, ir, false)) {
                 con_out("failed to generate global %s\n", asvalue->name);
@@ -4462,25 +4844,9 @@ bool parser_finish(const char *output)
             if (!ast_istype(parser->globals[i], ast_value))
                 continue;
             asvalue = (ast_value*)(parser->globals[i]);
-            if (asvalue->setter) {
-                if (!ast_global_codegen(asvalue->setter, ir, false) ||
-                    !ast_function_codegen(asvalue->setter->constval.vfunc, ir) ||
-                    !ir_function_finalize(asvalue->setter->constval.vfunc->ir_func))
-                {
-                    printf("failed to generate setter for %s\n", asvalue->name);
-                    ir_builder_delete(ir);
-                    return false;
-                }
-            }
-            if (asvalue->getter) {
-                if (!ast_global_codegen(asvalue->getter, ir, false) ||
-                    !ast_function_codegen(asvalue->getter->constval.vfunc, ir) ||
-                    !ir_function_finalize(asvalue->getter->constval.vfunc->ir_func))
-                {
-                    printf("failed to generate getter for %s\n", asvalue->name);
-                    ir_builder_delete(ir);
-                    return false;
-                }
+            if (!ast_generate_accessors(asvalue, ir)) {
+                ir_builder_delete(ir);
+                return false;
             }
         }
         for (i = 0; i < vec_size(parser->fields); ++i) {
@@ -4491,25 +4857,9 @@ bool parser_finish(const char *output)
                 continue;
             if (asvalue->expression.vtype != TYPE_ARRAY)
                 continue;
-            if (asvalue->setter) {
-                if (!ast_global_codegen(asvalue->setter, ir, false) ||
-                    !ast_function_codegen(asvalue->setter->constval.vfunc, ir) ||
-                    !ir_function_finalize(asvalue->setter->constval.vfunc->ir_func))
-                {
-                    printf("failed to generate setter for %s\n", asvalue->name);
-                    ir_builder_delete(ir);
-                    return false;
-                }
-            }
-            if (asvalue->getter) {
-                if (!ast_global_codegen(asvalue->getter, ir, false) ||
-                    !ast_function_codegen(asvalue->getter->constval.vfunc, ir) ||
-                    !ir_function_finalize(asvalue->getter->constval.vfunc->ir_func))
-                {
-                    printf("failed to generate getter for %s\n", asvalue->name);
-                    ir_builder_delete(ir);
-                    return false;
-                }
+            if (!ast_generate_accessors(asvalue, ir)) {
+                ir_builder_delete(ir);
+                return false;
             }
         }
         for (i = 0; i < vec_size(parser->functions); ++i) {
@@ -4519,7 +4869,7 @@ bool parser_finish(const char *output)
                 return false;
             }
         }
-        if (opts_dump)
+        if (opts.dump)
             ir_builder_dump(ir, con_out);
         for (i = 0; i < vec_size(parser->functions); ++i) {
             if (!ir_function_finalize(parser->functions[i]->ir_func)) {
@@ -4530,7 +4880,7 @@ bool parser_finish(const char *output)
         }
 
         if (retval) {
-            if (opts_dumpfin)
+            if (opts.dumpfin)
                 ir_builder_dump(ir, con_out);
 
             generate_checksum(parser);