diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b9d5e3108..c56f1fe15 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6814,11 +6814,21 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A } if (!context_allocator_error) { - ordered_operands[i].mode = Addressing_Value; - ordered_operands[i].type = e->type; - if (e->Variable.param_value.kind == ParameterValue_Nil) - ordered_operands[i].type = t_untyped_nil; - ordered_operands[i].expr = e->Variable.param_value.original_ast_expr; + if (is_type_polymorphic(e->type) && e->Variable.param_value.kind == ParameterValue_Constant) { + // NOTE(bill): The parameter type is still polymorphic, so a constant default (e.g. `y: T = 0`) cannot be typed as '$T' here. + // Defer it like an untyped argument: determination resolves the parameter from the other arguments, + // then the default is materialized against the concrete type (reporting a clear error if it does not fit). + ordered_operands[i].mode = Addressing_Invalid; + ordered_operands[i].type = t_invalid; + ordered_operands[i].expr = e->Variable.param_value.original_ast_expr; + ordered_operands[i].deferred_untyped_arg = true; + } else { + ordered_operands[i].mode = Addressing_Value; + ordered_operands[i].type = e->type; + if (e->Variable.param_value.kind == ParameterValue_Nil) + ordered_operands[i].type = t_untyped_nil; + ordered_operands[i].expr = e->Variable.param_value.original_ast_expr; + } dummy_argument_count += 1; score += assign_score_function(1); diff --git a/src/check_type.cpp b/src/check_type.cpp index b37f50ae0..968281b07 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -3108,6 +3108,12 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para if (default_value != nullptr) { if (type_expr != nullptr && type_expr->kind == Ast_TypeidType) { error(type_expr, "A type parameter may not have a default value"); + } else if (is_type_polymorphic(type)) { + // NOTE(bill): The parameter type is still polymorphic (`$T`, `[N]$T`, ...), + // so the default value cannot be checked against it yet. + // Evaluate it as a constant now without the assignment check. + // It is validated against the concrete parameter type when the procedure is instantiated (which re-runs this with a non-polymorphic `type`). + param_value = handle_parameter_value(ctx, nullptr, nullptr, default_value, true); } else { param_value = handle_parameter_value(ctx, type, nullptr, default_value, true); }