Browse Source

Basic support for new procedure code (non-polymorphic, non-proc-group)

gingerBill 2 years ago
parent
commit
2992ca5df1
4 changed files with 501 additions and 18 deletions
  1. 373 15
      src/check_expr.cpp
  2. 119 0
      src/llvm_backend_proc.cpp
  3. 6 0
      src/parser.hpp
  4. 3 3
      src/parser_pos.cpp

+ 373 - 15
src/check_expr.cpp

@@ -123,6 +123,8 @@ gb_internal bool is_diverging_expr(Ast *expr);
 
 gb_internal CallArgumentError check_order_of_call_arguments(CheckerContext *c, Type *proc_type, Ast *call, bool show_error);
 
+gb_internal isize get_procedure_param_count_excluding_defaults(Type *pt, isize *param_count_);
+
 enum LoadDirectiveResult {
 	LoadDirective_Success  = 0,
 	LoadDirective_Error    = 1,
@@ -5151,14 +5153,19 @@ enum UnpackFlag : u32 {
 };
 
 
-gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array<Operand> *operands, Slice<Ast *> const &rhs, UnpackFlags flags) {
+gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array<Operand> *operands, Slice<Ast *> const &rhs_arguments, UnpackFlags flags) {
 	bool allow_ok    = (flags & UnpackFlag_AllowOk) != 0;
 	bool is_variadic = (flags & UnpackFlag_IsVariadic) != 0;
 	bool allow_undef = (flags & UnpackFlag_AllowUndef) != 0;
 
 	bool optional_ok = false;
 	isize tuple_index = 0;
-	for_array(i, rhs) {
+	for (Ast *rhs : rhs_arguments) {
+		if (rhs->kind == Ast_FieldValue) {
+			error(rhs, "Invalid use of 'field = value'");
+			rhs = rhs->FieldValue.value;
+		}
+
 		CheckerContext c_ = *ctx;
 		CheckerContext *c = &c_;
 
@@ -5166,12 +5173,11 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize
 
 		Type *type_hint = nullptr;
 
+
 		if (lhs != nullptr && tuple_index < lhs_count) {
 			// NOTE(bill): override DeclInfo for dependency
 			Entity *e = lhs[tuple_index];
 			if (e != nullptr) {
-				// DeclInfo *decl = decl_info_of_entity(e);
-				// if (decl) c->decl = decl;
 				type_hint = e->type;
 				if (e->flags & EntityFlag_Ellipsis) {
 					GB_ASSERT(is_type_slice(e->type));
@@ -5183,8 +5189,6 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize
 			// NOTE(bill): override DeclInfo for dependency
 			Entity *e = lhs[lhs_count-1];
 			if (e != nullptr) {
-				// DeclInfo *decl = decl_info_of_entity(e);
-				// if (decl) c->decl = decl;
 				type_hint = e->type;
 				if (e->flags & EntityFlag_Ellipsis) {
 					GB_ASSERT(is_type_slice(e->type));
@@ -5194,15 +5198,15 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize
 			}
 		}
 
-		Ast *rhs_expr = unparen_expr(rhs[i]);
+		Ast *rhs_expr = unparen_expr(rhs);
 		if (allow_undef && rhs_expr != nullptr && rhs_expr->kind == Ast_Uninit) {
 			// NOTE(bill): Just handle this very specific logic here
 			o.type = t_untyped_uninit;
 			o.mode = Addressing_Value;
-			o.expr = rhs[i];
-			add_type_and_value(c, rhs[i], o.mode, o.type, o.value);
+			o.expr = rhs;
+			add_type_and_value(c, rhs, o.mode, o.type, o.value);
 		} else {
-			check_expr_base(c, &o, rhs[i], type_hint);
+			check_expr_base(c, &o, rhs, type_hint);
 		}
 		if (o.mode == Addressing_NoValue) {
 			error_operand_no_value(&o);
@@ -5210,7 +5214,7 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize
 		}
 
 		if (o.type == nullptr || o.type->kind != Type_Tuple) {
-			if (allow_ok && lhs_count == 2 && rhs.count == 1 &&
+			if (allow_ok && lhs_count == 2 && rhs_arguments.count == 1 &&
 			    (o.mode == Addressing_MapIndex || o.mode == Addressing_OptionalOk || o.mode == Addressing_OptionalOkPtr)) {
 				Ast *expr = unparen_expr(o.expr);
 
@@ -5966,14 +5970,364 @@ gb_internal CallArgumentError check_order_of_call_arguments(CheckerContext *c, T
 }
 
 
+gb_internal CallArgumentData check_call_arguments_single_procedure(CheckerContext *c, Operand *operand, Ast *call) {
+	Type *proc_type = nullptr;
+
+	CallArgumentData data = {};
+	data.result_type = t_invalid;
+
+
+	GB_ASSERT(operand->mode != Addressing_ProcGroup);
+
+	proc_type = base_type(operand->type);
+
+	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);
+
+		return data;
+	}
+
+	if (is_type_polymorphic(proc_type)) {
+		error(call, "Polymorphic procedures not yet supported");
+		return data;
+	}
+
+	TypeProc *pt = &proc_type->Proc;
+
+	TEMPORARY_ALLOCATOR_GUARD();
+	ast_node(ce, CallExpr, call);
+
+	bool any_failure = false;
+
+	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;
+		}
+	}
+	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);
+
+	defer (array_free(&positional_operands));
+	defer (array_free(&variadic_operands));
+
+	isize positional_operand_count = 0;
+
+	auto visited = slice_make<bool>(temporary_allocator(), pt->param_count);
+
+	if (positional_args.count > 0) {
+		isize lhs_count = -1;
+		bool is_variadic = false;
+		Entity **lhs = populate_proc_parameter_list(c, proc_type, &lhs_count, &is_variadic);
+		check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None);
+
+		if (!is_variadic) {
+			if (pt->param_count < positional_operands.count) {
+				char const *err_fmt = "Too many arguments for '%s', expected a maximum of %td arguments, got %td";
+				gbString c = expr_to_string(ce->proc);
+				error(call, err_fmt, c, pt->param_count, positional_operands.count);
+				gb_string_free(c);
+				return data;
+			}
+		}
+
+		for (Operand const &o : positional_operands) {
+			check_no_copy_assignment(o, str_lit("procedure call expression"));
+		}
+
+		positional_operand_count = gb_min(pt->param_count, positional_operands.count);
+		if (is_variadic) {
+			positional_operand_count = gb_min(pt->variadic_index, positional_operand_count);
+		}
+
+		for (isize i = 0; i < positional_operand_count; i++) {
+			visited[i] = true;
+		}
+		if (is_variadic) {
+			visited[pt->variadic_index] = true;
+		}
+
+		for (isize i = positional_operand_count; i < positional_operands.count; i++) {
+			array_add(&variadic_operands, positional_operands[i]);
+		}
+		if (vari_expand) {
+			if (variadic_operands.count > 1) {
+				error(variadic_operands[1].expr, "Unexpected position arguments after variadic expansion with '..'");
+				return data;
+			}
+		}
+
+		array_resize(&positional_operands, positional_operand_count);
+	}
+
+	isize param_count = 0;
+	isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(proc_type, &param_count);
+
+	if (named_args.count == 0 && positional_operand_count < param_count_excluding_defaults) {
+		char const *err_fmt = "Too few arguments for '%s', expected a minimum of %td arguments, got %td";
+		gbString c = expr_to_string(ce->proc);
+		error(call, err_fmt, c, param_count_excluding_defaults, positional_operands.count);
+		gb_string_free(c);
+		return data;
+	}
+
+	for_array(i, positional_operands) {
+		Operand &o = positional_operands[i];
+		isize param_index = i;
+
+		Entity *e = pt->params->Tuple.variables[param_index];
+		GB_ASSERT(e->kind == Entity_Variable);
+
+		check_assignment(c, &o, e->type, str_lit("procedure call expression"));
+	}
+
+	if (pt->variadic) {
+		isize param_index = pt->variadic_index;
+
+		Entity *e = pt->params->Tuple.variables[param_index];
+		GB_ASSERT(e->kind == Entity_Variable);
+		GB_ASSERT(e->flags & EntityFlag_Ellipsis);
+		GB_ASSERT(is_type_slice(e->type));
+
+		if (vari_expand) {
+			if (variadic_operands.count != 0) {
+				GB_ASSERT(variadic_operands.count == 1);
+				check_assignment(c, &variadic_operands[0], e->type, str_lit("procedure call expression"));
+			}
+		} else {
+			Type *elem = e->type->Slice.elem;
+			for (Operand &o : variadic_operands) {
+				check_assignment(c, &o, elem, str_lit("procedure call expression"));
+			}
+		}
+	}
+
+	array_resize(&positional_operands, pt->param_count);
+
+	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);
+			any_failure = true;
+			gb_string_free(expr_str);
+			continue;
+		}
+		String name = fv->field->Ident.token.string;
+		isize param_index = lookup_procedure_parameter(pt, name);
+		if (param_index < 0) {
+			error(arg, "No parameter named '%.*s' for this procedure type", LIT(name));
+			any_failure = true;
+			continue;
+		}
+
+		bool prev_visited = visited[param_index];
+		visited[param_index] = true;
+
+		if (prev_visited) {
+			error(arg, "Duplicate parameter named '%.*s' in procedure call", LIT(name));
+		}
+
+		Entity *e = pt->params->Tuple.variables[param_index];
+		GB_ASSERT(e->kind == Entity_Variable);
+
+		Operand o = {};
+		check_expr_with_type_hint(c, &o, fv->value, e->type);
+		if (o.mode == Addressing_Invalid) {
+			any_failure = true;
+		}
+
+		check_assignment(c, &o, e->type, str_lit("procedure call expression"));
+		check_no_copy_assignment(o, str_lit("procedure call expression"));
+
+		if (!prev_visited) {
+			positional_operands[param_index] = o;
+		}
+	}
+
+	for_array(i, visited) {
+		if (!visited[i]) {
+			if (pt->variadic && i == pt->variadic_index) {
+				visited[i] = true;
+				continue;
+			}
+
+			Entity *e = pt->params->Tuple.variables[i];
+			if (e->kind == Entity_Variable) {
+				if (has_parameter_value(e->Variable.param_value)) {
+					visited[i] = true;
+					continue;
+				}
+			}
+			gbString t = type_to_string(e->type);
+			error(call, "Missing parameter of type '%s' at index %td", t, i);
+			gb_string_free(t);
+		}
+	}
+
+	if (any_failure) {
+		return data;
+	}
+
+
+	auto ordered_args = gb_alloc_item(permanent_allocator(), AstOrderedArgs);
+	ordered_args->positional = positional_args;
+	ordered_args->named = named_args;
+
+	ce->ordered_args = ordered_args;
+
+	data.result_type = pt->results;
+	return data;
+#if 0
+
+
+
+	/*
+	auto visited = slice_make<bool>(temporary_allocator(), pt->param_count);
+	auto ordered_values = slice_make<ParameterValue>(temporary_allocator(), pt->param_count);
+
+
+	for_array(i, ce->args) {
+		Ast *arg = ce->args[i];
+
+		isize param_index = i;
+		String param_name = {};
+
+		if (arg->kind == Ast_FieldValue) {
+			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);
+				any_failure = true;
+				gb_string_free(expr_str);
+				continue;
+			}
+			param_name = fv->field->Ident.token.string;
+			param_index = lookup_procedure_parameter(pt, param_name);
+			if (param_index < 0) {
+				error(arg, "No parameter named '%.*s' for this procedure type", LIT(param_name));
+				any_failure = true;
+				continue;
+			}
+			arg = fv->value;
+		} else {
+			if (param_index >= pt->param_count) {
+				error(arg, "Too many parameters in procedure call, expected a maximum of %td, got %td", pt->param_count, ce->args.count);
+				any_failure = true;
+				break;
+			}
+
+			param_name = pt->params->Tuple.variables[param_index]->token.string;
+		}
+		if (visited[param_index]) {
+			error(arg, "Duplicate parameter '%.*s' in procedure call", LIT(param_name));
+			any_failure = true;
+		} else {
+			ParameterValue value = {};
+			value.kind = ParameterValue_Value;
+			value.original_ast_expr = arg;
+			value.ast_value = arg;
+			ordered_values[param_index] = value;
+		}
+		visited[param_index] = true;
+	}
+
+	for (isize i = 0; i < pt->param_count; i++) {
+		if (!visited[i]) {
+			Entity *e = pt->params->Tuple.variables[i];
+			if (e->kind == Entity_Variable &&
+			    has_parameter_value(e->Variable.param_value)) {
+				ordered_values[i] = e->Variable.param_value;
+				visited[i] = true;
+			}
+		}
+	}
+
+	for (isize i = 0; i < visited.count; i++) {
+		if (!visited[i]) {
+			Entity *e = pt->params->Tuple.variables[i];
+			gbString t = type_to_string(e->type);
+			defer (gb_string_free(t));
+			if (is_blank_ident(e->token)) {
+				error(call, "Missing parameter of type '%s' at index %td", t, i);
+			} else {
+				error(call, "Missing parameter '%.*s' of type '%s'", LIT(e->token.string), t, i);
+			}
+			return data;
+		}
+	}
+	*/
+	if (any_failure) {
+		return data;
+	}
+
+	error(call, "TODO: Implement check_call_arguments_single_procedure for %s", type_to_string(proc_type));
+
+	Array<Operand> operands = {};
+	defer (array_free(&operands));
+
+	Ast *ident = unparen_expr(operand->expr);
+	while (ident->kind == Ast_SelectorExpr) {
+		Ast *s = ident->SelectorExpr.selector;
+		ident = unparen_expr(s);
+	}
+
+	Entity *e = entity_of_node(ident);
+
+	CallArgumentError err = check_call_arguments_internal(c, call, proc_type, e, 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;
+#endif
+}
+
+
 gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Ast *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);
