| ... | ... | @@ -10760,7 +10760,7 @@ fn finishSwitchBr( |
| 10760 | 10760 | .@"enum" => if (else_is_named_only or |
| 10761 | 10761 | !item_ty.isNonexhaustiveEnum(zcu) or tagged_union_originally) |
| 10762 | 10762 | { |
| 10763 | | try branch_hints.ensureUnusedCapacity(gpa, @intCast(validated_switch.seen_enum_fields.len)); |
| 10763 | try branch_hints.ensureUnusedCapacity(gpa, @intCast(validated_switch.seen.enum_fields.len)); |
| 10764 | 10764 | break :check_enumerable .{ undefined, undefined }; |
| 10765 | 10765 | }, |
| 10766 | 10766 | .error_set => if (!operand_ty.isAnyError(zcu)) { |
| ... | ... | @@ -10881,13 +10881,13 @@ fn finishSwitchBr( |
| 10881 | 10881 | try branch_hints.append(gpa, prong_hint); |
| 10882 | 10882 | |
| 10883 | 10883 | try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".field_names.len + |
| 10884 | | (validated_switch.seen_enum_fields.len + 1 - zir_switch.totalItemsLen()) + // +1 because totalItemsLen includes the _ |
| 10884 | (validated_switch.seen.enum_fields.len + 1 - zir_switch.totalItemsLen()) + // +1 because totalItemsLen includes the _ |
| 10885 | 10885 | case_block.instructions.items.len); |
| 10886 | 10886 | const extra_case = cases_extra.addManyAsArrayAssumeCapacity( |
| 10887 | 10887 | @typeInfo(Air.SwitchBr.Case).@"struct".field_names.len, |
| 10888 | 10888 | ); |
| 10889 | 10889 | var items_len: u32 = 0; |
| 10890 | | for (validated_switch.seen_enum_fields, 0..) |seen_field, field_i| { |
| 10890 | for (validated_switch.seen.enum_fields, 0..) |seen_field, field_i| { |
| 10891 | 10891 | if (seen_field != null) continue; |
| 10892 | 10892 | const item_val = try pt.enumValueFieldIndex(item_ty, @intCast(field_i)); |
| 10893 | 10893 | const item_ref: Air.Inst.Ref = .fromValue(item_val); |
| ... | ... | @@ -10920,7 +10920,7 @@ fn finishSwitchBr( |
| 10920 | 10920 | } |
| 10921 | 10921 | if (tagged_union_originally) { |
| 10922 | 10922 | const union_obj = zcu.typeToUnion(operand_ty).?; |
| 10923 | | for (validated_switch.seen_enum_fields, 0..) |seen_field, field_i| { |
| 10923 | for (validated_switch.seen.enum_fields, 0..) |seen_field, field_i| { |
| 10924 | 10924 | if (seen_field != null) continue; |
| 10925 | 10925 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_i]); |
| 10926 | 10926 | if (!field_ty.isNoReturn(zcu)) break :analyze_body true; |
| ... | ... | @@ -11004,17 +11004,21 @@ fn finishSwitchBr( |
| 11004 | 11004 | } |
| 11005 | 11005 | |
| 11006 | 11006 | const ValidatedSwitchBlock = struct { |
| 11007 | | seen_enum_fields: []const ?LazySrcLoc, |
| 11008 | | seen_errors: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, LazySrcLoc), |
| 11009 | | seen_ranges: std.MultiArrayList(RangeSet.Range).Slice, |
| 11010 | | true_src: ?LazySrcLoc, |
| 11011 | | false_src: ?LazySrcLoc, |
| 11012 | | void_src: ?LazySrcLoc, |
| 11013 | | |
| 11007 | seen: Seen, |
| 11014 | 11008 | case_vals: []const Air.Inst.Ref, |
| 11015 | 11009 | else_case: Zir.UnwrappedSwitchBlock.Case.Else, |
| 11016 | 11010 | else_err_ty: ?Type, |
| 11017 | 11011 | |
| 11012 | const Seen = struct { |
| 11013 | enum_fields: []?LazySrcLoc, |
| 11014 | errors: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, LazySrcLoc), |
| 11015 | sparse_values: std.AutoHashMapUnmanaged(InternPool.Index, LazySrcLoc), |
| 11016 | ranges: RangeSet, |
| 11017 | true_src: ?LazySrcLoc, |
| 11018 | false_src: ?LazySrcLoc, |
| 11019 | void_src: ?LazySrcLoc, |
| 11020 | }; |
| 11021 | |
| 11018 | 11022 | fn iterateUnhandledItems( |
| 11019 | 11023 | validated_switch: *const ValidatedSwitchBlock, |
| 11020 | 11024 | /// May be `undefined` if `item_ty` isn't an `error_set`. |
| ... | ... | @@ -11023,28 +11027,26 @@ const ValidatedSwitchBlock = struct { |
| 11023 | 11027 | min_int: Value, |
| 11024 | 11028 | ) UnhandledIterator { |
| 11025 | 11029 | return .{ |
| 11030 | .error_names = error_names, |
| 11031 | .seen = &validated_switch.seen, |
| 11032 | |
| 11026 | 11033 | .next_idx = 0, |
| 11027 | 11034 | .next_val = min_int, |
| 11028 | | .error_names = error_names, |
| 11029 | | .seen_enum_fields = validated_switch.seen_enum_fields, |
| 11030 | | .seen_errors = &validated_switch.seen_errors, |
| 11031 | | .seen_ranges = validated_switch.seen_ranges, |
| 11032 | | .seen_true = validated_switch.true_src != null, |
| 11033 | | .seen_false = validated_switch.false_src != null, |
| 11034 | | .seen_void = validated_switch.void_src != null, |
| 11035 | .handled_true = validated_switch.seen.true_src != null, |
| 11036 | .handled_false = validated_switch.seen.false_src != null, |
| 11037 | .handled_void = validated_switch.seen.void_src != null, |
| 11035 | 11038 | }; |
| 11036 | 11039 | } |
| 11037 | 11040 | |
| 11038 | 11041 | const UnhandledIterator = struct { |
| 11042 | error_names: InternPool.NullTerminatedString.Slice, |
| 11043 | seen: *const Seen, |
| 11044 | |
| 11039 | 11045 | next_idx: u32, |
| 11040 | 11046 | next_val: ?Value, |
| 11041 | | error_names: InternPool.NullTerminatedString.Slice, |
| 11042 | | seen_enum_fields: []const ?LazySrcLoc, |
| 11043 | | seen_errors: *const std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, LazySrcLoc), |
| 11044 | | seen_ranges: std.MultiArrayList(RangeSet.Range).Slice, |
| 11045 | | seen_true: bool, |
| 11046 | | seen_false: bool, |
| 11047 | | seen_void: bool, |
| 11047 | handled_true: bool, |
| 11048 | handled_false: bool, |
| 11049 | handled_void: bool, |
| 11048 | 11050 | |
| 11049 | 11051 | fn next(it: *UnhandledIterator, sema: *Sema, item_ty: Type) CompileError!?Value { |
| 11050 | 11052 | const pt = sema.pt; |
| ... | ... | @@ -11052,7 +11054,7 @@ const ValidatedSwitchBlock = struct { |
| 11052 | 11054 | const ip = &zcu.intern_pool; |
| 11053 | 11055 | switch (item_ty.zigTypeTag(zcu)) { |
| 11054 | 11056 | .@"enum" => { |
| 11055 | | for (it.seen_enum_fields[it.next_idx..], it.next_idx..) |seen_field, field_i| { |
| 11057 | for (it.seen.enum_fields[it.next_idx..], it.next_idx..) |seen_field, field_i| { |
| 11056 | 11058 | if (seen_field != null) continue; |
| 11057 | 11059 | it.next_idx = @intCast(field_i + 1); |
| 11058 | 11060 | return try pt.enumValueFieldIndex(item_ty, @intCast(field_i)); |
| ... | ... | @@ -11061,7 +11063,7 @@ const ValidatedSwitchBlock = struct { |
| 11061 | 11063 | }, |
| 11062 | 11064 | .error_set => { |
| 11063 | 11065 | for (it.error_names.get(ip)[it.next_idx..], it.next_idx..) |err_name, name_i| { |
| 11064 | | if (it.seen_errors.contains(err_name)) continue; |
| 11066 | if (it.seen.errors.contains(err_name)) continue; |
| 11065 | 11067 | it.next_idx = @intCast(name_i + 1); |
| 11066 | 11068 | return .fromInterned(try pt.intern(.{ .err = .{ |
| 11067 | 11069 | .ty = item_ty.toIntern(), |
| ... | ... | @@ -11077,14 +11079,14 @@ const ValidatedSwitchBlock = struct { |
| 11077 | 11079 | .@"union", .@"struct" => item_ty.backingIntType(zcu), |
| 11078 | 11080 | else => unreachable, |
| 11079 | 11081 | }; |
| 11080 | | while (it.next_idx < it.seen_ranges.len and |
| 11081 | | cur_val.eql(it.seen_ranges.items(.first)[it.next_idx], int_ty, zcu)) |
| 11082 | while (it.next_idx < it.seen.ranges.list.len and |
| 11083 | cur_val.eql(it.seen.ranges.list.items(.first)[it.next_idx], int_ty, zcu)) |
| 11082 | 11084 | { |
| 11083 | 11085 | defer it.next_idx += 1; |
| 11084 | 11086 | const incr = try arith.incrementDefinedInt( |
| 11085 | 11087 | sema, |
| 11086 | 11088 | int_ty, |
| 11087 | | it.seen_ranges.items(.last)[it.next_idx], |
| 11089 | it.seen.ranges.list.items(.last)[it.next_idx], |
| 11088 | 11090 | ); |
| 11089 | 11091 | if (incr.overflow) { |
| 11090 | 11092 | it.next_val = null; |
| ... | ... | @@ -11101,19 +11103,19 @@ const ValidatedSwitchBlock = struct { |
| 11101 | 11103 | }; |
| 11102 | 11104 | }, |
| 11103 | 11105 | .bool => { |
| 11104 | | if (!it.seen_true) { |
| 11105 | | it.seen_true = true; |
| 11106 | if (!it.handled_true) { |
| 11107 | it.handled_true = true; |
| 11106 | 11108 | return .true; |
| 11107 | 11109 | } |
| 11108 | | if (!it.seen_false) { |
| 11109 | | it.seen_false = true; |
| 11110 | if (!it.handled_false) { |
| 11111 | it.handled_false = true; |
| 11110 | 11112 | return .false; |
| 11111 | 11113 | } |
| 11112 | 11114 | return null; |
| 11113 | 11115 | }, |
| 11114 | 11116 | .void => { |
| 11115 | | if (!it.seen_void) { |
| 11116 | | it.seen_void = true; |
| 11117 | if (!it.handled_void) { |
| 11118 | it.handled_void = true; |
| 11117 | 11119 | return .void; |
| 11118 | 11120 | } |
| 11119 | 11121 | return null; |
| ... | ... | @@ -11186,14 +11188,8 @@ fn validateSwitchBlock( |
| 11186 | 11188 | operand_ty.assertHasLayout(zcu); |
| 11187 | 11189 | const union_obj = ip.loadUnionType(operand_ty.toIntern()); |
| 11188 | 11190 | switch (union_obj.tag_usage) { |
| 11189 | | .tagged => { |
| 11190 | | break :item_ty .fromInterned(union_obj.enum_tag_type); |
| 11191 | | }, |
| 11192 | | .none => { |
| 11193 | | if (union_obj.layout == .@"packed") { |
| 11194 | | break :item_ty operand_ty; |
| 11195 | | } |
| 11196 | | }, |
| 11191 | .tagged => break :item_ty .fromInterned(union_obj.enum_tag_type), |
| 11192 | .none => if (union_obj.layout == .@"packed") break :item_ty operand_ty, |
| 11197 | 11193 | .safety => {}, |
| 11198 | 11194 | } |
| 11199 | 11195 | return sema.failWithOwnedErrorMsg(block, msg: { |
| ... | ... | @@ -11208,27 +11204,47 @@ fn validateSwitchBlock( |
| 11208 | 11204 | |
| 11209 | 11205 | .@"struct" => { |
| 11210 | 11206 | operand_ty.assertHasLayout(zcu); |
| 11211 | | const layout = operand_ty.containerLayout(zcu); |
| 11212 | | if (layout == .@"packed") { |
| 11213 | | break :item_ty operand_ty; |
| 11214 | | } |
| 11207 | if (operand_ty.containerLayout(zcu) == .@"packed") break :item_ty operand_ty; |
| 11215 | 11208 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 11216 | | const msg = try sema.errMsg(operand_src, "switch on struct with {t} layout", .{layout}); |
| 11209 | const msg = try sema.errMsg(operand_src, "switch on non-packed struct", .{}); |
| 11217 | 11210 | errdefer msg.destroy(sema.gpa); |
| 11218 | | if (operand_ty.srcLocOrNull(zcu)) |struct_src| { |
| 11219 | | try sema.errNote(struct_src, msg, "consider 'packed struct' here", .{}); |
| 11220 | | } |
| 11211 | try sema.addDeclaredHereNote(msg, operand_ty); |
| 11221 | 11212 | break :msg msg; |
| 11222 | 11213 | }); |
| 11223 | 11214 | }, |
| 11224 | 11215 | |
| 11225 | | .pointer => { |
| 11226 | | if (!operand_ty.isSlice(zcu)) { |
| 11227 | | break :item_ty operand_ty; |
| 11228 | | } |
| 11229 | | }, |
| 11216 | .pointer => if (!operand_ty.isSlice(zcu)) break :item_ty operand_ty, |
| 11230 | 11217 | |
| 11231 | | else => {}, |
| 11218 | .optional => return sema.failWithOwnedErrorMsg(block, msg: { |
| 11219 | const msg = try sema.errMsg(operand_src, "switch on optional type '{f}'", .{ |
| 11220 | operand_ty.fmt(pt), |
| 11221 | }); |
| 11222 | errdefer msg.destroy(gpa); |
| 11223 | try sema.errNote(operand_src, msg, "consider using '.?', 'orelse', or 'if'", .{}); |
| 11224 | break :msg msg; |
| 11225 | }), |
| 11226 | |
| 11227 | .error_union => return sema.failWithOwnedErrorMsg(block, msg: { |
| 11228 | const msg = try sema.errMsg(operand_src, "switch on error union type '{f}'", .{ |
| 11229 | operand_ty.fmt(pt), |
| 11230 | }); |
| 11231 | errdefer msg.destroy(gpa); |
| 11232 | try sema.errNote(operand_src, msg, "consider using 'try', 'catch', or 'if'", .{}); |
| 11233 | break :msg msg; |
| 11234 | }), |
| 11235 | |
| 11236 | .noreturn, |
| 11237 | .float, |
| 11238 | .comptime_float, |
| 11239 | .array, |
| 11240 | .vector, |
| 11241 | .undefined, |
| 11242 | .null, |
| 11243 | .@"opaque", |
| 11244 | .frame, |
| 11245 | .@"anyframe", |
| 11246 | .spirv, |
| 11247 | => {}, |
| 11232 | 11248 | } |
| 11233 | 11249 | return sema.fail(block, operand_src, "switch on type '{f}'", .{operand_ty.fmt(pt)}); |
| 11234 | 11250 | }; |
| ... | ... | @@ -11253,13 +11269,15 @@ fn validateSwitchBlock( |
| 11253 | 11269 | var case_vals: std.ArrayList(Air.Inst.Ref) = try .initCapacity(arena, zir_switch.item_infos.len); |
| 11254 | 11270 | |
| 11255 | 11271 | // Duplicate checking variables later also used for `inline else`. |
| 11256 | | var seen_enum_fields: []?LazySrcLoc = &.{}; |
| 11257 | | var seen_errors: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, LazySrcLoc) = .empty; |
| 11258 | | var seen_sparse_values: std.AutoHashMapUnmanaged(InternPool.Index, LazySrcLoc) = .empty; |
| 11259 | | var range_set: RangeSet = .empty; |
| 11260 | | var true_src: ?LazySrcLoc = null; |
| 11261 | | var false_src: ?LazySrcLoc = null; |
| 11262 | | var void_src: ?LazySrcLoc = null; |
| 11272 | var seen: ValidatedSwitchBlock.Seen = .{ |
| 11273 | .enum_fields = &.{}, |
| 11274 | .errors = .empty, |
| 11275 | .sparse_values = .empty, |
| 11276 | .ranges = .empty, |
| 11277 | .true_src = null, |
| 11278 | .false_src = null, |
| 11279 | .void_src = null, |
| 11280 | }; |
| 11263 | 11281 | |
| 11264 | 11282 | var else_err_ty: ?Type = null; |
| 11265 | 11283 | |
| ... | ... | @@ -11267,20 +11285,20 @@ fn validateSwitchBlock( |
| 11267 | 11285 | |
| 11268 | 11286 | switch (item_ty.zigTypeTag(zcu)) { |
| 11269 | 11287 | .@"enum" => { |
| 11270 | | seen_enum_fields = try arena.alloc(?LazySrcLoc, item_ty.enumFieldCount(zcu)); |
| 11271 | | @memset(seen_enum_fields, null); |
| 11272 | | // `range_set` is used for non-exhaustive enum values that do not |
| 11288 | seen.enum_fields = try arena.alloc(?LazySrcLoc, item_ty.enumFieldCount(zcu)); |
| 11289 | @memset(seen.enum_fields, null); |
| 11290 | // `seen.ranges` is used for non-exhaustive enum values that do not |
| 11273 | 11291 | // correspond to any tags. Since this is rare, we only allocate on |
| 11274 | 11292 | // demand in `validateSwitchItem`. |
| 11275 | 11293 | }, |
| 11276 | 11294 | .error_set => { |
| 11277 | | try seen_errors.ensureUnusedCapacity(arena, zir_switch.totalItemsLen()); |
| 11295 | try seen.errors.ensureUnusedCapacity(arena, zir_switch.totalItemsLen()); |
| 11278 | 11296 | }, |
| 11279 | 11297 | .int, .comptime_int, .@"union", .@"struct" => { |
| 11280 | | try range_set.ensureUnusedCapacity(arena, zir_switch.totalItemsLen()); |
| 11298 | try seen.ranges.ensureUnusedCapacity(arena, zir_switch.totalItemsLen()); |
| 11281 | 11299 | }, |
| 11282 | 11300 | .enum_literal, .@"fn", .pointer, .type => { |
| 11283 | | try seen_sparse_values.ensureUnusedCapacity(arena, zir_switch.totalItemsLen()); |
| 11301 | try seen.sparse_values.ensureUnusedCapacity(arena, zir_switch.totalItemsLen()); |
| 11284 | 11302 | }, |
| 11285 | 11303 | .bool, .void => {}, |
| 11286 | 11304 | |
| ... | ... | @@ -11323,7 +11341,7 @@ fn validateSwitchBlock( |
| 11323 | 11341 | case_vals.appendAssumeCapacity(.none); |
| 11324 | 11342 | } else { |
| 11325 | 11343 | const item, extra_index = try sema.resolveSwitchItem(block, item_src, item_ty, item_info, extra_index, switch_inst, prong_info.is_comptime_unreach); |
| 11326 | | try sema.validateSwitchItemOrRange(block, item_src, item.val, null, item_ty, seen_enum_fields, &seen_errors, &seen_sparse_values, &range_set, &true_src, &false_src, &void_src); |
| 11344 | try sema.validateSwitchItemOrRange(block, item_src, item.val, null, item_ty, &seen); |
| 11327 | 11345 | case_vals.appendAssumeCapacity(item.ref); |
| 11328 | 11346 | } |
| 11329 | 11347 | } |
| ... | ... | @@ -11338,7 +11356,7 @@ fn validateSwitchBlock( |
| 11338 | 11356 | const last_src = block.src(.{ .switch_case_item_range_last = range_offset }); |
| 11339 | 11357 | const first_item, extra_index = try sema.resolveSwitchItem(block, first_src, item_ty, range_info[0], extra_index, switch_inst, prong_info.is_comptime_unreach); |
| 11340 | 11358 | const last_item, extra_index = try sema.resolveSwitchItem(block, last_src, item_ty, range_info[1], extra_index, switch_inst, prong_info.is_comptime_unreach); |
| 11341 | | try sema.validateSwitchItemOrRange(block, range_src, first_item.val, last_item.val, item_ty, seen_enum_fields, &seen_errors, &seen_sparse_values, &range_set, &true_src, &false_src, &void_src); |
| 11359 | try sema.validateSwitchItemOrRange(block, range_src, first_item.val, last_item.val, item_ty, &seen); |
| 11342 | 11360 | case_vals.appendSliceAssumeCapacity(&.{ first_item.ref, last_item.ref }); |
| 11343 | 11361 | } |
| 11344 | 11362 | } |
| ... | ... | @@ -11369,13 +11387,13 @@ fn validateSwitchBlock( |
| 11369 | 11387 | // Validate for missing special prongs. |
| 11370 | 11388 | switch (item_ty.zigTypeTag(zcu)) { |
| 11371 | 11389 | .@"enum" => { |
| 11372 | | const all_tags_handled = for (seen_enum_fields) |seen_src| { |
| 11390 | const all_tags_handled = for (seen.enum_fields) |seen_src| { |
| 11373 | 11391 | if (seen_src == null) break false; |
| 11374 | 11392 | } else true; |
| 11375 | 11393 | |
| 11376 | 11394 | if (has_else) { |
| 11377 | 11395 | if (all_tags_handled) { |
| 11378 | | if (item_ty.isNonexhaustiveEnum(zcu)) { |
| 11396 | if (operand_ty.isNonexhaustiveEnum(zcu)) { |
| 11379 | 11397 | if (has_under) return sema.fail( |
| 11380 | 11398 | block, |
| 11381 | 11399 | else_prong_src, |
| ... | ... | @@ -11397,7 +11415,7 @@ fn validateSwitchBlock( |
| 11397 | 11415 | .{}, |
| 11398 | 11416 | ); |
| 11399 | 11417 | errdefer msg.destroy(sema.gpa); |
| 11400 | | for (seen_enum_fields, 0..) |seen_src, i| { |
| 11418 | for (seen.enum_fields, 0..) |seen_src, i| { |
| 11401 | 11419 | if (seen_src != null) continue; |
| 11402 | 11420 | |
| 11403 | 11421 | const field_name = item_ty.enumFieldName(i, zcu); |
| ... | ... | @@ -11449,7 +11467,7 @@ fn validateSwitchBlock( |
| 11449 | 11467 | |
| 11450 | 11468 | var seen_errors_from_set: u32 = 0; |
| 11451 | 11469 | for (error_names.get(ip)) |error_name| { |
| 11452 | | if (seen_errors.contains(error_name)) { |
| 11470 | if (seen.errors.contains(error_name)) { |
| 11453 | 11471 | seen_errors_from_set += 1; |
| 11454 | 11472 | } else if (!has_else) { |
| 11455 | 11473 | const msg = maybe_msg orelse blk: { |
| ... | ... | @@ -11491,7 +11509,7 @@ fn validateSwitchBlock( |
| 11491 | 11509 | var names: InferredErrorSet.NameMap = .{}; |
| 11492 | 11510 | try names.ensureUnusedCapacity(sema.arena, error_names.len); |
| 11493 | 11511 | for (error_names.get(ip)) |error_name| { |
| 11494 | | if (seen_errors.contains(error_name)) continue; |
| 11512 | if (seen.errors.contains(error_name)) continue; |
| 11495 | 11513 | names.putAssumeCapacityNoClobber(error_name, {}); |
| 11496 | 11514 | } |
| 11497 | 11515 | // No need to keep the hash map metadata correct; here we |
| ... | ... | @@ -11509,7 +11527,7 @@ fn validateSwitchBlock( |
| 11509 | 11527 | }; |
| 11510 | 11528 | const min_int = try int_ty.minInt(pt, int_ty); |
| 11511 | 11529 | const max_int = try int_ty.maxInt(pt, int_ty); |
| 11512 | | if (try range_set.spans(arena, min_int, max_int, int_ty, zcu)) { |
| 11530 | if (try seen.ranges.spans(arena, min_int, max_int, int_ty, zcu)) { |
| 11513 | 11531 | if (has_else) { |
| 11514 | 11532 | return sema.fail( |
| 11515 | 11533 | block, |
| ... | ... | @@ -11542,8 +11560,8 @@ fn validateSwitchBlock( |
| 11542 | 11560 | }, |
| 11543 | 11561 | .bool, .void => |type_tag| { |
| 11544 | 11562 | const all_values_handled = switch (type_tag) { |
| 11545 | | .bool => true_src != null and false_src != null, |
| 11546 | | .void => void_src != null, |
| 11563 | .bool => seen.true_src != null and seen.false_src != null, |
| 11564 | .void => seen.void_src != null, |
| 11547 | 11565 | else => unreachable, |
| 11548 | 11566 | }; |
| 11549 | 11567 | if (has_else) { |
| ... | ... | @@ -11570,13 +11588,7 @@ fn validateSwitchBlock( |
| 11570 | 11588 | } |
| 11571 | 11589 | |
| 11572 | 11590 | return .{ |
| 11573 | | .seen_enum_fields = seen_enum_fields, |
| 11574 | | .seen_errors = seen_errors, |
| 11575 | | .seen_ranges = range_set.ranges.slice(), |
| 11576 | | .true_src = true_src, |
| 11577 | | .false_src = false_src, |
| 11578 | | .void_src = void_src, |
| 11579 | | |
| 11591 | .seen = seen, |
| 11580 | 11592 | .case_vals = case_vals.items, |
| 11581 | 11593 | .else_case = else_case, |
| 11582 | 11594 | .else_err_ty = else_err_ty, |
| ... | ... | @@ -11754,7 +11766,7 @@ fn resolveSwitchBlock( |
| 11754 | 11766 | .{ else_case.index, else_case.body, else_case.capture, else_case.has_tag_capture, else_case.is_inline }; |
| 11755 | 11767 | if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_ref); |
| 11756 | 11768 | if (tagged_union_originally) { |
| 11757 | | for (validated_switch.seen_enum_fields, 0..) |maybe_seen, field_i| { |
| 11769 | for (validated_switch.seen.enum_fields, 0..) |maybe_seen, field_i| { |
| 11758 | 11770 | if (maybe_seen != null) continue; |
| 11759 | 11771 | if (!operand_ty.unionFieldTypeByIndex(field_i, zcu).isNoReturn(zcu)) break; |
| 11760 | 11772 | } else { |
| ... | ... | @@ -12559,13 +12571,7 @@ fn validateSwitchItemOrRange( |
| 12559 | 12571 | item_val: Value, |
| 12560 | 12572 | opt_last_val: ?Value, |
| 12561 | 12573 | item_ty: Type, |
| 12562 | | seen_enum_fields: []?LazySrcLoc, |
| 12563 | | seen_errors: *std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, LazySrcLoc), |
| 12564 | | seen_sparse_values: *std.AutoHashMapUnmanaged(InternPool.Index, LazySrcLoc), |
| 12565 | | range_set: *RangeSet, |
| 12566 | | true_src: *?LazySrcLoc, |
| 12567 | | false_src: *?LazySrcLoc, |
| 12568 | | void_src: *?LazySrcLoc, |
| 12574 | seen: *ValidatedSwitchBlock.Seen, |
| 12569 | 12575 | ) CompileError!void { |
| 12570 | 12576 | const pt = sema.pt; |
| 12571 | 12577 | const zcu = pt.zcu; |
| ... | ... | @@ -12574,88 +12580,117 @@ fn validateSwitchItemOrRange( |
| 12574 | 12580 | .@"enum" => { |
| 12575 | 12581 | const int = ip.indexToKey(item_val.toIntern()).enum_tag.int; |
| 12576 | 12582 | if (ip.loadEnumType(item_ty.toIntern()).tagValueIndex(ip, int)) |field_index| { |
| 12577 | | const maybe_prev_src = seen_enum_fields[field_index]; |
| 12578 | | seen_enum_fields[field_index] = item_src; |
| 12583 | const maybe_prev_src = seen.enum_fields[field_index]; |
| 12584 | seen.enum_fields[field_index] = item_src; |
| 12579 | 12585 | break :maybe_prev_src maybe_prev_src; |
| 12580 | 12586 | } else { |
| 12581 | | break :maybe_prev_src try range_set.add(sema.arena, .{ |
| 12587 | try seen.ranges.ensureUnusedCapacity(sema.arena, 1); |
| 12588 | break :maybe_prev_src if (seen.ranges.addAssumeCapacity(.{ |
| 12582 | 12589 | .first = .fromInterned(int), |
| 12583 | 12590 | .last = .fromInterned(int), |
| 12584 | 12591 | .src = item_src, |
| 12585 | | }, .fromInterned(ip.typeOf(int)), zcu); |
| 12592 | }, .fromInterned(ip.typeOf(int)), zcu)) |prev| prev.src else null; |
| 12586 | 12593 | } |
| 12587 | 12594 | }, |
| 12588 | 12595 | .error_set => { |
| 12589 | 12596 | const error_name = ip.indexToKey(item_val.toIntern()).err.name; |
| 12590 | | break :maybe_prev_src if (seen_errors.fetchPutAssumeCapacity(error_name, item_src)) |prev| |
| 12597 | break :maybe_prev_src if (seen.errors.fetchPutAssumeCapacity(error_name, item_src)) |prev| |
| 12591 | 12598 | prev.value |
| 12592 | 12599 | else |
| 12593 | 12600 | null; |
| 12594 | 12601 | }, |
| 12595 | 12602 | .int, .comptime_int => { |
| 12596 | | if (opt_last_val) |last_val| { |
| 12597 | | const first_val = item_val; |
| 12603 | const first_val = item_val; |
| 12604 | const last_val: Value = last_val: { |
| 12605 | const last_val = opt_last_val orelse break :last_val item_val; |
| 12598 | 12606 | if (try first_val.compareAll(.gt, last_val, item_ty, pt)) { |
| 12599 | 12607 | return sema.fail(block, item_src, "range start value is greater than the end value", .{}); |
| 12600 | 12608 | } |
| 12601 | | break :maybe_prev_src range_set.addAssumeCapacity(.{ |
| 12602 | | .first = first_val, |
| 12603 | | .last = last_val, |
| 12604 | | .src = item_src, |
| 12605 | | }, item_ty, zcu); |
| 12606 | | } else { |
| 12607 | | break :maybe_prev_src range_set.addAssumeCapacity(.{ |
| 12608 | | .first = item_val, |
| 12609 | | .last = item_val, |
| 12610 | | .src = item_src, |
| 12611 | | }, item_ty, zcu); |
| 12609 | break :last_val last_val; |
| 12610 | }; |
| 12611 | if (seen.ranges.addAssumeCapacity(.{ |
| 12612 | .first = first_val, |
| 12613 | .last = last_val, |
| 12614 | .src = item_src, |
| 12615 | }, item_ty, zcu)) |prev_range| { |
| 12616 | const overlap_start = first_val.numberMax(prev_range.first, zcu); |
| 12617 | const overlap_end = last_val.numberMin(prev_range.last, zcu); |
| 12618 | if (overlap_start.eql(overlap_end, item_ty, zcu)) { |
| 12619 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 12620 | const msg = try sema.errMsg(item_src, "duplicate switch value '{f}'", .{ |
| 12621 | overlap_start.fmtValueSema(pt, sema), |
| 12622 | }); |
| 12623 | errdefer msg.destroy(sema.gpa); |
| 12624 | if (prev_range.first.eql(prev_range.last, item_ty, zcu)) { |
| 12625 | try sema.errNote(prev_range.src, msg, "previous value here", .{}); |
| 12626 | } else { |
| 12627 | try sema.errNote(prev_range.src, msg, "previous value inside range here", .{}); |
| 12628 | } |
| 12629 | break :msg msg; |
| 12630 | }); |
| 12631 | } |
| 12632 | assert(!prev_range.first.eql(prev_range.last, item_ty, zcu)); |
| 12633 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 12634 | const msg = try sema.errMsg(item_src, "duplicate switch ranges", .{}); |
| 12635 | errdefer msg.destroy(sema.gpa); |
| 12636 | if (first_val.eql(prev_range.first, item_ty, zcu) and |
| 12637 | last_val.eql(prev_range.last, item_ty, zcu)) |
| 12638 | { |
| 12639 | try sema.errNote(prev_range.src, msg, "previous range here", .{}); |
| 12640 | } else { |
| 12641 | try sema.errNote(prev_range.src, msg, "overlaps with previous range here", .{}); |
| 12642 | try sema.errNote(prev_range.src, msg, "ranges overlap from '{f}' to '{f}'", .{ |
| 12643 | overlap_start.fmtValueSema(pt, sema), overlap_end.fmtValueSema(pt, sema), |
| 12644 | }); |
| 12645 | } |
| 12646 | break :msg msg; |
| 12647 | }); |
| 12612 | 12648 | } |
| 12649 | break :maybe_prev_src null; |
| 12613 | 12650 | }, |
| 12614 | 12651 | .@"union", .@"struct" => { |
| 12615 | 12652 | const backing_int_val = ip.indexToKey(item_val.toIntern()).bitpack.backing_int_val; |
| 12616 | | break :maybe_prev_src range_set.addAssumeCapacity(.{ |
| 12653 | break :maybe_prev_src if (seen.ranges.addAssumeCapacity(.{ |
| 12617 | 12654 | .first = .fromInterned(backing_int_val), |
| 12618 | 12655 | .last = .fromInterned(backing_int_val), |
| 12619 | 12656 | .src = item_src, |
| 12620 | | }, item_ty.backingIntType(zcu), zcu); |
| 12657 | }, item_ty.backingIntType(zcu), zcu)) |prev| prev.src else null; |
| 12621 | 12658 | }, |
| 12622 | 12659 | .enum_literal, .@"fn", .pointer, .type => { |
| 12623 | | break :maybe_prev_src if (seen_sparse_values.fetchPutAssumeCapacity(item_val.toIntern(), item_src)) |prev| |
| 12660 | break :maybe_prev_src if (seen.sparse_values.fetchPutAssumeCapacity(item_val.toIntern(), item_src)) |prev| |
| 12624 | 12661 | prev.value |
| 12625 | 12662 | else |
| 12626 | 12663 | null; |
| 12627 | 12664 | }, |
| 12628 | 12665 | .bool => { |
| 12629 | 12666 | if (item_val.toBool()) { |
| 12630 | | if (true_src.*) |prev_src| break :maybe_prev_src prev_src; |
| 12631 | | true_src.* = item_src; |
| 12667 | if (seen.true_src) |prev_src| break :maybe_prev_src prev_src; |
| 12668 | seen.true_src = item_src; |
| 12632 | 12669 | } else { |
| 12633 | | if (false_src.*) |prev_src| break :maybe_prev_src prev_src; |
| 12634 | | false_src.* = item_src; |
| 12670 | if (seen.false_src) |prev_src| break :maybe_prev_src prev_src; |
| 12671 | seen.false_src = item_src; |
| 12635 | 12672 | } |
| 12636 | 12673 | break :maybe_prev_src null; |
| 12637 | 12674 | }, |
| 12638 | 12675 | .void => { |
| 12639 | | if (void_src.*) |prev_src| break :maybe_prev_src prev_src; |
| 12640 | | void_src.* = item_src; |
| 12676 | if (seen.void_src) |prev_src| break :maybe_prev_src prev_src; |
| 12677 | seen.void_src = item_src; |
| 12641 | 12678 | break :maybe_prev_src null; |
| 12642 | 12679 | }, |
| 12643 | 12680 | else => unreachable, // should have already checked for invalid types |
| 12644 | 12681 | }; |
| 12645 | 12682 | if (maybe_prev_src) |prev_src| { |
| 12646 | 12683 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 12647 | | const msg = try sema.errMsg( |
| 12648 | | item_src, |
| 12649 | | "duplicate switch value", |
| 12650 | | .{}, |
| 12651 | | ); |
| 12684 | const msg = try sema.errMsg(item_src, "duplicate switch value '{f}'", .{ |
| 12685 | item_val.fmtValueSema(pt, sema), |
| 12686 | }); |
| 12652 | 12687 | errdefer msg.destroy(sema.gpa); |
| 12653 | | try sema.errNote( |
| 12654 | | prev_src, |
| 12655 | | msg, |
| 12656 | | "previous value here", |
| 12657 | | .{}, |
| 12658 | | ); |
| 12688 | try sema.errNote(prev_src, msg, "previous value here", .{}); |
| 12689 | if (item_ty.zigTypeTag(zcu) == .type) { |
| 12690 | try sema.addDeclaredHereNote(msg, item_val.toType()); |
| 12691 | } else { |
| 12692 | try sema.addDeclaredHereNote(msg, item_ty); |
| 12693 | } |
| 12659 | 12694 | break :msg msg; |
| 12660 | 12695 | }); |
| 12661 | 12696 | } |