aboutsummaryrefslogtreecommitdiff
path: root/src/move.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/move.c')
-rw-r--r--src/move.c143
1 files changed, 131 insertions, 12 deletions
diff --git a/src/move.c b/src/move.c
index 7f54427..a87d6dd 100644
--- a/src/move.c
+++ b/src/move.c
@@ -46,6 +46,34 @@ static void destroy_state(struct state *state)
moved_destroy(&state->referenced);
}
+struct rm_move {
+ struct ast_pair data;
+ struct state *owner;
+};
+
+static struct rm_move remove_move(struct state *state, struct ast *def)
+{
+ if (!state)
+ return (struct rm_move){.data = {}, .owner = NULL};
+
+ struct ast_pair search = {.def = def};
+ struct ast_pair *found = moved_find(&state->moved, search);
+ if (found) {
+ moved_remove_found(&state->moved, found);
+ return (struct rm_move){.data = *found, .owner = state};
+ }
+
+ return remove_move(state->parent, def);
+}
+
+static void reinsert_move(struct rm_move move)
+{
+ if (!move.owner)
+ return;
+
+ moved_insert(&move.owner->moved, move.data);
+}
+
static struct ast_pair *find_move(struct state *state, struct ast *def)
{
if (!state)
@@ -195,9 +223,66 @@ static int mvcheck_proc(struct state *state, struct ast *node)
return ret;
}
+static int total_check_single(struct state *state, struct scope *scope)
+{
+ int ret = 0;
+ foreach_visible(n, scope->symbols) {
+ struct ast *def = n->node;
+ if (def->k != AST_VAR_DEF)
+ continue;
+
+ if (is_trivially_copyable(def->t))
+ continue;
+
+ struct ast_pair *prev = find_move(state, def);
+ if (prev)
+ continue;
+
+ semantic_warn(scope, def, "%s not moved, might leak", var_id(def));
+ ret |= 1;
+ }
+
+ return ret;
+}
+
+static int total_check_proc(struct state *state, struct scope *scope)
+{
+ int ret = total_check_single(state, scope);
+ if (scope->flags & SCOPE_PROC)
+ return ret;
+
+ if (!scope->parent)
+ return ret;
+
+ return ret | total_check_proc(state, scope->parent);
+}
+
static int mvcheck_block(struct state *state, struct ast *node)
{
- return mvcheck_statements(state, block_body(node));
+ struct state new_state = create_state(state);
+ int ret = mvcheck_statements(&new_state, block_body(node));
+ if (ret) {
+ destroy_state(&new_state);
+ return ret;
+ }
+
+ if (block_error(node)) {
+ /* don't push state up since there's no way to continue
+ * execution after an error, so whatever code follows this
+ * didn't see any moves take place */
+ if (total_check_proc(&new_state, node->scope))
+ semantic_info(node->scope, node, "at end of block");
+
+ destroy_state(&new_state);
+ return 0;
+ }
+
+ if (total_check_single(&new_state, node->scope))
+ semantic_info(node->scope, node, "at end of block");
+
+ push_up(&new_state);
+ destroy_state(&new_state);
+ return 0;
}
static int mvcheck_call(struct state *state, struct ast *node)
@@ -233,6 +318,17 @@ static int mvcheck_call(struct state *state, struct ast *node)
break;
}
+ if (!ret && call_err(node)) {
+ struct state err_state = create_state(state);
+ ret = mvcheck(&err_state, call_err(node));
+ push_up(&err_state);
+ destroy_state(&err_state);
+ }
+ else if (!ret) {
+ if (total_check_proc(state, node->scope))
+ semantic_info(node->scope, node, "in implicit error branch");
+ }
+
destroy_state(&group_state);
return ret;
}
@@ -244,8 +340,11 @@ static int mvcheck_closure(struct state *state, struct ast *node)
int ret = mvcheck(&new_state, closure_body(node));
push_up(&new_state);
-
destroy_state(&new_state);
+
+ if (total_check_single(state, node->scope))
+ semantic_info(node->scope, node, "in closure");
+
return ret;
}
@@ -398,6 +497,24 @@ static int mvcheck_statements(struct state *state, struct ast *nodes)
return 0;
}
+static int mvcheck_err_branch(struct state *state, struct ast *node)
+{
+ return mvcheck(state, err_branch_body(node));
+}
+
+static int mvcheck_own(struct state *state, struct ast *node)
+{
+ struct ast *def = file_scope_find_symbol(node->scope, own_id(node));
+ assert(def);
+
+ struct rm_move prev = remove_move(state, def);
+ int ret = mvcheck(state, own_body(node));
+
+ /* insert back */
+ reinsert_move(prev);
+ return ret;
+}
+
static int mvcheck(struct state *state, struct ast *node)
{
if (is_unop(node))
@@ -410,16 +527,18 @@ static int mvcheck(struct state *state, struct ast *node)
return mvcheck_comparison(state, node);
switch (node->k) {
- case AST_PROC_DEF: return mvcheck_proc (state, node);
- case AST_BLOCK: return mvcheck_block (state, node);
- case AST_CALL: return mvcheck_call (state, node);
- case AST_CLOSURE: return mvcheck_closure (state, node);
- case AST_LET: return mvcheck_let (state, node);
- case AST_ID: return mvcheck_id (state, node);
- case AST_INIT: return mvcheck_init (state, node);
- case AST_IF: return mvcheck_if (state, node);
- case AST_REF: return mvcheck_ref (state, node);
- case AST_DEREF: return mvcheck_deref (state, node);
+ case AST_PROC_DEF: return mvcheck_proc (state, node);
+ case AST_BLOCK: return mvcheck_block (state, node);
+ case AST_CALL: return mvcheck_call (state, node);
+ case AST_CLOSURE: return mvcheck_closure (state, node);
+ case AST_ERR_BRANCH: return mvcheck_err_branch(state, node);
+ case AST_OWN: return mvcheck_own (state, node);
+ case AST_LET: return mvcheck_let (state, node);
+ case AST_ID: return mvcheck_id (state, node);
+ case AST_INIT: return mvcheck_init (state, node);
+ case AST_IF: return mvcheck_if (state, node);
+ case AST_REF: return mvcheck_ref (state, node);
+ case AST_DEREF: return mvcheck_deref (state, node);
case AST_EMPTY:
case AST_CONST_INT:
case AST_CONST_STR: