| author | |
| committer | |
| log | f8ca6c70c74db6e6f0d4462aa763adb6f1f41c7e |
| tree | 99bc3d06bb37abb267a2a59f3ba04523c2c430eb |
| parent | c89f77dd8e5005e60e8fb223c6c68b50566ac1ed |
14 files changed, 270 insertions(+), 27 deletions(-)
README.md+6-3| ... | @@ -45,7 +45,6 @@ make | ... | @@ -45,7 +45,6 @@ make |
| 45 | * variable declarations and assignment expressions | 45 | * variable declarations and assignment expressions |
| 46 | * Type checking | 46 | * Type checking |
| 47 | * loops | 47 | * loops |
| 48 | * labels and goto | ||
| 49 | * inline assembly and syscalls | 48 | * inline assembly and syscalls |
| 50 | * conditional compilation and ability to check target platform and architecture | 49 | * conditional compilation and ability to check target platform and architecture |
| 51 | * main function with command line arguments | 50 | * main function with command line arguments |
| ... | @@ -110,7 +109,9 @@ PointerType : token(Star) token(Const) Type | token(Star) token(Mut) Type | ... | @@ -110,7 +109,9 @@ PointerType : token(Star) token(Const) Type | token(Star) token(Mut) Type |
| 110 | 109 | ||
| 111 | Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace) | 110 | Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace) |
| 112 | 111 | ||
| 113 | Statement : NonBlockExpression token(Semicolon) | BlockExpression | 112 | Statement : Label | NonBlockExpression token(Semicolon) | BlockExpression |
| 113 | |||
| 114 | Label: token(Symbol) token(Colon) | ||
| 114 | 115 | ||
| 115 | Expression : BlockExpression | NonBlockExpression | 116 | Expression : BlockExpression | NonBlockExpression |
| 116 | 117 | ||
| ... | @@ -162,7 +163,9 @@ FnCallExpression : PrimaryExpression token(LParen) list(Expression, token(Comma) | ... | @@ -162,7 +163,9 @@ FnCallExpression : PrimaryExpression token(LParen) list(Expression, token(Comma) |
| 162 | 163 | ||
| 163 | PrefixOp : token(Not) | token(Dash) | token(Tilde) | 164 | PrefixOp : token(Not) | token(Dash) | token(Tilde) |
| 164 | 165 | ||
| 165 | PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | token(Symbol) | 166 | PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | token(Symbol) | Goto |
| 167 | |||
| 168 | Goto: token(Goto) token(Symbol) | ||
| 166 | 169 | ||
| 167 | GroupedExpression : token(LParen) Expression token(RParen) | 170 | GroupedExpression : token(LParen) Expression token(RParen) |
| 168 | ``` | 171 | ``` |
doc/vim/syntax/zig.vim+2-2| ... | @@ -1,13 +1,13 @@ | ... | @@ -1,13 +1,13 @@ |
| 1 | " Vim syntax file | 1 | " Vim syntax file |
| 2 | " Language: Zig | 2 | " Language: Zig |
| 3 | " Maintainer: Andrew Kelley | 3 | " Maintainer: Andrew Kelley |
| 4 | " Latest Revision: 27 November 2015 | 4 | " Latest Revision: 02 December 2015 |
| 5 | 5 | ||
| 6 | if exists("b:current_syntax") | 6 | if exists("b:current_syntax") |
| 7 | finish | 7 | finish |
| 8 | endif | 8 | endif |
| 9 | 9 | ||
| 10 | syn keyword zigKeyword fn return mut const extern unreachable export pub as use if else let void | 10 | syn keyword zigKeyword fn return mut const extern unreachable export pub as use if else let void goto |
| 11 | syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 | 11 | syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 |
| 12 | 12 | ||
| 13 | syn region zigCommentLine start="//" end="$" contains=zigTodo,@Spell | 13 | syn region zigCommentLine start="//" end="$" contains=zigTodo,@Spell |
example/hello_world/hello.zig+12-1| ... | @@ -6,7 +6,18 @@ extern { | ... | @@ -6,7 +6,18 @@ extern { |
| 6 | fn exit(code: i32) -> unreachable; | 6 | fn exit(code: i32) -> unreachable; |
| 7 | } | 7 | } |
| 8 | 8 | ||
| 9 | fn loop(a : i32) { | ||
| 10 | if a == 0 { | ||
| 11 | goto done; | ||
| 12 | } | ||
| 13 | puts("loop"); | ||
| 14 | loop(a - 1); | ||
| 15 | |||
| 16 | done: | ||
| 17 | return; | ||
| 18 | } | ||
| 19 | |||
| 9 | export fn _start() -> unreachable { | 20 | export fn _start() -> unreachable { |
| 10 | puts("Hello, world!"); | 21 | loop(3); |
| 11 | exit(0); | 22 | exit(0); |
| 12 | } | 23 | } |
src/analyze.cpp+65-7| ... | @@ -131,6 +131,25 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t | ... | @@ -131,6 +131,25 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 131 | resolve_type(g, node->data.fn_proto.return_type); | 131 | resolve_type(g, node->data.fn_proto.return_type); |
| 132 | } | 132 | } |
| 133 | 133 | ||
| 134 | static void preview_function_labels(CodeGen *g, AstNode *node, FnTableEntry *fn_table_entry) { | ||
| 135 | assert(node->type == NodeTypeBlock); | ||
| 136 | |||
| 137 | for (int i = 0; i < node->data.block.statements.length; i += 1) { | ||
| 138 | AstNode *label_node = node->data.block.statements.at(i); | ||
| 139 | if (label_node->type != NodeTypeLabel) | ||
| 140 | continue; | ||
| 141 | |||
| 142 | LabelTableEntry *label_entry = allocate<LabelTableEntry>(1); | ||
| 143 | label_entry->label_node = label_node; | ||
| 144 | Buf *name = &label_node->data.label.name; | ||
| 145 | fn_table_entry->label_table.put(name, label_entry); | ||
| 146 | |||
| 147 | assert(!label_node->codegen_node); | ||
| 148 | label_node->codegen_node = allocate<CodeGenNode>(1); | ||
| 149 | label_node->codegen_node->data.label_entry = label_entry; | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 134 | static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, AstNode *node) { | 153 | static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, AstNode *node) { |
| 135 | switch (node->type) { | 154 | switch (node->type) { |
| 136 | case NodeTypeExternBlock: | 155 | case NodeTypeExternBlock: |
| ... | @@ -158,6 +177,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, | ... | @@ -158,6 +177,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, |
| 158 | fn_table_entry->calling_convention = LLVMCCallConv; | 177 | fn_table_entry->calling_convention = LLVMCCallConv; |
| 159 | fn_table_entry->import_entry = import; | 178 | fn_table_entry->import_entry = import; |
| 160 | fn_table_entry->symbol_table.init(8); | 179 | fn_table_entry->symbol_table.init(8); |
| 180 | fn_table_entry->label_table.init(8); | ||
| 161 | 181 | ||
| 162 | resolve_function_proto(g, fn_proto, fn_table_entry); | 182 | resolve_function_proto(g, fn_proto, fn_table_entry); |
| 163 | 183 | ||
| ... | @@ -208,6 +228,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, | ... | @@ -208,6 +228,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, |
| 208 | fn_table_entry->internal_linkage = is_internal; | 228 | fn_table_entry->internal_linkage = is_internal; |
| 209 | fn_table_entry->calling_convention = is_internal ? LLVMFastCallConv : LLVMCCallConv; | 229 | fn_table_entry->calling_convention = is_internal ? LLVMFastCallConv : LLVMCCallConv; |
| 210 | fn_table_entry->symbol_table.init(8); | 230 | fn_table_entry->symbol_table.init(8); |
| 231 | fn_table_entry->label_table.init(8); | ||
| 211 | 232 | ||
| 212 | g->fn_protos.append(fn_table_entry); | 233 | g->fn_protos.append(fn_table_entry); |
| 213 | g->fn_defs.append(fn_table_entry); | 234 | g->fn_defs.append(fn_table_entry); |
| ... | @@ -222,6 +243,8 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, | ... | @@ -222,6 +243,8 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, |
| 222 | assert(!proto_node->codegen_node); | 243 | assert(!proto_node->codegen_node); |
| 223 | proto_node->codegen_node = allocate<CodeGenNode>(1); | 244 | proto_node->codegen_node = allocate<CodeGenNode>(1); |
| 224 | proto_node->codegen_node->data.fn_proto_node.fn_table_entry = fn_table_entry; | 245 | proto_node->codegen_node->data.fn_proto_node.fn_table_entry = fn_table_entry; |
| 246 | |||
| 247 | preview_function_labels(g, node->data.fn_def.body, fn_table_entry); | ||
| 225 | } | 248 | } |
| 226 | } | 249 | } |
| 227 | break; | 250 | break; |
| ... | @@ -290,6 +313,8 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, | ... | @@ -290,6 +313,8 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, |
| 290 | case NodeTypeCastExpr: | 313 | case NodeTypeCastExpr: |
| 291 | case NodeTypePrefixOpExpr: | 314 | case NodeTypePrefixOpExpr: |
| 292 | case NodeTypeIfExpr: | 315 | case NodeTypeIfExpr: |
| 316 | case NodeTypeLabel: | ||
| 317 | case NodeTypeGoto: | ||
| 293 | zig_unreachable(); | 318 | zig_unreachable(); |
| 294 | } | 319 | } |
| 295 | } | 320 | } |
| ... | @@ -339,6 +364,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, | ... | @@ -339,6 +364,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 339 | return_type = g->builtin_types.entry_void; | 364 | return_type = g->builtin_types.entry_void; |
| 340 | for (int i = 0; i < node->data.block.statements.length; i += 1) { | 365 | for (int i = 0; i < node->data.block.statements.length; i += 1) { |
| 341 | AstNode *child = node->data.block.statements.at(i); | 366 | AstNode *child = node->data.block.statements.at(i); |
| 367 | if (child->type == NodeTypeLabel) | ||
| 368 | continue; | ||
| 342 | if (return_type == g->builtin_types.entry_unreachable) { | 369 | if (return_type == g->builtin_types.entry_unreachable) { |
| 343 | if (child->type == NodeTypeVoid) { | 370 | if (child->type == NodeTypeVoid) { |
| 344 | // {unreachable;void;void} is allowed. | 371 | // {unreachable;void;void} is allowed. |
| ... | @@ -365,7 +392,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, | ... | @@ -365,7 +392,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 365 | 392 | ||
| 366 | if (actual_return_type == g->builtin_types.entry_unreachable) { | 393 | if (actual_return_type == g->builtin_types.entry_unreachable) { |
| 367 | // "return exit(0)" should just be "exit(0)". | 394 | // "return exit(0)" should just be "exit(0)". |
| 368 | add_node_error(g, node, buf_sprintf("returning is unreachable.")); | 395 | add_node_error(g, node, buf_sprintf("returning is unreachable")); |
| 369 | actual_return_type = g->builtin_types.entry_invalid; | 396 | actual_return_type = g->builtin_types.entry_invalid; |
| 370 | } | 397 | } |
| 371 | 398 | ||
| ... | @@ -373,7 +400,6 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, | ... | @@ -373,7 +400,6 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 373 | return_type = g->builtin_types.entry_unreachable; | 400 | return_type = g->builtin_types.entry_unreachable; |
| 374 | break; | 401 | break; |
| 375 | } | 402 | } |
| 376 | |||
| 377 | case NodeTypeVariableDeclaration: | 403 | case NodeTypeVariableDeclaration: |
| 378 | { | 404 | { |
| 379 | zig_panic("TODO: analyze variable declaration"); | 405 | zig_panic("TODO: analyze variable declaration"); |
| ... | @@ -382,6 +408,21 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, | ... | @@ -382,6 +408,21 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 382 | break; | 408 | break; |
| 383 | } | 409 | } |
| 384 | 410 | ||
| 411 | case NodeTypeGoto: | ||
| 412 | { | ||
| 413 | FnTableEntry *fn_table_entry = get_context_fn_entry(context); | ||
| 414 | auto table_entry = fn_table_entry->label_table.maybe_get(&node->data.go_to.name); | ||
| 415 | if (table_entry) { | ||
| 416 | assert(!node->codegen_node); | ||
| 417 | node->codegen_node = allocate<CodeGenNode>(1); | ||
| 418 | node->codegen_node->data.label_entry = table_entry->value; | ||
| 419 | } else { | ||
| 420 | add_node_error(g, node, | ||
| 421 | buf_sprintf("use of undeclared label '%s'", buf_ptr(&node->data.go_to.name))); | ||
| 422 | } | ||
| 423 | return_type = g->builtin_types.entry_unreachable; | ||
| 424 | break; | ||
| 425 | } | ||
| 385 | case NodeTypeBinOpExpr: | 426 | case NodeTypeBinOpExpr: |
| 386 | { | 427 | { |
| 387 | switch (node->data.bin_op_expr.bin_op) { | 428 | switch (node->data.bin_op_expr.bin_op) { |
| ... | @@ -563,8 +604,18 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, | ... | @@ -563,8 +604,18 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 563 | TypeTableEntry *then_type = analyze_expression(g, import, context, expected_type, | 604 | TypeTableEntry *then_type = analyze_expression(g, import, context, expected_type, |
| 564 | node->data.if_expr.then_block); | 605 | node->data.if_expr.then_block); |
| 565 | 606 | ||
| 566 | check_type_compatibility(g, node, expected_type, else_type); | 607 | TypeTableEntry *primary_type; |
| 567 | return_type = then_type; | 608 | TypeTableEntry *other_type; |
| 609 | if (then_type == g->builtin_types.entry_unreachable) { | ||
| 610 | primary_type = else_type; | ||
| 611 | other_type = then_type; | ||
| 612 | } else { | ||
| 613 | primary_type = then_type; | ||
| 614 | other_type = else_type; | ||
| 615 | } | ||
| 616 | |||
| 617 | check_type_compatibility(g, node, expected_type, other_type); | ||
| 618 | return_type = primary_type; | ||
| 568 | break; | 619 | break; |
| 569 | } | 620 | } |
| 570 | case NodeTypeDirective: | 621 | case NodeTypeDirective: |
| ... | @@ -577,14 +628,19 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, | ... | @@ -577,14 +628,19 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 577 | case NodeTypeExternBlock: | 628 | case NodeTypeExternBlock: |
| 578 | case NodeTypeFnDef: | 629 | case NodeTypeFnDef: |
| 579 | case NodeTypeUse: | 630 | case NodeTypeUse: |
| 631 | case NodeTypeLabel: | ||
| 580 | zig_unreachable(); | 632 | zig_unreachable(); |
| 581 | } | 633 | } |
| 582 | assert(return_type); | 634 | assert(return_type); |
| 583 | check_type_compatibility(g, node, expected_type, return_type); | 635 | check_type_compatibility(g, node, expected_type, return_type); |
| 584 | 636 | ||
| 585 | assert(!node->codegen_node); | 637 | if (node->codegen_node) { |
| 586 | node->codegen_node = allocate<CodeGenNode>(1); | 638 | assert(node->type == NodeTypeGoto); |
| 587 | node->codegen_node->data.expr_node.type_entry = return_type; | 639 | } else { |
| 640 | assert(node->type != NodeTypeGoto); | ||
| 641 | node->codegen_node = allocate<CodeGenNode>(1); | ||
| 642 | } | ||
| 643 | node->codegen_node->expr_node.type_entry = return_type; | ||
| 588 | 644 | ||
| 589 | return return_type; | 645 | return return_type; |
| 590 | } | 646 | } |
| ... | @@ -652,6 +708,8 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, | ... | @@ -652,6 +708,8 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, |
| 652 | case NodeTypeCastExpr: | 708 | case NodeTypeCastExpr: |
| 653 | case NodeTypePrefixOpExpr: | 709 | case NodeTypePrefixOpExpr: |
| 654 | case NodeTypeIfExpr: | 710 | case NodeTypeIfExpr: |
| 711 | case NodeTypeLabel: | ||
| 712 | case NodeTypeGoto: | ||
| 655 | zig_unreachable(); | 713 | zig_unreachable(); |
| 656 | } | 714 | } |
| 657 | } | 715 | } |
src/codegen.cpp+35-4| ... | @@ -115,7 +115,7 @@ static LLVMValueRef get_variable_value(CodeGen *g, Buf *name) { | ... | @@ -115,7 +115,7 @@ static LLVMValueRef get_variable_value(CodeGen *g, Buf *name) { |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | static TypeTableEntry *get_expr_type(AstNode *node) { | 117 | static TypeTableEntry *get_expr_type(AstNode *node) { |
| 118 | return node->codegen_node->data.expr_node.type_entry; | 118 | return node->codegen_node->expr_node.type_entry; |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { | 121 | static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { |
| ... | @@ -407,11 +407,13 @@ static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) { | ... | @@ -407,11 +407,13 @@ static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) { |
| 407 | 407 | ||
| 408 | LLVMPositionBuilderAtEnd(g->builder, then_block); | 408 | LLVMPositionBuilderAtEnd(g->builder, then_block); |
| 409 | LLVMValueRef then_expr_result = gen_expr(g, node->data.if_expr.then_block); | 409 | LLVMValueRef then_expr_result = gen_expr(g, node->data.if_expr.then_block); |
| 410 | LLVMBuildBr(g->builder, endif_block); | 410 | if (get_expr_type(node->data.if_expr.then_block) != g->builtin_types.entry_unreachable) |
| 411 | LLVMBuildBr(g->builder, endif_block); | ||
| 411 | 412 | ||
| 412 | LLVMPositionBuilderAtEnd(g->builder, else_block); | 413 | LLVMPositionBuilderAtEnd(g->builder, else_block); |
| 413 | LLVMValueRef else_expr_result = gen_expr(g, node->data.if_expr.else_node); | 414 | LLVMValueRef else_expr_result = gen_expr(g, node->data.if_expr.else_node); |
| 414 | LLVMBuildBr(g->builder, endif_block); | 415 | if (get_expr_type(node->data.if_expr.else_node) != g->builtin_types.entry_unreachable) |
| 416 | LLVMBuildBr(g->builder, endif_block); | ||
| 415 | 417 | ||
| 416 | LLVMPositionBuilderAtEnd(g->builder, endif_block); | 418 | LLVMPositionBuilderAtEnd(g->builder, endif_block); |
| 417 | if (use_expr_value) { | 419 | if (use_expr_value) { |
| ... | @@ -435,7 +437,8 @@ static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) { | ... | @@ -435,7 +437,8 @@ static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) { |
| 435 | 437 | ||
| 436 | LLVMPositionBuilderAtEnd(g->builder, then_block); | 438 | LLVMPositionBuilderAtEnd(g->builder, then_block); |
| 437 | gen_expr(g, node->data.if_expr.then_block); | 439 | gen_expr(g, node->data.if_expr.then_block); |
| 438 | LLVMBuildBr(g->builder, endif_block); | 440 | if (get_expr_type(node->data.if_expr.then_block) != g->builtin_types.entry_unreachable) |
| 441 | LLVMBuildBr(g->builder, endif_block); | ||
| 439 | 442 | ||
| 440 | LLVMPositionBuilderAtEnd(g->builder, endif_block); | 443 | LLVMPositionBuilderAtEnd(g->builder, endif_block); |
| 441 | return nullptr; | 444 | return nullptr; |
| ... | @@ -518,6 +521,17 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { | ... | @@ -518,6 +521,17 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { |
| 518 | } | 521 | } |
| 519 | case NodeTypeBlock: | 522 | case NodeTypeBlock: |
| 520 | return gen_block(g, node, nullptr); | 523 | return gen_block(g, node, nullptr); |
| 524 | case NodeTypeGoto: | ||
| 525 | add_debug_source_node(g, node); | ||
| 526 | return LLVMBuildBr(g->builder, node->codegen_node->data.label_entry->basic_block); | ||
| 527 | case NodeTypeLabel: | ||
| 528 | { | ||
| 529 | LLVMBasicBlockRef basic_block = node->codegen_node->data.label_entry->basic_block; | ||
| 530 | add_debug_source_node(g, node); | ||
| 531 | LLVMValueRef result = LLVMBuildBr(g->builder, basic_block); | ||
| 532 | LLVMPositionBuilderAtEnd(g->builder, basic_block); | ||
| 533 | return result; | ||
| 534 | } | ||
| 521 | case NodeTypeRoot: | 535 | case NodeTypeRoot: |
| 522 | case NodeTypeRootExportDecl: | 536 | case NodeTypeRootExportDecl: |
| 523 | case NodeTypeFnProto: | 537 | case NodeTypeFnProto: |
| ... | @@ -533,6 +547,20 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { | ... | @@ -533,6 +547,20 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { |
| 533 | zig_unreachable(); | 547 | zig_unreachable(); |
| 534 | } | 548 | } |
| 535 | 549 | ||
| 550 | static void build_label_blocks(CodeGen *g, AstNode *block_node) { | ||
| 551 | assert(block_node->type == NodeTypeBlock); | ||
| 552 | for (int i = 0; i < block_node->data.block.statements.length; i += 1) { | ||
| 553 | AstNode *label_node = block_node->data.block.statements.at(i); | ||
| 554 | if (label_node->type != NodeTypeLabel) | ||
| 555 | continue; | ||
| 556 | |||
| 557 | Buf *name = &label_node->data.label.name; | ||
| 558 | label_node->codegen_node->data.label_entry->basic_block = LLVMAppendBasicBlock( | ||
| 559 | g->cur_fn->fn_value, buf_ptr(name)); | ||
| 560 | } | ||
| 561 | |||
| 562 | } | ||
| 563 | |||
| 536 | static LLVMZigDISubroutineType *create_di_function_type(CodeGen *g, AstNodeFnProto *fn_proto, | 564 | static LLVMZigDISubroutineType *create_di_function_type(CodeGen *g, AstNodeFnProto *fn_proto, |
| 537 | LLVMZigDIFile *di_file) | 565 | LLVMZigDIFile *di_file) |
| 538 | { | 566 | { |
| ... | @@ -623,10 +651,13 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -623,10 +651,13 @@ static void do_code_gen(CodeGen *g) { |
| 623 | codegen_fn_def->params = allocate<LLVMValueRef>(LLVMCountParams(fn)); | 651 | codegen_fn_def->params = allocate<LLVMValueRef>(LLVMCountParams(fn)); |
| 624 | LLVMGetParams(fn, codegen_fn_def->params); | 652 | LLVMGetParams(fn, codegen_fn_def->params); |
| 625 | 653 | ||
| 654 | build_label_blocks(g, fn_def_node->data.fn_def.body); | ||
| 655 | |||
| 626 | TypeTableEntry *implicit_return_type = codegen_fn_def->implicit_return_type; | 656 | TypeTableEntry *implicit_return_type = codegen_fn_def->implicit_return_type; |
| 627 | gen_block(g, fn_def_node->data.fn_def.body, implicit_return_type); | 657 | gen_block(g, fn_def_node->data.fn_def.body, implicit_return_type); |
| 628 | 658 | ||
| 629 | g->block_scopes.pop(); | 659 | g->block_scopes.pop(); |
| 660 | |||
| 630 | } | 661 | } |
| 631 | assert(!g->errors.length); | 662 | assert(!g->errors.length); |
| 632 | 663 |
src/main.cpp+11-2| ... | @@ -9,6 +9,7 @@ | ... | @@ -9,6 +9,7 @@ |
| 9 | #include "buffer.hpp" | 9 | #include "buffer.hpp" |
| 10 | #include "codegen.hpp" | 10 | #include "codegen.hpp" |
| 11 | #include "os.hpp" | 11 | #include "os.hpp" |
| 12 | #include "error.hpp" | ||
| 12 | 13 | ||
| 13 | #include <stdio.h> | 14 | #include <stdio.h> |
| 14 | 15 | ||
| ... | @@ -48,6 +49,8 @@ struct Build { | ... | @@ -48,6 +49,8 @@ struct Build { |
| 48 | }; | 49 | }; |
| 49 | 50 | ||
| 50 | static int build(const char *arg0, Build *b) { | 51 | static int build(const char *arg0, Build *b) { |
| 52 | int err; | ||
| 53 | |||
| 51 | if (!b->in_file) | 54 | if (!b->in_file) |
| 52 | return usage(arg0); | 55 | return usage(arg0); |
| 53 | 56 | ||
| ... | @@ -59,11 +62,17 @@ static int build(const char *arg0, Build *b) { | ... | @@ -59,11 +62,17 @@ static int build(const char *arg0, Build *b) { |
| 59 | Buf root_source_name = BUF_INIT; | 62 | Buf root_source_name = BUF_INIT; |
| 60 | if (buf_eql_str(&in_file_buf, "-")) { | 63 | if (buf_eql_str(&in_file_buf, "-")) { |
| 61 | os_get_cwd(&root_source_dir); | 64 | os_get_cwd(&root_source_dir); |
| 62 | os_fetch_file(stdin, &root_source_code); | 65 | if ((err = os_fetch_file(stdin, &root_source_code))) { |
| 66 | fprintf(stderr, "unable to read stdin: %s\n", err_str(err)); | ||
| 67 | return 1; | ||
| 68 | } | ||
| 63 | buf_init_from_str(&root_source_name, ""); | 69 | buf_init_from_str(&root_source_name, ""); |
| 64 | } else { | 70 | } else { |
| 65 | os_path_split(&in_file_buf, &root_source_dir, &root_source_name); | 71 | os_path_split(&in_file_buf, &root_source_dir, &root_source_name); |
| 66 | os_fetch_file_path(buf_create_from_str(b->in_file), &root_source_code); | 72 | if ((err = os_fetch_file_path(buf_create_from_str(b->in_file), &root_source_code))) { |
| 73 | fprintf(stderr, "unable to open '%s': %s\n", b->in_file, err_str(err)); | ||
| 74 | return 1; | ||
| 75 | } | ||
| 67 | } | 76 | } |
| 68 | 77 | ||
| 69 | CodeGen *g = codegen_create(&root_source_dir); | 78 | CodeGen *g = codegen_create(&root_source_dir); |
src/parser.cpp+62-7| ... | @@ -95,6 +95,10 @@ const char *node_type_str(NodeType node_type) { | ... | @@ -95,6 +95,10 @@ const char *node_type_str(NodeType node_type) { |
| 95 | return "Void"; | 95 | return "Void"; |
| 96 | case NodeTypeIfExpr: | 96 | case NodeTypeIfExpr: |
| 97 | return "IfExpr"; | 97 | return "IfExpr"; |
| 98 | case NodeTypeLabel: | ||
| 99 | return "Label"; | ||
| 100 | case NodeTypeGoto: | ||
| 101 | return "Label"; | ||
| 98 | } | 102 | } |
| 99 | zig_unreachable(); | 103 | zig_unreachable(); |
| 100 | } | 104 | } |
| ... | @@ -260,6 +264,12 @@ void ast_print(AstNode *node, int indent) { | ... | @@ -260,6 +264,12 @@ void ast_print(AstNode *node, int indent) { |
| 260 | if (node->data.if_expr.else_node) | 264 | if (node->data.if_expr.else_node) |
| 261 | ast_print(node->data.if_expr.else_node, indent + 2); | 265 | ast_print(node->data.if_expr.else_node, indent + 2); |
| 262 | break; | 266 | break; |
| 267 | case NodeTypeLabel: | ||
| 268 | fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.label.name)); | ||
| 269 | break; | ||
| 270 | case NodeTypeGoto: | ||
| 271 | fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.go_to.name)); | ||
| 272 | break; | ||
| 263 | } | 273 | } |
| 264 | } | 274 | } |
| 265 | 275 | ||
| ... | @@ -581,7 +591,7 @@ static AstNode *ast_parse_grouped_expr(ParseContext *pc, int *token_index, bool | ... | @@ -581,7 +591,7 @@ static AstNode *ast_parse_grouped_expr(ParseContext *pc, int *token_index, bool |
| 581 | } | 591 | } |
| 582 | 592 | ||
| 583 | /* | 593 | /* |
| 584 | PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | token(Symbol) | 594 | PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | token(Symbol) | Goto |
| 585 | */ | 595 | */ |
| 586 | static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) { | 596 | static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) { |
| 587 | Token *token = &pc->tokens->at(*token_index); | 597 | Token *token = &pc->tokens->at(*token_index); |
| ... | @@ -609,6 +619,16 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool | ... | @@ -609,6 +619,16 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool |
| 609 | ast_buf_from_token(pc, token, &node->data.symbol); | 619 | ast_buf_from_token(pc, token, &node->data.symbol); |
| 610 | *token_index += 1; | 620 | *token_index += 1; |
| 611 | return node; | 621 | return node; |
| 622 | } else if (token->id == TokenIdKeywordGoto) { | ||
| 623 | AstNode *node = ast_create_node(pc, NodeTypeGoto, token); | ||
| 624 | *token_index += 1; | ||
| 625 | |||
| 626 | Token *dest_symbol = &pc->tokens->at(*token_index); | ||
| 627 | *token_index += 1; | ||
| 628 | ast_expect_token(pc, dest_symbol, TokenIdSymbol); | ||
| 629 | |||
| 630 | ast_buf_from_token(pc, dest_symbol, &node->data.go_to.name); | ||
| 631 | return node; | ||
| 612 | } | 632 | } |
| 613 | 633 | ||
| 614 | AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false); | 634 | AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false); |
| ... | @@ -1181,7 +1201,36 @@ static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool ma | ... | @@ -1181,7 +1201,36 @@ static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool ma |
| 1181 | } | 1201 | } |
| 1182 | 1202 | ||
| 1183 | /* | 1203 | /* |
| 1184 | Statement : NonBlockExpression token(Semicolon) | BlockExpression | 1204 | Label: token(Symbol) token(Colon) |
| 1205 | */ | ||
| 1206 | static AstNode *ast_parse_label(ParseContext *pc, int *token_index, bool mandatory) { | ||
| 1207 | Token *symbol_token = &pc->tokens->at(*token_index); | ||
| 1208 | if (symbol_token->id != TokenIdSymbol) { | ||
| 1209 | if (mandatory) { | ||
| 1210 | ast_invalid_token_error(pc, symbol_token); | ||
| 1211 | } else { | ||
| 1212 | return nullptr; | ||
| 1213 | } | ||
| 1214 | } | ||
| 1215 | |||
| 1216 | Token *colon_token = &pc->tokens->at(*token_index + 1); | ||
| 1217 | if (colon_token->id != TokenIdColon) { | ||
| 1218 | if (mandatory) { | ||
| 1219 | ast_invalid_token_error(pc, colon_token); | ||
| 1220 | } else { | ||
| 1221 | return nullptr; | ||
| 1222 | } | ||
| 1223 | } | ||
| 1224 | |||
| 1225 | *token_index += 2; | ||
| 1226 | |||
| 1227 | AstNode *node = ast_create_node(pc, NodeTypeLabel, symbol_token); | ||
| 1228 | ast_buf_from_token(pc, symbol_token, &node->data.label.name); | ||
| 1229 | return node; | ||
| 1230 | } | ||
| 1231 | |||
| 1232 | /* | ||
| 1233 | Statement : Label | NonBlockExpression token(Semicolon) | BlockExpression | ||
| 1185 | Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace) | 1234 | Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace) |
| 1186 | */ | 1235 | */ |
| 1187 | static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory) { | 1236 | static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory) { |
| ... | @@ -1204,12 +1253,18 @@ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandato | ... | @@ -1204,12 +1253,18 @@ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandato |
| 1204 | // {2;} -> {2;void} | 1253 | // {2;} -> {2;void} |
| 1205 | // {;2} -> {void;2} | 1254 | // {;2} -> {void;2} |
| 1206 | for (;;) { | 1255 | for (;;) { |
| 1207 | AstNode *statement_node = ast_parse_block_expr(pc, token_index, false); | 1256 | AstNode *statement_node = ast_parse_label(pc, token_index, false); |
| 1208 | bool semicolon_expected = !statement_node; | 1257 | bool semicolon_expected; |
| 1209 | if (!statement_node) { | 1258 | if (statement_node) { |
| 1210 | statement_node = ast_parse_non_block_expr(pc, token_index, false); | 1259 | semicolon_expected = false; |
| 1260 | } else { | ||
| 1261 | statement_node = ast_parse_block_expr(pc, token_index, false); | ||
| 1262 | semicolon_expected = !statement_node; | ||
| 1211 | if (!statement_node) { | 1263 | if (!statement_node) { |
| 1212 | statement_node = ast_create_node(pc, NodeTypeVoid, last_token); | 1264 | statement_node = ast_parse_non_block_expr(pc, token_index, false); |
| 1265 | if (!statement_node) { | ||
| 1266 | statement_node = ast_create_node(pc, NodeTypeVoid, last_token); | ||
| 1267 | } | ||
| 1213 | } | 1268 | } |
| 1214 | } | 1269 | } |
| 1215 | node->data.block.statements.append(statement_node); | 1270 | node->data.block.statements.append(statement_node); |
src/parser.hpp+12| ... | @@ -41,6 +41,8 @@ enum NodeType { | ... | @@ -41,6 +41,8 @@ enum NodeType { |
| 41 | NodeTypeUse, | 41 | NodeTypeUse, |
| 42 | NodeTypeVoid, | 42 | NodeTypeVoid, |
| 43 | NodeTypeIfExpr, | 43 | NodeTypeIfExpr, |
| 44 | NodeTypeLabel, | ||
| 45 | NodeTypeGoto, | ||
| 44 | }; | 46 | }; |
| 45 | 47 | ||
| 46 | struct AstNodeRoot { | 48 | struct AstNodeRoot { |
| ... | @@ -182,6 +184,14 @@ struct AstNodeIfExpr { | ... | @@ -182,6 +184,14 @@ struct AstNodeIfExpr { |
| 182 | AstNode *else_node; // null, block node, or other if expr node | 184 | AstNode *else_node; // null, block node, or other if expr node |
| 183 | }; | 185 | }; |
| 184 | 186 | ||
| 187 | struct AstNodeLabel { | ||
| 188 | Buf name; | ||
| 189 | }; | ||
| 190 | |||
| 191 | struct AstNodeGoto { | ||
| 192 | Buf name; | ||
| 193 | }; | ||
| 194 | |||
| 185 | struct AstNode { | 195 | struct AstNode { |
| 186 | enum NodeType type; | 196 | enum NodeType type; |
| 187 | int line; | 197 | int line; |
| ... | @@ -207,6 +217,8 @@ struct AstNode { | ... | @@ -207,6 +217,8 @@ struct AstNode { |
| 207 | AstNodeFnCallExpr fn_call_expr; | 217 | AstNodeFnCallExpr fn_call_expr; |
| 208 | AstNodeUse use; | 218 | AstNodeUse use; |
| 209 | AstNodeIfExpr if_expr; | 219 | AstNodeIfExpr if_expr; |
| 220 | AstNodeLabel label; | ||
| 221 | AstNodeGoto go_to; | ||
| 210 | Buf number; | 222 | Buf number; |
| 211 | Buf string; | 223 | Buf string; |
| 212 | Buf symbol; | 224 | Buf symbol; |
src/semantic_info.hpp+9-1| ... | @@ -43,6 +43,11 @@ struct SymbolTableEntry { | ... | @@ -43,6 +43,11 @@ struct SymbolTableEntry { |
| 43 | int param_index; // only valid in the case of parameters | 43 | int param_index; // only valid in the case of parameters |
| 44 | }; | 44 | }; |
| 45 | 45 | ||
| 46 | struct LabelTableEntry { | ||
| 47 | AstNode *label_node; | ||
| 48 | LLVMBasicBlockRef basic_block; | ||
| 49 | }; | ||
| 50 | |||
| 46 | struct FnTableEntry { | 51 | struct FnTableEntry { |
| 47 | LLVMValueRef fn_value; | 52 | LLVMValueRef fn_value; |
| 48 | AstNode *proto_node; | 53 | AstNode *proto_node; |
| ... | @@ -54,6 +59,7 @@ struct FnTableEntry { | ... | @@ -54,6 +59,7 @@ struct FnTableEntry { |
| 54 | 59 | ||
| 55 | // reminder: hash tables must be initialized before use | 60 | // reminder: hash tables must be initialized before use |
| 56 | HashMap<Buf *, SymbolTableEntry *, buf_hash, buf_eql_buf> symbol_table; | 61 | HashMap<Buf *, SymbolTableEntry *, buf_hash, buf_eql_buf> symbol_table; |
| 62 | HashMap<Buf *, LabelTableEntry *, buf_hash, buf_eql_buf> label_table; | ||
| 57 | }; | 63 | }; |
| 58 | 64 | ||
| 59 | struct CodeGen { | 65 | struct CodeGen { |
| ... | @@ -100,6 +106,7 @@ struct CodeGen { | ... | @@ -100,6 +106,7 @@ struct CodeGen { |
| 100 | 106 | ||
| 101 | OutType out_type; | 107 | OutType out_type; |
| 102 | FnTableEntry *cur_fn; | 108 | FnTableEntry *cur_fn; |
| 109 | LLVMBasicBlockRef cur_basic_block; | ||
| 103 | bool c_stdint_used; | 110 | bool c_stdint_used; |
| 104 | AstNode *root_export_decl; | 111 | AstNode *root_export_decl; |
| 105 | int version_major; | 112 | int version_major; |
| ... | @@ -132,9 +139,10 @@ struct CodeGenNode { | ... | @@ -132,9 +139,10 @@ struct CodeGenNode { |
| 132 | union { | 139 | union { |
| 133 | TypeNode type_node; // for NodeTypeType | 140 | TypeNode type_node; // for NodeTypeType |
| 134 | FnDefNode fn_def_node; // for NodeTypeFnDef | 141 | FnDefNode fn_def_node; // for NodeTypeFnDef |
| 135 | ExprNode expr_node; // for all the expression nodes | ||
| 136 | FnProtoNode fn_proto_node; // for NodeTypeFnProto | 142 | FnProtoNode fn_proto_node; // for NodeTypeFnProto |
| 143 | LabelTableEntry *label_entry; // for NodeTypeGoto and NodeTypeLabel | ||
| 137 | } data; | 144 | } data; |
| 145 | ExprNode expr_node; // for all the expression nodes | ||
| 138 | }; | 146 | }; |
| 139 | 147 | ||
| 140 | static inline Buf *hack_get_fn_call_name(CodeGen *g, AstNode *node) { | 148 | static inline Buf *hack_get_fn_call_name(CodeGen *g, AstNode *node) { |
src/tokenizer.cpp+3| ... | @@ -189,6 +189,8 @@ static void end_token(Tokenize *t) { | ... | @@ -189,6 +189,8 @@ static void end_token(Tokenize *t) { |
| 189 | t->cur_tok->id = TokenIdKeywordIf; | 189 | t->cur_tok->id = TokenIdKeywordIf; |
| 190 | } else if (mem_eql_str(token_mem, token_len, "else")) { | 190 | } else if (mem_eql_str(token_mem, token_len, "else")) { |
| 191 | t->cur_tok->id = TokenIdKeywordElse; | 191 | t->cur_tok->id = TokenIdKeywordElse; |
| 192 | } else if (mem_eql_str(token_mem, token_len, "goto")) { | ||
| 193 | t->cur_tok->id = TokenIdKeywordGoto; | ||
| 192 | } | 194 | } |
| 193 | 195 | ||
| 194 | t->cur_tok = nullptr; | 196 | t->cur_tok = nullptr; |
| ... | @@ -586,6 +588,7 @@ static const char * token_name(Token *token) { | ... | @@ -586,6 +588,7 @@ static const char * token_name(Token *token) { |
| 586 | case TokenIdKeywordVoid: return "Void"; | 588 | case TokenIdKeywordVoid: return "Void"; |
| 587 | case TokenIdKeywordIf: return "If"; | 589 | case TokenIdKeywordIf: return "If"; |
| 588 | case TokenIdKeywordElse: return "Else"; | 590 | case TokenIdKeywordElse: return "Else"; |
| 591 | case TokenIdKeywordGoto: return "Goto"; | ||
| 589 | case TokenIdLParen: return "LParen"; | 592 | case TokenIdLParen: return "LParen"; |
| 590 | case TokenIdRParen: return "RParen"; | 593 | case TokenIdRParen: return "RParen"; |
| 591 | case TokenIdComma: return "Comma"; | 594 | case TokenIdComma: return "Comma"; |
src/tokenizer.hpp+1| ... | @@ -27,6 +27,7 @@ enum TokenId { | ... | @@ -27,6 +27,7 @@ enum TokenId { |
| 27 | TokenIdKeywordVoid, | 27 | TokenIdKeywordVoid, |
| 28 | TokenIdKeywordIf, | 28 | TokenIdKeywordIf, |
| 29 | TokenIdKeywordElse, | 29 | TokenIdKeywordElse, |
| 30 | TokenIdKeywordGoto, | ||
| 30 | TokenIdLParen, | 31 | TokenIdLParen, |
| 31 | TokenIdRParen, | 32 | TokenIdRParen, |
| 32 | TokenIdComma, | 33 | TokenIdComma, |
src/zig_llvm.cpp+13| ... | @@ -255,6 +255,19 @@ void LLVMZigDIBuilderFinalize(LLVMZigDIBuilder *dibuilder) { | ... | @@ -255,6 +255,19 @@ void LLVMZigDIBuilderFinalize(LLVMZigDIBuilder *dibuilder) { |
| 255 | reinterpret_cast<DIBuilder*>(dibuilder)->finalize(); | 255 | reinterpret_cast<DIBuilder*>(dibuilder)->finalize(); |
| 256 | } | 256 | } |
| 257 | 257 | ||
| 258 | LLVMZigInsertionPoint *LLVMZigSaveInsertPoint(LLVMBuilderRef builder_wrapped) { | ||
| 259 | IRBuilderBase::InsertPoint *ip = new IRBuilderBase::InsertPoint(); | ||
| 260 | *ip = unwrap(builder_wrapped)->saveIP(); | ||
| 261 | return reinterpret_cast<LLVMZigInsertionPoint*>(ip); | ||
| 262 | } | ||
| 263 | |||
| 264 | void LLVMZigRestoreInsertPoint(LLVMBuilderRef builder, LLVMZigInsertionPoint *ip_wrapped) { | ||
| 265 | IRBuilderBase::InsertPoint *ip = reinterpret_cast<IRBuilderBase::InsertPoint*>(ip_wrapped); | ||
| 266 | unwrap(builder)->restoreIP(*ip); | ||
| 267 | } | ||
| 268 | |||
| 269 | //------------------------------------ | ||
| 270 | |||
| 258 | enum FloatAbi { | 271 | enum FloatAbi { |
| 259 | FloatAbiHard, | 272 | FloatAbiHard, |
| 260 | FloatAbiSoft, | 273 | FloatAbiSoft, |
src/zig_llvm.hpp+4| ... | @@ -22,6 +22,7 @@ struct LLVMZigDIFile; | ... | @@ -22,6 +22,7 @@ struct LLVMZigDIFile; |
| 22 | struct LLVMZigDILexicalBlock; | 22 | struct LLVMZigDILexicalBlock; |
| 23 | struct LLVMZigDISubprogram; | 23 | struct LLVMZigDISubprogram; |
| 24 | struct LLVMZigDISubroutineType; | 24 | struct LLVMZigDISubroutineType; |
| 25 | struct LLVMZigInsertionPoint; | ||
| 25 | 26 | ||
| 26 | void LLVMZigInitializeLoopStrengthReducePass(LLVMPassRegistryRef R); | 27 | void LLVMZigInitializeLoopStrengthReducePass(LLVMPassRegistryRef R); |
| 27 | void LLVMZigInitializeLowerIntrinsicsPass(LLVMPassRegistryRef R); | 28 | void LLVMZigInitializeLowerIntrinsicsPass(LLVMPassRegistryRef R); |
| ... | @@ -75,6 +76,9 @@ LLVMZigDISubprogram *LLVMZigCreateFunction(LLVMZigDIBuilder *dibuilder, LLVMZigD | ... | @@ -75,6 +76,9 @@ LLVMZigDISubprogram *LLVMZigCreateFunction(LLVMZigDIBuilder *dibuilder, LLVMZigD |
| 75 | 76 | ||
| 76 | void LLVMZigDIBuilderFinalize(LLVMZigDIBuilder *dibuilder); | 77 | void LLVMZigDIBuilderFinalize(LLVMZigDIBuilder *dibuilder); |
| 77 | 78 | ||
| 79 | LLVMZigInsertionPoint *LLVMZigSaveInsertPoint(LLVMBuilderRef builder); | ||
| 80 | void LLVMZigRestoreInsertPoint(LLVMBuilderRef builder, LLVMZigInsertionPoint *point); | ||
| 81 | |||
| 78 | 82 | ||
| 79 | /* | 83 | /* |
| 80 | * This stuff is not LLVM API but it depends on the LLVM C++ API so we put it here. | 84 | * This stuff is not LLVM API but it depends on the LLVM C++ API so we put it here. |
test/run_tests.cpp+35| ... | @@ -232,6 +232,30 @@ static void add_compiling_test_cases(void) { | ... | @@ -232,6 +232,30 @@ static void add_compiling_test_cases(void) { |
| 232 | exit(0); | 232 | exit(0); |
| 233 | } | 233 | } |
| 234 | )SOURCE", "pass\n"); | 234 | )SOURCE", "pass\n"); |
| 235 | |||
| 236 | add_simple_case("goto", R"SOURCE( | ||
| 237 | #link("c") | ||
| 238 | extern { | ||
| 239 | fn puts(s: *const u8) -> i32; | ||
| 240 | fn exit(code: i32) -> unreachable; | ||
| 241 | } | ||
| 242 | |||
| 243 | fn loop(a : i32) { | ||
| 244 | if a == 0 { | ||
| 245 | goto done; | ||
| 246 | } | ||
| 247 | puts("loop"); | ||
| 248 | loop(a - 1); | ||
| 249 | |||
| 250 | done: | ||
| 251 | return; | ||
| 252 | } | ||
| 253 | |||
| 254 | export fn _start() -> unreachable { | ||
| 255 | loop(3); | ||
| 256 | exit(0); | ||
| 257 | } | ||
| 258 | )SOURCE", "loop\nloop\nloop\n"); | ||
| 235 | } | 259 | } |
| 236 | 260 | ||
| 237 | static void add_compile_failure_test_cases(void) { | 261 | static void add_compile_failure_test_cases(void) { |
| ... | @@ -305,6 +329,17 @@ fn a() { | ... | @@ -305,6 +329,17 @@ fn a() { |
| 305 | )SOURCE", 2, | 329 | )SOURCE", 2, |
| 306 | ".tmp_source.zig:3:5: error: use of undeclared identifier 'b'", | 330 | ".tmp_source.zig:3:5: error: use of undeclared identifier 'b'", |
| 307 | ".tmp_source.zig:4:5: error: use of undeclared identifier 'c'"); | 331 | ".tmp_source.zig:4:5: error: use of undeclared identifier 'c'"); |
| 332 | |||
| 333 | add_compile_fail_case("goto cause unreachable code", R"SOURCE( | ||
| 334 | fn a() { | ||
| 335 | goto done; | ||
| 336 | b(); | ||
| 337 | done: | ||
| 338 | return; | ||
| 339 | } | ||
| 340 | fn b() {} | ||
| 341 | )SOURCE", 1, ".tmp_source.zig:4:5: error: unreachable code"); | ||
| 342 | |||
| 308 | } | 343 | } |
| 309 | 344 | ||
| 310 | static void print_compiler_invokation(TestCase *test_case, Buf *zig_stderr) { | 345 | static void print_compiler_invokation(TestCase *test_case, Buf *zig_stderr) { |