diff --git a/doc/langref.md b/doc/langref.md index 451643fa4e..ab06932562 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -79,7 +79,7 @@ BlockExpression = IfExpression | Block | WhileExpression | ForExpression | Switc SwitchExpression = "switch" "(" Expression ")" "{" many(SwitchProng) "}" -SwitchProng = (list(SwitchItem, ",") | "else") option(":" "(" "Symbol" ")") "=>" Expression "," +SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" "Symbol" "|") Expression "," SwitchItem = Expression | (Expression "..." Expression) diff --git a/src/analyze.cpp b/src/analyze.cpp index cc6990ab57..3ab41409bb 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4479,9 +4479,9 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, AstNode *expr_node = node->data.switch_expr.expr; TypeTableEntry *expr_type = analyze_expression(g, import, context, nullptr, expr_node); - if (expected_type == nullptr) { - zig_panic("TODO resolve peer compatibility of switch prongs"); - } + int prong_count = node->data.switch_expr.prongs.length; + AstNode **peer_nodes = allocate(prong_count); + TypeTableEntry **peer_types = allocate(prong_count); if (expr_type->id == TypeTableEntryIdInvalid) { return expr_type; @@ -4491,7 +4491,7 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, return g->builtin_types.entry_invalid; } else { AstNode *else_prong = nullptr; - for (int prong_i = 0; prong_i < node->data.switch_expr.prongs.length; prong_i += 1) { + for (int prong_i = 0; prong_i < prong_count; prong_i += 1) { AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i); TypeTableEntry *var_type; @@ -4528,11 +4528,12 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, var_type, true); } - analyze_expression(g, import, child_context, expected_type, + peer_types[prong_i] = analyze_expression(g, import, child_context, expected_type, prong_node->data.switch_prong.expr); + peer_nodes[prong_i] = prong_node->data.switch_prong.expr; } } - return expected_type; + return resolve_peer_type_compatibility(g, import, context, node, peer_nodes, peer_types, prong_count); } static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, diff --git a/src/parser.cpp b/src/parser.cpp index fe17495fc4..f9880156d8 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1834,7 +1834,7 @@ static AstNode *ast_parse_for_expr(ParseContext *pc, int *token_index, bool mand /* SwitchExpression : "switch" "(" Expression ")" "{" many(SwitchProng) "}" -SwitchProng : (list(SwitchItem, ",") | "else") option("," "(" "Symbol" ")") "=>" Expression "," +SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" "Symbol" "|") Expression "," SwitchItem : Expression | (Expression "..." Expression) */ static AstNode *ast_parse_switch_expr(ParseContext *pc, int *token_index, bool mandatory) { @@ -1895,15 +1895,15 @@ static AstNode *ast_parse_switch_expr(ParseContext *pc, int *token_index, bool m break; } - Token *arrow_or_colon = &pc->tokens->at(*token_index); - if (arrow_or_colon->id == TokenIdColon) { + ast_eat_token(pc, token_index, TokenIdFatArrow); + + Token *maybe_bar = &pc->tokens->at(*token_index); + if (maybe_bar->id == TokenIdBinOr) { *token_index += 1; - ast_eat_token(pc, token_index, TokenIdLParen); prong_node->data.switch_prong.var_symbol = ast_parse_symbol(pc, token_index); - ast_eat_token(pc, token_index, TokenIdRParen); + ast_eat_token(pc, token_index, TokenIdBinOr); } - ast_eat_token(pc, token_index, TokenIdFatArrow); prong_node->data.switch_prong.expr = ast_parse_expression(pc, token_index, true); ast_eat_token(pc, token_index, TokenIdComma); diff --git a/test/self_hosted.zig b/test/self_hosted.zig index 645726c657..2181a5e2b6 100644 --- a/test/self_hosted.zig +++ b/test/self_hosted.zig @@ -105,3 +105,27 @@ fn non_const_cast_bool_to_int(t: bool, f: bool) { if (i32(t) != i32(1)) unreachable{} if (i32(f) != i32(0)) unreachable{} } + + +#attribute("test") +fn switch_on_enum() { + const fruit = Fruit.Orange; + switch (fruit) { + Fruit.Apple => unreachable{}, + Fruit.Orange => {}, + Fruit.Banana => unreachable{}, + } + non_const_switch_on_enum(fruit); +} +enum Fruit { + Apple, + Orange, + Banana, +} +fn non_const_switch_on_enum(fruit: Fruit) { + switch (fruit) { + Fruit.Apple => unreachable{}, + Fruit.Orange => {}, + Fruit.Banana => unreachable{}, + } +}