mirror of
https://github.com/odin-lang/Odin.git
synced 2025-12-31 02:12:04 +00:00
Procedure overloading
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
337
src/check_expr.c
337
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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
177
src/checker.c
177
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
|
||||
|
||||
15
src/entity.c
15
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;
|
||||
|
||||
67
src/ir.c
67
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;
|
||||
}
|
||||
|
||||
@@ -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
src/types.c
55
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user