zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit 0182b7242e216ea6eabf465e32b7da60ad32ee2d (tree)
parent 2da62a710652571cefe81e958e86d9c9ef74c747
Author: mlugg <mlugg@mlugg.co.uk>
Date:   Wed, 20 Sep 2023 21:18:50 +0100

TypedValue: do not crash when failing to dereference pointer

All of the logic in `Value.elemValue` is quite questionable, but
printing an error is definitely better than crashing. Notably, this
should stop us from hitting crashes when dumping AIR.

Diffstat:
Msrc/TypedValue.zig | 13+++++++++----
Msrc/value.zig | 29+++++++++++++++++------------
2 files changed, 26 insertions(+), 16 deletions(-)

diff --git a/src/TypedValue.zig b/src/TypedValue.zig @@ -135,9 +135,10 @@ pub fn print( var i: u32 = 0; while (i < max_len) : (i += 1) { - const elem_val = payload.ptr.elemValue(mod, i) catch |err| switch (err) { + const maybe_elem_val = payload.ptr.maybeElemValue(mod, i) catch |err| switch (err) { error.OutOfMemory => @panic("OOM"), // TODO: eliminate this panic }; + const elem_val = maybe_elem_val orelse return writer.writeAll(".{ (reinterpreted data) }"); if (elem_val.isUndef(mod)) break :str; buf[i] = std.math.cast(u8, elem_val.toUnsignedInt(mod)) orelse break :str; } @@ -153,9 +154,10 @@ pub fn print( var i: u32 = 0; while (i < max_len) : (i += 1) { if (i != 0) try writer.writeAll(", "); - const elem_val = payload.ptr.elemValue(mod, i) catch |err| switch (err) { + const maybe_elem_val = payload.ptr.maybeElemValue(mod, i) catch |err| switch (err) { error.OutOfMemory => @panic("OOM"), // TODO: eliminate this panic }; + const elem_val = maybe_elem_val orelse return writer.writeAll("(reinterpreted data) }"); try print(.{ .ty = elem_ty, .val = elem_val, @@ -272,7 +274,8 @@ pub fn print( const max_len = @min(len, max_string_len); var buf: [max_string_len]u8 = undefined; for (buf[0..max_len], 0..) |*c, i| { - const elem = try val.elemValue(mod, i); + const maybe_elem = try val.maybeElemValue(mod, i); + const elem = maybe_elem orelse return writer.writeAll(".{ (reinterpreted data) }"); if (elem.isUndef(mod)) break :str; c.* = @as(u8, @intCast(elem.toUnsignedInt(mod))); } @@ -283,9 +286,11 @@ pub fn print( const max_len = @min(len, max_aggregate_items); for (0..max_len) |i| { if (i != 0) try writer.writeAll(", "); + const maybe_elem = try val.maybeElemValue(mod, i); + const elem = maybe_elem orelse return writer.writeAll("(reinterpreted data) }"); try print(.{ .ty = elem_ty, - .val = try val.elemValue(mod, i), + .val = elem, }, writer, level - 1, mod); } if (len > max_aggregate_items) { diff --git a/src/value.zig b/src/value.zig @@ -1520,33 +1520,38 @@ pub const Value = struct { /// Asserts the value is a single-item pointer to an array, or an array, /// or an unknown-length pointer, and returns the element value at the index. pub fn elemValue(val: Value, mod: *Module, index: usize) Allocator.Error!Value { + return (try val.maybeElemValue(mod, index)).?; + } + + /// Like `elemValue`, but returns `null` instead of asserting on failure. + pub fn maybeElemValue(val: Value, mod: *Module, index: usize) Allocator.Error!?Value { return switch (val.ip_index) { .none => switch (val.tag()) { .bytes => try mod.intValue(Type.u8, val.castTag(.bytes).?.data[index]), .repeated => val.castTag(.repeated).?.data, .aggregate => val.castTag(.aggregate).?.data[index], - .slice => val.castTag(.slice).?.data.ptr.elemValue(mod, index), - else => unreachable, + .slice => val.castTag(.slice).?.data.ptr.maybeElemValue(mod, index), + else => null, }, else => switch (mod.intern_pool.indexToKey(val.toIntern())) { .undef => |ty| (try mod.intern(.{ .undef = ty.toType().elemType2(mod).toIntern(), })).toValue(), .ptr => |ptr| switch (ptr.addr) { - .decl => |decl| mod.declPtr(decl).val.elemValue(mod, index), + .decl => |decl| mod.declPtr(decl).val.maybeElemValue(mod, index), .mut_decl => |mut_decl| (try mod.declPtr(mut_decl.decl).internValue(mod)) - .toValue().elemValue(mod, index), - .int, .eu_payload => unreachable, - .opt_payload => |base| base.toValue().elemValue(mod, index), - .comptime_field => |field_val| field_val.toValue().elemValue(mod, index), - .elem => |elem| elem.base.toValue().elemValue(mod, index + @as(usize, @intCast(elem.index))), + .toValue().maybeElemValue(mod, index), + .int, .eu_payload => null, + .opt_payload => |base| base.toValue().maybeElemValue(mod, index), + .comptime_field => |field_val| field_val.toValue().maybeElemValue(mod, index), + .elem => |elem| elem.base.toValue().maybeElemValue(mod, index + @as(usize, @intCast(elem.index))), .field => |field| if (field.base.toValue().pointerDecl(mod)) |decl_index| { const base_decl = mod.declPtr(decl_index); const field_val = try base_decl.val.fieldValue(mod, @as(usize, @intCast(field.index))); - return field_val.elemValue(mod, index); - } else unreachable, + return field_val.maybeElemValue(mod, index); + } else null, }, - .opt => |opt| opt.val.toValue().elemValue(mod, index), + .opt => |opt| opt.val.toValue().maybeElemValue(mod, index), .aggregate => |aggregate| { const len = mod.intern_pool.aggregateTypeLen(aggregate.ty); if (index < len) return switch (aggregate.storage) { @@ -1560,7 +1565,7 @@ pub const Value = struct { assert(index == len); return mod.intern_pool.indexToKey(aggregate.ty).array_type.sentinel.toValue(); }, - else => unreachable, + else => null, }, }; }