mirror of
https://github.com/odin-lang/Odin.git
synced 2026-04-18 20:40:28 +00:00
Explicit procedure group; Remove implicit procedure overloading
This commit is contained in:
@@ -148,9 +148,10 @@ div :: proc[
|
||||
];
|
||||
|
||||
inverse :: proc[mat4_inverse, quat_inverse];
|
||||
dot :: proc[vec_dot, quat_dot];
|
||||
cross :: proc[cross2, cross3];
|
||||
|
||||
|
||||
dot :: proc(a, b: $T/[$N]$E) -> E {
|
||||
vec_dot :: proc(a, b: $T/[$N]$E) -> E {
|
||||
res: E;
|
||||
for i in 0..N {
|
||||
res += a[i] * b[i];
|
||||
@@ -167,7 +168,6 @@ cross3 :: proc(a, b: $T/[3]$E) -> T {
|
||||
j := swizzle(a, 2, 0, 1) * swizzle(b, 1, 2, 0);
|
||||
return T(i - j);
|
||||
}
|
||||
cross :: proc[cross2, cross3];
|
||||
|
||||
|
||||
length :: proc(v: $T/[$N]$E) -> E { return sqrt(dot(v, v)); }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import win32 "core:sys/windows.odin" when ODIN_OS == "windows";
|
||||
import wgl "core:sys/wgl.odin" when ODIN_OS == "windows";
|
||||
when ODIN_OS == "windows" do import win32 "core:sys/windows.odin";
|
||||
when ODIN_OS == "windows" import wgl "core:sys/wgl.odin";
|
||||
import "core:fmt.odin";
|
||||
import "core:math.odin";
|
||||
import "core:os.odin";
|
||||
|
||||
@@ -308,7 +308,7 @@ String get_fullpath_core(gbAllocator a, String path) {
|
||||
}
|
||||
|
||||
|
||||
String const ODIN_VERSION = str_lit("0.7.1");
|
||||
String const ODIN_VERSION = str_lit("0.8.0-dev");
|
||||
String cross_compile_target = str_lit("");
|
||||
String cross_compile_lib_dir = str_lit("");
|
||||
|
||||
|
||||
@@ -23,9 +23,9 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex
|
||||
}
|
||||
|
||||
|
||||
if (operand->mode == Addressing_Overload) {
|
||||
if (operand->mode == Addressing_ProcGroup) {
|
||||
if (e->type == nullptr) {
|
||||
error(operand->expr, "Cannot determine type from overloaded procedure '%.*s'", LIT(operand->overload_entities[0]->token.string));
|
||||
error(operand->expr, "Cannot determine type from overloaded procedure '%.*s'", LIT(operand->proc_group->token.string));
|
||||
} else {
|
||||
check_assignment(c, operand, e->type, str_lit("variable assignment"));
|
||||
if (operand->mode != Addressing_Type) {
|
||||
@@ -275,11 +275,15 @@ void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init,
|
||||
e->type = t_invalid;
|
||||
return;
|
||||
|
||||
case Addressing_Overload:
|
||||
e->kind = Entity_Alias;
|
||||
e->Alias.base = operand.overload_entities[0];
|
||||
case Addressing_ProcGroup: {
|
||||
GB_ASSERT(operand.proc_group != nullptr);
|
||||
GB_ASSERT(operand.proc_group->kind == Entity_ProcGroup);
|
||||
|
||||
e->kind = Entity_ProcGroup;
|
||||
e->type = t_invalid;
|
||||
gb_memcopy(&e->ProcGroup, &operand.proc_group->ProcGroup, gb_size_of(e->ProcGroup));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#if 1
|
||||
@@ -708,11 +712,11 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count
|
||||
check_init_variables(c, entities, entity_count, init_expr_list, context_name);
|
||||
}
|
||||
|
||||
void check_proc_grouping_decl(Checker *c, Entity *pg_entity, DeclInfo *d) {
|
||||
GB_ASSERT(pg_entity->kind == Entity_ProcedureGrouping);
|
||||
auto *pge = &pg_entity->ProcedureGrouping;
|
||||
void check_proc_group_decl(Checker *c, Entity *pg_entity, DeclInfo *d) {
|
||||
GB_ASSERT(pg_entity->kind == Entity_ProcGroup);
|
||||
auto *pge = &pg_entity->ProcGroup;
|
||||
|
||||
ast_node(pg, ProcGrouping, d->init_expr);
|
||||
ast_node(pg, ProcGroup, d->init_expr);
|
||||
|
||||
array_init(&pge->entities, c->allocator, pg->args.count);
|
||||
|
||||
@@ -731,22 +735,87 @@ void check_proc_grouping_decl(Checker *c, Entity *pg_entity, DeclInfo *d) {
|
||||
e = check_selector(c, &o, arg, nullptr);
|
||||
}
|
||||
if (e == nullptr) {
|
||||
error(arg, "Expected a valid entity name in procedure grouping");
|
||||
error(arg, "Expected a valid entity name in procedure group");
|
||||
continue;
|
||||
}
|
||||
if (e->kind != Entity_Procedure) {
|
||||
if (e->kind == Entity_Variable) {
|
||||
if (!is_type_proc(e->type)) {
|
||||
error(arg, "Expected a procedure variable");
|
||||
continue;
|
||||
}
|
||||
} else if (e->kind != Entity_Procedure) {
|
||||
error(arg, "Expected a procedure entity");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ptr_set_exists(&entity_map, e)) {
|
||||
error(arg, "Previous use of `%.*s` in procedure grouping", LIT(e->token.string));
|
||||
error(arg, "Previous use of `%.*s` in procedure group", LIT(e->token.string));
|
||||
continue;
|
||||
}
|
||||
ptr_set_add(&entity_map, e);
|
||||
|
||||
array_add(&pge->entities, e);
|
||||
}
|
||||
|
||||
for_array(j, pge->entities) {
|
||||
Entity *p = pge->entities[j];
|
||||
if (p->type == t_invalid) {
|
||||
// NOTE(bill): This invalid overload has already been handled
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = p->token.string;
|
||||
|
||||
for (isize k = j+1; k < pge->entities.count; k++) {
|
||||
Entity *q = pge->entities[k];
|
||||
GB_ASSERT(p != q);
|
||||
|
||||
bool is_invalid = false;
|
||||
|
||||
TokenPos pos = q->token.pos;
|
||||
|
||||
if (q->type == nullptr || q->type == t_invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ProcTypeOverloadKind kind = are_proc_types_overload_safe(p->type, q->type);
|
||||
switch (kind) {
|
||||
case ProcOverload_Identical:
|
||||
error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in this scope", LIT(name));
|
||||
is_invalid = true;
|
||||
break;
|
||||
// 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_Polymorphic:
|
||||
#if 0
|
||||
error(p->token, "Overloaded procedure '%.*s' has a polymorphic counterpart in this scope which is not allowed", LIT(name));
|
||||
is_invalid = true;
|
||||
#endif
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {
|
||||
@@ -787,9 +856,8 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {
|
||||
check_proc_decl(c, e, d);
|
||||
break;
|
||||
|
||||
case Entity_ProcedureGrouping:
|
||||
// error(e->token, "Procedure groupings are not yet supported");
|
||||
check_proc_grouping_decl(c, e, d);
|
||||
case Entity_ProcGroup:
|
||||
check_proc_group_decl(c, e, d);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -127,14 +127,6 @@ void check_scope_decls(Checker *c, Array<AstNode *> nodes, isize reserve_size) {
|
||||
check_entity_decl(c, e, d, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
for_array(i, s->elements.entries) {
|
||||
Entity *e = s->elements.entries[i].value;
|
||||
if (e->kind != Entity_Procedure) {
|
||||
continue;
|
||||
}
|
||||
check_procedure_overloading(c, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -659,18 +651,16 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n
|
||||
return;
|
||||
}
|
||||
|
||||
if (operand->mode == Addressing_Overload) {
|
||||
if (operand->mode == Addressing_ProcGroup) {
|
||||
// GB_PANIC("HERE!\n");
|
||||
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
|
||||
defer (gb_temp_arena_memory_end(tmp));
|
||||
|
||||
Entity **procs = operand->overload_entities;
|
||||
isize overload_count = operand->overload_count;
|
||||
|
||||
Array<Entity *> procs = proc_group_entities(c, *operand);
|
||||
bool good = false;
|
||||
// NOTE(bill): These should be done
|
||||
for (isize i = 0; i < overload_count; i++) {
|
||||
for_array(i, procs) {
|
||||
Type *t = base_type(procs[i]->type);
|
||||
if (t == t_invalid) {
|
||||
continue;
|
||||
@@ -783,7 +773,10 @@ bool is_polymorphic_type_assignable(Checker *c, Type *poly, Type *source, bool c
|
||||
return false;
|
||||
case Type_Array:
|
||||
if (source->kind == Type_Array) {
|
||||
if (poly->Array.generic_type && modify_type) {
|
||||
|
||||
// IMPORTANT TODO(bill): Which is correct?
|
||||
// if (poly->Array.generic_type != nullptr && modify_type) {
|
||||
if (poly->Array.generic_type != nullptr) {
|
||||
Type *gt = poly->Array.generic_type;
|
||||
GB_ASSERT(gt->kind == Type_Generic);
|
||||
Entity *e = scope_lookup_entity(gt->Generic.scope, gt->Generic.name);
|
||||
@@ -808,8 +801,6 @@ bool is_polymorphic_type_assignable(Checker *c, Type *poly, Type *source, bool c
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return is_polymorphic_type_assignable(c, poly->Array.elem, source->Array.elem, true, modify_type);
|
||||
}
|
||||
if (poly->Array.count == source->Array.count) {
|
||||
return is_polymorphic_type_assignable(c, poly->Array.elem, source->Array.elem, true, modify_type);
|
||||
@@ -936,10 +927,6 @@ Entity *check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_overloaded = false;
|
||||
isize overload_count = 0;
|
||||
|
||||
bool is_alias = false;
|
||||
while (e->kind == Entity_Alias) {
|
||||
GB_ASSERT(e->Alias.base != nullptr);
|
||||
@@ -949,63 +936,14 @@ Entity *check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *
|
||||
|
||||
HashKey key = hash_string(e->token.string);
|
||||
|
||||
|
||||
if (e->kind == Entity_Procedure) {
|
||||
// NOTE(bill): Overloads are only allowed with the same scope
|
||||
Scope *s = e->scope;
|
||||
overload_count = multi_map_count(&s->elements, key);
|
||||
if (overload_count > 1) {
|
||||
is_overloaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_overloaded) {
|
||||
Scope *s = e->scope;
|
||||
bool skip = false;
|
||||
|
||||
Entity **procs = gb_alloc_array(heap_allocator(), Entity *, overload_count);
|
||||
multi_map_get_all(&s->elements, key, procs);
|
||||
if (type_hint != nullptr) {
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
|
||||
defer (gb_temp_arena_memory_end(tmp));
|
||||
|
||||
// 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 = {};
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!skip) {
|
||||
o->mode = Addressing_Overload;
|
||||
o->type = t_invalid;
|
||||
o->overload_count = overload_count;
|
||||
o->overload_entities = procs;
|
||||
return nullptr;
|
||||
}
|
||||
gb_free(heap_allocator(), procs);
|
||||
}
|
||||
|
||||
if (e->kind == Entity_ProcedureGrouping) {
|
||||
auto *pge = &e->ProcedureGrouping;
|
||||
if (e->kind == Entity_ProcGroup) {
|
||||
auto *pge = &e->ProcGroup;
|
||||
|
||||
DeclInfo *d = decl_info_of_entity(&c->info, e);
|
||||
check_entity_decl(c, e, d, nullptr);
|
||||
|
||||
|
||||
Entity **procs = pge->entities.data;
|
||||
isize overload_count = pge->entities.count;
|
||||
Array<Entity *> procs = pge->entities;
|
||||
bool skip = false;
|
||||
|
||||
if (type_hint != nullptr) {
|
||||
@@ -1013,7 +951,7 @@ Entity *check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *
|
||||
defer (gb_temp_arena_memory_end(tmp));
|
||||
|
||||
// NOTE(bill): These should be done
|
||||
for (isize i = 0; i < overload_count; i++) {
|
||||
for_array(i, procs) {
|
||||
Type *t = base_type(procs[i]->type);
|
||||
if (t == t_invalid) {
|
||||
continue;
|
||||
@@ -1031,10 +969,9 @@ Entity *check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *
|
||||
}
|
||||
|
||||
if (!skip) {
|
||||
o->mode = Addressing_Overload;
|
||||
o->type = t_invalid;
|
||||
o->overload_count = overload_count;
|
||||
o->overload_entities = procs;
|
||||
o->mode = Addressing_ProcGroup;
|
||||
o->type = t_invalid;
|
||||
o->proc_group = e;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@@ -1550,13 +1487,13 @@ void check_comparison(Checker *c, Operand *x, Operand *y, TokenKind op) {
|
||||
}
|
||||
} else {
|
||||
gbString xt, yt;
|
||||
if (x->mode == Addressing_Overload) {
|
||||
xt = gb_string_make(heap_allocator(), "overloaded procedure");
|
||||
if (x->mode == Addressing_ProcGroup) {
|
||||
xt = gb_string_make(heap_allocator(), "procedure group");
|
||||
} else {
|
||||
xt = type_to_string(x->type);
|
||||
}
|
||||
if (y->mode == Addressing_Overload) {
|
||||
yt = gb_string_make(heap_allocator(), "overloaded procedure");
|
||||
if (y->mode == Addressing_ProcGroup) {
|
||||
yt = gb_string_make(heap_allocator(), "procedure group");
|
||||
} else {
|
||||
yt = type_to_string(y->type);
|
||||
}
|
||||
@@ -2483,18 +2420,6 @@ bool check_index_value(Checker *c, bool open_range, AstNode *index_value, i64 ma
|
||||
return true;
|
||||
}
|
||||
|
||||
isize entity_overload_count(Scope *s, String name) {
|
||||
Entity *e = scope_lookup_entity(s, name);
|
||||
if (e == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
if (e->kind == Entity_Procedure) {
|
||||
// NOTE(bill): Overloads are only allowed with the same scope
|
||||
return multi_map_count(&s->elements, hash_string(e->token.string));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_hint) {
|
||||
ast_node(se, SelectorExpr, node);
|
||||
|
||||
@@ -2572,7 +2497,7 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h
|
||||
}
|
||||
|
||||
check_entity_decl(c, entity, nullptr, nullptr);
|
||||
GB_ASSERT(entity->type != nullptr || entity->kind == Entity_ProcedureGrouping);
|
||||
GB_ASSERT(entity->type != nullptr || entity->kind == Entity_ProcGroup);
|
||||
|
||||
if (is_alias) {
|
||||
// TODO(bill): Which scope do you search for for an alias?
|
||||
@@ -2580,46 +2505,26 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h
|
||||
entity_name = entity->token.string;
|
||||
}
|
||||
|
||||
isize overload_count = entity_overload_count(import_scope, entity_name);
|
||||
bool is_overloaded = overload_count > 1;
|
||||
|
||||
bool implicit_is_found = is_entity_implicitly_imported(e, entity);
|
||||
bool is_not_exported = !is_entity_exported(entity);
|
||||
if (entity->kind == Entity_ImportName) {
|
||||
is_not_exported = true;
|
||||
} else if (implicit_is_found) {
|
||||
is_not_exported = !is_overloaded;
|
||||
is_not_exported = true;
|
||||
}
|
||||
|
||||
|
||||
Entity **procs = nullptr;
|
||||
|
||||
if (is_overloaded) {
|
||||
HashKey key = hash_string(entity_name);
|
||||
procs = gb_alloc_array(heap_allocator(), Entity *, overload_count);
|
||||
multi_map_get_all(&import_scope->elements, key, procs);
|
||||
} else if (entity->kind == Entity_ProcedureGrouping) {
|
||||
is_overloaded = true;
|
||||
procs = entity->ProcedureGrouping.entities.data;
|
||||
overload_count = entity->ProcedureGrouping.entities.count;
|
||||
}
|
||||
|
||||
if (is_overloaded) {
|
||||
if (is_not_exported && entity->kind == Entity_ProcGroup) {
|
||||
check_entity_decl(c, entity, nullptr, nullptr);
|
||||
auto *pge = &entity->ProcGroup;
|
||||
Array<Entity *> procs = pge->entities;
|
||||
bool skip = false;
|
||||
for (isize i = 0; i < overload_count; i++) {
|
||||
for_array(i, procs) {
|
||||
Type *t = base_type(procs[i]->type);
|
||||
if (t == t_invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// NOTE(bill): Check to see if it's imported
|
||||
if (is_entity_implicitly_imported(e, procs[i])) {
|
||||
gb_swap(Entity *, procs[i], procs[overload_count-1]);
|
||||
overload_count--;
|
||||
i--; // NOTE(bill): Counteract the post event
|
||||
continue;
|
||||
}
|
||||
|
||||
Operand x = {};
|
||||
x.mode = Addressing_Value;
|
||||
x.type = t;
|
||||
@@ -2633,16 +2538,12 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h
|
||||
}
|
||||
|
||||
if (!skip) {
|
||||
if (overload_count > 0) {
|
||||
operand->mode = Addressing_Overload;
|
||||
operand->type = t_invalid;
|
||||
operand->expr = node;
|
||||
operand->overload_count = overload_count;
|
||||
operand->overload_entities = procs;
|
||||
return procs[0];
|
||||
} else {
|
||||
is_not_exported = true;
|
||||
}
|
||||
GB_ASSERT(e != nullptr);
|
||||
operand->mode = Addressing_ProcGroup;
|
||||
operand->type = t_invalid;
|
||||
operand->expr = node;
|
||||
operand->proc_group = e;
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2791,7 +2692,7 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h
|
||||
operand->builtin_id = cast(BuiltinProcId)entity->Builtin.id;
|
||||
break;
|
||||
|
||||
case Entity_ProcedureGrouping:
|
||||
case Entity_ProcGroup:
|
||||
entity->type = t_invalid;
|
||||
break;
|
||||
|
||||
@@ -4439,7 +4340,6 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
CallArgumentData check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode *call) {
|
||||
ast_node(ce, CallExpr, call);
|
||||
|
||||
@@ -4469,31 +4369,19 @@ CallArgumentData check_call_arguments(Checker *c, Operand *operand, Type *proc_t
|
||||
check_unpack_arguments(c, nullptr, -1, &operands, ce->args, false);
|
||||
}
|
||||
|
||||
if (operand->mode == Addressing_Overload) {
|
||||
// GB_ASSERT_MSG(operand->overload_entities != nullptr &&
|
||||
// operand->overload_count > 0,
|
||||
// "%p %td", operand->overload_entities, operand->overload_count);
|
||||
isize overload_count = operand->overload_count;
|
||||
Entity ** procs = operand->overload_entities;
|
||||
ValidIndexAndScore *valids = gb_alloc_array(heap_allocator(), ValidIndexAndScore, overload_count);
|
||||
isize valid_count = 0;
|
||||
if (operand->mode == Addressing_ProcGroup) {
|
||||
Array<Entity *> procs = proc_group_entities(c, *operand);
|
||||
|
||||
defer (gb_free(heap_allocator(), procs));
|
||||
ValidIndexAndScore *valids = gb_alloc_array(heap_allocator(), ValidIndexAndScore, procs.count);
|
||||
isize valid_count = 0;
|
||||
defer (gb_free(heap_allocator(), valids));
|
||||
|
||||
gbString expr_name = expr_to_string(operand->expr);
|
||||
defer (gb_string_free(expr_name));
|
||||
|
||||
for (isize i = 0; i < overload_count; i++) {
|
||||
Entity *e = procs[i];
|
||||
// GB_ASSERT(e->token.string == name);
|
||||
DeclInfo *d = decl_info_of_entity(&c->info, e);
|
||||
GB_ASSERT(d != nullptr);
|
||||
check_entity_decl(c, e, d, nullptr);
|
||||
}
|
||||
|
||||
for (isize i = 0; i < overload_count; i++) {
|
||||
for_array(i, procs) {
|
||||
Entity *p = procs[i];
|
||||
check_entity_decl(c, p, nullptr, nullptr);
|
||||
Type *pt = base_type(p->type);
|
||||
if (pt != nullptr && is_type_proc(pt)) {
|
||||
CallArgumentError err = CallArgumentError_None;
|
||||
@@ -4502,6 +4390,7 @@ CallArgumentData check_call_arguments(Checker *c, Operand *operand, Type *proc_t
|
||||
defer (c->context = prev_context);
|
||||
c->context.no_polymorphic_errors = true;
|
||||
c->context.allow_polymorphic_types = is_type_polymorphic(pt);
|
||||
|
||||
err = call_checker(c, call, pt, p, operands, CallArgumentMode_NoErrors, &data);
|
||||
|
||||
if (err == CallArgumentError_None) {
|
||||
@@ -4531,7 +4420,7 @@ CallArgumentData check_call_arguments(Checker *c, Operand *operand, Type *proc_t
|
||||
|
||||
|
||||
if (valid_count == 0) {
|
||||
error(operand->expr, "No overloads or ambiguous call for '%s' that match with the given arguments", expr_name);
|
||||
error(operand->expr, "No procedures or ambiguous call for procedure group '%s' that match with the given arguments", expr_name);
|
||||
gb_printf_err("\tGiven argument types -> (");
|
||||
for_array(i, operands) {
|
||||
Operand o = operands[i];
|
||||
@@ -4542,10 +4431,10 @@ CallArgumentData check_call_arguments(Checker *c, Operand *operand, Type *proc_t
|
||||
}
|
||||
gb_printf_err(")\n");
|
||||
|
||||
if (overload_count > 0) {
|
||||
if (procs.count > 0) {
|
||||
gb_printf_err("Did you mean to use one of the following:\n");
|
||||
}
|
||||
for (isize i = 0; i < overload_count; i++) {
|
||||
for_array(i, procs) {
|
||||
Entity *proc = procs[i];
|
||||
TokenPos pos = proc->token.pos;
|
||||
Type *t = base_type(proc->type);
|
||||
@@ -4562,12 +4451,12 @@ CallArgumentData check_call_arguments(Checker *c, Operand *operand, Type *proc_t
|
||||
// gb_printf_err("\t%.*s :: %s at %.*s(%td:%td)\n", LIT(name), pt, LIT(pos.file), pos.line, pos.column);
|
||||
gb_string_free(pt);
|
||||
}
|
||||
if (overload_count > 0) {
|
||||
if (procs.count > 0) {
|
||||
gb_printf_err("\n");
|
||||
}
|
||||
result_type = t_invalid;
|
||||
} else if (valid_count > 1) {
|
||||
error(operand->expr, "Ambiguous procedure call '%s' tha match with the given arguments", expr_name);
|
||||
error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name);
|
||||
gb_printf_err("\tGiven argument types -> (");
|
||||
for_array(i, operands) {
|
||||
Operand o = operands[i];
|
||||
@@ -4606,11 +4495,8 @@ CallArgumentData check_call_arguments(Checker *c, Operand *operand, Type *proc_t
|
||||
proc_type = e->type;
|
||||
CallArgumentData data = {};
|
||||
CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data);
|
||||
if (data.gen_entity != nullptr) {
|
||||
add_entity_use(c, ident, data.gen_entity);
|
||||
} else {
|
||||
add_entity_use(c, ident, e);
|
||||
}
|
||||
Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
|
||||
add_entity_use(c, ident, entity_to_use);
|
||||
return data;
|
||||
}
|
||||
} else {
|
||||
@@ -4623,11 +4509,8 @@ CallArgumentData check_call_arguments(Checker *c, Operand *operand, Type *proc_t
|
||||
Entity *e = entity_of_ident(&c->info, ident);
|
||||
CallArgumentData data = {};
|
||||
CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data);
|
||||
if (data.gen_entity != nullptr) {
|
||||
add_entity_use(c, ident, data.gen_entity);
|
||||
} else {
|
||||
add_entity_use(c, ident, e);
|
||||
}
|
||||
Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
|
||||
add_entity_use(c, ident, entity_to_use);
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -4953,7 +4836,7 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) {
|
||||
}
|
||||
|
||||
Type *proc_type = base_type(operand->type);
|
||||
if (operand->mode != Addressing_Overload) {
|
||||
if (operand->mode != Addressing_ProcGroup) {
|
||||
bool valid_type = (proc_type != nullptr) && is_type_proc(proc_type);
|
||||
bool valid_mode = is_operand_value(*operand);
|
||||
if (!valid_type || !valid_mode) {
|
||||
@@ -5206,8 +5089,8 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t
|
||||
o->mode = Addressing_Constant;
|
||||
case_end;
|
||||
|
||||
case_ast_node(pg, ProcGrouping, node);
|
||||
error(node, "Illegal use of a procedure grouping");
|
||||
case_ast_node(pg, ProcGroup, node);
|
||||
error(node, "Illegal use of a procedure group");
|
||||
o->mode = Addressing_Invalid;
|
||||
case_end;
|
||||
|
||||
@@ -6205,7 +6088,7 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
|
||||
str = gb_string_appendc(str, "---");
|
||||
case_end;
|
||||
|
||||
case_ast_node(pg, ProcGrouping, node);
|
||||
case_ast_node(pg, ProcGroup, node);
|
||||
str = gb_string_appendc(str, "proc[");
|
||||
for_array(i, pg->args) {
|
||||
if (i > 0) str = gb_string_appendc(str, ", ");
|
||||
|
||||
@@ -30,7 +30,7 @@ void check_stmt_list(Checker *c, Array<AstNode *> stmts, u32 flags) {
|
||||
if (i+1 < max) {
|
||||
switch (n->kind) {
|
||||
case AstNode_ReturnStmt:
|
||||
error(n, "Statements after this 'return' are never executed");
|
||||
error(n, "Statements after this 'return' are never execu");
|
||||
break;
|
||||
|
||||
case AstNode_BranchStmt:
|
||||
@@ -182,7 +182,7 @@ Type *check_assignment_variable(Checker *c, Operand *lhs, Operand *rhs) {
|
||||
return nullptr;
|
||||
}
|
||||
if (rhs->type == t_invalid &&
|
||||
rhs->mode != Addressing_Overload &&
|
||||
rhs->mode != Addressing_ProcGroup &&
|
||||
rhs->mode != Addressing_Builtin) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -203,17 +203,16 @@ Type *check_assignment_variable(Checker *c, Operand *lhs, Operand *rhs) {
|
||||
bool used = false;
|
||||
|
||||
if (lhs->mode == Addressing_Invalid ||
|
||||
(lhs->type == t_invalid && lhs->mode != Addressing_Overload)) {
|
||||
(lhs->type == t_invalid && lhs->mode != Addressing_ProcGroup)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (rhs->mode == Addressing_Overload) {
|
||||
isize overload_count = rhs->overload_count;
|
||||
Entity **procs = rhs->overload_entities;
|
||||
GB_ASSERT(procs != nullptr && overload_count > 0);
|
||||
if (rhs->mode == Addressing_ProcGroup) {
|
||||
Array<Entity *> procs = proc_group_entities(c, *rhs);
|
||||
GB_ASSERT(procs.count > 0);
|
||||
|
||||
// NOTE(bill): These should be done
|
||||
for (isize i = 0; i < overload_count; i++) {
|
||||
for_array(i, procs) {
|
||||
Type *t = base_type(procs[i]->type);
|
||||
if (t == t_invalid) {
|
||||
continue;
|
||||
@@ -232,8 +231,7 @@ Type *check_assignment_variable(Checker *c, Operand *lhs, Operand *rhs) {
|
||||
// HACK TODO(bill): Should the entities be freed as it's technically a leak
|
||||
rhs->mode = Addressing_Value;
|
||||
rhs->type = e->type;
|
||||
rhs->overload_count = 0;
|
||||
rhs->overload_entities = nullptr;
|
||||
rhs->proc_group = nullptr;
|
||||
}
|
||||
} else {
|
||||
if (node->kind == AstNode_Ident) {
|
||||
@@ -1680,176 +1678,177 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
case_end;
|
||||
|
||||
case_ast_node(vd, ValueDecl, node);
|
||||
if (!vd->is_mutable) {
|
||||
break;
|
||||
}
|
||||
Entity **entities = gb_alloc_array(c->allocator, Entity *, vd->names.count);
|
||||
isize entity_count = 0;
|
||||
if (vd->is_mutable) {
|
||||
Entity **entities = gb_alloc_array(c->allocator, Entity *, vd->names.count);
|
||||
isize entity_count = 0;
|
||||
|
||||
for_array(i, vd->names) {
|
||||
AstNode *name = vd->names[i];
|
||||
Entity *entity = nullptr;
|
||||
if (name->kind != AstNode_Ident) {
|
||||
error(name, "A variable declaration must be an identifier");
|
||||
} else {
|
||||
Token token = name->Ident.token;
|
||||
String str = token.string;
|
||||
Entity *found = nullptr;
|
||||
// NOTE(bill): Ignore assignments to '_'
|
||||
if (!is_blank_ident(str)) {
|
||||
found = current_scope_lookup_entity(c->context.scope, str);
|
||||
}
|
||||
if (found == nullptr) {
|
||||
entity = make_entity_variable(c->allocator, c->context.scope, token, nullptr, false);
|
||||
entity->identifier = name;
|
||||
|
||||
AstNode *fl = c->context.foreign_context.curr_library;
|
||||
if (fl != nullptr) {
|
||||
GB_ASSERT(fl->kind == AstNode_Ident);
|
||||
entity->Variable.is_foreign = true;
|
||||
entity->Variable.foreign_library_ident = fl;
|
||||
}
|
||||
for_array(i, vd->names) {
|
||||
AstNode *name = vd->names[i];
|
||||
Entity *entity = nullptr;
|
||||
if (name->kind != AstNode_Ident) {
|
||||
error(name, "A variable declaration must be an identifier");
|
||||
} else {
|
||||
TokenPos pos = found->token.pos;
|
||||
error(token,
|
||||
"Redeclaration of '%.*s' in this scope\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
LIT(str), LIT(pos.file), pos.line, pos.column);
|
||||
entity = found;
|
||||
}
|
||||
}
|
||||
if (entity == nullptr) {
|
||||
entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name));
|
||||
}
|
||||
entity->parent_proc_decl = c->context.curr_proc_decl;
|
||||
entities[entity_count++] = entity;
|
||||
}
|
||||
Token token = name->Ident.token;
|
||||
String str = token.string;
|
||||
Entity *found = nullptr;
|
||||
// NOTE(bill): Ignore assignments to '_'
|
||||
if (!is_blank_ident(str)) {
|
||||
found = current_scope_lookup_entity(c->context.scope, str);
|
||||
}
|
||||
if (found == nullptr) {
|
||||
entity = make_entity_variable(c->allocator, c->context.scope, token, nullptr, false);
|
||||
entity->identifier = name;
|
||||
|
||||
Type *init_type = nullptr;
|
||||
if (vd->type != nullptr) {
|
||||
init_type = check_type(c, vd->type, nullptr);
|
||||
if (init_type == nullptr) {
|
||||
init_type = t_invalid;
|
||||
} else if (is_type_polymorphic(base_type(init_type))) {
|
||||
gbString str = type_to_string(init_type);
|
||||
error(vd->type, "Invalid use of a polymorphic type '%s' in variable declaration", str);
|
||||
gb_string_free(str);
|
||||
init_type = t_invalid;
|
||||
} else if (is_type_empty_union(init_type)) {
|
||||
gbString str = type_to_string(init_type);
|
||||
error(vd->type, "An empty union '%s' cannot be instantiated in variable declaration", str);
|
||||
gb_string_free(str);
|
||||
init_type = t_invalid;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO NOTE(bill): This technically checks things multple times
|
||||
AttributeContext ac = make_attribute_context(c->context.foreign_context.link_prefix);
|
||||
check_decl_attributes(c, vd->attributes, var_decl_attribute, &ac);
|
||||
|
||||
for (isize i = 0; i < entity_count; i++) {
|
||||
Entity *e = entities[i];
|
||||
GB_ASSERT(e != nullptr);
|
||||
if (e->flags & EntityFlag_Visited) {
|
||||
e->type = t_invalid;
|
||||
continue;
|
||||
}
|
||||
e->flags |= EntityFlag_Visited;
|
||||
|
||||
if (e->type == nullptr) {
|
||||
e->type = init_type;
|
||||
}
|
||||
ac.link_name = handle_link_name(c, e->token, ac.link_name, ac.link_prefix);
|
||||
e->Variable.thread_local_model = ac.thread_local_model;
|
||||
|
||||
if (ac.link_name.len > 0) {
|
||||
e->Variable.link_name = ac.link_name;
|
||||
}
|
||||
}
|
||||
|
||||
check_arity_match(c, vd);
|
||||
check_init_variables(c, entities, entity_count, vd->values, str_lit("variable declaration"));
|
||||
|
||||
for (isize i = 0; i < entity_count; i++) {
|
||||
Entity *e = entities[i];
|
||||
if (e->Variable.is_foreign) {
|
||||
if (vd->values.count > 0) {
|
||||
error(e->token, "A foreign variable declaration cannot have a default value");
|
||||
}
|
||||
|
||||
String name = e->token.string;
|
||||
if (e->Variable.link_name.len > 0) {
|
||||
name = e->Variable.link_name;
|
||||
}
|
||||
|
||||
if (vd->values.count > 0) {
|
||||
error(e->token, "A foreign variable declaration cannot have a default value");
|
||||
}
|
||||
init_entity_foreign_library(c, e);
|
||||
|
||||
auto *fp = &c->info.foreigns;
|
||||
HashKey key = hash_string(name);
|
||||
Entity **found = map_get(fp, key);
|
||||
if (found) {
|
||||
Entity *f = *found;
|
||||
TokenPos pos = f->token.pos;
|
||||
Type *this_type = base_type(e->type);
|
||||
Type *other_type = base_type(f->type);
|
||||
if (!are_types_identical(this_type, other_type)) {
|
||||
error(e->token,
|
||||
"Foreign entity '%.*s' previously declared elsewhere with a different type\n"
|
||||
AstNode *fl = c->context.foreign_context.curr_library;
|
||||
if (fl != nullptr) {
|
||||
GB_ASSERT(fl->kind == AstNode_Ident);
|
||||
entity->Variable.is_foreign = true;
|
||||
entity->Variable.foreign_library_ident = fl;
|
||||
}
|
||||
} else {
|
||||
TokenPos pos = found->token.pos;
|
||||
error(token,
|
||||
"Redeclaration of '%.*s' in this scope\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
LIT(name), LIT(pos.file), pos.line, pos.column);
|
||||
LIT(str), LIT(pos.file), pos.line, pos.column);
|
||||
entity = found;
|
||||
}
|
||||
} else {
|
||||
map_set(fp, key, e);
|
||||
}
|
||||
}
|
||||
add_entity(c, c->context.scope, e->identifier, e);
|
||||
}
|
||||
|
||||
if (vd->is_using != 0) {
|
||||
Token token = ast_node_token(node);
|
||||
if (vd->type != nullptr && entity_count > 1) {
|
||||
error(token, "'using' can only be applied to one variable of the same type");
|
||||
// TODO(bill): Should a 'continue' happen here?
|
||||
if (entity == nullptr) {
|
||||
entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name));
|
||||
}
|
||||
entity->parent_proc_decl = c->context.curr_proc_decl;
|
||||
entities[entity_count++] = entity;
|
||||
}
|
||||
|
||||
for (isize entity_index = 0; entity_index < entity_count; entity_index++) {
|
||||
Entity *e = entities[entity_index];
|
||||
if (e == nullptr) {
|
||||
continue;
|
||||
Type *init_type = nullptr;
|
||||
if (vd->type != nullptr) {
|
||||
init_type = check_type(c, vd->type, nullptr);
|
||||
if (init_type == nullptr) {
|
||||
init_type = t_invalid;
|
||||
} else if (is_type_polymorphic(base_type(init_type))) {
|
||||
gbString str = type_to_string(init_type);
|
||||
error(vd->type, "Invalid use of a polymorphic type '%s' in variable declaration", str);
|
||||
gb_string_free(str);
|
||||
init_type = t_invalid;
|
||||
} else if (is_type_empty_union(init_type)) {
|
||||
gbString str = type_to_string(init_type);
|
||||
error(vd->type, "An empty union '%s' cannot be instantiated in variable declaration", str);
|
||||
gb_string_free(str);
|
||||
init_type = t_invalid;
|
||||
}
|
||||
if (e->kind != Entity_Variable) {
|
||||
continue;
|
||||
}
|
||||
bool is_immutable = e->Variable.is_immutable;
|
||||
String name = e->token.string;
|
||||
Type *t = base_type(type_deref(e->type));
|
||||
}
|
||||
|
||||
if (is_blank_ident(name)) {
|
||||
error(token, "'using' cannot be applied variable declared as '_'");
|
||||
} else if (is_type_struct(t) || is_type_raw_union(t)) {
|
||||
Scope *scope = scope_of_node(&c->info, t->Struct.node);
|
||||
for_array(i, scope->elements.entries) {
|
||||
Entity *f = scope->elements.entries[i].value;
|
||||
if (f->kind == Entity_Variable) {
|
||||
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
|
||||
uvar->Variable.is_immutable = is_immutable;
|
||||
Entity *prev = scope_insert_entity(c->context.scope, uvar);
|
||||
if (prev != nullptr) {
|
||||
error(token, "Namespace collision while 'using' '%.*s' of: %.*s", LIT(name), LIT(prev->token.string));
|
||||
return;
|
||||
|
||||
// TODO NOTE(bill): This technically checks things multple times
|
||||
AttributeContext ac = make_attribute_context(c->context.foreign_context.link_prefix);
|
||||
check_decl_attributes(c, vd->attributes, var_decl_attribute, &ac);
|
||||
|
||||
for (isize i = 0; i < entity_count; i++) {
|
||||
Entity *e = entities[i];
|
||||
GB_ASSERT(e != nullptr);
|
||||
if (e->flags & EntityFlag_Visited) {
|
||||
e->type = t_invalid;
|
||||
continue;
|
||||
}
|
||||
e->flags |= EntityFlag_Visited;
|
||||
|
||||
if (e->type == nullptr) {
|
||||
e->type = init_type;
|
||||
}
|
||||
ac.link_name = handle_link_name(c, e->token, ac.link_name, ac.link_prefix);
|
||||
e->Variable.thread_local_model = ac.thread_local_model;
|
||||
|
||||
if (ac.link_name.len > 0) {
|
||||
e->Variable.link_name = ac.link_name;
|
||||
}
|
||||
}
|
||||
|
||||
check_arity_match(c, vd);
|
||||
check_init_variables(c, entities, entity_count, vd->values, str_lit("variable declaration"));
|
||||
|
||||
for (isize i = 0; i < entity_count; i++) {
|
||||
Entity *e = entities[i];
|
||||
if (e->Variable.is_foreign) {
|
||||
if (vd->values.count > 0) {
|
||||
error(e->token, "A foreign variable declaration cannot have a default value");
|
||||
}
|
||||
|
||||
String name = e->token.string;
|
||||
if (e->Variable.link_name.len > 0) {
|
||||
name = e->Variable.link_name;
|
||||
}
|
||||
|
||||
if (vd->values.count > 0) {
|
||||
error(e->token, "A foreign variable declaration cannot have a default value");
|
||||
}
|
||||
init_entity_foreign_library(c, e);
|
||||
|
||||
auto *fp = &c->info.foreigns;
|
||||
HashKey key = hash_string(name);
|
||||
Entity **found = map_get(fp, key);
|
||||
if (found) {
|
||||
Entity *f = *found;
|
||||
TokenPos pos = f->token.pos;
|
||||
Type *this_type = base_type(e->type);
|
||||
Type *other_type = base_type(f->type);
|
||||
if (!are_types_identical(this_type, other_type)) {
|
||||
error(e->token,
|
||||
"Foreign entity '%.*s' previously declared elsewhere with a different type\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
LIT(name), LIT(pos.file), pos.line, pos.column);
|
||||
}
|
||||
} else {
|
||||
map_set(fp, key, e);
|
||||
}
|
||||
}
|
||||
add_entity(c, c->context.scope, e->identifier, e);
|
||||
}
|
||||
|
||||
if (vd->is_using != 0) {
|
||||
Token token = ast_node_token(node);
|
||||
if (vd->type != nullptr && entity_count > 1) {
|
||||
error(token, "'using' can only be applied to one variable of the same type");
|
||||
// TODO(bill): Should a 'continue' happen here?
|
||||
}
|
||||
|
||||
for (isize entity_index = 0; entity_index < entity_count; entity_index++) {
|
||||
Entity *e = entities[entity_index];
|
||||
if (e == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (e->kind != Entity_Variable) {
|
||||
continue;
|
||||
}
|
||||
bool is_immutable = e->Variable.is_immutable;
|
||||
String name = e->token.string;
|
||||
Type *t = base_type(type_deref(e->type));
|
||||
|
||||
if (is_blank_ident(name)) {
|
||||
error(token, "'using' cannot be applied variable declared as '_'");
|
||||
} else if (is_type_struct(t) || is_type_raw_union(t)) {
|
||||
Scope *scope = scope_of_node(&c->info, t->Struct.node);
|
||||
for_array(i, scope->elements.entries) {
|
||||
Entity *f = scope->elements.entries[i].value;
|
||||
if (f->kind == Entity_Variable) {
|
||||
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
|
||||
uvar->Variable.is_immutable = is_immutable;
|
||||
Entity *prev = scope_insert_entity(c->context.scope, uvar);
|
||||
if (prev != nullptr) {
|
||||
error(token, "Namespace collision while 'using' '%.*s' of: %.*s", LIT(name), LIT(prev->token.string));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// NOTE(bill): skip the rest to remove extra errors
|
||||
error(token, "'using' can only be applied to variables of type struct or raw_union");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// NOTE(bill): skip the rest to remove extra errors
|
||||
error(token, "'using' can only be applied to variables of type struct or raw_union");
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// constant value declarations
|
||||
}
|
||||
case_end;
|
||||
}
|
||||
|
||||
166
src/checker.cpp
166
src/checker.cpp
@@ -120,7 +120,7 @@ enum AddressingMode {
|
||||
Addressing_Constant, // constant
|
||||
Addressing_Type, // type
|
||||
Addressing_Builtin, // built-in procedure
|
||||
Addressing_Overload, // overloaded procedure
|
||||
Addressing_ProcGroup, // procedure group (overloaded procedure)
|
||||
Addressing_MapIndex, // map index expression -
|
||||
// lhs: acts like a Variable
|
||||
// rhs: acts like OptionalOk
|
||||
@@ -138,8 +138,7 @@ struct Operand {
|
||||
ExactValue value;
|
||||
AstNode * expr;
|
||||
BuiltinProcId builtin_id;
|
||||
isize overload_count;
|
||||
Entity ** overload_entities;
|
||||
Entity * proc_group;
|
||||
};
|
||||
|
||||
struct TypeAndValue {
|
||||
@@ -166,6 +165,8 @@ bool is_operand_undef(Operand o) {
|
||||
return o.mode == Addressing_Value && o.type == t_untyped_undef;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct BlockLabel {
|
||||
String name;
|
||||
AstNode *label; // AstNode_Label;
|
||||
@@ -780,28 +781,10 @@ Entity *scope_insert_entity(Scope *s, Entity *entity) {
|
||||
HashKey key = hash_string(name);
|
||||
Entity **found = map_get(&s->elements, key);
|
||||
|
||||
#ifndef DISABLE_PROCEDURE_OVERLOADING
|
||||
// IMPORTANT NOTE(bill): Procedure overloading code
|
||||
Entity *prev = nullptr;
|
||||
if (found) {
|
||||
prev = *found;
|
||||
if (prev->kind != Entity_Procedure ||
|
||||
entity->kind != Entity_Procedure) {
|
||||
return prev;
|
||||
}
|
||||
}
|
||||
|
||||
if (prev != nullptr && entity->kind == Entity_Procedure) {
|
||||
multi_map_insert(&s->elements, key, entity);
|
||||
} else {
|
||||
map_set(&s->elements, key, entity);
|
||||
}
|
||||
#else
|
||||
if (found) {
|
||||
return *found;
|
||||
}
|
||||
map_set(&s->elements, key, entity);
|
||||
#endif
|
||||
if (entity->scope == nullptr) {
|
||||
entity->scope = s;
|
||||
}
|
||||
@@ -1620,6 +1603,18 @@ Type *find_core_type(Checker *c, String name) {
|
||||
|
||||
void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type);
|
||||
|
||||
Array<Entity *> proc_group_entities(Checker *c, Operand o) {
|
||||
Array<Entity *> procs = {};
|
||||
if (o.mode == Addressing_ProcGroup) {
|
||||
GB_ASSERT(o.proc_group != nullptr);
|
||||
if (o.proc_group->kind == Entity_ProcGroup) {
|
||||
check_entity_decl(c, o.proc_group, nullptr, nullptr);
|
||||
return o.proc_group->ProcGroup.entities;
|
||||
}
|
||||
}
|
||||
return procs;
|
||||
}
|
||||
|
||||
void init_preload(Checker *c) {
|
||||
if (t_type_info == nullptr) {
|
||||
Entity *type_info_entity = find_core_entity(c, str_lit("Type_Info"));
|
||||
@@ -1736,118 +1731,6 @@ void check_collect_entities(Checker *c, Array<AstNode *> nodes);
|
||||
void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws);
|
||||
void check_delayed_file_import_entity(Checker *c, AstNode *decl);
|
||||
|
||||
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 = multi_map_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 = multi_map_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);
|
||||
multi_map_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;
|
||||
|
||||
if (q->type == nullptr || q->type == t_invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ProcTypeOverloadKind kind = are_proc_types_overload_safe(p->type, q->type);
|
||||
switch (kind) {
|
||||
case ProcOverload_Identical:
|
||||
error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in this scope", LIT(name));
|
||||
is_invalid = true;
|
||||
break;
|
||||
// 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_Polymorphic:
|
||||
#if 0
|
||||
error(p->token, "Overloaded procedure '%.*s' has a polymorphic counterpart in this scope which is not allowed", LIT(name));
|
||||
is_invalid = true;
|
||||
#endif
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
struct AttributeContext {
|
||||
String link_name;
|
||||
String link_prefix;
|
||||
@@ -2259,11 +2142,11 @@ void check_collect_value_decl(Checker *c, AstNode *decl) {
|
||||
}
|
||||
d->proc_lit = init;
|
||||
d->type_expr = pl->type;
|
||||
} else if (init->kind == AstNode_ProcGrouping) {
|
||||
ast_node(pg, ProcGrouping, init);
|
||||
e = make_entity_procedure_grouping(c->allocator, d->scope, token, nullptr);
|
||||
} else if (init->kind == AstNode_ProcGroup) {
|
||||
ast_node(pg, ProcGroup, init);
|
||||
e = make_entity_proc_group(c->allocator, d->scope, token, nullptr);
|
||||
if (fl != nullptr) {
|
||||
error(name, "Procedure groupings are not allowed within a foreign block");
|
||||
error(name, "Procedure groups are not allowed within a foreign block");
|
||||
}
|
||||
d->init_expr = init;
|
||||
} else {
|
||||
@@ -2447,15 +2330,6 @@ void check_all_global_entities(Checker *c) {
|
||||
init_preload(c);
|
||||
}
|
||||
}
|
||||
|
||||
for_array(i, c->info.entities.entries) {
|
||||
auto *entry = &c->info.entities.entries[i];
|
||||
Entity *e = cast(Entity *)entry->key.ptr;
|
||||
if (e->kind != Entity_Procedure) {
|
||||
continue;
|
||||
}
|
||||
check_procedure_overloading(c, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ struct DeclInfo;
|
||||
ENTITY_KIND(Variable) \
|
||||
ENTITY_KIND(TypeName) \
|
||||
ENTITY_KIND(Procedure) \
|
||||
ENTITY_KIND(ProcedureGrouping) \
|
||||
ENTITY_KIND(ProcGroup) \
|
||||
ENTITY_KIND(Builtin) \
|
||||
ENTITY_KIND(Alias) \
|
||||
ENTITY_KIND(ImportName) \
|
||||
@@ -110,7 +110,7 @@ struct Entity {
|
||||
} Procedure;
|
||||
struct {
|
||||
Array<Entity *> entities;
|
||||
} ProcedureGrouping;
|
||||
} ProcGroup;
|
||||
struct {
|
||||
i32 id;
|
||||
} Builtin;
|
||||
@@ -248,8 +248,8 @@ Entity *make_entity_procedure(gbAllocator a, Scope *scope, Token token, Type *si
|
||||
return entity;
|
||||
}
|
||||
|
||||
Entity *make_entity_procedure_grouping(gbAllocator a, Scope *scope, Token token, Type *type) {
|
||||
Entity *entity = alloc_entity(a, Entity_ProcedureGrouping, scope, token, type);
|
||||
Entity *make_entity_proc_group(gbAllocator a, Scope *scope, Token token, Type *type) {
|
||||
Entity *entity = alloc_entity(a, Entity_ProcGroup, scope, token, type);
|
||||
return entity;
|
||||
}
|
||||
|
||||
|
||||
21
src/ir.cpp
21
src/ir.cpp
@@ -3620,7 +3620,7 @@ String ir_mangle_name(irGen *s, String path, Entity *e) {
|
||||
isize base_len = ext-1-base;
|
||||
|
||||
isize max_len = base_len + 1 + 1 + 10 + 1 + name.len;
|
||||
bool require_suffix_id = check_is_entity_overloaded(e) || is_type_polymorphic(e->type);
|
||||
bool require_suffix_id = is_type_polymorphic(e->type);
|
||||
if (require_suffix_id) {
|
||||
max_len += 21;
|
||||
}
|
||||
@@ -8304,19 +8304,22 @@ void ir_gen_tree(irGen *s) {
|
||||
}
|
||||
|
||||
for_array(i, info->entities.entries) {
|
||||
auto *entry = &info->entities.entries[i];
|
||||
Entity *e = cast(Entity *)entry->key.ptr;
|
||||
String name = e->token.string;
|
||||
DeclInfo *decl = entry->value;
|
||||
Scope *scope = e->scope;
|
||||
auto * entry = &info->entities.entries[i];
|
||||
Entity * e = cast(Entity *)entry->key.ptr;
|
||||
String name = e->token.string;
|
||||
DeclInfo *decl = entry->value;
|
||||
Scope * scope = e->scope;
|
||||
|
||||
if (!scope->is_file) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e->kind == Entity_Variable || e->kind == Entity_ProcedureGrouping) {
|
||||
switch (e->kind) {
|
||||
case Entity_Variable:
|
||||
// NOTE(bill): Handled above as it requires a specific load order
|
||||
continue;
|
||||
case Entity_ProcGroup:
|
||||
continue;
|
||||
}
|
||||
|
||||
bool polymorphic_struct = false;
|
||||
@@ -8341,8 +8344,6 @@ void ir_gen_tree(irGen *s) {
|
||||
} else {
|
||||
name = ir_mangle_name(s, e->token.pos.file, e);
|
||||
}
|
||||
} else if (check_is_entity_overloaded(e)) {
|
||||
name = ir_mangle_name(s, e->token.pos.file, e);
|
||||
}
|
||||
ir_add_entity_name(m, e, name);
|
||||
|
||||
@@ -8373,7 +8374,7 @@ void ir_gen_tree(irGen *s) {
|
||||
ir_module_add_value(m, e, p);
|
||||
HashKey hash_name = hash_string(name);
|
||||
if (map_get(&m->members, hash_name) == nullptr) {
|
||||
multi_map_insert(&m->members, hash_name, p);
|
||||
map_set(&m->members, hash_name, p);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#define ALLOW_ARRAY_PROGRAMMING
|
||||
// #define DISABLE_PROCEDURE_OVERLOADING
|
||||
// #define NO_ARRAY_BOUNDS_CHECK
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// with the use of the `multi_*` procedures.
|
||||
// TODO(bill): I should probably allow the `multi_map_*` stuff to be #ifdefed out
|
||||
|
||||
#define MAP_ENABLE_MULTI_MAP 1
|
||||
|
||||
#ifndef MAP_UTIL_STUFF
|
||||
#define MAP_UTIL_STUFF
|
||||
// NOTE(bill): This util stuff is the same for every `Map`
|
||||
@@ -110,6 +112,7 @@ template <typename T> void map_clear (Map<T> *h);
|
||||
template <typename T> void map_grow (Map<T> *h);
|
||||
template <typename T> void map_rehash (Map<T> *h, isize new_count);
|
||||
|
||||
#if MAP_ENABLE_MULTI_MAP
|
||||
// Mutlivalued map procedure
|
||||
template <typename T> MapEntry<T> * multi_map_find_first(Map<T> *h, HashKey key);
|
||||
template <typename T> MapEntry<T> * multi_map_find_next (Map<T> *h, MapEntry<T> *e);
|
||||
@@ -119,7 +122,7 @@ template <typename T> void multi_map_get_all (Map<T> *h, HashKey key, T *item
|
||||
template <typename T> void multi_map_insert (Map<T> *h, HashKey key, T const &value);
|
||||
template <typename T> void multi_map_remove (Map<T> *h, HashKey key, MapEntry<T> *e);
|
||||
template <typename T> void multi_map_remove_all(Map<T> *h, HashKey key);
|
||||
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
gb_inline void map_init(Map<T> *h, gbAllocator a, isize capacity) {
|
||||
@@ -291,7 +294,7 @@ gb_inline void map_clear(Map<T> *h) {
|
||||
}
|
||||
|
||||
|
||||
#if 1
|
||||
#if MAP_ENABLE_MULTI_MAP
|
||||
template <typename T>
|
||||
MapEntry<T> *multi_map_find_first(Map<T> *h, HashKey key) {
|
||||
isize i = map__find(h, key).entry_index;
|
||||
|
||||
@@ -164,7 +164,7 @@ Array<AstNode *> make_ast_node_array(AstFile *f, isize init_capacity = 8) {
|
||||
Token token; \
|
||||
AstNode *expr; \
|
||||
}) \
|
||||
AST_NODE_KIND(ProcGrouping, "procedure grouping", struct { \
|
||||
AST_NODE_KIND(ProcGroup, "procedure group", struct { \
|
||||
Token token; \
|
||||
Token open; \
|
||||
Token close; \
|
||||
@@ -540,7 +540,7 @@ Token ast_node_token(AstNode *node) {
|
||||
case AstNode_Undef: return node->Undef;
|
||||
case AstNode_BasicLit: return node->BasicLit;
|
||||
case AstNode_BasicDirective: return node->BasicDirective.token;
|
||||
case AstNode_ProcGrouping: return node->ProcGrouping.token;
|
||||
case AstNode_ProcGroup: return node->ProcGroup.token;
|
||||
case AstNode_ProcLit: return ast_node_token(node->ProcLit.type);
|
||||
case AstNode_CompoundLit:
|
||||
if (node->CompoundLit.type != nullptr) {
|
||||
@@ -669,8 +669,8 @@ AstNode *clone_ast_node(gbAllocator a, AstNode *node) {
|
||||
case AstNode_Ellipsis:
|
||||
n->Ellipsis.expr = clone_ast_node(a, n->Ellipsis.expr);
|
||||
break;
|
||||
case AstNode_ProcGrouping:
|
||||
n->ProcGrouping.args = clone_ast_node_array(a, n->ProcGrouping.args);
|
||||
case AstNode_ProcGroup:
|
||||
n->ProcGroup.args = clone_ast_node_array(a, n->ProcGroup.args);
|
||||
break;
|
||||
case AstNode_ProcLit:
|
||||
n->ProcLit.type = clone_ast_node(a, n->ProcLit.type);
|
||||
@@ -1121,12 +1121,12 @@ AstNode *ast_ellipsis(AstFile *f, Token token, AstNode *expr) {
|
||||
}
|
||||
|
||||
|
||||
AstNode *ast_proc_grouping(AstFile *f, Token token, Token open, Token close, Array<AstNode *> args) {
|
||||
AstNode *result = make_ast_node(f, AstNode_ProcGrouping);
|
||||
result->ProcGrouping.token = token;
|
||||
result->ProcGrouping.open = open;
|
||||
result->ProcGrouping.close = close;
|
||||
result->ProcGrouping.args = args;
|
||||
AstNode *ast_proc_group(AstFile *f, Token token, Token open, Token close, Array<AstNode *> args) {
|
||||
AstNode *result = make_ast_node(f, AstNode_ProcGroup);
|
||||
result->ProcGroup.token = token;
|
||||
result->ProcGroup.open = open;
|
||||
result->ProcGroup.close = close;
|
||||
result->ProcGroup.args = args;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -2266,11 +2266,11 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
|
||||
return expr;
|
||||
} break;
|
||||
|
||||
// Parse Procedure Type or Literal or Grouping
|
||||
// Parse Procedure Type or Literal or Group
|
||||
case Token_proc: {
|
||||
Token token = expect_token(f, Token_proc);
|
||||
|
||||
if (f->curr_token.kind == Token_OpenBracket) { // ProcGrouping
|
||||
if (f->curr_token.kind == Token_OpenBracket) { // ProcGroup
|
||||
Token open = expect_token(f, Token_OpenBracket);
|
||||
|
||||
Array<AstNode *> args = {};
|
||||
@@ -2289,10 +2289,10 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
|
||||
Token close = expect_token(f, Token_CloseBracket);
|
||||
|
||||
if (args.count == 0) {
|
||||
syntax_error(token, "Expected a least 1 argument in a procedure grouping");
|
||||
syntax_error(token, "Expected a least 1 argument in a procedure group");
|
||||
}
|
||||
|
||||
return ast_proc_grouping(f, token, open, close, args);
|
||||
return ast_proc_group(f, token, open, close, args);
|
||||
}
|
||||
|
||||
AstNode *type = parse_proc_type(f, token);
|
||||
|
||||
Reference in New Issue
Block a user