]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
ast_return needs the correct context...
[xonotic/gmqcc.git] / parser.c
index d0340dd5b97ee8b1a129c0b208b9774423e16cbb..8d8e5a432baa030c60dbd836b9ff6cb9a6225beb 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -98,11 +98,13 @@ typedef struct {
     bool noref;
 } parser_t;
 
+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 qualifier, ast_value *cached_typedef, bool noref);
+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);
@@ -124,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, (opts_werror ? "error" : "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, (opts_werror ? "error" : "warning"), fmt, ap);
+       r = vcompile_warning(ctx, warntype, fmt, ap);
        va_end(ap);
-
-       return opts_werror;
+       return r;
 }
 
 /**********************************************************************
@@ -484,7 +468,7 @@ static bool rotate_entfield_array_index_nodes(ast_expression **out)
     return true;
 }
 
-static bool parser_sy_pop(parser_t *parser, shunt *sy)
+static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
 {
     const oper_info *op;
     lex_ctx ctx;
@@ -591,7 +575,7 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
             {
 #if 0
                 /* This is not broken in fteqcc anymore */
-                if (opts_standard != COMPILER_GMQCC) {
+                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"
@@ -603,11 +587,15 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
 
         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;
@@ -849,13 +837,19 @@ 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))));
             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;
+                }
                 out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]);
+            }
             break;
 
         case opid2('?',':'):
@@ -922,7 +916,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) &&
@@ -1204,9 +1200,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;
     }
 
@@ -1247,14 +1259,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"
@@ -1263,9 +1275,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
@@ -1279,9 +1291,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);
             }
         }
     }
@@ -1317,7 +1329,7 @@ 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;
         }
@@ -1333,7 +1345,7 @@ static bool parser_close_paren(parser_t *parser, shunt *sy, bool functions_only)
             vec_shrinkby(sy->ops, 1);
             return true;
         }
-        if (!parser_sy_pop(parser, sy))
+        if (!parser_sy_apply_operator(parser, sy))
             return false;
     }
     return true;
@@ -1419,7 +1431,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 */
@@ -1444,16 +1456,27 @@ 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;
-            }
-            if (ast_istype(var, ast_value)) {
-                ((ast_value*)var)->uses++;
+                /* 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_member)) {
-                ast_member *mem = (ast_member*)var;
-                if (ast_istype(mem->owner, ast_value))
-                    ((ast_value*)(mem->owner))->uses++;
+            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++;
+                }
             }
             vec_push(sy.out, syexp(parser_ctx(parser), var));
             DEBUGSHUNTDO(con_out("push %s\n", parser_tokval(parser)));
@@ -1567,6 +1590,10 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
             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");
@@ -1621,7 +1648,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];
@@ -1629,7 +1656,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) {
@@ -1671,7 +1698,6 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 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;
@@ -1708,7 +1734,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;
     }
 
@@ -2026,12 +2052,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, false))
+        if (!parse_variable(parser, block, true, CV_VAR, typevar, false, false))
             goto onerr;
     }
     else if (parser->tok != ';')
@@ -2130,7 +2156,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;
@@ -2139,7 +2165,7 @@ 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");
@@ -2171,11 +2197,12 @@ static bool parse_break_continue(parser_t *parser, ast_block *block, ast_express
 /* 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)
+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"))
@@ -2186,7 +2213,9 @@ static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bo
             had_var = true;
         else if (!strcmp(parser_tokval(parser), "noref"))
             had_noref = true;
-        else if (!had_const && !had_var && !had_noref) {
+        else if (!strcmp(parser_tokval(parser), "noreturn"))
+            had_noreturn = true;
+        else if (!had_const && !had_var && !had_noref && !had_noreturn) {
             return false;
         }
         else
@@ -2200,7 +2229,8 @@ static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bo
         *cvq = CV_VAR;
     else
         *cvq = CV_NONE;
-    *noref = had_noref;
+    *noref    = had_noref;
+    *noreturn = had_noreturn;
     return true;
 onerr:
     parseerror(parser, "parse error after variable qualifier");
@@ -2217,7 +2247,7 @@ static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **ou
     ast_switch_case swcase;
 
     int  cvq;
-    bool noref;
+    bool noref, noreturn;
 
     lex_ctx ctx = parser_ctx(parser);
 
@@ -2269,19 +2299,19 @@ static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **ou
         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)) {
+            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))
+        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)) {
+            if (!parse_variable(parser, block, false, cvq, NULL, noref, noreturn)) {
                 ast_delete(switchnode);
                 return false;
             }
@@ -2312,8 +2342,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->cvq != CV_CONST) { */
+                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;
@@ -2368,7 +2397,10 @@ 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;
+            }
         }
     }
 
