diff --git a/doc/langref.md b/doc/langref.md index 040acf2eca..25a99e36b6 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -69,7 +69,7 @@ AssignmentExpression = UnwrapExpression AssignmentOperator UnwrapExpression | Un AssignmentOperator = "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "&=" | "^=" | "|=" | "*%=" | "+%=" | "-%=" | "<<%=" -BlockExpression(body) = Block | IfExpression(body) | TryExpression(body) | WhileExpression(body) | ForExpression(body) | SwitchExpression | CompTimeExpression(body) +BlockExpression(body) = Block | IfExpression(body) | TryExpression(body) | TestExpression(body) | WhileExpression(body) | ForExpression(body) | SwitchExpression | CompTimeExpression(body) CompTimeExpression(body) = "comptime" body @@ -89,13 +89,11 @@ ReturnExpression = option("%") "return" option(Expression) Defer(body) = option("%") "defer" body -IfExpression(body) = IfVarExpression(body) | IfBoolExpression(body) +IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body)) -IfBoolExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body)) +TryExpression(body) = "try" "(" Expression ")" option("|" option("*") Symbol "|") body option("else" option("|" Symbol "|") BlockExpression(body)) -TryExpression(body) = "try" "(" option(("const" | "var") option("*") Symbol "=") Expression ")" body option("else" option("|" Symbol "|") BlockExpression(body)) - -IfVarExpression(body) = "if" "(" ("const" | "var") option("*") Symbol option(":" TypeExpr) "?=" Expression ")" body Option("else" BlockExpression(body)) +TestExpression(body) = "test" "(" Expression ")" option("|" option("*") Symbol "|") body option("else" option("|" Symbol "|") BlockExpression(body)) BoolAndExpression = ComparisonExpression "and" BoolAndExpression | ComparisonExpression diff --git a/src/all_types.hpp b/src/all_types.hpp index 2e21cdc86d..1d8c094c5d 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -344,7 +344,6 @@ enum NodeType { NodeTypeThisLiteral, NodeTypeUnreachable, NodeTypeIfBoolExpr, - NodeTypeIfVarExpr, NodeTypeWhileExpr, NodeTypeForExpr, NodeTypeSwitchExpr, @@ -364,6 +363,7 @@ enum NodeType { NodeTypeErrorType, NodeTypeVarLiteral, NodeTypeTryExpr, + NodeTypeTestExpr, NodeTypeInlineExpr, }; @@ -577,7 +577,6 @@ struct AstNodeIfBoolExpr { }; struct AstNodeTryExpr { - bool var_is_const; Buf *var_symbol; bool var_is_ptr; AstNode *target_node; @@ -586,11 +585,12 @@ struct AstNodeTryExpr { Buf *err_symbol; }; -struct AstNodeIfVarExpr { - AstNodeVariableDeclaration var_decl; - AstNode *then_block; - AstNode *else_node; // null, block node, or other if expr node +struct AstNodeTestExpr { + Buf *var_symbol; bool var_is_ptr; + AstNode *target_node; + AstNode *then_node; + AstNode *else_node; // null, block node, or other if expr node }; struct AstNodeWhileExpr { @@ -807,8 +807,8 @@ struct AstNode { AstNodeSliceExpr slice_expr; AstNodeUse use; AstNodeIfBoolExpr if_bool_expr; - AstNodeIfVarExpr if_var_expr; AstNodeTryExpr try_expr; + AstNodeTestExpr test_expr; AstNodeWhileExpr while_expr; AstNodeForExpr for_expr; AstNodeSwitchExpr switch_expr; diff --git a/src/analyze.cpp b/src/analyze.cpp index aef85ca0b5..b2ef1b64e8 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2099,7 +2099,6 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { case NodeTypeSymbol: case NodeTypePrefixOpExpr: case NodeTypeIfBoolExpr: - case NodeTypeIfVarExpr: case NodeTypeWhileExpr: case NodeTypeForExpr: case NodeTypeSwitchExpr: @@ -2119,6 +2118,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { case NodeTypeErrorType: case NodeTypeVarLiteral: case NodeTypeTryExpr: + case NodeTypeTestExpr: case NodeTypeInlineExpr: zig_unreachable(); } diff --git a/src/ast_render.cpp b/src/ast_render.cpp index fb9a25eaa2..36ce69387b 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -192,8 +192,6 @@ static const char *node_type_str(NodeType node_type) { return "ThisLiteral"; case NodeTypeIfBoolExpr: return "IfBoolExpr"; - case NodeTypeIfVarExpr: - return "IfVarExpr"; case NodeTypeWhileExpr: return "WhileExpr"; case NodeTypeForExpr: @@ -236,6 +234,8 @@ static const char *node_type_str(NodeType node_type) { return "VarLiteral"; case NodeTypeTryExpr: return "TryExpr"; + case NodeTypeTestExpr: + return "TestExpr"; case NodeTypeInlineExpr: return "InlineExpr"; } @@ -760,38 +760,16 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { fprintf(ar->f, "null"); break; } - case NodeTypeIfVarExpr: - { - AstNodeVariableDeclaration *var_decl = &node->data.if_var_expr.var_decl; - const char *var_str = var_decl->is_const ? "const" : "var"; - const char *var_name = buf_ptr(var_decl->symbol); - const char *ptr_str = node->data.if_var_expr.var_is_ptr ? "*" : ""; - fprintf(ar->f, "if (%s %s%s", var_str, ptr_str, var_name); - if (var_decl->type) { - fprintf(ar->f, ": "); - render_node_ungrouped(ar, var_decl->type); - } - fprintf(ar->f, " ?= "); - render_node_grouped(ar, var_decl->expr); - fprintf(ar->f, ") "); - render_node_grouped(ar, node->data.if_var_expr.then_block); - if (node->data.if_var_expr.else_node) { - fprintf(ar->f, " else "); - render_node_grouped(ar, node->data.if_var_expr.else_node); - } - break; - } case NodeTypeTryExpr: { fprintf(ar->f, "try ("); - if (node->data.try_expr.var_symbol) { - const char *var_str = node->data.try_expr.var_is_const ? "const" : "var"; - const char *var_name = buf_ptr(node->data.try_expr.var_symbol); - const char *ptr_str = node->data.try_expr.var_is_ptr ? "*" : ""; - fprintf(ar->f, "%s %s%s = ", var_str, ptr_str, var_name); - } render_node_grouped(ar, node->data.try_expr.target_node); fprintf(ar->f, ") "); + if (node->data.try_expr.var_symbol) { + const char *ptr_str = node->data.try_expr.var_is_ptr ? "*" : ""; + const char *var_name = buf_ptr(node->data.try_expr.var_symbol); + fprintf(ar->f, "|%s%s| ", ptr_str, var_name); + } render_node_grouped(ar, node->data.try_expr.then_node); if (node->data.try_expr.else_node) { fprintf(ar->f, " else "); @@ -802,6 +780,23 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { } break; } + case NodeTypeTestExpr: + { + fprintf(ar->f, "test ("); + render_node_grouped(ar, node->data.test_expr.target_node); + fprintf(ar->f, ") "); + if (node->data.test_expr.var_symbol) { + const char *ptr_str = node->data.test_expr.var_is_ptr ? "*" : ""; + const char *var_name = buf_ptr(node->data.test_expr.var_symbol); + fprintf(ar->f, "|%s%s| ", ptr_str, var_name); + } + render_node_grouped(ar, node->data.test_expr.then_node); + if (node->data.test_expr.else_node) { + fprintf(ar->f, " else "); + render_node_grouped(ar, node->data.test_expr.else_node); + } + break; + } case NodeTypeSwitchExpr: { AstNodeSwitchExpr *switch_expr = &node->data.switch_expr; diff --git a/src/ir.cpp b/src/ir.cpp index f4e2673030..75c49dc53a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4941,14 +4941,14 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod return ir_build_asm(irb, scope, node, input_list, output_types, output_vars, return_count, is_volatile); } -static IrInstruction *ir_gen_if_var_expr(IrBuilder *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeIfVarExpr); +static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeTestExpr); - AstNodeVariableDeclaration *var_decl = &node->data.if_var_expr.var_decl; - AstNode *expr_node = var_decl->expr; - AstNode *then_node = node->data.if_var_expr.then_block; - AstNode *else_node = node->data.if_var_expr.else_node; - bool var_is_ptr = node->data.if_var_expr.var_is_ptr; + Buf *var_symbol = node->data.test_expr.var_symbol; + AstNode *expr_node = node->data.test_expr.target_node; + AstNode *then_node = node->data.test_expr.then_node; + AstNode *else_node = node->data.test_expr.else_node; + bool var_is_ptr = node->data.test_expr.var_is_ptr; IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR); if (maybe_val_ptr == irb->codegen->invalid_instruction) @@ -4970,21 +4970,23 @@ static IrInstruction *ir_gen_if_var_expr(IrBuilder *irb, Scope *scope, AstNode * ir_build_cond_br(irb, scope, node, is_non_null, then_block, else_block, is_comptime); ir_set_cursor_at_end(irb, then_block); - IrInstruction *var_type = nullptr; - if (var_decl->type) { - var_type = ir_gen_node(irb, var_decl->type, scope); - if (var_type == irb->codegen->invalid_instruction) - return irb->codegen->invalid_instruction; - } - bool is_shadowable = false; - bool is_const = var_decl->is_const; - VariableTableEntry *var = ir_create_var(irb, node, scope, - var_decl->symbol, is_const, is_const, is_shadowable, is_comptime); - IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, scope, node, maybe_val_ptr, false); - IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, scope, node, var_ptr_value); - ir_build_var_decl(irb, scope, node, var, var_type, var_value); - IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var->child_scope); + Scope *var_scope; + if (var_symbol) { + IrInstruction *var_type = nullptr; + bool is_shadowable = false; + bool is_const = true; + VariableTableEntry *var = ir_create_var(irb, node, scope, + var_symbol, is_const, is_const, is_shadowable, is_comptime); + + IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, scope, node, maybe_val_ptr, false); + IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, scope, node, var_ptr_value); + ir_build_var_decl(irb, scope, node, var, var_type, var_value); + var_scope = var->child_scope; + } else { + var_scope = scope; + } + IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var_scope); if (then_expr_result == irb->codegen->invalid_instruction) return then_expr_result; IrBasicBlock *after_then_block = irb->current_basic_block; @@ -5022,7 +5024,7 @@ static IrInstruction *ir_gen_try_expr(IrBuilder *irb, Scope *scope, AstNode *nod AstNode *then_node = node->data.try_expr.then_node; AstNode *else_node = node->data.try_expr.else_node; bool var_is_ptr = node->data.try_expr.var_is_ptr; - bool var_is_const = node->data.try_expr.var_is_const; + bool var_is_const = true; Buf *var_symbol = node->data.try_expr.var_symbol; Buf *err_symbol = node->data.try_expr.err_symbol; @@ -5659,10 +5661,10 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_lval_wrap(irb, scope, ir_gen_null_literal(irb, scope, node), lval); case NodeTypeVarLiteral: return ir_lval_wrap(irb, scope, ir_gen_var_literal(irb, scope, node), lval); - case NodeTypeIfVarExpr: - return ir_lval_wrap(irb, scope, ir_gen_if_var_expr(irb, scope, node), lval); case NodeTypeTryExpr: return ir_lval_wrap(irb, scope, ir_gen_try_expr(irb, scope, node), lval); + case NodeTypeTestExpr: + return ir_lval_wrap(irb, scope, ir_gen_test_expr(irb, scope, node), lval); case NodeTypeSwitchExpr: return ir_lval_wrap(irb, scope, ir_gen_switch_expr(irb, scope, node), lval); case NodeTypeGoto: diff --git a/src/parser.cpp b/src/parser.cpp index 5349154a39..7cea800ac0 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -640,7 +640,7 @@ static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, b } /* -TryExpression(body) = "try" "(" option(("const" | "var") option("*") Symbol "=") Expression ")" body option("else" option("|" Symbol "|") body) +TryExpression(body) = "try" "(" Expression ")" option("|" option("*") Symbol "|") body option("else" option("|" Symbol "|") BlockExpression(body)) */ static AstNode *ast_parse_try_expr(ParseContext *pc, size_t *token_index, bool mandatory) { Token *try_token = &pc->tokens->at(*token_index); @@ -656,38 +656,25 @@ static AstNode *ast_parse_try_expr(ParseContext *pc, size_t *token_index, bool m AstNode *node = ast_create_node(pc, NodeTypeTryExpr, try_token); ast_eat_token(pc, token_index, TokenIdLParen); + node->data.try_expr.target_node = ast_parse_expression(pc, token_index, true); + ast_eat_token(pc, token_index, TokenIdRParen); - Token *var_token = &pc->tokens->at(*token_index); - bool have_vars; - if (var_token->id == TokenIdKeywordVar) { - node->data.try_expr.var_is_const = false; + Token *open_bar_tok = &pc->tokens->at(*token_index); + if (open_bar_tok->id == TokenIdBinOr) { *token_index += 1; - have_vars = true; - } else if (var_token->id == TokenIdKeywordConst) { - node->data.try_expr.var_is_const = true; - *token_index += 1; - have_vars = true; - } else { - have_vars = false; - } - if (have_vars) { - Token *star_token = &pc->tokens->at(*token_index); - if (star_token->id == TokenIdStar) { - node->data.try_expr.var_is_ptr = true; + Token *star_tok = &pc->tokens->at(*token_index); + if (star_tok->id == TokenIdStar) { *token_index += 1; + node->data.try_expr.var_is_ptr = true; } Token *var_name_tok = ast_eat_token(pc, token_index, TokenIdSymbol); node->data.try_expr.var_symbol = token_buf(var_name_tok); - ast_eat_token(pc, token_index, TokenIdEq); + ast_eat_token(pc, token_index, TokenIdBinOr); } - node->data.try_expr.target_node = ast_parse_expression(pc, token_index, true); - - ast_eat_token(pc, token_index, TokenIdRParen); - node->data.try_expr.then_node = ast_parse_block_or_expression(pc, token_index, true); Token *else_token = &pc->tokens->at(*token_index); @@ -709,6 +696,53 @@ static AstNode *ast_parse_try_expr(ParseContext *pc, size_t *token_index, bool m return node; } +/* +TestExpression(body) = "test" "(" Expression ")" option("|" option("*") Symbol "|") body option("else" BlockExpression(body)) +*/ +static AstNode *ast_parse_test_expr(ParseContext *pc, size_t *token_index, bool mandatory) { + Token *test_token = &pc->tokens->at(*token_index); + if (test_token->id == TokenIdKeywordTest) { + *token_index += 1; + } else if (mandatory) { + ast_expect_token(pc, test_token, TokenIdKeywordTest); + zig_unreachable(); + } else { + return nullptr; + } + + AstNode *node = ast_create_node(pc, NodeTypeTestExpr, test_token); + + ast_eat_token(pc, token_index, TokenIdLParen); + node->data.test_expr.target_node = ast_parse_expression(pc, token_index, true); + ast_eat_token(pc, token_index, TokenIdRParen); + + Token *open_bar_tok = &pc->tokens->at(*token_index); + if (open_bar_tok->id == TokenIdBinOr) { + *token_index += 1; + + Token *star_tok = &pc->tokens->at(*token_index); + if (star_tok->id == TokenIdStar) { + *token_index += 1; + node->data.test_expr.var_is_ptr = true; + } + + Token *var_name_tok = ast_eat_token(pc, token_index, TokenIdSymbol); + node->data.test_expr.var_symbol = token_buf(var_name_tok); + + ast_eat_token(pc, token_index, TokenIdBinOr); + } + + node->data.test_expr.then_node = ast_parse_block_or_expression(pc, token_index, true); + + Token *else_token = &pc->tokens->at(*token_index); + if (else_token->id == TokenIdKeywordElse) { + *token_index += 1; + node->data.test_expr.else_node = ast_parse_block_expr_or_expression(pc, token_index, true); + } + + return node; +} + /* PrimaryExpression = Number | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." Symbol) | ContainerDecl KeywordLiteral = "true" | "false" | "null" | "break" | "continue" | "undefined" | "error" | "this" | "unreachable" @@ -1405,9 +1439,7 @@ static AstNode *ast_parse_bool_and_expr(ParseContext *pc, size_t *token_index, b } /* -IfExpression(body) = IfVarExpression(body) | IfBoolExpression(body) -IfBoolExpression(body) = "if" "(" Expression ")" body option("else" body) -IfVarExpression(body) = "if" "(" ("const" | "var") option("*") Symbol option(":" TypeExpr) "?=" Expression ")" body Option("else" body) +IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body)) */ static AstNode *ast_parse_if_expr(ParseContext *pc, size_t *token_index, bool mandatory) { Token *if_token = &pc->tokens->at(*token_index); @@ -1423,63 +1455,18 @@ static AstNode *ast_parse_if_expr(ParseContext *pc, size_t *token_index, bool ma ast_eat_token(pc, token_index, TokenIdLParen); - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdKeywordConst || token->id == TokenIdKeywordVar) { - AstNode *node = ast_create_node(pc, NodeTypeIfVarExpr, if_token); - node->data.if_var_expr.var_decl.is_const = (token->id == TokenIdKeywordConst); + AstNode *node = ast_create_node(pc, NodeTypeIfBoolExpr, if_token); + node->data.if_bool_expr.condition = ast_parse_expression(pc, token_index, true); + ast_eat_token(pc, token_index, TokenIdRParen); + node->data.if_bool_expr.then_block = ast_parse_block_or_expression(pc, token_index, true); + + Token *else_token = &pc->tokens->at(*token_index); + if (else_token->id == TokenIdKeywordElse) { *token_index += 1; - - Token *star_or_symbol = &pc->tokens->at(*token_index); - if (star_or_symbol->id == TokenIdStar) { - *token_index += 1; - node->data.if_var_expr.var_is_ptr = true; - Token *name_token = ast_eat_token(pc, token_index, TokenIdSymbol); - node->data.if_var_expr.var_decl.symbol = token_buf(name_token); - } else if (star_or_symbol->id == TokenIdSymbol) { - *token_index += 1; - node->data.if_var_expr.var_decl.symbol = token_buf(star_or_symbol); - } else { - ast_invalid_token_error(pc, star_or_symbol); - } - - - Token *eq_or_colon = &pc->tokens->at(*token_index); - if (eq_or_colon->id == TokenIdMaybeAssign) { - *token_index += 1; - node->data.if_var_expr.var_decl.expr = ast_parse_expression(pc, token_index, true); - } else if (eq_or_colon->id == TokenIdColon) { - *token_index += 1; - node->data.if_var_expr.var_decl.type = ast_parse_type_expr(pc, token_index, true); - - ast_eat_token(pc, token_index, TokenIdMaybeAssign); - node->data.if_var_expr.var_decl.expr = ast_parse_expression(pc, token_index, true); - } else { - ast_invalid_token_error(pc, eq_or_colon); - } - ast_eat_token(pc, token_index, TokenIdRParen); - node->data.if_var_expr.then_block = ast_parse_block_or_expression(pc, token_index, true); - - Token *else_token = &pc->tokens->at(*token_index); - if (else_token->id == TokenIdKeywordElse) { - *token_index += 1; - node->data.if_var_expr.else_node = ast_parse_block_expr_or_expression(pc, token_index, true); - } - - return node; - } else { - AstNode *node = ast_create_node(pc, NodeTypeIfBoolExpr, if_token); - node->data.if_bool_expr.condition = ast_parse_expression(pc, token_index, true); - ast_eat_token(pc, token_index, TokenIdRParen); - node->data.if_bool_expr.then_block = ast_parse_block_or_expression(pc, token_index, true); - - Token *else_token = &pc->tokens->at(*token_index); - if (else_token->id == TokenIdKeywordElse) { - *token_index += 1; - node->data.if_bool_expr.else_node = ast_parse_block_expr_or_expression(pc, token_index, true); - } - - return node; + node->data.if_bool_expr.else_node = ast_parse_block_expr_or_expression(pc, token_index, true); } + + return node; } /* @@ -1862,7 +1849,7 @@ static AstNode *ast_parse_switch_expr(ParseContext *pc, size_t *token_index, boo } /* -BlockExpression(body) = Block | IfExpression(body) | TryExpression(body) | WhileExpression(body) | ForExpression(body) | SwitchExpression | CompTimeExpression(body) +BlockExpression(body) = Block | IfExpression(body) | TryExpression(body) | TestExpression(body) | WhileExpression(body) | ForExpression(body) | SwitchExpression | CompTimeExpression(body) */ static AstNode *ast_parse_block_expr(ParseContext *pc, size_t *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); @@ -1895,6 +1882,10 @@ static AstNode *ast_parse_block_expr(ParseContext *pc, size_t *token_index, bool if (try_node) return try_node; + AstNode *test_node = ast_parse_test_expr(pc, token_index, false); + if (test_node) + return test_node; + if (mandatory) ast_invalid_token_error(pc, token); @@ -2079,14 +2070,14 @@ static bool statement_terminates_without_semicolon(AstNode *node) { if (node->data.if_bool_expr.else_node) return statement_terminates_without_semicolon(node->data.if_bool_expr.else_node); return node->data.if_bool_expr.then_block->type == NodeTypeBlock; - case NodeTypeIfVarExpr: - if (node->data.if_var_expr.else_node) - return statement_terminates_without_semicolon(node->data.if_var_expr.else_node); - return node->data.if_var_expr.then_block->type == NodeTypeBlock; case NodeTypeTryExpr: if (node->data.try_expr.else_node) return statement_terminates_without_semicolon(node->data.try_expr.else_node); return node->data.try_expr.then_node->type == NodeTypeBlock; + case NodeTypeTestExpr: + if (node->data.test_expr.else_node) + return statement_terminates_without_semicolon(node->data.test_expr.else_node); + return node->data.test_expr.then_node->type == NodeTypeBlock; case NodeTypeWhileExpr: return node->data.while_expr.body->type == NodeTypeBlock; case NodeTypeForExpr: @@ -2667,17 +2658,16 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont visit_field(&node->data.if_bool_expr.then_block, visit, context); visit_field(&node->data.if_bool_expr.else_node, visit, context); break; - case NodeTypeIfVarExpr: - visit_field(&node->data.if_var_expr.var_decl.type, visit, context); - visit_field(&node->data.if_var_expr.var_decl.expr, visit, context); - visit_field(&node->data.if_var_expr.then_block, visit, context); - visit_field(&node->data.if_var_expr.else_node, visit, context); - break; case NodeTypeTryExpr: visit_field(&node->data.try_expr.target_node, visit, context); visit_field(&node->data.try_expr.then_node, visit, context); visit_field(&node->data.try_expr.else_node, visit, context); break; + case NodeTypeTestExpr: + visit_field(&node->data.test_expr.target_node, visit, context); + visit_field(&node->data.test_expr.then_node, visit, context); + visit_field(&node->data.test_expr.else_node, visit, context); + break; case NodeTypeWhileExpr: visit_field(&node->data.while_expr.condition, visit, context); visit_field(&node->data.while_expr.body, visit, context); diff --git a/std/buf_map.zig b/std/buf_map.zig index 3f7787a7c3..db9fd15205 100644 --- a/std/buf_map.zig +++ b/std/buf_map.zig @@ -28,7 +28,7 @@ pub const BufMap = struct { } pub fn set(self: &BufMap, key: []const u8, value: []const u8) -> %void { - if (const entry ?= self.hash_map.get(key)) { + test (self.hash_map.get(key)) |entry| { const value_copy = %return self.copy(value); %defer self.free(value_copy); %return self.hash_map.put(key, value_copy); diff --git a/std/build.zig b/std/build.zig index 8d24af177f..e83a458d32 100644 --- a/std/build.zig +++ b/std/build.zig @@ -309,7 +309,7 @@ pub const Builder = struct { } fn processNixOSEnvVars(self: &Builder) { - if (const nix_cflags_compile ?= os.getEnv("NIX_CFLAGS_COMPILE")) { + test (os.getEnv("NIX_CFLAGS_COMPILE")) |nix_cflags_compile| { var it = mem.split(nix_cflags_compile, ' '); while (true) { const word = it.next() ?? break; @@ -325,7 +325,7 @@ pub const Builder = struct { } } } - if (const nix_ldflags ?= os.getEnv("NIX_LDFLAGS")) { + test (os.getEnv("NIX_LDFLAGS")) |nix_ldflags| { var it = mem.split(nix_ldflags, ' '); while (true) { const word = it.next() ?? break; @@ -353,7 +353,7 @@ pub const Builder = struct { .type_id = type_id, .description = description, }; - if (const _ ?= %%self.available_options_map.put(name, available_option)) { + test (%%self.available_options_map.put(name, available_option)) { debug.panic("Option '{}' declared twice", name); } %%self.available_options_list.append(available_option); @@ -410,11 +410,11 @@ pub const Builder = struct { } pub fn addUserInputOption(self: &Builder, name: []const u8, value: []const u8) -> bool { - if (var prev_value ?= %%self.user_input_options.put(name, UserInputOption { + test (%%self.user_input_options.put(name, UserInputOption { .name = name, .value = UserValue.Scalar { value }, .used = false, - })) { + })) |*prev_value| { switch (prev_value.value) { UserValue.Scalar => |s| { var list = List([]const u8).init(self.allocator); @@ -444,11 +444,11 @@ pub const Builder = struct { } pub fn addUserInputFlag(self: &Builder, name: []const u8) -> bool { - if (const prev_value ?= %%self.user_input_options.put(name, UserInputOption { + test (%%self.user_input_options.put(name, UserInputOption { .name = name, .value = UserValue.Flag, .used = false, - })) { + })) |*prev_value| { switch (prev_value.value) { UserValue.Scalar => |s| { %%io.stderr.printf("Flag '-D{}' conflicts with option '-D{}={}'.\n", name, name, s); @@ -766,7 +766,7 @@ pub const LibOrExeStep = struct { %%zig_args.append("--release"); } - if (const output_path ?= self.output_path) { + test (self.output_path) |output_path| { %%zig_args.append("--output"); %%zig_args.append(builder.pathFromRoot(output_path)); } @@ -912,7 +912,7 @@ pub const ObjectStep = struct { %%zig_args.append("--release"); } - if (const output_path ?= self.output_path) { + test (self.output_path) |output_path| { %%zig_args.append("--output"); %%zig_args.append(builder.pathFromRoot(output_path)); } @@ -1017,7 +1017,7 @@ pub const AsmStep = struct { %%zig_args.append("--release"); } - if (const output_path ?= self.output_path) { + test (self.output_path) |output_path| { %%zig_args.append("--output"); %%zig_args.append(builder.pathFromRoot(output_path)); } @@ -1194,7 +1194,7 @@ pub const LinkStep = struct { %%zig_args.append("--static"); } - if (const output_path ?= self.output_path) { + test (self.output_path) |output_path| { %%zig_args.append("--output"); %%zig_args.append(builder.pathFromRoot(output_path)); } @@ -1316,7 +1316,7 @@ pub const TestStep = struct { %%zig_args.append("--release"); } - if (const filter ?= self.filter) { + test (self.filter) |filter| { %%zig_args.append("--test-filter"); %%zig_args.append(filter); } diff --git a/std/hash_map.zig b/std/hash_map.zig index fdb7e65e8f..1102e96eeb 100644 --- a/std/hash_map.zig +++ b/std/hash_map.zig @@ -246,7 +246,7 @@ test "basicHashMapTest" { assert((??map.get(2)).value == 22); _ = map.remove(2); assert(map.remove(2) == null); - assert(if (const entry ?= map.get(2)) false else true); + assert(test (map.get(2)) false else true); } fn hash_i32(x: i32) -> u32 { diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 7dd02635d8..8c09f25bee 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -56,9 +56,9 @@ pub const ChildProcess = struct { errno.EINVAL, errno.ECHILD => unreachable, errno.EINTR => continue, else => { - if (const *stdin ?= self.stdin) { stdin.close(); } - if (const *stdout ?= self.stdin) { stdout.close(); } - if (const *stderr ?= self.stdin) { stderr.close(); } + test (self.stdin) |*stdin| { stdin.close(); } + test (self.stdout) |*stdout| { stdout.close(); } + test (self.stderr) |*stderr| { stderr.close(); } return error.Unexpected; }, } @@ -66,9 +66,10 @@ pub const ChildProcess = struct { break; } - if (const *stdin ?= self.stdin) { stdin.close(); } - if (const *stdout ?= self.stdin) { stdout.close(); } - if (const *stderr ?= self.stdin) { stderr.close(); } + // TODO oops! + test (self.stdin) |*stdin| { stdin.close(); } + test (self.stdin) |*stdout| { stdout.close(); } + test (self.stdin) |*stderr| { stderr.close(); } // Write @maxValue(ErrInt) to the write end of the err_pipe. This is after // waitpid, so this write is guaranteed to be after the child diff --git a/std/os/index.zig b/std/os/index.zig index e9c182dafa..ec32b60d84 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -162,7 +162,7 @@ pub fn posixOpen(file_path: []const u8, flags: usize, perm: usize, allocator: ?& if (file_path.len < stack_buf.len) { path0 = stack_buf[0...file_path.len + 1]; - } else if (const a ?= allocator) { + } else test (allocator) |a| { path0 = %return a.alloc(u8, file_path.len + 1); need_free = true; } else { @@ -230,7 +230,7 @@ pub fn posixExecve(exe_path: []const u8, argv: []const []const u8, env_map: &con mem.set(?&u8, argv_buf, null); defer { for (argv_buf) |arg| { - const arg_buf = if (const ptr ?= arg) cstr.toSlice(ptr) else break; + const arg_buf = test (arg) |ptr| cstr.toSlice(ptr) else break; allocator.free(arg_buf); } allocator.free(argv_buf); @@ -257,7 +257,7 @@ pub fn posixExecve(exe_path: []const u8, argv: []const []const u8, env_map: &con mem.set(?&u8, envp_buf, null); defer { for (envp_buf) |env| { - const env_buf = if (const ptr ?= env) cstr.toSlice(ptr) else break; + const env_buf = test (env) |ptr| cstr.toSlice(ptr) else break; allocator.free(env_buf); } allocator.free(envp_buf); diff --git a/std/special/build_runner.zig b/std/special/build_runner.zig index 7e9b31b241..5b412d83d6 100644 --- a/std/special/build_runner.zig +++ b/std/special/build_runner.zig @@ -53,7 +53,7 @@ pub fn main() -> %void { %%io.stderr.printf("Expected option name after '-D'\n\n"); return usage(&builder, false, &io.stderr); } - if (const name_end ?= mem.indexOfScalar(u8, option_contents, '=')) { + test (mem.indexOfScalar(u8, option_contents, '=')) |name_end| { const option_name = option_contents[0...name_end]; const option_value = option_contents[name_end + 1...]; if (builder.addUserInputOption(option_name, option_value)) diff --git a/std/special/compiler_rt.zig b/std/special/compiler_rt.zig index 1fdec3f157..13429d16ac 100644 --- a/std/special/compiler_rt.zig +++ b/std/special/compiler_rt.zig @@ -34,7 +34,7 @@ export fn __udivmoddi4(a: du_int, b: du_int, maybe_rem: ?&du_int) -> du_int { // 0 X // --- // 0 X - if (const rem ?= maybe_rem) { + test (maybe_rem) |rem| { *rem = n[low] % d[low]; } return n[low] / d[low]; @@ -42,7 +42,7 @@ export fn __udivmoddi4(a: du_int, b: du_int, maybe_rem: ?&du_int) -> du_int { // 0 X // --- // K X - if (const rem ?= maybe_rem) { + test (maybe_rem) |rem| { *rem = n[low]; } return 0; @@ -53,7 +53,7 @@ export fn __udivmoddi4(a: du_int, b: du_int, maybe_rem: ?&du_int) -> du_int { // K X // --- // 0 0 - if (var rem ?= maybe_rem) { + test (maybe_rem) |rem| { *rem = n[high] % d[low]; } return n[high] / d[low]; @@ -63,7 +63,7 @@ export fn __udivmoddi4(a: du_int, b: du_int, maybe_rem: ?&du_int) -> du_int { // K 0 // --- // K 0 - if (var rem ?= maybe_rem) { + test (maybe_rem) |rem| { r[high] = n[high] % d[high]; r[low] = 0; *rem = *@ptrCast(&du_int, &r[0]); @@ -75,7 +75,7 @@ export fn __udivmoddi4(a: du_int, b: du_int, maybe_rem: ?&du_int) -> du_int { // K 0 // if d is a power of 2 if ((d[high] & (d[high] - 1)) == 0) { - if (var rem ?= maybe_rem) { + test (maybe_rem) |rem| { r[low] = n[low]; r[high] = n[high] & (d[high] - 1); *rem = *@ptrCast(&du_int, &r[0]); @@ -88,7 +88,7 @@ export fn __udivmoddi4(a: du_int, b: du_int, maybe_rem: ?&du_int) -> du_int { sr = @clz(su_int(d[high])) - @clz(su_int(n[high])); // 0 <= sr <= n_uword_bits - 2 or sr large if (sr > n_uword_bits - 2) { - if (var rem ?= maybe_rem) { + test (maybe_rem) |rem| { *rem = *@ptrCast(&du_int, &n[0]); } return 0; @@ -109,7 +109,7 @@ export fn __udivmoddi4(a: du_int, b: du_int, maybe_rem: ?&du_int) -> du_int { // 0 K // if d is a power of 2 if ((d[low] & (d[low] - 1)) == 0) { - if (var rem ?= maybe_rem) { + test (maybe_rem) |rem| { *rem = n[low] & (d[low] - 1); } if (d[low] == 1) { @@ -153,7 +153,7 @@ export fn __udivmoddi4(a: du_int, b: du_int, maybe_rem: ?&du_int) -> du_int { sr = @clz(su_int(d[high])) - @clz(su_int(n[high])); // 0 <= sr <= n_uword_bits - 1 or sr large if (sr > n_uword_bits - 1) { - if (var rem ?= maybe_rem) { + test (maybe_rem) |rem| { *rem = *@ptrCast(&du_int, &n[0]); } return 0; @@ -198,7 +198,7 @@ export fn __udivmoddi4(a: du_int, b: du_int, maybe_rem: ?&du_int) -> du_int { sr -= 1; } *@ptrCast(&du_int, &q[0]) = (*@ptrCast(&du_int, &q[0]) << 1) | u64(carry); - if (var rem ?= maybe_rem) { + test (maybe_rem) |rem| { *rem = *@ptrCast(&du_int, &r[0]); } return *@ptrCast(&du_int, &q[0]); diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 6bd8cc6cb1..de69e7afc9 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -128,16 +128,6 @@ fn testTruncate(x: u32) -> u8 { @truncate(u8, x) } -test "assignToIfVarPtr" { - var maybe_bool: ?bool = true; - - if (const *b ?= maybe_bool) { - *b = false; - } - - assert(??maybe_bool == false); -} - fn first4KeysOfHomeRow() -> []const u8 { "aoeu" } diff --git a/test/cases/null.zig b/test/cases/null.zig index ad0b53ddec..36a1bf7fd2 100644 --- a/test/cases/null.zig +++ b/test/cases/null.zig @@ -3,7 +3,7 @@ const assert = @import("std").debug.assert; test "nullableType" { const x : ?bool = @generatedCode(true); - if (const y ?= x) { + test (x) |y| { if (y) { // OK } else { @@ -26,16 +26,17 @@ test "nullableType" { assert(num == 13); } -test "assignToIfVarPtr" { +test "test maybe object and get a pointer to the inner value" { var maybe_bool: ?bool = true; - if (const *b ?= maybe_bool) { + test (maybe_bool) |*b| { *b = false; } assert(??maybe_bool == false); } + test "rhsMaybeUnwrapReturn" { const x: ?bool = @generatedCode(true); const y = x ?? return; @@ -49,7 +50,8 @@ test "maybe return" { fn maybeReturnImpl() { assert(??foo(1235)); - assert(if (const _ ?= foo(null)) false else true); + test (foo(null)) + unreachable; assert(!??foo(1234)); } @@ -64,10 +66,10 @@ test "ifVarMaybePointer" { } fn shouldBeAPlus1(p: &const Particle) -> u64 { var maybe_particle: ?Particle = *p; - if (const *particle ?= maybe_particle) { + test (maybe_particle) |*particle| { particle.a += 1; } - if (const particle ?= maybe_particle) { + test (maybe_particle) |particle| { return particle.a; } return 0; @@ -114,7 +116,7 @@ fn nullableVoidImpl() { } fn bar(x: ?void) -> ?void { - if (const _ ?= x) { + test (x) { return {}; } else { return null; diff --git a/test/cases/try.zig b/test/cases/try.zig index 6979d36a2f..98ed748f71 100644 --- a/test/cases/try.zig +++ b/test/cases/try.zig @@ -7,7 +7,7 @@ test "tryOnErrorUnion" { } fn tryOnErrorUnionImpl() { - const x = try (const val = returnsTen()) { + const x = try (returnsTen()) |val| { val + 1 } else |err| switch (err) { error.ItBroke, error.NoMem => 1, @@ -51,7 +51,7 @@ fn failIfTrue(ok: bool) -> %void { //fn tryThenNotExecutedWithAssignment() { // @setFnTest(this); // -// try (_ = failIfTrue(true)) { +// try (failIfTrue(true)) { // unreachable; // } else |err| { // assert(err == error.ItBroke); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 1f6c519d1a..5d6a456c38 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -118,92 +118,38 @@ pub fn addCases(cases: &tests.CompileErrorContext) { \\} , ".tmp_source.zig:5:5: error: invalid token: 'var'"); - cases.add("implicit semicolon - if(var) statement", - \\export fn entry() { - \\ if(_=foo()) {} - \\ var good = {}; - \\ if(_=foo()) ({}) - \\ var bad = {}; - \\} - , ".tmp_source.zig:5:5: error: invalid token: 'var'"); - - cases.add("implicit semicolon - if(var) expression", - \\export fn entry() { - \\ _ = if(_=foo()) {}; - \\ var good = {}; - \\ _ = if(_=foo()) {} - \\ var bad = {}; - \\} - , ".tmp_source.zig:5:5: error: invalid token: 'var'"); - - cases.add("implicit semicolon - if(var)-else statement", - \\export fn entry() { - \\ if(_=foo()) {} else {} - \\ var good = {}; - \\ if(_=foo()) ({}) else ({}) - \\ var bad = {}; - \\} - , ".tmp_source.zig:5:5: error: invalid token: 'var'"); - - cases.add("implicit semicolon - if(var)-else expression", - \\export fn entry() { - \\ _ = if(_=foo()) {} else {}; - \\ var good = {}; - \\ _ = if(_=foo()) {} else {} - \\ var bad = {}; - \\} - , ".tmp_source.zig:5:5: error: invalid token: 'var'"); - - cases.add("implicit semicolon - if(var)-else-if(var) statement", - \\export fn entry() { - \\ if(_=foo()) {} else if(_=foo()) {} - \\ var good = {}; - \\ if(_=foo()) ({}) else if(_=foo()) ({}) - \\ var bad = {}; - \\} - , ".tmp_source.zig:5:5: error: invalid token: 'var'"); - - cases.add("implicit semicolon - if(var)-else-if(var) expression", - \\export fn entry() { - \\ _ = if(_=foo()) {} else if(_=foo()) {}; - \\ var good = {}; - \\ _ = if(_=foo()) {} else if(_=foo()) {} - \\ var bad = {}; - \\} - , ".tmp_source.zig:5:5: error: invalid token: 'var'"); - - cases.add("implicit semicolon - if(var)-else-if(var)-else statement", - \\export fn entry() { - \\ if(_=foo()) {} else if(_=foo()) {} else {} - \\ var good = {}; - \\ if(_=foo()) ({}) else if(_=foo()) ({}) else ({}) - \\ var bad = {}; - \\} - , ".tmp_source.zig:5:5: error: invalid token: 'var'"); - - cases.add("implicit semicolon - if(var)-else-if(var)-else expression", - \\export fn entry() { - \\ _ = if(_=foo()) {} else if(_=foo()) {} else {}; - \\ var good = {}; - \\ _ = if(_=foo()) {} else if(_=foo()) {} else {} - \\ var bad = {}; - \\} - , ".tmp_source.zig:5:5: error: invalid token: 'var'"); - cases.add("implicit semicolon - try statement", \\export fn entry() { - \\ try (_ = foo()) {} + \\ try (foo()) {} \\ var good = {}; - \\ try (_ = foo()) ({}) + \\ try (foo()) ({}) \\ var bad = {}; \\} , ".tmp_source.zig:5:5: error: invalid token: 'var'"); cases.add("implicit semicolon - try expression", \\export fn entry() { - \\ _ = try (_ = foo()) {}; + \\ _ = try (foo()) {}; \\ var good = {}; - \\ _ = try (_ = foo()) {} + \\ _ = try (foo()) {} + \\ var bad = {}; + \\} + , ".tmp_source.zig:5:5: error: invalid token: 'var'"); + + cases.add("implicit semicolon - test statement", + \\export fn entry() { + \\ test (foo()) {} + \\ var good = {}; + \\ test (foo()) ({}) + \\ var bad = {}; + \\} + , ".tmp_source.zig:5:5: error: invalid token: 'var'"); + + cases.add("implicit semicolon - test expression", + \\export fn entry() { + \\ _ = test (foo()) {}; + \\ var good = {}; + \\ _ = test (foo()) {} \\ var bad = {}; \\} , ".tmp_source.zig:5:5: error: invalid token: 'var'"); @@ -554,9 +500,9 @@ pub fn addCases(cases: &tests.CompileErrorContext) { cases.add("invalid maybe type", \\export fn f() { - \\ if (const x ?= true) { } + \\ test (true) |x| { } \\} - , ".tmp_source.zig:2:20: error: expected nullable type, found 'bool'"); + , ".tmp_source.zig:2:11: error: expected nullable type, found 'bool'"); cases.add("cast unreachable", \\fn f() -> i32 { diff --git a/test/tests.zig b/test/tests.zig index 3340e5913c..354776140e 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -350,7 +350,7 @@ pub const CompareOutputContext = struct { Special.Asm => { const obj_path = %%os.path.join(b.allocator, "test_artifacts", "test.o"); const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "assemble-and-link {}", case.name); - if (const filter ?= self.test_filter) { + test (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } @@ -379,7 +379,7 @@ pub const CompareOutputContext = struct { for ([]bool{false, true}) |release| { const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "{} {} ({})", "compare-output", case.name, if (release) "release" else "debug"); - if (const filter ?= self.test_filter) { + test (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; } @@ -407,7 +407,7 @@ pub const CompareOutputContext = struct { Special.DebugSafety => { const obj_path = %%os.path.join(b.allocator, "test_artifacts", "test.o"); const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "debug-safety {}", case.name); - if (const filter ?= self.test_filter) { + test (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } @@ -626,7 +626,7 @@ pub const CompileErrorContext = struct { for ([]bool{false, true}) |release| { const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "compile-error {} ({})", case.name, if (release) "release" else "debug"); - if (const filter ?= self.test_filter) { + test (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; } @@ -661,7 +661,7 @@ pub const BuildExamplesContext = struct { const b = self.b; const annotated_case_name = b.fmt("build {}", build_file); - if (const filter ?= self.test_filter) { + test (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } @@ -692,7 +692,7 @@ pub const BuildExamplesContext = struct { for ([]bool{false, true}) |release| { const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "build {} ({})", root_src, if (release) "release" else "debug"); - if (const filter ?= self.test_filter) { + test (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; } @@ -880,7 +880,7 @@ pub const ParseHContext = struct { const b = self.b; const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "parseh {}", case.name); - if (const filter ?= self.test_filter) { + test (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; }