diff --git a/src/compiler/checkers/fzl_loop_check.c b/src/compiler/checkers/fzl_loop_check.c index 8958292..ab58bc4 100644 --- a/src/compiler/checkers/fzl_loop_check.c +++ b/src/compiler/checkers/fzl_loop_check.c @@ -6,42 +6,214 @@ #include #include #include +#include + +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; } } diff --git a/src/compiler/fzl_walker.c b/src/compiler/fzl_walker.c index ee6aa64..021bb53 100644 --- a/src/compiler/fzl_walker.c +++ b/src/compiler/fzl_walker.c @@ -14,7 +14,8 @@ #include #include -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 = ""; 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); diff --git a/tests/programs/neg_infinite_for.fzs b/tests/programs/neg_infinite_for.fzs index 1de7327..8a7eb77 100644 --- a/tests/programs/neg_infinite_for.fzs +++ b/tests/programs/neg_infinite_for.fzs @@ -1,3 +1,3 @@ -for (int i = 0; i < 5; i = i - 1) +for (int i = 0; i < 5; i = i + 0) {}