| author | |
| committer | |
| log | b78c91951a1db34c615a011e0444608285f1a74c |
| tree | 2cae0609ec2670a4a1e1adb7905ae495abc9a238 |
| parent | cd08c1f3be278366ee69c3cf4ab1091eb884e264 |
if and switch are implicitly inline if the condition/target
expression is known at compile time.
instead of:
```
inline if (condition) ...
inline switch (target) ...
```
one can use:
```
if (comptime condition) ...
switch (comptime target) ...
```7 files changed, 29 insertions(+), 64 deletions(-)
doc/langref.md+3-3| ... | @@ -73,7 +73,7 @@ BlockExpression = IfExpression | Block | WhileExpression | ForExpression | Switc | ... | @@ -73,7 +73,7 @@ BlockExpression = IfExpression | Block | WhileExpression | ForExpression | Switc |
| 73 | 73 | ||
| 74 | CompTimeExpression = option("comptime") Expression | 74 | CompTimeExpression = option("comptime") Expression |
| 75 | 75 | ||
| 76 | SwitchExpression = option("inline") "switch" "(" Expression ")" "{" many(SwitchProng) "}" | 76 | SwitchExpression = "switch" "(" Expression ")" "{" many(SwitchProng) "}" |
| 77 | 77 | ||
| 78 | SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" option("*") Symbol "|") Expression "," | 78 | SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" option("*") Symbol "|") Expression "," |
| 79 | 79 | ||
| ... | @@ -91,9 +91,9 @@ Defer = option("%" | "?") "defer" Expression | ... | @@ -91,9 +91,9 @@ Defer = option("%" | "?") "defer" Expression |
| 91 | 91 | ||
| 92 | IfExpression = IfVarExpression | IfBoolExpression | 92 | IfExpression = IfVarExpression | IfBoolExpression |
| 93 | 93 | ||
| 94 | IfBoolExpression = option("inline") "if" "(" Expression ")" Expression option(Else) | 94 | IfBoolExpression = "if" "(" Expression ")" Expression option(Else) |
| 95 | 95 | ||
| 96 | IfVarExpression = option("inline") "if" "(" ("const" | "var") option("*") Symbol option(":" TypeExpr) "?=" Expression ")" Expression Option(Else) | 96 | IfVarExpression = "if" "(" ("const" | "var") option("*") Symbol option(":" TypeExpr) "?=" Expression ")" Expression Option(Else) |
| 97 | 97 | ||
| 98 | Else = "else" Expression | 98 | Else = "else" Expression |
| 99 | 99 |
src/all_types.hpp-5| ... | @@ -509,7 +509,6 @@ struct AstNodeIfBoolExpr { | ... | @@ -509,7 +509,6 @@ struct AstNodeIfBoolExpr { |
| 509 | AstNode *condition; | 509 | AstNode *condition; |
| 510 | AstNode *then_block; | 510 | AstNode *then_block; |
| 511 | AstNode *else_node; // null, block node, or other if expr node | 511 | AstNode *else_node; // null, block node, or other if expr node |
| 512 | bool is_inline; | ||
| 513 | }; | 512 | }; |
| 514 | 513 | ||
| 515 | struct AstNodeIfVarExpr { | 514 | struct AstNodeIfVarExpr { |
| ... | @@ -517,7 +516,6 @@ struct AstNodeIfVarExpr { | ... | @@ -517,7 +516,6 @@ struct AstNodeIfVarExpr { |
| 517 | AstNode *then_block; | 516 | AstNode *then_block; |
| 518 | AstNode *else_node; // null, block node, or other if expr node | 517 | AstNode *else_node; // null, block node, or other if expr node |
| 519 | bool var_is_ptr; | 518 | bool var_is_ptr; |
| 520 | bool is_inline; | ||
| 521 | }; | 519 | }; |
| 522 | 520 | ||
| 523 | struct AstNodeWhileExpr { | 521 | struct AstNodeWhileExpr { |
| ... | @@ -539,7 +537,6 @@ struct AstNodeForExpr { | ... | @@ -539,7 +537,6 @@ struct AstNodeForExpr { |
| 539 | struct AstNodeSwitchExpr { | 537 | struct AstNodeSwitchExpr { |
| 540 | AstNode *expr; | 538 | AstNode *expr; |
| 541 | ZigList<AstNode *> prongs; | 539 | ZigList<AstNode *> prongs; |
| 542 | bool is_inline; | ||
| 543 | }; | 540 | }; |
| 544 | 541 | ||
| 545 | struct AstNodeSwitchProng { | 542 | struct AstNodeSwitchProng { |
| ... | @@ -677,11 +674,9 @@ struct AstNodeBoolLiteral { | ... | @@ -677,11 +674,9 @@ struct AstNodeBoolLiteral { |
| 677 | }; | 674 | }; |
| 678 | 675 | ||
| 679 | struct AstNodeBreakExpr { | 676 | struct AstNodeBreakExpr { |
| 680 | bool is_inline; // TODO | ||
| 681 | }; | 677 | }; |
| 682 | 678 | ||
| 683 | struct AstNodeContinueExpr { | 679 | struct AstNodeContinueExpr { |
| 684 | bool is_inline; // TODO | ||
| 685 | }; | 680 | }; |
| 686 | 681 | ||
| 687 | struct AstNodeArrayType { | 682 | struct AstNodeArrayType { |
src/ast_render.cpp+2-4| ... | @@ -844,14 +844,12 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { | ... | @@ -844,14 +844,12 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { |
| 844 | } | 844 | } |
| 845 | case NodeTypeBreak: | 845 | case NodeTypeBreak: |
| 846 | { | 846 | { |
| 847 | const char *inline_str = node->data.break_expr.is_inline ? "inline " : ""; | 847 | fprintf(ar->f, "break"); |
| 848 | fprintf(ar->f, "%sbreak", inline_str); | ||
| 849 | break; | 848 | break; |
| 850 | } | 849 | } |
| 851 | case NodeTypeContinue: | 850 | case NodeTypeContinue: |
| 852 | { | 851 | { |
| 853 | const char *inline_str = node->data.continue_expr.is_inline ? "inline " : ""; | 852 | fprintf(ar->f, "continue"); |
| 854 | fprintf(ar->f, "%scontinue", inline_str); | ||
| 855 | break; | 853 | break; |
| 856 | } | 854 | } |
| 857 | case NodeTypeSliceExpr: | 855 | case NodeTypeSliceExpr: |
src/ir.cpp+5-5| ... | @@ -4148,7 +4148,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode | ... | @@ -4148,7 +4148,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode |
| 4148 | return condition; | 4148 | return condition; |
| 4149 | 4149 | ||
| 4150 | IrInstruction *is_comptime; | 4150 | IrInstruction *is_comptime; |
| 4151 | if (ir_should_inline(irb->exec, scope) || node->data.if_bool_expr.is_inline) { | 4151 | if (ir_should_inline(irb->exec, scope)) { |
| 4152 | is_comptime = ir_build_const_bool(irb, scope, node, true); | 4152 | is_comptime = ir_build_const_bool(irb, scope, node, true); |
| 4153 | } else { | 4153 | } else { |
| 4154 | is_comptime = ir_build_test_comptime(irb, scope, node, condition); | 4154 | is_comptime = ir_build_test_comptime(irb, scope, node, condition); |
| ... | @@ -4695,7 +4695,7 @@ static IrInstruction *ir_gen_if_var_expr(IrBuilder *irb, Scope *scope, AstNode * | ... | @@ -4695,7 +4695,7 @@ static IrInstruction *ir_gen_if_var_expr(IrBuilder *irb, Scope *scope, AstNode * |
| 4695 | IrBasicBlock *endif_block = ir_build_basic_block(irb, scope, "MaybeEndIf"); | 4695 | IrBasicBlock *endif_block = ir_build_basic_block(irb, scope, "MaybeEndIf"); |
| 4696 | 4696 | ||
| 4697 | IrInstruction *is_comptime; | 4697 | IrInstruction *is_comptime; |
| 4698 | if (ir_should_inline(irb->exec, scope) || node->data.if_var_expr.is_inline) { | 4698 | if (ir_should_inline(irb->exec, scope)) { |
| 4699 | is_comptime = ir_build_const_bool(irb, scope, node, true); | 4699 | is_comptime = ir_build_const_bool(irb, scope, node, true); |
| 4700 | } else { | 4700 | } else { |
| 4701 | is_comptime = ir_build_test_comptime(irb, scope, node, is_non_null); | 4701 | is_comptime = ir_build_test_comptime(irb, scope, node, is_non_null); |
| ... | @@ -4807,7 +4807,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * | ... | @@ -4807,7 +4807,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * |
| 4807 | ZigList<IrInstructionSwitchBrCase> cases = {0}; | 4807 | ZigList<IrInstructionSwitchBrCase> cases = {0}; |
| 4808 | 4808 | ||
| 4809 | IrInstruction *is_comptime; | 4809 | IrInstruction *is_comptime; |
| 4810 | if (ir_should_inline(irb->exec, scope) || node->data.switch_expr.is_inline) { | 4810 | if (ir_should_inline(irb->exec, scope)) { |
| 4811 | is_comptime = ir_build_const_bool(irb, scope, node, true); | 4811 | is_comptime = ir_build_const_bool(irb, scope, node, true); |
| 4812 | } else { | 4812 | } else { |
| 4813 | is_comptime = ir_build_test_comptime(irb, scope, node, target_value); | 4813 | is_comptime = ir_build_test_comptime(irb, scope, node, target_value); |
| ... | @@ -5002,7 +5002,7 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *scope, AstNode *node) | ... | @@ -5002,7 +5002,7 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *scope, AstNode *node) |
| 5002 | LoopStackItem *loop_stack_item = &irb->loop_stack.last(); | 5002 | LoopStackItem *loop_stack_item = &irb->loop_stack.last(); |
| 5003 | 5003 | ||
| 5004 | IrInstruction *is_comptime; | 5004 | IrInstruction *is_comptime; |
| 5005 | if (ir_should_inline(irb->exec, scope) || node->data.break_expr.is_inline) { | 5005 | if (ir_should_inline(irb->exec, scope)) { |
| 5006 | is_comptime = ir_build_const_bool(irb, scope, node, true); | 5006 | is_comptime = ir_build_const_bool(irb, scope, node, true); |
| 5007 | } else { | 5007 | } else { |
| 5008 | is_comptime = loop_stack_item->is_comptime; | 5008 | is_comptime = loop_stack_item->is_comptime; |
| ... | @@ -5025,7 +5025,7 @@ static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *scope, AstNode *nod | ... | @@ -5025,7 +5025,7 @@ static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *scope, AstNode *nod |
| 5025 | LoopStackItem *loop_stack_item = &irb->loop_stack.last(); | 5025 | LoopStackItem *loop_stack_item = &irb->loop_stack.last(); |
| 5026 | 5026 | ||
| 5027 | IrInstruction *is_comptime; | 5027 | IrInstruction *is_comptime; |
| 5028 | if (ir_should_inline(irb->exec, scope) || node->data.continue_expr.is_inline) { | 5028 | if (ir_should_inline(irb->exec, scope)) { |
| 5029 | is_comptime = ir_build_const_bool(irb, scope, node, true); | 5029 | is_comptime = ir_build_const_bool(irb, scope, node, true); |
| 5030 | } else { | 5030 | } else { |
| 5031 | is_comptime = loop_stack_item->is_comptime; | 5031 | is_comptime = loop_stack_item->is_comptime; |
src/parser.cpp+12-43| ... | @@ -1313,30 +1313,17 @@ static AstNode *ast_parse_else(ParseContext *pc, size_t *token_index, bool manda | ... | @@ -1313,30 +1313,17 @@ static AstNode *ast_parse_else(ParseContext *pc, size_t *token_index, bool manda |
| 1313 | 1313 | ||
| 1314 | /* | 1314 | /* |
| 1315 | IfExpression : IfVarExpression | IfBoolExpression | 1315 | IfExpression : IfVarExpression | IfBoolExpression |
| 1316 | IfBoolExpression = option("inline") "if" "(" Expression ")" Expression option(Else) | 1316 | IfBoolExpression = "if" "(" Expression ")" Expression option(Else) |
| 1317 | IfVarExpression = option("inline") "if" "(" ("const" | "var") option("*") Symbol option(":" TypeExpr) "?=" Expression ")" Expression Option(Else) | 1317 | IfVarExpression = "if" "(" ("const" | "var") option("*") Symbol option(":" TypeExpr) "?=" Expression ")" Expression Option(Else) |
| 1318 | */ | 1318 | */ |
| 1319 | static AstNode *ast_parse_if_expr(ParseContext *pc, size_t *token_index, bool mandatory) { | 1319 | static AstNode *ast_parse_if_expr(ParseContext *pc, size_t *token_index, bool mandatory) { |
| 1320 | Token *first_token = &pc->tokens->at(*token_index); | 1320 | Token *if_token = &pc->tokens->at(*token_index); |
| 1321 | Token *if_tok; | ||
| 1322 | 1321 | ||
| 1323 | bool is_inline; | 1322 | if (if_token->id == TokenIdKeywordIf) { |
| 1324 | if (first_token->id == TokenIdKeywordInline) { | ||
| 1325 | if_tok = &pc->tokens->at(*token_index + 1); | ||
| 1326 | if (if_tok->id == TokenIdKeywordIf) { | ||
| 1327 | is_inline = true; | ||
| 1328 | *token_index += 2; | ||
| 1329 | } else if (mandatory) { | ||
| 1330 | ast_expect_token(pc, if_tok, TokenIdKeywordIf); | ||
| 1331 | } else { | ||
| 1332 | return nullptr; | ||
| 1333 | } | ||
| 1334 | } else if (first_token->id == TokenIdKeywordIf) { | ||
| 1335 | if_tok = first_token; | ||
| 1336 | is_inline = false; | ||
| 1337 | *token_index += 1; | 1323 | *token_index += 1; |
| 1338 | } else if (mandatory) { | 1324 | } else if (mandatory) { |
| 1339 | ast_expect_token(pc, first_token, TokenIdKeywordIf); | 1325 | ast_expect_token(pc, if_token, TokenIdKeywordIf); |
| 1326 | zig_unreachable(); | ||
| 1340 | } else { | 1327 | } else { |
| 1341 | return nullptr; | 1328 | return nullptr; |
| 1342 | } | 1329 | } |
| ... | @@ -1345,8 +1332,7 @@ static AstNode *ast_parse_if_expr(ParseContext *pc, size_t *token_index, bool ma | ... | @@ -1345,8 +1332,7 @@ static AstNode *ast_parse_if_expr(ParseContext *pc, size_t *token_index, bool ma |
| 1345 | 1332 | ||
| 1346 | Token *token = &pc->tokens->at(*token_index); | 1333 | Token *token = &pc->tokens->at(*token_index); |
| 1347 | if (token->id == TokenIdKeywordConst || token->id == TokenIdKeywordVar) { | 1334 | if (token->id == TokenIdKeywordConst || token->id == TokenIdKeywordVar) { |
| 1348 | AstNode *node = ast_create_node(pc, NodeTypeIfVarExpr, if_tok); | 1335 | AstNode *node = ast_create_node(pc, NodeTypeIfVarExpr, if_token); |
| 1349 | node->data.if_var_expr.is_inline = is_inline; | ||
| 1350 | node->data.if_var_expr.var_decl.is_const = (token->id == TokenIdKeywordConst); | 1336 | node->data.if_var_expr.var_decl.is_const = (token->id == TokenIdKeywordConst); |
| 1351 | *token_index += 1; | 1337 | *token_index += 1; |
| 1352 | 1338 | ||
| ... | @@ -1383,8 +1369,7 @@ static AstNode *ast_parse_if_expr(ParseContext *pc, size_t *token_index, bool ma | ... | @@ -1383,8 +1369,7 @@ static AstNode *ast_parse_if_expr(ParseContext *pc, size_t *token_index, bool ma |
| 1383 | 1369 | ||
| 1384 | return node; | 1370 | return node; |
| 1385 | } else { | 1371 | } else { |
| 1386 | AstNode *node = ast_create_node(pc, NodeTypeIfBoolExpr, if_tok); | 1372 | AstNode *node = ast_create_node(pc, NodeTypeIfBoolExpr, if_token); |
| 1387 | node->data.if_bool_expr.is_inline = is_inline; | ||
| 1388 | node->data.if_bool_expr.condition = ast_parse_expression(pc, token_index, true); | 1373 | node->data.if_bool_expr.condition = ast_parse_expression(pc, token_index, true); |
| 1389 | ast_eat_token(pc, token_index, TokenIdRParen); | 1374 | ast_eat_token(pc, token_index, TokenIdRParen); |
| 1390 | node->data.if_bool_expr.then_block = ast_parse_expression(pc, token_index, true); | 1375 | node->data.if_bool_expr.then_block = ast_parse_expression(pc, token_index, true); |
| ... | @@ -1700,38 +1685,22 @@ static AstNode *ast_parse_for_expr(ParseContext *pc, size_t *token_index, bool m | ... | @@ -1700,38 +1685,22 @@ static AstNode *ast_parse_for_expr(ParseContext *pc, size_t *token_index, bool m |
| 1700 | } | 1685 | } |
| 1701 | 1686 | ||
| 1702 | /* | 1687 | /* |
| 1703 | SwitchExpression = option("inline") "switch" "(" Expression ")" "{" many(SwitchProng) "}" | 1688 | SwitchExpression = "switch" "(" Expression ")" "{" many(SwitchProng) "}" |
| 1704 | SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" option("*") Symbol "|") Expression "," | 1689 | SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" option("*") Symbol "|") Expression "," |
| 1705 | SwitchItem : Expression | (Expression "..." Expression) | 1690 | SwitchItem : Expression | (Expression "..." Expression) |
| 1706 | */ | 1691 | */ |
| 1707 | static AstNode *ast_parse_switch_expr(ParseContext *pc, size_t *token_index, bool mandatory) { | 1692 | static AstNode *ast_parse_switch_expr(ParseContext *pc, size_t *token_index, bool mandatory) { |
| 1708 | Token *first_token = &pc->tokens->at(*token_index); | 1693 | Token *switch_token = &pc->tokens->at(*token_index); |
| 1709 | Token *switch_token; | 1694 | if (switch_token->id == TokenIdKeywordSwitch) { |
| 1710 | bool is_inline; | ||
| 1711 | if (first_token->id == TokenIdKeywordInline) { | ||
| 1712 | is_inline = true; | ||
| 1713 | switch_token = &pc->tokens->at(*token_index + 1); | ||
| 1714 | if (switch_token->id == TokenIdKeywordSwitch) { | ||
| 1715 | *token_index += 2; | ||
| 1716 | } else if (mandatory) { | ||
| 1717 | ast_expect_token(pc, first_token, TokenIdKeywordSwitch); | ||
| 1718 | zig_unreachable(); | ||
| 1719 | } else { | ||
| 1720 | return nullptr; | ||
| 1721 | } | ||
| 1722 | } else if (first_token->id == TokenIdKeywordSwitch) { | ||
| 1723 | is_inline = false; | ||
| 1724 | switch_token = first_token; | ||
| 1725 | *token_index += 1; | 1695 | *token_index += 1; |
| 1726 | } else if (mandatory) { | 1696 | } else if (mandatory) { |
| 1727 | ast_expect_token(pc, first_token, TokenIdKeywordSwitch); | 1697 | ast_expect_token(pc, switch_token, TokenIdKeywordSwitch); |
| 1728 | zig_unreachable(); | 1698 | zig_unreachable(); |
| 1729 | } else { | 1699 | } else { |
| 1730 | return nullptr; | 1700 | return nullptr; |
| 1731 | } | 1701 | } |
| 1732 | 1702 | ||
| 1733 | AstNode *node = ast_create_node(pc, NodeTypeSwitchExpr, switch_token); | 1703 | AstNode *node = ast_create_node(pc, NodeTypeSwitchExpr, switch_token); |
| 1734 | node->data.switch_expr.is_inline = is_inline; | ||
| 1735 | 1704 | ||
| 1736 | ast_eat_token(pc, token_index, TokenIdLParen); | 1705 | ast_eat_token(pc, token_index, TokenIdLParen); |
| 1737 | node->data.switch_expr.expr = ast_parse_expression(pc, token_index, true); | 1706 | node->data.switch_expr.expr = ast_parse_expression(pc, token_index, true); |
std/io.zig+1-1| ... | @@ -150,7 +150,7 @@ pub const OutStream = struct { | ... | @@ -150,7 +150,7 @@ pub const OutStream = struct { |
| 150 | @compileError("Incomplete format string: " ++ format); | 150 | @compileError("Incomplete format string: " ++ format); |
| 151 | } | 151 | } |
| 152 | } | 152 | } |
| 153 | inline if (start_index < format.len) { | 153 | if (start_index < format.len) { |
| 154 | %return self.write(format[start_index...format.len]); | 154 | %return self.write(format[start_index...format.len]); |
| 155 | } | 155 | } |
| 156 | %return self.flush(); | 156 | %return self.flush(); |
test/cases/switch.zig+6-3| ... | @@ -33,18 +33,21 @@ fn testSwitchWithAllRanges(x: u32, y: u32) -> u32 { | ... | @@ -33,18 +33,21 @@ fn testSwitchWithAllRanges(x: u32, y: u32) -> u32 { |
| 33 | } | 33 | } |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | fn inlineSwitch() { | 36 | fn implicitComptimeSwitch() { |
| 37 | @setFnTest(this); | 37 | @setFnTest(this); |
| 38 | 38 | ||
| 39 | const x = 3 + 4; | 39 | const x = 3 + 4; |
| 40 | const result = inline switch (x) { | 40 | const result = switch (x) { |
| 41 | 3 => 10, | 41 | 3 => 10, |
| 42 | 4 => 11, | 42 | 4 => 11, |
| 43 | 5, 6 => 12, | 43 | 5, 6 => 12, |
| 44 | 7, 8 => 13, | 44 | 7, 8 => 13, |
| 45 | else => 14, | 45 | else => 14, |
| 46 | }; | 46 | }; |
| 47 | assert(result + 1 == 14); | 47 | |
| 48 | comptime { | ||
| 49 | assert(result + 1 == 14); | ||
| 50 | } | ||
| 48 | } | 51 | } |
| 49 | 52 | ||
| 50 | fn switchOnEnum() { | 53 | fn switchOnEnum() { |