| author | |
| committer | |
| log | ad9759bc8e993364490d7268d491620ee4a2cc24 |
| tree | 952a04c2e6d7e37132a0881e45e4ac1d4227e2e0 |
| parent | 3eca42c17be1105dbc76c22fdc8447bccf11de0d |
7 files changed, 209 insertions(+), 21 deletions(-)
doc/langref.md+3-3| ... | @@ -94,7 +94,7 @@ BlockExpression : IfExpression | Block | WhileExpression | ForExpression | Switc | ... | @@ -94,7 +94,7 @@ BlockExpression : IfExpression | Block | WhileExpression | ForExpression | Switc |
| 94 | 94 | ||
| 95 | SwitchExpression : "switch" "(" Expression ")" "{" many(SwitchProng) "}" | 95 | SwitchExpression : "switch" "(" Expression ")" "{" many(SwitchProng) "}" |
| 96 | 96 | ||
| 97 | SwitchProng : (list(SwitchItem, ",") | "else") option("," "(" "Symbol" ")") "=>" Expression "," | 97 | SwitchProng : (list(SwitchItem, ",") | "else") option(":" "(" "Symbol" ")") "=>" Expression "," |
| 98 | 98 | ||
| 99 | SwitchItem : Expression | (Expression "..." Expression) | 99 | SwitchItem : Expression | (Expression "..." Expression) |
| 100 | 100 | ||
| ... | @@ -197,8 +197,8 @@ x{} | ... | @@ -197,8 +197,8 @@ x{} |
| 197 | | Example | Characters | Escapes | Null Term | Type | 197 | | Example | Characters | Escapes | Null Term | Type |
| 198 | ----------------|----------|-------------|----------------|-----------|---------- | 198 | ----------------|----------|-------------|----------------|-----------|---------- |
| 199 | Byte | 'H' | All ASCII | Byte | No | u8 | 199 | Byte | 'H' | All ASCII | Byte | No | u8 |
| 200 | UTF-8 Bytes | "hello" | All Unicode | Byte & Unicode | No | [5; u8] | 200 | UTF-8 Bytes | "hello" | All Unicode | Byte & Unicode | No | [5]u8 |
| 201 | UTF-8 C string | c"hello" | All Unicode | Byte & Unicode | Yes | *const u8 | 201 | UTF-8 C string | c"hello" | All Unicode | Byte & Unicode | Yes | &const u8 |
| 202 | 202 | ||
| 203 | ### Byte Escapes | 203 | ### Byte Escapes |
| 204 | 204 |
example/list/list.zig+12-14| ... | @@ -68,37 +68,35 @@ pub fn free#(T: type)(ptr: ?&T) { | ... | @@ -68,37 +68,35 @@ pub fn free#(T: type)(ptr: ?&T) { |
| 68 | 68 | ||
| 69 | ////////////////// alternate | 69 | ////////////////// alternate |
| 70 | 70 | ||
| 71 | // previously proposed, but with : instead of -> | 71 | // previously proposed but without -> |
| 72 | // `:` means "parser should expect a type now" | 72 | fn max#(T: type)(a: T, b: T) T { |
| 73 | fn max#(T :type)(a :T, b :T) :T { | ||
| 74 | if (a > b) a else b | 73 | if (a > b) a else b |
| 75 | } | 74 | } |
| 76 | 75 | ||
| 77 | // andy's new idea | 76 | // andy's new idea |
| 78 | // parameters can talk about @typeof() for previous parameters. | 77 | // parameters can reference other inline parameters. |
| 79 | // using :T here is equivalent to @child_type(@typeof(T)) | 78 | fn max(inline T: type, a: T, b: T) T { |
| 80 | fn max(T :type, a :T, b :T) :T { | ||
| 81 | if (a > b) a else b | 79 | if (a > b) a else b |
| 82 | } | 80 | } |
| 83 | 81 | ||
| 84 | fn f() { | 82 | fn f() { |
| 85 | const x :i32 = 1234; | 83 | const x: i32 = 1234; |
| 86 | const y :i32 = 5678; | 84 | const y: i32 = 5678; |
| 87 | const z = max(@typeof(x), x, y); | 85 | const z = max(@typeof(x), x, y); |
| 88 | } | 86 | } |
| 89 | 87 | ||
| 90 | // So, type-generic functions don't need any fancy syntax. type-generic | 88 | // So, type-generic functions don't need any fancy syntax. type-generic |
| 91 | // containers still do, though: | 89 | // containers still do, though: |
| 92 | 90 | ||
| 93 | pub struct List(T :type) { | 91 | pub struct List(T: type) { |
| 94 | items :?&T, | 92 | items: ?&T, |
| 95 | length :isize, | 93 | length: isize, |
| 96 | capacity :isize, | 94 | capacity: isize, |
| 97 | } | 95 | } |
| 98 | 96 | ||
| 99 | // Types are always marked with ':' so we don't need '#' to indicate type generic parameters. | 97 | // we don't need '#' to indicate type generic parameters. |
| 100 | 98 | ||
| 101 | fn f() { | 99 | fn f() { |
| 102 | var list :List(:u8); | 100 | var list: List(u8); |
| 103 | } | 101 | } |
| 104 | 102 |
src/all_types.hpp+12| ... | @@ -26,6 +26,7 @@ struct BuiltinFnEntry; | ... | @@ -26,6 +26,7 @@ struct BuiltinFnEntry; |
| 26 | struct LabelTableEntry; | 26 | struct LabelTableEntry; |
| 27 | struct TypeStructField; | 27 | struct TypeStructField; |
| 28 | struct CodeGen; | 28 | struct CodeGen; |
| 29 | struct ConstExprValue; | ||
| 29 | 30 | ||
| 30 | enum OutType { | 31 | enum OutType { |
| 31 | OutTypeUnknown, | 32 | OutTypeUnknown, |
| ... | @@ -57,6 +58,11 @@ struct Cast { | ... | @@ -57,6 +58,11 @@ struct Cast { |
| 57 | AstNode *source_node; | 58 | AstNode *source_node; |
| 58 | }; | 59 | }; |
| 59 | 60 | ||
| 61 | struct ConstEnumValue { | ||
| 62 | uint64_t tag; | ||
| 63 | ConstExprValue *payload; | ||
| 64 | }; | ||
| 65 | |||
| 60 | struct ConstExprValue { | 66 | struct ConstExprValue { |
| 61 | bool ok; // true if constant expression evalution worked | 67 | bool ok; // true if constant expression evalution worked |
| 62 | bool depends_on_compile_var; | 68 | bool depends_on_compile_var; |
| ... | @@ -69,6 +75,7 @@ struct ConstExprValue { | ... | @@ -69,6 +75,7 @@ struct ConstExprValue { |
| 69 | FnTableEntry *x_fn; | 75 | FnTableEntry *x_fn; |
| 70 | TypeTableEntry *x_type; | 76 | TypeTableEntry *x_type; |
| 71 | ConstExprValue *x_maybe; | 77 | ConstExprValue *x_maybe; |
| 78 | ConstEnumValue x_enum; | ||
| 72 | } data; | 79 | } data; |
| 73 | }; | 80 | }; |
| 74 | 81 | ||
| ... | @@ -426,6 +433,10 @@ struct AstNodeSwitchProng { | ... | @@ -426,6 +433,10 @@ struct AstNodeSwitchProng { |
| 426 | ZigList<AstNode *> items; | 433 | ZigList<AstNode *> items; |
| 427 | AstNode *var_symbol; | 434 | AstNode *var_symbol; |
| 428 | AstNode *expr; | 435 | AstNode *expr; |
| 436 | |||
| 437 | // populated by semantic analyzer | ||
| 438 | BlockContext *block_context; | ||
| 439 | VariableTableEntry *var; | ||
| 429 | }; | 440 | }; |
| 430 | 441 | ||
| 431 | struct AstNodeSwitchRange { | 442 | struct AstNodeSwitchRange { |
| ... | @@ -933,6 +944,7 @@ struct CodeGen { | ... | @@ -933,6 +944,7 @@ struct CodeGen { |
| 933 | 944 | ||
| 934 | OutType out_type; | 945 | OutType out_type; |
| 935 | FnTableEntry *cur_fn; | 946 | FnTableEntry *cur_fn; |
| 947 | // TODO remove this in favor of get_resolved_expr(expr_node)->context | ||
| 936 | BlockContext *cur_block_context; | 948 | BlockContext *cur_block_context; |
| 937 | ZigList<LLVMBasicBlockRef> break_block_stack; | 949 | ZigList<LLVMBasicBlockRef> break_block_stack; |
| 938 | ZigList<LLVMBasicBlockRef> continue_block_stack; | 950 | ZigList<LLVMBasicBlockRef> continue_block_stack; |
src/analyze.cpp+80-1| ... | @@ -1350,6 +1350,11 @@ static TypeTableEntry *analyze_enum_value_expr(CodeGen *g, ImportTableEntry *imp | ... | @@ -1350,6 +1350,11 @@ static TypeTableEntry *analyze_enum_value_expr(CodeGen *g, ImportTableEntry *imp |
| 1350 | buf_ptr(&enum_type->name), | 1350 | buf_ptr(&enum_type->name), |
| 1351 | buf_ptr(field_name), | 1351 | buf_ptr(field_name), |
| 1352 | buf_ptr(&type_enum_field->type_entry->name))); | 1352 | buf_ptr(&type_enum_field->type_entry->name))); |
| 1353 | } else { | ||
| 1354 | Expr *expr = get_resolved_expr(field_access_node); | ||
| 1355 | expr->const_val.ok = true; | ||
| 1356 | expr->const_val.data.x_enum.tag = type_enum_field->value; | ||
| 1357 | expr->const_val.data.x_enum.payload = nullptr; | ||
| 1353 | } | 1358 | } |
| 1354 | } else { | 1359 | } else { |
| 1355 | add_node_error(g, field_access_node, | 1360 | add_node_error(g, field_access_node, |
| ... | @@ -1945,6 +1950,25 @@ static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *im | ... | @@ -1945,6 +1950,25 @@ static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *im |
| 1945 | } | 1950 | } |
| 1946 | } else if (resolved_type->id == TypeTableEntryIdFloat) { | 1951 | } else if (resolved_type->id == TypeTableEntryIdFloat) { |
| 1947 | answer = eval_bool_bin_op_float(op1_val->data.x_float, bin_op_type, op2_val->data.x_float); | 1952 | answer = eval_bool_bin_op_float(op1_val->data.x_float, bin_op_type, op2_val->data.x_float); |
| 1953 | } else if (resolved_type->id == TypeTableEntryIdEnum) { | ||
| 1954 | ConstEnumValue *enum1 = &op1_val->data.x_enum; | ||
| 1955 | ConstEnumValue *enum2 = &op2_val->data.x_enum; | ||
| 1956 | bool are_equal = false; | ||
| 1957 | if (enum1->tag == enum2->tag) { | ||
| 1958 | TypeEnumField *enum_field = &op1_type->data.enumeration.fields[enum1->tag]; | ||
| 1959 | if (enum_field->type_entry->size_in_bits > 0) { | ||
| 1960 | zig_panic("TODO const expr analyze enum special value for equality"); | ||
| 1961 | } else { | ||
| 1962 | are_equal = true; | ||
| 1963 | } | ||
| 1964 | } | ||
| 1965 | if (bin_op_type == BinOpTypeCmpEq) { | ||
| 1966 | answer = are_equal; | ||
| 1967 | } else if (bin_op_type == BinOpTypeCmpNotEq) { | ||
| 1968 | answer = !are_equal; | ||
| 1969 | } else { | ||
| 1970 | zig_unreachable(); | ||
| 1971 | } | ||
| 1948 | } else { | 1972 | } else { |
| 1949 | zig_unreachable(); | 1973 | zig_unreachable(); |
| 1950 | } | 1974 | } |
| ... | @@ -3017,7 +3041,62 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo | ... | @@ -3017,7 +3041,62 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo |
| 3017 | static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | 3041 | static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 3018 | TypeTableEntry *expected_type, AstNode *node) | 3042 | TypeTableEntry *expected_type, AstNode *node) |
| 3019 | { | 3043 | { |
| 3020 | zig_panic("TODO analyze_switch_expr"); | 3044 | AstNode *expr_node = node->data.switch_expr.expr; |
| 3045 | TypeTableEntry *expr_type = analyze_expression(g, import, context, nullptr, expr_node); | ||
| 3046 | |||
| 3047 | if (expected_type == nullptr) { | ||
| 3048 | zig_panic("TODO resolve peer compatibility of switch prongs"); | ||
| 3049 | } | ||
| 3050 | |||
| 3051 | if (expr_type->id == TypeTableEntryIdInvalid) { | ||
| 3052 | return expr_type; | ||
| 3053 | } else if (expr_type->id == TypeTableEntryIdUnreachable) { | ||
| 3054 | add_node_error(g, first_executing_node(expr_node), | ||
| 3055 | buf_sprintf("switch on unreachable expression not allowed")); | ||
| 3056 | return g->builtin_types.entry_invalid; | ||
| 3057 | } else { | ||
| 3058 | AstNode *else_prong = nullptr; | ||
| 3059 | for (int prong_i = 0; prong_i < node->data.switch_expr.prongs.length; prong_i += 1) { | ||
| 3060 | AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i); | ||
| 3061 | |||
| 3062 | TypeTableEntry *var_type; | ||
| 3063 | if (prong_node->data.switch_prong.items.length == 0) { | ||
| 3064 | if (else_prong) { | ||
| 3065 | add_node_error(g, prong_node, buf_sprintf("multiple else prongs in switch expression")); | ||
| 3066 | } else { | ||
| 3067 | else_prong = prong_node; | ||
| 3068 | } | ||
| 3069 | var_type = expr_type; | ||
| 3070 | } else { | ||
| 3071 | for (int item_i = 0; item_i < prong_node->data.switch_prong.items.length; item_i += 1) { | ||
| 3072 | AstNode *item_node = prong_node->data.switch_prong.items.at(item_i); | ||
| 3073 | if (item_node->type == NodeTypeSwitchRange) { | ||
| 3074 | zig_panic("TODO range in switch statement"); | ||
| 3075 | } | ||
| 3076 | analyze_expression(g, import, context, expr_type, item_node); | ||
| 3077 | ConstExprValue *const_val = &get_resolved_expr(item_node)->const_val; | ||
| 3078 | if (!const_val->ok) { | ||
| 3079 | add_node_error(g, item_node, buf_sprintf("unable to resolve constant expression")); | ||
| 3080 | } | ||
| 3081 | } | ||
| 3082 | var_type = expr_type; | ||
| 3083 | } | ||
| 3084 | |||
| 3085 | BlockContext *child_context = new_block_context(node, context); | ||
| 3086 | prong_node->data.switch_prong.block_context = child_context; | ||
| 3087 | AstNode *var_node = prong_node->data.switch_prong.var_symbol; | ||
| 3088 | if (var_node) { | ||
| 3089 | assert(var_node->type == NodeTypeSymbol); | ||
| 3090 | Buf *var_name = &var_node->data.symbol_expr.symbol; | ||
| 3091 | prong_node->data.switch_prong.var = add_local_var(g, var_node, child_context, var_name, | ||
| 3092 | var_type, true); | ||
| 3093 | } | ||
| 3094 | |||
| 3095 | analyze_expression(g, import, child_context, expected_type, | ||
| 3096 | prong_node->data.switch_prong.expr); | ||
| 3097 | } | ||
| 3098 | } | ||
| 3099 | return expected_type; | ||
| 3021 | } | 3100 | } |
| 3022 | 3101 | ||
| 3023 | static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, | 3102 | static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
src/codegen.cpp+63-1| ... | @@ -1968,7 +1968,69 @@ static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) { | ... | @@ -1968,7 +1968,69 @@ static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) { |
| 1968 | static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) { | 1968 | static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) { |
| 1969 | assert(node->type == NodeTypeSwitchExpr); | 1969 | assert(node->type == NodeTypeSwitchExpr); |
| 1970 | 1970 | ||
| 1971 | zig_panic("TODO gen_switch_expr"); | 1971 | LLVMValueRef target_value = gen_expr(g, node->data.switch_expr.expr); |
| 1972 | |||
| 1973 | bool end_unreachable = (get_expr_type(node)->id == TypeTableEntryIdUnreachable); | ||
| 1974 | |||
| 1975 | LLVMBasicBlockRef end_block = end_unreachable ? | ||
| 1976 | nullptr : LLVMAppendBasicBlock(g->cur_fn->fn_value, "SwitchEnd"); | ||
| 1977 | LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SwitchElse"); | ||
| 1978 | int prong_count = node->data.switch_expr.prongs.length; | ||
| 1979 | |||
| 1980 | add_debug_source_node(g, node); | ||
| 1981 | LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, target_value, else_block, prong_count); | ||
| 1982 | |||
| 1983 | ZigList<LLVMValueRef> incoming_values = {0}; | ||
| 1984 | ZigList<LLVMBasicBlockRef> incoming_blocks = {0}; | ||
| 1985 | |||
| 1986 | AstNode *else_prong = nullptr; | ||
| 1987 | for (int prong_i = 0; prong_i < prong_count; prong_i += 1) { | ||
| 1988 | AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i); | ||
| 1989 | LLVMBasicBlockRef prong_block; | ||
| 1990 | if (prong_node->data.switch_prong.items.length == 0) { | ||
| 1991 | assert(!else_prong); | ||
| 1992 | else_prong = prong_node; | ||
| 1993 | prong_block = else_block; | ||
| 1994 | } else { | ||
| 1995 | prong_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SwitchProng"); | ||
| 1996 | for (int item_i = 0; item_i < prong_node->data.switch_prong.items.length; item_i += 1) { | ||
| 1997 | AstNode *item_node = prong_node->data.switch_prong.items.at(item_i); | ||
| 1998 | assert(item_node->type != NodeTypeSwitchRange); | ||
| 1999 | assert(get_resolved_expr(item_node)->const_val.ok); | ||
| 2000 | LLVMValueRef val = gen_expr(g, item_node); | ||
| 2001 | LLVMAddCase(switch_instr, val, prong_block); | ||
| 2002 | } | ||
| 2003 | } | ||
| 2004 | assert(!prong_node->data.switch_prong.var_symbol); | ||
| 2005 | LLVMPositionBuilderAtEnd(g->builder, prong_block); | ||
| 2006 | AstNode *prong_expr = prong_node->data.switch_prong.expr; | ||
| 2007 | LLVMValueRef prong_val = gen_expr(g, prong_expr); | ||
| 2008 | |||
| 2009 | if (get_expr_type(prong_expr)->id != TypeTableEntryIdUnreachable) { | ||
| 2010 | add_debug_source_node(g, prong_expr); | ||
| 2011 | LLVMBuildBr(g->builder, end_block); | ||
| 2012 | incoming_values.append(prong_val); | ||
| 2013 | incoming_blocks.append(prong_block); | ||
| 2014 | } | ||
| 2015 | } | ||
| 2016 | |||
| 2017 | if (!else_prong) { | ||
| 2018 | LLVMPositionBuilderAtEnd(g->builder, else_block); | ||
| 2019 | add_debug_source_node(g, node); | ||
| 2020 | LLVMBuildUnreachable(g->builder); | ||
| 2021 | } | ||
| 2022 | |||
| 2023 | if (end_unreachable) { | ||
| 2024 | return nullptr; | ||
| 2025 | } | ||
| 2026 | |||
| 2027 | LLVMPositionBuilderAtEnd(g->builder, end_block); | ||
| 2028 | |||
| 2029 | add_debug_source_node(g, node); | ||
| 2030 | LLVMValueRef phi = LLVMBuildPhi(g->builder, get_expr_type(node)->type_ref, ""); | ||
| 2031 | LLVMAddIncoming(phi, incoming_values.items, incoming_blocks.items, incoming_values.length); | ||
| 2032 | |||
| 2033 | return phi; | ||
| 1972 | } | 2034 | } |
| 1973 | 2035 | ||
| 1974 | static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { | 2036 | static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { |
src/parser.cpp+2-2| ... | @@ -2255,8 +2255,8 @@ static AstNode *ast_parse_switch_expr(ParseContext *pc, int *token_index, bool m | ... | @@ -2255,8 +2255,8 @@ static AstNode *ast_parse_switch_expr(ParseContext *pc, int *token_index, bool m |
| 2255 | break; | 2255 | break; |
| 2256 | } | 2256 | } |
| 2257 | 2257 | ||
| 2258 | Token *arrow_or_comma = &pc->tokens->at(*token_index); | 2258 | Token *arrow_or_colon = &pc->tokens->at(*token_index); |
| 2259 | if (arrow_or_comma->id == TokenIdComma) { | 2259 | if (arrow_or_colon->id == TokenIdColon) { |
| 2260 | *token_index += 1; | 2260 | *token_index += 1; |
| 2261 | ast_eat_token(pc, token_index, TokenIdLParen); | 2261 | ast_eat_token(pc, token_index, TokenIdLParen); |
| 2262 | prong_node->data.switch_prong.var_symbol = ast_parse_symbol(pc, token_index); | 2262 | prong_node->data.switch_prong.var_symbol = ast_parse_symbol(pc, token_index); |
test/run_tests.cpp+37| ... | @@ -1180,6 +1180,33 @@ fn fn2() u32 => {6} | ... | @@ -1180,6 +1180,33 @@ fn fn2() u32 => {6} |
| 1180 | fn fn3() u32 => {7} | 1180 | fn fn3() u32 => {7} |
| 1181 | fn fn4() u32 => {8} | 1181 | fn fn4() u32 => {8} |
| 1182 | )SOURCE", "5\n6\n7\n8\n"); | 1182 | )SOURCE", "5\n6\n7\n8\n"); |
| 1183 | |||
| 1184 | add_simple_case("switch statement", R"SOURCE( | ||
| 1185 | import "std.zig"; | ||
| 1186 | |||
| 1187 | enum Foo { | ||
| 1188 | A, | ||
| 1189 | B, | ||
| 1190 | C, | ||
| 1191 | D, | ||
| 1192 | } | ||
| 1193 | |||
| 1194 | pub fn main(args: [][]u8) i32 => { | ||
| 1195 | const foo = Foo.C; | ||
| 1196 | const val: i32 = switch (foo) { | ||
| 1197 | Foo.A => 1, | ||
| 1198 | Foo.B => 2, | ||
| 1199 | Foo.C => 3, | ||
| 1200 | Foo.D => 4, | ||
| 1201 | }; | ||
| 1202 | if (val != 3) { | ||
| 1203 | print_str("BAD\n"); | ||
| 1204 | } | ||
| 1205 | |||
| 1206 | print_str("OK\n"); | ||
| 1207 | return 0; | ||
| 1208 | } | ||
| 1209 | )SOURCE", "OK\n"); | ||
| 1183 | } | 1210 | } |
| 1184 | 1211 | ||
| 1185 | 1212 | ||
| ... | @@ -1511,6 +1538,16 @@ fn f(Foo: i32) => { | ... | @@ -1511,6 +1538,16 @@ fn f(Foo: i32) => { |
| 1511 | } | 1538 | } |
| 1512 | )SOURCE", 2, ".tmp_source.zig:5:6: error: variable shadows type 'Foo'", | 1539 | )SOURCE", 2, ".tmp_source.zig:5:6: error: variable shadows type 'Foo'", |
| 1513 | ".tmp_source.zig:6:5: error: variable shadows type 'Bar'"); | 1540 | ".tmp_source.zig:6:5: error: variable shadows type 'Bar'"); |
| 1541 | |||
| 1542 | add_compile_fail_case("multiple else prongs in a switch", R"SOURCE( | ||
| 1543 | fn f() => { | ||
| 1544 | const value: bool = switch (u32(111)) { | ||
| 1545 | 1234 => false, | ||
| 1546 | else => true, | ||
| 1547 | else => true, | ||
| 1548 | }; | ||
| 1549 | } | ||
| 1550 | )SOURCE", 1, ".tmp_source.zig:6:9: error: multiple else prongs in switch expression"); | ||
| 1514 | } | 1551 | } |
| 1515 | 1552 | ||
| 1516 | static void print_compiler_invocation(TestCase *test_case) { | 1553 | static void print_compiler_invocation(TestCase *test_case) { |