Răsfoiți Sursa

Rudimentary para-poly procedures

Ginger Bill 8 ani în urmă
părinte
comite
1ced92be47
8 a modificat fișierele cu 186 adăugiri și 77 ștergeri
  1. 4 1
      code/demo.odin
  2. 9 2
      src/check_decl.cpp
  3. 107 47
      src/check_expr.cpp
  4. 34 20
      src/checker.cpp
  5. 1 1
      src/gb/gb.h
  6. 17 2
      src/ir.cpp
  7. 10 4
      src/ir_print.cpp
  8. 4 0
      src/types.cpp

+ 4 - 1
code/demo.odin

@@ -3,11 +3,14 @@ import (
 )
 
 proc new_type(T: type) -> ^T {
-	return ^T(alloc_align(size_of(T), align_of(T)));
+	return ^T(alloc(size_of(T), align_of(T)));
 }
 
 proc main() {
 	var ptr = new_type(int);
+	ptr^ = 123;
+
+	fmt.println(ptr^);
 }
 
 /*

+ 9 - 2
src/check_decl.cpp

@@ -307,7 +307,12 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
 		return;
 	}
 
-	Type *proc_type = make_type_proc(c->allocator, e->scope, NULL, 0, NULL, 0, false, ProcCC_Odin);
+	Type *proc_type = e->type;
+	if (d->gen_proc_type != NULL) {
+		proc_type = d->gen_proc_type;
+	} else {
+		proc_type = make_type_proc(c->allocator, e->scope, NULL, 0, NULL, 0, false, ProcCC_Odin);
+	}
 	e->type = proc_type;
 	ast_node(pd, ProcDecl, d->proc_decl);
 
@@ -575,7 +580,9 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
 		TypeTuple *params = &type->Proc.params->Tuple;
 		for (isize i = 0; i < params->variable_count; i++) {
 			Entity *e = params->variables[i];
-			GB_ASSERT(e->kind == Entity_Variable);
+			if (e->kind != Entity_Variable) {
+				continue;
+			}
 			if (!(e->flags & EntityFlag_Using)) {
 				continue;
 			}

+ 107 - 47
src/check_expr.cpp

@@ -1,3 +1,37 @@
+enum CallArgumentError {
+	CallArgumentError_None,
+	CallArgumentError_NoneProcedureType,
+	CallArgumentError_WrongTypes,
+	CallArgumentError_NonVariadicExpand,
+	CallArgumentError_VariadicTuple,
+	CallArgumentError_MultipleVariadicExpand,
+	CallArgumentError_ArgumentCount,
+	CallArgumentError_TooFewArguments,
+	CallArgumentError_TooManyArguments,
+	CallArgumentError_InvalidFieldValue,
+	CallArgumentError_ParameterNotFound,
+	CallArgumentError_ParameterMissing,
+	CallArgumentError_DuplicateParameter,
+	CallArgumentError_GenericProcedureNotSupported,
+};
+
+enum CallArgumentErrorMode {
+	CallArgumentMode_NoErrors,
+	CallArgumentMode_ShowErrors,
+};
+
+struct CallArgumentData {
+	Entity *gen_entity;
+	i64     score;
+	Type *  result_type;
+};
+
+
+#define CALL_ARGUMENT_CHECKER(name) CallArgumentError name(Checker *c, AstNode *call, Type *proc_type, Entity *entity, Array<Operand> operands, CallArgumentErrorMode show_error_mode, CallArgumentData *data)
+typedef CALL_ARGUMENT_CHECKER(CallArgumentCheckerType);
+
+
+
 void     check_expr                     (Checker *c, Operand *operand, AstNode *expression);
 void     check_multi_expr               (Checker *c, Operand *operand, AstNode *expression);
 void     check_expr_or_type             (Checker *c, Operand *operand, AstNode *expression);
@@ -19,7 +53,7 @@ void     check_stmt                     (Checker *c, AstNode *node, u32 flags);
 void     check_stmt_list                (Checker *c, Array<AstNode *> stmts, u32 flags);
 void     check_init_constant            (Checker *c, Entity *e, Operand *operand);
 bool     check_representable_as_constant(Checker *c, ExactValue in_value, Type *type, ExactValue *out_value);
-Type *   check_call_arguments           (Checker *c, Operand *operand, Type *proc_type, AstNode *call);
+CallArgumentData check_call_arguments   (Checker *c, Operand *operand, Type *proc_type, AstNode *call);
 
 
 void error_operand_not_expression(Operand *o) {
@@ -1173,13 +1207,16 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
 			if (ast_node_expect(name, AstNode_Ident)) {
 				Entity *param = NULL;
 				bool is_generic = type->kind == Type_Generic;
+				Type *gen_type = type;
 				if (operands != NULL) {
 					Operand o = (*operands)[j];
 					is_generic = o.mode == Addressing_Type && o.type == type;
+					if (is_generic) gen_type = o.type;
 				}
 
 				if (is_generic) {
-					param = make_entity_type_name(c->allocator, scope, name->Ident, type);
+					param = make_entity_type_name(c->allocator, scope, name->Ident, gen_type);
+					param->TypeName.is_type_alias = true;
 				} else {
 					param = make_entity_param(c->allocator, scope, name->Ident, type,
 					                          (p->flags&FieldFlag_using) != 0, false);
@@ -4858,28 +4895,6 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 	return true;
 }
 
-enum CallArgumentError {
-	CallArgumentError_None,
-	CallArgumentError_NoneProcedureType,
-	CallArgumentError_WrongTypes,
-	CallArgumentError_NonVariadicExpand,
-	CallArgumentError_VariadicTuple,
-	CallArgumentError_MultipleVariadicExpand,
-	CallArgumentError_ArgumentCount,
-	CallArgumentError_TooFewArguments,
-	CallArgumentError_TooManyArguments,
-	CallArgumentError_InvalidFieldValue,
-	CallArgumentError_ParameterNotFound,
-	CallArgumentError_ParameterMissing,
-	CallArgumentError_DuplicateParameter,
-	CallArgumentError_GenericProcedureNotSupported,
-};
-
-enum CallArgumentErrorMode {
-	CallArgumentMode_NoErrors,
-	CallArgumentMode_ShowErrors,
-};
-
 
 struct ValidProcAndScore {
 	isize index;
@@ -4934,8 +4949,7 @@ bool check_unpack_arguments(Checker *c, isize lhs_count, Array<Operand> *operand
 	return optional_ok;
 }
 
-#define CALL_ARGUMENT_CHECKER(name) CallArgumentError name(Checker *c, AstNode *call, Type *proc_type, Array<Operand> operands, CallArgumentErrorMode show_error_mode, i64 *score_, Type **result_type_)
-typedef CALL_ARGUMENT_CHECKER(CallArgumentCheckerType);
+
 
 
 CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
@@ -4984,6 +4998,7 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 
 	CallArgumentError err = CallArgumentError_None;
 	Type *final_proc_type = proc_type;
+	Entity *gen_entity = NULL;
 
 	if (vari_expand && !variadic) {
 		if (show_error) {
@@ -5022,14 +5037,44 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 				gb_string_free(proc_str);
 			}
 		} else {
+			// NOTE(bill): Generate the procedure type for this generic instance
+			// TODO(bill): Clean this shit up!
 			if (pt->is_generic) {
-				Scope *scope = make_scope(pt->scope->parent, c->allocator);
-				CheckerContext prev = c->context;
-				defer (c->context = prev);
-				c->context.scope = scope;
+				GB_ASSERT(entity != NULL);
+				DeclInfo *old_decl = decl_info_of_entity(&c->info, entity);
+				GB_ASSERT(old_decl != NULL);
+
+				gbAllocator a = heap_allocator();
+
+				Scope *scope = entity->scope;
+
+				AstNode *proc_decl = clone_ast_node(a, old_decl->proc_decl);
+				ast_node(pd, ProcDecl, proc_decl);
 
-				final_proc_type = alloc_type(c->allocator, Type_Proc);
+				check_open_scope(c, pd->type);
+				defer (check_close_scope(c));
+
+				final_proc_type = make_type_proc(c->allocator, c->context.scope, NULL, 0, NULL, 0, false, pt->calling_convention);
 				check_procedure_type(c, final_proc_type, pt->node, &operands);
+
+				u64 tags = entity->Procedure.tags;
+				AstNode *ident = clone_ast_node(a, entity->identifier);
+				Token token = ident->Ident;
+				DeclInfo *d = make_declaration_info(c->allocator, c->context.scope, old_decl->parent);
+				d->gen_proc_type = final_proc_type;
+				d->type_expr = pd->type;
+				d->proc_decl = proc_decl;
+
+				gen_entity = make_entity_procedure(c->allocator, entity->scope, token, final_proc_type, tags);
+				gen_entity->identifier = ident;
+
+				add_entity_and_decl_info(c, ident, gen_entity, d);
+				add_entity_definition(&c->info, ident, gen_entity);
+
+				add_entity_use(c, ident, gen_entity);
+				add_entity_use(c, ce->proc, gen_entity);
+
+				check_procedure_later(c, c->curr_ast_file, token, d, final_proc_type, pd->body, tags);
 			}
 
 
@@ -5082,7 +5127,11 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 							if (show_error) {
 								error(o.expr, "`..` in a variadic procedure can only have one variadic argument at the end");
 							}
-							if (score_) *score_ = score;
+							if (data) {
+								data->score = score;
+								data->result_type = final_proc_type->Proc.results;
+								data->gen_entity = gen_entity;
+							}
 							return CallArgumentError_MultipleVariadicExpand;
 						}
 					}
@@ -5099,8 +5148,11 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 		}
 	}
 
-	if (score_) *score_ = score;
-	if (result_type_) *result_type_ = final_proc_type->Proc.results;
+	if (data) {
+		data->score = score;
+		data->result_type = final_proc_type->Proc.results;
+		data->gen_entity = gen_entity;
+	}
 
 	return err;
 }
@@ -5250,14 +5302,17 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
 		err = CallArgumentError_GenericProcedureNotSupported;
 	}
 
-	if (score_) *score_ = score;
-	if (result_type_) *result_type_ = pt->results;
+	if (data) {
+		data->score = score;
+		data->result_type = pt->results;
+		data->gen_entity = NULL;
+	}
 
 	return err;
 }
 
 
-Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode *call) {
+CallArgumentData check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode *call) {
 	ast_node(ce, CallExpr, call);
 
 	CallArgumentCheckerType *call_checker = check_call_arguments_internal;
@@ -5310,11 +5365,11 @@ Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNod
 			Entity *p = procs[i];
 			Type *pt = base_type(p->type);
 			if (pt != NULL && is_type_proc(pt)) {
-				i64 score = 0;
-				CallArgumentError err = call_checker(c, call, pt, operands, CallArgumentMode_NoErrors, &score, &result_type);
+				CallArgumentData data = {};
+				CallArgumentError err = call_checker(c, call, pt, p, operands, CallArgumentMode_NoErrors, &data);
 				if (err == CallArgumentError_None) {
 					valids[valid_count].index = i;
-					valids[valid_count].score = score;
+					valids[valid_count].score = data.score;
 					valid_count++;
 				}
 			}
@@ -5355,15 +5410,20 @@ Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNod
 			Entity *e = procs[valids[0].index];
 			add_entity_use(c, expr, e);
 			proc_type = e->type;
-			i64 score = 0;
-			CallArgumentError err = call_checker(c, call, proc_type, operands, CallArgumentMode_ShowErrors, &score, &result_type);
+			CallArgumentData data = {};
+			CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data);
+			return data;
 		}
 	} else {
-		i64 score = 0;
-		CallArgumentError err = call_checker(c, call, proc_type, operands, CallArgumentMode_ShowErrors, &score, &result_type);
+		Entity *e = entity_of_ident(&c->info, operand->expr);
+		CallArgumentData data = {};
+		CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data);
+		return data;
 	}
 
-	return result_type;
+	CallArgumentData data = {};
+	data.result_type = t_invalid;
+	return data;
 }
 
 
@@ -5498,8 +5558,8 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) {
 		}
 	}
 
-	Type *result_type = check_call_arguments(c, operand, proc_type, call);
-
+	CallArgumentData data = check_call_arguments(c, operand, proc_type, call);
+	Type *result_type = data.result_type;
 	gb_zero_item(operand);
 	operand->expr = call;
 

+ 34 - 20
src/checker.cpp

@@ -188,6 +188,7 @@ struct DeclInfo {
 	AstNode *         type_expr;
 	AstNode *         init_expr;
 	AstNode *         proc_decl; // AstNode_ProcDecl
+	Type *            gen_proc_type; // Precalculated
 
 	Map<bool>         deps; // Key: Entity *
 	Array<BlockLabel> labels;
@@ -202,7 +203,7 @@ struct ProcedureInfo {
 	DeclInfo *            decl;
 	Type *                type; // Type_Procedure
 	AstNode *             body; // AstNode_BlockStmt
-	u32                   tags;
+	u64                   tags;
 };
 
 // ExprInfo stores information used for "untyped" expressions
@@ -266,6 +267,7 @@ struct CheckerContext {
 	AstNode *  curr_foreign_library;
 };
 
+
 // CheckerInfo stores all the symbol information for a type-checked program
 struct CheckerInfo {
 	Map<TypeAndValue>    types;           // Key: AstNode * | Expression -> Type (and value)
@@ -277,7 +279,6 @@ struct CheckerInfo {
 	Map<DeclInfo *>      entities;        // Key: Entity *
 	Map<Entity *>        foreigns;        // Key: String
 	Map<AstFile *>       files;           // Key: String (full path)
-
 	Map<isize>           type_info_map;   // Key: Type *
 	isize                type_info_count;
 };
@@ -286,28 +287,29 @@ struct Checker {
 	Parser *    parser;
 	CheckerInfo info;
 
-	AstFile *              curr_ast_file;
-	Scope *                global_scope;
+	AstFile *                  curr_ast_file;
+	Scope *                    global_scope;
 	// NOTE(bill): Procedures to check
-	Map<ProcedureInfo>     procs; // Key: DeclInfo *
-	Array<DelayedDecl>     delayed_imports;
-	Array<DelayedDecl>     delayed_foreign_libraries;
-	Array<CheckerFileNode> file_nodes;
+	Map<ProcedureInfo>         procs; // Key: DeclInfo *
+	Map<Array<ProcedureInfo> > gen_procs;
+	Array<DelayedDecl>         delayed_imports;
+	Array<DelayedDecl>         delayed_foreign_libraries;
+	Array<CheckerFileNode>     file_nodes;
 
-	gbArena                arena;
-	gbArena                tmp_arena;
-	gbAllocator            allocator;
-	gbAllocator            tmp_allocator;
+	gbArena                    arena;
+	gbArena                    tmp_arena;
+	gbAllocator                allocator;
+	gbAllocator                tmp_allocator;
 
-	CheckerContext         context;
+	CheckerContext             context;
 
-	Array<Type *>          proc_stack;
-	bool                   done_preload;
+	Array<Type *>              proc_stack;
+	bool                       done_preload;
 };
 
 
 
-HashKey hash_node     (AstNode *node)  { return hash_ptr_and_id(node, 0); }
+HashKey hash_node     (AstNode *node)  { return hash_pointer(node); }
 HashKey hash_ast_file (AstFile *file)  { return hash_pointer(file); }
 HashKey hash_entity   (Entity *e)      { return hash_pointer(e); }
 HashKey hash_type     (Type *t)        { return hash_pointer(t); }
@@ -741,6 +743,7 @@ void init_checker(Checker *c, Parser *parser) {
 
 	array_init(&c->proc_stack, a);
 	map_init(&c->procs, a);
+	map_init(&c->gen_procs, a);
 	array_init(&c->delayed_imports, a);
 	array_init(&c->delayed_foreign_libraries, a);
 	array_init(&c->file_nodes, a);
@@ -778,6 +781,7 @@ void destroy_checker(Checker *c) {
 	destroy_scope(c->global_scope);
 	array_free(&c->proc_stack);
 	map_destroy(&c->procs);
+	map_destroy(&c->gen_procs);
 	array_free(&c->delayed_imports);
 	array_free(&c->delayed_foreign_libraries);
 	array_free(&c->file_nodes);
@@ -946,7 +950,7 @@ void add_entity_definition(CheckerInfo *i, AstNode *identifier, Entity *entity)
 		HashKey key = hash_node(identifier);
 		map_set(&i->definitions, key, entity);
 	} else {
-		// NOTE(bill): Error should handled elsewhere
+		// NOTE(bill): Error should be handled elsewhere
 	}
 }
 
@@ -1161,7 +1165,7 @@ void add_type_info_type(Checker *c, Type *t) {
 }
 
 
-void check_procedure_later(Checker *c, AstFile *file, Token token, DeclInfo *decl, Type *type, AstNode *body, u32 tags) {
+void check_procedure_later(Checker *c, AstFile *file, Token token, DeclInfo *decl, Type *type, AstNode *body, u64 tags) {
 	ProcedureInfo info = {};
 	info.file = file;
 	info.token = token;
@@ -1204,6 +1208,14 @@ void add_dependency_to_map(Map<Entity *> *map, CheckerInfo *info, Entity *entity
 	if (entity == NULL) {
 		return;
 	}
+	if (entity->type != NULL &&
+	    is_type_gen_proc(entity->type)) {
+		DeclInfo *decl = decl_info_of_entity(info, entity);
+		if (decl->gen_proc_type == NULL) {
+			return;
+		}
+	}
+
 	if (map_get(map, hash_entity(entity)) != NULL) {
 		return;
 	}
@@ -2241,8 +2253,10 @@ void check_parsed_files(Checker *c) {
 
 		TypeProc *pt = &pi->type->Proc;
 		if (pt->is_generic) {
-			error(pi->token, "Generic procedures are not yet supported");
-			continue;
+			if (pi->decl->gen_proc_type == NULL) {
+				continue;
+			}
+			// gb_printf_err("Generic procedure `%.*s` -> %s\n", LIT(pi->token.string), type_to_string(pi->decl->gen_proc_type));
 		}
 
 		add_curr_ast_file(c, pi->file);

+ 1 - 1
src/gb/gb.h

@@ -3602,7 +3602,7 @@ extern "C" {
 #endif
 
 void gb_assert_handler(char const *condition, char const *file, i32 line, char const *msg, ...) {
-	gb_printf_err("%s:%d: Assert Failure: ", file, line);
+	gb_printf_err("%s(%d): Assert Failure: ", file, line);
 	if (condition)
 		gb_printf_err( "`%s` ", condition);
 	if (msg) {

+ 17 - 2
src/ir.cpp

@@ -1510,7 +1510,11 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, irValue **args, isize arg_
 		GB_ASSERT(param_count == arg_count);
 	}
 	for (isize i = 0; i < param_count; i++) {
-		Type *original_type = pt->Proc.params->Tuple.variables[i]->type;
+		Entity *e = pt->Proc.params->Tuple.variables[i];
+		if (e->kind != Entity_Variable) {
+			continue;
+		}
+		Type *original_type = e->type;
 		Type *new_type = pt->Proc.abi_compat_params[i];
 		if (original_type != new_type) {
 			if (is_type_pointer(new_type)) {
@@ -4360,6 +4364,11 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 	TypeAndValue tv = type_and_value_of_expr(proc->module->info, expr);
 	GB_ASSERT(tv.mode != Addressing_Invalid);
 
+	if (tv.mode == Addressing_Type) {
+		// TODO(bill): Handle this correctly
+		return ir_value_nil(proc->module->allocator, tv.type);
+	}
+
 	if (tv.value.kind != ExactValue_Invalid) {
 		// NOTE(bill): Edge case
 		if (tv.value.kind != ExactValue_Compound &&
@@ -4417,7 +4426,7 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 		} else if (e != NULL && e->kind == Entity_Variable) {
 			return ir_addr_load(proc, ir_build_addr(proc, expr));
 		}
-		GB_PANIC("NULL value for expression from identifier: %.*s", LIT(i->string));
+		GB_PANIC("NULL value for expression from identifier: %.*s @ %p", LIT(i->string), expr);
 		return NULL;
 	case_end;
 
@@ -6911,6 +6920,10 @@ void ir_begin_procedure_body(irProcedure *proc) {
 			AstNode *name = field->names[q_index++];
 
 			Entity *e = params->variables[i];
+			if (e->kind != Entity_Variable) {
+				continue;
+			}
+
 			Type *abi_type = proc->type->Proc.abi_compat_params[i];
 			if (e->token.string != "" &&
 			    e->token.string != "_") {
@@ -7385,6 +7398,8 @@ void ir_gen_tree(irGen *s) {
 			ast_node(pd, ProcDecl, decl->proc_decl);
 			String original_name = name;
 			AstNode *body = pd->body;
+
+
 			if (e->Procedure.is_foreign) {
 				name = e->token.string; // NOTE(bill): Don't use the mangled name
 				ir_add_foreign_library_path(m, e->Procedure.foreign_library);

+ 10 - 4
src/ir_print.cpp

@@ -1280,6 +1280,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
 		}
 
 
+		isize param_index = 0;
 		if (call->arg_count > 0) {
 			TypeTuple *params = &proc_type->Proc.params->Tuple;
 			if (proc_type->Proc.c_vararg) {
@@ -1287,8 +1288,9 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
 				for (; i < params->variable_count-1; i++) {
 					Entity *e = params->variables[i];
 					GB_ASSERT(e != NULL);
+					if (e->kind != Entity_Variable) continue;
 					Type *t = proc_type->Proc.abi_compat_params[i];
-					if (i > 0) {
+					if (param_index > 0) {
 						ir_fprintf(f, ", ");
 					}
 					ir_print_type(f, m, t);
@@ -1298,9 +1300,10 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
 					ir_fprintf(f, " ");
 					irValue *arg = call->args[i];
 					ir_print_value(f, m, arg, t);
+					param_index++;
 				}
 				for (; i < call->arg_count; i++) {
-					if (i > 0) {
+					if (param_index > 0) {
 						ir_fprintf(f, ", ");
 					}
 
@@ -1309,6 +1312,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
 					ir_print_type(f, m, t);
 					ir_fprintf(f, " ");
 					ir_print_value(f, m, arg, t);
+					param_index++;
 				}
 			} else {
 				GB_ASSERT(call->arg_count == params->variable_count);
@@ -1316,9 +1320,10 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
 				for (isize i = 0; i < param_count; i++) {
 					Entity *e = params->variables[i];
 					GB_ASSERT(e != NULL);
+					if (e->kind != Entity_Variable) continue;
 					irValue *arg = call->args[i];
 					Type *t = proc_type->Proc.abi_compat_params[i];
-					if (i > 0) {
+					if (param_index > 0) {
 						ir_fprintf(f, ", ");
 					}
 					ir_print_type(f, m, t);
@@ -1327,11 +1332,12 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
 					}
 					ir_fprintf(f, " ");
 					ir_print_value(f, m, arg, t);
+					param_index++;
 				}
 			}
 		}
 		if (proc_type->Proc.calling_convention == ProcCC_Odin) {
-			if (proc_type->Proc.param_count > 0) {
+			if (param_index > 0) {
 				ir_fprintf(f, ", ");
 			}
 			ir_print_type(f, m, t_context_ptr);

+ 4 - 0
src/types.cpp

@@ -2234,6 +2234,10 @@ gbString write_type_to_string(gbString str, Type *type) {
 		str = gb_string_append_length(str, type->Basic.name.text, type->Basic.name.len);
 		break;
 
+	case Type_Generic:
+		str = gb_string_appendc(str, "type");
+		break;
+
 	case Type_Pointer:
 		str = gb_string_appendc(str, "^");
 		str = write_type_to_string(str, type->Pointer.elem);