authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-04-09 16:41:17-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-04-09 16:41:17-07:00
log21eca6478f80f871f839b20044869cead55582a5
treee638b655a8873ec255cc66493ed7cc6778a91c39
parentfdf6a184615abab3314664b940ea18bc44de4546

re-introduce goto

see #44

5 files changed, 159 insertions(+), 9 deletions(-)

src/all_types.hpp+13
...@@ -24,6 +24,7 @@ struct BlockContext;...@@ -24,6 +24,7 @@ struct BlockContext;
24struct TypeTableEntry;24struct TypeTableEntry;
25struct VariableTableEntry;25struct VariableTableEntry;
26struct ErrorTableEntry;26struct ErrorTableEntry;
27struct LabelTableEntry;
27struct BuiltinFnEntry;28struct BuiltinFnEntry;
28struct TypeStructField;29struct TypeStructField;
29struct CodeGen;30struct CodeGen;
...@@ -538,6 +539,7 @@ struct AstNodeLabel {...@@ -538,6 +539,7 @@ struct AstNodeLabel {
538539
539 // populated by semantic analyzer540 // populated by semantic analyzer
540 Expr resolved_expr;541 Expr resolved_expr;
542 LabelTableEntry *label_entry;
541};543};
542544
543struct AstNodeGoto {545struct AstNodeGoto {
...@@ -545,6 +547,7 @@ struct AstNodeGoto {...@@ -545,6 +547,7 @@ struct AstNodeGoto {
545547
546 // populated by semantic analyzer548 // populated by semantic analyzer
547 Expr resolved_expr;549 Expr resolved_expr;
550 LabelTableEntry *label_entry;
548};551};
549552
550struct AsmOutput {553struct AsmOutput {
...@@ -1009,6 +1012,7 @@ struct FnTableEntry {...@@ -1009,6 +1012,7 @@ struct FnTableEntry {
1009 ImportTableEntry *import_entry;1012 ImportTableEntry *import_entry;
1010 // Required to be a pre-order traversal of the AST. (parents must come before children)1013 // Required to be a pre-order traversal of the AST. (parents must come before children)
1011 ZigList<BlockContext *> all_block_contexts;1014 ZigList<BlockContext *> all_block_contexts;
1015 ZigList<LabelTableEntry *> all_labels;
1012 Buf symbol_name;1016 Buf symbol_name;
1013 TypeTableEntry *type_entry; // function type1017 TypeTableEntry *type_entry; // function type
1014 bool is_inline;1018 bool is_inline;
...@@ -1020,6 +1024,7 @@ struct FnTableEntry {...@@ -1020,6 +1024,7 @@ struct FnTableEntry {
1020 ZigList<AstNode *> cast_alloca_list;1024 ZigList<AstNode *> cast_alloca_list;
1021 ZigList<StructValExprCodeGen *> struct_val_expr_alloca_list;1025 ZigList<StructValExprCodeGen *> struct_val_expr_alloca_list;
1022 ZigList<VariableTableEntry *> variable_list;1026 ZigList<VariableTableEntry *> variable_list;
1027 ZigList<AstNode *> goto_list;
1023};1028};
10241029
1025enum BuiltinFnId {1030enum BuiltinFnId {
...@@ -1217,6 +1222,13 @@ struct ErrorTableEntry {...@@ -1217,6 +1222,13 @@ struct ErrorTableEntry {
1217 AstNode *decl_node;1222 AstNode *decl_node;
1218};1223};
12191224
1225struct LabelTableEntry {
1226 AstNode *decl_node;
1227 LLVMBasicBlockRef basic_block;
1228 bool used;
1229 bool entered_from_fallthrough;
1230};
1231
1220struct BlockContext {1232struct BlockContext {
1221 // One of: NodeTypeFnDef, NodeTypeBlock, NodeTypeRoot, NodeTypeDefer, NodeTypeVariableDeclaration1233 // One of: NodeTypeFnDef, NodeTypeBlock, NodeTypeRoot, NodeTypeDefer, NodeTypeVariableDeclaration
1222 AstNode *node;1234 AstNode *node;
...@@ -1224,6 +1236,7 @@ struct BlockContext {...@@ -1224,6 +1236,7 @@ struct BlockContext {
1224 // any variables that are introduced by this scope1236 // any variables that are introduced by this scope
1225 HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> decl_table;1237 HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> decl_table;
1226 HashMap<Buf *, VariableTableEntry *, buf_hash, buf_eql_buf> var_table;1238 HashMap<Buf *, VariableTableEntry *, buf_hash, buf_eql_buf> var_table;
1239 HashMap<Buf *, LabelTableEntry *, buf_hash, buf_eql_buf> label_table;
12271240
1228 // if the block is inside a function, this is the function it is in:1241 // if the block is inside a function, this is the function it is in:
1229 FnTableEntry *fn_entry;1242 FnTableEntry *fn_entry;
src/analyze.cpp+66-5
...@@ -1992,6 +1992,7 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) {...@@ -1992,6 +1992,7 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) {
1992 context->parent = parent;1992 context->parent = parent;
1993 context->decl_table.init(1);1993 context->decl_table.init(1);
1994 context->var_table.init(1);1994 context->var_table.init(1);
1995 context->label_table.init(1);
19951996
1996 if (parent) {1997 if (parent) {
1997 context->parent_loop_node = parent->parent_loop_node;1998 context->parent_loop_node = parent->parent_loop_node;
...@@ -2037,6 +2038,18 @@ static VariableTableEntry *find_variable(CodeGen *g, BlockContext *orig_context,...@@ -2037,6 +2038,18 @@ static VariableTableEntry *find_variable(CodeGen *g, BlockContext *orig_context,
2037 return nullptr;2038 return nullptr;
2038}2039}
20392040
2041static LabelTableEntry *find_label(CodeGen *g, BlockContext *orig_context, Buf *name) {
2042 BlockContext *context = orig_context;
2043 while (context && context->fn_entry) {
2044 auto entry = context->label_table.maybe_get(name);
2045 if (entry) {
2046 return entry->value;
2047 }
2048 context = context->parent;
2049 }
2050 return nullptr;
2051}
2052
2040static TypeEnumField *get_enum_field(TypeTableEntry *enum_type, Buf *name) {2053static TypeEnumField *get_enum_field(TypeTableEntry *enum_type, Buf *name) {
2041 for (uint32_t i = 0; i < enum_type->data.enumeration.field_count; i += 1) {2054 for (uint32_t i = 0; i < enum_type->data.enumeration.field_count; i += 1) {
2042 TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i];2055 TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i];
...@@ -5326,8 +5339,19 @@ static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import,...@@ -5326,8 +5339,19 @@ static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import,
5326 for (int i = 0; i < node->data.block.statements.length; i += 1) {5339 for (int i = 0; i < node->data.block.statements.length; i += 1) {
5327 AstNode *child = node->data.block.statements.at(i);5340 AstNode *child = node->data.block.statements.at(i);
5328 if (child->type == NodeTypeLabel) {5341 if (child->type == NodeTypeLabel) {
5329 add_node_error(g, child,5342 FnTableEntry *fn_table_entry = child_context->fn_entry;
5330 buf_sprintf("label and goto not supported yet, see https://github.com/andrewrk/zig/issues/44"));5343 assert(fn_table_entry);
5344
5345 LabelTableEntry *label = allocate<LabelTableEntry>(1);
5346 label->decl_node = child;
5347 label->entered_from_fallthrough = (return_type->id != TypeTableEntryIdUnreachable);
5348
5349 child->block_context = child_context;
5350 child->data.label.label_entry = label;
5351 fn_table_entry->all_labels.append(label);
5352
5353 child_context->label_table.put(&child->data.label.name, label);
5354
5331 return_type = g->builtin_types.entry_void;5355 return_type = g->builtin_types.entry_void;
5332 continue;5356 continue;
5333 }5357 }
...@@ -5405,13 +5429,35 @@ static TypeTableEntry *analyze_asm_expr(CodeGen *g, ImportTableEntry *import, Bl...@@ -5405,13 +5429,35 @@ static TypeTableEntry *analyze_asm_expr(CodeGen *g, ImportTableEntry *import, Bl
5405 return return_type;5429 return return_type;
5406}5430}
54075431
5408static TypeTableEntry *analyze_goto(CodeGen *g, ImportTableEntry *import, BlockContext *context,5432static TypeTableEntry *analyze_goto_pass1(CodeGen *g, ImportTableEntry *import, BlockContext *context,
5409 TypeTableEntry *expected_type, AstNode *node)5433 TypeTableEntry *expected_type, AstNode *node)
5410{5434{
5411 add_node_error(g, node, buf_sprintf("goto is broken, see https://github.com/andrewrk/zig/issues/44"));5435 assert(node->type == NodeTypeGoto);
5436
5437 FnTableEntry *fn_table_entry = context->fn_entry;
5438 assert(fn_table_entry);
5439
5440 fn_table_entry->goto_list.append(node);
5441
5412 return g->builtin_types.entry_unreachable;5442 return g->builtin_types.entry_unreachable;
5413}5443}
54145444
5445static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *node) {
5446 assert(node->type == NodeTypeGoto);
5447 Buf *label_name = &node->data.goto_expr.name;
5448 BlockContext *context = node->block_context;
5449 assert(context);
5450 LabelTableEntry *label = find_label(g, context, label_name);
5451
5452 if (!label) {
5453 add_node_error(g, node, buf_sprintf("no label in scope named '%s'", buf_ptr(label_name)));
5454 return;
5455 }
5456
5457 label->used = true;
5458 node->data.goto_expr.label_entry = label;
5459}
5460
5415static TypeTableEntry *analyze_expression_pointer_only(CodeGen *g, ImportTableEntry *import,5461static TypeTableEntry *analyze_expression_pointer_only(CodeGen *g, ImportTableEntry *import,
5416 BlockContext *context, TypeTableEntry *expected_type, AstNode *node, bool pointer_only)5462 BlockContext *context, TypeTableEntry *expected_type, AstNode *node, bool pointer_only)
5417{5463{
...@@ -5433,7 +5479,7 @@ static TypeTableEntry *analyze_expression_pointer_only(CodeGen *g, ImportTableEn...@@ -5433,7 +5479,7 @@ static TypeTableEntry *analyze_expression_pointer_only(CodeGen *g, ImportTableEn
5433 return_type = g->builtin_types.entry_void;5479 return_type = g->builtin_types.entry_void;
5434 break;5480 break;
5435 case NodeTypeGoto:5481 case NodeTypeGoto:
5436 analyze_goto(g, import, context, expected_type, node);5482 return_type = analyze_goto_pass1(g, import, context, expected_type, node);
5437 break;5483 break;
5438 case NodeTypeBreak:5484 case NodeTypeBreak:
5439 return_type = analyze_break_expr(g, import, context, expected_type, node);5485 return_type = analyze_break_expr(g, import, context, expected_type, node);
...@@ -5611,6 +5657,21 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {...@@ -5611,6 +5657,21 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
5611 TypeTableEntry *block_return_type = analyze_expression(g, import, context, expected_type, node->data.fn_def.body);5657 TypeTableEntry *block_return_type = analyze_expression(g, import, context, expected_type, node->data.fn_def.body);
56125658
5613 node->data.fn_def.implicit_return_type = block_return_type;5659 node->data.fn_def.implicit_return_type = block_return_type;
5660
5661 for (int i = 0; i < fn_table_entry->goto_list.length; i += 1) {
5662 AstNode *goto_node = fn_table_entry->goto_list.at(i);
5663 assert(goto_node->type == NodeTypeGoto);
5664 analyze_goto_pass2(g, import, goto_node);
5665 }
5666
5667 for (int i = 0; i < fn_table_entry->all_labels.length; i += 1) {
5668 LabelTableEntry *label = fn_table_entry->all_labels.at(i);
5669 if (!label->used) {
5670 add_node_error(g, label->decl_node,
5671 buf_sprintf("label '%s' defined but not used",
5672 buf_ptr(&label->decl_node->data.label.name)));
5673 }
5674 }
5614}5675}
56155676
5616static void add_top_level_decl(CodeGen *g, ImportTableEntry *import, BlockContext *block_context,5677static void add_top_level_decl(CodeGen *g, ImportTableEntry *import, BlockContext *block_context,
src/codegen.cpp+36-4
...@@ -2719,6 +2719,29 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {...@@ -2719,6 +2719,29 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {
2719 }2719 }
2720}2720}
27212721
2722static LLVMValueRef gen_goto(CodeGen *g, AstNode *node) {
2723 assert(node->type == NodeTypeGoto);
2724
2725 add_debug_source_node(g, node);
2726 LLVMBuildBr(g->builder, node->data.goto_expr.label_entry->basic_block);
2727 return nullptr;
2728}
2729
2730static LLVMValueRef gen_label(CodeGen *g, AstNode *node) {
2731 assert(node->type == NodeTypeLabel);
2732
2733 LabelTableEntry *label = node->data.label.label_entry;
2734 assert(label);
2735
2736 LLVMBasicBlockRef basic_block = label->basic_block;
2737 if (label->entered_from_fallthrough) {
2738 add_debug_source_node(g, node);
2739 LLVMBuildBr(g->builder, basic_block);
2740 }
2741 LLVMPositionBuilderAtEnd(g->builder, basic_block);
2742 return nullptr;
2743}
2744
2722static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {2745static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
2723 Expr *expr = get_resolved_expr(node);2746 Expr *expr = get_resolved_expr(node);
2724 if (expr->const_val.ok) {2747 if (expr->const_val.ok) {
...@@ -2766,13 +2789,13 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {...@@ -2766,13 +2789,13 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
2766 case NodeTypeBlock:2789 case NodeTypeBlock:
2767 return gen_block(g, node, nullptr);2790 return gen_block(g, node, nullptr);
2768 case NodeTypeGoto:2791 case NodeTypeGoto:
2769 zig_unreachable();2792 return gen_goto(g, node);
2770 case NodeTypeBreak:2793 case NodeTypeBreak:
2771 return gen_break(g, node);2794 return gen_break(g, node);
2772 case NodeTypeContinue:2795 case NodeTypeContinue:
2773 return gen_continue(g, node);2796 return gen_continue(g, node);
2774 case NodeTypeLabel:2797 case NodeTypeLabel:
2775 zig_unreachable();2798 return gen_label(g, node);
2776 case NodeTypeContainerInitExpr:2799 case NodeTypeContainerInitExpr:
2777 return gen_container_init_expr(g, node);2800 return gen_container_init_expr(g, node);
2778 case NodeTypeSwitchExpr:2801 case NodeTypeSwitchExpr:
...@@ -3105,6 +3128,16 @@ static void generate_error_name_table(CodeGen *g) {...@@ -3105,6 +3128,16 @@ static void generate_error_name_table(CodeGen *g) {
3105 LLVMSetUnnamedAddr(g->err_name_table, true);3128 LLVMSetUnnamedAddr(g->err_name_table, true);
3106}3129}
31073130
3131static void build_label_blocks(CodeGen *g, FnTableEntry *fn) {
3132 LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn->fn_value, "entry");
3133 for (int i = 0; i < fn->all_labels.length; i += 1) {
3134 LabelTableEntry *label = fn->all_labels.at(i);
3135 Buf *name = &label->decl_node->data.label.name;
3136 label->basic_block = LLVMAppendBasicBlock(fn->fn_value, buf_ptr(name));
3137 }
3138 LLVMPositionBuilderAtEnd(g->builder, entry_block);
3139}
3140
3108static void do_code_gen(CodeGen *g) {3141static void do_code_gen(CodeGen *g) {
3109 assert(!g->errors.length);3142 assert(!g->errors.length);
31103143
...@@ -3279,8 +3312,7 @@ static void do_code_gen(CodeGen *g) {...@@ -3279,8 +3312,7 @@ static void do_code_gen(CodeGen *g) {
3279 assert(proto_node->type == NodeTypeFnProto);3312 assert(proto_node->type == NodeTypeFnProto);
3280 AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;3313 AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
32813314
3282 LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn, "entry");3315 build_label_blocks(g, fn_table_entry);
3283 LLVMPositionBuilderAtEnd(g->builder, entry_block);
32843316
32853317
3286 // Set up debug info for blocks3318 // Set up debug info for blocks
test/run_tests.cpp+22
...@@ -1788,6 +1788,28 @@ fn test1(a: i32, b: i32) -> i32 {...@@ -1788,6 +1788,28 @@ fn test1(a: i32, b: i32) -> i32 {
1788 return foo(a)(b);1788 return foo(a)(b);
1789}1789}
1790 )SOURCE", 1, ".tmp_source.zig:4:16: error: unable to resolve constant expression");1790 )SOURCE", 1, ".tmp_source.zig:4:16: error: unable to resolve constant expression");
1791
1792 add_compile_fail_case("goto jumping into block", R"SOURCE(
1793fn f() {
1794 {
1795a_label:
1796 }
1797 goto a_label;
1798}
1799 )SOURCE", 2,
1800 ".tmp_source.zig:4:1: error: label 'a_label' defined but not used",
1801 ".tmp_source.zig:6:5: error: no label in scope named 'a_label'");
1802
1803 add_compile_fail_case("goto jumping past a defer", R"SOURCE(
1804fn f(b: bool) {
1805 if (b) goto label;
1806 defer derp();
1807label:
1808}
1809fn derp(){}
1810 )SOURCE", 2,
1811 ".tmp_source.zig:3:12: error: no label in scope named 'label'",
1812 ".tmp_source.zig:5:1: error: label 'label' defined but not used");
1791}1813}
17921814
1793//////////////////////////////////////////////////////////////////////////////1815//////////////////////////////////////////////////////////////////////////////
test/self_hosted.zig+22
...@@ -569,3 +569,25 @@ fn error_name_string() {...@@ -569,3 +569,25 @@ fn error_name_string() {
569 assert(str_eql(@err_name(error.AnError), "AnError"));569 assert(str_eql(@err_name(error.AnError), "AnError"));
570 assert(str_eql(@err_name(error.ALongerErrorName), "ALongerErrorName"));570 assert(str_eql(@err_name(error.ALongerErrorName), "ALongerErrorName"));
571}571}
572
573
574#attribute("test")
575fn goto_and_labels() {
576 goto_loop();
577 assert(goto_counter == 10);
578}
579fn goto_loop() {
580 var i: i32 = 0;
581 goto cond;
582loop:
583 i += 1;
584cond:
585 if (!(i < 10)) goto end;
586 goto_counter += 1;
587 goto loop;
588end:
589}
590var goto_counter: i32 = 0;
591
592
593