+	}
+
 	ast_node(ce, CallExpr, call);
 
 	CallArgumentCheckerType *call_checker = check_call_arguments_internal;
 	Array<Operand> operands = {};
 	defer (array_free(&operands));
 
-
 	Type *result_type = t_invalid;
 	Type *proc_type = base_type(operand->type);
 
@@ -6102,7 +6456,7 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op
 	}
 
 	for (Operand const &o : operands) {
-		check_no_copy_assignment(o, str_lit("call expression"));
+		check_no_copy_assignment(o, str_lit("procedure call expression"));
 	}
 
 	if (operand->mode == Addressing_ProcGroup) {
@@ -6861,6 +7215,7 @@ gb_internal bool check_call_parameter_mixture(Operand *operand, Slice<Ast *> con
 				if (was_named && arg->kind != Ast_FieldValue) {
 					error(arg, "Non-named parameter is not allowed to follow named parameter i.e. 'field = value' in a %s", context);
 					failure = true;
+					break;
 				}
 				was_named = was_named || arg->kind == Ast_FieldValue;
 			}
@@ -7034,8 +7389,11 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
 		return builtin_procs[id].kind;
 	}
 
-	// CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("procedure call", true);
-	CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("procedure call");
+	if (operand->mode == Addressing_ProcGroup) {
+		CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("procedure group call");
+	} else {
+		CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("procedure call", true);
+	}
 
 	Entity *initial_entity = entity_of_node(operand->expr);
 

