#include "type_checker.h" #include "scope.h" #include #include #define TYPE_PANIC(format, ...) {\ fprintf(\ stderr,\ "ccc: type error: " format "\n" __VA_OPT__(,)\ __VA_ARGS__);\ exit(1);\ } static struct ast* ast_ref; static struct scope* scope; static void type_check_expr(struct expr_node* node); static void type_check_stmt(struct stmt_node* node); static void type_check_group(struct group_node* node); static const char* get_type_name(const struct type* type) { switch (type->type) { case TP_DATA: return type->data.data_type->name; case TP_PTR: /* TODO: this is a stub */ return type->pointer.data_type->name; } TYPE_PANIC("unhandled type of type case"); } static void assert_cast_compatible( const struct type* lval_type, const struct type* rval_type ) { /* TODO: impl */ /* TODO: we should also insert cast nodes eventually */ if (false) { TYPE_PANIC( "cannot assign value of type '%s' to '%s'", get_type_name(lval_type), get_type_name(rval_type)); } } static void enter_scope(struct scope* child_scope) { if (child_scope == NULL || child_scope->next_out != scope) TYPE_PANIC("enter_scope: scopes are misaligned"); scope = child_scope; } static void exit_scope(struct scope* child_scope) { if (child_scope != scope || child_scope->next_out == NULL) TYPE_PANIC("exit_scope: scopes are misaligned"); scope = child_scope->next_out; } static struct type* allocate_data_type( const struct data_type* data_type, bool is_unsigned ) { if (is_unsigned && data_type->is_floating) TYPE_PANIC("floating type '%s' cannot be unsigned", data_type->name); struct type* type = ccc_alloc(sizeof(struct type)); type->type = TP_DATA; type->data.data_type = data_type; type->data.is_signed = !is_unsigned; return type; } static struct type* allocate_pointer_type( const struct data_type* data_type, integral_t ptr_level ) { struct type* type = ccc_alloc(sizeof(struct type)); type->type = TP_PTR; type->pointer.data_type = data_type; type->pointer.ptr_level = ptr_level; return type; } static struct type* copy_type(const struct type* source_type) { struct type* type = ccc_alloc(sizeof(struct type)); *type = *source_type; return type; } static struct type* resolve_var_ref(struct var_ref_node* node) { if (node->def_ref->type == NULL) TYPE_PANIC("variable '%s' has undefined type.", node->def_ref->name); return copy_type(node->def_ref->type); } static void type_check_var_decl(struct var_decl_node* node) { node->def_ref->type = &node->type; if (node->initial_value != NULL) { type_check_expr(node->initial_value); assert_cast_compatible(&node->type, node->initial_value->resolved_type); } } static struct type* resolve_assign(struct assign_node* node) { switch (node->lval->type) { case EXPR_VAR_REF: break; default: TYPE_PANIC("expression is not assignable"); } type_check_expr(node->lval); type_check_expr(node->rval); const struct type* lval_type = node->lval->resolved_type; const struct type* rval_type = node->rval->resolved_type; assert_cast_compatible(lval_type, rval_type); return copy_type(lval_type); } static struct type* resolve_call(struct call_node* node) { struct decl_list_node* arg_decl = node->called_fn_ref->args; struct expr_list_node* arg_eval = node->args; while (arg_decl != NULL && arg_eval != NULL) { if (arg_decl->decl->initial_value != NULL) TYPE_PANIC("C does not support default arguments"); type_check_var_decl(arg_decl->decl); const struct type* decl_type = arg_decl->decl->def_ref->type; type_check_expr(arg_eval->expr); const struct type* eval_type = arg_eval->expr->resolved_type; assert_cast_compatible(decl_type, eval_type); arg_decl = arg_decl->next; arg_eval = arg_eval->next; } if (arg_decl != NULL || arg_eval != NULL) TYPE_PANIC( "mismatched argument count in call to '%s'", node->called_fn_ref->name); return copy_type(&node->called_fn_ref->return_type); } static struct type* resolve_unary(struct unary_node* node) { type_check_expr(node->expr); /* TODO: delegate by individual operation to prohibit shit like -"yes" */ return copy_type(node->expr->resolved_type); } static struct type* resolve_binary(struct binary_node* node) { /* TODO: math rules and such lol */ type_check_expr(node->lhs); type_check_expr(node->rhs); assert_cast_compatible(node->lhs->resolved_type, node->rhs->resolved_type); return copy_type(node->lhs->resolved_type); } static void type_check_expr(struct expr_node* node) { switch (node->type) { case EXPR_INT_LIT: node->resolved_type = allocate_data_type(integral_type, false); break; case EXPR_FLOAT_LIT: node->resolved_type = allocate_data_type(floating_type, false); break; case EXPR_CHAR_LIT: node->resolved_type = allocate_data_type(character_type, false); break; case EXPR_STR_LIT: node->resolved_type = allocate_pointer_type(character_type, 1); break; case EXPR_VAR_REF: node->resolved_type = resolve_var_ref(&node->inner.var_ref); break; case EXPR_ASSIGN: node->resolved_type = resolve_assign(&node->inner.assign); break; case EXPR_CALL: node->resolved_type = resolve_call(&node->inner.call); break; case EXPR_UNARY: node->resolved_type = resolve_unary(&node->inner.unary); break; case EXPR_BINARY: node->resolved_type = resolve_binary(&node->inner.binary); break; } } static void type_check_return(struct return_node* node) { if (node->ret_val != NULL) type_check_expr(node->ret_val); } static void type_check_if(struct if_node* node) { enter_scope(node->scope); type_check_expr(node->cond); type_check_stmt(node->true_branch); if (node->false_branch != NULL) type_check_stmt(node->false_branch); exit_scope(node->scope); } static void type_check_expr_list(struct expr_list_node* node) { for (; node != NULL; node = node->next) type_check_expr(node->expr); } static void type_check_loop_init(struct loop_init_node* node) { switch (node->type) { case INIT_EXPR_LIST: type_check_expr_list(node->expr_list); break; case INIT_DECL: type_check_var_decl(node->decl); break; } } static void type_check_loop(struct loop_node* node) { enter_scope(node->scope); if (node->init != NULL) type_check_loop_init(node->init); if (node->cond != NULL) type_check_expr(node->cond); if (node->incr != NULL) type_check_expr(node->incr); type_check_stmt(node->body); exit_scope(node->scope); } static void type_check_stmt(struct stmt_node* node) { switch (node->type) { case STMT_EMPTY: break; case STMT_EXPR: type_check_expr(&node->inner.expr); break; case STMT_VAR_DECL: type_check_var_decl(&node->inner.var_decl); break; case STMT_RETURN: type_check_return(&node->inner.return_); break; case STMT_GROUP: type_check_group(&node->inner.group); break; case STMT_IF: type_check_if(&node->inner.if_); break; case STMT_LOOP: type_check_loop(&node->inner.loop); break; } } static void type_check_group(struct group_node* node) { enter_scope(node->scope); struct stmt_node* stmt = node->head; while (stmt != NULL) { type_check_stmt(stmt); stmt = stmt->next; } exit_scope(node->scope); } static void type_check_fn_decl(struct fn_decl_node* node) { enter_scope(node->scope); for (struct decl_list_node* arg_decl = node->args; arg_decl != NULL; arg_decl = arg_decl->next) type_check_var_decl(arg_decl->decl); type_check_group(&node->body); exit_scope(node->scope); } static void type_check_root(struct root_node* node) { switch (node->type) { case ROOT_FN_DECL: type_check_fn_decl(&node->inner.fn_decl); break; } } void type_check(struct ast* ast) { ast_ref = ast; scope = ast->root_scope; struct root_node* node = ast->root_node; while (node != NULL) { type_check_root(node); node = node->next; } ast_ref = NULL; scope = NULL; }