summaryrefslogtreecommitdiff
path: root/src/analysis/typecheck.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/analysis/typecheck.c')
-rw-r--r--src/analysis/typecheck.c493
1 files changed, 222 insertions, 271 deletions
diff --git a/src/analysis/typecheck.c b/src/analysis/typecheck.c
index a19393b..e3abf10 100644
--- a/src/analysis/typecheck.c
+++ b/src/analysis/typecheck.c
@@ -1,327 +1,278 @@
+
#include "typecheck.h"
-#include "../ast/ast.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// ** Internal Helpers **
-static void tc_error(TypeChecker *tc, Token t, const char *msg) {
- fprintf(stderr, "Type Error at %s:%d:%d: %s\n", g_current_filename, t.line,
- t.col, msg);
- tc->error_count++;
-}
-
-static void tc_enter_scope(TypeChecker *tc) {
- Scope *s = malloc(sizeof(Scope));
- s->symbols = NULL;
- s->parent = tc->current_scope;
- tc->current_scope = s;
+static void tc_error(TypeChecker *tc, Token t, const char *msg)
+{
+ fprintf(stderr, "Type Error at %s:%d:%d: %s\n", g_current_filename, t.line, t.col, msg);
+ tc->error_count++;
}
-static void tc_exit_scope(TypeChecker *tc) {
- if (!tc->current_scope) {
- return;
- }
- Scope *old = tc->current_scope;
- tc->current_scope = old->parent;
-
- Symbol *sym = old->symbols;
- while (sym) {
- Symbol *next = sym->next;
- free(sym);
- sym = next;
- }
- free(old);
+static void tc_enter_scope(TypeChecker *tc)
+{
+ Scope *s = malloc(sizeof(Scope));
+ s->symbols = NULL;
+ s->parent = tc->current_scope;
+ tc->current_scope = s;
}
-static void tc_add_symbol(TypeChecker *tc, const char *name, Type *type,
- Token t) {
- Symbol *s = malloc(sizeof(Symbol));
- memset(s, 0, sizeof(Symbol));
- s->name = strdup(name);
- s->type_info = type;
- s->decl_token = t;
- s->next = tc->current_scope->symbols;
- tc->current_scope->symbols = s;
-}
-
-static Symbol *tc_lookup(TypeChecker *tc, const char *name) {
- Scope *s = tc->current_scope;
- while (s) {
- Symbol *curr = s->symbols;
- while (curr) {
- if (0 == strcmp(curr->name, name)) {
- return curr;
- }
- curr = curr->next;
+static void tc_exit_scope(TypeChecker *tc)
+{
+ if (!tc->current_scope)
+ {
+ return;
}
- s = s->parent;
- }
- return NULL;
-}
-
-static int is_integer_type(Type *t) {
- if (!t)
- return 0;
- // Check all integer kinds defined in TypeKind
- return (t->kind >= TYPE_I8 && t->kind <= TYPE_U128) || t->kind == TYPE_INT ||
- t->kind == TYPE_UINT || t->kind == TYPE_USIZE || t->kind == TYPE_ISIZE;
+ Scope *old = tc->current_scope;
+ tc->current_scope = old->parent;
+
+ Symbol *sym = old->symbols;
+ while (sym)
+ {
+ Symbol *next = sym->next;
+ free(sym);
+ sym = next;
+ }
+ free(old);
}
-static int is_signed_integer(Type *t) {
- if (!t)
- return 0;
- return (t->kind == TYPE_I8 || t->kind == TYPE_I16 || t->kind == TYPE_I32 ||
- t->kind == TYPE_I64 || t->kind == TYPE_I128 || t->kind == TYPE_INT ||
- t->kind == TYPE_ISIZE);
+static void tc_add_symbol(TypeChecker *tc, const char *name, Type *type, Token t)
+{
+ Symbol *s = malloc(sizeof(Symbol));
+ memset(s, 0, sizeof(Symbol));
+ s->name = strdup(name);
+ s->type_info = type;
+ s->decl_token = t;
+ s->next = tc->current_scope->symbols;
+ tc->current_scope->symbols = s;
}
-// Check if node is a literal integer
-// This allows '0' to be used with unsigned types without casting.
-static int is_safe_integer_literal(ASTNode *node) {
- if (!node || node->type != NODE_EXPR_LITERAL)
- return 0;
-
- // In Zen C, string/char literals have string_val set.
- if (node->literal.string_val != NULL)
- return 0;
-
- // In codegen.c, type_kind == 1 represents floats
- if (node->literal.type_kind == 1)
- return 0;
-
- // If it's not a string, char, or float, it's an integer literal.
- // Parsed integer literals (e.g. 123) are positive.
- // Negative numbers are handled as UnaryOp(-, Literal(123)), so this
- // safe check correctly returns false for negative literals.
- return 1;
+static Symbol *tc_lookup(TypeChecker *tc, const char *name)
+{
+ Scope *s = tc->current_scope;
+ while (s)
+ {
+ Symbol *curr = s->symbols;
+ while (curr)
+ {
+ if (0 == strcmp(curr->name, name))
+ {
+ return curr;
+ }
+ curr = curr->next;
+ }
+ s = s->parent;
+ }
+ return NULL;
}
// ** Node Checkers **
static void check_node(TypeChecker *tc, ASTNode *node);
-static void check_block(TypeChecker *tc, ASTNode *block) {
- tc_enter_scope(tc);
- ASTNode *stmt = block->block.statements;
- while (stmt) {
- check_node(tc, stmt);
- stmt = stmt->next;
- }
- tc_exit_scope(tc);
+static void check_block(TypeChecker *tc, ASTNode *block)
+{
+ tc_enter_scope(tc);
+ ASTNode *stmt = block->block.statements;
+ while (stmt)
+ {
+ check_node(tc, stmt);
+ stmt = stmt->next;
+ }
+ tc_exit_scope(tc);
}
-static int check_type_compatibility(TypeChecker *tc, Type *target,
- ASTNode *value_expr, Token t) {
- if (!target || !value_expr)
- return 1;
-
- Type *value_type = value_expr->type_info;
- if (!value_type)
- return 1; // Can't check yet
-
- // 1. Exact Match
- if (type_eq(target, value_type))
- return 1;
-
- // 2. Void Pointer Generics (void* matches any pointer)
- if (target->kind == TYPE_POINTER && target->inner->kind == TYPE_VOID)
- return 1;
- if (value_type->kind == TYPE_POINTER && value_type->inner->kind == TYPE_VOID)
- return 1;
-
- // 3. Integer Promotion / Safety Checks
- if (is_integer_type(target) && is_integer_type(value_type)) {
- int target_signed = is_signed_integer(target);
- int value_signed = is_signed_integer(value_type);
-
- // A. Signed/Unsigned Mismatch
- if (target_signed != value_signed) {
- // ERGONOMIC FIX: Allow implicit conversion IF it is a safe positive
- // literal e.g. 'usize x = 0;' or 'if (len > 0)'
- if (is_safe_integer_literal(value_expr)) {
- return 1; // Safe!
- }
-
- // Otherwise, warn strict
- char *t_str = type_to_string(target);
- char *v_str = type_to_string(value_type);
- char msg[256];
- snprintf(msg, 255,
- "Sign mismatch: cannot implicitly convert '%s' to '%s' (use "
- "cast or unsigned literal)",
- v_str, t_str);
- tc_error(tc, t, msg);
- free(t_str);
- free(v_str);
- return 0;
+static int check_type_compatibility(TypeChecker *tc, Type *target, Type *value, Token t)
+{
+ if (!target || !value)
+ {
+ return 1; // Can't check
}
- // B. Size truncation could be checked here (optional)
+ // Simple equality check for now... This will be changed.
+ if (!type_eq(target, value))
+ {
+
+ // For now we have strict equality on structure.
+
+ // In Zen C (like C), void* is generic.
+ if (TYPE_POINTER == target->kind && TYPE_VOID == target->inner->kind)
+ {
+ return 1;
+ }
+ if (TYPE_POINTER == value->kind && TYPE_VOID == value->inner->kind)
+ {
+ return 1;
+ }
+
+ // Exception: integer promotion/demotion.
+
+ if (is_integer_type(target) && is_integer_type(value))
+ {
+ return 1;
+ }
+
+ char *t_str = type_to_string(target);
+ char *v_str = type_to_string(value);
+ char msg[256];
+ snprintf(msg, 255, "Type mismatch: expected '%s', got '%s'", t_str, v_str);
+ tc_error(tc, t, msg);
+ free(t_str);
+ free(v_str);
+ return 0;
+ }
return 1;
- }
-
- // 4. Default Failure
- char *t_str = type_to_string(target);
- char *v_str = type_to_string(value_type);
- char msg[256];
- snprintf(msg, 255, "Type mismatch: expected '%s', got '%s'", t_str, v_str);
- tc_error(tc, t, msg);
- free(t_str);
- free(v_str);
- return 0;
}
-static void check_var_decl(TypeChecker *tc, ASTNode *node) {
- if (node->var_decl.init_expr) {
- check_node(tc, node->var_decl.init_expr);
+static void check_var_decl(TypeChecker *tc, ASTNode *node)
+{
+ if (node->var_decl.init_expr)
+ {
+ check_node(tc, node->var_decl.init_expr);
- Type *decl_type = node->type_info;
+ Type *decl_type = node->type_info;
+ Type *init_type = node->var_decl.init_expr->type_info;
- if (decl_type) {
- // Pass the full expression node to allow literal checking
- check_type_compatibility(tc, decl_type, node->var_decl.init_expr,
- node->token);
+ if (decl_type && init_type)
+ {
+ check_type_compatibility(tc, decl_type, init_type, node->token);
+ }
}
- }
- Type *t = node->type_info;
- if (!t && node->var_decl.init_expr) {
- t = node->var_decl.init_expr->type_info;
- }
+ // If type is not explicit, we should ideally infer it from init_expr.
+ Type *t = node->type_info;
+ if (!t && node->var_decl.init_expr)
+ {
+ t = node->var_decl.init_expr->type_info;
+ }
- tc_add_symbol(tc, node->var_decl.name, t, node->token);
- node->type_info = t;
+ tc_add_symbol(tc, node->var_decl.name, t, node->token);
}
-static void check_function(TypeChecker *tc, ASTNode *node) {
- (void)tc_error;
+static void check_function(TypeChecker *tc, ASTNode *node)
+{
+ // Just to suppress the warning.
+ (void)tc_error;
- tc->current_func = node;
- tc_enter_scope(tc);
+ tc->current_func = node;
+ tc_enter_scope(tc);
- for (int i = 0; i < node->func.arg_count; i++) {
- if (node->func.param_names && node->func.param_names[i]) {
- // FIXED: use arg_types from struct, not param_types
- Type *t = (node->func.arg_types) ? node->func.arg_types[i] : NULL;
- tc_add_symbol(tc, node->func.param_names[i], t, (Token){0});
+ for (int i = 0; i < node->func.arg_count; i++)
+ {
+ if (node->func.param_names && node->func.param_names[i])
+ {
+ tc_add_symbol(tc, node->func.param_names[i], NULL, (Token){0});
+ }
}
- }
- check_node(tc, node->func.body);
+ check_node(tc, node->func.body);
- tc_exit_scope(tc);
- tc->current_func = NULL;
+ tc_exit_scope(tc);
+ tc->current_func = NULL;
}
-static void check_expr_var(TypeChecker *tc, ASTNode *node) {
- Symbol *sym = tc_lookup(tc, node->var_ref.name);
- if (sym && sym->type_info) {
- node->type_info = sym->type_info;
- }
+static void check_expr_var(TypeChecker *tc, ASTNode *node)
+{
+ Symbol *sym = tc_lookup(tc, node->var_ref.name);
+ if (!sym)
+ {
+ // Check global functions/contexts if not found in locals
+ // This is a naive check.
+ // We really want to warn here if it's truly unknown.
+ }
+ if (sym && sym->type_info)
+ {
+ node->type_info = sym->type_info;
+ }
}
-static void check_node(TypeChecker *tc, ASTNode *node) {
- if (!node) {
- return;
- }
-
- switch (node->type) {
- case NODE_ROOT:
- check_node(tc, node->root.children);
- break;
- case NODE_BLOCK:
- check_block(tc, node);
- break;
- case NODE_VAR_DECL:
- check_var_decl(tc, node);
- break;
- case NODE_FUNCTION:
- check_function(tc, node);
- break;
- case NODE_EXPR_VAR:
- check_expr_var(tc, node);
- break;
- case NODE_RETURN:
- if (node->ret.value) {
- check_node(tc, node->ret.value);
- // Check return type matches function signature
- if (tc->current_func && tc->current_func->type_info) {
- check_type_compatibility(tc, tc->current_func->type_info,
- node->ret.value, node->token);
- }
+static void check_node(TypeChecker *tc, ASTNode *node)
+{
+ if (!node)
+ {
+ return;
}
- break;
-
- case NODE_IF:
- check_node(tc, node->if_stmt.condition);
- check_node(tc, node->if_stmt.then_body);
- check_node(tc, node->if_stmt.else_body);
- break;
- case NODE_WHILE:
- check_node(tc, node->while_stmt.condition);
- check_node(tc, node->while_stmt.body);
- break;
- case NODE_FOR:
- tc_enter_scope(tc);
- check_node(tc, node->for_stmt.init);
- check_node(tc, node->for_stmt.condition);
- check_node(tc, node->for_stmt.step);
- check_node(tc, node->for_stmt.body);
- tc_exit_scope(tc);
- break;
-
- case NODE_EXPR_BINARY:
- check_node(tc, node->binary.left);
- check_node(tc, node->binary.right);
- // Infer type from left operand
- if (node->binary.left->type_info) {
- node->type_info = node->binary.left->type_info;
-
- // Perform the comparison check using the robust compatibility logic
- // This will trigger is_safe_integer_literal for cases like (usize > 0)
- check_type_compatibility(tc, node->binary.left->type_info,
- node->binary.right, node->token);
- }
- break;
-
- case NODE_EXPR_CALL:
- check_node(tc, node->call.callee);
- check_node(tc, node->call.args);
- // Propagate return type
- if (node->call.callee->type_info) {
- node->type_info = node->call.callee->type_info;
+ switch (node->type)
+ {
+ case NODE_ROOT:
+ check_node(tc, node->root.children);
+ break;
+ case NODE_BLOCK:
+ check_block(tc, node);
+ break;
+ case NODE_VAR_DECL:
+ check_var_decl(tc, node);
+ break;
+ case NODE_FUNCTION:
+ check_function(tc, node);
+ break;
+ case NODE_EXPR_VAR:
+ check_expr_var(tc, node);
+ break;
+ case NODE_RETURN:
+ if (node->ret.value)
+ {
+ check_node(tc, node->ret.value);
+ }
+
+ break;
+
+ // Control flow with nested nodes.
+ case NODE_IF:
+ check_node(tc, node->if_stmt.condition);
+ check_node(tc, node->if_stmt.then_body);
+ check_node(tc, node->if_stmt.else_body);
+ break;
+ case NODE_WHILE:
+ check_node(tc, node->while_stmt.condition);
+ check_node(tc, node->while_stmt.body);
+ break;
+ case NODE_FOR:
+ tc_enter_scope(tc); // For loop init variable is scoped
+ check_node(tc, node->for_stmt.init);
+ check_node(tc, node->for_stmt.condition);
+ check_node(tc, node->for_stmt.step);
+ check_node(tc, node->for_stmt.body);
+ tc_exit_scope(tc);
+ break;
+ case NODE_EXPR_BINARY:
+ check_node(tc, node->binary.left);
+ check_node(tc, node->binary.right);
+ break;
+ case NODE_EXPR_CALL:
+ check_node(tc, node->call.callee);
+ check_node(tc, node->call.args);
+ break;
+ default:
+ // Generic recursion for lists.
+ break;
}
- break;
-
- case NODE_EXPR_LITERAL:
- // Literals usually have their type set during parsing/inference phases
- break;
- default:
- break;
- }
-
- if (node->next) {
- check_node(tc, node->next);
- }
+ if (node->next)
+ {
+ check_node(tc, node->next);
+ }
}
// ** Entry Point **
-int check_program(ParserContext *ctx, ASTNode *root) {
- TypeChecker tc = {0};
- tc.pctx = ctx;
+int check_program(ParserContext *ctx, ASTNode *root)
+{
+ TypeChecker tc = {0};
+ tc.pctx = ctx;
- printf("[TypeCheck] Starting semantic analysis...\n");
- check_node(&tc, root);
+ printf("[TypeCheck] Starting semantic analysis...\n");
+ check_node(&tc, root);
- if (tc.error_count > 0) {
- printf("[TypeCheck] Found %d errors.\n", tc.error_count);
- return 1;
- }
- printf("[TypeCheck] Passed.\n");
- return 0;
+ if (tc.error_count > 0)
+ {
+ printf("[TypeCheck] Found %d errors.\n", tc.error_count);
+ return 1;
+ }
+ printf("[TypeCheck] Passed.\n");
+ return 0;
}