diff --git a/code/demo.odin b/code/demo.odin index b26adee95..7c9e206ba 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -1,9 +1,19 @@ #import "fmt.odin"; main :: proc() { - fmt.printf("%f\n", 0.0); - fmt.printf("%f\n", 1.0); - fmt.printf("%f\n", -0.5); - fmt.printf("%+f\n", 1334.67); - fmt.printf("%f\n", 789.789); + foo :: proc() { + fmt.printf("Zero args\n"); + } + foo :: proc(i: int) { + fmt.printf("One arg, i=%d\n", i); + } + THING :: 14451; + + foo(); + foo(THING); + fmt.println(THING); + + x: proc(); + x = foo; + x(); } diff --git a/src/check_expr.c b/src/check_expr.c index eee69ee45..763fc360a 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -29,26 +29,35 @@ gb_inline Type *check_type(Checker *c, AstNode *expression) { void check_scope_decls(Checker *c, AstNodeArray nodes, isize reserve_size) { - GB_ASSERT(!c->context.scope->is_file); - DelayedEntities delayed_entities; - array_init_reserve(&delayed_entities, heap_allocator(), reserve_size); - check_collect_entities(c, nodes, NULL, &delayed_entities); + Scope *s = c->context.scope; + GB_ASSERT(!s->is_file); - for_array(i, delayed_entities) { - DelayedEntity delayed = delayed_entities.e[i]; - if (delayed.entity->kind == Entity_TypeName) { - check_entity_decl(c, delayed.entity, delayed.decl, NULL); + check_collect_entities(c, nodes, false); + + for_array(i, s->elements.entries) { + Entity *e = s->elements.entries.e[i].value; + switch (e->kind) { + case Entity_Constant: + case Entity_TypeName: + case Entity_Procedure: + break; + default: + continue; } - } - for_array(i, delayed_entities) { - DelayedEntity delayed = delayed_entities.e[i]; - if (delayed.entity->kind == Entity_Constant) { - add_entity_and_decl_info(c, delayed.ident, delayed.entity, delayed.decl); - check_entity_decl(c, delayed.entity, delayed.decl, NULL); + DeclInfo **found = map_decl_info_get(&c->info.entities, hash_pointer(e)); + if (found != NULL) { + DeclInfo *d = *found; + check_entity_decl(c, e, d, NULL); } } - array_free(&delayed_entities); + for_array(i, s->elements.entries) { + Entity *e = s->elements.entries.e[i].value; + if (e->kind != Entity_Procedure) { + continue; + } + check_procedure_overloading(c, e); + } } @@ -785,16 +794,17 @@ void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) { } -void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type) { +void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *type_hint) { GB_ASSERT(n->kind == AstNode_Ident); o->mode = Addressing_Invalid; o->expr = n; - Entity *e = scope_lookup_entity(c->context.scope, n->Ident.string); + String name = n->Ident.string; + Entity *e = scope_lookup_entity(c->context.scope, name); if (e == NULL) { - if (str_eq(n->Ident.string, str_lit("_"))) { + if (str_eq(name, str_lit("_"))) { error(n->Ident, "`_` cannot be used as a value type"); } else { - error(n->Ident, "Undeclared name: %.*s", LIT(n->Ident.string)); + error(n->Ident, "Undeclared name: %.*s", LIT(name)); } o->type = t_invalid; o->mode = Addressing_Invalid; @@ -803,15 +813,65 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type) { } return; } - add_entity_use(c, n, e); - check_entity_decl(c, e, NULL, named_type); + bool is_overloaded = false; + isize overload_count = 0; + HashKey key = hash_string(name); - if (e->type == NULL) { - compiler_error("Compiler error: How did this happen? type: %s; identifier: %.*s\n", type_to_string(e->type), LIT(n->Ident.string)); - return; + if (e->kind == Entity_Procedure) { + // NOTE(bill): Overloads are only allowed with the same scope + Scope *s = e->scope; + overload_count = map_entity_multi_count(&s->elements, key); + if (overload_count > 1) { + is_overloaded = true; + } } + if (is_overloaded) { + Scope *s = e->scope; + bool skip = false; + + if (type_hint != NULL) { + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + Entity **procs = gb_alloc_array(c->tmp_allocator, Entity *, overload_count); + map_entity_multi_get_all(&s->elements, key, procs); + // NOTE(bill): These should be done + for (isize i = 0; i < overload_count; i++) { + Type *t = base_type(procs[i]->type); + if (t == t_invalid) { + continue; + } + Operand x = {0}; + x.mode = Addressing_Value; + x.type = t; + if (check_is_assignable_to(c, &x, type_hint)) { + e = procs[i]; + add_entity_use(c, n, e); + skip = true; + break; + } + } + gb_temp_arena_memory_end(tmp); + + } + + if (!skip) { + o->mode = Addressing_Overload; + o->type = t_invalid; + o->overload_count = overload_count; + o->initial_overload_entity = e; + return; + } + } + + add_entity_use(c, n, e); + check_entity_decl(c, e, NULL, named_type); + + + if (e->type == NULL) { + compiler_error("Compiler error: How did this happen? type: %s; identifier: %.*s\n", type_to_string(e->type), LIT(name)); + return; + } Type *type = e->type; @@ -915,7 +975,7 @@ Type *check_type_extra(Checker *c, AstNode *e, Type *named_type) { switch (e->kind) { case_ast_node(i, Ident, e); Operand o = {0}; - check_identifier(c, &o, e, named_type); + check_identifier(c, &o, e, named_type, NULL); switch (o.mode) { case Addressing_Invalid: @@ -1743,13 +1803,15 @@ Operand check_ptr_addition(Checker *c, TokenKind op, Operand *ptr, Operand *offs return operand; } + void check_binary_expr(Checker *c, Operand *x, AstNode *node) { GB_ASSERT(node->kind == AstNode_BinaryExpr); Operand y_ = {0}, *y = &y_; ast_node(be, BinaryExpr, node); - if (be->op.kind == Token_as) { + switch (be->op.kind) { + case Token_as: { check_expr(c, x, be->left); Type *type = check_type(c, be->right); if (x->mode == Addressing_Invalid) { @@ -1796,7 +1858,8 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) { x->type = type; return; - } else if (be->op.kind == Token_transmute) { + } break; + case Token_transmute: { check_expr(c, x, be->left); Type *type = check_type(c, be->right); if (x->mode == Addressing_Invalid) { @@ -1834,7 +1897,8 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) { x->type = type; return; - } else if (be->op.kind == Token_down_cast) { + } break; + case Token_down_cast: { check_expr(c, x, be->left); Type *type = check_type(c, be->right); if (x->mode == Addressing_Invalid) { @@ -1898,7 +1962,8 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) { x->mode = Addressing_Value; x->type = type; return; - } else if (be->op.kind == Token_union_cast) { + } break; + case Token_union_cast: { check_expr(c, x, be->left); Type *type = check_type(c, be->right); if (x->mode == Addressing_Invalid) { @@ -1974,6 +2039,7 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) { x->type = tuple; x->mode = Addressing_Value; return; + } break; } check_expr(c, x, be->left); @@ -3330,12 +3396,19 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id return true; } +typedef enum CallArgumentError { + CallArgumentError_None, + CallArgumentError_WrongTypes, + CallArgumentError_NonVariadicExpand, + CallArgumentError_VariadicTuple, + CallArgumentError_MultipleVariadicExpand, + CallArgumentError_ArgumentCount, + CallArgumentError_TooFewArguments, + CallArgumentError_TooManyArguments, +} CallArgumentError; -void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode *call) { - GB_ASSERT(call->kind == AstNode_CallExpr); - GB_ASSERT(proc_type->kind == Type_Proc); +CallArgumentError check_call_arguments_internal(Checker *c, AstNode *call, Type *proc_type, Operand *operands, isize operand_count, bool show_error, i64 *score) { ast_node(ce, CallExpr, call); - isize param_count = 0; bool variadic = proc_type->Proc.variadic; bool vari_expand = (ce->ellipsis.pos.line != 0); @@ -3348,20 +3421,96 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode } if (vari_expand && !variadic) { - error(ce->ellipsis, - "Cannot use `..` in call to a non-variadic procedure: `%.*s`", - LIT(ce->proc->Ident.string)); - return; + if (show_error) { + error(ce->ellipsis, + "Cannot use `..` in call to a non-variadic procedure: `%.*s`", + LIT(ce->proc->Ident.string)); + } + return CallArgumentError_NonVariadicExpand; } - if (ce->args.count == 0 && param_count == 0) { - return; + if (operand_count == 0 && param_count == 0) { + return CallArgumentError_None; } - gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + i32 error_code = 0; + if (operand_count < param_count) { + error_code = -1; + } else if (!variadic && operand_count > param_count) { + error_code = +1; + } + if (error_code != 0) { + CallArgumentError err = CallArgumentError_TooManyArguments; + char *err_fmt = "Too many arguments for `%s`, expected %td arguments"; + if (error_code < 0) { + err = CallArgumentError_TooFewArguments; + err_fmt = "Too few arguments for `%s`, expected %td arguments"; + } + + if (show_error) { + gbString proc_str = expr_to_string(ce->proc); + error_node(call, err_fmt, proc_str, param_count); + gb_string_free(proc_str); + } + return err; + } + + bool err = CallArgumentError_None; + + GB_ASSERT(proc_type->Proc.params != NULL); + Entity **sig_params = proc_type->Proc.params->Tuple.variables; + isize operand_index = 0; + for (; operand_index < param_count; operand_index++) { + Type *t = sig_params[operand_index]->type; + Operand o = operands[operand_index]; + if (variadic) { + o = operands[operand_index]; + } + if (!check_is_assignable_to(c, &o, t)) { + if (show_error) { + check_assignment(c, &o, t, str_lit("argument")); + } + err = CallArgumentError_WrongTypes; + } + } + + if (variadic) { + bool variadic_expand = false; + Type *slice = sig_params[param_count]->type; + GB_ASSERT(is_type_slice(slice)); + Type *elem = base_type(slice)->Slice.elem; + Type *t = elem; + for (; operand_index < operand_count; operand_index++) { + Operand o = operands[operand_index]; + if (vari_expand) { + variadic_expand = true; + t = slice; + if (operand_index != param_count) { + if (show_error) { + error_node(o.expr, "`..` in a variadic procedure can only have one variadic argument at the end"); + } + return CallArgumentError_MultipleVariadicExpand; + } + } + if (!check_is_assignable_to(c, &o, t)) { + if (show_error) { + check_assignment(c, &o, t, str_lit("argument")); + } + err = CallArgumentError_WrongTypes; + } + } + } + + return err; +} + +Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode *call) { + GB_ASSERT(call->kind == AstNode_CallExpr); + + ast_node(ce, CallExpr, call); Array(Operand) operands; - array_init_reserve(&operands, c->tmp_allocator, 2*param_count); + array_init_reserve(&operands, heap_allocator(), 2*ce->args.count); for_array(i, ce->args) { Operand o = {0}; @@ -3370,11 +3519,6 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode array_add(&operands, o); } else { TypeTuple *tuple = &o.type->Tuple; - if (variadic && i >= param_count) { - error_node(ce->args.e[i], "`..` in a variadic procedure cannot be applied to a %td-valued expression", tuple->variable_count); - operand->mode = Addressing_Invalid; - goto end; - } for (isize j = 0; j < tuple->variable_count; j++) { o.type = tuple->variables[j]->type; array_add(&operands, o); @@ -3382,6 +3526,63 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode } } + + if (operand->mode == Addressing_Overload) { + Scope *s = operand->initial_overload_entity->scope; + String name = operand->initial_overload_entity->token.string; + HashKey key = hash_string(name); + + isize overload_count = operand->overload_count; + Entity **procs = gb_alloc_array(heap_allocator(), Entity *, overload_count); + isize *valid_procs = gb_alloc_array(heap_allocator(), isize, overload_count); + isize valid_proc_count = 0; + + map_entity_multi_get_all(&s->elements, key, procs); + + for (isize i = 0; i < overload_count; i++) { + Entity *e = procs[i]; + DeclInfo **found = map_decl_info_get(&c->info.entities, hash_pointer(e)); + GB_ASSERT(found != NULL); + DeclInfo *d = *found; + check_entity_decl(c, e, d, NULL); + } + + for (isize i = 0; i < overload_count; i++) { + Entity *p = procs[i]; + Type *proc_type = base_type(p->type); + if (proc_type != NULL && is_type_proc(proc_type)) { + i64 score = 0; + CallArgumentError err = check_call_arguments_internal(c, call, proc_type, operands.e, operands.count, false, &score); + if (err == CallArgumentError_None) { + valid_procs[valid_proc_count++] = i; + } + } + } + + + if (valid_proc_count == 0) { + error_node(operand->expr, "No overloads for `%.*s` that match the specified arguments", LIT(name)); + proc_type = t_invalid; + } else { + GB_ASSERT(operand->expr->kind == AstNode_Ident); + // IMPORTANT TODO(bill): Get the best proc by its score + Entity *e = procs[valid_procs[0]]; + add_entity_use(c, operand->expr, e); + proc_type = e->type; + i64 score = 0; + CallArgumentError err = check_call_arguments_internal(c, call, proc_type, operands.e, operands.count, true, &score); + } + + gb_free(heap_allocator(), valid_procs); + gb_free(heap_allocator(), procs); + } else { + i64 score = 0; + CallArgumentError err = check_call_arguments_internal(c, call, proc_type, operands.e, operands.count, true, &score); + array_free(&operands); + } + return proc_type; + +/* i32 error_code = 0; if (operands.count < param_count) { error_code = -1; @@ -3433,7 +3634,9 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode } } end: - gb_temp_arena_memory_end(tmp); + + return proc_type; +*/ } @@ -3484,32 +3687,44 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) { } Type *proc_type = base_type(operand->type); - if (proc_type == NULL || proc_type->kind != Type_Proc || - !(operand->mode == Addressing_Value || operand->mode == Addressing_Variable)) { - AstNode *e = operand->expr; - gbString str = expr_to_string(e); - error_node(e, "Cannot call a non-procedure: `%s`", str); - gb_string_free(str); + if (operand->mode != Addressing_Overload) { + bool valid_type = (proc_type != NULL) && is_type_proc(proc_type); + bool valid_mode = (operand->mode == Addressing_Value) || (operand->mode == Addressing_Variable); + if (!valid_type || !valid_mode) { + AstNode *e = operand->expr; + gbString str = expr_to_string(e); + error_node(e, "Cannot call a non-procedure: `%s` %d", str, operand->mode); + gb_string_free(str); - operand->mode = Addressing_Invalid; - operand->expr = call; + operand->mode = Addressing_Invalid; + operand->expr = call; - return Expr_Stmt; + return Expr_Stmt; + } } - check_call_arguments(c, operand, proc_type, call); + proc_type = check_call_arguments(c, operand, proc_type, call); - switch (proc_type->Proc.result_count) { + gb_zero_item(operand); + + Type *pt = base_type(proc_type); + if (pt == NULL || !is_type_proc(pt)) { + operand->mode = Addressing_Invalid; + operand->type = t_invalid; + operand->expr = call; + return Expr_Stmt; + } + switch (pt->Proc.result_count) { case 0: operand->mode = Addressing_NoValue; break; case 1: operand->mode = Addressing_Value; - operand->type = proc_type->Proc.results->Tuple.variables[0]->type; + operand->type = pt->Proc.results->Tuple.variables[0]->type; break; default: operand->mode = Addressing_Value; - operand->type = proc_type->Proc.results; + operand->type = pt->Proc.results; break; } @@ -3638,7 +3853,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint case_end; case_ast_node(i, Ident, node); - check_identifier(c, o, node, type_hint); + check_identifier(c, o, node, NULL, type_hint); case_end; case_ast_node(bl, BasicLit, node); diff --git a/src/check_stmt.c b/src/check_stmt.c index 5ca553600..e877bc190 100644 --- a/src/check_stmt.c +++ b/src/check_stmt.c @@ -184,7 +184,7 @@ bool check_is_terminating(AstNode *node) { Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) { if (op_a->mode == Addressing_Invalid || - op_a->type == t_invalid) { + (op_a->type == t_invalid && op_a->mode != Addressing_Overload)) { return NULL; } @@ -195,33 +195,73 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) { str_eq(node->Ident.string, str_lit("_"))) { add_entity_definition(&c->info, node, NULL); check_assignment(c, op_a, NULL, str_lit("assignment to `_` identifier")); - if (op_a->mode == Addressing_Invalid) + if (op_a->mode == Addressing_Invalid) { return NULL; + } return op_a->type; } Entity *e = NULL; bool used = false; - if (node->kind == AstNode_Ident) { - ast_node(i, Ident, node); - e = scope_lookup_entity(c->context.scope, i->string); - if (e != NULL && e->kind == Entity_Variable) { - used = (e->flags & EntityFlag_Used) != 0; // TODO(bill): Make backup just in case - } - } - - Operand op_b = {Addressing_Invalid}; - check_expr(c, &op_b, lhs); - if (e) { - e->flags |= EntityFlag_Used*used; - } + + check_expr(c, &op_b, lhs); if (op_b.mode == Addressing_Invalid || op_b.type == t_invalid) { return NULL; } + + if (op_a->mode == Addressing_Overload) { + isize overload_count = op_a->overload_count; + Entity *entity = op_a->initial_overload_entity; + String name = entity->token.string; + Scope *s = entity->scope; + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + Entity **procs = gb_alloc_array(c->tmp_allocator, Entity *, overload_count); + + HashKey key = hash_string(name); + map_entity_multi_get_all(&s->elements, key, procs); + // NOTE(bill): These should be done + for (isize i = 0; i < overload_count; i++) { + Type *t = base_type(procs[i]->type); + if (t == t_invalid) { + continue; + } + Operand x = {0}; + x.mode = Addressing_Value; + x.type = t; + if (check_is_assignable_to(c, &x, op_b.type)) { + e = procs[i]; + add_entity_use(c, op_a->expr, e); + break; + } + } + gb_temp_arena_memory_end(tmp); + + if (e != NULL) { + op_a->mode = Addressing_Value; + op_a->type = e->type; + op_a->overload_count = 0; + op_a->initial_overload_entity = NULL; + } + + } else { + if (node->kind == AstNode_Ident) { + ast_node(i, Ident, node); + e = scope_lookup_entity(c->context.scope, i->string); + if (e != NULL && e->kind == Entity_Variable) { + used = (e->flags & EntityFlag_Used) != 0; // TODO(bill): Make backup just in case + } + } + + } + + if (e != NULL && used) { + e->flags |= EntityFlag_Used; + } + switch (op_b.mode) { case Addressing_Invalid: return NULL; diff --git a/src/checker.c b/src/checker.c index c64f8c19d..1e6acf382 100644 --- a/src/checker.c +++ b/src/checker.c @@ -9,12 +9,14 @@ typedef enum AddressingMode { Addressing_Invalid, + Addressing_NoValue, Addressing_Value, Addressing_Variable, Addressing_Constant, Addressing_Type, Addressing_Builtin, + Addressing_Overload, Addressing_Count, } AddressingMode; @@ -24,6 +26,8 @@ typedef struct Operand { ExactValue value; AstNode * expr; BuiltinProcId builtin_id; + isize overload_count; + Entity * initial_overload_entity; } Operand; typedef struct TypeAndValue { @@ -501,10 +505,27 @@ Entity *scope_insert_entity(Scope *s, Entity *entity) { String name = entity->token.string; HashKey key = hash_string(name); Entity **found = map_entity_get(&s->elements, key); + Entity *prev = NULL; if (found) { - return *found; + prev = *found; + GB_ASSERT(prev != entity); + + if (prev->kind != Entity_Procedure && + entity->kind != Entity_Procedure) { + return prev; + } + } + + if (prev != NULL && entity->kind == Entity_Procedure) { + // TODO(bill): Remove from final release + isize prev_count, next_count; + prev_count = map_entity_multi_count(&s->elements, key); + map_entity_multi_insert(&s->elements, key, entity); + next_count = map_entity_multi_count(&s->elements, key); + GB_ASSERT(prev_count < next_count); + } else { + map_entity_set(&s->elements, key, entity); } - map_entity_set(&s->elements, key, entity); if (entity->scope == NULL) { entity->scope = s; } @@ -756,7 +777,8 @@ void add_entity_definition(CheckerInfo *i, AstNode *identifier, Entity *entity) } bool add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) { - if (str_ne(entity->token.string, str_lit("_"))) { + String name = entity->token.string; + if (!str_eq(name, str_lit("_"))) { Entity *insert_entity = scope_insert_entity(scope, entity); if (insert_entity) { Entity *up = insert_entity->using_parent; @@ -764,7 +786,7 @@ bool add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) { error(entity->token, "Redeclararation of `%.*s` in this scope through `using`\n" "\tat %.*s(%td:%td)", - LIT(entity->token.string), + LIT(name), LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column); return false; } else { @@ -776,7 +798,7 @@ bool add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) { error(entity->token, "Redeclararation of `%.*s` in this scope\n" "\tat %.*s(%td:%td)", - LIT(entity->token.string), + LIT(name), LIT(pos.file), pos.line, pos.column); return false; } @@ -801,6 +823,7 @@ void add_entity_use(Checker *c, AstNode *identifier, Entity *entity) { void add_entity_and_decl_info(Checker *c, AstNode *identifier, Entity *e, DeclInfo *d) { GB_ASSERT(identifier->kind == AstNode_Ident); + GB_ASSERT(e != NULL && d != NULL); GB_ASSERT(str_eq(identifier->Ident.string, e->token.string)); add_entity(c, e->scope, identifier, e); map_decl_info_set(&c->info.entities, hash_pointer(e), d); @@ -1129,8 +1152,107 @@ void init_preload(Checker *c) { bool check_arity_match(Checker *c, AstNodeValueDecl *d); -void check_collect_entities(Checker *c, AstNodeArray nodes, MapScope *file_scopes, DelayedEntities *delayed_entities); -void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, MapScope *file_scopes, DelayedEntities *delayed_entities); +void check_collect_entities(Checker *c, AstNodeArray nodes, bool is_file_scope); +void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, bool is_file_scope); + +bool check_is_entity_overloaded(Entity *e) { + if (e->kind != Entity_Procedure) { + return false; + } + Scope *s = e->scope; + HashKey key = hash_string(e->token.string); + isize overload_count = map_entity_multi_count(&s->elements, key); + return overload_count > 1; +} + +void check_procedure_overloading(Checker *c, Entity *e) { + GB_ASSERT(e->kind == Entity_Procedure); + if (e->type == t_invalid) { + return; + } + if (e->Procedure.overload_kind != Overload_Unknown) { + // NOTE(bill): The overloading has already been handled + return; + } + + + // NOTE(bill): Procedures call only overload other procedures in the same scope + + String name = e->token.string; + HashKey key = hash_string(name); + Scope *s = e->scope; + isize overload_count = map_entity_multi_count(&s->elements, key); + GB_ASSERT(overload_count >= 1); + if (overload_count == 1) { + e->Procedure.overload_kind = Overload_No; + return; + } + GB_ASSERT(overload_count > 1); + + + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + Entity **procs = gb_alloc_array(c->tmp_allocator, Entity *, overload_count); + map_entity_multi_get_all(&s->elements, key, procs); + + for (isize j = 0; j < overload_count; j++) { + Entity *p = procs[j]; + if (p->type == t_invalid) { + // NOTE(bill): This invalid overload has already been handled + continue; + } + + + String name = p->token.string; + + GB_ASSERT(p->kind == Entity_Procedure); + for (isize k = j+1; k < overload_count; k++) { + Entity *q = procs[k]; + GB_ASSERT(p != q); + + bool is_invalid = false; + GB_ASSERT(q->kind == Entity_Procedure); + + TokenPos pos = q->token.pos; + + ProcTypeOverloadKind kind = are_proc_types_overload_safe(p->type, q->type); + switch (kind) { + case ProcOverload_Identical: + case ProcOverload_CallingConvention: + error(p->token, "Overloaded procedure `%.*s` as the same type as another procedure in this scope", LIT(name)); + is_invalid = true; + break; + case ProcOverload_ParamVariadic: + error(p->token, "Overloaded procedure `%.*s` as the same type as another procedure in this scope", LIT(name)); + is_invalid = true; + break; + case ProcOverload_ResultCount: + case ProcOverload_ResultTypes: + error(p->token, "Overloaded procedure `%.*s` as the same parameters but different results in this scope", LIT(name)); + is_invalid = true; + break; + case ProcOverload_ParamCount: + case ProcOverload_ParamTypes: + // This is okay :) + break; + } + + if (is_invalid) { + gb_printf_err("\tprevious procedure at %.*s(%td:%td)\n", LIT(pos.file), pos.line, pos.column); + q->type = t_invalid; + } + } + } + + for (isize j = 0; j < overload_count; j++) { + Entity *p = procs[j]; + if (p->type != t_invalid) { + p->Procedure.overload_kind = Overload_Yes; + } + } + + gb_temp_arena_memory_end(tmp); +} + #include "check_expr.c" #include "check_decl.c" @@ -1169,10 +1291,7 @@ bool check_arity_match(Checker *c, AstNodeValueDecl *d) { return true; } -void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, MapScope *file_scopes, DelayedEntities *delayed_entities) { - // NOTE(bill): File scope and local scope are different kinds of scopes - GB_ASSERT(file_scopes == NULL || delayed_entities == NULL); - +void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, bool is_file_scope) { Operand operand = {Addressing_Invalid}; check_expr(c, &operand, ws->cond); if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) { @@ -1186,14 +1305,14 @@ void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, MapS } else { if (operand.value.kind == ExactValue_Bool && operand.value.value_bool) { - check_collect_entities(c, ws->body->BlockStmt.stmts, file_scopes, delayed_entities); + check_collect_entities(c, ws->body->BlockStmt.stmts, is_file_scope); } else if (ws->else_stmt) { switch (ws->else_stmt->kind) { case AstNode_BlockStmt: - check_collect_entities(c, ws->else_stmt->BlockStmt.stmts, file_scopes, delayed_entities); + check_collect_entities(c, ws->else_stmt->BlockStmt.stmts, is_file_scope); break; case AstNode_WhenStmt: - check_collect_entities_from_when_stmt(c, &ws->else_stmt->WhenStmt, file_scopes, delayed_entities); + check_collect_entities_from_when_stmt(c, &ws->else_stmt->WhenStmt, is_file_scope); break; default: error_node(ws->else_stmt, "Invalid `else` statement in `when` statement"); @@ -1204,13 +1323,11 @@ void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, MapS } // NOTE(bill): If file_scopes == NULL, this will act like a local scope -void check_collect_entities(Checker *c, AstNodeArray nodes, MapScope *file_scopes, DelayedEntities *delayed_entities) { +void check_collect_entities(Checker *c, AstNodeArray nodes, bool is_file_scope) { // NOTE(bill): File scope and local scope are different kinds of scopes - GB_ASSERT(file_scopes == NULL || delayed_entities == NULL); - if (file_scopes != NULL) { + if (is_file_scope) { GB_ASSERT(c->context.scope->is_file); - } - if (delayed_entities != NULL) { + } else { GB_ASSERT(!c->context.scope->is_file); } @@ -1359,7 +1476,7 @@ void check_collect_entities(Checker *c, AstNodeArray nodes, MapScope *file_scope AstNode *node = nodes.e[i]; switch (node->kind) { case_ast_node(ws, WhenStmt, node); - check_collect_entities_from_when_stmt(c, ws, file_scopes, delayed_entities); + check_collect_entities_from_when_stmt(c, ws, is_file_scope); case_end; } } @@ -1390,15 +1507,26 @@ void check_all_global_entities(Checker *c) { continue; } - Scope *prev_scope = c->context.scope; + CheckerContext prev_context = c->context; + c->context.decl = d; c->context.scope = d->scope; check_entity_decl(c, e, d, NULL); + c->context = prev_context; if (d->scope->is_init && !c->done_preload) { init_preload(c); } } + + for_array(i, c->info.entities.entries) { + MapDeclInfoEntry *entry = &c->info.entities.entries.e[i]; + Entity *e = cast(Entity *)cast(uintptr)entry->key.key; + if (e->kind != Entity_Procedure) { + continue; + } + check_procedure_overloading(c, e); + } } @@ -1463,8 +1591,8 @@ void check_import_entities(Checker *c, MapScope *file_scopes) { continue; } // NOTE(bill): Do not add other imported entities - add_entity(c, parent_scope, NULL, e); - if (id->is_import) { // `#import`ed entities don't get exported + bool ok = add_entity(c, parent_scope, NULL, e); + if (ok && id->is_import) { // `#import`ed entities don't get exported HashKey key = hash_string(e->token.string); map_entity_set(&parent_scope->implicit, key, e); } @@ -1575,13 +1703,12 @@ void check_parsed_files(Checker *c) { AstFile *f = &c->parser->files.e[i]; CheckerContext prev_context = c->context; add_curr_ast_file(c, f); - check_collect_entities(c, f->decls, &file_scopes, NULL); + check_collect_entities(c, f->decls, true); c->context = prev_context; } check_import_entities(c, &file_scopes); - check_all_global_entities(c); init_preload(c); // NOTE(bill): This could be setup previously through the use of `type_info(_of_val)` // NOTE(bill): Nothing in the global scope _should_ depend on this implicit value as implicit diff --git a/src/entity.c b/src/entity.c index a35c68d08..f4d48965f 100644 --- a/src/entity.c +++ b/src/entity.c @@ -37,6 +37,12 @@ typedef enum EntityFlag { EntityFlag_VectorElem = 1<<5, } EntityFlag; +typedef enum OverloadKind { + Overload_No = -1, + Overload_Unknown = 0, + Overload_Yes = +1, +} OverloadKind; + typedef struct Entity Entity; struct Entity { EntityKind kind; @@ -61,10 +67,11 @@ struct Entity { } Variable; i32 TypeName; struct { - bool is_foreign; - String foreign_name; - String link_name; - u64 tags; + bool is_foreign; + String foreign_name; + String link_name; + u64 tags; + OverloadKind overload_kind; } Procedure; struct { BuiltinProcId id; diff --git a/src/ir.c b/src/ir.c index 082e29d8f..0087c8c08 100644 --- a/src/ir.c +++ b/src/ir.c @@ -32,10 +32,10 @@ typedef struct irModule { // String triple; MapEntity min_dep_map; // Key: Entity * - MapIrValue values; // Key: Entity * - MapIrValue members; // Key: String + MapIrValue values; // Key: Entity * + MapIrValue members; // Key: String MapString type_names; // Key: Type * - MapIrDebugInfo debug_info; // Key: Unique pointer + MapIrDebugInfo debug_info; // Key: Unique pointer i32 global_string_index; i32 global_array_index; // For ConstantSlice @@ -54,14 +54,14 @@ typedef struct irDomNode { typedef struct irBlock { - i32 index; - String label; + i32 index; + String label; irProcedure *parent; - AstNode * node; // Can be NULL - Scope * scope; - isize scope_index; - irDomNode dom; - i32 gaps; + AstNode * node; // Can be NULL + Scope * scope; + isize scope_index; + irDomNode dom; + i32 gaps; irValueArray instrs; irValueArray locals; @@ -103,27 +103,27 @@ struct irProcedure { irProcedure * parent; Array(irProcedure *) children; - Entity * entity; + Entity * entity; irModule * module; - String name; - Type * type; - AstNode * type_expr; - AstNode * body; - u64 tags; + String name; + Type * type; + AstNode * type_expr; + AstNode * body; + u64 tags; irValueArray params; Array(irDefer) defer_stmts; Array(irBlock *) blocks; - i32 scope_index; + i32 scope_index; irBlock * decl_block; irBlock * entry_block; irBlock * curr_block; irTargetList * target_list; irValueArray referrers; - i32 local_count; - i32 instr_count; - i32 block_count; + i32 local_count; + i32 instr_count; + i32 block_count; }; #define IR_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime" @@ -533,13 +533,6 @@ typedef struct irGen { bool opt_called; } irGen; -irValue *ir_lookup_member(irModule *m, String name) { - irValue **v = map_ir_value_get(&m->members, hash_string(name)); - if (v != NULL) { - return *v; - } - return NULL; -} Type *ir_type(irValue *value); @@ -3340,7 +3333,6 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) { irAddr val = {0}; return val; } - Entity *e = entity_of_ident(proc->module->info, expr); return ir_build_addr_from_entity(proc, e, expr); case_end; @@ -5118,10 +5110,10 @@ void ir_gen_destroy(irGen *s) { gb_file_close(&s->output_file); } -String ir_mangle_name(irGen *s, String path, String name) { +String ir_mangle_name(irGen *s, String path, Entity *e) { // NOTE(bill): prefix names not in the init scope // TODO(bill): make robust and not just rely on the file's name - + String name = e->token.string; irModule *m = &s->module; CheckerInfo *info = m->info; gbAllocator a = m->allocator; @@ -5141,6 +5133,11 @@ String ir_mangle_name(irGen *s, String path, String name) { isize base_len = ext-1-base; isize max_len = base_len + 1 + 10 + 1 + name.len; + bool is_overloaded = check_is_entity_overloaded(e); + if (is_overloaded) { + max_len += 21; + } + u8 *new_name = gb_alloc_array(a, u8, max_len); isize new_name_len = gb_snprintf( cast(char *)new_name, max_len, @@ -5148,6 +5145,12 @@ String ir_mangle_name(irGen *s, String path, String name) { cast(int)base_len, base, file->id, LIT(name)); + if (is_overloaded) { + char *str = cast(char *)new_name + new_name_len-1; + isize len = max_len-new_name_len; + isize extra = gb_snprintf(str, len, "-%tu", cast(usize)cast(uintptr)e); + new_name_len += extra-1; + } return make_string(new_name, new_name_len-1); } @@ -5238,7 +5241,7 @@ void ir_gen_tree(irGen *s) { } else if (e->kind == Entity_Procedure && e->Procedure.link_name.len > 0) { } else if (scope->is_init && e->kind == Entity_Procedure && str_eq(name, str_lit("main"))) { } else { - name = ir_mangle_name(s, e->token.pos.file, name); + name = ir_mangle_name(s, e->token.pos.file, e); } } @@ -5298,7 +5301,7 @@ void ir_gen_tree(irGen *s) { ir_module_add_value(m, e, p); HashKey hash_name = hash_string(name); if (map_ir_value_get(&m->members, hash_name) == NULL) { - map_ir_value_set(&m->members, hash_name, p); + map_ir_value_multi_insert(&m->members, hash_name, p); } } break; } diff --git a/src/map.c b/src/map.c index f974a46db..60af7f668 100644 --- a/src/map.c +++ b/src/map.c @@ -320,6 +320,7 @@ void _J2(MAP_PROC,multi_insert)(MAP_NAME *h, HashKey key, MAP_TYPE value) { if (h->hashes.count == 0) { _J2(MAP_PROC,grow)(h); } + // Make fr = _J2(MAP_PROC,_find)(h, key); i = _J2(MAP_PROC,_add_entry)(h, key); if (fr.entry_prev < 0) { @@ -329,6 +330,7 @@ void _J2(MAP_PROC,multi_insert)(MAP_NAME *h, HashKey key, MAP_TYPE value) { } h->entries.e[i].next = fr.entry_index; h->entries.e[i].value = value; + // Grow if needed if (_J2(MAP_PROC,_full)(h)) { _J2(MAP_PROC,grow)(h); } diff --git a/src/types.c b/src/types.c index c8ad70f68..a17aa98d2 100644 --- a/src/types.c +++ b/src/types.c @@ -822,6 +822,7 @@ bool are_types_identical(Type *x, Type *y) { case Type_Proc: if (y->kind == Type_Proc) { return x->Proc.calling_convention == y->Proc.calling_convention && + x->Proc.variadic == y->Proc.variadic && are_types_identical(x->Proc.params, y->Proc.params) && are_types_identical(x->Proc.results, y->Proc.results); } @@ -908,6 +909,60 @@ bool is_type_cte_safe(Type *type) { return false; } +typedef enum ProcTypeOverloadKind { + ProcOverload_Identical, // The types are identical + + ProcOverload_CallingConvention, + ProcOverload_ParamCount, + ProcOverload_ParamVariadic, + ProcOverload_ParamTypes, + ProcOverload_ResultCount, + ProcOverload_ResultTypes, + +} ProcTypeOverloadKind; + + +ProcTypeOverloadKind are_proc_types_overload_safe(Type *x, Type *y) { + GB_ASSERT(is_type_proc(x)); + GB_ASSERT(is_type_proc(y)); + TypeProc *px = &base_type(x)->Proc; + TypeProc *py = &base_type(y)->Proc; + + if (px->calling_convention != py->calling_convention) { + return ProcOverload_CallingConvention; + } + + if (px->param_count != py->param_count) { + return ProcOverload_ParamCount; + } + + for (isize i = 0; i < px->param_count; i++) { + Entity *ex = px->params->Tuple.variables[i]; + Entity *ey = py->params->Tuple.variables[i]; + if (!are_types_identical(ex->type, ey->type)) { + return ProcOverload_ParamTypes; + } + } + // IMPORTANT TODO(bill): Determine the rules for overloading procedures with variadic parameters + if (px->variadic != py->variadic) { + return ProcOverload_ParamVariadic; + } + + if (px->result_count != py->result_count) { + return ProcOverload_ResultCount; + } + + for (isize i = 0; i < px->result_count; i++) { + Entity *ex = px->results->Tuple.variables[i]; + Entity *ey = py->results->Tuple.variables[i]; + if (!are_types_identical(ex->type, ey->type)) { + return ProcOverload_ResultTypes; + } + } + + return ProcOverload_Identical; +} +