Browse Source

Fix cyclic polymorphic procedure usage and improve its error message

gingerBill 7 years ago
parent
commit
cd7e260f4e
4 changed files with 91 additions and 29 deletions
  1. 26 12
      src/check_expr.cpp
  2. 6 1
      src/check_type.cpp
  3. 39 6
      src/checker.cpp
  4. 20 10
      src/checker.hpp

+ 26 - 12
src/check_expr.cpp

@@ -28,7 +28,7 @@ struct CallArgumentData {
 
 
 struct PolyProcData {
 struct PolyProcData {
 	Entity *      gen_entity;
 	Entity *      gen_entity;
-	ProcedureInfo proc_info;
+	ProcInfo proc_info;
 };
 };
 
 
 struct ValidIndexAndScore {
 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,
 bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_entity, Type *type,
-                                            Array<Operand> *param_operands, PolyProcData *poly_proc_data) {
+                                            Array<Operand> *param_operands, AstNode *poly_def_node, PolyProcData *poly_proc_data) {
 	///////////////////////////////////////////////////////////////////////////////
 	///////////////////////////////////////////////////////////////////////////////
 	//                                                                           //
 	//                                                                           //
 	// TODO CLEANUP(bill): This procedure is very messy and hacky. Clean this!!! //
 	// 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);
 	AstNode *proc_lit = clone_ast_node(a, old_decl->proc_lit);
 	ast_node(pl, ProcLit, proc_lit);
 	ast_node(pl, ProcLit, proc_lit);
 	// NOTE(bill): Associate the scope declared above withinth this procedure declaration's type
 	// 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_poly_specialized = true;
 	final_proc_type->Proc.is_polymorphic = 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;
 	u64 tags = base_entity->Procedure.tags;
 	AstNode *ident = clone_ast_node(a, base_entity->identifier);
 	AstNode *ident = clone_ast_node(a, base_entity->identifier);
 	Token token = ident->Ident.token;
 	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.file  = file;
 	proc_info.token = token;
 	proc_info.token = token;
 	proc_info.decl  = d;
 	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.body  = pl->body;
 	proc_info.tags  = tags;
 	proc_info.tags  = tags;
 	proc_info.generated_from_polymorphic = true;
 	proc_info.generated_from_polymorphic = true;
+	proc_info.poly_def_node = poly_def_node;
 
 
 	if (found_gen_procs) {
 	if (found_gen_procs) {
 		array_add(found_gen_procs, entity);
 		array_add(found_gen_procs, entity);
@@ -377,15 +390,15 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
 	return true;
 	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;
 	if (operand->expr == nullptr) return false;
 	Entity *base_entity = entity_of_ident(operand->expr);
 	Entity *base_entity = entity_of_ident(operand->expr);
 	if (base_entity == nullptr) return false;
 	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<Operand> *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<Operand> *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);
 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;
 			return 3;
 		}
 		}
 		PolyProcData poly_proc_data = {};
 		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);
 			add_entity_use(c, operand->expr, poly_proc_data.gen_entity);
 			return 4;
 			return 4;
 		}
 		}
@@ -4088,7 +4101,6 @@ void check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count,
 }
 }
 
 
 
 
-
 CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 	ast_node(ce, CallExpr, call);
 	ast_node(ce, CallExpr, call);
 	GB_ASSERT(is_type_proc(proc_type));
 	GB_ASSERT(is_type_proc(proc_type));
@@ -4195,7 +4207,7 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 			PolyProcData poly_proc_data = {};
 			PolyProcData poly_proc_data = {};
 
 
 			if (pt->is_polymorphic && !pt->is_poly_specialized) {
 			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;
 					gen_entity = poly_proc_data.gen_entity;
 					GB_ASSERT(is_type_proc(gen_entity->type));
 					GB_ASSERT(is_type_proc(gen_entity->type));
 					final_proc_type = 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) {
 CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
 	ast_node(ce, CallExpr, call);
 	ast_node(ce, CallExpr, call);
 	GB_ASSERT(is_type_proc(proc_type));
 	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;
 	i64 score = 0;
 	bool show_error = show_error_mode == CallArgumentMode_ShowErrors;
 	bool show_error = show_error_mode == CallArgumentMode_ShowErrors;
@@ -4419,10 +4432,11 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
 	Entity *gen_entity = nullptr;
 	Entity *gen_entity = nullptr;
 	if (pt->is_polymorphic && !pt->is_poly_specialized && err == CallArgumentError_None) {
 	if (pt->is_polymorphic && !pt->is_poly_specialized && err == CallArgumentError_None) {
 		PolyProcData poly_proc_data = {};
 		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;
 			gen_entity = poly_proc_data.gen_entity;
 			Type *gept = base_type(gen_entity->type);
 			Type *gept = base_type(gen_entity->type);
 			GB_ASSERT(is_type_proc(gept));
 			GB_ASSERT(is_type_proc(gept));
+			proc_type = gept;
 			pt = &gept->Proc;
 			pt = &gept->Proc;
 		}
 		}
 	}
 	}

