| ... | @@ -3451,7 +3451,7 @@ static IrInstruction *ir_build_err_to_int(IrBuilder *irb, Scope *scope, AstNode | ... | @@ -3451,7 +3451,7 @@ static IrInstruction *ir_build_err_to_int(IrBuilder *irb, Scope *scope, AstNode |
| 3451 | | 3451 | |
| 3452 | static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope, AstNode *source_node, | 3452 | static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope, AstNode *source_node, |
| 3453 | IrInstruction *target_value, IrInstructionCheckSwitchProngsRange *ranges, size_t range_count, | 3453 | IrInstruction *target_value, IrInstructionCheckSwitchProngsRange *ranges, size_t range_count, |
| 3454 | bool have_else_prong) | 3454 | bool have_else_prong, bool have_underscore_prong) |
| 3455 | { | 3455 | { |
| 3456 | IrInstructionCheckSwitchProngs *instruction = ir_build_instruction<IrInstructionCheckSwitchProngs>( | 3456 | IrInstructionCheckSwitchProngs *instruction = ir_build_instruction<IrInstructionCheckSwitchProngs>( |
| 3457 | irb, scope, source_node); | 3457 | irb, scope, source_node); |
| ... | @@ -3459,6 +3459,7 @@ static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope, | ... | @@ -3459,6 +3459,7 @@ static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope, |
| 3459 | instruction->ranges = ranges; | 3459 | instruction->ranges = ranges; |
| 3460 | instruction->range_count = range_count; | 3460 | instruction->range_count = range_count; |
| 3461 | instruction->have_else_prong = have_else_prong; | 3461 | instruction->have_else_prong = have_else_prong; |
| | 3462 | instruction->have_underscore_prong = have_underscore_prong; |
| 3462 | | 3463 | |
| 3463 | ir_ref_instruction(target_value, irb->current_basic_block); | 3464 | ir_ref_instruction(target_value, irb->current_basic_block); |
| 3464 | for (size_t i = 0; i < range_count; i += 1) { | 3465 | for (size_t i = 0; i < range_count; i += 1) { |
| ... | @@ -8090,34 +8091,11 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * | ... | @@ -8090,34 +8091,11 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * |
| 8090 | Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); | 8091 | Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); |
| 8091 | Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope); | 8092 | Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope); |
| 8092 | AstNode *else_prong = nullptr; | 8093 | AstNode *else_prong = nullptr; |
| | 8094 | AstNode *underscore_prong = nullptr; |
| 8093 | for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) { | 8095 | for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) { |
| 8094 | AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i); | 8096 | AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i); |
| 8095 | size_t prong_item_count = prong_node->data.switch_prong.items.length; | 8097 | size_t prong_item_count = prong_node->data.switch_prong.items.length; |
| 8096 | if (prong_item_count == 0) { | 8098 | if (prong_node->data.switch_prong.any_items_are_range) { |
| 8097 | ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent); | | |
| 8098 | if (else_prong) { | | |
| 8099 | ErrorMsg *msg = add_node_error(irb->codegen, prong_node, | | |
| 8100 | buf_sprintf("multiple else prongs in switch expression")); | | |
| 8101 | add_error_note(irb->codegen, msg, else_prong, | | |
| 8102 | buf_sprintf("previous else prong is here")); | | |
| 8103 | return irb->codegen->invalid_instruction; | | |
| 8104 | } | | |
| 8105 | else_prong = prong_node; | | |
| 8106 | | | |
| 8107 | IrBasicBlock *prev_block = irb->current_basic_block; | | |
| 8108 | if (peer_parent->peers.length > 0) { | | |
| 8109 | peer_parent->peers.last()->next_bb = else_block; | | |
| 8110 | } | | |
| 8111 | peer_parent->peers.append(this_peer_result_loc); | | |
| 8112 | ir_set_cursor_at_end_and_append_block(irb, else_block); | | |
| 8113 | if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, | | |
| 8114 | is_comptime, var_is_comptime, target_value_ptr, nullptr, 0, &incoming_blocks, &incoming_values, | | |
| 8115 | &switch_else_var, LValNone, &this_peer_result_loc->base)) | | |
| 8116 | { | | |
| 8117 | return irb->codegen->invalid_instruction; | | |
| 8118 | } | | |
| 8119 | ir_set_cursor_at_end(irb, prev_block); | | |
| 8120 | } else if (prong_node->data.switch_prong.any_items_are_range) { | | |
| 8121 | ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent); | 8099 | ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent); |
| 8122 | | 8100 | |
| 8123 | IrInstruction *ok_bit = nullptr; | 8101 | IrInstruction *ok_bit = nullptr; |
| ... | @@ -8195,6 +8173,59 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * | ... | @@ -8195,6 +8173,59 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * |
| 8195 | } | 8173 | } |
| 8196 | | 8174 | |
| 8197 | ir_set_cursor_at_end_and_append_block(irb, range_block_no); | 8175 | ir_set_cursor_at_end_and_append_block(irb, range_block_no); |
| | 8176 | } else { |
| | 8177 | if (prong_item_count == 0) { |
| | 8178 | if (else_prong) { |
| | 8179 | ErrorMsg *msg = add_node_error(irb->codegen, prong_node, |
| | 8180 | buf_sprintf("multiple else prongs in switch expression")); |
| | 8181 | add_error_note(irb->codegen, msg, else_prong, |
| | 8182 | buf_sprintf("previous else prong is here")); |
| | 8183 | return irb->codegen->invalid_instruction; |
| | 8184 | } |
| | 8185 | else_prong = prong_node; |
| | 8186 | if (underscore_prong) { |
| | 8187 | ErrorMsg *msg = add_node_error(irb->codegen, prong_node, |
| | 8188 | buf_sprintf("else and '_' prong in switch expression")); |
| | 8189 | add_error_note(irb->codegen, msg, underscore_prong, |
| | 8190 | buf_sprintf("'_' prong is here")); |
| | 8191 | return irb->codegen->invalid_instruction; |
| | 8192 | } |
| | 8193 | } else if (prong_item_count == 1 && |
| | 8194 | prong_node->data.switch_prong.items.at(0)->type == NodeTypeSymbol && |
| | 8195 | buf_eql_str(prong_node->data.switch_prong.items.at(0)->data.symbol_expr.symbol, "_")) { |
| | 8196 | if (underscore_prong) { |
| | 8197 | ErrorMsg *msg = add_node_error(irb->codegen, prong_node, |
| | 8198 | buf_sprintf("multiple '_' prongs in switch expression")); |
| | 8199 | add_error_note(irb->codegen, msg, underscore_prong, |
| | 8200 | buf_sprintf("previous '_' prong is here")); |
| | 8201 | return irb->codegen->invalid_instruction; |
| | 8202 | } |
| | 8203 | underscore_prong = prong_node; |
| | 8204 | if (else_prong) { |
| | 8205 | ErrorMsg *msg = add_node_error(irb->codegen, prong_node, |
| | 8206 | buf_sprintf("else and '_' prong in switch expression")); |
| | 8207 | add_error_note(irb->codegen, msg, else_prong, |
| | 8208 | buf_sprintf("else prong is here")); |
| | 8209 | return irb->codegen->invalid_instruction; |
| | 8210 | } |
| | 8211 | } else { |
| | 8212 | continue; |
| | 8213 | } |
| | 8214 | ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent); |
| | 8215 | |
| | 8216 | IrBasicBlock *prev_block = irb->current_basic_block; |
| | 8217 | if (peer_parent->peers.length > 0) { |
| | 8218 | peer_parent->peers.last()->next_bb = else_block; |
| | 8219 | } |
| | 8220 | peer_parent->peers.append(this_peer_result_loc); |
| | 8221 | ir_set_cursor_at_end_and_append_block(irb, else_block); |
| | 8222 | if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, |
| | 8223 | is_comptime, var_is_comptime, target_value_ptr, nullptr, 0, &incoming_blocks, &incoming_values, |
| | 8224 | &switch_else_var, LValNone, &this_peer_result_loc->base)) |
| | 8225 | { |
| | 8226 | return irb->codegen->invalid_instruction; |
| | 8227 | } |
| | 8228 | ir_set_cursor_at_end(irb, prev_block); |
| 8198 | } | 8229 | } |
| 8199 | } | 8230 | } |
| 8200 | | 8231 | |
| ... | @@ -8206,6 +8237,8 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * | ... | @@ -8206,6 +8237,8 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * |
| 8206 | continue; | 8237 | continue; |
| 8207 | if (prong_node->data.switch_prong.any_items_are_range) | 8238 | if (prong_node->data.switch_prong.any_items_are_range) |
| 8208 | continue; | 8239 | continue; |
| | 8240 | if (underscore_prong == prong_node) |
| | 8241 | continue; |
| 8209 | | 8242 | |
| 8210 | ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent); | 8243 | ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent); |
| 8211 | | 8244 | |
| ... | @@ -8249,7 +8282,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * | ... | @@ -8249,7 +8282,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * |
| 8249 | } | 8282 | } |
| 8250 | | 8283 | |
| 8251 | IrInstruction *switch_prongs_void = ir_build_check_switch_prongs(irb, scope, node, target_value, | 8284 | IrInstruction *switch_prongs_void = ir_build_check_switch_prongs(irb, scope, node, target_value, |
| 8252 | check_ranges.items, check_ranges.length, else_prong != nullptr); | 8285 | check_ranges.items, check_ranges.length, else_prong != nullptr, underscore_prong != nullptr); |
| 8253 | | 8286 | |
| 8254 | IrInstruction *br_instruction; | 8287 | IrInstruction *br_instruction; |
| 8255 | if (cases.length == 0) { | 8288 | if (cases.length == 0) { |
| ... | @@ -8269,7 +8302,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * | ... | @@ -8269,7 +8302,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * |
| 8269 | peer_parent->peers.at(i)->base.source_instruction = peer_parent->base.source_instruction; | 8302 | peer_parent->peers.at(i)->base.source_instruction = peer_parent->base.source_instruction; |
| 8270 | } | 8303 | } |
| 8271 | | 8304 | |
| 8272 | if (!else_prong) { | 8305 | if (!else_prong && !underscore_prong) { |
| 8273 | if (peer_parent->peers.length != 0) { | 8306 | if (peer_parent->peers.length != 0) { |
| 8274 | peer_parent->peers.last()->next_bb = else_block; | 8307 | peer_parent->peers.last()->next_bb = else_block; |
| 8275 | } | 8308 | } |
| ... | @@ -12790,7 +12823,7 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour | ... | @@ -12790,7 +12823,7 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour |
| 12790 | return ira->codegen->invalid_instruction; | 12823 | return ira->codegen->invalid_instruction; |
| 12791 | | 12824 | |
| 12792 | TypeEnumField *field = find_enum_field_by_tag(wanted_type, &val->data.x_bigint); | 12825 | TypeEnumField *field = find_enum_field_by_tag(wanted_type, &val->data.x_bigint); |
| 12793 | if (field == nullptr && wanted_type->data.enumeration.layout != ContainerLayoutExtern) { | 12826 | if (field == nullptr && !wanted_type->data.enumeration.non_exhaustive) { |
| 12794 | Buf *val_buf = buf_alloc(); | 12827 | Buf *val_buf = buf_alloc(); |
| 12795 | bigint_append_buf(val_buf, &val->data.x_bigint, 10); | 12828 | bigint_append_buf(val_buf, &val->data.x_bigint, 10); |
| 12796 | ErrorMsg *msg = ir_add_error(ira, source_instr, | 12829 | ErrorMsg *msg = ir_add_error(ira, source_instr, |
| ... | @@ -26433,10 +26466,23 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, | ... | @@ -26433,10 +26466,23 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, |
| 26433 | bigint_incr(&field_index); | 26466 | bigint_incr(&field_index); |
| 26434 | } | 26467 | } |
| 26435 | } | 26468 | } |
| 26436 | if (!instruction->have_else_prong) { | 26469 | if (switch_type->data.enumeration.non_exhaustive && instruction->have_underscore_prong) { |
| 26437 | if (switch_type->data.enumeration.layout == ContainerLayoutExtern) { | 26470 | for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) { |
| | 26471 | TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i]; |
| | 26472 | if (buf_eql_str(enum_field->name, "_")) |
| | 26473 | continue; |
| | 26474 | |
| | 26475 | auto entry = field_prev_uses.maybe_get(enum_field->value); |
| | 26476 | if (!entry) { |
| | 26477 | ir_add_error(ira, &instruction->base, |
| | 26478 | buf_sprintf("enumeration value '%s.%s' not handled in switch", buf_ptr(&switch_type->name), |
| | 26479 | buf_ptr(enum_field->name))); |
| | 26480 | } |
| | 26481 | } |
| | 26482 | } else if (!instruction->have_else_prong) { |
| | 26483 | if (switch_type->data.enumeration.non_exhaustive) { |
| 26438 | ir_add_error(ira, &instruction->base, | 26484 | ir_add_error(ira, &instruction->base, |
| 26439 | buf_sprintf("switch on an extern enum must have an else prong")); | 26485 | buf_sprintf("switch on non-exhaustive enum must include `else` or `_` prong")); |
| 26440 | } | 26486 | } |
| 26441 | for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) { | 26487 | for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) { |
| 26442 | TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i]; | 26488 | TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i]; |