]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - ast.c
don't set the request-Lvalue flag for an array index
[xonotic/gmqcc.git] / ast.c
diff --git a/ast.c b/ast.c
index 716ba1ac4a51f031a94839c13952bbc620480de6..cab7def717497be12e371098091be640b73b6762 100644 (file)
--- a/ast.c
+++ b/ast.c
@@ -35,6 +35,7 @@
     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, ...)
 {
@@ -47,6 +48,7 @@ static void asterror(lex_ctx ctx, const char *msg, ...)
 /* It must not be possible to get here. */
 static GMQCC_NORETURN void _ast_node_destroy(ast_node *self)
 {
+    (void)self;
     con_err("ast node missing destroy()\n");
     abort();
 }
@@ -58,8 +60,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)
@@ -320,7 +331,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));
 
@@ -338,7 +350,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:
@@ -384,6 +396,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 ||
@@ -413,6 +428,8 @@ 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;
@@ -449,6 +466,8 @@ 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
@@ -471,6 +490,9 @@ ast_return* ast_return_new(lex_ctx ctx, ast_expression *expr)
 
     self->operand = expr;
 
+    if (expr)
+        ast_propagate_effects(self, expr);
+
     return self;
 }
 
@@ -505,6 +527,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);
@@ -522,7 +546,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) {
@@ -549,7 +573,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;
 }
@@ -584,6 +614,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);
@@ -623,6 +655,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;
 }
@@ -651,6 +688,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);
@@ -685,6 +725,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;
 }
 
@@ -728,6 +779,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;
 }
 
@@ -747,12 +800,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;
 
