| author | |
| committer | |
| log | 12e4c648ccc68f5190dd5076088b3959ebeee65d |
| tree | e045157d9fba4648bb450510a4762f4937799308 |
| parent | 4155d2ae242d18c0bc280aa22f733bf7dcb6e1f0 |
| signature |
3 files changed, 196 insertions(+), 2 deletions(-)
src/RangeSet.zig created+76| ... | @@ -0,0 +1,76 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Order = std.math.Order; | ||
| 3 | const Value = @import("value.zig").Value; | ||
| 4 | const RangeSet = @This(); | ||
| 5 | |||
| 6 | ranges: std.ArrayList(Range), | ||
| 7 | |||
| 8 | pub const Range = struct { | ||
| 9 | start: Value, | ||
| 10 | end: Value, | ||
| 11 | src: usize, | ||
| 12 | }; | ||
| 13 | |||
| 14 | pub fn init(allocator: *std.mem.Allocator) RangeSet { | ||
| 15 | return .{ | ||
| 16 | .ranges = std.ArrayList(Range).init(allocator), | ||
| 17 | }; | ||
| 18 | } | ||
| 19 | |||
| 20 | pub fn deinit(self: *RangeSet) void { | ||
| 21 | self.ranges.deinit(); | ||
| 22 | } | ||
| 23 | |||
| 24 | pub fn add(self: *RangeSet, start: Value, end: Value, src: usize) !?usize { | ||
| 25 | for (self.ranges.items) |range| { | ||
| 26 | if ((start.compare(.gte, range.start) and start.compare(.lte, range.end)) or | ||
| 27 | (end.compare(.gte, range.start) and end.compare(.lte, range.end))) | ||
| 28 | { | ||
| 29 | // ranges overlap | ||
| 30 | return range.src; | ||
| 31 | } | ||
| 32 | } | ||
| 33 | try self.ranges.append(.{ | ||
| 34 | .start = start, | ||
| 35 | .end = end, | ||
| 36 | .src = src, | ||
| 37 | }); | ||
| 38 | return null; | ||
| 39 | } | ||
| 40 | |||
| 41 | /// Assumes a and b do not overlap | ||
| 42 | fn lessThan(_: void, a: Range, b: Range) bool { | ||
| 43 | return a.start.compare(.lt, b.start); | ||
| 44 | } | ||
| 45 | |||
| 46 | pub fn spans(self: *RangeSet, start: Value, end: Value) !bool { | ||
| 47 | std.sort.sort(Range, self.ranges.items, {}, lessThan); | ||
| 48 | |||
| 49 | if (!self.ranges.items[0].start.eql(start) or | ||
| 50 | !self.ranges.items[self.ranges.items.len - 1].end.eql(end)) | ||
| 51 | { | ||
| 52 | return false; | ||
| 53 | } | ||
| 54 | |||
| 55 | var space: Value.BigIntSpace = undefined; | ||
| 56 | |||
| 57 | var counter = try std.math.big.int.Managed.init(self.ranges.allocator); | ||
| 58 | defer counter.deinit(); | ||
| 59 | |||
| 60 | // look for gaps | ||
| 61 | for (self.ranges.items[1..]) |cur, i| { | ||
| 62 | // i starts counting from the second item. | ||
| 63 | const prev = self.ranges.items[i]; | ||
| 64 | |||
| 65 | // prev.end + 1 == cur.start | ||
| 66 | try counter.copy(prev.end.toBigInt(&space)); | ||
| 67 | try counter.addScalar(counter.toConst(), 1); | ||
| 68 | |||
| 69 | const cur_start_int = cur.start.toBigInt(&space); | ||
| 70 | if (!cur_start_int.eq(counter.toConst())) { | ||
| 71 | return false; | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | return true; | ||
| 76 | } | ||
src/type.zig+72| ... | @@ -2863,6 +2863,78 @@ pub const Type = extern union { | ... | @@ -2863,6 +2863,78 @@ pub const Type = extern union { |
| 2863 | }; | 2863 | }; |
| 2864 | } | 2864 | } |
| 2865 | 2865 | ||
| 2866 | /// Asserts that self.zigTypeTag() == .Int. | ||
| 2867 | pub fn minInt(self: Type, arena: *std.heap.ArenaAllocator, target: Target) !Value { | ||
| 2868 | assert(self.zigTypeTag() == .Int); | ||
| 2869 | const info = self.intInfo(target); | ||
| 2870 | |||
| 2871 | if (!info.signed) { | ||
| 2872 | return Value.initTag(.zero); | ||
| 2873 | } | ||
| 2874 | |||
| 2875 | if ((info.bits - 1) <= std.math.maxInt(u6)) { | ||
| 2876 | const payload = try arena.allocator.create(Value.Payload.Int_i64); | ||
| 2877 | payload.* = .{ | ||
| 2878 | .int = -(@as(i64, 1) << @truncate(u6, info.bits - 1)), | ||
| 2879 | }; | ||
| 2880 | return Value.initPayload(&payload.base); | ||
| 2881 | } | ||
| 2882 | |||
| 2883 | var res = try std.math.big.int.Managed.initSet(&arena.allocator, 1); | ||
| 2884 | try res.shiftLeft(res, info.bits - 1); | ||
| 2885 | res.negate(); | ||
| 2886 | |||
| 2887 | const res_const = res.toConst(); | ||
| 2888 | if (res_const.positive) { | ||
| 2889 | const val_payload = try arena.allocator.create(Value.Payload.IntBigPositive); | ||
| 2890 | val_payload.* = .{ .limbs = res_const.limbs }; | ||
| 2891 | return Value.initPayload(&val_payload.base); | ||
| 2892 | } else { | ||
| 2893 | const val_payload = try arena.allocator.create(Value.Payload.IntBigNegative); | ||
| 2894 | val_payload.* = .{ .limbs = res_const.limbs }; | ||
| 2895 | return Value.initPayload(&val_payload.base); | ||
| 2896 | } | ||
| 2897 | } | ||
| 2898 | |||
| 2899 | /// Asserts that self.zigTypeTag() == .Int. | ||
| 2900 | pub fn maxInt(self: Type, arena: *std.heap.ArenaAllocator, target: Target) !Value { | ||
| 2901 | assert(self.zigTypeTag() == .Int); | ||
| 2902 | const info = self.intInfo(target); | ||
| 2903 | |||
| 2904 | if (info.signed and (info.bits - 1) <= std.math.maxInt(u6)) { | ||
| 2905 | const payload = try arena.allocator.create(Value.Payload.Int_i64); | ||
| 2906 | payload.* = .{ | ||
| 2907 | .int = (@as(i64, 1) << @truncate(u6, info.bits - 1)) - 1, | ||
| 2908 | }; | ||
| 2909 | return Value.initPayload(&payload.base); | ||
| 2910 | } else if (!info.signed and info.bits <= std.math.maxInt(u6)) { | ||
| 2911 | const payload = try arena.allocator.create(Value.Payload.Int_u64); | ||
| 2912 | payload.* = .{ | ||
| 2913 | .int = (@as(u64, 1) << @truncate(u6, info.bits)) - 1, | ||
| 2914 | }; | ||
| 2915 | return Value.initPayload(&payload.base); | ||
| 2916 | } | ||
| 2917 | |||
| 2918 | var res = try std.math.big.int.Managed.initSet(&arena.allocator, 1); | ||
| 2919 | try res.shiftLeft(res, info.bits - @boolToInt(info.signed)); | ||
| 2920 | const one = std.math.big.int.Const{ | ||
| 2921 | .limbs = &[_]std.math.big.Limb{1}, | ||
| 2922 | .positive = true, | ||
| 2923 | }; | ||
| 2924 | res.sub(res.toConst(), one) catch unreachable; | ||
| 2925 | |||
| 2926 | const res_const = res.toConst(); | ||
| 2927 | if (res_const.positive) { | ||
| 2928 | const val_payload = try arena.allocator.create(Value.Payload.IntBigPositive); | ||
| 2929 | val_payload.* = .{ .limbs = res_const.limbs }; | ||
| 2930 | return Value.initPayload(&val_payload.base); | ||
| 2931 | } else { | ||
| 2932 | const val_payload = try arena.allocator.create(Value.Payload.IntBigNegative); | ||
| 2933 | val_payload.* = .{ .limbs = res_const.limbs }; | ||
| 2934 | return Value.initPayload(&val_payload.base); | ||
| 2935 | } | ||
| 2936 | } | ||
| 2937 | |||
| 2866 | /// This enum does not directly correspond to `std.builtin.TypeId` because | 2938 | /// This enum does not directly correspond to `std.builtin.TypeId` because |
| 2867 | /// it has extra enum tags in it, as a way of using less memory. For example, | 2939 | /// it has extra enum tags in it, as a way of using less memory. For example, |
| 2868 | /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types | 2940 | /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types |
src/zir_sema.zig+48-2| ... | @@ -1268,7 +1268,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In | ... | @@ -1268,7 +1268,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In |
| 1268 | .body = .{ .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items) }, | 1268 | .body = .{ .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items) }, |
| 1269 | }; | 1269 | }; |
| 1270 | } | 1270 | } |
| 1271 | 1271 | ||
| 1272 | return mod.addSwitchBr(parent_block, inst.base.src, target_ptr, cases); | 1272 | return mod.addSwitchBr(parent_block, inst.base.src, target_ptr, cases); |
| 1273 | } | 1273 | } |
| 1274 | 1274 | ||
| ... | @@ -1292,10 +1292,56 @@ fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Sw | ... | @@ -1292,10 +1292,56 @@ fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Sw |
| 1292 | 1292 | ||
| 1293 | // validate for duplicate items/missing else prong | 1293 | // validate for duplicate items/missing else prong |
| 1294 | switch (target.ty.zigTypeTag()) { | 1294 | switch (target.ty.zigTypeTag()) { |
| 1295 | .Int, .ComptimeInt => return mod.fail(scope, inst.base.src, "TODO validateSwitch .Int, .ComptimeInt", .{}), | ||
| 1296 | .Enum => return mod.fail(scope, inst.base.src, "TODO validateSwitch .Enum", .{}), | 1295 | .Enum => return mod.fail(scope, inst.base.src, "TODO validateSwitch .Enum", .{}), |
| 1297 | .ErrorSet => return mod.fail(scope, inst.base.src, "TODO validateSwitch .ErrorSet", .{}), | 1296 | .ErrorSet => return mod.fail(scope, inst.base.src, "TODO validateSwitch .ErrorSet", .{}), |
| 1298 | .Union => return mod.fail(scope, inst.base.src, "TODO validateSwitch .Union", .{}), | 1297 | .Union => return mod.fail(scope, inst.base.src, "TODO validateSwitch .Union", .{}), |
| 1298 | .Int, .ComptimeInt => { | ||
| 1299 | var range_set = @import("RangeSet.zig").init(mod.gpa); | ||
| 1300 | defer range_set.deinit(); | ||
| 1301 | |||
| 1302 | for (inst.positionals.items) |item| { | ||
| 1303 | const maybe_src = if (item.castTag(.switch_range)) |range| blk: { | ||
| 1304 | const start_resolved = try resolveInst(mod, scope, range.positionals.lhs); | ||
| 1305 | const start_casted = try mod.coerce(scope, target.ty, start_resolved); | ||
| 1306 | const end_resolved = try resolveInst(mod, scope, range.positionals.rhs); | ||
| 1307 | const end_casted = try mod.coerce(scope, target.ty, end_resolved); | ||
| 1308 | |||
| 1309 | break :blk try range_set.add( | ||
| 1310 | try mod.resolveConstValue(scope, start_casted), | ||
| 1311 | try mod.resolveConstValue(scope, end_casted), | ||
| 1312 | item.src, | ||
| 1313 | ); | ||
| 1314 | } else blk: { | ||
| 1315 | const resolved = try resolveInst(mod, scope, item); | ||
| 1316 | const casted = try mod.coerce(scope, target.ty, resolved); | ||
| 1317 | const value = try mod.resolveConstValue(scope, casted); | ||
| 1318 | break :blk try range_set.add(value, value, item.src); | ||
| 1319 | }; | ||
| 1320 | |||
| 1321 | if (maybe_src) |previous_src| { | ||
| 1322 | return mod.fail(scope, item.src, "duplicate switch value", .{}); | ||
| 1323 | // TODO notes "previous value is here" previous_src | ||
| 1324 | } | ||
| 1325 | } | ||
| 1326 | |||
| 1327 | if (target.ty.zigTypeTag() == .Int) { | ||
| 1328 | var arena = std.heap.ArenaAllocator.init(mod.gpa); | ||
| 1329 | defer arena.deinit(); | ||
| 1330 | |||
| 1331 | const start = try target.ty.minInt(&arena, mod.getTarget()); | ||
| 1332 | const end = try target.ty.maxInt(&arena, mod.getTarget()); | ||
| 1333 | if (try range_set.spans(start, end)) { | ||
| 1334 | if (inst.kw_args.special_prong == .@"else") { | ||
| 1335 | return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{}); | ||
| 1336 | } | ||
| 1337 | return; | ||
| 1338 | } | ||
| 1339 | } | ||
| 1340 | |||
| 1341 | if (inst.kw_args.special_prong != .@"else") { | ||
| 1342 | return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{}); | ||
| 1343 | } | ||
| 1344 | }, | ||
| 1299 | .Bool => { | 1345 | .Bool => { |
| 1300 | var true_count: u8 = 0; | 1346 | var true_count: u8 = 0; |
| 1301 | var false_count: u8 = 0; | 1347 | var false_count: u8 = 0; |