Browse Source

Fix line error printing for error messages

gingerBill 2 years ago
parent
commit
6568625dea
7 changed files with 765 additions and 83 deletions
  1. 3 1
      src/array.cpp
  2. 695 55
      src/check_expr.cpp
  3. 19 12
      src/error.cpp
  4. 33 6
      src/gb/gb.h
  5. 7 7
      src/llvm_backend_proc.cpp
  6. 2 2
      src/parser.hpp
  7. 6 0
      src/types.cpp

+ 3 - 1
src/array.cpp

@@ -80,7 +80,9 @@ gb_internal Slice<T> slice_make(gbAllocator const &allocator, isize count) {
 	GB_ASSERT(count >= 0);
 	Slice<T> s = {};
 	s.data = gb_alloc_array(allocator, T, count);
-	GB_ASSERT(s.data != nullptr);
+	if (count > 0) {
+		GB_ASSERT(s.data != nullptr);
+	}
 	s.count = count;
 	return s;
 }

+ 695 - 55
src/check_expr.cpp

@@ -66,7 +66,7 @@ gb_internal int valid_index_and_score_cmp(void const *a, void const *b) {
 
 
 
-#define CALL_ARGUMENT_CHECKER(name) CallArgumentError name(CheckerContext *c, Ast *call, Type *proc_type, Entity *entity, Array<Operand> operands, CallArgumentErrorMode show_error_mode, CallArgumentData *data)
+#define CALL_ARGUMENT_CHECKER(name) CallArgumentError name(CheckerContext *c, Ast *call, Type *proc_type, Entity *entity, Array<Operand> operands, Array<Operand> const &named_operands, CallArgumentErrorMode show_error_mode, CallArgumentData *data)
 typedef CALL_ARGUMENT_CHECKER(CallArgumentCheckerType);
 
 
@@ -5314,7 +5314,24 @@ gb_internal isize get_procedure_param_count_excluding_defaults(Type *pt, isize *
 }
 
 
+gb_internal isize lookup_procedure_parameter(TypeProc *pt, String parameter_name) {
+	isize param_count = pt->param_count;
+	for (isize i = 0; i < param_count; i++) {
+		Entity *e = pt->params->Tuple.variables[i];
+		String name = e->token.string;
+		if (is_blank_ident(name)) {
+			continue;
+		}
+		if (name == parameter_name) {
+			return i;
+		}
+	}
+	return -1;
+}
+
 gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
+	CallArgumentError err = CallArgumentError_None;
+
 	ast_node(ce, CallExpr, call);
 	GB_ASSERT(is_type_proc(proc_type));
 	proc_type = base_type(proc_type);
@@ -5327,6 +5344,52 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 	i64 score = 0;
 	bool show_error = show_error_mode == CallArgumentMode_ShowErrors;
 
+	auto ordered_operands = array_make<Operand>(temporary_allocator(), pt->param_count);
+	bool *visited = gb_alloc_array(temporary_allocator(), bool, pt->param_count);
+
+	if (ce->split_args) {
+		GB_ASSERT(ce->split_args->named.count == named_operands.count);
+		for_array(i, ce->split_args->named) {
+			Ast *arg = ce->split_args->named[i];
+			Operand operand = named_operands[i];
+
+			ast_node(fv, FieldValue, arg);
+			if (fv->field->kind != Ast_Ident) {
+				if (show_error) {
+					gbString expr_str = expr_to_string(fv->field);
+					error(arg, "Invalid parameter name '%s' in procedure call", expr_str);
+					gb_string_free(expr_str);
+				}
+				err = CallArgumentError_InvalidFieldValue;
+				continue;
+			}
+			String name = fv->field->Ident.token.string;
+			isize param_index = lookup_procedure_parameter(pt, name);
+			if (param_index < 0) {
+				if (show_error) {
+					error(arg, "No parameter named '%.*s' for this procedure type", LIT(name));
+				}
+				err = CallArgumentError_ParameterNotFound;
+				continue;
+			}
+			if (visited[param_index]) {
+				if (show_error) {
+					error(arg, "Duplicate parameter '%.*s' in procedure call", LIT(name));
+				}
+				err = CallArgumentError_DuplicateParameter;
+				continue;
+			}
+
+			visited[param_index] = true;
+			ordered_operands[param_index] = operand;
+
+			err = CallArgumentError_DuplicateParameter;
+		}
+		if (err) {
+			return err;
+		}
+	}
+
 
 	TypeTuple *param_tuple = nullptr;
 	if (pt->params != nullptr) {
@@ -5334,7 +5397,6 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 	}
 
 
-	CallArgumentError err = CallArgumentError_None;
 	Type *final_proc_type = proc_type;
 	Entity *gen_entity = nullptr;
 
@@ -5571,21 +5633,6 @@ gb_internal bool is_call_expr_field_value(AstCallExpr *ce) {
 	return ce->args[0]->kind == Ast_FieldValue;
 }
 
-gb_internal isize lookup_procedure_parameter(TypeProc *pt, String parameter_name) {
-	isize param_count = pt->param_count;
-	for (isize i = 0; i < param_count; i++) {
-		Entity *e = pt->params->Tuple.variables[i];
-		String name = e->token.string;
-		if (is_blank_ident(name)) {
-			continue;
-		}
-		if (name == parameter_name) {
-			return i;
-		}
-	}
-	return -1;
-}
-
 gb_internal CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
 	ast_node(ce, CallExpr, call);
 	GB_ASSERT(is_type_proc(proc_type));
@@ -5969,32 +6016,568 @@ gb_internal CallArgumentError check_order_of_call_arguments(CheckerContext *c, T
 	return err;
 }
 
+gb_internal bool check_named_arguments(CheckerContext *c, Type *type, Slice<Ast *> const &named_args, Array<Operand> *named_operands, bool show_error) {
+	bool success = true;
 
-gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContext *c, Operand *operand, Ast *call) {
-	Type *proc_type = nullptr;
+	type = base_type(type);
+	if (named_args.count > 0) {
+		TypeProc *pt = nullptr;
+		if (is_type_proc(type)) {
+			pt = &type->Proc;
+		}
+
+		for_array(i, named_args) {
+			Ast *arg = named_args[i];
+			if (arg->kind != Ast_FieldValue) {
+				if (show_error) {
+					error(arg, "Expected a 'field = value'");
+				}
+				return false;
+			}
+			ast_node(fv, FieldValue, arg);
+			if (fv->field->kind != Ast_Ident) {
+				if (show_error) {
+					gbString expr_str = expr_to_string(fv->field);
+					error(arg, "Invalid parameter name '%s' in procedure call", expr_str);
+					gb_string_free(expr_str);
+				}
+				success = false;
+				continue;
+			}
+			String key = fv->field->Ident.token.string;
+			Ast *value = fv->value;
+
+			Type *type_hint = nullptr;
+			if (pt) {
+				isize param_index = lookup_procedure_parameter(pt, key);
+				if (param_index < 0) {
+					if (show_error) {
+						error(value, "No parameter named '%.*s' for this procedure type %s", LIT(key), type_to_string(type));
+					}
+					success = false;
+					continue;
+				}
+
+				// bool prev_visited = visited[param_index];
+				// if (prev_visited) {
+				// 	error(value, "Duplicate parameter named '%.*s' in procedure call", LIT(key));
+				// 	success = false;
+				// }
+
+				Entity *e = pt->params->Tuple.variables[param_index];
+				if (!is_type_polymorphic(e->type)) {
+					type_hint = e->type;
+				}
+
+			}
+			Operand o = {};
+			check_expr_with_type_hint(c, &o, value, type_hint);
+			if (o.mode == Addressing_Invalid) {
+				success = false;
+			}
+			array_add(named_operands, o);
+		}
+
+	}
+	return success;
+}
+
+
+gb_internal CallArgumentData check_call_arguments_new_and_improved_proc_group(CheckerContext *c, Operand *operand, Ast *call) {
+	ast_node(ce, CallExpr, call);
+	GB_ASSERT(ce->split_args != nullptr);
+
+	Slice<Ast *> const &positional_args = ce->split_args->positional;
+	Slice<Ast *> const &named_args      = ce->split_args->named;
 
 	CallArgumentData data = {};
 	data.result_type = t_invalid;
 
-	proc_type = base_type(operand->type);
+	GB_ASSERT(operand->mode == Addressing_ProcGroup);
+	auto procs = proc_group_entities_cloned(c, *operand);
+
+	if (procs.count > 1) {
+		isize max_arg_count = positional_args.count + named_args.count;
+		for (Ast *arg : positional_args) {
+			// NOTE(bill): The only thing that may have multiple values
+			// will be a call expression (assuming `or_return` and `()` will be stripped)
+			arg = strip_or_return_expr(arg);
+			if (arg && arg->kind == Ast_CallExpr) {
+				max_arg_count = ISIZE_MAX;
+				break;
+			}
+		}
+		if (max_arg_count != ISIZE_MAX) for (Ast *arg : named_args) {
+			// NOTE(bill): The only thing that may have multiple values
+			// will be a call expression (assuming `or_return` and `()` will be stripped)
+			if (arg->kind == Ast_FieldValue) {
+				arg = strip_or_return_expr(arg->FieldValue.value);
+				if (arg && arg->kind == Ast_CallExpr) {
+					max_arg_count = ISIZE_MAX;
+					break;
+				}
+			}
+		}
 
-	if (operand->mode == Addressing_ProcGroup) {
-		Array<Entity *> procs = proc_group_entities(c, *operand);
-		if (procs.count == 1) {
-			proc_type = base_type(procs[0]->type);
-		} else {
-			error(call, "Procedure groups >1 not yet supported");
+		for (isize proc_index = 0; proc_index < procs.count; /**/) {
+			Entity *proc = procs[proc_index];
+			Type *pt = base_type(proc->type);
+			if (!(pt != nullptr && is_type_proc(pt))) {
+				proc_index++;
+				continue;
+			}
+
+			isize param_count = 0;
+			isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(pt, &param_count);
+
+			if (param_count_excluding_defaults > max_arg_count) {
+				array_unordered_remove(&procs, proc_index);
+				continue;
+			}
+
+			// for (Ast *arg : named_args) {
+			// 	if (arg->kind != Ast_FieldValue) {
+			// 		continue;
+			// 	}
+			// 	ast_node(fv, FieldValue, arg);
+			// 	if (fv->field->kind != Ast_Ident) {
+			// 		continue;
+			// 	}
+			// 	bool ok = false;
+			// 	String key = fv->field->Ident.token.string;
+			// 	if (param_count) for (Entity *e : pt->Proc.params->Tuple.variables) {
+			// 		if (e->token.string == key) {
+			// 			ok = true;
+			// 			break;
+			// 		}
+			// 	}
+			// 	if (!ok) {
+			// 		if (proc_index < procs.count) {
+			// 			array_unordered_remove(&procs, proc_index);
+			// 			continue;
+			// 		} else {
+			// 			break;
+			// 		}
+			// 	}
+			// }
+
+			proc_index++;
+		}
+	}
+
+	Entity **lhs = nullptr;
+	isize lhs_count = -1;
+	bool is_variadic = false;
+
+	auto positional_operands = array_make<Operand>(heap_allocator(), 0, 0);
+	auto named_operands = array_make<Operand>(heap_allocator(), 0, 0);
+	defer (array_free(&positional_operands));
+	defer (array_free(&named_operands));
+
+	if (procs.count == 1) {
+		Ast *ident = operand->expr;
+		while (ident->kind == Ast_SelectorExpr) {
+			Ast *s = ident->SelectorExpr.selector;
+			ident = s;
+		}
+
+
+		Entity *e = procs[0];
+
+		lhs = populate_proc_parameter_list(c, e->type, &lhs_count, &is_variadic);
+		check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None);
+
+		if (!check_named_arguments(c, e->type, named_args, &named_operands, true)) {
+			return data;
+		}
+
+		CallArgumentError err = check_call_arguments_internal(c, call, e->type, e, positional_operands, named_operands, CallArgumentMode_ShowErrors, &data);
+		if (err != CallArgumentError_None) {
+			// handle error
+		}
+
+		Type *proc_type = base_type(operand->type);
+
+		Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
+		add_entity_use(c, ident, entity_to_use);
+		if (entity_to_use != nullptr) {
+			update_untyped_expr_type(c, operand->expr, entity_to_use->type, true);
+
+			proc_type = base_type(entity_to_use->type);
+		}
+
+		if (proc_type && proc_type->kind == Type_Proc) {
+			data.result_type = proc_type->Proc.results;
+			add_type_and_value(c, operand->expr, operand->mode, proc_type, operand->value);
+		} else if (err == CallArgumentError_None) {
+			data.result_type = nullptr;
+		}
+
+		if (data.gen_entity != nullptr) {
+			Entity *e = data.gen_entity;
+			DeclInfo *decl = data.gen_entity->decl_info;
+			CheckerContext ctx = *c;
+			ctx.scope = decl->scope;
+			ctx.decl = decl;
+			ctx.proc_name = e->token.string;
+			ctx.curr_proc_decl = decl;
+			ctx.curr_proc_sig  = e->type;
+
+			GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit);
+			bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
+			decl->where_clauses_evaluated = true;
+
+			if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) {
+				check_procedure_later(c->checker, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags);
+			}
+		}
+		return data;
+	}
+
+	{
+		// NOTE(bill, 2019-07-13): This code is used to improve the type inference for procedure groups
+		// where the same positional parameter has the same type value (and ellipsis)
+		bool proc_arg_count_all_equal = true;
+		isize proc_arg_count = -1;
+		for (Entity *p : procs) {
+			Type *pt = base_type(p->type);
+			if (pt != nullptr && is_type_proc(pt)) {
+				if (proc_arg_count < 0) {
+					proc_arg_count = pt->Proc.param_count;
+				} else {
+					if (proc_arg_count != pt->Proc.param_count) {
+						proc_arg_count_all_equal = false;
+						break;
+					}
+				}
+			}
+		}
+
+		if (proc_arg_count >= 0 && proc_arg_count_all_equal) {
+			lhs_count = proc_arg_count;
+			if (lhs_count > 0)  {
+				lhs = gb_alloc_array(heap_allocator(), Entity *, lhs_count);
+				for (isize param_index = 0; param_index < lhs_count; param_index++) {
+					Entity *e = nullptr;
+					for (Entity *p : procs) {
+						Type *pt = base_type(p->type);
+						if (!(pt != nullptr && is_type_proc(pt))) {
+							continue;
+						}
+
+						if (e == nullptr) {
+							e = pt->Proc.params->Tuple.variables[param_index];
+						} else {
+							Entity *f = pt->Proc.params->Tuple.variables[param_index];
+							if (e == f) {
+								continue;
+							}
+							if (are_types_identical(e->type, f->type)) {
+								bool ee = (e->flags & EntityFlag_Ellipsis) != 0;
+								bool fe = (f->flags & EntityFlag_Ellipsis) != 0;
+								if (ee == fe) {
+									continue;
+								}
+							}
+							// NOTE(bill): Entities are not close enough to be used
+							e = nullptr;
+							break;
+						}
+					}
+					lhs[param_index] = e;
+				}
+			}
+		}
+	}
+
+	check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None);
+
+	for_array(i, named_args) {
+		Ast *arg = named_args[i];
+		if (arg->kind != Ast_FieldValue) {
+			error(arg, "Expected a 'field = value'");
+			return data;
+		}
+		ast_node(fv, FieldValue, arg);
+		if (fv->field->kind != Ast_Ident) {
+			gbString expr_str = expr_to_string(fv->field);
+			error(arg, "Invalid parameter name '%s' in procedure call", expr_str);
+			gb_string_free(expr_str);
 			return data;
 		}
+		String key = fv->field->Ident.token.string;
+		Ast *value = fv->value;
+
+		Type *type_hint = nullptr;
+
+		for (isize lhs_idx = 0; lhs_idx < lhs_count; lhs_idx++) {
+			Entity *e = lhs[lhs_idx];
+			if (e != nullptr && e->token.string == key &&
+			    !is_type_polymorphic(e->type)) {
+				type_hint = e->type;
+				break;
+			}
+		}
+		Operand o = {};
+		check_expr_with_type_hint(c, &o, value, type_hint);
+		array_add(&named_operands, o);
 	}
