| ... | @@ -8247,7 +8247,7 @@ fn zirSwitchCond( | ... | @@ -8247,7 +8247,7 @@ fn zirSwitchCond( |
| 8247 | ) CompileError!Air.Inst.Ref { | 8247 | ) CompileError!Air.Inst.Ref { |
| 8248 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | 8248 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 8249 | const src = inst_data.src(); | 8249 | const src = inst_data.src(); |
| 8250 | const operand_src = src; // TODO make this point at the switch operand | 8250 | const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node }; |
| 8251 | const operand_ptr = try sema.resolveInst(inst_data.operand); | 8251 | const operand_ptr = try sema.resolveInst(inst_data.operand); |
| 8252 | const operand = if (is_ref) | 8252 | const operand = if (is_ref) |
| 8253 | try sema.analyzeLoad(block, src, operand_ptr, operand_src) | 8253 | try sema.analyzeLoad(block, src, operand_ptr, operand_src) |
| ... | @@ -8345,12 +8345,19 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError | ... | @@ -8345,12 +8345,19 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError |
| 8345 | }, | 8345 | }, |
| 8346 | }; | 8346 | }; |
| 8347 | | 8347 | |
| | 8348 | const union_originally = blk: { |
| | 8349 | const zir_data = sema.code.instructions.items(.data); |
| | 8350 | const cond_index = Zir.refToIndex(extra.data.operand).?; |
| | 8351 | const raw_operand = sema.resolveInst(zir_data[cond_index].un_node.operand) catch unreachable; |
| | 8352 | break :blk sema.typeOf(raw_operand).zigTypeTag() == .Union; |
| | 8353 | }; |
| | 8354 | |
| 8348 | const operand_ty = sema.typeOf(operand); | 8355 | const operand_ty = sema.typeOf(operand); |
| 8349 | | 8356 | |
| 8350 | var else_error_ty: ?Type = null; | 8357 | var else_error_ty: ?Type = null; |
| 8351 | | 8358 | |
| 8352 | // Validate usage of '_' prongs. | 8359 | // Validate usage of '_' prongs. |
| 8353 | if (special_prong == .under and !operand_ty.isNonexhaustiveEnum()) { | 8360 | if (special_prong == .under and (!operand_ty.isNonexhaustiveEnum() or union_originally)) { |
| 8354 | const msg = msg: { | 8361 | const msg = msg: { |
| 8355 | const msg = try sema.errMsg( | 8362 | const msg = try sema.errMsg( |
| 8356 | block, | 8363 | block, |
| ... | @@ -8375,6 +8382,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError | ... | @@ -8375,6 +8382,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError |
| 8375 | | 8382 | |
| 8376 | // Validate for duplicate items, missing else prong, and invalid range. | 8383 | // Validate for duplicate items, missing else prong, and invalid range. |
| 8377 | switch (operand_ty.zigTypeTag()) { | 8384 | switch (operand_ty.zigTypeTag()) { |
| | 8385 | .Union => unreachable, // handled in zirSwitchCond |
| 8378 | .Enum => { | 8386 | .Enum => { |
| 8379 | var seen_fields = try gpa.alloc(?Module.SwitchProngSrc, operand_ty.enumFieldCount()); | 8387 | var seen_fields = try gpa.alloc(?Module.SwitchProngSrc, operand_ty.enumFieldCount()); |
| 8380 | defer gpa.free(seen_fields); | 8388 | defer gpa.free(seen_fields); |
| ... | @@ -8432,60 +8440,54 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError | ... | @@ -8432,60 +8440,54 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError |
| 8432 | } | 8440 | } |
| 8433 | const all_tags_handled = for (seen_fields) |seen_src| { | 8441 | const all_tags_handled = for (seen_fields) |seen_src| { |
| 8434 | if (seen_src == null) break false; | 8442 | if (seen_src == null) break false; |
| 8435 | } else !operand_ty.isNonexhaustiveEnum(); | 8443 | } else true; |
| 8436 | | | |
| 8437 | switch (special_prong) { | | |
| 8438 | .none => { | | |
| 8439 | if (!all_tags_handled) { | | |
| 8440 | const msg = msg: { | | |
| 8441 | const msg = try sema.errMsg( | | |
| 8442 | block, | | |
| 8443 | src, | | |
| 8444 | "switch must handle all possibilities", | | |
| 8445 | .{}, | | |
| 8446 | ); | | |
| 8447 | errdefer msg.destroy(sema.gpa); | | |
| 8448 | for (seen_fields) |seen_src, i| { | | |
| 8449 | if (seen_src != null) continue; | | |
| 8450 | | 8444 | |
| 8451 | const field_name = operand_ty.enumFieldName(i); | 8445 | if (special_prong == .@"else") { |
| 8452 | | 8446 | if (all_tags_handled and !operand_ty.isNonexhaustiveEnum()) return sema.fail( |
| 8453 | // TODO have this point to the tag decl instead of here | 8447 | block, |
| 8454 | try sema.errNote( | 8448 | special_prong_src, |
| 8455 | block, | 8449 | "unreachable else prong; all cases already handled", |
| 8456 | src, | 8450 | .{}, |
| 8457 | msg, | 8451 | ); |
| 8458 | "unhandled enumeration value: '{s}'", | 8452 | } else if (!all_tags_handled) { |
| 8459 | .{field_name}, | 8453 | const msg = msg: { |
| 8460 | ); | 8454 | const msg = try sema.errMsg( |
| 8461 | } | | |
| 8462 | try sema.mod.errNoteNonLazy( | | |
| 8463 | operand_ty.declSrcLoc(sema.mod), | | |
| 8464 | msg, | | |
| 8465 | "enum '{}' declared here", | | |
| 8466 | .{operand_ty.fmt(sema.mod)}, | | |
| 8467 | ); | | |
| 8468 | break :msg msg; | | |
| 8469 | }; | | |
| 8470 | return sema.failWithOwnedErrorMsg(block, msg); | | |
| 8471 | } | | |
| 8472 | }, | | |
| 8473 | .under => { | | |
| 8474 | if (all_tags_handled) return sema.fail( | | |
| 8475 | block, | 8455 | block, |
| 8476 | special_prong_src, | 8456 | src, |
| 8477 | "unreachable '_' prong; all cases already handled", | 8457 | "switch must handle all possibilities", |
| 8478 | .{}, | 8458 | .{}, |
| 8479 | ); | 8459 | ); |
| 8480 | }, | 8460 | errdefer msg.destroy(sema.gpa); |
| 8481 | .@"else" => { | 8461 | for (seen_fields) |seen_src, i| { |
| 8482 | if (all_tags_handled) return sema.fail( | 8462 | if (seen_src != null) continue; |
| 8483 | block, | 8463 | |
| 8484 | special_prong_src, | 8464 | const field_name = operand_ty.enumFieldName(i); |
| 8485 | "unreachable else prong; all cases already handled", | 8465 | |
| 8486 | .{}, | 8466 | const field_src = src; // TODO better source location |
| | 8467 | try sema.errNote( |
| | 8468 | block, |
| | 8469 | field_src, |
| | 8470 | msg, |
| | 8471 | "unhandled enumeration value: '{s}'", |
| | 8472 | .{field_name}, |
| | 8473 | ); |
| | 8474 | } |
| | 8475 | try sema.mod.errNoteNonLazy( |
| | 8476 | operand_ty.declSrcLoc(sema.mod), |
| | 8477 | msg, |
| | 8478 | "enum '{}' declared here", |
| | 8479 | .{operand_ty.fmt(sema.mod)}, |
| 8487 | ); | 8480 | ); |
| 8488 | }, | 8481 | break :msg msg; |
| | 8482 | }; |
| | 8483 | return sema.failWithOwnedErrorMsg(block, msg); |
| | 8484 | } else if (special_prong == .none and operand_ty.isNonexhaustiveEnum() and !union_originally) { |
| | 8485 | return sema.fail( |
| | 8486 | block, |
| | 8487 | src, |
| | 8488 | "switch on non-exhaustive enum must include 'else' or '_' prong", |
| | 8489 | .{}, |
| | 8490 | ); |
| 8489 | } | 8491 | } |
| 8490 | }, | 8492 | }, |
| 8491 | .ErrorSet => { | 8493 | .ErrorSet => { |
| ... | @@ -8625,7 +8627,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError | ... | @@ -8625,7 +8627,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError |
| 8625 | else_error_ty = try Type.Tag.error_set_merged.create(sema.arena, names); | 8627 | else_error_ty = try Type.Tag.error_set_merged.create(sema.arena, names); |
| 8626 | } | 8628 | } |
| 8627 | }, | 8629 | }, |
| 8628 | .Union => return sema.fail(block, src, "TODO validate switch .Union", .{}), | | |
| 8629 | .Int, .ComptimeInt => { | 8630 | .Int, .ComptimeInt => { |
| 8630 | var range_set = RangeSet.init(gpa, sema.mod); | 8631 | var range_set = RangeSet.init(gpa, sema.mod); |
| 8631 | defer range_set.deinit(); | 8632 | defer range_set.deinit(); |