diff options
| author | Andrew Kelley <superjoe30@gmail.com> | 2016-02-03 21:34:09 -0700 |
|---|---|---|
| committer | Andrew Kelley <superjoe30@gmail.com> | 2016-02-03 21:34:09 -0700 |
| commit | 2521afef699d40917db906ebb27a87c5ea287fbe (patch) | |
| tree | a42906cf33e4199b81f8aba70153120cd4aa5c0f | |
| parent | 5c310f43432ec723f8b7d449313c1ea20f1f2d78 (diff) | |
| download | zig-2521afef699d40917db906ebb27a87c5ea287fbe.tar.gz zig-2521afef699d40917db906ebb27a87c5ea287fbe.zip | |
add ability to call function pointer field
also introduce the self hosted tests
closes #108
| -rw-r--r-- | src/all_types.hpp | 4 | ||||
| -rw-r--r-- | src/analyze.cpp | 140 | ||||
| -rw-r--r-- | src/codegen.cpp | 47 | ||||
| -rw-r--r-- | std/test_runner.zig | 4 | ||||
| -rw-r--r-- | test/run_tests.cpp | 45 | ||||
| -rw-r--r-- | test/self_hosted.zig | 37 |
6 files changed, 161 insertions, 116 deletions
diff --git a/src/all_types.hpp b/src/all_types.hpp index cb0406b326..89c277914e 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -357,6 +357,7 @@ struct AstNodeFnCallExpr { Expr resolved_expr; FnTableEntry *fn_entry; CastOp cast_op; + TypeTableEntry *enum_type; // if cast_op is CastOpArrayToString, this will be a pointer to // the string struct on the stack LLVMValueRef tmp_ptr; @@ -390,6 +391,9 @@ struct AstNodeFieldAccessExpr { TypeEnumField *type_enum_field; Expr resolved_expr; StructValExprCodeGen resolved_struct_val_expr; // for enum values + bool is_fn_call; + TypeTableEntry *bare_struct_type; + bool is_member_fn; }; struct AstNodeDirective { diff --git a/src/analyze.cpp b/src/analyze.cpp index 1b0b4a131b..50fdafff71 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -28,6 +28,7 @@ static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import, TypeTableEntry *expected_type, AstNode *node); static TypeTableEntry *resolve_expr_const_val_as_void(CodeGen *g, AstNode *node); static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, FnTableEntry *fn); +static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node, TypeTableEntry *type); static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *node); static void analyze_top_level_decls_root(CodeGen *g, ImportTableEntry *import, AstNode *node); @@ -2220,15 +2221,28 @@ 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 == TypeTableEntryIdStruct || (struct_type->id == TypeTableEntryIdPointer && struct_type->data.pointer.child_type->id == TypeTableEntryIdStruct)) { TypeTableEntry *bare_struct_type = (struct_type->id == TypeTableEntryIdStruct) ? struct_type : struct_type->data.pointer.child_type; + node->data.field_access_expr.bare_struct_type = bare_struct_type; node->data.field_access_expr.type_struct_field = find_struct_type_field(bare_struct_type, field_name); if (node->data.field_access_expr.type_struct_field) { return node->data.field_access_expr.type_struct_field->type_entry; + } else if (wrapped_in_fn_call) { + auto table_entry = bare_struct_type->data.structure.fn_table.maybe_get(field_name); + if (table_entry) { + node->data.field_access_expr.is_member_fn = true; + return resolve_expr_const_val_as_fn(g, node, table_entry->value); + } else { + 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; + } } else { add_node_error(g, node, buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&struct_type->name))); @@ -2251,6 +2265,8 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i if (child_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; + } else if (wrapped_in_fn_call) { + return resolve_expr_const_val_as_type(g, node, child_type); } else if (child_type->id == TypeTableEntryIdEnum) { return analyze_enum_value_expr(g, import, context, node, nullptr, child_type, field_name); } else if (child_type->id == TypeTableEntryIdStruct) { @@ -4128,72 +4144,7 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import } if (fn_ref_expr->type == NodeTypeFieldAccessExpr) { - fn_ref_expr->block_context = context; - AstNode *first_param_expr = fn_ref_expr->data.field_access_expr.struct_expr; - TypeTableEntry *struct_type = analyze_expression(g, import, context, nullptr, first_param_expr); - Buf *name = &fn_ref_expr->data.field_access_expr.field_name; - if (struct_type->id == TypeTableEntryIdStruct || - (struct_type->id == TypeTableEntryIdPointer && - struct_type->data.pointer.child_type->id == TypeTableEntryIdStruct)) - { - TypeTableEntry *bare_struct_type = (struct_type->id == TypeTableEntryIdStruct) ? - struct_type : struct_type->data.pointer.child_type; - - auto table_entry = bare_struct_type->data.structure.fn_table.maybe_get(name); - if (table_entry) { - return analyze_fn_call_raw(g, import, context, expected_type, node, - table_entry->value, bare_struct_type); - } else { - add_node_error(g, fn_ref_expr, - buf_sprintf("no function named '%s' in '%s'", - buf_ptr(name), buf_ptr(&bare_struct_type->name))); - return g->builtin_types.entry_invalid; - } - } else if (struct_type->id == TypeTableEntryIdInvalid) { - return struct_type; - } else if (struct_type->id == TypeTableEntryIdMetaType) { - TypeTableEntry *child_type = resolve_type(g, first_param_expr); - - if (child_type->id == TypeTableEntryIdInvalid) { - return g->builtin_types.entry_invalid; - } else if (child_type->id == TypeTableEntryIdEnum) { - Buf *field_name = &fn_ref_expr->data.field_access_expr.field_name; - int param_count = node->data.fn_call_expr.params.length; - if (param_count > 1) { - add_node_error(g, first_executing_node(node->data.fn_call_expr.params.at(1)), - buf_sprintf("enum values accept only one parameter")); - return child_type; - } else { - AstNode *value_node; - if (param_count == 1) { - value_node = node->data.fn_call_expr.params.at(0); - } else { - value_node = nullptr; - } - - return analyze_enum_value_expr(g, import, context, fn_ref_expr, value_node, - child_type, field_name); - } - } else if (child_type->id == TypeTableEntryIdStruct) { - Buf *field_name = &fn_ref_expr->data.field_access_expr.field_name; - auto entry = child_type->data.structure.fn_table.maybe_get(field_name); - if (entry) { - return analyze_fn_call_raw(g, import, context, expected_type, node, - entry->value, nullptr); - } else { - add_node_error(g, node, - buf_sprintf("struct '%s' has no function called '%s'", - buf_ptr(&child_type->name), buf_ptr(field_name))); - return g->builtin_types.entry_invalid; - } - } else { - add_node_error(g, first_param_expr, buf_sprintf("member reference base type not struct or enum")); - return g->builtin_types.entry_invalid; - } - } else { - add_node_error(g, first_param_expr, buf_sprintf("member reference base type not struct or enum")); - return g->builtin_types.entry_invalid; - } + fn_ref_expr->data.field_access_expr.is_fn_call = true; } TypeTableEntry *invoke_type_entry = analyze_expression(g, import, context, nullptr, fn_ref_expr); @@ -4207,9 +4158,62 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import if (const_val->ok) { if (invoke_type_entry->id == TypeTableEntryIdMetaType) { - return analyze_cast_expr(g, import, context, node); + if (fn_ref_expr->type == NodeTypeFieldAccessExpr) { + TypeTableEntry *child_type = resolve_type(g, fn_ref_expr); + + if (child_type->id == TypeTableEntryIdInvalid) { + return g->builtin_types.entry_invalid; + } else if (child_type->id == TypeTableEntryIdEnum) { + Buf *field_name = &fn_ref_expr->data.field_access_expr.field_name; + int param_count = node->data.fn_call_expr.params.length; + if (param_count > 1) { + add_node_error(g, first_executing_node(node->data.fn_call_expr.params.at(1)), + buf_sprintf("enum values accept only one parameter")); + return child_type; + } else { + AstNode *value_node; + if (param_count == 1) { + value_node = node->data.fn_call_expr.params.at(0); + } else { + value_node = nullptr; + } + + node->data.fn_call_expr.enum_type = child_type; + + return analyze_enum_value_expr(g, import, context, fn_ref_expr, value_node, + child_type, field_name); + } + } else if (child_type->id == TypeTableEntryIdStruct) { + Buf *field_name = &fn_ref_expr->data.field_access_expr.field_name; + auto entry = child_type->data.structure.fn_table.maybe_get(field_name); + if (entry) { + return analyze_fn_call_raw(g, import, context, expected_type, node, + entry->value, nullptr); + } else { + add_node_error(g, node, + buf_sprintf("struct '%s' has no function called '%s'", + buf_ptr(&child_type->name), buf_ptr(field_name))); + return g->builtin_types.entry_invalid; + } + } else { + add_node_error(g, fn_ref_expr, buf_sprintf("member reference base type not struct or enum")); + return g->builtin_types.entry_invalid; + } + } else { + return analyze_cast_expr(g, import, context, node); + } } else if (invoke_type_entry->id == TypeTableEntryIdFn) { - return analyze_fn_call_raw(g, import, context, expected_type, node, const_val->data.x_fn, nullptr); + TypeTableEntry *bare_struct_type; + if (fn_ref_expr->type == NodeTypeFieldAccessExpr && + fn_ref_expr->data.field_access_expr.is_member_fn) + { + bare_struct_type = fn_ref_expr->data.field_access_expr.bare_struct_type; + } else { + bare_struct_type = nullptr; + } + + return analyze_fn_call_raw(g, import, context, expected_type, node, + const_val->data.x_fn, bare_struct_type); } else { add_node_error(g, fn_ref_expr, buf_sprintf("type '%s' not a function", buf_ptr(&invoke_type_entry->name))); diff --git a/src/codegen.cpp b/src/codegen.cpp index d95b0c394d..cfae4da2a1 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -544,41 +544,28 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { return gen_cast_expr(g, node); } - FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry; AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; + if (node->data.fn_call_expr.enum_type) { + int param_count = node->data.fn_call_expr.params.length; + AstNode *arg1_node; + if (param_count == 1) { + arg1_node = node->data.fn_call_expr.params.at(0); + } else { + assert(param_count == 0); + arg1_node = nullptr; + } + return gen_enum_value_expr(g, fn_ref_expr, node->data.fn_call_expr.enum_type, arg1_node); + } + + FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry; TypeTableEntry *struct_type = nullptr; AstNode *first_param_expr = nullptr; - if (fn_ref_expr->type == NodeTypeFieldAccessExpr) { + + if (fn_ref_expr->type == NodeTypeFieldAccessExpr && + fn_ref_expr->data.field_access_expr.is_member_fn) + { first_param_expr = fn_ref_expr->data.field_access_expr.struct_expr; struct_type = get_expr_type(first_param_expr); - if (struct_type->id == TypeTableEntryIdStruct) { - fn_table_entry = node->data.fn_call_expr.fn_entry; - } else if (struct_type->id == TypeTableEntryIdPointer) { - assert(struct_type->data.pointer.child_type->id == TypeTableEntryIdStruct); - fn_table_entry = node->data.fn_call_expr.fn_entry; - } else if (struct_type->id == TypeTableEntryIdMetaType) { - TypeTableEntry *child_type = get_type_for_type_node(first_param_expr); - - if (child_type->id == TypeTableEntryIdEnum) { - int param_count = node->data.fn_call_expr.params.length; - AstNode *arg1_node; - if (param_count == 1) { - arg1_node = node->data.fn_call_expr.params.at(0); - } else { - assert(param_count == 0); - arg1_node = nullptr; - } - return gen_enum_value_expr(g, fn_ref_expr, child_type, arg1_node); - } else if (child_type->id == TypeTableEntryIdStruct) { - struct_type = nullptr; - first_param_expr = nullptr; - fn_table_entry = node->data.fn_call_expr.fn_entry; - } else { - zig_unreachable(); - } - } else { - zig_unreachable(); - } } TypeTableEntry *fn_type; diff --git a/std/test_runner.zig b/std/test_runner.zig index 87d81a57a9..3ad1a9c3b9 100644 --- a/std/test_runner.zig +++ b/std/test_runner.zig @@ -17,9 +17,7 @@ pub fn main(args: [][]u8) -> %void { %%stderr.print_str(test_fn.name); %%stderr.print_str("..."); - // TODO support calling function pointers as fields directly - const fn_ptr = test_fn.func; - fn_ptr(); + test_fn.func(); %%stderr.print_str("OK\n"); diff --git a/test/run_tests.cpp b/test/run_tests.cpp index f3a7145f17..3f9eafcda0 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -25,6 +25,7 @@ struct TestCase { ZigList<const char *> compiler_args; ZigList<const char *> program_args; bool is_parseh; + bool is_self_hosted; }; static ZigList<TestCase*> test_cases = {0}; @@ -157,21 +158,6 @@ fn this_is_a_function() -> unreachable { } )SOURCE", "OK\n"); - add_simple_case("comments", R"SOURCE( -import "std.zig"; - -/** - * multi line doc comment - */ -fn another_function() {} - -/// this is a documentation comment -/// doc comment line 2 -pub fn main(args: [][]u8) -> %void { - %%stdout.printf(/* mid-line comment /* nested */ */ "OK\n"); -} - )SOURCE", "OK\n"); - { TestCase *tc = add_simple_case("multiple files with private function", R"SOURCE( import "std.zig"; @@ -2205,6 +2191,30 @@ extern void (*fn_ptr)(void); })SOURCE"); } +static void run_self_hosted_test(void) { + Buf zig_stderr = BUF_INIT; + Buf zig_stdout = BUF_INIT; + int return_code; + ZigList<const char *> args = {0}; + args.append("test"); + args.append("../test/self_hosted.zig"); + os_exec_process(zig_exe, args, &return_code, &zig_stderr, &zig_stdout); + + if (return_code) { + printf("\nSelf-hosted tests failed:\n"); + printf("./zig test ../test/self_hosted.zig\n"); + printf("%s\n", buf_ptr(&zig_stderr)); + exit(1); + } +} + +static void add_self_hosted_tests(void) { + TestCase *test_case = allocate<TestCase>(1); + test_case->case_name = "self hosted tests"; + test_case->is_self_hosted = true; + test_cases.append(test_case); +} + static void print_compiler_invocation(TestCase *test_case) { printf("%s", zig_exe); for (int i = 0; i < test_case->compiler_args.length; i += 1) { @@ -2214,6 +2224,10 @@ static void print_compiler_invocation(TestCase *test_case) { } static void run_test(TestCase *test_case) { + if (test_case->is_self_hosted) { + return run_self_hosted_test(); + } + for (int i = 0; i < test_case->source_files.length; i += 1) { TestSourceFile *test_source = &test_case->source_files.at(i); os_write_file( @@ -2359,6 +2373,7 @@ int main(int argc, char **argv) { add_compiling_test_cases(); add_compile_failure_test_cases(); add_parseh_test_cases(); + add_self_hosted_tests(); run_all_tests(reverse); cleanup(); } diff --git a/test/self_hosted.zig b/test/self_hosted.zig new file mode 100644 index 0000000000..cc5fd73750 --- /dev/null +++ b/test/self_hosted.zig @@ -0,0 +1,37 @@ +#attribute("test") +fn empty_function() {} + + + + +/** + * multi line doc comment + */ +/// this is a documentation comment +/// doc comment line 2 +#attribute("test") +fn comments() { + comments_f1(/* mid-line comment /* nested */ */ "OK\n"); +} + +fn comments_f1(s: []u8) {} + + + + +#attribute("test") +fn fn_call_of_struct_field() { + if (call_struct_field(Foo {.ptr = a_func,}) != 13) { + unreachable{}; + } +} + +struct Foo { + ptr: fn() -> i32, +} + +fn a_func() -> i32 { 13 } + +fn call_struct_field(foo: Foo) -> i32 { + return foo.ptr(); +} |