+ 119 - 0
src/llvm_backend_proc.cpp

@@ -3145,6 +3145,118 @@ gb_internal lbValue lb_build_call_expr(lbProcedure *p, Ast *expr) {
 	}
 	return res;
 }
+
+gb_internal void lb_add_values_to_array(lbProcedure *p, Array<lbValue> *args, lbValue value) {
+	if (is_type_tuple(value.type)) {
+		for_array(i, value.type->Tuple.variables) {
+			lbValue sub_value = lb_emit_struct_ev(p, value, cast(i32)i);
+			array_add(args, sub_value);
+		}
+	} else {
+		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) {
+	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) {
+		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);
+			if (variadic.count != 0) {
+				// variadic call argument generation
+				Type *slice_type = e->type;
+				GB_ASSERT(slice_type->kind == Type_Slice);
+				if (vari_expand) {
+					GB_ASSERT(variadic.count == 1);
+					variadic_args = lb_build_expr(p, variadic[0]);
+					variadic_args = lb_emit_conv(p, variadic_args, slice_type);
+				} else {
+					Type *elem_type = slice_type->Slice.elem;
+
+					auto var_args = array_make<lbValue>(heap_allocator(), 0, variadic.count);
+					defer (array_free(&var_args));
+					for (Ast *var_arg : variadic) {
+						lbValue v = lb_build_expr(p, var_arg);
+						lb_add_values_to_array(p, &var_args, v);
+					}
+					isize slice_len = var_args.count;
+					if (slice_len > 0) {
+						lbAddr slice = lb_add_local_generated(p, slice_type, true);
+						lbAddr base_array = lb_add_local_generated(p, alloc_type_array(elem_type, slice_len), true);
+
+						for (isize i = 0; i < var_args.count; i++) {
+							lbValue addr = lb_emit_array_epi(p, base_array.addr, cast(i32)i);
+							lbValue var_arg = var_args[i];
+							var_arg = lb_emit_conv(p, var_arg, elem_type);
+							lb_emit_store(p, addr, var_arg);
+						}
+
+						lbValue base_elem = lb_emit_array_epi(p, base_array.addr, 0);
+						lbValue len = lb_const_int(p->module, t_int, slice_len);
+						lb_fill_slice(p, slice, base_elem, len);
+
+						variadic_args = lb_addr_load(p, slice);
+					}
+				}
+			}
+			array_add(&args, variadic_args);
+
+			break;
+		} else {
+			lbValue value = lb_build_expr(p, ordered_args->positional[i]);
+			lb_add_values_to_array(p, &args, value);
+		}
+	}
+
+	array_resize(&args, pt->param_count);
+
+	for (Ast *arg : ordered_args->named) {
+		ast_node(fv, FieldValue, arg);
+		GB_ASSERT(fv->field->kind == Ast_Ident);
+		String name = fv->field->Ident.token.string;
+		gb_unused(name);
+		isize param_index = lookup_procedure_parameter(pt, name);
+		GB_ASSERT(param_index >= 0);
+
+		lbValue value = lb_build_expr(p, fv->value);
+		GB_ASSERT(!is_type_tuple(value.type));
+		args[param_index] = value;
+	}
+
+	TokenPos pos = ast_token(ce->proc).pos;
+
+	for_array(i, args) {
+		Entity *e = pt->params->Tuple.variables[i];
+		lbValue arg = args[i];
+		if (arg.value == nullptr) {
+			switch (e->kind) {
+			case Entity_TypeName:
+				args[i] = lb_const_nil(p->module, e->type);
+				break;
+			case Entity_Variable:
+				args[i] = lb_handle_param_value(p, e->type, e->Variable.param_value, pos);
+				break;
+			case Entity_Constant:
+				GB_PANIC("TODO constant parameter");
+				break;
+			}
+		} else {
+			args[i] = lb_emit_conv(p, arg, e->type);
+		}
+	}
+
+
+	return lb_emit_call(p, value, args, ce->inlining);
+}
+
 gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
 	lbModule *m = p->module;
 
