| author | |
| committer | |
| log | d0378057d1276d0bbbf0f0cac05e252bcc131692 |
| tree | 945fed7b6d4914c629cd201c41bd52ca57765401 |
| parent | d1b65c6f46ab0891db3c80db2eedbb434168aabe |
closes #235 files changed, 143 insertions(+), 15 deletions(-)
src/all_types.hpp+1| ... | @@ -691,6 +691,7 @@ struct AstNodeSymbolExpr { | ... | @@ -691,6 +691,7 @@ struct AstNodeSymbolExpr { |
| 691 | // set this to instead of analyzing the node, pretend it's a type entry and it's this one. | 691 | // set this to instead of analyzing the node, pretend it's a type entry and it's this one. |
| 692 | TypeTableEntry *override_type_entry; | 692 | TypeTableEntry *override_type_entry; |
| 693 | TypeEnumField *enum_field; | 693 | TypeEnumField *enum_field; |
| 694 | uint32_t err_value; | ||
| 694 | }; | 695 | }; |
| 695 | 696 | ||
| 696 | struct AstNodeBoolLiteral { | 697 | struct AstNodeBoolLiteral { |
src/analyze.cpp+61-4| ... | @@ -5116,8 +5116,11 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, | ... | @@ -5116,8 +5116,11 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, |
| 5116 | 5116 | ||
| 5117 | 5117 | ||
| 5118 | int *field_use_counts = nullptr; | 5118 | int *field_use_counts = nullptr; |
| 5119 | HashMap<int, AstNode *, int_hash, int_eq> err_use_nodes; | ||
| 5119 | if (expr_type->id == TypeTableEntryIdEnum) { | 5120 | if (expr_type->id == TypeTableEntryIdEnum) { |
| 5120 | field_use_counts = allocate<int>(expr_type->data.enumeration.field_count); | 5121 | field_use_counts = allocate<int>(expr_type->data.enumeration.field_count); |
| 5122 | } else if (expr_type->id == TypeTableEntryIdErrorUnion) { | ||
| 5123 | err_use_nodes.init(10); | ||
| 5121 | } | 5124 | } |
| 5122 | 5125 | ||
| 5123 | int *const_chosen_prong_index = &node->data.switch_expr.const_chosen_prong_index; | 5126 | int *const_chosen_prong_index = &node->data.switch_expr.const_chosen_prong_index; |
| ... | @@ -5186,8 +5189,54 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, | ... | @@ -5186,8 +5189,54 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, |
| 5186 | add_node_error(g, item_node, buf_sprintf("expected enum tag name")); | 5189 | add_node_error(g, item_node, buf_sprintf("expected enum tag name")); |
| 5187 | any_errors = true; | 5190 | any_errors = true; |
| 5188 | } | 5191 | } |
| 5192 | } else if (expr_type->id == TypeTableEntryIdErrorUnion) { | ||
| 5193 | if (item_node->type == NodeTypeSymbol) { | ||
| 5194 | Buf *err_name = &item_node->data.symbol_expr.symbol; | ||
| 5195 | bool is_ok_case = buf_eql_str(err_name, "Ok"); | ||
| 5196 | auto err_table_entry = is_ok_case ? nullptr: g->error_table.maybe_get(err_name); | ||
| 5197 | if (is_ok_case || err_table_entry) { | ||
| 5198 | uint32_t err_value = is_ok_case ? 0 : err_table_entry->value->value; | ||
| 5199 | item_node->data.symbol_expr.err_value = err_value; | ||
| 5200 | TypeTableEntry *this_var_type; | ||
| 5201 | if (is_ok_case) { | ||
| 5202 | this_var_type = expr_type->data.error.child_type; | ||
| 5203 | } else { | ||
| 5204 | this_var_type = g->builtin_types.entry_pure_error; | ||
| 5205 | } | ||
| 5206 | if (!var_type) { | ||
| 5207 | var_type = this_var_type; | ||
| 5208 | } | ||
| 5209 | if (this_var_type != var_type) { | ||
| 5210 | all_agree_on_var_type = false; | ||
| 5211 | } | ||
| 5212 | |||
| 5213 | // detect duplicate switch values | ||
| 5214 | auto existing_entry = err_use_nodes.maybe_get(err_value); | ||
| 5215 | if (existing_entry) { | ||
| 5216 | add_node_error(g, existing_entry->value, | ||
| 5217 | buf_sprintf("duplicate switch value: '%s'", buf_ptr(err_name))); | ||
| 5218 | any_errors = true; | ||
| 5219 | } else { | ||
| 5220 | err_use_nodes.put(err_value, item_node); | ||
| 5221 | } | ||
| 5222 | |||
| 5223 | if (!any_errors && expr_val->ok) { | ||
| 5224 | if (expr_val->data.x_err.err->value == err_value) { | ||
| 5225 | *const_chosen_prong_index = prong_i; | ||
| 5226 | } | ||
| 5227 | } | ||
| 5228 | } else { | ||
| 5229 | add_node_error(g, item_node, | ||
| 5230 | buf_sprintf("use of undeclared error value '%s'", buf_ptr(err_name))); | ||
| 5231 | any_errors = true; | ||
| 5232 | } | ||
| 5233 | } else { | ||
| 5234 | add_node_error(g, item_node, buf_sprintf("expected error value name")); | ||
| 5235 | any_errors = true; | ||
| 5236 | } | ||
| 5189 | } else { | 5237 | } else { |
| 5190 | if (!any_errors && expr_val->ok) { | 5238 | if (!any_errors && expr_val->ok) { |
| 5239 | // note: there is now a function in eval.cpp for doing const expr comparison | ||
| 5191 | zig_panic("TODO determine if const exprs are equal"); | 5240 | zig_panic("TODO determine if const exprs are equal"); |
| 5192 | } | 5241 | } |
| 5193 | TypeTableEntry *item_type = analyze_expression(g, import, context, expr_type, item_node); | 5242 | TypeTableEntry *item_type = analyze_expression(g, import, context, expr_type, item_node); |
| ... | @@ -5252,17 +5301,25 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, | ... | @@ -5252,17 +5301,25 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, |
| 5252 | return g->builtin_types.entry_invalid; | 5301 | return g->builtin_types.entry_invalid; |
| 5253 | } | 5302 | } |
| 5254 | 5303 | ||
| 5304 | TypeTableEntry *result_type = resolve_peer_type_compatibility(g, import, context, node, | ||
| 5305 | peer_nodes, peer_types, prong_count); | ||
| 5306 | |||
| 5255 | if (expr_val->ok) { | 5307 | if (expr_val->ok) { |
| 5256 | assert(*const_chosen_prong_index != -1); | 5308 | assert(*const_chosen_prong_index != -1); |
| 5257 | 5309 | ||
| 5258 | *const_val = get_resolved_expr(peer_nodes[*const_chosen_prong_index])->const_val; | 5310 | *const_val = get_resolved_expr(peer_nodes[*const_chosen_prong_index])->const_val; |
| 5259 | // the target expr depends on a compile var, | 5311 | // the target expr depends on a compile var because we have an error on unnecessary |
| 5260 | // so the entire if statement does too | 5312 | // switch statement, so the entire switch statement does too |
| 5261 | const_val->depends_on_compile_var = true; | 5313 | const_val->depends_on_compile_var = true; |
| 5262 | } | ||
| 5263 | 5314 | ||
| 5315 | if (!const_val->ok) { | ||
| 5316 | return add_error_if_type_is_num_lit(g, result_type, node); | ||
| 5317 | } | ||
| 5318 | } else { | ||
| 5319 | return add_error_if_type_is_num_lit(g, result_type, node); | ||
| 5320 | } | ||
| 5264 | 5321 | ||
| 5265 | return resolve_peer_type_compatibility(g, import, context, node, peer_nodes, peer_types, prong_count); | 5322 | return result_type; |
| 5266 | } | 5323 | } |
| 5267 | 5324 | ||
| 5268 | static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | 5325 | static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
src/codegen.cpp+40-11| ... | @@ -2627,12 +2627,6 @@ static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) { | ... | @@ -2627,12 +2627,6 @@ static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) { |
| 2627 | } | 2627 | } |
| 2628 | 2628 | ||
| 2629 | zig_unreachable(); | 2629 | zig_unreachable(); |
| 2630 | |||
| 2631 | /* TODO delete | ||
| 2632 | FnTableEntry *fn_entry = node->data.symbol_expr.fn_entry; | ||
| 2633 | assert(fn_entry); | ||
| 2634 | return fn_entry->fn_value; | ||
| 2635 | */ | ||
| 2636 | } | 2630 | } |
| 2637 | 2631 | ||
| 2638 | static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) { | 2632 | static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) { |
| ... | @@ -2653,6 +2647,10 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) { | ... | @@ -2653,6 +2647,10 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) { |
| 2653 | add_debug_source_node(g, node); | 2647 | add_debug_source_node(g, node); |
| 2654 | LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, target_value_handle, 0, ""); | 2648 | LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, target_value_handle, 0, ""); |
| 2655 | target_value = LLVMBuildLoad(g->builder, tag_field_ptr, ""); | 2649 | target_value = LLVMBuildLoad(g->builder, tag_field_ptr, ""); |
| 2650 | } else if (target_type->id == TypeTableEntryIdErrorUnion) { | ||
| 2651 | add_debug_source_node(g, node); | ||
| 2652 | LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, target_value_handle, 0, ""); | ||
| 2653 | target_value = LLVMBuildLoad(g->builder, tag_field_ptr, ""); | ||
| 2656 | } else { | 2654 | } else { |
| 2657 | zig_unreachable(); | 2655 | zig_unreachable(); |
| 2658 | } | 2656 | } |
| ... | @@ -2696,12 +2694,23 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) { | ... | @@ -2696,12 +2694,23 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) { |
| 2696 | 2694 | ||
| 2697 | assert(item_node->type != NodeTypeSwitchRange); | 2695 | assert(item_node->type != NodeTypeSwitchRange); |
| 2698 | LLVMValueRef val; | 2696 | LLVMValueRef val; |
| 2699 | if (target_type->id == TypeTableEntryIdEnum) { | 2697 | if (target_type->id == TypeTableEntryIdEnum || |
| 2698 | target_type->id == TypeTableEntryIdErrorUnion) | ||
| 2699 | { | ||
| 2700 | assert(item_node->type == NodeTypeSymbol); | 2700 | assert(item_node->type == NodeTypeSymbol); |
| 2701 | TypeEnumField *enum_field = item_node->data.symbol_expr.enum_field; | 2701 | TypeEnumField *enum_field = nullptr; |
| 2702 | assert(enum_field); | 2702 | uint32_t err_value = 0; |
| 2703 | val = LLVMConstInt(target_type->data.enumeration.tag_type->type_ref, | 2703 | if (target_type->id == TypeTableEntryIdEnum) { |
| 2704 | enum_field->value, false); | 2704 | enum_field = item_node->data.symbol_expr.enum_field; |
| 2705 | assert(enum_field); | ||
| 2706 | val = LLVMConstInt(target_type->data.enumeration.tag_type->type_ref, | ||
| 2707 | enum_field->value, false); | ||
| 2708 | } else if (target_type->id == TypeTableEntryIdErrorUnion) { | ||
| 2709 | err_value = item_node->data.symbol_expr.err_value; | ||
| 2710 | val = LLVMConstInt(g->err_tag_type->type_ref, err_value, false); | ||
| 2711 | } else { | ||
| 2712 | zig_unreachable(); | ||
| 2713 | } | ||
| 2705 | 2714 | ||
| 2706 | if (prong_var && type_has_bits(prong_var->type)) { | 2715 | if (prong_var && type_has_bits(prong_var->type)) { |
| 2707 | LLVMBasicBlockRef item_block; | 2716 | LLVMBasicBlockRef item_block; |
| ... | @@ -2721,6 +2730,7 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) { | ... | @@ -2721,6 +2730,7 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) { |
| 2721 | gen_assign_raw(g, var_node, BinOpTypeAssign, | 2730 | gen_assign_raw(g, var_node, BinOpTypeAssign, |
| 2722 | prong_var->value_ref, target_value, prong_var->type, target_type); | 2731 | prong_var->value_ref, target_value, prong_var->type, target_type); |
| 2723 | } else if (target_type->id == TypeTableEntryIdEnum) { | 2732 | } else if (target_type->id == TypeTableEntryIdEnum) { |
| 2733 | assert(enum_field); | ||
| 2724 | assert(type_has_bits(enum_field->type_entry)); | 2734 | assert(type_has_bits(enum_field->type_entry)); |
| 2725 | LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, target_value_handle, | 2735 | LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, target_value_handle, |
| 2726 | 1, ""); | 2736 | 1, ""); |
| ... | @@ -2731,6 +2741,25 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) { | ... | @@ -2731,6 +2741,25 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) { |
| 2731 | 2741 | ||
| 2732 | gen_assign_raw(g, var_node, BinOpTypeAssign, | 2742 | gen_assign_raw(g, var_node, BinOpTypeAssign, |
| 2733 | prong_var->value_ref, handle_val, prong_var->type, enum_field->type_entry); | 2743 | prong_var->value_ref, handle_val, prong_var->type, enum_field->type_entry); |
| 2744 | } else if (target_type->id == TypeTableEntryIdErrorUnion) { | ||
| 2745 | if (err_value == 0) { | ||
| 2746 | // variable is the payload | ||
| 2747 | LLVMValueRef err_payload_ptr = LLVMBuildStructGEP(g->builder, | ||
| 2748 | target_value_handle, 1, ""); | ||
| 2749 | LLVMValueRef handle_val = get_handle_value(g, var_node, | ||
| 2750 | err_payload_ptr, prong_var->type); | ||
| 2751 | gen_assign_raw(g, var_node, BinOpTypeAssign, | ||
| 2752 | prong_var->value_ref, handle_val, prong_var->type, prong_var->type); | ||
| 2753 | } else { | ||
| 2754 | // variable is the pure error value | ||
| 2755 | LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, | ||
| 2756 | target_value_handle, 0, ""); | ||
| 2757 | LLVMValueRef handle_val = LLVMBuildLoad(g->builder, err_tag_ptr, ""); | ||
| 2758 | gen_assign_raw(g, var_node, BinOpTypeAssign, | ||
| 2759 | prong_var->value_ref, handle_val, prong_var->type, g->err_tag_type); | ||
| 2760 | } | ||
| 2761 | } else { | ||
| 2762 | zig_unreachable(); | ||
| 2734 | } | 2763 | } |
| 2735 | if (make_item_blocks) { | 2764 | if (make_item_blocks) { |
| 2736 | LLVMBuildBr(g->builder, prong_block); | 2765 | LLVMBuildBr(g->builder, prong_block); |
test/run_tests.cpp+12| ... | @@ -1233,6 +1233,18 @@ fn bad_eql_2(a: EnumWithData, b: EnumWithData) -> bool { | ... | @@ -1233,6 +1233,18 @@ fn bad_eql_2(a: EnumWithData, b: EnumWithData) -> bool { |
| 1233 | )SOURCE", 2, | 1233 | )SOURCE", 2, |
| 1234 | ".tmp_source.zig:3:7: error: operator not allowed for type '[]u8'", | 1234 | ".tmp_source.zig:3:7: error: operator not allowed for type '[]u8'", |
| 1235 | ".tmp_source.zig:10:7: error: operator not allowed for type 'EnumWithData'"); | 1235 | ".tmp_source.zig:10:7: error: operator not allowed for type 'EnumWithData'"); |
| 1236 | |||
| 1237 | add_compile_fail_case("non-const switch number literal", R"SOURCE( | ||
| 1238 | fn foo() { | ||
| 1239 | const x = switch (bar()) { | ||
| 1240 | 1, 2 => 1, | ||
| 1241 | 3, 4 => 2, | ||
| 1242 | else => 3, | ||
| 1243 | }; | ||
| 1244 | } | ||
| 1245 | #static_eval_enable(false) | ||
| 1246 | fn bar() -> i32 { 2 } | ||
| 1247 | )SOURCE", 1, ".tmp_source.zig:3:15: error: unable to infer expression type"); | ||
| 1236 | } | 1248 | } |
| 1237 | 1249 | ||
| 1238 | ////////////////////////////////////////////////////////////////////////////// | 1250 | ////////////////////////////////////////////////////////////////////////////// |
test/self_hosted.zig+29| ... | @@ -1339,3 +1339,32 @@ fn character_literals() { | ... | @@ -1339,3 +1339,32 @@ fn character_literals() { |
| 1339 | assert('\'' == single_quote); | 1339 | assert('\'' == single_quote); |
| 1340 | } | 1340 | } |
| 1341 | const single_quote = '\''; | 1341 | const single_quote = '\''; |
| 1342 | |||
| 1343 | |||
| 1344 | #attribute("test") | ||
| 1345 | fn switch_with_multiple_expressions() { | ||
| 1346 | const x: i32 = switch (returns_five()) { | ||
| 1347 | 1, 2, 3 => 1, | ||
| 1348 | 4, 5, 6 => 2, | ||
| 1349 | else => 3, | ||
| 1350 | }; | ||
| 1351 | assert(x == 2); | ||
| 1352 | } | ||
| 1353 | #static_eval_enable(false) | ||
| 1354 | fn returns_five() -> i32 { 5 } | ||
| 1355 | |||
| 1356 | |||
| 1357 | #attribute("test") | ||
| 1358 | fn switch_on_error_union() { | ||
| 1359 | const x = switch (returns_ten()) { | ||
| 1360 | Ok => |val| val + 1, | ||
| 1361 | ItBroke, NoMem => 1, | ||
| 1362 | CrappedOut => 2, | ||
| 1363 | }; | ||
| 1364 | assert(x == 11); | ||
| 1365 | } | ||
| 1366 | error ItBroke; | ||
| 1367 | error NoMem; | ||
| 1368 | error CrappedOut; | ||
| 1369 | #static_eval_enable(false) | ||
| 1370 | fn returns_ten() -> %i32 { 10 } |