diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 07fe9cbc2..a8453f0cb 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -255,13 +255,18 @@ i64 check_distance_between_types(Checker *c, Operand *operand, Type *type) { } +i64 assign_score_function(i64 distance) { + // TODO(bill): A decent score function + return gb_max(1000000 - distance*distance, 0); +} + + bool check_is_assignable_to_with_score(Checker *c, Operand *operand, Type *type, i64 *score_) { i64 score = 0; i64 distance = check_distance_between_types(c, operand, type); bool ok = distance >= 0; if (ok) { - // TODO(bill): A decent score function - score = gb_max(1000000 - distance*distance, 0); + score = assign_score_function(distance); } if (score_) *score_ = score; return ok; @@ -1051,7 +1056,22 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari } ast_node(p, Field, params[i]); AstNode *type_expr = p->type; - if (type_expr) { + Type *type = NULL; + AstNode *default_value = p->default_value; + ExactValue value = {}; + + if (type_expr == NULL) { + Operand o = {}; + check_expr(c, &o, default_value); + + if (o.mode != Addressing_Constant) { + error_node(default_value, "Default parameter must be a constant"); + } else { + value = o.value; + } + + type = default_type(o.type); + } else { if (type_expr->kind == AstNode_Ellipsis) { type_expr = type_expr->Ellipsis.expr; if (i+1 == params.count) { @@ -1061,28 +1081,49 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari } } - Type *type = check_type(c, type_expr); - if (p->flags&FieldFlag_no_alias) { - if (!is_type_pointer(type)) { - error_node(params[i], "`no_alias` can only be applied to fields of pointer type"); - p->flags &= ~FieldFlag_no_alias; // Remove the flag + type = check_type(c, type_expr); + + if (default_value != NULL) { + Operand o = {}; + check_expr(c, &o, default_value); + + if (o.mode != Addressing_Constant) { + error_node(default_value, "Default parameter must be a constant"); + } else { + value = o.value; } + + check_is_assignable_to(c, &o, type); } - for_array(j, p->names) { - AstNode *name = p->names[j]; - if (ast_node_expect(name, AstNode_Ident)) { - Entity *param = make_entity_param(c->allocator, scope, name->Ident, type, - (p->flags&FieldFlag_using) != 0, (p->flags&FieldFlag_immutable) != 0); - if (p->flags&FieldFlag_no_alias) { - param->flags |= EntityFlag_NoAlias; - } - if (p->flags&FieldFlag_immutable) { - param->Variable.is_immutable = true; - } - add_entity(c, scope, name, param); - variables[variable_index++] = param; + } + if (type == NULL) { + error_node(params[i], "Invalid parameter type"); + type = t_invalid; + } + + if (p->flags&FieldFlag_no_alias) { + if (!is_type_pointer(type)) { + error_node(params[i], "`no_alias` can only be applied to fields of pointer type"); + p->flags &= ~FieldFlag_no_alias; // Remove the flag + } + } + + for_array(j, p->names) { + AstNode *name = p->names[j]; + if (ast_node_expect(name, AstNode_Ident)) { + Entity *param = make_entity_param(c->allocator, scope, name->Ident, type, + (p->flags&FieldFlag_using) != 0, (p->flags&FieldFlag_immutable) != 0); + if (p->flags&FieldFlag_no_alias) { + param->flags |= EntityFlag_NoAlias; } + if (p->flags&FieldFlag_immutable) { + param->Variable.is_immutable = true; + } + param->Variable.default_value = value; + + add_entity(c, scope, name, param); + variables[variable_index++] = param; } } } @@ -4775,18 +4816,35 @@ typedef CALL_ARGUMENT_CHECKER(CallArgumentCheckerType); CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { ast_node(ce, CallExpr, call); isize param_count = 0; + isize param_count_excluding_defaults = 0; bool variadic = proc_type->Proc.variadic; bool vari_expand = (ce->ellipsis.pos.line != 0); i64 score = 0; bool show_error = show_error_mode == CallArgumentMode_ShowErrors; + TypeTuple *param_tuple = NULL; + if (proc_type->Proc.params != NULL) { - param_count = proc_type->Proc.params->Tuple.variable_count; + param_tuple = &proc_type->Proc.params->Tuple; + + param_count = param_tuple->variable_count; if (variadic) { param_count--; } } + param_count_excluding_defaults = param_count; + if (param_tuple != NULL) { + for (isize i = param_count-1; i >= 0; i--) { + Entity *e = param_tuple->variables[i]; + GB_ASSERT(e->kind == Entity_Variable); + if (e->Variable.default_value.kind == ExactValue_Invalid) { + break; + } + param_count_excluding_defaults--; + } + } + if (vari_expand && !variadic) { if (show_error) { error(ce->ellipsis, @@ -4797,13 +4855,13 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { return CallArgumentError_NonVariadicExpand; } - if (operands.count == 0 && param_count == 0) { + if (operands.count == 0 && param_count_excluding_defaults == 0) { if (score_) *score_ = score; return CallArgumentError_None; } i32 error_code = 0; - if (operands.count < param_count) { + if (operands.count < param_count_excluding_defaults) { error_code = -1; } else if (!variadic && operands.count > param_count) { error_code = +1; @@ -4818,7 +4876,7 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { if (show_error) { gbString proc_str = expr_to_string(ce->proc); - error_node(call, err_fmt, proc_str, param_count); + error_node(call, err_fmt, proc_str, param_count_excluding_defaults); gb_string_free(proc_str); } if (score_) *score_ = score; @@ -4828,9 +4886,9 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { CallArgumentError err = CallArgumentError_None; GB_ASSERT(proc_type->Proc.params != NULL); - Entity **sig_params = proc_type->Proc.params->Tuple.variables; + Entity **sig_params = param_tuple->variables; isize operand_index = 0; - for (; operand_index < param_count; operand_index++) { + for (; operand_index < param_count_excluding_defaults; operand_index++) { Type *t = sig_params[operand_index]->type; Operand o = operands[operand_index]; if (variadic) { @@ -4972,6 +5030,12 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) { if (e->token.string == "_") { continue; } + GB_ASSERT(e->kind == Entity_Variable); + if (e->Variable.default_value.kind != ExactValue_Invalid) { + score += assign_score_function(1); + continue; + } + if (show_error) { gbString str = type_to_string(e->type); error_node(call, "Parameter `%.*s` of type `%s` is missing in procedure call", diff --git a/src/entity.cpp b/src/entity.cpp index d8159d449..26e94dbf1 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -81,10 +81,11 @@ struct Entity { ExactValue value; } Constant; struct { - i32 field_index; - i32 field_src_index; - bool is_immutable; - bool is_thread_local; + i32 field_index; + i32 field_src_index; + bool is_immutable; + bool is_thread_local; + ExactValue default_value; } Variable; struct { bool is_type_alias; diff --git a/src/ir.cpp b/src/ir.cpp index a8cfb0649..95c277f59 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4616,16 +4616,20 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) { } TypeTuple *pt = &type->params->Tuple; for (isize i = 0; i < param_count; i++) { - Type *param_type = pt->variables[i]->type; + Entity *e = pt->variables[i]; + GB_ASSERT(e->kind == Entity_Variable); if (args[i] == NULL) { - args[i] = ir_value_nil(proc->module->allocator, param_type); + if (e->Variable.default_value.kind != ExactValue_Invalid) { + args[i] = ir_value_constant(proc->module->allocator, e->type, e->Variable.default_value); + } else { + args[i] = ir_value_nil(proc->module->allocator, e->type); + } } else { - args[i] = ir_emit_conv(proc, args[i], param_type); + args[i] = ir_emit_conv(proc, args[i], e->type); } } return ir_emit_call(proc, value, args, param_count); - // GB_PANIC("HERE!\n"); } isize arg_index = 0; @@ -4640,7 +4644,9 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) { arg_count++; } } - irValue **args = gb_alloc_array(proc->module->allocator, irValue *, arg_count); + + + irValue **args = gb_alloc_array(proc->module->allocator, irValue *, gb_max(type->param_count, arg_count)); bool variadic = type->variadic; bool vari_expand = ce->ellipsis.pos.line != 0; @@ -4660,6 +4666,23 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) { TypeTuple *pt = &type->params->Tuple; + if (arg_count < type->param_count) { + isize end = type->param_count; + if (variadic) { + end--; + } + while (arg_index < end) { + Entity *e = pt->variables[arg_index]; + GB_ASSERT(e->kind == Entity_Variable); + if (e->Variable.default_value.kind != ExactValue_Invalid) { + args[arg_index++] = ir_value_constant(proc->module->allocator, e->type, e->Variable.default_value); + } else { + args[arg_index++] = ir_value_nil(proc->module->allocator, e->type); + } + } + } + + if (variadic) { isize i = 0; for (; i < type->param_count-1; i++) { @@ -4674,7 +4697,7 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) { } } } else { - for (isize i = 0; i < arg_count; i++) { + for (isize i = 0; i < type->param_count; i++) { args[i] = ir_emit_conv(proc, args[i], pt->variables[i]->type); } } @@ -4704,7 +4727,7 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) { args[arg_count-1] = ir_emit_load(proc, slice); } - return ir_emit_call(proc, value, args, arg_count); + return ir_emit_call(proc, value, args, type->param_count); case_end; case_ast_node(se, SliceExpr, expr); @@ -6680,7 +6703,7 @@ void ir_number_proc_registers(irProcedure *proc) { b->index = i; for_array(j, b->instrs) { irValue *value = b->instrs[j]; - GB_ASSERT(value->kind == irValue_Instr); + GB_ASSERT_MSG(value->kind == irValue_Instr, "%.*s", LIT(proc->name)); irInstr *instr = &value->Instr; if (ir_instr_type(instr) == NULL) { // NOTE(bill): Ignore non-returning instructions value->index = -1; diff --git a/src/parser.cpp b/src/parser.cpp index d741e569f..2feaa1fc2 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -326,9 +326,10 @@ AST_NODE_KIND(_DeclBegin, "", i32) \ }) \ AST_NODE_KIND(_DeclEnd, "", i32) \ AST_NODE_KIND(Field, "field", struct { \ - Array names; \ - AstNode * type; \ - u32 flags; \ + Array names; \ + AstNode * type; \ + AstNode * default_value; \ + u32 flags; \ }) \ AST_NODE_KIND(FieldList, "field list", struct { \ Token token; \ @@ -1276,10 +1277,11 @@ AstNode *ast_bad_decl(AstFile *f, Token begin, Token end) { return result; } -AstNode *ast_field(AstFile *f, Array names, AstNode *type, u32 flags) { +AstNode *ast_field(AstFile *f, Array names, AstNode *type, AstNode *default_value, u32 flags) { AstNode *result = make_ast_node(f, AstNode_Field); result->Field.names = names; result->Field.type = type; + result->Field.default_value = default_value; result->Field.flags = flags; return result; } @@ -2645,7 +2647,7 @@ AstNode *parse_results(AstFile *f) { Array empty_names = {}; Array list = make_ast_node_array(f); AstNode *type = parse_type(f); - array_add(&list, ast_field(f, empty_names, type, 0)); + array_add(&list, ast_field(f, empty_names, type, NULL, 0)); return ast_field_list(f, begin_token, list); } @@ -2839,6 +2841,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok Array list = {}; array_init(&list, heap_allocator()); // LEAK(bill): isize total_name_count = 0; bool allow_ellipsis = allowed_flags&FieldFlag_ellipsis; + bool is_procedure = (allowed_flags&FieldFlag_Signature) != 0; while (f->curr_token.kind != follow && f->curr_token.kind != Token_Colon && @@ -2866,8 +2869,25 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok total_name_count += names.count; expect_token_after(f, Token_Colon, "field list"); - AstNode *type = parse_var_type(f, allow_ellipsis); - AstNode *param = ast_field(f, names, type, set_flags); + AstNode *type = NULL; + AstNode *default_value = NULL; + + if (f->curr_token.kind != Token_Eq) { + type = parse_var_type(f, allow_ellipsis); + } + if (allow_token(f, Token_Eq)) { + // TODO(bill): Should this be true==lhs or false==rhs? + default_value = parse_expr(f, true); + if (!is_procedure) { + syntax_error(f->curr_token, "Default parameters are only allowed for procedures"); + } + } + + if (default_value != NULL && names.count > 1) { + syntax_error(f->curr_token, "Default parameters can only be applied to single values"); + } + + AstNode *param = ast_field(f, names, type, default_value, set_flags); array_add(¶ms, param); parse_expect_field_separator(f, type); @@ -2884,8 +2904,24 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok total_name_count += names.count; expect_token_after(f, Token_Colon, "field list"); - AstNode *type = parse_var_type(f, allow_ellipsis); - AstNode *param = ast_field(f, names, type, set_flags); + AstNode *type = NULL; + AstNode *default_value = NULL; + if (f->curr_token.kind != Token_Eq) { + type = parse_var_type(f, allow_ellipsis); + } + if (allow_token(f, Token_Eq)) { + // TODO(bill): Should this be true==lhs or false==rhs? + default_value = parse_expr(f, true); + if (!is_procedure) { + syntax_error(f->curr_token, "Default parameters are only allowed for procedures"); + } + } + + if (default_value != NULL && names.count > 1) { + syntax_error(f->curr_token, "Default parameters can only be applied to single values"); + } + + AstNode *param = ast_field(f, names, type, default_value, set_flags); array_add(¶ms, param); if (!parse_expect_field_separator(f, param)) { @@ -2907,7 +2943,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok names[0] = ast_ident(f, token); u32 flags = check_field_prefixes(f, list.count, allowed_flags, list[i].flags); - AstNode *param = ast_field(f, names, list[i].node, flags); + AstNode *param = ast_field(f, names, list[i].node, NULL, flags); array_add(¶ms, param); } @@ -3084,7 +3120,7 @@ AstNode *parse_type_or_ident(AstFile *f) { total_decl_name_count += names.count; expect_token_after(f, Token_Colon, "field list"); AstNode *type = parse_var_type(f, false); - array_add(&decls, ast_field(f, names, type, set_flags)); + array_add(&decls, ast_field(f, names, type, NULL, set_flags)); } else { Array names = parse_ident_list(f); if (names.count == 0) { @@ -3095,7 +3131,7 @@ AstNode *parse_type_or_ident(AstFile *f) { total_decl_name_count += names.count; expect_token_after(f, Token_Colon, "field list"); AstNode *type = parse_var_type(f, false); - array_add(&decls, ast_field(f, names, type, set_flags)); + array_add(&decls, ast_field(f, names, type, NULL, set_flags)); } else { AstNode *name = names[0]; Token open = expect_token(f, Token_OpenBrace);