@@ -807,6 +911,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;
@@ -846,6 +952,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);
@@ -888,9 +1000,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)
     {
+        asterror(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;
     }
@@ -908,7 +1024,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;
@@ -921,7 +1037,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*
@@ -966,6 +1082,8 @@ const char* ast_function_label(ast_function *self, const char *prefix)
 
 bool ast_value_codegen(ast_value *self, ast_function *func, bool lvalue, ir_value **out)
 {
+    (void)func;
+    (void)lvalue;
     /* NOTE: This is the codegen for a variable used in an expression.
      * It is not the codegen to generate the value. For this purpose,
      * ast_local_codegen and ast_global_codegen are to be used before this
@@ -987,7 +1105,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)
@@ -1004,7 +1122,7 @@ 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) {
+        if (self->hasvalue) {
             asterror(ast_ctx(self), "TODO: constant field pointers with value");
             goto error;
         }
@@ -1032,7 +1150,7 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
 
             v = ir_builder_create_field(ir, self->name, vtype);
             if (!v) {
-                asterror(ast_ctx(self), "ir_builder_create_global failed");
+                asterror(ast_ctx(self), "ir_builder_create_global failed on `%s`", self->name);
                 return false;
             }
             if (vtype == TYPE_FIELD)
@@ -1051,7 +1169,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");
+                    asterror(ast_ctx(self), "ir_builder_create_global failed on `%s`", name);
                     return false;
                 }
                 if (vtype == TYPE_FIELD)
@@ -1085,7 +1203,7 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
 
         v = ir_builder_create_global(ir, self->name, vtype);
         if (!v) {
-            asterror(ast_ctx(self), "ir_builder_create_global failed");
+            asterror(ast_ctx(self), "ir_builder_create_global failed `%s`", self->name);
             return false;
         }
         if (vtype == TYPE_FIELD)
@@ -1103,7 +1221,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");
+                asterror(ast_ctx(self), "ir_builder_create_global failed `%s`", name);
                 return false;
             }
             if (vtype == TYPE_FIELD)
@@ -1119,7 +1237,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");
+            asterror(ast_ctx(self), "ir_builder_create_global failed on `%s`", self->name);
             return false;
         }
         if (self->expression.vtype == TYPE_FIELD)
@@ -1127,7 +1245,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:
@@ -1169,7 +1287,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.
@@ -1219,7 +1337,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");
+                asterror(ast_ctx(self), "ir_builder_create_global failed on `%s`", name);
                 return false;
             }
             if (vtype == TYPE_FIELD)
@@ -1240,7 +1358,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:
@@ -1290,6 +1408,8 @@ bool ast_function_codegen(ast_function *self, ir_builder *ir)
     ast_expression_common *ec;
     size_t    i;
 
+    (void)ir;
+
     irf = self->ir_func;
     if (!irf) {
         asterror(ast_ctx(self), "ast_function's related ast_value was not generated yet");
@@ -1396,7 +1516,7 @@ 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) {
+        if (func->curblock->final && !ast_istype(self->exprs[i], ast_label)) {
             asterror(ast_ctx(self->exprs[i]), "unreachable statement");
             return false;
         }
@@ -1435,7 +1555,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;
     }
 
@@ -1636,7 +1756,12 @@ bool ast_binary_codegen(ast_binary *self, ast_function *func, bool lvalue, ir_va
 bool ast_binstore_codegen(ast_binstore *self, ast_function *func, bool lvalue, ir_value **out)
 {
     ast_expression_codegen *cgen;
-    ir_value *leftl, *leftr, *right, *bin;
+    ir_value *leftl = NULL, *leftr, *right, *bin;
+
+    ast_value       *arr;
+    ast_value       *idx = 0;
+    ast_array_index *ai = NULL;
+    ir_value        *iridx = NULL;
 
     if (lvalue && self->expression.outl) {
         *out = self->expression.outl;
@@ -1648,8 +1773,23 @@ bool ast_binstore_codegen(ast_binstore *self, ast_function *func, bool lvalue, i
         return true;
     }
 
+    if (ast_istype(self->dest, ast_array_index))
+    {
+
+        ai = (ast_array_index*)self->dest;
+        idx = (ast_value*)ai->index;
+
+        if (ast_istype(ai->index, ast_value) && idx->hasvalue)
+            ai = NULL;
+    }
+
     /* for a binstore we need both an lvalue and an rvalue for the left side */
     /* rvalue of destination! */
+    if (ai) {
+        cgen = idx->expression.codegen;
+        if (!(*cgen)((ast_expression*)(idx), func, false, &iridx))
+            return false;
+    }
     cgen = self->dest->expression.codegen;
     if (!(*cgen)((ast_expression*)(self->dest), func, false, &leftr))
         return false;
@@ -1664,16 +1804,45 @@ bool ast_binstore_codegen(ast_binstore *self, ast_function *func, bool lvalue, i
                                 self->opbin, leftr, right);
     self->expression.outr = bin;
 
-    /* now store them */
-    cgen = self->dest->expression.codegen;
-    /* lvalue of destination */
-    if (!(*cgen)((ast_expression*)(self->dest), func, true, &leftl))
-        return false;
-    self->expression.outl = leftl;
 
-    if (!ir_block_create_store_op(func->curblock, self->opstore, leftl, bin))
-        return false;
-    self->expression.outr = bin;
+    if (ai) {
+        /* we need to call the setter */
+        ir_value  *funval;
+        ir_instr  *call;
+
+        if (lvalue) {
+            asterror(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);
+            return false;
+        }
+
+        cgen = arr->setter->expression.codegen;
+        if (!(*cgen)((ast_expression*)(arr->setter), func, true, &funval))
+            return false;
+
+        call = ir_block_create_call(func->curblock, ast_function_label(func, "store"), funval);
+        if (!call)
+            return false;
+        ir_call_param(call, iridx);
+        ir_call_param(call, bin);
+        self->expression.outr = bin;
+    } else {
+        /* now store them */
+        cgen = self->dest->expression.codegen;
+        /* lvalue of destination */
+        if (!(*cgen)((ast_expression*)(self->dest), func, true, &leftl))
+            return false;
+        self->expression.outl = leftl;
+
+        if (!ir_block_create_store_op(func->curblock, self->opstore, leftl, bin))
+            return false;
+        self->expression.outr = bin;
+    }
 
     /* Theoretically, an assinment returns its left side as an
      * lvalue, if we don't need an lvalue though, we return
@@ -1722,6 +1891,8 @@ bool ast_return_codegen(ast_return *self, ast_function *func, bool lvalue, ir_va
     ast_expression_codegen *cgen;
     ir_value *operand;
 
+    *out = NULL;
+
     /* In the context of a return operation, we don't actually return
      * anything...
      */
@@ -1857,7 +2028,7 @@ 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;
@@ -1874,7 +2045,7 @@ bool ast_array_index_codegen(ast_array_index *self, ast_function *func, bool lva
         }
 
         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;
@@ -2336,6 +2507,8 @@ bool ast_breakcont_codegen(ast_breakcont *self, ast_function *func, bool lvalue,
 {
     ir_block *target;
 
+    *out = NULL;
+
     if (lvalue) {
         asterror(ast_ctx(self), "break/continue expression is not an l-value");
         return false;
@@ -2511,6 +2684,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) {
+        asterror(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(func->ir_func, self->name);
+    if (!self->irblock) {
+        asterror(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) {
+        asterror(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)) {
+                asterror(ast_ctx(self), "failed to generate goto to `%s`", self->name);
+                return false;
+            }
+        }
+        else
+        {
+            if (!ir_block_create_jump(func->curblock, self->target->irblock)) {
+                asterror(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;