mirror of
https://github.com/ziglang/zig.git
synced 2026-02-20 16:24:51 +00:00
add ability to call function pointer field
also introduce the self hosted tests closes #108
This commit is contained in:
parent
5c310f4343
commit
2521afef69
@ -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 {
|
||||
|
||||
140
src/analyze.cpp
140
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)));
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
37
test/self_hosted.zig
Normal file
37
test/self_hosted.zig
Normal file
@ -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();
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user