Pārlūkot izejas kodu

Procedure overloading

Ginger Bill 8 gadi atpakaļ
vecāks
revīzija
ac736aa4ec
8 mainītis faili ar 599 papildinājumiem un 140 dzēšanām
  1. 15 5
      code/demo.odin
  2. 275 60
      src/check_expr.c
  3. 54 14
      src/check_stmt.c
  4. 152 25
      src/checker.c
  5. 11 4
      src/entity.c
  6. 35 32
      src/ir.c
  7. 2 0
      src/map.c
  8. 55 0
      src/types.c

+ 15 - 5
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();
 }

+ 275 - 60
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,16 +813,66 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type) {
 		}
 		return;
 	}
-	add_entity_use(c, n, e);
 
+	bool is_overloaded = false;
+	isize overload_count = 0;
+	HashKey key = hash_string(name);
+
+	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(n->Ident.string));
+		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;
 
 	switch (e->kind) {
@@ -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;
 }
 
-
-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);
+typedef enum CallArgumentError {
+	CallArgumentError_None,
+	CallArgumentError_WrongTypes,
+	CallArgumentError_NonVariadicExpand,
+	CallArgumentError_VariadicTuple,
+	CallArgumentError_MultipleVariadicExpand,
+	CallArgumentError_ArgumentCount,
+	CallArgumentError_TooFewArguments,
+	CallArgumentError_TooManyArguments,
+} CallArgumentError;
+
+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);

+ 54 - 14
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};
 
 
-	Operand op_b = {Addressing_Invalid};
 	check_expr(c, &op_b, lhs);
-	if (e) {
-		e->flags |= EntityFlag_Used*used;
-	}
-
 	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;

+ 152 - 25
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

+ 11 - 4
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;

+ 35 - 32
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;
 		}

+ 2 - 0
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);
 	}

+ 55 - 0
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;
+}
+