Browse Source

Generic procedures generate types on use

Ginger Bill 8 years ago
parent
commit
15dbea6899
6 changed files with 244 additions and 168 deletions
  1. 9 0
      code/demo.odin
  2. 165 134
      src/check_expr.cpp
  3. 3 3
      src/checker.cpp
  4. 27 7
      src/ir_print.cpp
  5. 11 2
      src/parser.cpp
  6. 29 22
      src/types.cpp

+ 9 - 0
code/demo.odin

@@ -2,7 +2,15 @@ import (
 	"fmt.odin";
 )
 
+proc new_type(T: type) -> ^T {
+	return ^T(alloc_align(size_of(T), align_of(T)));
+}
+
 proc main() {
+	var ptr = new_type(int);
+}
+
+/*
 	let program = "+ + * - /";
 	var accumulator = 0;
 
@@ -18,4 +26,5 @@ proc main() {
 
 	fmt.printf("The program \"%s\" calculates the value %d\n",
 	           program, accumulator);
+*/
 }

+ 165 - 134
src/check_expr.cpp

@@ -1030,7 +1030,7 @@ void check_bit_field_type(Checker *c, Type *bit_field_type, Type *named_type, As
 
 
 
-Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_variadic_) {
+Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_variadic_, Array<Operand> *operands) {
 	if (_params == NULL) {
 		return NULL;
 	}
@@ -1041,6 +1041,11 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
 		return NULL;
 	}
 
+	if (operands != NULL) {
+		GB_ASSERT(operands->count == params.count);
+	}
+
+
 	isize variable_count = 0;
 	for_array(i, params) {
 		AstNode *field = params[i];
@@ -1096,32 +1101,45 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
 				}
 			}
 			if (type_expr->kind == AstNode_HelperType) {
-				type = make_type_generic(c->allocator, 0);
+				if (operands != NULL) {
+					Operand o = (*operands)[i];
+					if (o.mode == Addressing_Type) {
+						type = o.type;
+					} else {
+						error(o.expr, "Expected a type to assign to a type parameter");
+					}
+				} else {
+					type = make_type_generic(c->allocator, 0);
+				}
 			} else {
 				type = check_type(c, type_expr);
 			}
 
 			if (default_value != NULL) {
-				Operand o = {};
-				if (default_value->kind == AstNode_BasicDirective &&
-				    default_value->BasicDirective.name == "caller_location") {
-					init_preload(c);
-					default_is_location = true;
-					o.type = t_source_code_location;
-					o.mode = Addressing_Value;
+				if (type_expr->kind == AstNode_HelperType) {
+					error(default_value, "A type parameter may not have a default value");
 				} else {
-					check_expr_with_type_hint(c, &o, default_value, type);
-
-					if (is_operand_nil(o)) {
-						default_is_nil = true;
-					} else if (o.mode != Addressing_Constant) {
-						error(default_value, "Default parameter must be a constant");
+					Operand o = {};
+					if (default_value->kind == AstNode_BasicDirective &&
+					    default_value->BasicDirective.name == "caller_location") {
+						init_preload(c);
+						default_is_location = true;
+						o.type = t_source_code_location;
+						o.mode = Addressing_Value;
 					} else {
-						value = o.value;
+						check_expr_with_type_hint(c, &o, default_value, type);
+
+						if (is_operand_nil(o)) {
+							default_is_nil = true;
+						} else if (o.mode != Addressing_Constant) {
+							error(default_value, "Default parameter must be a constant");
+						} else {
+							value = o.value;
+						}
 					}
-				}
 
-				check_is_assignable_to(c, &o, type);
+					check_is_assignable_to(c, &o, type);
+				}
 			}
 
 		}
