authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-02-03 21:34:09-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-02-03 21:34:09-07:00
log2521afef699d40917db906ebb27a87c5ea287fbe
treea42906cf33e4199b81f8aba70153120cd4aa5c0f
parent5c310f43432ec723f8b7d449313c1ea20f1f2d78

add ability to call function pointer field

also introduce the self hosted tests closes #108

6 files changed, 161 insertions(+), 116 deletions(-)

src/all_types.hpp+4
...@@ -357,6 +357,7 @@ struct AstNodeFnCallExpr {...@@ -357,6 +357,7 @@ struct AstNodeFnCallExpr {
357 Expr resolved_expr;357 Expr resolved_expr;
358 FnTableEntry *fn_entry;358 FnTableEntry *fn_entry;
359 CastOp cast_op;359 CastOp cast_op;
360 TypeTableEntry *enum_type;
360 // if cast_op is CastOpArrayToString, this will be a pointer to361 // if cast_op is CastOpArrayToString, this will be a pointer to
361 // the string struct on the stack362 // the string struct on the stack
362 LLVMValueRef tmp_ptr;363 LLVMValueRef tmp_ptr;
...@@ -390,6 +391,9 @@ struct AstNodeFieldAccessExpr {...@@ -390,6 +391,9 @@ struct AstNodeFieldAccessExpr {
390 TypeEnumField *type_enum_field;391 TypeEnumField *type_enum_field;
391 Expr resolved_expr;392 Expr resolved_expr;
392 StructValExprCodeGen resolved_struct_val_expr; // for enum values393 StructValExprCodeGen resolved_struct_val_expr; // for enum values
394 bool is_fn_call;
395 TypeTableEntry *bare_struct_type;
396 bool is_member_fn;
393};397};
394398
395struct AstNodeDirective {399struct AstNodeDirective {
src/analyze.cpp+72-68
...@@ -28,6 +28,7 @@ static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import,...@@ -28,6 +28,7 @@ static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import,
28 TypeTableEntry *expected_type, AstNode *node);28 TypeTableEntry *expected_type, AstNode *node);
29static TypeTableEntry *resolve_expr_const_val_as_void(CodeGen *g, AstNode *node);29static TypeTableEntry *resolve_expr_const_val_as_void(CodeGen *g, AstNode *node);
30static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, FnTableEntry *fn);30static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, FnTableEntry *fn);
31static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node, TypeTableEntry *type);
31static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *node);32static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *node);
32static void analyze_top_level_decls_root(CodeGen *g, ImportTableEntry *import, AstNode *node);33static void analyze_top_level_decls_root(CodeGen *g, ImportTableEntry *import, AstNode *node);
3334
...@@ -2220,15 +2221,28 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i...@@ -2220,15 +2221,28 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
2220 TypeTableEntry *struct_type = analyze_expression(g, import, context, nullptr, struct_expr_node);2221 TypeTableEntry *struct_type = analyze_expression(g, import, context, nullptr, struct_expr_node);
2221 Buf *field_name = &node->data.field_access_expr.field_name;2222 Buf *field_name = &node->data.field_access_expr.field_name;
22222223
2224 bool wrapped_in_fn_call = node->data.field_access_expr.is_fn_call;
2225
2223 if (struct_type->id == TypeTableEntryIdStruct || (struct_type->id == TypeTableEntryIdPointer &&2226 if (struct_type->id == TypeTableEntryIdStruct || (struct_type->id == TypeTableEntryIdPointer &&
2224 struct_type->data.pointer.child_type->id == TypeTableEntryIdStruct))2227 struct_type->data.pointer.child_type->id == TypeTableEntryIdStruct))
2225 {2228 {
2226 TypeTableEntry *bare_struct_type = (struct_type->id == TypeTableEntryIdStruct) ?2229 TypeTableEntry *bare_struct_type = (struct_type->id == TypeTableEntryIdStruct) ?
2227 struct_type : struct_type->data.pointer.child_type;2230 struct_type : struct_type->data.pointer.child_type;
22282231
2232 node->data.field_access_expr.bare_struct_type = bare_struct_type;
2229 node->data.field_access_expr.type_struct_field = find_struct_type_field(bare_struct_type, field_name);2233 node->data.field_access_expr.type_struct_field = find_struct_type_field(bare_struct_type, field_name);
2230 if (node->data.field_access_expr.type_struct_field) {2234 if (node->data.field_access_expr.type_struct_field) {
2231 return node->data.field_access_expr.type_struct_field->type_entry;2235 return node->data.field_access_expr.type_struct_field->type_entry;
2236 } else if (wrapped_in_fn_call) {
2237 auto table_entry = bare_struct_type->data.structure.fn_table.maybe_get(field_name);
2238 if (table_entry) {
2239 node->data.field_access_expr.is_member_fn = true;
2240 return resolve_expr_const_val_as_fn(g, node, table_entry->value);
2241 } else {
2242 add_node_error(g, node, buf_sprintf("no member named '%s' in '%s'",
2243 buf_ptr(field_name), buf_ptr(&bare_struct_type->name)));
2244 return g->builtin_types.entry_invalid;
2245 }
2232 } else {2246 } else {
2233 add_node_error(g, node,2247 add_node_error(g, node,
2234 buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&struct_type->name)));2248 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...@@ -2251,6 +2265,8 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
22512265
2252 if (child_type->id == TypeTableEntryIdInvalid) {2266 if (child_type->id == TypeTableEntryIdInvalid) {
2253 return g->builtin_types.entry_invalid;2267 return g->builtin_types.entry_invalid;
2268 } else if (wrapped_in_fn_call) {
2269 return resolve_expr_const_val_as_type(g, node, child_type);
2254 } else if (child_type->id == TypeTableEntryIdEnum) {2270 } else if (child_type->id == TypeTableEntryIdEnum) {
2255 return analyze_enum_value_expr(g, import, context, node, nullptr, child_type, field_name);2271 return analyze_enum_value_expr(g, import, context, node, nullptr, child_type, field_name);
2256 } else if (child_type->id == TypeTableEntryIdStruct) {2272 } else if (child_type->id == TypeTableEntryIdStruct) {
...@@ -4128,72 +4144,7 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import...@@ -4128,72 +4144,7 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import
4128 }4144 }
41294145
4130 if (fn_ref_expr->type == NodeTypeFieldAccessExpr) {4146 if (fn_ref_expr->type == NodeTypeFieldAccessExpr) {
4131 fn_ref_expr->block_context = context;4147 fn_ref_expr->data.field_access_expr.is_fn_call = true;
4132 AstNode *first_param_expr = fn_ref_expr->data.field_access_expr.struct_expr;
4133 TypeTableEntry *struct_type = analyze_expression(g, import, context, nullptr, first_param_expr);
4134 Buf *name = &fn_ref_expr->data.field_access_expr.field_name;
4135 if (struct_type->id == TypeTableEntryIdStruct ||
4136 (struct_type->id == TypeTableEntryIdPointer &&
4137 struct_type->data.pointer.child_type->id == TypeTableEntryIdStruct))
4138 {
4139 TypeTableEntry *bare_struct_type = (struct_type->id == TypeTableEntryIdStruct) ?
4140 struct_type : struct_type->data.pointer.child_type;
4141
4142 auto table_entry = bare_struct_type->data.structure.fn_table.maybe_get(name);
4143 if (table_entry) {
4144 return analyze_fn_call_raw(g, import, context, expected_type, node,
4145 table_entry->value, bare_struct_type);
4146 } else {
4147 add_node_error(g, fn_ref_expr,
4148 buf_sprintf("no function named '%s' in '%s'",
4149 buf_ptr(name), buf_ptr(&bare_struct_type->name)));
4150 return g->builtin_types.entry_invalid;
4151 }
4152 } else if (struct_type->id == TypeTableEntryIdInvalid) {
4153 return struct_type;
4154 } else if (struct_type->id == TypeTableEntryIdMetaType) {
4155 TypeTableEntry *child_type = resolve_type(g, first_param_expr);
4156
4157 if (child_type->id == TypeTableEntryIdInvalid) {
4158 return g->builtin_types.entry_invalid;
4159 } else if (child_type->id == TypeTableEntryIdEnum) {
4160 Buf *field_name = &fn_ref_expr->data.field_access_expr.field_name;
4161 int param_count = node->data.fn_call_expr.params.length;
4162 if (param_count > 1) {
4163 add_node_error(g, first_executing_node(node->data.fn_call_expr.params.at(1)),
4164 buf_sprintf("enum values accept only one parameter"));
4165 return child_type;
4166 } else {
4167 AstNode *value_node;
4168 if (param_count == 1) {
4169 value_node = node->data.fn_call_expr.params.at(0);
4170 } else {
4171 value_node = nullptr;
4172 }
4173
4174 return analyze_enum_value_expr(g, import, context, fn_ref_expr, value_node,
4175 child_type, field_name);
4176 }
4177 } else if (child_type->id == TypeTableEntryIdStruct) {
4178 Buf *field_name = &fn_ref_expr->data.field_access_expr.field_name;
4179 auto entry = child_type->data.structure.fn_table.maybe_get(field_name);
4180 if (entry) {
4181 return analyze_fn_call_raw(g, import, context, expected_type, node,
4182 entry->value, nullptr);
4183 } else {
4184 add_node_error(g, node,
4185 buf_sprintf("struct '%s' has no function called '%s'",
4186 buf_ptr(&child_type->name), buf_ptr(field_name)));
4187 return g->builtin_types.entry_invalid;
4188 }
4189 } else {
4190 add_node_error(g, first_param_expr, buf_sprintf("member reference base type not struct or enum"));
4191 return g->builtin_types.entry_invalid;
4192 }
4193 } else {
4194 add_node_error(g, first_param_expr, buf_sprintf("member reference base type not struct or enum"));
4195 return g->builtin_types.entry_invalid;
4196 }
4197 }4148 }
41984149
4199 TypeTableEntry *invoke_type_entry = analyze_expression(g, import, context, nullptr, fn_ref_expr);4150 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...@@ -4207,9 +4158,62 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import
42074158
4208 if (const_val->ok) {4159 if (const_val->ok) {
4209 if (invoke_type_entry->id == TypeTableEntryIdMetaType) {4160 if (invoke_type_entry->id == TypeTableEntryIdMetaType) {
4210 return analyze_cast_expr(g, import, context, node);4161 if (fn_ref_expr->type == NodeTypeFieldAccessExpr) {
4162 TypeTableEntry *child_type = resolve_type(g, fn_ref_expr);
4163
4164 if (child_type->id == TypeTableEntryIdInvalid) {
4165 return g->builtin_types.entry_invalid;
4166 } else if (child_type->id == TypeTableEntryIdEnum) {
4167 Buf *field_name = &fn_ref_expr->data.field_access_expr.field_name;
4168 int param_count = node->data.fn_call_expr.params.length;
4169 if (param_count > 1) {
4170 add_node_error(g, first_executing_node(node->data.fn_call_expr.params.at(1)),
4171 buf_sprintf("enum values accept only one parameter"));
4172 return child_type;
4173 } else {
4174 AstNode *value_node;
4175 if (param_count == 1) {
4176 value_node = node->data.fn_call_expr.params.at(0);
4177 } else {
4178 value_node = nullptr;
4179 }
4180
4181 node->data.fn_call_expr.enum_type = child_type;
4182
4183 return analyze_enum_value_expr(g, import, context, fn_ref_expr, value_node,
4184 child_type, field_name);
4185 }
4186 } else if (child_type->id == TypeTableEntryIdStruct) {
4187 Buf *field_name = &fn_ref_expr->data.field_access_expr.field_name;
4188 auto entry = child_type->data.structure.fn_table.maybe_get(field_name);
4189 if (entry) {
4190 return analyze_fn_call_raw(g, import, context, expected_type, node,
4191 entry->value, nullptr);
4192 } else {
4193 add_node_error(g, node,
4194 buf_sprintf("struct '%s' has no function called '%s'",
4195 buf_ptr(&child_type->name), buf_ptr(field_name)));
4196 return g->builtin_types.entry_invalid;
4197 }
4198 } else {
4199 add_node_error(g, fn_ref_expr, buf_sprintf("member reference base type not struct or enum"));
4200 return g->builtin_types.entry_invalid;
4201 }
4202 } else {
4203 return analyze_cast_expr(g, import, context, node);
4204 }
4211 } else if (invoke_type_entry->id == TypeTableEntryIdFn) {4205 } else if (invoke_type_entry->id == TypeTableEntryIdFn) {
4212 return analyze_fn_call_raw(g, import, context, expected_type, node, const_val->data.x_fn, nullptr);4206 TypeTableEntry *bare_struct_type;
4207 if (fn_ref_expr->type == NodeTypeFieldAccessExpr &&
4208 fn_ref_expr->data.field_access_expr.is_member_fn)
4209 {
4210 bare_struct_type = fn_ref_expr->data.field_access_expr.bare_struct_type;
4211 } else {
4212 bare_struct_type = nullptr;
4213 }
4214
4215 return analyze_fn_call_raw(g, import, context, expected_type, node,
4216 const_val->data.x_fn, bare_struct_type);
4213 } else {4217 } else {
4214 add_node_error(g, fn_ref_expr,4218 add_node_error(g, fn_ref_expr,
4215 buf_sprintf("type '%s' not a function", buf_ptr(&invoke_type_entry->name)));4219 buf_sprintf("type '%s' not a function", buf_ptr(&invoke_type_entry->name)));
src/codegen.cpp+17-30
...@@ -544,41 +544,28 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {...@@ -544,41 +544,28 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
544 return gen_cast_expr(g, node);544 return gen_cast_expr(g, node);
545 }545 }
546546
547 FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry;
548 AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;547 AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
548 if (node->data.fn_call_expr.enum_type) {
549 int param_count = node->data.fn_call_expr.params.length;
550 AstNode *arg1_node;
551 if (param_count == 1) {
552 arg1_node = node->data.fn_call_expr.params.at(0);
553 } else {
554 assert(param_count == 0);
555 arg1_node = nullptr;
556 }
557 return gen_enum_value_expr(g, fn_ref_expr, node->data.fn_call_expr.enum_type, arg1_node);
558 }
559
560 FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry;
549 TypeTableEntry *struct_type = nullptr;561 TypeTableEntry *struct_type = nullptr;
550 AstNode *first_param_expr = nullptr;562 AstNode *first_param_expr = nullptr;
551 if (fn_ref_expr->type == NodeTypeFieldAccessExpr) {563
564 if (fn_ref_expr->type == NodeTypeFieldAccessExpr &&
565 fn_ref_expr->data.field_access_expr.is_member_fn)
566 {
552 first_param_expr = fn_ref_expr->data.field_access_expr.struct_expr;567 first_param_expr = fn_ref_expr->data.field_access_expr.struct_expr;
553 struct_type = get_expr_type(first_param_expr);568 struct_type = get_expr_type(first_param_expr);
554 if (struct_type->id == TypeTableEntryIdStruct) {
555 fn_table_entry = node->data.fn_call_expr.fn_entry;
556 } else if (struct_type->id == TypeTableEntryIdPointer) {
557 assert(struct_type->data.pointer.child_type->id == TypeTableEntryIdStruct);
558 fn_table_entry = node->data.fn_call_expr.fn_entry;
559 } else if (struct_type->id == TypeTableEntryIdMetaType) {
560 TypeTableEntry *child_type = get_type_for_type_node(first_param_expr);
561
562 if (child_type->id == TypeTableEntryIdEnum) {
563 int param_count = node->data.fn_call_expr.params.length;
564 AstNode *arg1_node;
565 if (param_count == 1) {
566 arg1_node = node->data.fn_call_expr.params.at(0);
567 } else {
568 assert(param_count == 0);
569 arg1_node = nullptr;
570 }
571 return gen_enum_value_expr(g, fn_ref_expr, child_type, arg1_node);
572 } else if (child_type->id == TypeTableEntryIdStruct) {
573 struct_type = nullptr;
574 first_param_expr = nullptr;
575 fn_table_entry = node->data.fn_call_expr.fn_entry;
576 } else {
577 zig_unreachable();
578 }
579 } else {
580 zig_unreachable();
581 }
582 }569 }
583570
584 TypeTableEntry *fn_type;571 TypeTableEntry *fn_type;
std/test_runner.zig+1-3
...@@ -17,9 +17,7 @@ pub fn main(args: [][]u8) -> %void {...@@ -17,9 +17,7 @@ pub fn main(args: [][]u8) -> %void {
17 %%stderr.print_str(test_fn.name);17 %%stderr.print_str(test_fn.name);
18 %%stderr.print_str("...");18 %%stderr.print_str("...");
1919
20 // TODO support calling function pointers as fields directly20 test_fn.func();
21 const fn_ptr = test_fn.func;
22 fn_ptr();
2321
2422
25 %%stderr.print_str("OK\n");23 %%stderr.print_str("OK\n");
test/run_tests.cpp+30-15
...@@ -25,6 +25,7 @@ struct TestCase {...@@ -25,6 +25,7 @@ struct TestCase {
25 ZigList<const char *> compiler_args;25 ZigList<const char *> compiler_args;
26 ZigList<const char *> program_args;26 ZigList<const char *> program_args;
27 bool is_parseh;27 bool is_parseh;
28 bool is_self_hosted;
28};29};
2930
30static ZigList<TestCase*> test_cases = {0};31static ZigList<TestCase*> test_cases = {0};
...@@ -157,21 +158,6 @@ fn this_is_a_function() -> unreachable {...@@ -157,21 +158,6 @@ fn this_is_a_function() -> unreachable {
157}158}
158 )SOURCE", "OK\n");159 )SOURCE", "OK\n");
159160
160 add_simple_case("comments", R"SOURCE(
161import "std.zig";
162
163/**
164 * multi line doc comment
165 */
166fn another_function() {}
167
168/// this is a documentation comment
169/// doc comment line 2
170pub fn main(args: [][]u8) -> %void {
171 %%stdout.printf(/* mid-line comment /* nested */ */ "OK\n");
172}
173 )SOURCE", "OK\n");
174
175 {161 {
176 TestCase *tc = add_simple_case("multiple files with private function", R"SOURCE(162 TestCase *tc = add_simple_case("multiple files with private function", R"SOURCE(
177import "std.zig";163import "std.zig";
...@@ -2205,6 +2191,30 @@ extern void (*fn_ptr)(void);...@@ -2205,6 +2191,30 @@ extern void (*fn_ptr)(void);
2205})SOURCE");2191})SOURCE");
2206}2192}
22072193
2194static void run_self_hosted_test(void) {
2195 Buf zig_stderr = BUF_INIT;
2196 Buf zig_stdout = BUF_INIT;
2197 int return_code;
2198 ZigList<const char *> args = {0};
2199 args.append("test");
2200 args.append("../test/self_hosted.zig");
2201 os_exec_process(zig_exe, args, &return_code, &zig_stderr, &zig_stdout);
2202
2203 if (return_code) {
2204 printf("\nSelf-hosted tests failed:\n");
2205 printf("./zig test ../test/self_hosted.zig\n");
2206 printf("%s\n", buf_ptr(&zig_stderr));
2207 exit(1);
2208 }
2209}
2210
2211static void add_self_hosted_tests(void) {
2212 TestCase *test_case = allocate<TestCase>(1);
2213 test_case->case_name = "self hosted tests";
2214 test_case->is_self_hosted = true;
2215 test_cases.append(test_case);
2216}
2217
2208static void print_compiler_invocation(TestCase *test_case) {2218static void print_compiler_invocation(TestCase *test_case) {
2209 printf("%s", zig_exe);2219 printf("%s", zig_exe);
2210 for (int i = 0; i < test_case->compiler_args.length; i += 1) {2220 for (int i = 0; i < test_case->compiler_args.length; i += 1) {
...@@ -2214,6 +2224,10 @@ static void print_compiler_invocation(TestCase *test_case) {...@@ -2214,6 +2224,10 @@ static void print_compiler_invocation(TestCase *test_case) {
2214}2224}
22152225
2216static void run_test(TestCase *test_case) {2226static void run_test(TestCase *test_case) {
2227 if (test_case->is_self_hosted) {
2228 return run_self_hosted_test();
2229 }
2230
2217 for (int i = 0; i < test_case->source_files.length; i += 1) {2231 for (int i = 0; i < test_case->source_files.length; i += 1) {
2218 TestSourceFile *test_source = &test_case->source_files.at(i);2232 TestSourceFile *test_source = &test_case->source_files.at(i);
2219 os_write_file(2233 os_write_file(
...@@ -2359,6 +2373,7 @@ int main(int argc, char **argv) {...@@ -2359,6 +2373,7 @@ int main(int argc, char **argv) {
2359 add_compiling_test_cases();2373 add_compiling_test_cases();
2360 add_compile_failure_test_cases();2374 add_compile_failure_test_cases();
2361 add_parseh_test_cases();2375 add_parseh_test_cases();
2376 add_self_hosted_tests();
2362 run_all_tests(reverse);2377 run_all_tests(reverse);
2363 cleanup();2378 cleanup();
2364}2379}
test/self_hosted.zig created+37
...@@ -0,0 +1,37 @@
1#attribute("test")
2fn empty_function() {}
3
4
5
6
7/**
8 * multi line doc comment
9 */
10/// this is a documentation comment
11/// doc comment line 2
12#attribute("test")
13fn comments() {
14 comments_f1(/* mid-line comment /* nested */ */ "OK\n");
15}
16
17fn comments_f1(s: []u8) {}
18
19
20
21
22#attribute("test")
23fn fn_call_of_struct_field() {
24 if (call_struct_field(Foo {.ptr = a_func,}) != 13) {
25 unreachable{};
26 }
27}
28
29struct Foo {
30 ptr: fn() -> i32,
31}
32
33fn a_func() -> i32 { 13 }
34
35fn call_struct_field(foo: Foo) -> i32 {
36 return foo.ptr();
37}