]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - ast.c
actually removing -Wmissing-return-values from parser.c and enabling the ast code...
[xonotic/gmqcc.git] / ast.c
diff --git a/ast.c b/ast.c
index 7061f1eff909e5ab63d30d5647398499d69d4a78..604e6a0c12534ca6db484206d97126ea36b1bd07 100644 (file)
--- a/ast.c
+++ b/ast.c
     ast_node_init((ast_node*)self, ctx, TYPE_##T);                  \
     ( (ast_node*)self )->node.destroy = (ast_node_delete*)destroyfn
 
-/* error handling */
-static void asterror(lex_ctx ctx, const char *msg, ...)
-{
-    va_list ap;
-    va_start(ap, msg);
-    con_cvprintmsg((void*)&ctx, LVL_ERROR, "error", msg, ap);
-    va_end(ap);
-}
 
 /* It must not be possible to get here. */
 static GMQCC_NORETURN void _ast_node_destroy(ast_node *self)
@@ -59,8 +51,17 @@ static void ast_node_init(ast_node *self, lex_ctx ctx, int nodetype)
     self->node.destroy = &_ast_node_destroy;
     self->node.keep    = false;
     self->node.nodetype = nodetype;
+    self->node.side_effects = false;
 }
 
+/* weight and side effects */
+static void _ast_propagate_effects(ast_node *self, ast_node *other)
+{
+    if (ast_side_effects(other))
+        ast_side_effects(self) = true;
+}
+#define ast_propagate_effects(s,o) _ast_propagate_effects(((ast_node*)(s)), ((ast_node*)(o)))
+
 /* General expression initialization */
 static void ast_expression_init(ast_expression *self,
                                 ast_expression_codegen *codegen)
@@ -321,7 +322,8 @@ ast_value* ast_value_new(lex_ctx ctx, const char *name, int t)
     self->name = name ? util_strdup(name) : NULL;
     self->expression.vtype = t;
     self->expression.next  = NULL;
-    self->isconst = false;
+    self->constant = false;
+    self->hasvalue = false;
     self->uses    = 0;
     memset(&self->constval, 0, sizeof(self->constval));
 
