authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-09-26 00:40:09-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-09-26 01:01:02-04:00
log4b68224c60d78c82515882637ede223aee48091a
treebea5e71e14148ed9604b945fa7757848be29e733
parent01e13de7ca39bbc9208d49e14066f72a7f168163

add error message for method call on non method

closes #199

3 files changed, 86 insertions(+), 25 deletions(-)

src/all_types.hpp-1
...@@ -452,7 +452,6 @@ struct AstNodeFieldAccessExpr {...@@ -452,7 +452,6 @@ struct AstNodeFieldAccessExpr {
452 TypeEnumField *type_enum_field;452 TypeEnumField *type_enum_field;
453 Expr resolved_expr;453 Expr resolved_expr;
454 StructValExprCodeGen resolved_struct_val_expr; // for enum values454 StructValExprCodeGen resolved_struct_val_expr; // for enum values
455 bool is_fn_call;
456 TypeTableEntry *bare_container_type;455 TypeTableEntry *bare_container_type;
457 bool is_member_fn;456 bool is_member_fn;
458 AstNode *container_init_expr_node;457 AstNode *container_init_expr_node;
src/analyze.cpp+36-23
...@@ -2798,11 +2798,11 @@ static void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) {...@@ -2798,11 +2798,11 @@ static void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) {
2798 }2798 }
2799}2799}
28002800
2801static TypeTableEntry *analyze_container_member_access_inner(CodeGen *g, bool wrapped_in_fn_call,2801static TypeTableEntry *analyze_container_member_access_inner(CodeGen *g,
2802 TypeTableEntry *bare_struct_type, Buf *field_name, AstNode *node, TypeTableEntry *struct_type)2802 TypeTableEntry *bare_struct_type, Buf *field_name, AstNode *node, TypeTableEntry *struct_type)
2803{2803{
2804 assert(node->type == NodeTypeFieldAccessExpr);2804 assert(node->type == NodeTypeFieldAccessExpr);
2805 if (wrapped_in_fn_call && !is_slice(bare_struct_type)) {2805 if (!is_slice(bare_struct_type)) {
2806 BlockContext *container_block_context = get_container_block_context(bare_struct_type);2806 BlockContext *container_block_context = get_container_block_context(bare_struct_type);
2807 assert(container_block_context);2807 assert(container_block_context);
2808 auto entry = container_block_context->decl_table.maybe_get(field_name);2808 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...@@ -2821,19 +2821,14 @@ static TypeTableEntry *analyze_container_member_access_inner(CodeGen *g, bool wr
2821 } else {2821 } else {
2822 return resolve_expr_const_val_as_fn(g, node, fn_entry, false);2822 return resolve_expr_const_val_as_fn(g, node, fn_entry, false);
2823 }2823 }
2824 } else {
2825 add_node_error(g, node, buf_sprintf("no function named '%s' in '%s'",
2826 buf_ptr(field_name), buf_ptr(&bare_struct_type->name)));
2827 return g->builtin_types.entry_invalid;
2828 }2824 }
2829 } else {
2830 add_node_error(g, node,
2831 buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&struct_type->name)));
2832 return g->builtin_types.entry_invalid;
2833 }2825 }
2826 add_node_error(g, node,
2827 buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&bare_struct_type->name)));
2828 return g->builtin_types.entry_invalid;
2834}2829}
28352830
2836static TypeTableEntry *analyze_container_member_access(CodeGen *g, bool wrapped_in_fn_call,2831static TypeTableEntry *analyze_container_member_access(CodeGen *g,
2837 Buf *field_name, AstNode *node, TypeTableEntry *struct_type)2832 Buf *field_name, AstNode *node, TypeTableEntry *struct_type)
2838{2833{
2839 TypeTableEntry *bare_type = container_ref_type(struct_type);2834 TypeTableEntry *bare_type = container_ref_type(struct_type);
...@@ -2848,7 +2843,7 @@ static TypeTableEntry *analyze_container_member_access(CodeGen *g, bool wrapped_...@@ -2848,7 +2843,7 @@ static TypeTableEntry *analyze_container_member_access(CodeGen *g, bool wrapped_
2848 if (node->data.field_access_expr.type_struct_field) {2843 if (node->data.field_access_expr.type_struct_field) {
2849 return node->data.field_access_expr.type_struct_field->type_entry;2844 return node->data.field_access_expr.type_struct_field->type_entry;
2850 } else {2845 } else {
2851 return analyze_container_member_access_inner(g, wrapped_in_fn_call, bare_type, field_name,2846 return analyze_container_member_access_inner(g, bare_type, field_name,
2852 node, struct_type);2847 node, struct_type);
2853 }2848 }
2854 } else if (bare_type->id == TypeTableEntryIdEnum) {2849 } else if (bare_type->id == TypeTableEntryIdEnum) {
...@@ -2856,7 +2851,7 @@ static TypeTableEntry *analyze_container_member_access(CodeGen *g, bool wrapped_...@@ -2856,7 +2851,7 @@ static TypeTableEntry *analyze_container_member_access(CodeGen *g, bool wrapped_
2856 if (node->data.field_access_expr.type_enum_field) {2851 if (node->data.field_access_expr.type_enum_field) {
2857 return node->data.field_access_expr.type_enum_field->type_entry;2852 return node->data.field_access_expr.type_enum_field->type_entry;
2858 } else {2853 } else {
2859 return analyze_container_member_access_inner(g, wrapped_in_fn_call, bare_type, field_name,2854 return analyze_container_member_access_inner(g, bare_type, field_name,
2860 node, struct_type);2855 node, struct_type);
2861 }2856 }
2862 } else if (bare_type->id == TypeTableEntryIdUnion) {2857 } else if (bare_type->id == TypeTableEntryIdUnion) {
...@@ -2875,12 +2870,10 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i...@@ -2875,12 +2870,10 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
2875 TypeTableEntry *struct_type = analyze_expression(g, import, context, nullptr, *struct_expr_node);2870 TypeTableEntry *struct_type = analyze_expression(g, import, context, nullptr, *struct_expr_node);
2876 Buf *field_name = node->data.field_access_expr.field_name;2871 Buf *field_name = node->data.field_access_expr.field_name;
28772872
2878 bool wrapped_in_fn_call = node->data.field_access_expr.is_fn_call;
2879
2880 if (struct_type->id == TypeTableEntryIdInvalid) {2873 if (struct_type->id == TypeTableEntryIdInvalid) {
2881 return struct_type;2874 return struct_type;
2882 } else if (is_container_ref(struct_type)) {2875 } else if (is_container_ref(struct_type)) {
2883 return analyze_container_member_access(g, wrapped_in_fn_call, field_name, node, struct_type);2876 return analyze_container_member_access(g, field_name, node, struct_type);
2884 } else if (struct_type->id == TypeTableEntryIdArray) {2877 } else if (struct_type->id == TypeTableEntryIdArray) {
2885 if (buf_eql_str(field_name, "len")) {2878 if (buf_eql_str(field_name, "len")) {
2886 return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type,2879 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...@@ -2949,8 +2942,6 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
2949 buf_ptr(&child_type->name), buf_ptr(field_name)));2942 buf_ptr(&child_type->name), buf_ptr(field_name)));
2950 return g->builtin_types.entry_invalid;2943 return g->builtin_types.entry_invalid;
2951 }2944 }
2952 } else if (wrapped_in_fn_call) { // this branch should go last, before the error in the else case
2953 return resolve_expr_const_val_as_type(g, node, child_type, false);
2954 } else {2945 } else {
2955 add_node_error(g, node,2946 add_node_error(g, node,
2956 buf_sprintf("type '%s' does not support field access", buf_ptr(&struct_type->name)));2947 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...@@ -5581,6 +5572,19 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
5581 zig_unreachable();5572 zig_unreachable();
5582}5573}
55835574
5575static TypeTableEntry *bad_method_call(CodeGen *g, AstNode *node, TypeTableEntry *container_type,
5576 TypeTableEntry *expected_param_type, FnTableEntry *fn_table_entry)
5577{
5578 ErrorMsg *msg = add_node_error(g, node,
5579 buf_sprintf("function called as method of '%s', but first parameter is of type '%s'",
5580 buf_ptr(&container_type->name),
5581 buf_ptr(&expected_param_type->name)));
5582 if (fn_table_entry) {
5583 add_error_note(g, msg, fn_table_entry->proto_node, buf_sprintf("function declared here"));
5584 }
5585 return g->builtin_types.entry_invalid;
5586}
5587
5584// Before calling this function, set node->data.fn_call_expr.fn_table_entry if the function is known5588// Before calling this function, set node->data.fn_call_expr.fn_table_entry if the function is known
5585// at compile time. Otherwise this is a function pointer call.5589// at compile time. Otherwise this is a function pointer call.
5586static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, BlockContext *context,5590static 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,...@@ -5623,10 +5627,23 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import,
5623 bool all_args_const_expr = true;5627 bool all_args_const_expr = true;
56245628
5625 if (struct_node) {5629 if (struct_node) {
5626 ConstExprValue *struct_const_val = &get_resolved_expr(struct_node)->const_val;5630 Expr *struct_expr = get_resolved_expr(struct_node);
5631 ConstExprValue *struct_const_val = &struct_expr->const_val;
5627 if (!struct_const_val->ok) {5632 if (!struct_const_val->ok) {
5628 all_args_const_expr = false;5633 all_args_const_expr = false;
5629 }5634 }
5635
5636 FnTypeParamInfo *param_info = &fn_type->data.fn.fn_type_id.param_info[0];
5637 TypeTableEntry *expected_param_type = param_info->type;
5638 TypeTableEntry *container_bare_type = container_ref_type(struct_expr->type_entry);
5639 if (is_container_ref(expected_param_type)) {
5640 TypeTableEntry *param_bare_type = container_ref_type(expected_param_type);
5641 if (param_bare_type != container_bare_type) {
5642 return bad_method_call(g, node, container_bare_type, expected_param_type, fn_table_entry);
5643 }
5644 } else {
5645 return bad_method_call(g, node, container_bare_type, expected_param_type, fn_table_entry);
5646 }
5630 }5647 }
56315648
5632 // analyze each parameter. in the case of a method, we already analyzed the5649 // 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...@@ -5925,10 +5942,6 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import
5925 return analyze_builtin_fn_call_expr(g, import, context, expected_type, node);5942 return analyze_builtin_fn_call_expr(g, import, context, expected_type, node);
5926 }5943 }
59275944
5928 if (fn_ref_expr->type == NodeTypeFieldAccessExpr) {
5929 fn_ref_expr->data.field_access_expr.is_fn_call = true;
5930 }
5931
5932 TypeTableEntry *invoke_type_entry = analyze_expression(g, import, context, nullptr, fn_ref_expr);5945 TypeTableEntry *invoke_type_entry = analyze_expression(g, import, context, nullptr, fn_ref_expr);
5933 if (invoke_type_entry->id == TypeTableEntryIdInvalid) {5946 if (invoke_type_entry->id == TypeTableEntryIdInvalid) {
5934 return g->builtin_types.entry_invalid;5947 return g->builtin_types.entry_invalid;
test/run_tests.cpp+50-1
...@@ -1533,7 +1533,56 @@ fn foo() {...@@ -1533,7 +1533,56 @@ fn foo() {
1533}1533}
1534 )SOURCE", 2,1534 )SOURCE", 2,
1535 ".tmp_source.zig:6:16: error: use of undeclared identifier 'JsonList'",1535 ".tmp_source.zig:6:16: error: use of undeclared identifier 'JsonList'",
1536 ".tmp_source.zig:27:8: error: no function named 'init' in 'JsonNode'");1536 ".tmp_source.zig:27:8: error: no member named 'init' in 'JsonNode'");
1537
1538 add_compile_fail_case("method call with first arg type primitive", R"SOURCE(
1539struct Foo {
1540 x: i32,
1541
1542 fn init(x: i32) -> Foo {
1543 Foo {
1544 .x = x,
1545 }
1546 }
1547}
1548
1549fn f() {
1550 const derp = Foo.init(3);
1551
1552 derp.init();
1553}
1554 )SOURCE", 2,
1555 ".tmp_source.zig:15:14: error: function called as method of 'Foo', but first parameter is of type 'i32'",
1556 ".tmp_source.zig:5:5: note: function declared here");
1557
1558 add_compile_fail_case("method call with first arg type wrong container", R"SOURCE(
1559pub struct List {
1560 len: usize,
1561 allocator: &Allocator,
1562
1563 pub fn init(allocator: &Allocator) -> List {
1564 List {
1565 .len = 0,
1566 .allocator = allocator,
1567 }
1568 }
1569}
1570
1571pub var global_allocator = Allocator {
1572 .field = 1234,
1573};
1574
1575pub struct Allocator {
1576 field: i32,
1577}
1578
1579fn foo() {
1580 var x = List.init(&global_allocator);
1581 x.init();
1582}
1583 )SOURCE", 2,
1584 ".tmp_source.zig:24:11: error: function called as method of 'List', but first parameter is of type '&Allocator'",
1585 ".tmp_source.zig:6:9: note: function declared here");
1537}1586}
15381587
1539//////////////////////////////////////////////////////////////////////////////1588//////////////////////////////////////////////////////////////////////////////