+ 6 - 1
src/check_type.cpp

@@ -401,6 +401,11 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, AstNode *node, Ar
 				is_poly_specialized = false;
 				is_poly_specialized = false;
 				break;
 				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) {
 	if (et->is_export) {
 		Scope *parent = ctx->scope->parent;
 		Scope *parent = ctx->scope->parent;
 		if (parent->is_file) {
 		if (parent->is_file) {
-			// NOTE(bhall): Use package scope
+			// NOTE(bill): Use package scope
 			parent = parent->parent;
 			parent = parent->parent;
 		}
 		}
 		for_array(i, fields) {
 		for_array(i, fields) {

+ 39 - 6
src/checker.cpp

@@ -620,11 +620,14 @@ CheckerContext make_checker_context(Checker *c) {
 
 
 	ctx.type_path = new_checker_type_path();
 	ctx.type_path = new_checker_type_path();
 	ctx.type_level = 0;
 	ctx.type_level = 0;
+	ctx.poly_path = new_checker_poly_path();
+	ctx.poly_level = 0;
 	return ctx;
 	return ctx;
 }
 }
 
 
 void destroy_checker_context(CheckerContext *ctx) {
 void destroy_checker_context(CheckerContext *ctx) {
 	destroy_checker_type_path(ctx->type_path);
 	destroy_checker_type_path(ctx->type_path);
+	destroy_checker_poly_path(ctx->poly_path);
 }
 }
 
 
 void init_checker(Checker *c, Parser *parser) {
 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);
 	GB_ASSERT(info.decl != nullptr);
 	array_add(&c->procs_to_check, info);
 	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) {
 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.file  = file;
 	info.token = token;
 	info.token = token;
 	info.decl  = decl;
 	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);
 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) {
 	if (pi.type == nullptr) {
 		return;
 		return;
 	}
 	}
@@ -3107,8 +3135,13 @@ void check_proc_info(Checker *c, ProcedureInfo pi) {
 
 
 	TypeProc *pt = &pi.type->Proc;
 	TypeProc *pt = &pi.type->Proc;
 	String name = pi.token.string;
 	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;
 	bool bounds_check    = (pi.tags & ProcTag_bounds_check)    != 0;
@@ -3203,7 +3236,7 @@ void check_parsed_files(Checker *c) {
 	TIME_SECTION("check procedure bodies");
 	TIME_SECTION("check procedure bodies");
 	// NOTE(bill): Nested procedures bodies will be added to this "queue"
 	// NOTE(bill): Nested procedures bodies will be added to this "queue"
 	for_array(i, c->procs_to_check) {
 	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);
 		check_proc_info(c, pi);
 	}
 	}
 
 

+ 20 - 10
src/checker.hpp

@@ -218,15 +218,16 @@ struct DeclInfo {
 	Array<BlockLabel> labels;
 	Array<BlockLabel> 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<Entity *> CheckerTypePath;
 typedef Array<Entity *> CheckerTypePath;
+typedef Array<Type *>   CheckerPolyPath;
 
 
 // CheckerInfo stores all the symbol information for a type-checked program
 // CheckerInfo stores all the symbol information for a type-checked program
 struct CheckerInfo {
 struct CheckerInfo {
@@ -342,6 +344,8 @@ struct CheckerContext {
 
 
 	CheckerTypePath *type_path;
 	CheckerTypePath *type_path;
 	isize            type_level; // TODO(bill): Actually handle correctly
 	isize            type_level; // TODO(bill): Actually handle correctly
+	CheckerPolyPath *poly_path;
+	isize            poly_level; // TODO(bill): Actually handle correctly
 
 
 	bool       in_enum_type;
 	bool       in_enum_type;
 	bool       collect_delayed_decls;
 	bool       collect_delayed_decls;
@@ -355,7 +359,7 @@ struct Checker {
 	Parser *    parser;
 	Parser *    parser;
 	CheckerInfo info;
 	CheckerInfo info;
 
 
-	Array<ProcedureInfo> procs_to_check;
+	Array<ProcInfo> procs_to_check;
 	PtrSet<AstPackage *> checked_packages;
 	PtrSet<AstPackage *> checked_packages;
 
 
 	gbAllocator    allocator;
 	gbAllocator    allocator;
@@ -419,3 +423,9 @@ void destroy_checker_type_path(CheckerTypePath *tp);
 
 
 void    check_type_path_push(CheckerContext *c, Entity *e);
 void    check_type_path_push(CheckerContext *c, Entity *e);
 Entity *check_type_path_pop (CheckerContext *c);
 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);