diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 02b54c80a..01e971ac7 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7338,6 +7338,7 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper Entity *e = params->variables[i]; if (e->kind == Entity_Constant) { check_expr_with_type_hint(c, &operands[i], fv->value, e->type); + continue; } } @@ -7371,9 +7372,22 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper TypeTuple *tuple = get_record_polymorphic_params(original_type); isize param_count = tuple->variables.count; + isize minimum_param_count = param_count; + for (minimum_param_count = tuple->variables.count-1; minimum_param_count >= 0; minimum_param_count--) { + Entity *e = tuple->variables[minimum_param_count]; + if (e->kind != Entity_Constant) { + break; + } + if (e->Constant.param_value.kind == ParameterValue_Invalid) { + break; + } + } Array ordered_operands = operands; - if (named_fields) { + if (!named_fields) { + ordered_operands = array_make(c->allocator, param_count); + array_copy(&ordered_operands, operands, 0); + } else { bool *visited = gb_alloc_array(c->allocator, bool, param_count); // LEAK(bill) @@ -7440,26 +7454,55 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper return err; } - if (param_count < ordered_operands.count) { - error(call, "Too many polymorphic type arguments, expected %td, got %td", param_count, ordered_operands.count); - err = CallArgumentError_TooManyArguments; - } else if (param_count > ordered_operands.count) { - error(call, "Too few polymorphic type arguments, expected %td, got %td", param_count, ordered_operands.count); - err = CallArgumentError_TooFewArguments; + if (minimum_param_count != param_count) { + if (param_count < ordered_operands.count) { + error(call, "Too many polymorphic type arguments, expected a maximum of %td, got %td", param_count, ordered_operands.count); + err = CallArgumentError_TooManyArguments; + } else if (minimum_param_count > ordered_operands.count) { + error(call, "Too few polymorphic type arguments, expected a minimum of %td, got %td", minimum_param_count, ordered_operands.count); + err = CallArgumentError_TooFewArguments; + } + } else { + if (param_count < ordered_operands.count) { + error(call, "Too many polymorphic type arguments, expected %td, got %td", param_count, ordered_operands.count); + err = CallArgumentError_TooManyArguments; + } else if (param_count > ordered_operands.count) { + error(call, "Too few polymorphic type arguments, expected %td, got %td", param_count, ordered_operands.count); + err = CallArgumentError_TooFewArguments; + } } if (err != 0) { return err; } + if (minimum_param_count != param_count) { + isize missing_count = 0; + // NOTE(bill): Replace missing operands with the default values (if possible) + for_array(i, ordered_operands) { + Operand *o = &ordered_operands[i]; + if (o->expr == nullptr) { + Entity *e = tuple->variables[i]; + if (e->kind == Entity_Constant) { + missing_count += 1; + o->mode = Addressing_Constant; + o->type = default_type(e->type); + o->expr = unparen_expr(e->Constant.param_value.original_ast_expr); + if (e->Constant.param_value.kind == ParameterValue_Constant) { + o->value = e->Constant.param_value.value; + } + } + } + } + } + i64 score = 0; for (isize i = 0; i < param_count; i++) { + Entity *e = tuple->variables[i]; Operand *o = &ordered_operands[i]; if (o->mode == Addressing_Invalid) { continue; } - Entity *e = tuple->variables[i]; - if (e->kind == Entity_TypeName) { if (o->mode != Addressing_Type) { if (show_error) { @@ -7800,7 +7843,7 @@ void check_expr_with_type_hint(CheckerContext *c, Operand *o, Ast *e, Type *t) { err_str = "used as a value"; break; case Addressing_Type: - err_str = "is not an expression"; + err_str = "is not an expression but a"; break; case Addressing_Builtin: err_str = "must be called"; diff --git a/src/check_type.cpp b/src/check_type.cpp index 2b9bcdb33..ace1ef898 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1,3 +1,4 @@ +ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type **out_type_, Ast *expr, bool allow_caller_location); void populate_using_array_index(CheckerContext *ctx, Ast *node, AstField *field, Type *t, String name, i32 idx) { t = base_type(t); @@ -408,32 +409,50 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array< } ast_node(p, Field, param); Ast *type_expr = p->type; + Ast *default_value = unparen_expr(p->default_value); Type *type = nullptr; bool is_type_param = false; bool is_type_polymorphic_type = false; - if (type_expr == nullptr) { + if (type_expr == nullptr && default_value == nullptr) { error(param, "Expected a type for this parameter"); continue; } - if (type_expr->kind == Ast_Ellipsis) { - type_expr = type_expr->Ellipsis.expr; - error(param, "A polymorphic parameter cannot be variadic"); - } - if (type_expr->kind == Ast_TypeidType) { - is_type_param = true; - Type *specialization = nullptr; - if (type_expr->TypeidType.specialization != nullptr) { - Ast *s = type_expr->TypeidType.specialization; - specialization = check_type(ctx, s); + + if (type_expr != nullptr) { + if (type_expr->kind == Ast_Ellipsis) { + type_expr = type_expr->Ellipsis.expr; + error(param, "A polymorphic parameter cannot be variadic"); } - type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization); - } else { - type = check_type(ctx, type_expr); - if (is_type_polymorphic(type)) { - is_type_polymorphic_type = true; + if (type_expr->kind == Ast_TypeidType) { + is_type_param = true; + Type *specialization = nullptr; + if (type_expr->TypeidType.specialization != nullptr) { + Ast *s = type_expr->TypeidType.specialization; + specialization = check_type(ctx, s); + } + type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization); + } else { + type = check_type(ctx, type_expr); + if (is_type_polymorphic(type)) { + is_type_polymorphic_type = true; + } } } + ParameterValue param_value = {}; + if (default_value != nullptr) { + Type *out_type = nullptr; + param_value = handle_parameter_value(ctx, type, &out_type, default_value, false); + if (type == nullptr && out_type != nullptr) { + type = out_type; + } + if (param_value.kind != ParameterValue_Constant && param_value.kind != ParameterValue_Nil) { + error(default_value, "Invalid parameter value"); + param_value = {}; + } + } + + if (type == nullptr) { error(params[i], "Invalid parameter type"); type = t_invalid; @@ -471,7 +490,14 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array< Token token = name->Ident.token; if (poly_operands != nullptr) { - Operand operand = (*poly_operands)[entities.count]; + Operand operand = {}; + operand.type = t_invalid; + if (entities.count < poly_operands->count) { + operand = (*poly_operands)[entities.count]; + } else if (param_value.kind != ParameterValue_Invalid) { + operand.mode = Addressing_Constant; + operand.value = param_value.value; + } if (is_type_param) { if (is_type_polymorphic(base_type(operand.type))) { is_polymorphic = true; @@ -486,6 +512,7 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array< } if (e == nullptr) { e = alloc_entity_constant(scope, token, operand.type, operand.value); + e->Constant.param_value = param_value; } } } else { @@ -493,7 +520,8 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array< e = alloc_entity_type_name(scope, token, type); e->TypeName.is_type_alias = true; } else { - e = alloc_entity_constant(scope, token, type, empty_exact_value); + e = alloc_entity_constant(scope, token, type, param_value.value); + e->Constant.param_value = param_value; } } @@ -599,29 +627,45 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Arraytype; + Ast *default_value = unparen_expr(p->default_value); Type *type = nullptr; bool is_type_param = false; bool is_type_polymorphic_type = false; - if (type_expr == nullptr) { + if (type_expr == nullptr && default_value == nullptr) { error(param, "Expected a type for this parameter"); continue; } - if (type_expr->kind == Ast_Ellipsis) { - type_expr = type_expr->Ellipsis.expr; - error(param, "A polymorphic parameter cannot be variadic"); - } - if (type_expr->kind == Ast_TypeidType) { - is_type_param = true; - Type *specialization = nullptr; - if (type_expr->TypeidType.specialization != nullptr) { - Ast *s = type_expr->TypeidType.specialization; - specialization = check_type(ctx, s); + if (type_expr != nullptr) { + if (type_expr->kind == Ast_Ellipsis) { + type_expr = type_expr->Ellipsis.expr; + error(param, "A polymorphic parameter cannot be variadic"); } - type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization); - } else { - type = check_type(ctx, type_expr); - if (is_type_polymorphic(type)) { - is_type_polymorphic_type = true; + if (type_expr->kind == Ast_TypeidType) { + is_type_param = true; + Type *specialization = nullptr; + if (type_expr->TypeidType.specialization != nullptr) { + Ast *s = type_expr->TypeidType.specialization; + specialization = check_type(ctx, s); + } + type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization); + } else { + type = check_type(ctx, type_expr); + if (is_type_polymorphic(type)) { + is_type_polymorphic_type = true; + } + } + } + + ParameterValue param_value = {}; + if (default_value != nullptr) { + Type *out_type = nullptr; + param_value = handle_parameter_value(ctx, type, &out_type, default_value, false); + if (type == nullptr && out_type != nullptr) { + type = out_type; + } + if (param_value.kind != ParameterValue_Constant && param_value.kind != ParameterValue_Nil) { + error(default_value, "Invalid parameter value"); + param_value = {}; } } @@ -662,7 +706,14 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, ArrayIdent.token; if (poly_operands != nullptr) { - Operand operand = (*poly_operands)[entities.count]; + Operand operand = {}; + operand.type = t_invalid; + if (entities.count < poly_operands->count) { + operand = (*poly_operands)[entities.count]; + } else if (param_value.kind != ParameterValue_Invalid) { + operand.mode = Addressing_Constant; + operand.value = param_value.value; + } if (is_type_param) { GB_ASSERT(operand.mode == Addressing_Type || operand.mode == Addressing_Invalid); @@ -675,6 +726,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, ArrayConstant.param_value = param_value; } } else { if (is_type_param) { @@ -682,6 +734,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, ArrayTypeName.is_type_alias = true; } else { e = alloc_entity_constant(scope, token, type, empty_exact_value); + e->Constant.param_value = param_value; } } diff --git a/src/entity.cpp b/src/entity.cpp index 3d354b9c8..a9d598735 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -120,6 +120,7 @@ struct Entity { union { struct { ExactValue value; + ParameterValue param_value; } Constant; struct { Ast *init_expr; // only used for some variables within procedure bodies diff --git a/src/parser.cpp b/src/parser.cpp index 330d78263..794ff231d 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2122,7 +2122,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { if (allow_token(f, Token_OpenParen)) { isize param_count = 0; - polymorphic_params = parse_field_list(f, ¶m_count, 0, Token_CloseParen, false, true); + polymorphic_params = parse_field_list(f, ¶m_count, 0, Token_CloseParen, true, true); if (param_count == 0) { syntax_error(polymorphic_params, "Expected at least 1 polymorphic parameter"); polymorphic_params = nullptr; @@ -2203,7 +2203,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { if (allow_token(f, Token_OpenParen)) { isize param_count = 0; - polymorphic_params = parse_field_list(f, ¶m_count, 0, Token_CloseParen, false, true); + polymorphic_params = parse_field_list(f, ¶m_count, 0, Token_CloseParen, true, true); if (param_count == 0) { syntax_error(polymorphic_params, "Expected at least 1 polymorphic parametric"); polymorphic_params = nullptr;