@@ -339,7 +341,7 @@ void ast_value_delete(ast_value* self)
 {
     if (self->name)
         mem_d((void*)self->name);
-    if (self->isconst) {
+    if (self->hasvalue) {
         switch (self->expression.vtype)
         {
         case TYPE_STRING:
@@ -385,6 +387,9 @@ ast_binary* ast_binary_new(lex_ctx ctx, int op,
     self->left = left;
     self->right = right;
 
+    ast_propagate_effects(self, left);
+    ast_propagate_effects(self, right);
+
     if (op >= INSTR_EQ_F && op <= INSTR_GT)
         self->expression.vtype = TYPE_FLOAT;
     else if (op == INSTR_AND || op == INSTR_OR ||
@@ -414,11 +419,15 @@ ast_binstore* ast_binstore_new(lex_ctx ctx, int storop, int op,
     ast_instantiate(ast_binstore, ctx, ast_binstore_delete);
     ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_binstore_codegen);
 
+    ast_side_effects(self) = true;
+
     self->opstore = storop;
     self->opbin   = op;
     self->dest    = left;
     self->source  = right;
 
+    self->keep_dest = false;
+
     self->expression.vtype = left->expression.vtype;
     if (left->expression.next) {
         self->expression.next = ast_type_copy(ctx, left);
@@ -435,7 +444,8 @@ ast_binstore* ast_binstore_new(lex_ctx ctx, int storop, int op,
 
 void ast_binstore_delete(ast_binstore *self)
 {
-    ast_unref(self->dest);
+    if (!self->keep_dest)
+        ast_unref(self->dest);
     ast_unref(self->source);
     ast_expression_delete((ast_expression*)self);
     mem_d(self);
@@ -450,10 +460,12 @@ ast_unary* ast_unary_new(lex_ctx ctx, int op,
     self->op = op;
     self->operand = expr;
 
+    ast_propagate_effects(self, expr);
+
     if (op >= INSTR_NOT_F && op <= INSTR_NOT_FNC) {
         self->expression.vtype = TYPE_FLOAT;
     } else
-        asterror(ctx, "cannot determine type of unary operation %s", asm_instr[op].m);
+        compile_error(ctx, "cannot determine type of unary operation %s", asm_instr[op].m);
 
     return self;
 }
@@ -472,6 +484,9 @@ ast_return* ast_return_new(lex_ctx ctx, ast_expression *expr)
 
     self->operand = expr;
 
+    if (expr)
+        ast_propagate_effects(self, expr);
+
     return self;
 }
 
@@ -486,7 +501,7 @@ void ast_return_delete(ast_return *self)
 ast_entfield* ast_entfield_new(lex_ctx ctx, ast_expression *entity, ast_expression *field)
 {
     if (field->expression.vtype != TYPE_FIELD) {
-        asterror(ctx, "ast_entfield_new with expression not of type field");
+        compile_error(ctx, "ast_entfield_new with expression not of type field");
         return NULL;
     }
     return ast_entfield_new_force(ctx, entity, field, field->expression.next);
@@ -506,6 +521,8 @@ ast_entfield* ast_entfield_new_force(lex_ctx ctx, ast_expression *entity, ast_ex
 
     self->entity = entity;
     self->field  = field;
+    ast_propagate_effects(self, entity);
+    ast_propagate_effects(self, field);
 
     if (!ast_type_adopt(self, outtype)) {
         ast_entfield_delete(self);
@@ -523,7 +540,7 @@ void ast_entfield_delete(ast_entfield *self)
     mem_d(self);
 }
 
-ast_member* ast_member_new(lex_ctx ctx, ast_expression *owner, unsigned int field)
+ast_member* ast_member_new(lex_ctx ctx, ast_expression *owner, unsigned int field, const char *name)
 {
     ast_instantiate(ast_member, ctx, ast_member_delete);
     if (field >= 3) {
@@ -533,7 +550,7 @@ ast_member* ast_member_new(lex_ctx ctx, ast_expression *owner, unsigned int fiel
 
     if (owner->expression.vtype != TYPE_VECTOR &&
         owner->expression.vtype != TYPE_FIELD) {
-        asterror(ctx, "member-access on an invalid owner of type %s", type_name[owner->expression.vtype]);
+        compile_error(ctx, "member-access on an invalid owner of type %s", type_name[owner->expression.vtype]);
         mem_d(self);
         return NULL;
     }
@@ -550,7 +567,13 @@ ast_member* ast_member_new(lex_ctx ctx, ast_expression *owner, unsigned int fiel
     }
 
     self->owner = owner;
+    ast_propagate_effects(self, owner);
+
     self->field = field;
+    if (name)
+        self->name = util_strdup(name);
+    else
+        self->name = NULL;
 
     return self;
 }
@@ -585,6 +608,8 @@ ast_array_index* ast_array_index_new(lex_ctx ctx, ast_expression *array, ast_exp
 
     self->array = array;
     self->index = index;
+    ast_propagate_effects(self, array);
+    ast_propagate_effects(self, index);
 
     if (!ast_type_adopt(self, outtype)) {
         ast_array_index_delete(self);
@@ -592,7 +617,7 @@ ast_array_index* ast_array_index_new(lex_ctx ctx, ast_expression *array, ast_exp
     }
     if (array->expression.vtype == TYPE_FIELD && outtype->expression.vtype == TYPE_ARRAY) {
         if (self->expression.vtype != TYPE_ARRAY) {
-            asterror(ast_ctx(self), "array_index node on type");
+            compile_error(ast_ctx(self), "array_index node on type");
             ast_array_index_delete(self);
             return NULL;
         }
@@ -624,6 +649,11 @@ ast_ifthen* ast_ifthen_new(lex_ctx ctx, ast_expression *cond, ast_expression *on
     self->cond     = cond;
     self->on_true  = ontrue;
     self->on_false = onfalse;
+    ast_propagate_effects(self, cond);
+    if (ontrue)
+        ast_propagate_effects(self, ontrue);
+    if (onfalse)
+        ast_propagate_effects(self, onfalse);
 
     return self;
 }
@@ -652,6 +682,9 @@ ast_ternary* ast_ternary_new(lex_ctx ctx, ast_expression *cond, ast_expression *
     self->cond     = cond;
     self->on_true  = ontrue;
     self->on_false = onfalse;
+    ast_propagate_effects(self, cond);
+    ast_propagate_effects(self, ontrue);
+    ast_propagate_effects(self, onfalse);
 
     if (!ast_type_adopt(self, ontrue)) {
         ast_ternary_delete(self);
@@ -686,6 +719,17 @@ ast_loop* ast_loop_new(lex_ctx ctx,
     self->increment = increment;
     self->body      = body;
 
+    if (initexpr)
+        ast_propagate_effects(self, initexpr);
+    if (precond)
+        ast_propagate_effects(self, precond);
+    if (postcond)
+        ast_propagate_effects(self, postcond);
+    if (increment)
+        ast_propagate_effects(self, increment);
+    if (body)
+        ast_propagate_effects(self, body);
+
     return self;
 }
 
@@ -729,6 +773,8 @@ ast_switch* ast_switch_new(lex_ctx ctx, ast_expression *op)
     self->operand = op;
     self->cases   = NULL;
 
+    ast_propagate_effects(self, op);
+
     return self;
 }
 
@@ -748,12 +794,63 @@ void ast_switch_delete(ast_switch *self)
     mem_d(self);
 }
 
+ast_label* ast_label_new(lex_ctx ctx, const char *name)
+{
+    ast_instantiate(ast_label, ctx, ast_label_delete);
+    ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_label_codegen);
+
+    self->name    = util_strdup(name);
+    self->irblock = NULL;
+    self->gotos   = NULL;
+
+    return self;
+}
+
+void ast_label_delete(ast_label *self)
+{
+    mem_d((void*)self->name);
+    vec_free(self->gotos);
+    ast_expression_delete((ast_expression*)self);
+    mem_d(self);
+}
+
+void ast_label_register_goto(ast_label *self, ast_goto *g)
+{
+    vec_push(self->gotos, g);
+}
+
+ast_goto* ast_goto_new(lex_ctx ctx, const char *name)
+{
+    ast_instantiate(ast_goto, ctx, ast_goto_delete);
+    ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_goto_codegen);
+
+    self->name    = util_strdup(name);
+    self->target  = NULL;
+    self->irblock_from = NULL;
+
+    return self;
+}
+
+void ast_goto_delete(ast_goto *self)
+{
+    mem_d((void*)self->name);
+    ast_expression_delete((ast_expression*)self);
+    mem_d(self);
+}
+
+void ast_goto_set_label(ast_goto *self, ast_label *label)
+{
+    self->target = label;
+}
+
 ast_call* ast_call_new(lex_ctx ctx,
                        ast_expression *funcexpr)
 {
     ast_instantiate(ast_call, ctx, ast_call_delete);
     ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_call_codegen);
 
+    ast_side_effects(self) = true;
+
     self->params = NULL;
     self->func   = funcexpr;
 
@@ -793,7 +890,7 @@ bool ast_call_check_types(ast_call *self)
             char tgot[1024];
             ast_type_to_string(self->params[i], tgot, sizeof(tgot));
             ast_type_to_string((ast_expression*)func->expression.params[i], texp, sizeof(texp));
-            asterror(ast_ctx(self), "invalid type for parameter %u in function call: expected %s, got %s",
+            compile_error(ast_ctx(self), "invalid type for parameter %u in function call: expected %s, got %s",
                      (unsigned int)(i+1), texp, tgot);
             /* we don't immediately return */
             retval = false;
@@ -808,6 +905,8 @@ ast_store* ast_store_new(lex_ctx ctx, int op,
     ast_instantiate(ast_store, ctx, ast_store_delete);
     ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_store_codegen);
 
+    ast_side_effects(self) = true;
+
     self->op = op;
     self->dest = dest;
     self->source = source;
@@ -847,6 +946,12 @@ ast_block* ast_block_new(lex_ctx ctx)
     return self;
 }
 
+void ast_block_add_expr(ast_block *self, ast_expression *e)
+{
+    ast_propagate_effects(self, e);
+    vec_push(self->exprs, e);
+}
+
 void ast_block_collect(ast_block *self, ast_expression *expr)
 {
     vec_push(self->collect, expr);
@@ -889,9 +994,13 @@ ast_function* ast_function_new(lex_ctx ctx, const char *name, ast_value *vtype)
     ast_instantiate(ast_function, ctx, ast_function_delete);
 
     if (!vtype ||
-        vtype->isconst ||
+        vtype->hasvalue ||
         vtype->expression.vtype != TYPE_FUNCTION)
     {
+        compile_error(ast_ctx(self), "internal error: ast_function_new condition %i %i type=%i",
+                 (int)!vtype,
+                 (int)vtype->hasvalue,
+                 vtype->expression.vtype);
         mem_d(self);
         return NULL;
     }
@@ -909,7 +1018,7 @@ ast_function* ast_function_new(lex_ctx ctx, const char *name, ast_value *vtype)
     self->breakblock    = NULL;
     self->continueblock = NULL;
 
-    vtype->isconst = true;
+    vtype->hasvalue = true;
     vtype->constval.vfunc = self;
 
     return self;
@@ -922,7 +1031,7 @@ void ast_function_delete(ast_function *self)
         mem_d((void*)self->name);
     if (self->vtype) {
         /* ast_value_delete(self->vtype); */
-        self->vtype->isconst = false;
+        self->vtype->hasvalue = false;
         self->vtype->constval.vfunc = NULL;
         /* We use unref - if it was stored in a global table it is supposed
          * to be deleted from *there*
@@ -979,7 +1088,7 @@ bool ast_value_codegen(ast_value *self, ast_function *func, bool lvalue, ir_valu
     if (!self->ir_v) {
         char typename[1024];
         ast_type_to_string((ast_expression*)self, typename, sizeof(typename));
-        asterror(ast_ctx(self), "ast_value used before generated %s %s", typename, self->name);
+        compile_error(ast_ctx(self), "ast_value used before generated %s %s", typename, self->name);
         return false;
     }
     *out = self->ir_v;
@@ -990,7 +1099,7 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
 {
     ir_value *v = NULL;
 
-    if (self->isconst && self->expression.vtype == TYPE_FUNCTION)
+    if (self->hasvalue && self->expression.vtype == TYPE_FUNCTION)
     {
         ir_function *func = ir_builder_create_function(ir, self->name, self->expression.next->expression.vtype);
         if (!func)
@@ -1007,8 +1116,8 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
     if (isfield && self->expression.vtype == TYPE_FIELD) {
         ast_expression *fieldtype = self->expression.next;
 
-        if (self->isconst) {
-            asterror(ast_ctx(self), "TODO: constant field pointers with value");
+        if (self->hasvalue) {
+            compile_error(ast_ctx(self), "TODO: constant field pointers with value");
             goto error;
         }
 
@@ -1022,20 +1131,20 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
             ast_value             *array = (ast_value*)fieldtype;
 
             if (!ast_istype(fieldtype, ast_value)) {
-                asterror(ast_ctx(self), "internal error: ast_value required");
+                compile_error(ast_ctx(self), "internal error: ast_value required");
                 return false;
             }
 
             /* we are lame now - considering the way QC works we won't tolerate arrays > 1024 elements */
             if (!array->expression.count || array->expression.count > opts_max_array_size)
-                asterror(ast_ctx(self), "Invalid array of size %lu", (unsigned long)array->expression.count);
+                compile_error(ast_ctx(self), "Invalid array of size %lu", (unsigned long)array->expression.count);
 
             elemtype = &array->expression.next->expression;
             vtype = elemtype->vtype;
 
             v = ir_builder_create_field(ir, self->name, vtype);
             if (!v) {
-                asterror(ast_ctx(self), "ir_builder_create_global failed");
+                compile_error(ast_ctx(self), "ir_builder_create_global failed on `%s`", self->name);
                 return false;
             }
             if (vtype == TYPE_FIELD)
@@ -1054,7 +1163,7 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
                 array->ir_values[ai] = ir_builder_create_field(ir, name, vtype);
                 if (!array->ir_values[ai]) {
                     mem_d(name);
-                    asterror(ast_ctx(self), "ir_builder_create_global failed");
+                    compile_error(ast_ctx(self), "ir_builder_create_global failed on `%s`", name);
                     return false;
                 }
                 if (vtype == TYPE_FIELD)
@@ -1084,11 +1193,11 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
 
         /* same as with field arrays */
         if (!self->expression.count || self->expression.count > opts_max_array_size)
-            asterror(ast_ctx(self), "Invalid array of size %lu", (unsigned long)self->expression.count);
+            compile_error(ast_ctx(self), "Invalid array of size %lu", (unsigned long)self->expression.count);
 
         v = ir_builder_create_global(ir, self->name, vtype);
         if (!v) {
-            asterror(ast_ctx(self), "ir_builder_create_global failed");
+            compile_error(ast_ctx(self), "ir_builder_create_global failed `%s`", self->name);
             return false;
         }
         if (vtype == TYPE_FIELD)
@@ -1106,7 +1215,7 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
             self->ir_values[ai] = ir_builder_create_global(ir, name, vtype);
             if (!self->ir_values[ai]) {
                 mem_d(name);
-                asterror(ast_ctx(self), "ir_builder_create_global failed");
+                compile_error(ast_ctx(self), "ir_builder_create_global failed `%s`", name);
                 return false;
             }
             if (vtype == TYPE_FIELD)
@@ -1122,7 +1231,7 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
          */
         v = ir_builder_create_global(ir, self->name, self->expression.vtype);
         if (!v) {
-            asterror(ast_ctx(self), "ir_builder_create_global failed");
+            compile_error(ast_ctx(self), "ir_builder_create_global failed on `%s`", self->name);
             return false;
         }
         if (self->expression.vtype == TYPE_FIELD)
@@ -1130,7 +1239,7 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
         v->context = ast_ctx(self);
     }
 
-    if (self->isconst) {
+    if (self->hasvalue) {
         switch (self->expression.vtype)
         {
             case TYPE_FLOAT:
@@ -1146,16 +1255,16 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
                     goto error;
                 break;
             case TYPE_ARRAY:
-                asterror(ast_ctx(self), "TODO: global constant array");
+                compile_error(ast_ctx(self), "TODO: global constant array");
                 break;
             case TYPE_FUNCTION:
-                asterror(ast_ctx(self), "global of type function not properly generated");
+                compile_error(ast_ctx(self), "global of type function not properly generated");
                 goto error;
                 /* Cannot generate an IR value for a function,
                  * need a pointer pointing to a function rather.
                  */
             default:
-                asterror(ast_ctx(self), "TODO: global constant type %i", self->expression.vtype);
+                compile_error(ast_ctx(self), "TODO: global constant type %i", self->expression.vtype);
                 break;
         }
     }
@@ -1172,7 +1281,7 @@ error: /* clean up */
 bool ast_local_codegen(ast_value *self, ir_function *func, bool param)
 {
     ir_value *v = NULL;
-    if (self->isconst && self->expression.vtype == TYPE_FUNCTION)
+    if (self->hasvalue && self->expression.vtype == TYPE_FUNCTION)
     {
         /* Do we allow local functions? I think not...
          * this is NOT a function pointer atm.
@@ -1189,24 +1298,24 @@ bool ast_local_codegen(ast_value *self, ir_function *func, bool param)
         int vtype = elemtype->vtype;
 
         if (param) {
-            asterror(ast_ctx(self), "array-parameters are not supported");
+            compile_error(ast_ctx(self), "array-parameters are not supported");
             return false;
         }
 
         /* we are lame now - considering the way QC works we won't tolerate arrays > 1024 elements */
         if (!self->expression.count || self->expression.count > opts_max_array_size) {
-            asterror(ast_ctx(self), "Invalid array of size %lu", (unsigned long)self->expression.count);
+            compile_error(ast_ctx(self), "Invalid array of size %lu", (unsigned long)self->expression.count);
         }
 
         self->ir_values = (ir_value**)mem_a(sizeof(self->ir_values[0]) * self->expression.count);
         if (!self->ir_values) {
-            asterror(ast_ctx(self), "failed to allocate array values");
+            compile_error(ast_ctx(self), "failed to allocate array values");
             return false;
         }
 
         v = ir_function_create_local(func, self->name, vtype, param);
         if (!v) {
-            asterror(ast_ctx(self), "ir_function_create_local failed");
+            compile_error(ast_ctx(self), "ir_function_create_local failed");
             return false;
         }
         if (vtype == TYPE_FIELD)
@@ -1222,7 +1331,7 @@ bool ast_local_codegen(ast_value *self, ir_function *func, bool param)
             snprintf(name + namelen, 16, "[%u]", (unsigned int)ai);
             self->ir_values[ai] = ir_function_create_local(func, name, vtype, param);
             if (!self->ir_values[ai]) {
-                asterror(ast_ctx(self), "ir_builder_create_global failed");
+                compile_error(ast_ctx(self), "ir_builder_create_global failed on `%s`", name);
                 return false;
             }
             if (vtype == TYPE_FIELD)
@@ -1243,7 +1352,7 @@ bool ast_local_codegen(ast_value *self, ir_function *func, bool param)
     /* A constant local... hmmm...
      * I suppose the IR will have to deal with this
      */
-    if (self->isconst) {
+    if (self->hasvalue) {
         switch (self->expression.vtype)
         {
             case TYPE_FLOAT:
@@ -1259,7 +1368,7 @@ bool ast_local_codegen(ast_value *self, ir_function *func, bool param)
                     goto error;
                 break;
             default:
-                asterror(ast_ctx(self), "TODO: global constant type %i", self->expression.vtype);
+                compile_error(ast_ctx(self), "TODO: global constant type %i", self->expression.vtype);
                 break;
         }
     }
@@ -1297,7 +1406,7 @@ bool ast_function_codegen(ast_function *self, ir_builder *ir)
 
     irf = self->ir_func;
     if (!irf) {
-        asterror(ast_ctx(self), "ast_function's related ast_value was not generated yet");
+        compile_error(ast_ctx(self), "ast_function's related ast_value was not generated yet");
         return false;
     }
 
@@ -1318,13 +1427,13 @@ bool ast_function_codegen(ast_function *self, ir_builder *ir)
     }
 
     if (!vec_size(self->blocks)) {
-        asterror(ast_ctx(self), "function `%s` has no body", self->name);
+        compile_error(ast_ctx(self), "function `%s` has no body", self->name);
         return false;
     }
 
-    self->curblock = ir_function_create_block(irf, "entry");
+    self->curblock = ir_function_create_block(ast_ctx(self), irf, "entry");
     if (!self->curblock) {
-        asterror(ast_ctx(self), "failed to allocate entry block for `%s`", self->name);
+        compile_error(ast_ctx(self), "failed to allocate entry block for `%s`", self->name);
         return false;
     }
 
@@ -1337,21 +1446,24 @@ bool ast_function_codegen(ast_function *self, ir_builder *ir)
     /* TODO: check return types */
     if (!self->curblock->is_return)
     {
+        /*
         return ir_block_create_return(self->curblock, NULL);
-        /* From now on the parser has to handle this situation */
-#if 0
+        */
         if (!self->vtype->expression.next ||
             self->vtype->expression.next->expression.vtype == TYPE_VOID)
         {
             return ir_block_create_return(self->curblock, NULL);
         }
-        else
+        else if (vec_size(self->curblock->entries))
         {
             /* error("missing return"); */
-            asterror(ast_ctx(self), "function `%s` missing return value", self->name);
-            return false;
+            if (compile_warning(ast_ctx(self), WARN_MISSING_RETURN_VALUES,
+                                "control reaches end of non-void function (`%s`)",
+                                self->name))
+            {
+                return false;
+            }
         }
-#endif
     }
     return true;
 }
@@ -1371,7 +1483,7 @@ bool ast_block_codegen(ast_block *self, ast_function *func, bool lvalue, ir_valu
      * of the form: (a, b, c) = x should not assign to c...
      */
     if (lvalue) {
-        asterror(ast_ctx(self), "not an l-value (code-block)");
+        compile_error(ast_ctx(self), "not an l-value (code-block)");
         return false;
     }
 
@@ -1393,7 +1505,7 @@ bool ast_block_codegen(ast_block *self, ast_function *func, bool lvalue, ir_valu
     {
         if (!ast_local_codegen(self->locals[i], func->ir_func, false)) {
             if (opts_debug)
-                asterror(ast_ctx(self), "failed to generate local `%s`", self->locals[i]->name);
+                compile_error(ast_ctx(self), "failed to generate local `%s`", self->locals[i]->name);
             return false;
         }
     }
@@ -1401,8 +1513,8 @@ bool ast_block_codegen(ast_block *self, ast_function *func, bool lvalue, ir_valu
     for (i = 0; i < vec_size(self->exprs); ++i)
     {
         ast_expression_codegen *gen = self->exprs[i]->expression.codegen;
-        if (func->curblock->final) {
-            asterror(ast_ctx(self->exprs[i]), "unreachable statement");
+        if (func->curblock->final && !ast_istype(self->exprs[i], ast_label)) {
+            compile_error(ast_ctx(self->exprs[i]), "unreachable statement");
             return false;
         }
         if (!(*gen)(self->exprs[i], func, false, out))
@@ -1440,7 +1552,7 @@ bool ast_store_codegen(ast_store *self, ast_function *func, bool lvalue, ir_valu
         ai = (ast_array_index*)self->dest;
         idx = (ast_value*)ai->index;
 
-        if (ast_istype(ai->index, ast_value) && idx->isconst)
+        if (ast_istype(ai->index, ast_value) && idx->hasvalue)
             ai = NULL;
     }
 
@@ -1450,13 +1562,13 @@ bool ast_store_codegen(ast_store *self, ast_function *func, bool lvalue, ir_valu
         ir_instr  *call;
 
         if (lvalue) {
-            asterror(ast_ctx(self), "array-subscript assignment cannot produce lvalues");
+            compile_error(ast_ctx(self), "array-subscript assignment cannot produce lvalues");
             return false;
         }
 
         arr = (ast_value*)ai->array;
         if (!ast_istype(ai->array, ast_value) || !arr->setter) {
-            asterror(ast_ctx(self), "value has no setter (%s)", arr->name);
+            compile_error(ast_ctx(self), "value has no setter (%s)", arr->name);
             return false;
         }
 
@@ -1518,7 +1630,7 @@ bool ast_binary_codegen(ast_binary *self, ast_function *func, bool lvalue, ir_va
 
     /* A binary operation cannot yield an l-value */
     if (lvalue) {
-        asterror(ast_ctx(self), "not an l-value (binop)");
+        compile_error(ast_ctx(self), "not an l-value (binop)");
         return false;
     }
 
@@ -1547,7 +1659,7 @@ bool ast_binary_codegen(ast_binary *self, ast_function *func, bool lvalue, ir_va
          */
 
         merge_id = vec_size(func->ir_func->blocks);
-        merge = ir_function_create_block(func->ir_func, ast_function_label(func, "sce_merge"));
+        merge = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "sce_merge"));
 
         cgen = self->left->expression.codegen;
         if (!(*cgen)((ast_expression*)(self->left), func, false, &left))
@@ -1555,7 +1667,7 @@ bool ast_binary_codegen(ast_binary *self, ast_function *func, bool lvalue, ir_va
         if (!OPTS_FLAG(PERL_LOGIC)) {
             notop = type_not_instr[left->vtype];
             if (notop == AINSTR_END) {
-                asterror(ast_ctx(self), "don't know how to cast to bool...");
+                compile_error(ast_ctx(self), "don't know how to cast to bool...");
                 return false;
             }
             left = ir_block_create_unary(func->curblock,
@@ -1565,7 +1677,7 @@ bool ast_binary_codegen(ast_binary *self, ast_function *func, bool lvalue, ir_va
         }
         from_left = func->curblock;
 
-        other = ir_function_create_block(func->ir_func, ast_function_label(func, "sce_other"));
+        other = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "sce_other"));
         if ( !(self->op == INSTR_OR) != !OPTS_FLAG(PERL_LOGIC) ) {
             if (!ir_block_create_if(func->curblock, left, other, merge))
                 return false;
@@ -1583,7 +1695,7 @@ bool ast_binary_codegen(ast_binary *self, ast_function *func, bool lvalue, ir_va
         if (!OPTS_FLAG(PERL_LOGIC)) {
             notop = type_not_instr[right->vtype];
             if (notop == AINSTR_END) {
-                asterror(ast_ctx(self), "don't know how to cast to bool...");
+                compile_error(ast_ctx(self), "don't know how to cast to bool...");
                 return false;
             }
             right = ir_block_create_unary(func->curblock,
@@ -1607,7 +1719,7 @@ bool ast_binary_codegen(ast_binary *self, ast_function *func, bool lvalue, ir_va
         if (!OPTS_FLAG(PERL_LOGIC)) {
             notop = type_not_instr[(*out)->vtype];
             if (notop == AINSTR_END) {
-                asterror(ast_ctx(self), "don't know how to cast to bool...");
+                compile_error(ast_ctx(self), "don't know how to cast to bool...");
                 return false;
             }
             *out = ir_block_create_unary(func->curblock,
@@ -1664,7 +1776,7 @@ bool ast_binstore_codegen(ast_binstore *self, ast_function *func, bool lvalue, i
         ai = (ast_array_index*)self->dest;
         idx = (ast_value*)ai->index;
 
-        if (ast_istype(ai->index, ast_value) && idx->isconst)
+        if (ast_istype(ai->index, ast_value) && idx->hasvalue)
             ai = NULL;
     }
 
@@ -1696,13 +1808,13 @@ bool ast_binstore_codegen(ast_binstore *self, ast_function *func, bool lvalue, i
         ir_instr  *call;
 
         if (lvalue) {
-            asterror(ast_ctx(self), "array-subscript assignment cannot produce lvalues");
+            compile_error(ast_ctx(self), "array-subscript assignment cannot produce lvalues");
             return false;
         }
 
         arr = (ast_value*)ai->array;
         if (!ast_istype(ai->array, ast_value) || !arr->setter) {
-            asterror(ast_ctx(self), "value has no setter (%s)", arr->name);
+            compile_error(ast_ctx(self), "value has no setter (%s)", arr->name);
             return false;
         }
 
@@ -1748,7 +1860,7 @@ bool ast_unary_codegen(ast_unary *self, ast_function *func, bool lvalue, ir_valu
 
     /* An unary operation cannot yield an l-value */
     if (lvalue) {
-        asterror(ast_ctx(self), "not an l-value (binop)");
+        compile_error(ast_ctx(self), "not an l-value (binop)");
         return false;
     }
 
@@ -1782,12 +1894,12 @@ bool ast_return_codegen(ast_return *self, ast_function *func, bool lvalue, ir_va
      * anything...
      */
     if (lvalue) {
-        asterror(ast_ctx(self), "return-expression is not an l-value");
+        compile_error(ast_ctx(self), "return-expression is not an l-value");
         return false;
     }
 
     if (self->expression.outr) {
-        asterror(ast_ctx(self), "internal error: ast_return cannot be reused, it bears no result!");
+        compile_error(ast_ctx(self), "internal error: ast_return cannot be reused, it bears no result!");
         return false;
     }
     self->expression.outr = (ir_value*)1;
@@ -1845,7 +1957,7 @@ bool ast_entfield_codegen(ast_entfield *self, ast_function *func, bool lvalue, i
                                              ent, field, self->expression.vtype);
     }
     if (!*out) {
-        asterror(ast_ctx(self), "failed to create %s instruction (output type %s)",
+        compile_error(ast_ctx(self), "failed to create %s instruction (output type %s)",
                  (lvalue ? "ADDRESS" : "FIELD"),
                  type_name[self->expression.vtype]);
         return false;
@@ -1901,7 +2013,7 @@ bool ast_array_index_codegen(ast_array_index *self, ast_function *func, bool lva
     }
 
     if (!ast_istype(self->array, ast_value)) {
-        asterror(ast_ctx(self), "array indexing this way is not supported");
+        compile_error(ast_ctx(self), "array indexing this way is not supported");
         /* note this would actually be pointer indexing because the left side is
          * not an actual array but (hopefully) an indexable expression.
          * Once we get integer arithmetic, and GADDRESS/GSTORE/GLOAD instruction
@@ -1913,24 +2025,24 @@ bool ast_array_index_codegen(ast_array_index *self, ast_function *func, bool lva
     arr = (ast_value*)self->array;
     idx = (ast_value*)self->index;
 
-    if (!ast_istype(self->index, ast_value) || !idx->isconst) {
+    if (!ast_istype(self->index, ast_value) || !idx->hasvalue) {
         /* Time to use accessor functions */
         ast_expression_codegen *cgen;
         ir_value               *iridx, *funval;
         ir_instr               *call;
 
         if (lvalue) {
-            asterror(ast_ctx(self), "(.2) array indexing here needs a compile-time constant");
+            compile_error(ast_ctx(self), "(.2) array indexing here needs a compile-time constant");
             return false;
         }
 
         if (!arr->getter) {
-            asterror(ast_ctx(self), "value has no getter, don't know how to index it");
+            compile_error(ast_ctx(self), "value has no getter, don't know how to index it");
             return false;
         }
 
         cgen = self->index->expression.codegen;
-        if (!(*cgen)((ast_expression*)(self->index), func, true, &iridx))
+        if (!(*cgen)((ast_expression*)(self->index), func, false, &iridx))
             return false;
 
         cgen = arr->getter->expression.codegen;
@@ -1952,7 +2064,7 @@ bool ast_array_index_codegen(ast_array_index *self, ast_function *func, bool lva
     else if (idx->expression.vtype == TYPE_INTEGER)
         *out = arr->ir_values[idx->constval.vint];
     else {
-        asterror(ast_ctx(self), "array indexing here needs an integer constant");
+        compile_error(ast_ctx(self), "array indexing here needs an integer constant");
         return false;
     }
     return true;
@@ -1977,7 +2089,7 @@ bool ast_ifthen_codegen(ast_ifthen *self, ast_function *func, bool lvalue, ir_va
     (void)lvalue;
 
     if (self->expression.outr) {
-        asterror(ast_ctx(self), "internal error: ast_ifthen cannot be reused, it bears no result!");
+        compile_error(ast_ctx(self), "internal error: ast_ifthen cannot be reused, it bears no result!");
         return false;
     }
     self->expression.outr = (ir_value*)1;
@@ -1993,7 +2105,7 @@ bool ast_ifthen_codegen(ast_ifthen *self, ast_function *func, bool lvalue, ir_va
 
     if (self->on_true) {
         /* create on-true block */
-        ontrue = ir_function_create_block(func->ir_func, ast_function_label(func, "ontrue"));
+        ontrue = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "ontrue"));
         if (!ontrue)
             return false;
 
@@ -2013,7 +2125,7 @@ bool ast_ifthen_codegen(ast_ifthen *self, ast_function *func, bool lvalue, ir_va
     /* on-false path */
     if (self->on_false) {
         /* create on-false block */
-        onfalse = ir_function_create_block(func->ir_func, ast_function_label(func, "onfalse"));
+        onfalse = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "onfalse"));
         if (!onfalse)
             return false;
 
@@ -2031,7 +2143,7 @@ bool ast_ifthen_codegen(ast_ifthen *self, ast_function *func, bool lvalue, ir_va
         onfalse = NULL;
 
     /* Merge block were they all merge in to */
-    merge = ir_function_create_block(func->ir_func, ast_function_label(func, "endif"));
+    merge = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "endif"));
     if (!merge)
         return false;
     /* add jumps ot the merge block */
@@ -2064,8 +2176,9 @@ bool ast_ternary_codegen(ast_ternary *self, ast_function *func, bool lvalue, ir_
     ir_instr *phi;
 
     ir_block *cond = func->curblock;
-    ir_block *ontrue;
-    ir_block *onfalse;
+    ir_block *cond_out = NULL;
+    ir_block *ontrue, *ontrue_out = NULL;
+    ir_block *onfalse, *onfalse_out = NULL;
     ir_block *merge;
 
     /* Ternary can never create an lvalue... */
@@ -2089,9 +2202,10 @@ bool ast_ternary_codegen(ast_ternary *self, ast_function *func, bool lvalue, ir_
     cgen = self->cond->expression.codegen;
     if (!(*cgen)((ast_expression*)(self->cond), func, false, &condval))
         return false;
+    cond_out = func->curblock;
 
     /* create on-true block */
-    ontrue = ir_function_create_block(func->ir_func, ast_function_label(func, "tern_T"));
+    ontrue = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "tern_T"));
     if (!ontrue)
         return false;
     else
@@ -2103,10 +2217,12 @@ bool ast_ternary_codegen(ast_ternary *self, ast_function *func, bool lvalue, ir_
         cgen = self->on_true->expression.codegen;
         if (!(*cgen)((ast_expression*)(self->on_true), func, false, &trueval))
             return false;
+
+        ontrue_out = func->curblock;
     }
 
     /* create on-false block */
-    onfalse = ir_function_create_block(func->ir_func, ast_function_label(func, "tern_F"));
+    onfalse = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "tern_F"));
     if (!onfalse)
         return false;
     else
@@ -2118,20 +2234,22 @@ bool ast_ternary_codegen(ast_ternary *self, ast_function *func, bool lvalue, ir_
         cgen = self->on_false->expression.codegen;
         if (!(*cgen)((ast_expression*)(self->on_false), func, false, &falseval))
             return false;
+
+        onfalse_out = func->curblock;
     }
 
     /* create merge block */
-    merge = ir_function_create_block(func->ir_func, ast_function_label(func, "tern_out"));
+    merge = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "tern_out"));
     if (!merge)
         return false;
     /* jump to merge block */
-    if (!ir_block_create_jump(ontrue, merge))
+    if (!ir_block_create_jump(ontrue_out, merge))
         return false;
-    if (!ir_block_create_jump(onfalse, merge))
+    if (!ir_block_create_jump(onfalse_out, merge))
         return false;
 
     /* create if instruction */
-    if (!ir_block_create_if(cond, condval, ontrue, onfalse))
+    if (!ir_block_create_if(cond_out, condval, ontrue, onfalse))
         return false;
 
     /* Now enter the merge block */
@@ -2149,8 +2267,8 @@ bool ast_ternary_codegen(ast_ternary *self, ast_function *func, bool lvalue, ir_
     phi = ir_block_create_phi(merge, ast_function_label(func, "phi"), trueval->vtype);
     if (!phi)
         return false;
-    ir_phi_add(phi, ontrue,  trueval);
-    ir_phi_add(phi, onfalse, falseval);
+    ir_phi_add(phi, ontrue_out,  trueval);
+    ir_phi_add(phi, onfalse_out, falseval);
 
     self->expression.outr = ir_phi_value(phi);
     *out = self->expression.outr;
@@ -2192,7 +2310,7 @@ bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value
     (void)out;
 
     if (self->expression.outr) {
-        asterror(ast_ctx(self), "internal error: ast_loop cannot be reused, it bears no result!");
+        compile_error(ast_ctx(self), "internal error: ast_loop cannot be reused, it bears no result!");
         return false;
     }
     self->expression.outr = (ir_value*)1;
@@ -2221,7 +2339,7 @@ bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value
      */
     if (self->precond)
     {
-        bprecond = ir_function_create_block(func->ir_func, ast_function_label(func, "pre_loop_cond"));
+        bprecond = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "pre_loop_cond"));
         if (!bprecond)
             return false;
 
@@ -2245,7 +2363,7 @@ bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value
      * generate them this early for 'break' and 'continue'.
      */
     if (self->increment) {
-        bincrement = ir_function_create_block(func->ir_func, ast_function_label(func, "loop_increment"));
+        bincrement = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "loop_increment"));
         if (!bincrement)
             return false;
         bcontinue = bincrement; /* increment comes before the pre-loop-condition */
@@ -2254,7 +2372,7 @@ bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value
     }
 
     if (self->postcond) {
-        bpostcond = ir_function_create_block(func->ir_func, ast_function_label(func, "post_loop_cond"));
+        bpostcond = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "post_loop_cond"));
         if (!bpostcond)
             return false;
         bcontinue = bpostcond; /* postcond comes before the increment */
@@ -2263,7 +2381,7 @@ bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value
     }
 
     bout_id = vec_size(func->ir_func->blocks);
-    bout = ir_function_create_block(func->ir_func, ast_function_label(func, "after_loop"));
+    bout = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "after_loop"));
     if (!bout)
         return false;
     bbreak = bout;
@@ -2271,7 +2389,7 @@ bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value
     /* The loop body... */
     if (self->body)
     {
-        bbody = ir_function_create_block(func->ir_func, ast_function_label(func, "loop_body"));
+        bbody = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "loop_body"));
         if (!bbody)
             return false;
 
@@ -2282,6 +2400,8 @@ bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value
         old_bcontinue       = func->continueblock;
         func->breakblock    = bbreak;
         func->continueblock = bcontinue;
+        if (!func->continueblock)
+            func->continueblock = bbody;
 
         /* generate */
         cgen = self->body->expression.codegen;
@@ -2395,12 +2515,12 @@ bool ast_breakcont_codegen(ast_breakcont *self, ast_function *func, bool lvalue,
     *out = NULL;
 
     if (lvalue) {
-        asterror(ast_ctx(self), "break/continue expression is not an l-value");
+        compile_error(ast_ctx(self), "break/continue expression is not an l-value");
         return false;
     }
 
     if (self->expression.outr) {
-        asterror(ast_ctx(self), "internal error: ast_breakcont cannot be reused!");
+        compile_error(ast_ctx(self), "internal error: ast_breakcont cannot be reused!");
         return false;
     }
     self->expression.outr = (ir_value*)1;
@@ -2410,6 +2530,11 @@ bool ast_breakcont_codegen(ast_breakcont *self, ast_function *func, bool lvalue,
     else
         target = func->breakblock;
 
+    if (!target) {
+        compile_error(ast_ctx(self), "%s is lacking a target block", (self->is_continue ? "continue" : "break"));
+        return false;
+    }
+
     if (!ir_block_create_jump(func->curblock, target))
         return false;
     return true;
@@ -2434,12 +2559,12 @@ bool ast_switch_codegen(ast_switch *self, ast_function *func, bool lvalue, ir_va
     uint16_t  cmpinstr;
 
     if (lvalue) {
-        asterror(ast_ctx(self), "switch expression is not an l-value");
+        compile_error(ast_ctx(self), "switch expression is not an l-value");
         return false;
     }
 
     if (self->expression.outr) {
-        asterror(ast_ctx(self), "internal error: ast_switch cannot be reused!");
+        compile_error(ast_ctx(self), "internal error: ast_switch cannot be reused!");
         return false;
     }
     self->expression.outr = (ir_value*)1;
@@ -2457,12 +2582,12 @@ bool ast_switch_codegen(ast_switch *self, ast_function *func, bool lvalue, ir_va
     cmpinstr = type_eq_instr[irop->vtype];
     if (cmpinstr >= AINSTR_END) {
         ast_type_to_string(self->operand, typestr, sizeof(typestr));
-        asterror(ast_ctx(self), "invalid type to perform a switch on: %s", typestr);
+        compile_error(ast_ctx(self), "invalid type to perform a switch on: %s", typestr);
         return false;
     }
 
     bout_id = vec_size(func->ir_func->blocks);
-    bout = ir_function_create_block(func->ir_func, ast_function_label(func, "after_switch"));
+    bout = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "after_switch"));
     if (!bout)
         return false;
 
@@ -2489,9 +2614,9 @@ bool ast_switch_codegen(ast_switch *self, ast_function *func, bool lvalue, ir_va
             if (!cond)
                 return false;
 
-            bcase = ir_function_create_block(func->ir_func, ast_function_label(func, "case"));
+            bcase = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "case"));
             bnot_id = vec_size(func->ir_func->blocks);
-            bnot = ir_function_create_block(func->ir_func, ast_function_label(func, "not_case"));
+            bnot = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "not_case"));
             if (!bcase || !bnot)
                 return false;
             if (!ir_block_create_if(func->curblock, cond, bcase, bnot))
@@ -2569,6 +2694,78 @@ bool ast_switch_codegen(ast_switch *self, ast_function *func, bool lvalue, ir_va
     return true;
 }
 
+bool ast_label_codegen(ast_label *self, ast_function *func, bool lvalue, ir_value **out)
+{
+    size_t i;
+    ir_value *dummy;
+
+    *out = NULL;
+    if (lvalue) {
+        compile_error(ast_ctx(self), "internal error: ast_label cannot be an lvalue");
+        return false;
+    }
+
+    /* simply create a new block and jump to it */
+    self->irblock = ir_function_create_block(ast_ctx(self), func->ir_func, self->name);
+    if (!self->irblock) {
+        compile_error(ast_ctx(self), "failed to allocate label block `%s`", self->name);
+        return false;
+    }
+    if (!func->curblock->final) {
+        if (!ir_block_create_jump(func->curblock, self->irblock))
+            return false;
+    }
+
+    /* enter the new block */
+    func->curblock = self->irblock;
+
+    /* Generate all the leftover gotos */
+    for (i = 0; i < vec_size(self->gotos); ++i) {
+        if (!ast_goto_codegen(self->gotos[i], func, false, &dummy))
+            return false;
+    }
+
+    return true;
+}
+
+bool ast_goto_codegen(ast_goto *self, ast_function *func, bool lvalue, ir_value **out)
+{
+    *out = NULL;
+    if (lvalue) {
+        compile_error(ast_ctx(self), "internal error: ast_goto cannot be an lvalue");
+        return false;
+    }
+
+    if (self->target->irblock) {
+        if (self->irblock_from) {
+            /* we already tried once, this is the callback */
+            self->irblock_from->final = false;
+            if (!ir_block_create_jump(self->irblock_from, self->target->irblock)) {
+                compile_error(ast_ctx(self), "failed to generate goto to `%s`", self->name);
+                return false;
+            }
+        }
+        else
+        {
+            if (!ir_block_create_jump(func->curblock, self->target->irblock)) {
+                compile_error(ast_ctx(self), "failed to generate goto to `%s`", self->name);
+                return false;
+            }
+        }
+    }
+    else
+    {
+        /* the target has not yet been created...
+         * close this block in a sneaky way:
+         */
+        func->curblock->final = true;
+        self->irblock_from = func->curblock;
+        ast_label_register_goto(self->target, self);
+    }
+
+    return true;
+}
+
 bool ast_call_codegen(ast_call *self, ast_function *func, bool lvalue, ir_value **out)
 {
     ast_expression_codegen *cgen;
@@ -2580,7 +2777,7 @@ bool ast_call_codegen(ast_call *self, ast_function *func, bool lvalue, ir_value
 
     /* return values are never lvalues */
     if (lvalue) {
-        asterror(ast_ctx(self), "not an l-value (function call)");
+        compile_error(ast_ctx(self), "not an l-value (function call)");
         return false;
     }