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