-	if (proc_type == nullptr || proc_type->kind != Type_Proc) {
-		gbString s = type_to_string(proc_type);
-		error(call, "Expected a procedure to call, got %s", s);
-		gb_string_free(s);
 
+	gb_free(heap_allocator(), lhs);
+
+	auto valids = array_make<ValidIndexAndScore>(heap_allocator(), 0, procs.count);
+	defer (array_free(&valids));
+
+	auto proc_entities = array_make<Entity *>(heap_allocator(), 0, procs.count*2 + 1);
+	defer (array_free(&proc_entities));
+	for (Entity *proc : procs) {
+		array_add(&proc_entities, proc);
+	}
+
+
+	gbString expr_name = expr_to_string(operand->expr);
+	defer (gb_string_free(expr_name));
+
+	for_array(i, procs) {
+		Entity *p = procs[i];
+		Type *pt = base_type(p->type);
+		if (pt != nullptr && is_type_proc(pt)) {
+			CallArgumentError err = CallArgumentError_None;
+			CallArgumentData data = {};
+			CheckerContext ctx = *c;
+
+			ctx.no_polymorphic_errors = true;
+			ctx.allow_polymorphic_types = is_type_polymorphic(pt);
+			ctx.hide_polymorphic_errors = true;
+
+			err = check_call_arguments_internal(&ctx, call, pt, p, positional_operands, named_operands, CallArgumentMode_NoErrors, &data);
+			if (err != CallArgumentError_None) {
+				continue;
+			}
+			isize index = i;
+
+			if (data.gen_entity != nullptr) {
+				Entity *e = data.gen_entity;
+				DeclInfo *decl = data.gen_entity->decl_info;
+				ctx.scope = decl->scope;
+				ctx.decl = decl;
+				ctx.proc_name = e->token.string;
+				ctx.curr_proc_decl = decl;
+				ctx.curr_proc_sig  = e->type;
+
+				GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit);
+				if (!evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, false)) {
+					continue;
+				}
+
+				array_add(&proc_entities, data.gen_entity);
+				index = proc_entities.count-1;
+			}
+
+			ValidIndexAndScore item = {};
+			item.index = index;
+			item.score = data.score;
+			array_add(&valids, item);
+		}
+	}
+
+	if (valids.count > 1) {
+		gb_sort_array(valids.data, valids.count, valid_index_and_score_cmp);
+		i64 best_score = valids[0].score;
+		Entity *best_entity = proc_entities[valids[0].index];
+		GB_ASSERT(best_entity != nullptr);
+		for (isize i = 1; i < valids.count; i++) {
+			if (best_score > valids[i].score) {
+				valids.count = i;
+				break;
+			}
+			if (best_entity == proc_entities[valids[i].index]) {
+				valids.count = i;
+				break;
+			}
+		}
+	}
+
+	auto print_argument_types = [&]() {
+		error_line("\tGiven argument types: (");
+		isize i = 0;
+		for (Operand const &o : positional_operands) {
+			if (i++ > 0) error_line(", ");
+			gbString type = type_to_string(o.type);
+			defer (gb_string_free(type));
+			error_line("%s", type);
+		}
+		for (Operand const &o : named_operands) {
+			if (i++ > 0) error_line(", ");
+
+			gbString type = type_to_string(o.type);
+			defer (gb_string_free(type));
+
+			if (i < ce->split_args->named.count) {
+				Ast *named_field = ce->split_args->named[i];
+				ast_node(fv, FieldValue, named_field);
+
+				gbString field = expr_to_string(fv->field);
+				defer (gb_string_free(field));
+
+				error_line("%s = %s", field, type);
+			} else {
+				error_line("%s", type);
+			}
+		}
+		error_line(")\n");
+	};
+
+	if (valids.count == 0) {
+		begin_error_block();
+		defer (end_error_block());
+
+		error(operand->expr, "No procedures or ambiguous call for procedure group '%s' that match with the given arguments", expr_name);
+		if (positional_operands.count == 0 && named_operands.count == 0) {
+			error_line("\tNo given arguments\n");
+		} else {
+			print_argument_types();
+		}
+
+		if (procs.count > 0) {
+			error_line("Did you mean to use one of the following:\n");
+		}
+		for (Entity *proc : procs) {
+			TokenPos pos = proc->token.pos;
+			Type *t = base_type(proc->type);
+			if (t == t_invalid) continue;
+			GB_ASSERT(t->kind == Type_Proc);
+			gbString pt;
+			defer (gb_string_free(pt));
+			if (t->Proc.node != nullptr) {
+				pt = expr_to_string(t->Proc.node);
+			} else {
+				pt = type_to_string(t);
+			}
+			String prefix = {};
+			String prefix_sep = {};
+			if (proc->pkg) {
+				prefix = proc->pkg->name;
+				prefix_sep = str_lit(".");
+			}
+			String name = proc->token.string;
+
+			char const *sep = "::";
+			if (proc->kind == Entity_Variable) {
+				sep = ":=";
+			}
+			error_line("\t%.*s%.*s%.*s %s %s at %s\n", LIT(prefix), LIT(prefix_sep), LIT(name), sep, pt, token_pos_to_string(pos));
+		}
+		if (procs.count > 0) {
+			error_line("\n");
+		}
+
+		data.result_type = t_invalid;
+	} else if (valids.count > 1) {
+		begin_error_block();
+		defer (end_error_block());
+
+		error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name);
+		print_argument_types();
+
+		for (isize i = 0; i < valids.count; i++) {
+			Entity *proc = proc_entities[valids[i].index];
+			GB_ASSERT(proc != nullptr);
+			TokenPos pos = proc->token.pos;
+			Type *t = base_type(proc->type); GB_ASSERT(t->kind == Type_Proc);
+			gbString pt = nullptr;
+			defer (gb_string_free(pt));
+			if (t->Proc.node != nullptr) {
+				pt = expr_to_string(t->Proc.node);
+			} else {
+				pt = type_to_string(t);
+			}
+			String name = proc->token.string;
+			char const *sep = "::";
+			if (proc->kind == Entity_Variable) {
+				sep = ":=";
+			}
+			error_line("\t%.*s %s %s ", LIT(name), sep, pt);
+			if (proc->decl_info->proc_lit != nullptr) {
+				GB_ASSERT(proc->decl_info->proc_lit->kind == Ast_ProcLit);
+				auto *pl = &proc->decl_info->proc_lit->ProcLit;
+				if (pl->where_token.kind != Token_Invalid) {
+					error_line("\n\t\twhere ");
+					for_array(j, pl->where_clauses) {
+						Ast *clause = pl->where_clauses[j];
+						if (j != 0) {
+							error_line("\t\t      ");
+						}
+						gbString str = expr_to_string(clause);
+						error_line("%s", str);
+						gb_string_free(str);
+
+						if (j != pl->where_clauses.count-1) {
+							error_line(",");
+						}
+					}
+					error_line("\n\t");
+				}
+			}
+			error_line("at %s\n", token_pos_to_string(pos));
+		}
+		data.result_type = t_invalid;
+	} else {
+		GB_ASSERT(valids.count == 1);
+		Ast *ident = operand->expr;
+		while (ident->kind == Ast_SelectorExpr) {
+			Ast *s = ident->SelectorExpr.selector;
+			ident = s;
+		}
+
+		Entity *e = proc_entities[valids[0].index];
+		GB_ASSERT(e != nullptr);
+
+		Array<Operand> named_operands = {};
+
+		Type *proc_type = e->type;
+		CallArgumentData data = {};
+		CallArgumentError err = check_call_arguments_internal(c, call, proc_type, e, positional_operands, named_operands, CallArgumentMode_ShowErrors, &data);
+		gb_unused(err);
+		Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
+		add_entity_use(c, ident, entity_to_use);
+		if (entity_to_use != nullptr) {
+			update_untyped_expr_type(c, operand->expr, entity_to_use->type, true);
+		}
+
+		if (data.gen_entity != nullptr) {
+			Entity *e = data.gen_entity;
+			DeclInfo *decl = data.gen_entity->decl_info;
+			CheckerContext ctx = *c;
+			ctx.scope = decl->scope;
+			ctx.decl = decl;
+			ctx.proc_name = e->token.string;
+			ctx.curr_proc_decl = decl;
+			ctx.curr_proc_sig  = e->type;
+
+			GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit);
+			bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
+			decl->where_clauses_evaluated = true;
+
+			if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) {
+				check_procedure_later(c->checker, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags);
+			}
+		}
 		return data;
 	}
 
