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, ...)
{
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,
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 ||
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->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
self->operand = expr;
+ if (expr)
+ ast_propagate_effects(self, expr);
+
return self;
}
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);
}
self->owner = owner;
+ ast_propagate_effects(self, owner);
+
self->field = field;
if (name)
self->name = util_strdup(name);
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);
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;
}
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);
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;
}
self->operand = op;
self->cases = NULL;
+ ast_propagate_effects(self, op);
+
return self;
}
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;
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;
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);
#define ast_istype(x, t) ( ((ast_node_common*)x)->nodetype == (TYPE_##t) )
#define ast_ctx(node) (((ast_node_common*)(node))->context)
+#define ast_side_effects(node) (((ast_node_common*)(node))->side_effects)
/* Node interface with common components
*/
* prevents its dtor from destroying this node as well.
*/
bool keep;
+ bool side_effects;
} ast_node_common;
#define ast_delete(x) ( ( (ast_node*)(x) ) -> node.destroy )((ast_node*)(x))
bool ast_block_codegen(ast_block*, ast_function*, bool lvalue, ir_value**);
void ast_block_collect(ast_block*, ast_expression*);
+void ast_block_add_expr(ast_block*, ast_expression*);
+
/* Function
*
* Contains a list of blocks... at least in theory.
{ "==", 2, opid2('=','='), ASSOC_LEFT, 10, 0 },
{ "!=", 2, opid2('!','='), ASSOC_LEFT, 10, 0 },
+ { "?", 3, opid2('?',':'), ASSOC_RIGHT, 9, 0 },
+ { ":", 3, opid2(':','?'), ASSOC_RIGHT, 9, 0 },
+
{ "=", 2, opid1('='), ASSOC_RIGHT, 8, 0 },
{ "+=", 2, opid2('+','='), ASSOC_RIGHT, 8, 0 },
{ "-=", 2, opid2('-','='), ASSOC_RIGHT, 8, 0 },
{ "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0 },
{ "||", 2, opid2('|','|'), ASSOC_LEFT, 5, 0 },
- { ",", 2, opid1(','), ASSOC_LEFT, 2, 0 },
-
- { "?", 3, opid2('?',':'), ASSOC_RIGHT, 1, 0 },
- { ":", 3, opid2(':','?'), ASSOC_RIGHT, 1, 0 }
+ { ",", 2, opid1(','), ASSOC_LEFT, 2, 0 }
};
static const size_t fte_operator_count = (sizeof(fte_operators) / sizeof(fte_operators[0]));
case opid1(','):
if (blocks[0]) {
- vec_push(blocks[0]->exprs, exprs[1]);
+ ast_block_add_expr(blocks[0], exprs[1]);
} else {
blocks[0] = ast_block_new(ctx);
- vec_push(blocks[0]->exprs, exprs[0]);
- vec_push(blocks[0]->exprs, exprs[1]);
+ ast_block_add_expr(blocks[0], exprs[0]);
+ ast_block_add_expr(blocks[0], exprs[1]);
}
if (!ast_block_set_type(blocks[0], exprs[1]))
return false;
increment = parse_expression_leave(parser, false);
if (!increment)
goto onerr;
- if (!ast_istype(increment, ast_store) &&
- !ast_istype(increment, ast_call) &&
- !ast_istype(increment, ast_binstore))
- {
+ if (!ast_side_effects(increment)) {
if (genwarning(ast_ctx(increment), WARN_EFFECTLESS_STATEMENT, "statement has no effect"))
goto onerr;
}
}
if (!expr)
continue;
- vec_push(caseblock->exprs, expr);
+ ast_block_add_expr(caseblock, expr);
}
}
if (!exp)
return false;
*out = exp;
- if (!ast_istype(exp, ast_store) &&
- !ast_istype(exp, ast_call) &&
- !ast_istype(exp, ast_binstore))
- {
+ if (!ast_side_effects(exp)) {
if (genwarning(ast_ctx(exp), WARN_EFFECTLESS_STATEMENT, "statement has no effect"))
return false;
}
}
if (!expr)
continue;
- vec_push(block->exprs, expr);
+ ast_block_add_expr(block, expr);
}
if (parser->tok != '}') {
if (store_think) ast_delete(store_think);
retval = false;
}
- vec_push(block->exprs, (ast_expression*)store_frame);
- vec_push(block->exprs, (ast_expression*)store_nextthink);
- vec_push(block->exprs, (ast_expression*)store_think);
+ 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 (!retval) {
return NULL;
}
- vec_push(block->exprs, (ast_expression*)st);
+ ast_block_add_expr(block, (ast_expression*)st);
ret = ast_return_new(ctx, NULL);
if (!ret) {
return NULL;
}
- vec_push(block->exprs, (ast_expression*)ret);
+ ast_block_add_expr(block, (ast_expression*)ret);
return (ast_expression*)block;
} else {
return NULL;
}
- vec_push(block->exprs, (ast_expression*)st);
+ ast_block_add_expr(block, (ast_expression*)st);
ret = ast_return_new(ctx, NULL);
if (!ret) {
return NULL;
}
- vec_push(block->exprs, (ast_expression*)ret);
+ ast_block_add_expr(block, (ast_expression*)ret);
return (ast_expression*)block;
} else {
goto cleanup;
}
- vec_push(func->blocks[0]->exprs, root);
+ ast_block_add_expr(func->blocks[0], root);
array->setter = fval;
return true;
cleanup:
goto cleanup;
}
- vec_push(func->blocks[0]->exprs, root);
+ ast_block_add_expr(func->blocks[0], root);
array->setter = fval;
return true;
cleanup:
goto cleanup;
}
- vec_push(func->blocks[0]->exprs, root);
+ ast_block_add_expr(func->blocks[0], root);
array->getter = fval;
return true;
cleanup:
else {
if (vec_size(sy.out) != 1 && vec_size(sy.ops) != 0)
parseerror(parser, "internal error: leaked operands");
- vec_push(localblock->exprs, (ast_expression*)sy.out[0].out);
+ ast_block_add_expr(localblock, (ast_expression*)sy.out[0].out);
}
vec_free(sy.out);
vec_free(sy.ops);