@@ -1154,7 +1172,13 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
 			AstNode *name = p->names[j];
 			if (ast_node_expect(name, AstNode_Ident)) {
 				Entity *param = NULL;
-				if (type->kind == Type_Generic) {
+				bool is_generic = type->kind == Type_Generic;
+				if (operands != NULL) {
+					Operand o = (*operands)[j];
+					is_generic = o.mode == Addressing_Type && o.type == type;
+				}
+
+				if (is_generic) {
 					param = make_entity_type_name(c->allocator, scope, name->Ident, type);
 				} else {
 					param = make_entity_param(c->allocator, scope, name->Ident, type,
@@ -1497,11 +1521,12 @@ bool abi_compat_return_by_value(gbAllocator a, ProcCallingConvention cc, Type *a
 	return false;
 }
 
-void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) {
+// NOTE(bill): `operands` is for generating non generic procedure type
+void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node, Array<Operand> *operands = NULL) {
 	ast_node(pt, ProcType, proc_type_node);
 
 	bool variadic = false;
-	Type *params  = check_get_params(c, c->context.scope, pt->params, &variadic);
+	Type *params  = check_get_params(c, c->context.scope, pt->params, &variadic, operands);
 	Type *results = check_get_results(c, c->context.scope, pt->results);
 
 	isize param_count = 0;
@@ -1509,6 +1534,7 @@ void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) {
 	if (params)  param_count  = params ->Tuple.variable_count;
 	if (results) result_count = results->Tuple.variable_count;
 
+	type->Proc.node               = proc_type_node;
 	type->Proc.scope              = c->context.scope;
 	type->Proc.params             = params;
 	type->Proc.param_count        = param_count;
@@ -1541,14 +1567,19 @@ void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) {
 			is_generic = true;
 		}
 	}
-	GB_ASSERT(type->Proc.is_generic == is_generic);
+	if (operands == NULL) {
+		GB_ASSERT(type->Proc.is_generic == is_generic);
+	}
 
 
 	type->Proc.abi_compat_params = gb_alloc_array(c->allocator, Type *, param_count);
 	for (isize i = 0; i < param_count; i++) {
-		Type *original_type = type->Proc.params->Tuple.variables[i]->type;
-		Type *new_type = type_to_abi_compat_param_type(c->allocator, original_type);
-		type->Proc.abi_compat_params[i] = new_type;
+		Entity *e = type->Proc.params->Tuple.variables[i];
+		if (e->kind == Entity_Variable) {
+			Type *original_type = e->type;
+			Type *new_type = type_to_abi_compat_param_type(c->allocator, original_type);
+			type->Proc.abi_compat_params[i] = new_type;
+		}
 	}
 
 	// NOTE(bill): The types are the same
@@ -4829,6 +4860,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 
 enum CallArgumentError {
 	CallArgumentError_None,
+	CallArgumentError_NoneProcedureType,
 	CallArgumentError_WrongTypes,
 	CallArgumentError_NonVariadicExpand,
 	CallArgumentError_VariadicTuple,
@@ -4902,23 +4934,28 @@ 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_)
+#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) {
 	ast_node(ce, CallExpr, call);
+	GB_ASSERT(is_type_proc(proc_type));
+	proc_type = base_type(proc_type);
+	TypeProc *pt = &proc_type->Proc;
+
 	isize param_count = 0;
 	isize param_count_excluding_defaults = 0;
-	bool variadic = proc_type->Proc.variadic;
+	bool variadic = pt->variadic;
 	bool vari_expand = (ce->ellipsis.pos.line != 0);
 	i64 score = 0;
 	bool show_error = show_error_mode == CallArgumentMode_ShowErrors;
 
+
 	TypeTuple *param_tuple = NULL;
 
-	if (proc_type->Proc.params != NULL) {
-		param_tuple = &proc_type->Proc.params->Tuple;
+	if (pt->params != NULL) {
+		param_tuple = &pt->params->Tuple;
 
 		param_count = param_tuple->variable_count;
 		if (variadic) {
@@ -4945,120 +4982,126 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 		}
 	}
 
+	CallArgumentError err = CallArgumentError_None;
+	Type *final_proc_type = proc_type;
+
 	if (vari_expand && !variadic) {
 		if (show_error) {
 			error(ce->ellipsis,
 			      "Cannot use `..` in call to a non-variadic procedure: `%.*s`",
 			      LIT(ce->proc->Ident.string));
 		}
-		if (score_) *score_ = score;
-		return CallArgumentError_NonVariadicExpand;
-	}
-	if (vari_expand && proc_type->Proc.c_vararg) {
+		err = CallArgumentError_NonVariadicExpand;
+	} else if (vari_expand && pt->c_vararg) {
 		if (show_error) {
 			error(ce->ellipsis,
 			      "Cannot use `..` in call to a `#c_vararg` variadic procedure: `%.*s`",
 			      LIT(ce->proc->Ident.string));
 		}
-		if (score_) *score_ = score;
-		return CallArgumentError_NonVariadicExpand;
-	}
-
-	if (operands.count == 0 && param_count_excluding_defaults == 0) {
-		if (score_) *score_ = score;
-		return CallArgumentError_None;
-	}
-
-	i32 error_code = 0;
-	if (operands.count < param_count_excluding_defaults) {
-		error_code = -1;
-	} else if (!variadic && operands.count > param_count) {
-		error_code = +1;
-	}
-	if (error_code != 0) {
-		CallArgumentError err = CallArgumentError_TooManyArguments;
-		char *err_fmt = "Too many arguments for `%s`, expected %td arguments";
-		if (error_code < 0) {
-			err = CallArgumentError_TooFewArguments;
-			err_fmt = "Too few arguments for `%s`, expected %td arguments";
-		}
-
-		if (show_error) {
-			gbString proc_str = expr_to_string(ce->proc);
-			error(call, err_fmt, proc_str, param_count_excluding_defaults);
-			gb_string_free(proc_str);
+		err = CallArgumentError_NonVariadicExpand;
+	} else if (operands.count == 0 && param_count_excluding_defaults == 0) {
+		err = CallArgumentError_None;
+	} else {
+		i32 error_code = 0;
+		if (operands.count < param_count_excluding_defaults) {
+			error_code = -1;
+		} else if (!variadic && operands.count > param_count) {
+			error_code = +1;
 		}
-		if (score_) *score_ = score;
-		return err;
-	}
-
-	CallArgumentError err = CallArgumentError_None;
-
-
-	GB_ASSERT(proc_type->Proc.params != NULL);
-	Entity **sig_params = param_tuple->variables;
-	isize operand_index = 0;
-	isize max_operand_count = gb_min(param_count, operands.count);
-	for (; operand_index < max_operand_count; operand_index++) {
-		Entity *e = sig_params[operand_index];
-		Type *t = e->type;
-		Operand o = operands[operand_index];
-		if (e->kind == Entity_TypeName) {
-			GB_ASSERT(proc_type->Proc.is_generic);
-			GB_ASSERT(!variadic);
-			if (o.mode == Addressing_Invalid) {
-				continue;
-			} else if (o.mode != Addressing_Type) {
-				error(o.expr, "Expected a type for the argument");
+		if (error_code != 0) {
+			err = CallArgumentError_TooManyArguments;
+			char *err_fmt = "Too many arguments for `%s`, expected %td arguments";
+			if (error_code < 0) {
+				err = CallArgumentError_TooFewArguments;
+				err_fmt = "Too few arguments for `%s`, expected %td arguments";
 			}
 
-			score += assign_score_function(1);
-			continue;
-		}
-		if (variadic) {
-			o = operands[operand_index];
-		}
-		i64 s = 0;
-		if (!check_is_assignable_to_with_score(c, &o, t, &s)) {
 			if (show_error) {
-				check_assignment(c, &o, t, str_lit("argument"));
+				gbString proc_str = expr_to_string(ce->proc);
+				error(call, err_fmt, proc_str, param_count_excluding_defaults);
+				gb_string_free(proc_str);
 			}
-			err = CallArgumentError_WrongTypes;
-		}
-		score += s;
-	}
+		} else {
+			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;
+
+				final_proc_type = alloc_type(c->allocator, Type_Proc);
+				check_procedure_type(c, final_proc_type, pt->node, &operands);
+			}
+
+
+			TypeProc *pt = &final_proc_type->Proc;
+
+			GB_ASSERT(pt->params != NULL);
+			Entity **sig_params = pt->params->Tuple.variables;
+			isize operand_index = 0;
+			isize max_operand_count = gb_min(param_count, operands.count);
+			for (; operand_index < max_operand_count; operand_index++) {
+				Entity *e = sig_params[operand_index];
+				Type *t = e->type;
+				Operand o = operands[operand_index];
+				if (e->kind == Entity_TypeName) {
+					// GB_ASSERT(!variadic);
+					if (o.mode == Addressing_Invalid) {
+						continue;
+					} else if (o.mode != Addressing_Type) {
+						error(o.expr, "Expected a type for the argument");
+					}
 
-	if (variadic) {
-		bool variadic_expand = false;
-		Type *slice = sig_params[param_count]->type;
-		GB_ASSERT(is_type_slice(slice));
-		Type *elem = base_type(slice)->Slice.elem;
-		Type *t = elem;
-		for (; operand_index < operands.count; operand_index++) {
-			Operand o = operands[operand_index];
-			if (vari_expand) {
-				variadic_expand = true;
-				t = slice;
-				if (operand_index != param_count) {
+					score += assign_score_function(1);
+					continue;
+				}
+				if (variadic) {
+					o = operands[operand_index];
+				}
+				i64 s = 0;
+				if (!check_is_assignable_to_with_score(c, &o, t, &s)) {
 					if (show_error) {
-						error(o.expr, "`..` in a variadic procedure can only have one variadic argument at the end");
+						check_assignment(c, &o, t, str_lit("argument"));
 					}
-					if (score_) *score_ = score;
-					return CallArgumentError_MultipleVariadicExpand;
+					err = CallArgumentError_WrongTypes;
 				}
-			}
-			i64 s = 0;
-			if (!check_is_assignable_to_with_score(c, &o, t, &s)) {
-				if (show_error) {
-					check_assignment(c, &o, t, str_lit("argument"));
+				score += s;
+			}
+
+			if (variadic) {
+				bool variadic_expand = false;
+				Type *slice = sig_params[param_count]->type;
+				GB_ASSERT(is_type_slice(slice));
+				Type *elem = base_type(slice)->Slice.elem;
+				Type *t = elem;
+				for (; operand_index < operands.count; operand_index++) {
+					Operand o = operands[operand_index];
+					if (vari_expand) {
+						variadic_expand = true;
+						t = slice;
+						if (operand_index != param_count) {
+							if (show_error) {
+								error(o.expr, "`..` in a variadic procedure can only have one variadic argument at the end");
+							}
+							if (score_) *score_ = score;
+							return CallArgumentError_MultipleVariadicExpand;
+						}
+					}
+					i64 s = 0;
+					if (!check_is_assignable_to_with_score(c, &o, t, &s)) {
+						if (show_error) {
+							check_assignment(c, &o, t, str_lit("argument"));
+						}
+						err = CallArgumentError_WrongTypes;
+					}
+					score += s;
 				}
-				err = CallArgumentError_WrongTypes;
 			}
-			score += s;
 		}
 	}
 
 	if (score_) *score_ = score;
+	if (result_type_) *result_type_ = final_proc_type->Proc.results;
+
 	return err;
 }
 
@@ -5208,6 +5251,7 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
 	}
 
 	if (score_) *score_ = score;
+	if (result_type_) *result_type_ = pt->results;
 
 	return err;
 }
@@ -5267,7 +5311,7 @@ Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNod
 			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);
+				CallArgumentError err = call_checker(c, call, pt, operands, CallArgumentMode_NoErrors, &score, &result_type);
 				if (err == CallArgumentError_None) {
 					valids[valid_count].index = i;
 					valids[valid_count].score = score;
@@ -5312,18 +5356,11 @@ Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNod
 			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);
-
-			if (proc_type != NULL && is_type_proc(proc_type)) {
-				result_type = base_type(proc_type)->Proc.results;
-			}
+			CallArgumentError err = call_checker(c, call, proc_type, operands, CallArgumentMode_ShowErrors, &score, &result_type);
 		}
 	} else {
 		i64 score = 0;
-		CallArgumentError err = call_checker(c, call, proc_type, operands, CallArgumentMode_ShowErrors, &score);
-		if (proc_type != NULL && is_type_proc(proc_type)) {
-			result_type = base_type(proc_type)->Proc.results;
-		}
+		CallArgumentError err = call_checker(c, call, proc_type, operands, CallArgumentMode_ShowErrors, &score, &result_type);
 	}
 
 	return result_type;
@@ -5473,13 +5510,7 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) {
 	}
 
 	Type *pt = base_type(proc_type);
-	bool results_are_generic = false;
-	if (is_type_proc(pt) && pt->Proc.results != NULL) {
-		results_are_generic = is_type_generic(pt->Proc.results);
-	}
-	if (results_are_generic) {
-		operand->mode = Addressing_NoValue;
-	} else if (result_type == NULL) {
+	if (result_type == NULL) {
 		operand->mode = Addressing_NoValue;
 	} else {
 		GB_ASSERT(is_type_tuple(result_type));

+ 3 - 3
src/checker.cpp

@@ -221,6 +221,7 @@ ExprInfo make_expr_info(bool is_lhs, AddressingMode mode, Type *type, ExactValue
 
 
 struct Scope {
+	AstNode *        node;
 	Scope *          parent;
 	Scope *          prev, *next;
 	Scope *          first_child;
@@ -241,8 +242,6 @@ gb_global Scope *universal_scope = NULL;
 
 
 
-
-
 struct DelayedDecl {
 	Scope *  parent;
 	AstNode *decl;
@@ -278,6 +277,7 @@ 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;
 };
@@ -423,12 +423,12 @@ void destroy_scope(Scope *scope) {
 void add_scope(Checker *c, AstNode *node, Scope *scope) {
 	GB_ASSERT(node != NULL);
 	GB_ASSERT(scope != NULL);
+	scope->node = node;
 	map_set(&c->info.scopes, hash_node(node), scope);
 }
 
 
 void check_open_scope(Checker *c, AstNode *node) {
-	GB_ASSERT(node != NULL);
 	node = unparen_expr(node);
 	GB_ASSERT(node->kind == AstNode_Invalid ||
 	          is_ast_node_stmt(node) ||

+ 27 - 7
src/ir_print.cpp

@@ -189,18 +189,26 @@ void ir_print_proc_type_without_pointer(irFileBuffer *f, irModule *m, Type *t) {
 			ir_fprintf(f, ", ");
 		}
 	}
+	isize param_index = 0;
 	for (isize i = 0; i < param_count; i++) {
-		if (i > 0) {
+		if (param_index > 0) {
 			ir_fprintf(f, ", ");
 		}
+		Entity *e = t->Proc.params->Tuple.variables[i];
+		if (e->kind != Entity_Variable) {
+			continue;
+		}
+
 		if (i+1 == param_count && t->Proc.c_vararg) {
 			ir_fprintf(f, "...");
 		} else {
 			ir_print_type(f, m, t->Proc.abi_compat_params[i]);
 		}
+
+		param_index++;
 	}
 	if (t->Proc.calling_convention == ProcCC_Odin) {
-		if (param_count > 0) {
+		if (param_index > 0) {
 			ir_fprintf(f, ", ");
 		}
 		ir_print_type(f, m, t_context_ptr);
@@ -354,11 +362,16 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
 			ir_print_type(f, m, t->Tuple.variables[0]->type);
 		} else {
 			ir_fprintf(f, "{");
+			isize index = 0;
 			for (isize i = 0; i < t->Tuple.variable_count; i++) {
-				if (i > 0) {
+				if (index > 0) {
 					ir_fprintf(f, ", ");
 				}
-				ir_print_type(f, m, t->Tuple.variables[i]->type);
+				Entity *e = t->Tuple.variables[i];
+				if (e->kind == Entity_Variable) {
+					ir_print_type(f, m, e->type);
+					index++;
+				}
 			}
 			ir_fprintf(f, "}");
 		}
@@ -1551,17 +1564,22 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
 		}
 	}
 
+	isize param_index = 0;
 	if (param_count > 0) {
 		TypeTuple *params = &proc_type->params->Tuple;
 		for (isize i = 0; i < param_count; i++) {
 			Entity *e = params->variables[i];
 			Type *original_type = e->type;
 			Type *abi_type = proc_type->abi_compat_params[i];
-			if (i > 0) {
+			if (param_index > 0) {
 				ir_fprintf(f, ", ");
 			}
+			if (e->kind != Entity_Variable) {
+				continue;
+			}
+
 			if (i+1 == params->variable_count && proc_type->c_vararg) {
-					ir_fprintf(f, " ...");
+				ir_fprintf(f, " ...");
 			} else {
 				ir_print_type(f, m, abi_type);
 				if (e->flags&EntityFlag_NoAlias) {
@@ -1577,10 +1595,12 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
 					}
 				}
 			}
+
+			param_index++;
 		}
 	}
 	if (proc_type->calling_convention == ProcCC_Odin) {
-		if (param_count > 0) {
+		if (param_index > 0) {
 			ir_fprintf(f, ", ");
 		}
 		ir_print_type(f, m, t_context_ptr);

+ 11 - 2
src/parser.cpp

@@ -34,6 +34,7 @@ struct AstFile {
 	// NOTE(bill): Used to prevent type literals in control clauses
 	isize          expr_level;
 	bool           allow_range; // NOTE(bill): Ranges are only allowed in certain cases
+	bool           allow_gen_proc_type;
 	bool           in_foreign_block;
 
 	Array<AstNode *> decls;
@@ -2729,9 +2730,13 @@ AstNode *parse_proc_decl(AstFile *f) {
 	String link_name = {};
 
 
+	bool prev_allow_gen_proc_type = f->allow_gen_proc_type;
+	f->allow_gen_proc_type = true;
 	AstNode *name = parse_ident(f);
 	AstNode *type = parse_proc_type(f, token, &link_name);
 	u64 tags = type->ProcType.tags;
+	f->allow_gen_proc_type = prev_allow_gen_proc_type;
+
 
 	if (f->curr_token.kind == Token_OpenBrace) {
 		if ((tags & ProcTag_foreign) != 0) {
@@ -3404,12 +3409,13 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
 
 	isize total_name_count = 0;
 	bool allow_ellipsis = allowed_flags&FieldFlag_ellipsis;
+	bool allow_type_token = f->allow_gen_proc_type && allow_default_parameters;
 
 	while (f->curr_token.kind != follow &&
 	       f->curr_token.kind != Token_Colon &&
 	       f->curr_token.kind != Token_EOF) {
 		u32 flags = parse_field_prefixes(f);
-		AstNode *param = parse_var_type(f, allow_ellipsis, allow_default_parameters);
+		AstNode *param = parse_var_type(f, allow_ellipsis, allow_type_token);
 		AstNodeAndFlags naf = {param, flags};
 		array_add(&list, naf);
 		if (f->curr_token.kind != Token_Comma) {
@@ -3436,7 +3442,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
 
 		if (f->curr_token.kind != Token_Eq) {
 			expect_token_after(f, Token_Colon, "field list");
-			type = parse_var_type(f, allow_ellipsis, allow_default_parameters);
+			type = parse_var_type(f, allow_ellipsis, allow_type_token);
 		}
 		if (allow_token(f, Token_Eq)) {
 			// TODO(bill): Should this be true==lhs or false==rhs?
@@ -3788,7 +3794,10 @@ AstNode *parse_type_or_ident(AstFile *f) {
 
 	case Token_proc: {
 		Token token = f->curr_token; next_token(f);
+		bool prev_allow_gen_proc_type = f->allow_gen_proc_type;
+		f->allow_gen_proc_type = false;
 		AstNode *pt = parse_proc_type(f, token, NULL);
+		f->allow_gen_proc_type = prev_allow_gen_proc_type;
 		if (pt->ProcType.tags != 0) {
 			syntax_error(token, "A procedure type cannot have tags");
 		}

+ 29 - 22
src/types.cpp

@@ -1,4 +1,5 @@
 struct Scope;
+struct AstNode;
 
 enum BasicKind {
 	Basic_Invalid,
@@ -135,18 +136,19 @@ struct TypeRecord {
 		i64 *    offsets;                                 \
 	})                                                    \
 	TYPE_KIND(Proc, struct {                              \
-		Scope *scope;                                     \
-		Type * params;  /* Type_Tuple */                  \
-		Type * results; /* Type_Tuple */                  \
-		i32    param_count;                               \
-		i32    result_count;                              \
-		bool   return_by_pointer;                         \
-		Type **abi_compat_params;                         \
-		Type * abi_compat_result_type;                    \
-		bool   variadic;                                  \
-		bool   require_results;                           \
-		bool   c_vararg;                                  \
-		bool   is_generic;                                \
+		AstNode *node;                                    \
+		Scope *  scope;                                   \
+		Type *   params;  /* Type_Tuple */                \
+		Type *   results; /* Type_Tuple */                \
+		i32      param_count;                             \
+		i32      result_count;                            \
+		bool     return_by_pointer;                       \
+		Type **  abi_compat_params;                       \
+		Type *   abi_compat_result_type;                  \
+		bool     variadic;                                \
+		bool     require_results;                         \
+		bool     c_vararg;                                \
+		bool     is_generic;                              \
 		ProcCallingConvention calling_convention;         \
 	})                                                    \
 	TYPE_KIND(Map, struct {                               \
@@ -1164,7 +1166,7 @@ bool are_types_identical(Type *x, Type *y) {
 				for (isize i = 0; i < x->Tuple.variable_count; i++) {
 					Entity *xe = x->Tuple.variables[i];
 					Entity *ye = y->Tuple.variables[i];
-					if (!are_types_identical(xe->type, ye->type)) {
+					if (xe->kind != ye->kind || !are_types_identical(xe->type, ye->type)) {
 						return false;
 					}
 				}
@@ -2383,19 +2385,24 @@ gbString write_type_to_string(gbString str, Type *type) {
 			for (isize i = 0; i < type->Tuple.variable_count; i++) {
 				Entity *var = type->Tuple.variables[i];
 				if (var != NULL) {
-					GB_ASSERT(var->kind == Entity_Variable);
 					if (i > 0) {
 						str = gb_string_appendc(str, ", ");
 					}
-					if (var->flags&EntityFlag_CVarArg) {
-						str = gb_string_appendc(str, "#c_vararg ");
-					}
-					if (var->flags&EntityFlag_Ellipsis) {
-						Type *slice = base_type(var->type);
-						str = gb_string_appendc(str, "..");
-						GB_ASSERT(is_type_slice(var->type));
-						str = write_type_to_string(str, slice->Slice.elem);
+					if (var->kind == Entity_Variable) {
+						if (var->flags&EntityFlag_CVarArg) {
+							str = gb_string_appendc(str, "#c_vararg ");
+						}
+						if (var->flags&EntityFlag_Ellipsis) {
+							Type *slice = base_type(var->type);
+							str = gb_string_appendc(str, "..");
+							GB_ASSERT(is_type_slice(var->type));
+							str = write_type_to_string(str, slice->Slice.elem);
+						} else {
+							str = write_type_to_string(str, var->type);
+						}
 					} else {
+						GB_ASSERT(var->kind == Entity_TypeName);
+						str = gb_string_appendc(str, "type/");
 						str = write_type_to_string(str, var->type);
 					}
 				}