Browse Source

Correct `check_call_arguments_new_and_improved` logic

gingerBill 2 years ago
parent
commit
26e06ba6a6
4 changed files with 180 additions and 100 deletions
  1. 107 32
      src/check_expr.cpp
  2. 7 1
      src/check_stmt.cpp
  3. 66 65
      src/check_type.cpp
  4. 0 2
      src/types.cpp

+ 107 - 32
src/check_expr.cpp

@@ -34,6 +34,7 @@ gb_global char const *CallArgumentError_strings[CallArgumentError_MAX] = {
 	"ParameterMissing",
 	"DuplicateParameter",
 	"NoneConstantParameter",
+	"OutOfOrderParameters",
 };
 
 
@@ -95,7 +96,7 @@ gb_internal void     check_stmt                     (CheckerContext *c, Ast *nod
 gb_internal void     check_stmt_list                (CheckerContext *c, Slice<Ast *> const &stmts, u32 flags);
 gb_internal void     check_init_constant            (CheckerContext *c, Entity *e, Operand *operand);
 gb_internal bool     check_representable_as_constant(CheckerContext *c, ExactValue in_value, Type *type, ExactValue *out_value);
-gb_internal bool     check_procedure_type           (CheckerContext *c, Type *type, Ast *proc_type_node, Array<Operand> *operands = nullptr);
+gb_internal bool     check_procedure_type           (CheckerContext *c, Type *type, Ast *proc_type_node, Array<Operand> const *operands = nullptr);
 gb_internal void     check_struct_type              (CheckerContext *c, Type *struct_type, Ast *node, Array<Operand> *poly_operands,
                                                      Type *named_type = nullptr, Type *original_type_for_poly = nullptr);
 gb_internal void     check_union_type               (CheckerContext *c, Type *union_type, Ast *node, Array<Operand> *poly_operands,
@@ -338,7 +339,7 @@ gb_internal void check_scope_decls(CheckerContext *c, Slice<Ast *> const &nodes,
 }
 
 gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_entity, Type *type,
-                                                        Array<Operand> *param_operands, Ast *poly_def_node, PolyProcData *poly_proc_data) {
+                                                        Array<Operand> const *param_operands, Ast *poly_def_node, PolyProcData *poly_proc_data) {
 	///////////////////////////////////////////////////////////////////////////////
 	//                                                                           //
 	// TODO CLEANUP(bill): This procedure is very messy and hacky. Clean this!!! //
@@ -605,7 +606,7 @@ gb_internal bool check_polymorphic_procedure_assignment(CheckerContext *c, Opera
 	return find_or_generate_polymorphic_procedure(c, base_entity, type, nullptr, poly_def_node, poly_proc_data);
 }
 
-gb_internal bool find_or_generate_polymorphic_procedure_from_parameters(CheckerContext *c, Entity *base_entity, Array<Operand> *operands, Ast *poly_def_node, PolyProcData *poly_proc_data) {
+gb_internal bool find_or_generate_polymorphic_procedure_from_parameters(CheckerContext *c, Entity *base_entity, Array<Operand> const *operands, Ast *poly_def_node, PolyProcData *poly_proc_data) {
 	return find_or_generate_polymorphic_procedure(c, base_entity, nullptr, operands, poly_def_node, poly_proc_data);
 }
 
@@ -5337,6 +5338,8 @@ gb_internal isize lookup_procedure_parameter(Type *type, String const &parameter
 
 
 gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
+	TEMPORARY_ALLOCATOR_GUARD();
+
 	CallArgumentError err = CallArgumentError_None;
 
 	ast_node(ce, CallExpr, call);
@@ -5371,8 +5374,19 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 	}
 
 	if (ce->split_args) {
-		auto ordered_operands = array_make<Operand>(temporary_allocator(), pt->param_count);
 		auto visited = slice_make<bool>(temporary_allocator(), pt->param_count);
+		auto ordered_operands = array_make<Operand>(temporary_allocator(), pt->param_count);
+		defer ({
+			for (Operand const &o : ordered_operands) {
+				if (o.expr != nullptr) {
+					call->viral_state_flags |= o.expr->viral_state_flags;
+				}
+			}
+		});
+
+		if (check_order_of_call_arguments(c, proc_type, call, show_error)) {
+			return CallArgumentError_OutOfOrderParameters;
+		}
 
 		isize positional_operand_count = positional_operands.count;
 		if (variadic) {
@@ -5449,11 +5463,36 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 				err = CallArgumentError_DuplicateParameter;
 			} else {
 				visited[pt->variadic_index] = true;
+
+				if (vari_expand) {
+					GB_ASSERT(variadic_operands.count != 0);
+					ordered_operands[pt->variadic_index] = variadic_operands[0];
+				} else {
+					AstFile *f = call->file();
+
+					// HACK(bill): this is an awful hack
+					Operand o = {};
+					o.mode = Addressing_Value;
+					o.expr = ast_ident(f, make_token_ident("nil"));
+					o.expr->Ident.token.pos = ast_token(call).pos;
+					if (variadic_operands.count != 0) {
+						o.expr->Ident.token.pos = ast_token(variadic_operands[0].expr).pos;
+
+						Entity *vt = pt->params->Tuple.variables[pt->variadic_index];
+						if (is_type_polymorphic(vt->type)) {
+							o.type = alloc_type_slice(default_type(variadic_operands[0].type));
+						} else {
+							o.type = vt->type;
+						}
+					} else {
+						o.type = t_untyped_nil;
+					}
+					ordered_operands[pt->variadic_index] = o;
+				}
 			}
 
 		}
 
-
 		for (Operand const &o : ordered_operands) {
 			if (o.mode != Addressing_Invalid) {
 				check_no_copy_assignment(o, str_lit("procedure call expression"));
@@ -5508,44 +5547,23 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 					gen_entity = poly_proc_data.gen_entity;
 					Type *gept = base_type(gen_entity->type);
 					GB_ASSERT(is_type_proc(gept));
-					proc_type = gept;
+					final_proc_type = gen_entity->type;
 					pt = &gept->Proc;
+
 				} else {
 					err = CallArgumentError_WrongTypes;
 				}
 			}
 
 			for (isize i = 0; i < pt->param_count; i++) {
-				Entity *e = pt->params->Tuple.variables[i];
 				Operand *o = &ordered_operands[i];
-				bool param_is_variadic = pt->variadic && pt->variadic_index == i;
-
 				if (o->mode == Addressing_Invalid) {
-					if (param_is_variadic) {
-						Type *slice = e->type;
-						GB_ASSERT(is_type_slice(slice));
-						Type *elem = base_type(slice)->Slice.elem;
-
-						if (variadic_operands.count == 0) {
-							if (is_type_polymorphic(elem)) {
-								error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input");
-								err = CallArgumentError_AmbiguousPolymorphicVariadic;
-								return err;
-							}
-						} else {
-							if (vari_expand) {
-								GB_ASSERT(variadic_operands.count == 1);
-								check_assignment(c, &variadic_operands[0], slice, str_lit("variadic expanded argument"));
-							} else {
-								for (Operand &vo : variadic_operands) {
-									check_assignment(c, &vo, elem, str_lit("variadic argument"));
-								}
-							}
-						}
-					}
 					continue;
 				}
 
+				Entity *e = pt->params->Tuple.variables[i];
+				bool param_is_variadic = pt->variadic && pt->variadic_index == i;
+
 				if (e->kind == Entity_TypeName) {
 					GB_ASSERT(pt->is_polymorphic);
 					if (o->mode != Addressing_Type) {
@@ -5563,6 +5581,11 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 					i64 s = 0;
 					if (!check_is_assignable_to_with_score(c, o, e->type, &s, param_is_variadic)) {
 						bool ok = false;
+						if (e->flags & EntityFlag_AnyInt) {
+							if (is_type_integer(e->type)) {
+								ok = check_is_castable_to(c, o, e->type);
+							}
+						}
 						if (ok) {
 							s = assign_score_function(MAXIMUM_TYPE_DISTANCE);
 						} else {
@@ -5589,6 +5612,57 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 				if (o->mode == Addressing_Type && is_type_typeid(e->type)) {
 					add_type_info_type(c, o->type);
 					add_type_and_value(c, o->expr, Addressing_Value, e->type, exact_value_typeid(o->type));
+				} else if (show_error && is_type_untyped(o->type)) {
+					update_untyped_expr_type(c, o->expr, e->type, true);
+				}
+			}
+		}
+
+		if (variadic) {
+			Type *slice = pt->params->Tuple.variables[pt->variadic_index]->type;
+			GB_ASSERT(is_type_slice(slice));
+			Type *elem = base_type(slice)->Slice.elem;
+			Type *t = elem;
+
+			if (is_type_polymorphic(t)) {
+				error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input %s", type_to_string(final_proc_type));
+				err = CallArgumentError_AmbiguousPolymorphicVariadic;
+			}
+
+			for_array(operand_index, variadic_operands) {
+				Operand &o = variadic_operands[operand_index];
+				if (vari_expand) {
+					t = slice;
+					if (operand_index > 0) {
+						if (show_error) {
+							error(o.expr, "'..' in a variadic procedure can only have one variadic argument at the end");
+						}
+						if (data) {
+							data->score = score;
+							data->result_type = final_proc_type->Proc.results;
+							data->gen_entity = gen_entity;
+						}
+						return CallArgumentError_MultipleVariadicExpand;
+					}
+				}
+				i64 s = 0;
+				if (!check_is_assignable_to_with_score(c, &o, t, &s, true)) {
+					if (show_error) {
+						check_assignment(c, &o, t, str_lit("variadic argument"));
+					}
+					err = CallArgumentError_WrongTypes;
+				} else if (show_error) {
+					check_assignment(c, &o, t, str_lit("variadic argument"));
+				}
+				score += s;
+				if (is_type_any(elem)) {
+					add_type_info_type(c, o.type);
+				}
+				if (o.mode == Addressing_Type && is_type_typeid(t)) {
+					add_type_info_type(c, o.type);
+					add_type_and_value(c, o.expr, Addressing_Value, t, exact_value_typeid(o.type));
+				} else if (show_error && is_type_untyped(o.type)) {
+					update_untyped_expr_type(c, o.expr, t, true);
 				}
 			}
 		}
@@ -6756,6 +6830,7 @@ gb_internal CallArgumentData check_call_arguments_new_and_improved_proc_group(Ch
 		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);
+			add_type_and_value(c, operand->expr, operand->mode, entity_to_use->type, operand->value);
 		}
 
 		if (data.gen_entity != nullptr) {
@@ -6950,7 +7025,7 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op
 				break;
 			}
 		}
-		if (all_non_poly) {
+		if (true) {
 			return check_call_arguments_new_and_improved(c, operand, call);
 		}
 	} else {

+ 7 - 1
src/check_stmt.cpp

@@ -2207,7 +2207,13 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) {
 	} else if (operands.count != result_count) {
 		// Ignore error message as it has most likely already been reported
 		if (all_operands_valid(operands)) {
-			error(node, "Expected %td return values, got %td", result_count, operands.count);
+			if (operands.count == 1) {
+				gbString t = type_to_string(operands[0].type);
+				error(node, "Expected %td return values, got %td (%s)", result_count, operands.count, t);
+				gb_string_free(t);
+			} else {
+				error(node, "Expected %td return values, got %td", result_count, operands.count);
+			}
 		}
 	} else {
 		for (isize i = 0; i < result_count; i++) {

+ 66 - 65
src/check_type.cpp

@@ -1416,7 +1416,7 @@ gb_internal ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_
 }
 
 
-gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is_variadic_, isize *variadic_index_, bool *success_, isize *specialization_count_, Array<Operand> *operands) {
+gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is_variadic_, isize *variadic_index_, bool *success_, isize *specialization_count_, Array<Operand> const *operands) {
 	if (_params == nullptr) {
 		return nullptr;
 	}
@@ -1664,81 +1664,82 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
 				ExactValue poly_const = {};
 
 				if (operands != nullptr && variables.count < operands->count) {
-
 					Operand op = (*operands)[variables.count];
-					if (op.expr == nullptr) {
-						// NOTE(bill): 2019-03-30
-						// This is just to add the error message to determine_type_from_polymorphic which
-						// depends on valid position information
-						op.expr = _params;
-						op.mode = Addressing_Invalid;
-						op.type = t_invalid;
-					}
-					if (is_type_polymorphic_type) {
-						type = determine_type_from_polymorphic(ctx, type, op);
-						if (type == t_invalid) {
-							success = false;
-						} else if (!ctx->no_polymorphic_errors) {
-							// NOTE(bill): The type should be determined now and thus, no need to determine the type any more
-							is_type_polymorphic_type = false;
-							Entity *proc_entity = entity_from_expr(op.expr);
-							if ((proc_entity != nullptr) && (op.value.kind == ExactValue_Procedure)) {
-								if (is_type_polymorphic(proc_entity->type, false)) {
-									error(op.expr, "Cannot determine complete type of partial polymorphic procedure");
+					{
+						if (op.expr == nullptr) {
+							// NOTE(bill): 2019-03-30
+							// This is just to add the error message to determine_type_from_polymorphic which
+							// depends on valid position information
+							op.expr = _params;
+							op.mode = Addressing_Invalid;
+							op.type = t_invalid;
+						}
+						if (is_type_polymorphic_type) {
+							type = determine_type_from_polymorphic(ctx, type, op);
+							if (type == t_invalid) {
+								success = false;
+							} else if (!ctx->no_polymorphic_errors) {
+								// NOTE(bill): The type should be determined now and thus, no need to determine the type any more
+								is_type_polymorphic_type = false;
+								Entity *proc_entity = entity_from_expr(op.expr);
+								if ((proc_entity != nullptr) && (op.value.kind == ExactValue_Procedure)) {
+									if (is_type_polymorphic(proc_entity->type, false)) {
+										error(op.expr, "Cannot determine complete type of partial polymorphic procedure");
+									}
 								}
 							}
 						}
-					}
-					if (is_poly_name) {
-						bool valid = false;
-						if (is_type_proc(op.type)) {
-							Ast *expr = unparen_expr(op.expr);
-							Entity *proc_entity = entity_from_expr(expr);
-							if (proc_entity) {
-								poly_const = exact_value_procedure(proc_entity->identifier.load() ? proc_entity->identifier.load() : op.expr);
-								valid = true;
-							} else if (expr->kind == Ast_ProcLit) {
-								poly_const = exact_value_procedure(expr);
-								valid = true;
+						if (is_poly_name) {
+							bool valid = false;
+							if (is_type_proc(op.type)) {
+								Ast *expr = unparen_expr(op.expr);
+								Entity *proc_entity = entity_from_expr(expr);
+								if (proc_entity) {
+									poly_const = exact_value_procedure(proc_entity->identifier.load() ? proc_entity->identifier.load() : op.expr);
+									valid = true;
+								} else if (expr->kind == Ast_ProcLit) {
+									poly_const = exact_value_procedure(expr);
+									valid = true;
+								}
 							}
-						}
-						if (!valid) {
-							if (op.mode == Addressing_Constant) {
-								poly_const = op.value;
-							} else {
-								error(op.expr, "Expected a constant value for this polymorphic name parameter, got %s", expr_to_string(op.expr));
-								success = false;
+							if (!valid) {
+								if (op.mode == Addressing_Constant) {
+									poly_const = op.value;
+								} else {
+									error(op.expr, "Expected a constant value for this polymorphic name parameter, got %s", expr_to_string(op.expr));
+									success = false;
+								}
 							}
 						}
-					}
-					if (type != t_invalid && !check_is_assignable_to(ctx, &op, type)) {
-						bool ok = true;
-						if (p->flags&FieldFlag_any_int) {
-							if ((!is_type_integer(op.type) && !is_type_enum(op.type)) || (!is_type_integer(type) && !is_type_enum(type))) {
-								ok = false;
-							} else if (!check_is_castable_to(ctx, &op, type)) {
-								ok = false;
+						if (type != t_invalid && !check_is_assignable_to(ctx, &op, type)) {
+							bool ok = true;
+							if (p->flags&FieldFlag_any_int) {
+								if ((!is_type_integer(op.type) && !is_type_enum(op.type)) || (!is_type_integer(type) && !is_type_enum(type))) {
+									ok = false;
+								} else if (!check_is_castable_to(ctx, &op, type)) {
+									ok = false;
+								}
+							}
+							if (!ok) {
+								success = false;
+								#if 0
+									gbString got = type_to_string(op.type);
+									gbString expected = type_to_string(type);
+									error(op.expr, "Cannot assigned type to parameter, got type '%s', expected '%s'", got, expected);
+									gb_string_free(expected);
+									gb_string_free(got);
+								#endif
 							}
 						}
-						if (!ok) {
+
+						if (is_type_untyped(default_type(type))) {
+							gbString str = type_to_string(type);
+							error(op.expr, "Cannot determine type from the parameter, got '%s'", str);
+							gb_string_free(str);
 							success = false;
-							#if 0
-								gbString got = type_to_string(op.type);
-								gbString expected = type_to_string(type);
-								error(op.expr, "Cannot assigned type to parameter, got type '%s', expected '%s'", got, expected);
-								gb_string_free(expected);
-								gb_string_free(got);
-							#endif
+							type = t_invalid;
 						}
 					}
-
-					if (is_type_untyped(default_type(type))) {
-						gbString str = type_to_string(type);
-						error(op.expr, "Cannot determine type from the parameter, got '%s'", str);
-						gb_string_free(str);
-						success = false;
-						type = t_invalid;
-					}
 				}
 
 				if (p->flags&FieldFlag_no_alias) {
@@ -1967,7 +1968,7 @@ gb_internal Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_res
 
 
 // NOTE(bill): 'operands' is for generating non generic procedure type
-gb_internal bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, Array<Operand> *operands) {
+gb_internal bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, Array<Operand> const *operands) {
 	ast_node(pt, ProcType, proc_type_node);
 
 	if (ctx->polymorphic_scope == nullptr && ctx->allow_polymorphic_types) {

+ 0 - 2
src/types.cpp

@@ -2123,7 +2123,6 @@ gb_internal bool is_type_polymorphic(Type *t, bool or_specialized=false) {
 		if (t->Proc.is_polymorphic) {
 			return true;
 		}
-		#if 1
 		if (t->Proc.param_count > 0 &&
 		    is_type_polymorphic(t->Proc.params, or_specialized)) {
 			return true;
@@ -2132,7 +2131,6 @@ gb_internal bool is_type_polymorphic(Type *t, bool or_specialized=false) {
 		    is_type_polymorphic(t->Proc.results, or_specialized)) {
 			return true;
 		}
-		#endif
 		break;
 
 	case Type_Enum: