| author | |
| committer | |
| log | 58e375d0a1423c04f4d3faabe4d84bfc11028d56 |
| tree | 733056bbfef07b984aabaea612d1dae67f698f68 |
| parent | 29f24e3c5066e7cb28876d40a811a4a64f9d4b33 |
8 files changed, 168 insertions(+), 53 deletions(-)
README.md-1| ... | @@ -43,7 +43,6 @@ make | ... | @@ -43,7 +43,6 @@ make |
| 43 | ## Roadmap | 43 | ## Roadmap |
| 44 | 44 | ||
| 45 | * variable declarations and assignment expressions | 45 | * variable declarations and assignment expressions |
| 46 | * Multiple files | ||
| 47 | * Type checking | 46 | * Type checking |
| 48 | * inline assembly and syscalls | 47 | * inline assembly and syscalls |
| 49 | * running code at compile time | 48 | * running code at compile time |
example/multiple_files/foo.zig+1-1| ... | @@ -6,6 +6,6 @@ fn private_function() { | ... | @@ -6,6 +6,6 @@ fn private_function() { |
| 6 | puts("it works!"); | 6 | puts("it works!"); |
| 7 | } | 7 | } |
| 8 | 8 | ||
| 9 | fn print_text() { | 9 | pub fn print_text() { |
| 10 | private_function(); | 10 | private_function(); |
| 11 | } | 11 | } |
example/multiple_files/libc.zig+2-2| ... | @@ -1,5 +1,5 @@ | ... | @@ -1,5 +1,5 @@ |
| 1 | #link("c") | 1 | #link("c") |
| 2 | extern { | 2 | extern { |
| 3 | fn puts(s: *mut u8) -> i32; | 3 | pub fn puts(s: *mut u8) -> i32; |
| 4 | fn exit(code: i32) -> unreachable; | 4 | pub fn exit(code: i32) -> unreachable; |
| 5 | } | 5 | } |
example/multiple_files/main.zig+1-1| ... | @@ -3,7 +3,7 @@ export executable "test"; | ... | @@ -3,7 +3,7 @@ export executable "test"; |
| 3 | use "libc.zig"; | 3 | use "libc.zig"; |
| 4 | use "foo.zig"; | 4 | use "foo.zig"; |
| 5 | 5 | ||
| 6 | fn _start() -> unreachable { | 6 | export fn _start() -> unreachable { |
| 7 | private_function(); | 7 | private_function(); |
| 8 | } | 8 | } |
| 9 | 9 |
src/analyze.cpp+73-30| ... | @@ -137,6 +137,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, | ... | @@ -137,6 +137,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, |
| 137 | AstNode *fn_decl = node->data.extern_block.fn_decls.at(fn_decl_i); | 137 | AstNode *fn_decl = node->data.extern_block.fn_decls.at(fn_decl_i); |
| 138 | assert(fn_decl->type == NodeTypeFnDecl); | 138 | assert(fn_decl->type == NodeTypeFnDecl); |
| 139 | AstNode *fn_proto = fn_decl->data.fn_decl.fn_proto; | 139 | AstNode *fn_proto = fn_decl->data.fn_decl.fn_proto; |
| 140 | bool is_pub = (fn_proto->data.fn_proto.visib_mod == FnProtoVisibModPub); | ||
| 140 | resolve_function_proto(g, fn_proto); | 141 | resolve_function_proto(g, fn_proto); |
| 141 | Buf *name = &fn_proto->data.fn_proto.name; | 142 | Buf *name = &fn_proto->data.fn_proto.name; |
| 142 | 143 | ||
| ... | @@ -145,7 +146,12 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, | ... | @@ -145,7 +146,12 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, |
| 145 | fn_table_entry->is_extern = true; | 146 | fn_table_entry->is_extern = true; |
| 146 | fn_table_entry->calling_convention = LLVMCCallConv; | 147 | fn_table_entry->calling_convention = LLVMCCallConv; |
| 147 | fn_table_entry->import_entry = import; | 148 | fn_table_entry->import_entry = import; |
| 148 | g->fn_table.put(name, fn_table_entry); | 149 | |
| 150 | g->fn_protos.append(fn_table_entry); | ||
| 151 | import->fn_table.put(name, fn_table_entry); | ||
| 152 | if (is_pub) { | ||
| 153 | g->fn_table.put(name, fn_table_entry); | ||
| 154 | } | ||
| 149 | } | 155 | } |
| 150 | break; | 156 | break; |
| 151 | case NodeTypeFnDef: | 157 | case NodeTypeFnDef: |
| ... | @@ -153,27 +159,44 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, | ... | @@ -153,27 +159,44 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, |
| 153 | AstNode *proto_node = node->data.fn_def.fn_proto; | 159 | AstNode *proto_node = node->data.fn_def.fn_proto; |
| 154 | assert(proto_node->type == NodeTypeFnProto); | 160 | assert(proto_node->type == NodeTypeFnProto); |
| 155 | Buf *proto_name = &proto_node->data.fn_proto.name; | 161 | Buf *proto_name = &proto_node->data.fn_proto.name; |
| 156 | auto entry = g->fn_table.maybe_get(proto_name); | 162 | auto entry = import->fn_table.maybe_get(proto_name); |
| 163 | bool skip = false; | ||
| 164 | bool is_internal = (proto_node->data.fn_proto.visib_mod != FnProtoVisibModExport); | ||
| 165 | bool is_pub = (proto_node->data.fn_proto.visib_mod == FnProtoVisibModPub); | ||
| 157 | if (entry) { | 166 | if (entry) { |
| 158 | add_node_error(g, node, | 167 | add_node_error(g, node, |
| 159 | buf_sprintf("redefinition of '%s'", buf_ptr(proto_name))); | 168 | buf_sprintf("redefinition of '%s'", buf_ptr(proto_name))); |
| 160 | assert(!node->codegen_node); | 169 | assert(!node->codegen_node); |
| 161 | node->codegen_node = allocate<CodeGenNode>(1); | 170 | node->codegen_node = allocate<CodeGenNode>(1); |
| 162 | node->codegen_node->data.fn_def_node.skip = true; | 171 | node->codegen_node->data.fn_def_node.skip = true; |
| 163 | } else { | 172 | skip = true; |
| 173 | } else if (is_pub) { | ||
| 174 | auto entry = g->fn_table.maybe_get(proto_name); | ||
| 175 | if (entry) { | ||
| 176 | add_node_error(g, node, | ||
| 177 | buf_sprintf("redefinition of '%s'", buf_ptr(proto_name))); | ||
| 178 | assert(!node->codegen_node); | ||
| 179 | node->codegen_node = allocate<CodeGenNode>(1); | ||
| 180 | node->codegen_node->data.fn_def_node.skip = true; | ||
| 181 | skip = true; | ||
| 182 | } | ||
| 183 | } | ||
| 184 | if (!skip) { | ||
| 164 | FnTableEntry *fn_table_entry = allocate<FnTableEntry>(1); | 185 | FnTableEntry *fn_table_entry = allocate<FnTableEntry>(1); |
| 165 | fn_table_entry->import_entry = import; | 186 | fn_table_entry->import_entry = import; |
| 166 | fn_table_entry->proto_node = proto_node; | 187 | fn_table_entry->proto_node = proto_node; |
| 167 | fn_table_entry->fn_def_node = node; | 188 | fn_table_entry->fn_def_node = node; |
| 168 | fn_table_entry->internal_linkage = proto_node->data.fn_proto.visib_mod != FnProtoVisibModExport; | 189 | fn_table_entry->internal_linkage = is_internal; |
| 169 | if (fn_table_entry->internal_linkage) { | 190 | fn_table_entry->calling_convention = is_internal ? LLVMFastCallConv : LLVMCCallConv; |
| 170 | fn_table_entry->calling_convention = LLVMFastCallConv; | 191 | |
| 171 | } else { | 192 | g->fn_protos.append(fn_table_entry); |
| 172 | fn_table_entry->calling_convention = LLVMCCallConv; | ||
| 173 | } | ||
| 174 | g->fn_table.put(proto_name, fn_table_entry); | ||
| 175 | g->fn_defs.append(fn_table_entry); | 193 | g->fn_defs.append(fn_table_entry); |
| 176 | 194 | ||
| 195 | import->fn_table.put(proto_name, fn_table_entry); | ||
| 196 | if (is_pub) { | ||
| 197 | g->fn_table.put(proto_name, fn_table_entry); | ||
| 198 | } | ||
| 199 | |||
| 177 | resolve_function_proto(g, proto_node); | 200 | resolve_function_proto(g, proto_node); |
| 178 | } | 201 | } |
| 179 | } | 202 | } |
| ... | @@ -297,28 +320,31 @@ static void check_fn_def_control_flow(CodeGen *g, AstNode *node) { | ... | @@ -297,28 +320,31 @@ static void check_fn_def_control_flow(CodeGen *g, AstNode *node) { |
| 297 | } | 320 | } |
| 298 | } | 321 | } |
| 299 | 322 | ||
| 300 | static void analyze_expression(CodeGen *g, AstNode *node) { | 323 | static void analyze_expression(CodeGen *g, ImportTableEntry *import, AstNode *node) { |
| 301 | switch (node->type) { | 324 | switch (node->type) { |
| 302 | case NodeTypeBlock: | 325 | case NodeTypeBlock: |
| 303 | for (int i = 0; i < node->data.block.statements.length; i += 1) { | 326 | for (int i = 0; i < node->data.block.statements.length; i += 1) { |
| 304 | AstNode *child = node->data.block.statements.at(i); | 327 | AstNode *child = node->data.block.statements.at(i); |
| 305 | analyze_expression(g, child); | 328 | analyze_expression(g, import, child); |
| 306 | } | 329 | } |
| 307 | break; | 330 | break; |
| 308 | case NodeTypeReturnExpr: | 331 | case NodeTypeReturnExpr: |
| 309 | if (node->data.return_expr.expr) { | 332 | if (node->data.return_expr.expr) { |
| 310 | analyze_expression(g, node->data.return_expr.expr); | 333 | analyze_expression(g, import, node->data.return_expr.expr); |
| 311 | } | 334 | } |
| 312 | break; | 335 | break; |
| 313 | case NodeTypeBinOpExpr: | 336 | case NodeTypeBinOpExpr: |
| 314 | analyze_expression(g, node->data.bin_op_expr.op1); | 337 | analyze_expression(g, import, node->data.bin_op_expr.op1); |
| 315 | analyze_expression(g, node->data.bin_op_expr.op2); | 338 | analyze_expression(g, import, node->data.bin_op_expr.op2); |
| 316 | break; | 339 | break; |
| 317 | case NodeTypeFnCallExpr: | 340 | case NodeTypeFnCallExpr: |
| 318 | { | 341 | { |
| 319 | Buf *name = hack_get_fn_call_name(g, node->data.fn_call_expr.fn_ref_expr); | 342 | Buf *name = hack_get_fn_call_name(g, node->data.fn_call_expr.fn_ref_expr); |
| 320 | 343 | ||
| 321 | auto entry = g->fn_table.maybe_get(name); | 344 | auto entry = import->fn_table.maybe_get(name); |
| 345 | if (!entry) | ||
| 346 | entry = g->fn_table.maybe_get(name); | ||
| 347 | |||
| 322 | if (!entry) { | 348 | if (!entry) { |
| 323 | add_node_error(g, node, | 349 | add_node_error(g, node, |
| 324 | buf_sprintf("undefined function: '%s'", buf_ptr(name))); | 350 | buf_sprintf("undefined function: '%s'", buf_ptr(name))); |
| ... | @@ -336,7 +362,7 @@ static void analyze_expression(CodeGen *g, AstNode *node) { | ... | @@ -336,7 +362,7 @@ static void analyze_expression(CodeGen *g, AstNode *node) { |
| 336 | 362 | ||
| 337 | for (int i = 0; i < node->data.fn_call_expr.params.length; i += 1) { | 363 | for (int i = 0; i < node->data.fn_call_expr.params.length; i += 1) { |
| 338 | AstNode *child = node->data.fn_call_expr.params.at(i); | 364 | AstNode *child = node->data.fn_call_expr.params.at(i); |
| 339 | analyze_expression(g, child); | 365 | analyze_expression(g, import, child); |
| 340 | } | 366 | } |
| 341 | break; | 367 | break; |
| 342 | } | 368 | } |
| ... | @@ -366,7 +392,7 @@ static void analyze_expression(CodeGen *g, AstNode *node) { | ... | @@ -366,7 +392,7 @@ static void analyze_expression(CodeGen *g, AstNode *node) { |
| 366 | } | 392 | } |
| 367 | } | 393 | } |
| 368 | 394 | ||
| 369 | static void analyze_top_level_declaration(CodeGen *g, AstNode *node) { | 395 | static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, AstNode *node) { |
| 370 | switch (node->type) { | 396 | switch (node->type) { |
| 371 | case NodeTypeFnDef: | 397 | case NodeTypeFnDef: |
| 372 | { | 398 | { |
| ... | @@ -387,7 +413,7 @@ static void analyze_top_level_declaration(CodeGen *g, AstNode *node) { | ... | @@ -387,7 +413,7 @@ static void analyze_top_level_declaration(CodeGen *g, AstNode *node) { |
| 387 | } | 413 | } |
| 388 | 414 | ||
| 389 | check_fn_def_control_flow(g, node); | 415 | check_fn_def_control_flow(g, node); |
| 390 | analyze_expression(g, node->data.fn_def.body); | 416 | analyze_expression(g, import, node->data.fn_def.body); |
| 391 | } | 417 | } |
| 392 | break; | 418 | break; |
| 393 | 419 | ||
| ... | @@ -423,33 +449,50 @@ static void analyze_top_level_declaration(CodeGen *g, AstNode *node) { | ... | @@ -423,33 +449,50 @@ static void analyze_top_level_declaration(CodeGen *g, AstNode *node) { |
| 423 | } | 449 | } |
| 424 | } | 450 | } |
| 425 | 451 | ||
| 426 | static void analyze_root(CodeGen *g, ImportTableEntry *import, AstNode *node) { | 452 | static void find_function_declarations_root(CodeGen *g, ImportTableEntry *import, AstNode *node) { |
| 427 | assert(node->type == NodeTypeRoot); | 453 | assert(node->type == NodeTypeRoot); |
| 428 | 454 | ||
| 429 | // find function declarations | ||
| 430 | for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) { | 455 | for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) { |
| 431 | AstNode *child = node->data.root.top_level_decls.at(i); | 456 | AstNode *child = node->data.root.top_level_decls.at(i); |
| 432 | preview_function_declarations(g, import, child); | 457 | preview_function_declarations(g, import, child); |
| 433 | } | 458 | } |
| 434 | 459 | ||
| 460 | } | ||
| 461 | |||
| 462 | static void analyze_top_level_decls_root(CodeGen *g, ImportTableEntry *import, AstNode *node) { | ||
| 463 | assert(node->type == NodeTypeRoot); | ||
| 464 | |||
| 435 | for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) { | 465 | for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) { |
| 436 | AstNode *child = node->data.root.top_level_decls.at(i); | 466 | AstNode *child = node->data.root.top_level_decls.at(i); |
| 437 | analyze_top_level_declaration(g, child); | 467 | analyze_top_level_declaration(g, import, child); |
| 438 | } | 468 | } |
| 439 | |||
| 440 | } | 469 | } |
| 441 | 470 | ||
| 442 | void semantic_analyze(CodeGen *g) { | 471 | void semantic_analyze(CodeGen *g) { |
| 443 | auto it = g->import_table.entry_iterator(); | 472 | { |
| 444 | for (;;) { | 473 | auto it = g->import_table.entry_iterator(); |
| 445 | auto *entry = it.next(); | 474 | for (;;) { |
| 446 | if (!entry) | 475 | auto *entry = it.next(); |
| 447 | break; | 476 | if (!entry) |
| 477 | break; | ||
| 448 | 478 | ||
| 449 | ImportTableEntry *import = entry->value; | 479 | ImportTableEntry *import = entry->value; |
| 450 | analyze_root(g, import, import->root); | 480 | find_function_declarations_root(g, import, import->root); |
| 481 | } | ||
| 482 | } | ||
| 483 | { | ||
| 484 | auto it = g->import_table.entry_iterator(); | ||
| 485 | for (;;) { | ||
| 486 | auto *entry = it.next(); | ||
| 487 | if (!entry) | ||
| 488 | break; | ||
| 489 | |||
| 490 | ImportTableEntry *import = entry->value; | ||
| 491 | analyze_top_level_decls_root(g, import, import->root); | ||
| 492 | } | ||
| 451 | } | 493 | } |
| 452 | 494 | ||
| 495 | |||
| 453 | if (!g->root_out_name) { | 496 | if (!g->root_out_name) { |
| 454 | add_node_error(g, g->root_import->root, | 497 | add_node_error(g, g->root_import->root, |
| 455 | buf_sprintf("missing export declaration and output name not provided")); | 498 | buf_sprintf("missing export declaration and output name not provided")); |
src/codegen.cpp+13-11| ... | @@ -125,7 +125,13 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { | ... | @@ -125,7 +125,13 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { |
| 125 | 125 | ||
| 126 | Buf *name = hack_get_fn_call_name(g, node->data.fn_call_expr.fn_ref_expr); | 126 | Buf *name = hack_get_fn_call_name(g, node->data.fn_call_expr.fn_ref_expr); |
| 127 | 127 | ||
| 128 | FnTableEntry *fn_table_entry = g->fn_table.get(name); | 128 | FnTableEntry *fn_table_entry; |
| 129 | auto entry = g->cur_fn->import_entry->fn_table.maybe_get(name); | ||
| 130 | if (entry) | ||
| 131 | fn_table_entry = entry->value; | ||
| 132 | else | ||
| 133 | fn_table_entry = g->fn_table.get(name); | ||
| 134 | |||
| 129 | assert(fn_table_entry->proto_node->type == NodeTypeFnProto); | 135 | assert(fn_table_entry->proto_node->type == NodeTypeFnProto); |
| 130 | int expected_param_count = fn_table_entry->proto_node->data.fn_proto.params.length; | 136 | int expected_param_count = fn_table_entry->proto_node->data.fn_proto.params.length; |
| 131 | int actual_param_count = node->data.fn_call_expr.params.length; | 137 | int actual_param_count = node->data.fn_call_expr.params.length; |
| ... | @@ -478,13 +484,8 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -478,13 +484,8 @@ static void do_code_gen(CodeGen *g) { |
| 478 | 484 | ||
| 479 | 485 | ||
| 480 | // Generate function prototypes | 486 | // Generate function prototypes |
| 481 | auto it = g->fn_table.entry_iterator(); | 487 | for (int i = 0; i < g->fn_protos.length; i += 1) { |
| 482 | for (;;) { | 488 | FnTableEntry *fn_table_entry = g->fn_protos.at(i); |
| 483 | auto *entry = it.next(); | ||
| 484 | if (!entry) | ||
| 485 | break; | ||
| 486 | |||
| 487 | FnTableEntry *fn_table_entry = entry->value; | ||
| 488 | 489 | ||
| 489 | AstNode *proto_node = fn_table_entry->proto_node; | 490 | AstNode *proto_node = fn_table_entry->proto_node; |
| 490 | assert(proto_node->type == NodeTypeFnProto); | 491 | assert(proto_node->type == NodeTypeFnProto); |
| ... | @@ -547,6 +548,7 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -547,6 +548,7 @@ static void do_code_gen(CodeGen *g) { |
| 547 | assert(codegen_node); | 548 | assert(codegen_node); |
| 548 | 549 | ||
| 549 | FnDefNode *codegen_fn_def = &codegen_node->data.fn_def_node; | 550 | FnDefNode *codegen_fn_def = &codegen_node->data.fn_def_node; |
| 551 | assert(codegen_fn_def); | ||
| 550 | codegen_fn_def->params = allocate<LLVMValueRef>(LLVMCountParams(fn)); | 552 | codegen_fn_def->params = allocate<LLVMValueRef>(LLVMCountParams(fn)); |
| 551 | LLVMGetParams(fn, codegen_fn_def->params); | 553 | LLVMGetParams(fn, codegen_fn_def->params); |
| 552 | 554 | ||
| ... | @@ -733,9 +735,9 @@ static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *source_path, Buf *sou | ... | @@ -733,9 +735,9 @@ static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *source_path, Buf *sou |
| 733 | if (!entry) { | 735 | if (!entry) { |
| 734 | Buf full_path = BUF_INIT; | 736 | Buf full_path = BUF_INIT; |
| 735 | os_path_join(g->root_source_dir, &top_level_decl->data.use.path, &full_path); | 737 | os_path_join(g->root_source_dir, &top_level_decl->data.use.path, &full_path); |
| 736 | Buf import_code = BUF_INIT; | 738 | Buf *import_code = buf_alloc(); |
| 737 | os_fetch_file_path(&full_path, &import_code); | 739 | os_fetch_file_path(&full_path, import_code); |
| 738 | codegen_add_code(g, &top_level_decl->data.use.path, &import_code); | 740 | codegen_add_code(g, &top_level_decl->data.use.path, import_code); |
| 739 | } | 741 | } |
| 740 | } | 742 | } |
| 741 | 743 |
src/semantic_info.hpp+7| ... | @@ -80,7 +80,14 @@ struct CodeGen { | ... | @@ -80,7 +80,14 @@ struct CodeGen { |
| 80 | Buf *root_source_dir; | 80 | Buf *root_source_dir; |
| 81 | Buf *root_out_name; | 81 | Buf *root_out_name; |
| 82 | ZigList<LLVMZigDIScope *> block_scopes; | 82 | ZigList<LLVMZigDIScope *> block_scopes; |
| 83 | |||
| 84 | // The function definitions this module includes. There must be a corresponding | ||
| 85 | // fn_protos entry. | ||
| 83 | ZigList<FnTableEntry *> fn_defs; | 86 | ZigList<FnTableEntry *> fn_defs; |
| 87 | // The function prototypes this module includes. In the case of external declarations, | ||
| 88 | // there will not be a corresponding fn_defs entry. | ||
| 89 | ZigList<FnTableEntry *> fn_protos; | ||
| 90 | |||
| 84 | OutType out_type; | 91 | OutType out_type; |
| 85 | FnTableEntry *cur_fn; | 92 | FnTableEntry *cur_fn; |
| 86 | bool c_stdint_used; | 93 | bool c_stdint_used; |
test/run_tests.cpp+71-7| ... | @@ -14,13 +14,13 @@ | ... | @@ -14,13 +14,13 @@ |
| 14 | 14 | ||
| 15 | struct TestSourceFile { | 15 | struct TestSourceFile { |
| 16 | const char *relative_path; | 16 | const char *relative_path; |
| 17 | const char *text; | 17 | const char *source_code; |
| 18 | }; | 18 | }; |
| 19 | 19 | ||
| 20 | struct TestCase { | 20 | struct TestCase { |
| 21 | const char *case_name; | 21 | const char *case_name; |
| 22 | const char *output; | 22 | const char *output; |
| 23 | const char *source; | 23 | ZigList<TestSourceFile> source_files; |
| 24 | ZigList<const char *> compile_errors; | 24 | ZigList<const char *> compile_errors; |
| 25 | ZigList<const char *> compiler_args; | 25 | ZigList<const char *> compiler_args; |
| 26 | ZigList<const char *> program_args; | 26 | ZigList<const char *> program_args; |
| ... | @@ -31,11 +31,20 @@ static const char *tmp_source_path = ".tmp_source.zig"; | ... | @@ -31,11 +31,20 @@ static const char *tmp_source_path = ".tmp_source.zig"; |
| 31 | static const char *tmp_exe_path = "./.tmp_exe"; | 31 | static const char *tmp_exe_path = "./.tmp_exe"; |
| 32 | static const char *zig_exe = "./zig"; | 32 | static const char *zig_exe = "./zig"; |
| 33 | 33 | ||
| 34 | static void add_simple_case(const char *case_name, const char *source, const char *output) { | 34 | static void add_source_file(TestCase *test_case, const char *path, const char *source) { |
| 35 | test_case->source_files.add_one(); | ||
| 36 | test_case->source_files.last().relative_path = path; | ||
| 37 | test_case->source_files.last().source_code = source; | ||
| 38 | } | ||
| 39 | |||
| 40 | static TestCase *add_simple_case(const char *case_name, const char *source, const char *output) { | ||
| 35 | TestCase *test_case = allocate<TestCase>(1); | 41 | TestCase *test_case = allocate<TestCase>(1); |
| 36 | test_case->case_name = case_name; | 42 | test_case->case_name = case_name; |
| 37 | test_case->output = output; | 43 | test_case->output = output; |
| 38 | test_case->source = source; | 44 | |
| 45 | test_case->source_files.resize(1); | ||
| 46 | test_case->source_files.at(0).relative_path = tmp_source_path; | ||
| 47 | test_case->source_files.at(0).source_code = source; | ||
| 39 | 48 | ||
| 40 | test_case->compiler_args.append("build"); | 49 | test_case->compiler_args.append("build"); |
| 41 | test_case->compiler_args.append(tmp_source_path); | 50 | test_case->compiler_args.append(tmp_source_path); |
| ... | @@ -52,15 +61,19 @@ static void add_simple_case(const char *case_name, const char *source, const cha | ... | @@ -52,15 +61,19 @@ static void add_simple_case(const char *case_name, const char *source, const cha |
| 52 | test_case->compiler_args.append("on"); | 61 | test_case->compiler_args.append("on"); |
| 53 | 62 | ||
| 54 | test_cases.append(test_case); | 63 | test_cases.append(test_case); |
| 64 | |||
| 65 | return test_case; | ||
| 55 | } | 66 | } |
| 56 | 67 | ||
| 57 | static void add_compile_fail_case(const char *case_name, const char *source, int count, ...) { | 68 | static TestCase *add_compile_fail_case(const char *case_name, const char *source, int count, ...) { |
| 58 | va_list ap; | 69 | va_list ap; |
| 59 | va_start(ap, count); | 70 | va_start(ap, count); |
| 60 | 71 | ||
| 61 | TestCase *test_case = allocate<TestCase>(1); | 72 | TestCase *test_case = allocate<TestCase>(1); |
| 62 | test_case->case_name = case_name; | 73 | test_case->case_name = case_name; |
| 63 | test_case->source = source; | 74 | test_case->source_files.resize(1); |
| 75 | test_case->source_files.at(0).relative_path = tmp_source_path; | ||
| 76 | test_case->source_files.at(0).source_code = source; | ||
| 64 | 77 | ||
| 65 | for (int i = 0; i < count; i += 1) { | 78 | for (int i = 0; i < count; i += 1) { |
| 66 | const char *arg = va_arg(ap, const char *); | 79 | const char *arg = va_arg(ap, const char *); |
| ... | @@ -78,6 +91,8 @@ static void add_compile_fail_case(const char *case_name, const char *source, int | ... | @@ -78,6 +91,8 @@ static void add_compile_fail_case(const char *case_name, const char *source, int |
| 78 | test_cases.append(test_case); | 91 | test_cases.append(test_case); |
| 79 | 92 | ||
| 80 | va_end(ap); | 93 | va_end(ap); |
| 94 | |||
| 95 | return test_case; | ||
| 81 | } | 96 | } |
| 82 | 97 | ||
| 83 | static void add_compiling_test_cases(void) { | 98 | static void add_compiling_test_cases(void) { |
| ... | @@ -135,6 +150,45 @@ static void add_compiling_test_cases(void) { | ... | @@ -135,6 +150,45 @@ static void add_compiling_test_cases(void) { |
| 135 | exit(0); | 150 | exit(0); |
| 136 | } | 151 | } |
| 137 | )SOURCE", "OK\n"); | 152 | )SOURCE", "OK\n"); |
| 153 | |||
| 154 | { | ||
| 155 | TestCase *tc = add_simple_case("multiple files with private function", R"SOURCE( | ||
| 156 | use "libc.zig"; | ||
| 157 | use "foo.zig"; | ||
| 158 | |||
| 159 | export fn _start() -> unreachable { | ||
| 160 | private_function(); | ||
| 161 | } | ||
| 162 | |||
| 163 | fn private_function() -> unreachable { | ||
| 164 | print_text(); | ||
| 165 | exit(0); | ||
| 166 | } | ||
| 167 | )SOURCE", "OK\n"); | ||
| 168 | |||
| 169 | add_source_file(tc, "libc.zig", R"SOURCE( | ||
| 170 | #link("c") | ||
| 171 | extern { | ||
| 172 | pub fn puts(s: *mut u8) -> i32; | ||
| 173 | pub fn exit(code: i32) -> unreachable; | ||
| 174 | } | ||
| 175 | )SOURCE"); | ||
| 176 | |||
| 177 | add_source_file(tc, "foo.zig", R"SOURCE( | ||
| 178 | use "libc.zig"; | ||
| 179 | |||
| 180 | // purposefully conflicting function with main source file | ||
| 181 | // but it's private so it should be OK | ||
| 182 | fn private_function() { | ||
| 183 | puts("OK"); | ||
| 184 | } | ||
| 185 | |||
| 186 | pub fn print_text() { | ||
| 187 | private_function(); | ||
| 188 | } | ||
| 189 | )SOURCE"); | ||
| 190 | } | ||
| 191 | |||
| 138 | } | 192 | } |
| 139 | 193 | ||
| 140 | static void add_compile_failure_test_cases(void) { | 194 | static void add_compile_failure_test_cases(void) { |
| ... | @@ -207,7 +261,12 @@ static void print_compiler_invokation(TestCase *test_case, Buf *zig_stderr) { | ... | @@ -207,7 +261,12 @@ static void print_compiler_invokation(TestCase *test_case, Buf *zig_stderr) { |
| 207 | } | 261 | } |
| 208 | 262 | ||
| 209 | static void run_test(TestCase *test_case) { | 263 | static void run_test(TestCase *test_case) { |
| 210 | os_write_file(buf_create_from_str(tmp_source_path), buf_create_from_str(test_case->source)); | 264 | for (int i = 0; i < test_case->source_files.length; i += 1) { |
| 265 | TestSourceFile *test_source = &test_case->source_files.at(i); | ||
| 266 | os_write_file( | ||
| 267 | buf_create_from_str(test_source->relative_path), | ||
| 268 | buf_create_from_str(test_source->source_code)); | ||
| 269 | } | ||
| 211 | 270 | ||
| 212 | Buf zig_stderr = BUF_INIT; | 271 | Buf zig_stderr = BUF_INIT; |
| 213 | Buf zig_stdout = BUF_INIT; | 272 | Buf zig_stdout = BUF_INIT; |
| ... | @@ -265,6 +324,11 @@ static void run_test(TestCase *test_case) { | ... | @@ -265,6 +324,11 @@ static void run_test(TestCase *test_case) { |
| 265 | printf("=======================================\n"); | 324 | printf("=======================================\n"); |
| 266 | exit(1); | 325 | exit(1); |
| 267 | } | 326 | } |
| 327 | |||
| 328 | for (int i = 0; i < test_case->source_files.length; i += 1) { | ||
| 329 | TestSourceFile *test_source = &test_case->source_files.at(i); | ||
| 330 | remove(test_source->relative_path); | ||
| 331 | } | ||
| 268 | } | 332 | } |
| 269 | 333 | ||
| 270 | static void run_all_tests(void) { | 334 | static void run_all_tests(void) { |