diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 59f285ed1..1529010ca 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -28,7 +28,7 @@ struct CallArgumentData { struct PolyProcData { Entity * gen_entity; - ProcedureInfo proc_info; + ProcInfo proc_info; }; struct ValidIndexAndScore { @@ -166,7 +166,7 @@ bool check_is_assignable_to_using_subtype(Type *src, Type *dst) { } bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_entity, Type *type, - Array *param_operands, PolyProcData *poly_proc_data) { + Array *param_operands, AstNode *poly_def_node, PolyProcData *poly_proc_data) { /////////////////////////////////////////////////////////////////////////////// // // // TODO CLEANUP(bill): This procedure is very messy and hacky. Clean this!!! // @@ -315,6 +315,7 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti } + AstNode *proc_lit = clone_ast_node(a, old_decl->proc_lit); ast_node(pl, ProcLit, proc_lit); // NOTE(bill): Associate the scope declared above withinth this procedure declaration's type @@ -322,6 +323,17 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti final_proc_type->Proc.is_poly_specialized = true; final_proc_type->Proc.is_polymorphic = true; + + for (isize i = 0; i < operands.count; i++) { + Operand o = operands[i]; + if (final_proc_type == o.type || + base_entity->type == o.type) { + // NOTE(bill): Cycle + final_proc_type->Proc.is_poly_specialized = false; + break; + } + } + u64 tags = base_entity->Procedure.tags; AstNode *ident = clone_ast_node(a, base_entity->identifier); Token token = ident->Ident.token; @@ -347,7 +359,7 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti } } - ProcedureInfo proc_info = {}; + ProcInfo proc_info = {}; proc_info.file = file; proc_info.token = token; proc_info.decl = d; @@ -355,6 +367,7 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti proc_info.body = pl->body; proc_info.tags = tags; proc_info.generated_from_polymorphic = true; + proc_info.poly_def_node = poly_def_node; if (found_gen_procs) { array_add(found_gen_procs, entity); @@ -377,15 +390,15 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti return true; } -bool check_polymorphic_procedure_assignment(CheckerContext *c, Operand *operand, Type *type, PolyProcData *poly_proc_data) { +bool check_polymorphic_procedure_assignment(CheckerContext *c, Operand *operand, Type *type, AstNode *poly_def_node, PolyProcData *poly_proc_data) { if (operand->expr == nullptr) return false; Entity *base_entity = entity_of_ident(operand->expr); if (base_entity == nullptr) return false; - return find_or_generate_polymorphic_procedure(c, base_entity, type, nullptr, poly_proc_data); + return find_or_generate_polymorphic_procedure(c, base_entity, type, nullptr, poly_def_node, poly_proc_data); } -bool find_or_generate_polymorphic_procedure_from_parameters(CheckerContext *c, Entity *base_entity, Array *operands, PolyProcData *poly_proc_data) { - return find_or_generate_polymorphic_procedure(c, base_entity, nullptr, operands, poly_proc_data); +bool find_or_generate_polymorphic_procedure_from_parameters(CheckerContext *c, Entity *base_entity, Array *operands, AstNode *poly_def_node, PolyProcData *poly_proc_data) { + return find_or_generate_polymorphic_procedure(c, base_entity, nullptr, operands, poly_def_node, poly_proc_data); } bool check_type_specialization_to(CheckerContext *c, Type *specialization, Type *type, bool compound, bool modify_type); @@ -551,7 +564,7 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type return 3; } PolyProcData poly_proc_data = {}; - if (check_polymorphic_procedure_assignment(c, operand, type, &poly_proc_data)) { + if (check_polymorphic_procedure_assignment(c, operand, type, operand->expr, &poly_proc_data)) { add_entity_use(c, operand->expr, poly_proc_data.gen_entity); return 4; } @@ -4088,7 +4101,6 @@ void check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, } - CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { ast_node(ce, CallExpr, call); GB_ASSERT(is_type_proc(proc_type)); @@ -4195,7 +4207,7 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { PolyProcData poly_proc_data = {}; if (pt->is_polymorphic && !pt->is_poly_specialized) { - if (find_or_generate_polymorphic_procedure_from_parameters(c, entity, &operands, &poly_proc_data)) { + if (find_or_generate_polymorphic_procedure_from_parameters(c, entity, &operands, call, &poly_proc_data)) { gen_entity = poly_proc_data.gen_entity; GB_ASSERT(is_type_proc(gen_entity->type)); final_proc_type = gen_entity->type; @@ -4333,7 +4345,8 @@ isize lookup_procedure_result(TypeProc *pt, String result_name) { CALL_ARGUMENT_CHECKER(check_named_call_arguments) { ast_node(ce, CallExpr, call); GB_ASSERT(is_type_proc(proc_type)); - TypeProc *pt = &base_type(proc_type)->Proc; + proc_type = base_type(proc_type); + TypeProc *pt = &proc_type->Proc; i64 score = 0; bool show_error = show_error_mode == CallArgumentMode_ShowErrors; @@ -4419,10 +4432,11 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) { Entity *gen_entity = nullptr; if (pt->is_polymorphic && !pt->is_poly_specialized && err == CallArgumentError_None) { PolyProcData poly_proc_data = {}; - if (find_or_generate_polymorphic_procedure_from_parameters(c, entity, &ordered_operands, &poly_proc_data)) { + if (find_or_generate_polymorphic_procedure_from_parameters(c, entity, &ordered_operands, call, &poly_proc_data)) { gen_entity = poly_proc_data.gen_entity; Type *gept = base_type(gen_entity->type); GB_ASSERT(is_type_proc(gept)); + proc_type = gept; pt = &gept->Proc; } } diff --git a/src/check_type.cpp b/src/check_type.cpp index 7751dca59..9a2493aed 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -401,6 +401,11 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, AstNode *node, Ar is_poly_specialized = false; break; } + if (struct_type == o.type) { + // NOTE(bill): Cycle + is_poly_specialized = false; + break; + } } } @@ -604,7 +609,7 @@ void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *named_type, Ast if (et->is_export) { Scope *parent = ctx->scope->parent; if (parent->is_file) { - // NOTE(bhall): Use package scope + // NOTE(bill): Use package scope parent = parent->parent; } for_array(i, fields) { diff --git a/src/checker.cpp b/src/checker.cpp index 3ec95e842..97f1e61e2 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -620,11 +620,14 @@ CheckerContext make_checker_context(Checker *c) { ctx.type_path = new_checker_type_path(); ctx.type_level = 0; + ctx.poly_path = new_checker_poly_path(); + ctx.poly_level = 0; return ctx; } void destroy_checker_context(CheckerContext *ctx) { destroy_checker_type_path(ctx->type_path); + destroy_checker_poly_path(ctx->poly_path); } void init_checker(Checker *c, Parser *parser) { @@ -1090,13 +1093,13 @@ void add_type_info_type(CheckerContext *c, Type *t) { } } -void check_procedure_later(Checker *c, ProcedureInfo info) { +void check_procedure_later(Checker *c, ProcInfo info) { GB_ASSERT(info.decl != nullptr); array_add(&c->procs_to_check, info); } void check_procedure_later(Checker *c, AstFile *file, Token token, DeclInfo *decl, Type *type, AstNode *body, u64 tags) { - ProcedureInfo info = {}; + ProcInfo info = {}; info.file = file; info.token = token; info.decl = decl; @@ -1505,6 +1508,31 @@ Entity *check_type_path_pop(CheckerContext *c) { } +CheckerPolyPath *new_checker_poly_path(void) { + gbAllocator a = heap_allocator(); + auto *pp = gb_alloc_item(a, CheckerPolyPath); + array_init(pp, a, 0, 16); + return pp; +} + +void destroy_checker_poly_path(CheckerPolyPath *pp) { + array_free(pp); + gb_free(heap_allocator(), pp); +} + + +void check_poly_path_push(CheckerContext *c, Type *t) { + GB_ASSERT(c->poly_path != nullptr); + GB_ASSERT(t != nullptr); + GB_ASSERT(is_type_polymorphic(t)); + array_add(c->poly_path, t); +} + +Type *check_poly_path_pop(CheckerContext *c) { + GB_ASSERT(c->poly_path != nullptr); + return array_pop(c->poly_path); +} + void check_entity_decl(CheckerContext *c, Entity *e, DeclInfo *d, Type *named_type); @@ -3095,7 +3123,7 @@ void calculate_global_init_order(Checker *c) { } -void check_proc_info(Checker *c, ProcedureInfo pi) { +void check_proc_info(Checker *c, ProcInfo pi) { if (pi.type == nullptr) { return; } @@ -3107,8 +3135,13 @@ void check_proc_info(Checker *c, ProcedureInfo pi) { TypeProc *pt = &pi.type->Proc; String name = pi.token.string; - if (pt->is_polymorphic) { - GB_ASSERT_MSG(pt->is_poly_specialized, "%.*s", LIT(name)); + if (pt->is_polymorphic && !pt->is_poly_specialized) { + Token token = pi.token; + if (pi.poly_def_node != nullptr) { + token = ast_node_token(pi.poly_def_node); + } + error(token, "Unspecialized polymorphic procedure '%.*s'", LIT(name)); + return; } bool bounds_check = (pi.tags & ProcTag_bounds_check) != 0; @@ -3203,7 +3236,7 @@ void check_parsed_files(Checker *c) { TIME_SECTION("check procedure bodies"); // NOTE(bill): Nested procedures bodies will be added to this "queue" for_array(i, c->procs_to_check) { - ProcedureInfo pi = c->procs_to_check[i]; + ProcInfo pi = c->procs_to_check[i]; check_proc_info(c, pi); } diff --git a/src/checker.hpp b/src/checker.hpp index a4987dd0b..d80e012d5 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -218,15 +218,16 @@ struct DeclInfo { Array labels; }; -// ProcedureInfo stores the information needed for checking a procedure -struct ProcedureInfo { - AstFile * file; - Token token; - DeclInfo * decl; - Type * type; // Type_Procedure - AstNode * body; // AstNode_BlockStmt - u64 tags; - bool generated_from_polymorphic; +// ProcInfo stores the information needed for checking a procedure +struct ProcInfo { + AstFile * file; + Token token; + DeclInfo *decl; + Type * type; // Type_Procedure + AstNode * body; // AstNode_BlockStmt + u64 tags; + bool generated_from_polymorphic; + AstNode * poly_def_node; }; @@ -296,6 +297,7 @@ struct ForeignContext { }; typedef Array CheckerTypePath; +typedef Array CheckerPolyPath; // CheckerInfo stores all the symbol information for a type-checked program struct CheckerInfo { @@ -342,6 +344,8 @@ struct CheckerContext { CheckerTypePath *type_path; isize type_level; // TODO(bill): Actually handle correctly + CheckerPolyPath *poly_path; + isize poly_level; // TODO(bill): Actually handle correctly bool in_enum_type; bool collect_delayed_decls; @@ -355,7 +359,7 @@ struct Checker { Parser * parser; CheckerInfo info; - Array procs_to_check; + Array procs_to_check; PtrSet checked_packages; gbAllocator allocator; @@ -419,3 +423,9 @@ void destroy_checker_type_path(CheckerTypePath *tp); void check_type_path_push(CheckerContext *c, Entity *e); Entity *check_type_path_pop (CheckerContext *c); + +CheckerPolyPath *new_checker_poly_path(); +void destroy_checker_poly_path(CheckerPolyPath *); + +void check_poly_path_push(CheckerContext *c, Type *t); +Type *check_poly_path_pop (CheckerContext *c);