| author | |
| committer | |
| log | 4619b5de0609e98c0c98fe352f3bc32f036b6ad4 |
| tree | b58718b204a63ba21f48d7eeac067baee53e903f |
| parent | 24b65e41ee241c805e0eff8212ef49c5c39e4b8e |
5 files changed, 68 insertions(+), 11 deletions(-)
doc/langref.md+1-1| ... | ... | @@ -73,7 +73,7 @@ AssignmentOperator = "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "& |
| 73 | 73 | |
| 74 | 74 | BlockExpression = IfExpression | Block | WhileExpression | ForExpression | SwitchExpression |
| 75 | 75 | |
| 76 | SwitchExpression = "switch" "(" Expression ")" "{" many(SwitchProng) "}" | |
| 76 | SwitchExpression = option("inline") "switch" "(" Expression ")" "{" many(SwitchProng) "}" | |
| 77 | 77 | |
| 78 | 78 | SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" option("*") Symbol "|") Expression "," |
| 79 | 79 |
src/all_types.hpp+1| ... | ... | @@ -557,6 +557,7 @@ struct AstNodeForExpr { |
| 557 | 557 | struct AstNodeSwitchExpr { |
| 558 | 558 | AstNode *expr; |
| 559 | 559 | ZigList<AstNode *> prongs; |
| 560 | bool is_inline; | |
| 560 | 561 | |
| 561 | 562 | // populated by semantic analyzer |
| 562 | 563 | Expr resolved_expr; |
src/ir.cpp+32-2| ... | ... | @@ -2264,7 +2264,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, AstNode *node) { |
| 2264 | 2264 | |
| 2265 | 2265 | size_t prong_count = node->data.switch_expr.prongs.length; |
| 2266 | 2266 | ZigList<IrInstructionSwitchBrCase> cases = {0}; |
| 2267 | bool is_inline = (node->block_context->fn_entry == nullptr); | |
| 2267 | bool is_inline = node->data.switch_expr.is_inline || (node->block_context->fn_entry == nullptr); | |
| 2268 | 2268 | |
| 2269 | 2269 | ZigList<IrInstruction *> incoming_values = {0}; |
| 2270 | 2270 | ZigList<IrBasicBlock *> incoming_blocks = {0}; |
| ... | ... | @@ -5249,7 +5249,37 @@ static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira, |
| 5249 | 5249 | bool is_inline = switch_br_instruction->is_inline; |
| 5250 | 5250 | |
| 5251 | 5251 | if (is_inline || target_value->static_value.special != ConstValSpecialRuntime) { |
| 5252 | zig_panic("TODO compile time switch br"); | |
| 5252 | ConstExprValue *target_val = ir_resolve_const(ira, target_value); | |
| 5253 | if (!target_val) | |
| 5254 | return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); | |
| 5255 | ||
| 5256 | for (size_t i = 0; i < case_count; i += 1) { | |
| 5257 | IrInstructionSwitchBrCase *old_case = &switch_br_instruction->cases[i]; | |
| 5258 | IrInstruction *case_value = old_case->value->other; | |
| 5259 | if (case_value->type_entry->id == TypeTableEntryIdInvalid) | |
| 5260 | return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); | |
| 5261 | ||
| 5262 | IrInstruction *casted_case_value = ir_get_casted_value(ira, case_value, target_value->type_entry); | |
| 5263 | if (casted_case_value->type_entry->id == TypeTableEntryIdInvalid) | |
| 5264 | return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); | |
| 5265 | ||
| 5266 | ConstExprValue *case_val = ir_resolve_const(ira, casted_case_value); | |
| 5267 | if (!case_val) | |
| 5268 | return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); | |
| 5269 | ||
| 5270 | if (const_values_equal(target_val, case_val, target_value->type_entry)) { | |
| 5271 | IrBasicBlock *old_dest_block = old_case->block; | |
| 5272 | if (is_inline || old_dest_block->ref_count == 1) { | |
| 5273 | ir_inline_bb(ira, old_dest_block); | |
| 5274 | return ira->codegen->builtin_types.entry_unreachable; | |
| 5275 | } else { | |
| 5276 | IrBasicBlock *new_dest_block = ir_get_new_bb(ira, old_dest_block); | |
| 5277 | ir_build_br_from(&ira->new_irb, &switch_br_instruction->base, new_dest_block); | |
| 5278 | return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); | |
| 5279 | } | |
| 5280 | } | |
| 5281 | } | |
| 5282 | ||
| 5253 | 5283 | } |
| 5254 | 5284 | |
| 5255 | 5285 | IrInstructionSwitchBrCase *cases = allocate<IrInstructionSwitchBrCase>(case_count); |
src/parser.cpp+21-8| ... | ... | @@ -1642,23 +1642,36 @@ static AstNode *ast_parse_for_expr(ParseContext *pc, size_t *token_index, bool m |
| 1642 | 1642 | } |
| 1643 | 1643 | |
| 1644 | 1644 | /* |
| 1645 | SwitchExpression : "switch" "(" Expression ")" "{" many(SwitchProng) "}" | |
| 1645 | SwitchExpression = option("inline") "switch" "(" Expression ")" "{" many(SwitchProng) "}" | |
| 1646 | 1646 | SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" option("*") Symbol "|") Expression "," |
| 1647 | 1647 | SwitchItem : Expression | (Expression "..." Expression) |
| 1648 | 1648 | */ |
| 1649 | 1649 | static AstNode *ast_parse_switch_expr(ParseContext *pc, size_t *token_index, bool mandatory) { |
| 1650 | Token *token = &pc->tokens->at(*token_index); | |
| 1651 | ||
| 1652 | if (token->id != TokenIdKeywordSwitch) { | |
| 1653 | if (mandatory) { | |
| 1654 | ast_expect_token(pc, token, TokenIdKeywordSwitch); | |
| 1650 | Token *first_token = &pc->tokens->at(*token_index); | |
| 1651 | Token *switch_token; | |
| 1652 | bool is_inline; | |
| 1653 | if (first_token->id == TokenIdKeywordInline) { | |
| 1654 | is_inline = true; | |
| 1655 | switch_token = &pc->tokens->at(*token_index + 1); | |
| 1656 | if (switch_token->id == TokenIdKeywordSwitch) { | |
| 1657 | *token_index += 2; | |
| 1658 | } else if (mandatory) { | |
| 1659 | ast_expect_token(pc, first_token, TokenIdKeywordSwitch); | |
| 1655 | 1660 | } else { |
| 1656 | 1661 | return nullptr; |
| 1657 | 1662 | } |
| 1663 | } else if (first_token->id == TokenIdKeywordSwitch) { | |
| 1664 | is_inline = false; | |
| 1665 | switch_token = first_token; | |
| 1666 | *token_index += 1; | |
| 1667 | } else if (mandatory) { | |
| 1668 | ast_expect_token(pc, first_token, TokenIdKeywordSwitch); | |
| 1669 | } else { | |
| 1670 | return nullptr; | |
| 1658 | 1671 | } |
| 1659 | *token_index += 1; | |
| 1660 | 1672 | |
| 1661 | AstNode *node = ast_create_node(pc, NodeTypeSwitchExpr, token); | |
| 1673 | AstNode *node = ast_create_node(pc, NodeTypeSwitchExpr, switch_token); | |
| 1674 | node->data.switch_expr.is_inline = is_inline; | |
| 1662 | 1675 | |
| 1663 | 1676 | ast_eat_token(pc, token_index, TokenIdLParen); |
| 1664 | 1677 | node->data.switch_expr.expr = ast_parse_expression(pc, token_index, true); |
test/self_hosted2.zig+13| ... | ... | @@ -49,6 +49,18 @@ fn testSwitchWithAllRanges(x: u32, y: u32) -> u32 { |
| 49 | 49 | } |
| 50 | 50 | } |
| 51 | 51 | |
| 52 | fn testInlineSwitch() { | |
| 53 | const x = 3 + 4; | |
| 54 | const result = inline switch (x) { | |
| 55 | 3 => 10, | |
| 56 | 4 => 11, | |
| 57 | 5, 6 => 12, | |
| 58 | 7, 8 => 13, | |
| 59 | else => 14, | |
| 60 | }; | |
| 61 | assert(result + 1 == 14); | |
| 62 | } | |
| 63 | ||
| 52 | 64 | fn assert(ok: bool) { |
| 53 | 65 | if (!ok) |
| 54 | 66 | @unreachable(); |
| ... | ... | @@ -60,6 +72,7 @@ fn runAllTests() { |
| 60 | 72 | inlinedLoop(); |
| 61 | 73 | switchWithNumbers(); |
| 62 | 74 | switchWithAllRanges(); |
| 75 | testInlineSwitch(); | |
| 63 | 76 | } |
| 64 | 77 | |
| 65 | 78 | export nakedcc fn _start() -> unreachable { |