feat: implement simple infinite loop checker

This commit is contained in:
samsonium
2026-10-22 20:24:48 +03:00
parent 5dde75523d
commit a20743a835
3 changed files with 202 additions and 27 deletions
+196 -24
View File
@@ -6,42 +6,214 @@
#include <compiler/utils/fzl_symtable.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
ssize_t check_top;
fzl_nodetype_t check_tree[256];
void ghost_walk_changed(fzl_node_t *node, bool *changed, fzl_sym_t *sym)
{
if (!node || !changed || *changed)
return;
check_tree[check_top++] = node->type;
switch (node->type)
{
case FZL_NODETYPE_DECLARATION:
case FZL_NODETYPE_CONSTRUCT:
case FZL_NODETYPE_CLASS:
case FZL_NODETYPE_FUNCTION:
case FZL_NODETYPE_LITERAL:
break;
case FZL_NODETYPE_UNARY_PLUS:
case FZL_NODETYPE_UNARY_MINUS:
case FZL_NODETYPE_NOT:
ghost_walk_changed(node->unary_op.operand, changed, sym);
break;
case FZL_NODETYPE_ADD:
case FZL_NODETYPE_SUB:
case FZL_NODETYPE_MUL:
case FZL_NODETYPE_DIV:
case FZL_NODETYPE_POW:
case FZL_NODETYPE_MOD:
case FZL_NODETYPE_OR:
case FZL_NODETYPE_AND:
case FZL_NODETYPE_EQUAL:
case FZL_NODETYPE_NEQUAL:
case FZL_NODETYPE_LESS:
case FZL_NODETYPE_LESS_EQUAL:
case FZL_NODETYPE_GREATER:
case FZL_NODETYPE_GREATER_EQUAL:
case FZL_NODETYPE_ASSIGN:
ghost_walk_changed(node->binary_op.left, changed, sym);
ghost_walk_changed(node->binary_op.right, changed, sym);
break;
case FZL_NODETYPE_FIELD:
ghost_walk_changed(node->memb.parent, changed, sym);
ghost_walk_changed(node->memb.field, changed, sym);
break;
case FZL_NODETYPE_RETURN:
ghost_walk_changed(node->ret.value, changed, sym);
break;
case FZL_NODETYPE_CONDITIONAL:
ghost_walk_changed(node->cond.condition, changed, sym);
ghost_walk_changed(node->cond.true_block, changed, sym);
ghost_walk_changed(node->cond.else_block, changed, sym);
break;
case FZL_NODETYPE_LOOP:
ghost_walk_changed(node->loop.cond, changed, sym);
ghost_walk_changed(node->loop.step, changed, sym);
ghost_walk_changed(node->loop.body, changed, sym);
break;
case FZL_NODETYPE_BLOCK:
for (size_t i = 0; i < node->list.entry_count; i++)
ghost_walk_changed(node->list.entries[i], changed, sym);
break;
case FZL_NODETYPE_CALL:
ghost_walk_changed(node->call.callee, changed, sym);
for (size_t i = 0; i < node->call.args_count; i++)
ghost_walk_changed(node->call.args[i], changed, sym);
break;
case FZL_NODETYPE_IDENTIFIER:
if (strcmp(node->token->value, sym->name) == 0)
{
*changed = true;
}
if (*changed)
{
*changed = false;
for (ssize_t i = check_top - 1; i >= 0; i--)
{
fzl_nodetype_t cur_t = check_tree[i];
if (cur_t == FZL_NODETYPE_ADD || cur_t == FZL_NODETYPE_SUB ||
cur_t == FZL_NODETYPE_MUL || cur_t == FZL_NODETYPE_DIV ||
cur_t == FZL_NODETYPE_POW || cur_t == FZL_NODETYPE_MOD ||
cur_t == FZL_NODETYPE_UNARY_MINUS)
{
*changed = true;
break;
}
}
}
break;
}
check_tree[--check_top] = 0;
}
// Whether value changed in loop body
bool value_is_changed(fzl_node_t *node, fzl_sym_t *sym)
{
if (!node || !sym)
return false;
bool changed = false;
check_top = 0;
memset(check_tree, 0, sizeof(check_tree));
ghost_walk_changed(node, &changed, sym);
return changed;
}
// Try to find symbol in loop condition node
bool retrieve_cond_sym(fzl_walker_t *walker, fzl_node_t *node, fzl_sym_t *sym)
{
if (!walker || !node)
return false;
switch (node->type)
{
case FZL_NODETYPE_BLOCK:
case FZL_NODETYPE_CALL:
case FZL_NODETYPE_LITERAL:
case FZL_NODETYPE_CONDITIONAL:
case FZL_NODETYPE_FUNCTION:
case FZL_NODETYPE_RETURN:
case FZL_NODETYPE_CLASS:
case FZL_NODETYPE_FIELD:
case FZL_NODETYPE_CONSTRUCT:
case FZL_NODETYPE_LOOP:
case FZL_NODETYPE_ASSIGN:
case FZL_NODETYPE_DECLARATION:
return false;
case FZL_NODETYPE_UNARY_MINUS:
case FZL_NODETYPE_UNARY_PLUS:
case FZL_NODETYPE_NOT:
return retrieve_cond_sym(walker, node->unary_op.operand, sym);
case FZL_NODETYPE_ADD:
case FZL_NODETYPE_SUB:
case FZL_NODETYPE_MUL:
case FZL_NODETYPE_DIV:
case FZL_NODETYPE_POW:
case FZL_NODETYPE_MOD:
case FZL_NODETYPE_AND:
case FZL_NODETYPE_OR:
case FZL_NODETYPE_LESS:
case FZL_NODETYPE_LESS_EQUAL:
case FZL_NODETYPE_GREATER:
case FZL_NODETYPE_GREATER_EQUAL:
case FZL_NODETYPE_EQUAL:
case FZL_NODETYPE_NEQUAL:
if (retrieve_cond_sym(walker, node->binary_op.left, sym))
return true;
if (retrieve_cond_sym(walker, node->binary_op.right, sym))
return true;
return false;
case FZL_NODETYPE_IDENTIFIER:
{
bool exists;
for (ssize_t i = walker->sym_top - 1; i >= 0; i--)
{
if ((exists = fzl_symt_get(walker->syms[i], (char *)node->token->value,
sym)))
break;
}
return exists;
}
}
return false;
}
// Check for infinite loop
bool is_infinite_loop(fzl_walker_t *walker, size_t start_ip, size_t end_ip)
bool is_infinite_loop(fzl_walker_t *walker, fzl_node_t *node, size_t start_ip,
size_t end_ip)
{
if (!walker)
return false;
end_ip = (end_ip >= walker->op_count) ? (walker->op_count - 1) : end_ip;
// Can we determine an init block?
size_t maybe_init_start = start_ip + 1;
size_t maybe_init_end = start_ip + 6;
fzl_const_t *declared = NULL;
for (size_t i = maybe_init_start; i <= maybe_init_end; i++)
fzl_sym_t sym;
bool sym_exists = retrieve_cond_sym(walker, node->loop.cond, &sym);
if (sym_exists)
{
if (walker->ops[i] != FZL_OPCODE_STORE)
continue;
declared = &walker->consts->array[walker->ops[i - 1]];
break;
}
if (declared)
{
fzl_sym_t sym;
bool found = false;
for (ssize_t i = walker->sym_top - 1; i >= 0; i--)
if ((found = fzl_symt_get(walker->syms[i],
(char *)declared->as.string.chars, &sym)))
break;
if (!found)
return false; // suspicious
// fzl_sym_t sym;
// bool found = false;
// for (ssize_t i = walker->sym_top - 1; i >= 0; i--)
// if ((found = fzl_symt_get(walker->syms[i],
// (char *)declared->as.string.chars, &sym)))
// break;
if (sym.type >= FZL_TYPE_INT8)
{
bool value_changed = value_is_changed(node, &sym);
if (value_changed)
return false;
}
}
+5 -2
View File
@@ -14,7 +14,8 @@
#include <stdlib.h>
#include <string.h>
extern bool is_infinite_loop(fzl_walker_t *walker, size_t ls, size_t le);
extern bool is_infinite_loop(fzl_walker_t *walker, fzl_node_t *node, size_t ls,
size_t le);
char *null_name = "<null>";
char wlast_err[256];
@@ -221,6 +222,7 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
push_op(walker, FZL_OPCODE_MK_ENVTABLE);
walker->syms[walker->sym_top++] = fzl_mksymt();
size_t loop_begin_ip = walker->op_count;
if (node->loop.init)
{
@@ -274,9 +276,10 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
push_op(walker, FZL_OPCODE_RM_ENVTABLE);
bool is_infinite =
is_infinite_loop(walker, start_ip, walker->op_count - 2);
is_infinite_loop(walker, node, loop_begin_ip, walker->op_count - 2);
if (is_infinite)
{
free(newctx->breaks);
snprintf(wlast_err, sizeof(wlast_err),
"'%s': infinite loop detected at %ld:%ld",
node->token->filename, node->token->ln, node->token->cn);
+1 -1
View File
@@ -1,3 +1,3 @@
for (int i = 0; i < 5; i = i - 1)
for (int i = 0; i < 5; i = i + 0)
{}