diff options
| author | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-20 18:05:40 +0000 |
|---|---|---|
| committer | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-20 18:05:40 +0000 |
| commit | 2e468d337c32d50ce1609783464149458b5f8e81 (patch) | |
| tree | 5a735da4bbc5eff488e06115c861c250e53dbaef /src/analysis | |
| parent | 50e2dde008820ef5ae8bd502d2646e00e5139bc4 (diff) | |
Resource semantics
Diffstat (limited to 'src/analysis')
| -rw-r--r-- | src/analysis/typecheck.c | 138 |
1 files changed, 133 insertions, 5 deletions
diff --git a/src/analysis/typecheck.c b/src/analysis/typecheck.c index e3abf10..2b06bb4 100644 --- a/src/analysis/typecheck.c +++ b/src/analysis/typecheck.c @@ -69,10 +69,114 @@ static Symbol *tc_lookup(TypeChecker *tc, const char *name) return NULL; } +// ** Move Semantics Helpers ** + +static int is_safe_to_copy(TypeChecker *tc, Type *t) +{ + // Use parser's helper if available, or simple heuristic + return is_type_copy(tc->pctx, t); +} + +static void check_use_validity(TypeChecker *tc, ASTNode *var_node, Symbol *sym) +{ + if (!sym || !var_node) + { + return; + } + + if (sym->is_moved) + { + char msg[256]; + snprintf( + msg, 255, + "Use of moved value '%s'. This type owns resources and cannot be implicitly copied.", + sym->name); + tc_error(tc, var_node->token, msg); + } +} + +static void mark_symbol_moved(TypeChecker *tc, Symbol *sym, ASTNode *context_node) +{ + if (!sym) + { + return; + } + + // Only move if type is NOT Copy + Type *t = sym->type_info; + if (t && !is_safe_to_copy(tc, t)) + { + sym->is_moved = 1; + } +} + +static void mark_symbol_valid(TypeChecker *tc, Symbol *sym) +{ + if (sym) + { + sym->is_moved = 0; + } +} + // ** Node Checkers ** static void check_node(TypeChecker *tc, ASTNode *node); +static void check_expr_binary(TypeChecker *tc, ASTNode *node) +{ + check_node(tc, node->binary.left); + check_node(tc, node->binary.right); + + // Assignment Logic for Moves + if (strcmp(node->binary.op, "=") == 0) + { + // If RHS is a var, it might Move + if (node->binary.right->type == NODE_EXPR_VAR) + { + Symbol *rhs_sym = tc_lookup(tc, node->binary.right->var_ref.name); + if (rhs_sym) + { + mark_symbol_moved(tc, rhs_sym, node); + } + } + + // LHS is being (re-)initialized, so it becomes Valid. + if (node->binary.left->type == NODE_EXPR_VAR) + { + Symbol *lhs_sym = tc_lookup(tc, node->binary.left->var_ref.name); + if (lhs_sym) + { + mark_symbol_valid(tc, lhs_sym); + } + } + } +} + +static void check_expr_call(TypeChecker *tc, ASTNode *node) +{ + check_node(tc, node->call.callee); + + // Check arguments + ASTNode *arg = node->call.args; + while (arg) + { + check_node(tc, arg); + + // If argument is passed by VALUE, and it's a variable, it MOVES. + // If passed by ref (UNARY '&'), the child was checked but Is Not A Var Node itself. + if (arg->type == NODE_EXPR_VAR) + { + Symbol *sym = tc_lookup(tc, arg->var_ref.name); + if (sym) + { + mark_symbol_moved(tc, sym, node); + } + } + + arg = arg->next; + } +} + static void check_block(TypeChecker *tc, ASTNode *block) { tc_enter_scope(tc); @@ -140,6 +244,16 @@ static void check_var_decl(TypeChecker *tc, ASTNode *node) { check_type_compatibility(tc, decl_type, init_type, node->token); } + + // Move Analysis: If initializing from another variable, it moves. + if (node->var_decl.init_expr->type == NODE_EXPR_VAR) + { + Symbol *init_sym = tc_lookup(tc, node->var_decl.init_expr->var_ref.name); + if (init_sym) + { + mark_symbol_moved(tc, init_sym, node); + } + } } // If type is not explicit, we should ideally infer it from init_expr. @@ -187,6 +301,9 @@ static void check_expr_var(TypeChecker *tc, ASTNode *node) { node->type_info = sym->type_info; } + + // Check for Use-After-Move + check_use_validity(tc, node, sym); } static void check_node(TypeChecker *tc, ASTNode *node) @@ -240,15 +357,26 @@ static void check_node(TypeChecker *tc, ASTNode *node) tc_exit_scope(tc); break; case NODE_EXPR_BINARY: - check_node(tc, node->binary.left); - check_node(tc, node->binary.right); + check_expr_binary(tc, node); break; case NODE_EXPR_CALL: - check_node(tc, node->call.callee); - check_node(tc, node->call.args); + check_expr_call(tc, node); break; default: - // Generic recursion for lists. + // Generic recursion for lists and other nodes. + // Special case for Return to trigger move? + if (node->type == NODE_RETURN && node->ret.value) + { + // If returning a variable by value, it is moved. + if (node->ret.value->type == NODE_EXPR_VAR) + { + Symbol *sym = tc_lookup(tc, node->ret.value->var_ref.name); + if (sym) + { + mark_symbol_moved(tc, sym, node); + } + } + } break; } |