+	return data;
+}
+
+
+gb_internal CallArgumentData check_call_arguments_new_and_improved(CheckerContext *c, Operand *operand, Ast *call) {
+	Type *proc_type = nullptr;
+
+	CallArgumentData data = {};
+	data.result_type = t_invalid;
+
+	proc_type = base_type(operand->type);
+
 	if (proc_type != nullptr && is_type_polymorphic(proc_type)) {
 		error(call, "Polymorphic procedures not yet supported");
 		return data;
@@ -6012,19 +6595,38 @@ gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContex
 
 	bool vari_expand = (ce->ellipsis.pos.line != 0);
 
-	auto positional_args = ce->args;
-	for (isize i = 0; i < ce->args.count; i++) {
-		Ast *arg = ce->args.data[i];
-		if (arg->kind == Ast_FieldValue) {
-			positional_args.count = i;
-			break;
+	// Split positional and named args into separate arrays/slices
+	Slice<Ast *> positional_args = {};
+	Slice<Ast *> named_args = {};
+
+	if (ce->split_args == nullptr) {
+		positional_args = ce->args;
+		for (isize i = 0; i < ce->args.count; i++) {
+			Ast *arg = ce->args.data[i];
+			if (arg->kind == Ast_FieldValue) {
+				positional_args.count = i;
+				break;
+			}
 		}
+		named_args = slice(ce->args, positional_args.count, ce->args.count);
+
+		auto split_args = gb_alloc_item(permanent_allocator(), AstSplitArgs);
+		split_args->positional = positional_args;
+		split_args->named = named_args;
+		ce->split_args = split_args;
+	} else {
+		positional_args = ce->split_args->positional;
+		named_args      = ce->split_args->named;
+	}
+
+	if (operand->mode == Addressing_ProcGroup) {
+		return check_call_arguments_new_and_improved_proc_group(c, operand, call);
 	}
-	auto named_args = slice(ce->args, positional_args.count, ce->args.count);
+
 
 	auto positional_operands = array_make<Operand>(heap_allocator(), 0, positional_args.count);
-	auto variadic_operands = array_make<Operand>(heap_allocator(), 0, 0);
-	auto named_operands = array_make<Operand>(heap_allocator(), 0, 0);
+	auto variadic_operands   = array_make<Operand>(heap_allocator(), 0, 0);
+	auto named_operands      = array_make<Operand>(heap_allocator(), 0, 0);
 
 	defer (array_free(&positional_operands));
 	defer (array_free(&variadic_operands));
@@ -6038,14 +6640,16 @@ gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContex
 		isize lhs_count = -1;
 		bool is_variadic = false;
 		Entity **lhs =  nullptr;
-		if (proc_type != nullptr && is_type_proc(proc_type))  {
+		if (pt != nullptr)  {
 			lhs = populate_proc_parameter_list(c, proc_type, &lhs_count, &is_variadic);
+		} else if (operand->mode == Addressing_ProcGroup) {
+			// TODO
 		}
 		check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None);
 	}
 	positional_operand_count = positional_operands.count;
 
-	if (proc_type != nullptr) {
+	if (pt != nullptr) {
 		visited = slice_make<bool>(temporary_allocator(), pt->param_count);
 
 		if (!pt->variadic) {
@@ -6161,7 +6765,7 @@ gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContex
 		string_map_init(&type_hint_map, 2*named_args.count);
 		defer (string_map_destroy(&type_hint_map));
 
-		if (proc_type != nullptr) {
+		if (pt != nullptr) {
 			TypeTuple *param_tuple = &pt->params->Tuple;
 			for (Entity *e : param_tuple->variables) {
 				if (is_blank_ident(e->token)) {
@@ -6252,7 +6856,7 @@ gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContex
 
 	}
 
-	if (proc_type != nullptr) {
+	if (pt != nullptr) {
 		if (pt->variadic) {
 			visited[pt->variadic_index] = true;
 		}
@@ -6293,14 +6897,45 @@ gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContex
 		return data;
 	}
 
+	{
+		Ast *ident = operand->expr;
+		while (ident->kind == Ast_SelectorExpr) {
+			Ast *s = ident->SelectorExpr.selector;
+			ident = s;
+		}
 
-	auto ordered_args = gb_alloc_item(permanent_allocator(), AstOrderedArgs);
-	ordered_args->positional = positional_args;
-	ordered_args->named = named_args;
+		Entity *e = entity_of_node(ident);
 
-	ce->ordered_args = ordered_args;
+		Array<Operand> operands = array_clone(heap_allocator(), positional_operands);
 
-	if (proc_type != nullptr) {
+		CallArgumentError err = check_call_arguments_internal(c, call, proc_type, e, operands, named_operands, CallArgumentMode_ShowErrors, &data);
+		gb_unused(err);
+		Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
+		add_entity_use(c, ident, entity_to_use);
+		if (entity_to_use != nullptr) {
+			update_untyped_expr_type(c, operand->expr, entity_to_use->type, true);
+		}
+		if (data.gen_entity != nullptr) {
+			Entity *e = data.gen_entity;
+			DeclInfo *decl = data.gen_entity->decl_info;
+			CheckerContext ctx = *c;
+			ctx.scope = decl->scope;
+			ctx.decl = decl;
+			ctx.proc_name = e->token.string;
+			ctx.curr_proc_decl = decl;
+			ctx.curr_proc_sig  = e->type;
+
+			GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit);
+			bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
+			decl->where_clauses_evaluated = true;
+
+			if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) {
+				check_procedure_later(c->checker, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags);
+			}
+		}
+	}
+
+	if (pt != nullptr) {
 		data.result_type = pt->results;
 	}
 	return data;
@@ -6438,13 +7073,13 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op
 				break;
 			}
 		}
-		if (all_non_poly && (procs.count == 1 || c->pkg->name == "bug")) {
-			return check_call_arguments_single_procedure(c, operand, call);
+		if (all_non_poly) {
+			return check_call_arguments_new_and_improved(c, operand, call);
 		}
 	}
 
 	if (operand->mode != Addressing_ProcGroup && is_type_proc(operand->type) && !is_type_polymorphic(operand->type)) {
-		return check_call_arguments_single_procedure(c, operand, call);
+		return check_call_arguments_new_and_improved(c, operand, call);
 	}
 
 	ast_node(ce, CallExpr, call);
@@ -6635,8 +7270,9 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op
 			lhs = populate_proc_parameter_list(c, e->type, &lhs_count, &is_variadic);
 			check_unpack_arguments(c, lhs, lhs_count, &operands, args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None);
 
+			Array<Operand> named_operands = {};
 			CallArgumentData data = {};
-			CallArgumentError err = call_checker(c, call, e->type, e, operands, CallArgumentMode_ShowErrors, &data);
+			CallArgumentError err = call_checker(c, call, e->type, e, operands, named_operands, CallArgumentMode_ShowErrors, &data);
 			if (err != CallArgumentError_None) {
 				// handle error
 			}
@@ -6740,7 +7376,8 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op
 				ctx.allow_polymorphic_types = is_type_polymorphic(pt);
 				ctx.hide_polymorphic_errors = true;
 
-				err = call_checker(&ctx, call, pt, p, operands, CallArgumentMode_NoErrors, &data);
+				Array<Operand> named_operands = {};
+				err = call_checker(&ctx, call, pt, p, operands, named_operands, CallArgumentMode_NoErrors, &data);
 				if (err != CallArgumentError_None) {
 					continue;
 				}
@@ -6910,9 +7547,11 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op
 			Entity *e = proc_entities[valids[0].index];
 			GB_ASSERT(e != nullptr);
 
+			Array<Operand> named_operands = {};
+
 			proc_type = e->type;
 			CallArgumentData data = {};
-			CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data);
+			CallArgumentError err = call_checker(c, call, proc_type, e, operands, named_operands, CallArgumentMode_ShowErrors, &data);
 			gb_unused(err);
 			Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
 			add_entity_use(c, ident, entity_to_use);
@@ -6949,9 +7588,10 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op
 
 		Entity *e = entity_of_node(ident);
 
+		Array<Operand> named_operands = {};
 
 		CallArgumentData data = {};
-		CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data);
+		CallArgumentError err = call_checker(c, call, proc_type, e, operands, named_operands, CallArgumentMode_ShowErrors, &data);
 		gb_unused(err);
 		Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
 		add_entity_use(c, ident, entity_to_use);

+ 19 - 12
src/error.cpp

@@ -265,7 +265,8 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) {
 	defer (gb_string_free(the_line));
 
 	if (the_line != nullptr) {
-		String line = make_string(cast(u8 const *)the_line, gb_string_length(the_line));
+		char const *line_text = the_line;
+		isize line_len = gb_string_length(the_line);
 
 		// TODO(bill): This assumes ASCII
 
@@ -285,21 +286,27 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) {
 
 		isize squiggle_extra = 0;
 
-		if (line.len > MAX_LINE_LENGTH_PADDED) {
+		if (line_len > MAX_LINE_LENGTH_PADDED) {
 			i32 left = MAX_TAB_WIDTH;
-			line.text += offset-left;
-			line.len  -= offset-left;
-			offset = left+MAX_TAB_WIDTH/2;
-			if (line.len > MAX_LINE_LENGTH_PADDED) {
-				line.len = MAX_LINE_LENGTH_PADDED;
-				if (error_length > line.len-left) {
-					error_length = cast(i32)line.len - left;
+			if (offset > 0) {
+				line_text += offset-left;
+				line_len  -= offset-left;
+				offset = left+MAX_TAB_WIDTH/2;
+			}
+			if (line_len > MAX_LINE_LENGTH_PADDED) {
+				line_len = MAX_LINE_LENGTH_PADDED;
+				if (error_length > line_len-left) {
+					error_length = cast(i32)line_len - left;
 					squiggle_extra = 1;
 				}
 			}
-			error_out("... %.*s ...", LIT(line));
+			if (offset > 0) {
+				error_out("... %.*s ...", cast(i32)line_len, line_text);
+			} else {
+				error_out("%.*s ...", cast(i32)line_len, line_text);
+			}
 		} else {
-			error_out("%.*s", LIT(line));
+			error_out("%.*s", cast(i32)line_len, line_text);
 		}
 		error_out("\n\t");
 
@@ -312,7 +319,7 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) {
 		error_out("^");
 		if (end.file_id == pos.file_id) {
 			if (end.line > pos.line) {
-				for (i32 i = offset; i < line.len; i++) {
+				for (i32 i = offset; i < line_len; i++) {
 					error_out("~");
 				}
 			} else if (end.line == pos.line && end.column > pos.column) {

+ 33 - 6
src/gb/gb.h

@@ -3299,12 +3299,39 @@ void const *gb_memrchr(void const *data, u8 c, isize n) {
 
 
 
-gb_inline void *gb_alloc_align (gbAllocator a, isize size, isize alignment)                                { return a.proc(a.data, gbAllocation_Alloc, size, alignment, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS); }
-gb_inline void *gb_alloc       (gbAllocator a, isize size)                                                 { return gb_alloc_align(a, size, GB_DEFAULT_MEMORY_ALIGNMENT); }
-gb_inline void  gb_free        (gbAllocator a, void *ptr)                                                  { if (ptr != NULL) a.proc(a.data, gbAllocation_Free, 0, 0, ptr, 0, GB_DEFAULT_ALLOCATOR_FLAGS); }
-gb_inline void  gb_free_all    (gbAllocator a)                                                             { a.proc(a.data, gbAllocation_FreeAll, 0, 0, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS); }
-gb_inline void *gb_resize      (gbAllocator a, void *ptr, isize old_size, isize new_size)                  { return gb_resize_align(a, ptr, old_size, new_size, GB_DEFAULT_MEMORY_ALIGNMENT); }
-gb_inline void *gb_resize_align(gbAllocator a, void *ptr, isize old_size, isize new_size, isize alignment) { return a.proc(a.data, gbAllocation_Resize, new_size, alignment, ptr, old_size, GB_DEFAULT_ALLOCATOR_FLAGS); }
+gb_inline void *gb_alloc_align (gbAllocator a, isize size, isize alignment) {
+	if (size == 0) {
+		return NULL;
+	}
+	return a.proc(a.data, gbAllocation_Alloc, size, alignment, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS);
+}
+gb_inline void *gb_alloc(gbAllocator a, isize size) {
+	return gb_alloc_align(a, size, GB_DEFAULT_MEMORY_ALIGNMENT);
+}
+gb_inline void  gb_free(gbAllocator a, void *ptr) {
+	if (ptr != NULL) {
+		a.proc(a.data, gbAllocation_Free, 0, 0, ptr, 0, GB_DEFAULT_ALLOCATOR_FLAGS);
+	}
+}
+gb_inline void  gb_free_all(gbAllocator a) {
+	a.proc(a.data, gbAllocation_FreeAll, 0, 0, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS);
+}
+gb_inline void *gb_resize(gbAllocator a, void *ptr, isize old_size, isize new_size) {
+	return gb_resize_align(a, ptr, old_size, new_size, GB_DEFAULT_MEMORY_ALIGNMENT);
+}
+gb_inline void *gb_resize_align(gbAllocator a, void *ptr, isize old_size, isize new_size, isize alignment) {
+	if (new_size == 0) {
+		if (ptr != NULL) {
+			return a.proc(a.data, gbAllocation_Free, 0, 0, ptr, old_size, GB_DEFAULT_ALLOCATOR_FLAGS);
+		}
+		return NULL;
+	} else if (ptr == NULL) {
+		return a.proc(a.data, gbAllocation_Alloc, new_size, alignment, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS);
+	} else if (old_size == new_size && ((uintptr)ptr % (uintptr)alignment) == 0) {
+		return ptr;
+	}
+	return a.proc(a.data, gbAllocation_Resize, new_size, alignment, ptr, old_size, GB_DEFAULT_ALLOCATOR_FLAGS);
+}
 
 gb_inline void *gb_alloc_copy      (gbAllocator a, void const *src, isize size) {
 	return gb_memcopy(gb_alloc(a, size), src, size);

+ 7 - 7
src/llvm_backend_proc.cpp

@@ -3156,20 +3156,20 @@ gb_internal void lb_add_values_to_array(lbProcedure *p, Array<lbValue> *args, lb
 		array_add(args, value);
 	}
 }
-gb_internal lbValue lb_build_call_expr_internal_with_arg_ordered_args(lbProcedure *p, lbValue value, TypeProc *pt, Ast *call, AstOrderedArgs *ordered_args) {
+gb_internal lbValue lb_build_call_expr_internal_with_arg_split_args(lbProcedure *p, lbValue value, TypeProc *pt, Ast *call, AstSplitArgs *split_args) {
 	ast_node(ce, CallExpr, call);
 
 	auto args = array_make<lbValue>(permanent_allocator(), 0, pt->param_count);
 
 	bool vari_expand = (ce->ellipsis.pos.line != 0);
 
-	for_array(i, ordered_args->positional) {
+	for_array(i, split_args->positional) {
 		Entity *e = pt->params->Tuple.variables[i];
 		GB_ASSERT(e->kind == Entity_Variable);
 
 		if (pt->variadic && pt->variadic_index == i) {
 			lbValue variadic_args = lb_const_nil(p->module, e->type);
-			auto variadic = slice(ordered_args->positional, pt->variadic_index, ordered_args->positional.count);
+			auto variadic = slice(split_args->positional, pt->variadic_index, split_args->positional.count);
 			if (variadic.count != 0) {
 				// variadic call argument generation
 				Type *slice_type = e->type;
@@ -3211,14 +3211,14 @@ gb_internal lbValue lb_build_call_expr_internal_with_arg_ordered_args(lbProcedur
 
 			break;
 		} else {
-			lbValue value = lb_build_expr(p, ordered_args->positional[i]);
+			lbValue value = lb_build_expr(p, split_args->positional[i]);
 			lb_add_values_to_array(p, &args, value);
 		}
 	}
 
 	array_resize(&args, pt->param_count);
 
-	for (Ast *arg : ordered_args->named) {
+	for (Ast *arg : split_args->named) {
 		ast_node(fv, FieldValue, arg);
 		GB_ASSERT(fv->field->kind == Ast_Ident);
 		String name = fv->field->Ident.token.string;
@@ -3331,8 +3331,8 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
 	GB_ASSERT(proc_type_->kind == Type_Proc);
 	TypeProc *pt = &proc_type_->Proc;
 
-	if (ce->ordered_args)  {
-		return lb_build_call_expr_internal_with_arg_ordered_args(p, value, pt, expr, ce->ordered_args);
+	if (ce->split_args)  {
+		return lb_build_call_expr_internal_with_arg_split_args(p, value, pt, expr, ce->split_args);
 	}
 
 	if (is_call_expr_field_value(ce)) {

+ 2 - 2
src/parser.hpp

@@ -367,7 +367,7 @@ gb_global char const *union_type_kind_strings[UnionType_COUNT] = {
 	"#shared_nil",
 };
 
-struct AstOrderedArgs {
+struct AstSplitArgs {
 	Slice<Ast *> positional;
 	Slice<Ast *> named;
 };
@@ -447,7 +447,7 @@ AST_KIND(_ExprBegin,  "",  bool) \
 		ProcInlining inlining; \
 		bool         optional_ok_one; \
 		bool         was_selector; \
-		AstOrderedArgs *ordered_args; \
+		AstSplitArgs *split_args; \
 	}) \
 	AST_KIND(FieldValue,      "field value",              struct { Token eq; Ast *field, *value; }) \
 	AST_KIND(EnumFieldValue,  "enum field value",         struct { \

+ 6 - 0
src/types.cpp

@@ -3326,6 +3326,9 @@ gb_internal bool are_struct_fields_reordered(Type *type) {
 	type = base_type(type);
 	GB_ASSERT(type->kind == Type_Struct);
 	type_set_offsets(type);
+	if (type->Struct.fields.count == 0) {
+		return false;
+	}
 	GB_ASSERT(type->Struct.offsets != nullptr);
 	
 	i64 prev_offset = 0;
@@ -3344,6 +3347,9 @@ gb_internal Slice<i32> struct_fields_index_by_increasing_offset(gbAllocator allo
 	type = base_type(type);
 	GB_ASSERT(type->kind == Type_Struct);
 	type_set_offsets(type);
+	if (type->Struct.fields.count == 0) {
+		return {};
+	}
 	GB_ASSERT(type->Struct.offsets != nullptr);
 	
 	auto indices = slice_make<i32>(allocator, type->Struct.fields.count);