@@ -3219,6 +3331,10 @@ 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 (is_call_expr_field_value(ce)) {
 		auto args = array_make<lbValue>(permanent_allocator(), pt->param_count);
 
@@ -3274,6 +3390,9 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
 	isize arg_count = 0;
 	for_array(i, ce->args) {
 		Ast *arg = ce->args[i];
+		if (arg->kind == Ast_FieldValue) {
+			arg = arg->FieldValue.value;
+		}
 		TypeAndValue tav = type_and_value_of_expr(arg);
 		GB_ASSERT_MSG(tav.mode != Addressing_Invalid, "%s %s %d", expr_to_string(arg), expr_to_string(expr), tav.mode);
 		GB_ASSERT_MSG(tav.mode != Addressing_ProcGroup, "%s", expr_to_string(arg));

+ 6 - 0
src/parser.hpp

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

+ 3 - 3
src/parser_pos.cpp

@@ -37,9 +37,9 @@ gb_internal Token ast_token(Ast *node) {
 			return ast_token(node->ImplicitSelectorExpr.selector);
 		}
 		return node->ImplicitSelectorExpr.token;
-	case Ast_IndexExpr:          return node->IndexExpr.open;
-	case Ast_MatrixIndexExpr:    return node->MatrixIndexExpr.open;
-	case Ast_SliceExpr:          return node->SliceExpr.open;
+	case Ast_IndexExpr:          return ast_token(node->IndexExpr.expr);
+	case Ast_MatrixIndexExpr:    return ast_token(node->MatrixIndexExpr.expr);
+	case Ast_SliceExpr:          return ast_token(node->SliceExpr.expr);
 	case Ast_Ellipsis:           return node->Ellipsis.token;
 	case Ast_FieldValue:
 		if (node->FieldValue.field) {