@@ -2466,7 +2498,7 @@ static bool parse_pragma_do(parser_t *parser)
     }
     else
     {
-        parseerror(parser, "unrecognized hash-keyword: `%s`", parser_tokval(parser));
+        (void)!parsewarning(parser, WARN_UNKNOWN_PRAGMAS, "ignoring #pragma %s", parser_tokval(parser));
         return false;
     }
 
@@ -2494,7 +2526,7 @@ static bool parse_pragma(parser_t *parser)
 
 static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases)
 {
-    bool       noref;
+    bool       noref, noreturn;
     int        cvq = CV_NONE;
     ast_value *typevar = NULL;
 
@@ -2510,19 +2542,19 @@ 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, false))
+        if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false))
             return false;
         return true;
     }
-    else if (parse_var_qualifiers(parser, !!block, &cvq, &noref))
+    else if (parse_var_qualifiers(parser, !!block, &cvq, &noref, &noreturn))
     {
         if (cvq == CV_WRONG)
             return false;
-        return parse_variable(parser, block, true, cvq, NULL, noref);
+        return parse_variable(parser, block, true, cvq, NULL, noref, noreturn);
     }
     else if (parser->tok == TOKEN_KEYWORD)
     {
@@ -2577,7 +2609,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
         }
         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;
             }
@@ -2704,7 +2736,11 @@ static bool parse_block_into(parser_t *parser, ast_block *block)
         }
         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 != '}') {
@@ -2789,7 +2825,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"))
         {
@@ -2969,9 +3005,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) {
@@ -3034,7 +3073,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;
 
@@ -3120,7 +3159,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) {
@@ -3128,7 +3170,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 {
@@ -3189,7 +3234,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) {
@@ -3197,7 +3245,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 {
@@ -3309,9 +3360,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);
@@ -3358,9 +3408,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);
@@ -3405,9 +3454,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);
@@ -3484,7 +3532,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 */
@@ -3496,7 +3544,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;
@@ -3693,7 +3742,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");
@@ -3761,7 +3810,7 @@ static bool parse_typedef(parser_t *parser)
     return true;
 }
 
-static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref)
+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;
@@ -3792,7 +3841,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) {
@@ -3815,7 +3864,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) {
@@ -3828,6 +3877,8 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
         /* 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, ...)
@@ -3878,7 +3929,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");
@@ -3918,7 +3969,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;
@@ -3942,7 +3993,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");
@@ -3973,7 +4024,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;
@@ -4097,7 +4148,7 @@ skipvar:
             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) )
@@ -4117,7 +4168,7 @@ skipvar:
                 break;
             }
         }
-        else if (opts_standard == COMPILER_QCC) {
+        else if (opts.standard == COMPILER_QCC) {
             parseerror(parser, "expected '=' before function body in this standard");
         }
 
@@ -4204,7 +4255,7 @@ skipvar:
                     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) &&
                         qualifier != CV_VAR)
                     {
@@ -4227,12 +4278,13 @@ skipvar:
                 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);
@@ -4292,22 +4344,23 @@ cleanup:
 
 static bool parser_global_statement(parser_t *parser)
 {
-    int        cvq = CV_WRONG;
-    bool       noref = false;
-    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, false);
+        return parse_variable(parser, NULL, false, CV_NONE, istype, false, false);
     }
-    else if (parse_var_qualifiers(parser, false, &cvq, &noref))
+    else if (parse_var_qualifiers(parser, false, &cvq, &noref, &noreturn))
     {
         if (cvq == CV_WRONG)
             return false;
-        return parse_variable(parser, NULL, true, cvq, NULL, noref);
+        return parse_variable(parser, NULL, true, cvq, NULL, noref, noreturn);
     }
     else if (parser->tok == TOKEN_KEYWORD)
     {
@@ -4671,7 +4724,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)) {
@@ -4682,7 +4735,7 @@ bool parser_finish(const char *output)
         }
 
         if (retval) {
-            if (opts_dumpfin)
+            if (opts.dumpfin)
                 ir_builder_dump(ir, con_out);
 
             generate_checksum(parser);