diff --git a/src/all_types.hpp b/src/all_types.hpp index 76c2ae9076..7861dec98c 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -452,7 +452,6 @@ struct AstNodeFieldAccessExpr { TypeEnumField *type_enum_field; Expr resolved_expr; StructValExprCodeGen resolved_struct_val_expr; // for enum values - bool is_fn_call; TypeTableEntry *bare_container_type; bool is_member_fn; AstNode *container_init_expr_node; diff --git a/src/analyze.cpp b/src/analyze.cpp index 337b55f91c..e17e1b4526 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2798,11 +2798,11 @@ static void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) { } } -static TypeTableEntry *analyze_container_member_access_inner(CodeGen *g, bool wrapped_in_fn_call, +static TypeTableEntry *analyze_container_member_access_inner(CodeGen *g, TypeTableEntry *bare_struct_type, Buf *field_name, AstNode *node, TypeTableEntry *struct_type) { assert(node->type == NodeTypeFieldAccessExpr); - if (wrapped_in_fn_call && !is_slice(bare_struct_type)) { + if (!is_slice(bare_struct_type)) { BlockContext *container_block_context = get_container_block_context(bare_struct_type); assert(container_block_context); auto entry = container_block_context->decl_table.maybe_get(field_name); @@ -2821,19 +2821,14 @@ static TypeTableEntry *analyze_container_member_access_inner(CodeGen *g, bool wr } else { return resolve_expr_const_val_as_fn(g, node, fn_entry, false); } - } else { - add_node_error(g, node, buf_sprintf("no function named '%s' in '%s'", - buf_ptr(field_name), buf_ptr(&bare_struct_type->name))); - return g->builtin_types.entry_invalid; } - } else { - add_node_error(g, node, - buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&struct_type->name))); - return g->builtin_types.entry_invalid; } + add_node_error(g, node, + buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&bare_struct_type->name))); + return g->builtin_types.entry_invalid; } -static TypeTableEntry *analyze_container_member_access(CodeGen *g, bool wrapped_in_fn_call, +static TypeTableEntry *analyze_container_member_access(CodeGen *g, Buf *field_name, AstNode *node, TypeTableEntry *struct_type) { TypeTableEntry *bare_type = container_ref_type(struct_type); @@ -2848,7 +2843,7 @@ static TypeTableEntry *analyze_container_member_access(CodeGen *g, bool wrapped_ if (node->data.field_access_expr.type_struct_field) { return node->data.field_access_expr.type_struct_field->type_entry; } else { - return analyze_container_member_access_inner(g, wrapped_in_fn_call, bare_type, field_name, + return analyze_container_member_access_inner(g, bare_type, field_name, node, struct_type); } } else if (bare_type->id == TypeTableEntryIdEnum) { @@ -2856,7 +2851,7 @@ static TypeTableEntry *analyze_container_member_access(CodeGen *g, bool wrapped_ if (node->data.field_access_expr.type_enum_field) { return node->data.field_access_expr.type_enum_field->type_entry; } else { - return analyze_container_member_access_inner(g, wrapped_in_fn_call, bare_type, field_name, + return analyze_container_member_access_inner(g, bare_type, field_name, node, struct_type); } } else if (bare_type->id == TypeTableEntryIdUnion) { @@ -2875,12 +2870,10 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i TypeTableEntry *struct_type = analyze_expression(g, import, context, nullptr, *struct_expr_node); Buf *field_name = node->data.field_access_expr.field_name; - bool wrapped_in_fn_call = node->data.field_access_expr.is_fn_call; - if (struct_type->id == TypeTableEntryIdInvalid) { return struct_type; } else if (is_container_ref(struct_type)) { - return analyze_container_member_access(g, wrapped_in_fn_call, field_name, node, struct_type); + return analyze_container_member_access(g, field_name, node, struct_type); } else if (struct_type->id == TypeTableEntryIdArray) { if (buf_eql_str(field_name, "len")) { return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type, @@ -2949,8 +2942,6 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i buf_ptr(&child_type->name), buf_ptr(field_name))); return g->builtin_types.entry_invalid; } - } else if (wrapped_in_fn_call) { // this branch should go last, before the error in the else case - return resolve_expr_const_val_as_type(g, node, child_type, false); } else { add_node_error(g, node, buf_sprintf("type '%s' does not support field access", buf_ptr(&struct_type->name))); @@ -5581,6 +5572,19 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry zig_unreachable(); } +static TypeTableEntry *bad_method_call(CodeGen *g, AstNode *node, TypeTableEntry *container_type, + TypeTableEntry *expected_param_type, FnTableEntry *fn_table_entry) +{ + ErrorMsg *msg = add_node_error(g, node, + buf_sprintf("function called as method of '%s', but first parameter is of type '%s'", + buf_ptr(&container_type->name), + buf_ptr(&expected_param_type->name))); + if (fn_table_entry) { + add_error_note(g, msg, fn_table_entry->proto_node, buf_sprintf("function declared here")); + } + return g->builtin_types.entry_invalid; +} + // Before calling this function, set node->data.fn_call_expr.fn_table_entry if the function is known // at compile time. Otherwise this is a function pointer call. static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, BlockContext *context, @@ -5623,10 +5627,23 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, bool all_args_const_expr = true; if (struct_node) { - ConstExprValue *struct_const_val = &get_resolved_expr(struct_node)->const_val; + Expr *struct_expr = get_resolved_expr(struct_node); + ConstExprValue *struct_const_val = &struct_expr->const_val; if (!struct_const_val->ok) { all_args_const_expr = false; } + + FnTypeParamInfo *param_info = &fn_type->data.fn.fn_type_id.param_info[0]; + TypeTableEntry *expected_param_type = param_info->type; + TypeTableEntry *container_bare_type = container_ref_type(struct_expr->type_entry); + if (is_container_ref(expected_param_type)) { + TypeTableEntry *param_bare_type = container_ref_type(expected_param_type); + if (param_bare_type != container_bare_type) { + return bad_method_call(g, node, container_bare_type, expected_param_type, fn_table_entry); + } + } else { + return bad_method_call(g, node, container_bare_type, expected_param_type, fn_table_entry); + } } // analyze each parameter. in the case of a method, we already analyzed the @@ -5925,10 +5942,6 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import return analyze_builtin_fn_call_expr(g, import, context, expected_type, node); } - if (fn_ref_expr->type == NodeTypeFieldAccessExpr) { - fn_ref_expr->data.field_access_expr.is_fn_call = true; - } - TypeTableEntry *invoke_type_entry = analyze_expression(g, import, context, nullptr, fn_ref_expr); if (invoke_type_entry->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 4f86519879..f7b3d042a8 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -1533,7 +1533,56 @@ fn foo() { } )SOURCE", 2, ".tmp_source.zig:6:16: error: use of undeclared identifier 'JsonList'", - ".tmp_source.zig:27:8: error: no function named 'init' in 'JsonNode'"); + ".tmp_source.zig:27:8: error: no member named 'init' in 'JsonNode'"); + + add_compile_fail_case("method call with first arg type primitive", R"SOURCE( +struct Foo { + x: i32, + + fn init(x: i32) -> Foo { + Foo { + .x = x, + } + } +} + +fn f() { + const derp = Foo.init(3); + + derp.init(); +} + )SOURCE", 2, + ".tmp_source.zig:15:14: error: function called as method of 'Foo', but first parameter is of type 'i32'", + ".tmp_source.zig:5:5: note: function declared here"); + + add_compile_fail_case("method call with first arg type wrong container", R"SOURCE( +pub struct List { + len: usize, + allocator: &Allocator, + + pub fn init(allocator: &Allocator) -> List { + List { + .len = 0, + .allocator = allocator, + } + } +} + +pub var global_allocator = Allocator { + .field = 1234, +}; + +pub struct Allocator { + field: i32, +} + +fn foo() { + var x = List.init(&global_allocator); + x.init(); +} + )SOURCE", 2, + ".tmp_source.zig:24:11: error: function called as method of 'List', but first parameter is of type '&Allocator'", + ".tmp_source.zig:6:9: note: function declared here"); } //////////////////////////////////////////////////////////////////////////////