authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-09-20 21:18:50+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-09-21 14:48:40-07:00
log0182b7242e216ea6eabf465e32b7da60ad32ee2d
treed286e0ce25374525e8106ecbe93de25e325e1b47
parent2da62a710652571cefe81e958e86d9c9ef74c747

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.

2 files changed, 26 insertions(+), 16 deletions(-)

src/TypedValue.zig+9-4
...@@ -135,9 +135,10 @@ pub fn print(...@@ -135,9 +135,10 @@ pub fn print(
135135
136 var i: u32 = 0;136 var i: u32 = 0;
137 while (i < max_len) : (i += 1) {137 while (i < max_len) : (i += 1) {
138 const elem_val = payload.ptr.elemValue(mod, i) catch |err| switch (err) {138 const maybe_elem_val = payload.ptr.maybeElemValue(mod, i) catch |err| switch (err) {
139 error.OutOfMemory => @panic("OOM"), // TODO: eliminate this panic139 error.OutOfMemory => @panic("OOM"), // TODO: eliminate this panic
140 };140 };
141 const elem_val = maybe_elem_val orelse return writer.writeAll(".{ (reinterpreted data) }");
141 if (elem_val.isUndef(mod)) break :str;142 if (elem_val.isUndef(mod)) break :str;
142 buf[i] = std.math.cast(u8, elem_val.toUnsignedInt(mod)) orelse break :str;143 buf[i] = std.math.cast(u8, elem_val.toUnsignedInt(mod)) orelse break :str;
143 }144 }
...@@ -153,9 +154,10 @@ pub fn print(...@@ -153,9 +154,10 @@ pub fn print(
153 var i: u32 = 0;154 var i: u32 = 0;
154 while (i < max_len) : (i += 1) {155 while (i < max_len) : (i += 1) {
155 if (i != 0) try writer.writeAll(", ");156 if (i != 0) try writer.writeAll(", ");
156 const elem_val = payload.ptr.elemValue(mod, i) catch |err| switch (err) {157 const maybe_elem_val = payload.ptr.maybeElemValue(mod, i) catch |err| switch (err) {
157 error.OutOfMemory => @panic("OOM"), // TODO: eliminate this panic158 error.OutOfMemory => @panic("OOM"), // TODO: eliminate this panic
158 };159 };
160 const elem_val = maybe_elem_val orelse return writer.writeAll("(reinterpreted data) }");
159 try print(.{161 try print(.{
160 .ty = elem_ty,162 .ty = elem_ty,
161 .val = elem_val,163 .val = elem_val,
...@@ -272,7 +274,8 @@ pub fn print(...@@ -272,7 +274,8 @@ pub fn print(
272 const max_len = @min(len, max_string_len);274 const max_len = @min(len, max_string_len);
273 var buf: [max_string_len]u8 = undefined;275 var buf: [max_string_len]u8 = undefined;
274 for (buf[0..max_len], 0..) |*c, i| {276 for (buf[0..max_len], 0..) |*c, i| {
275 const elem = try val.elemValue(mod, i);277 const maybe_elem = try val.maybeElemValue(mod, i);
278 const elem = maybe_elem orelse return writer.writeAll(".{ (reinterpreted data) }");
276 if (elem.isUndef(mod)) break :str;279 if (elem.isUndef(mod)) break :str;
277 c.* = @as(u8, @intCast(elem.toUnsignedInt(mod)));280 c.* = @as(u8, @intCast(elem.toUnsignedInt(mod)));
278 }281 }
...@@ -283,9 +286,11 @@ pub fn print(...@@ -283,9 +286,11 @@ pub fn print(
283 const max_len = @min(len, max_aggregate_items);286 const max_len = @min(len, max_aggregate_items);
284 for (0..max_len) |i| {287 for (0..max_len) |i| {
285 if (i != 0) try writer.writeAll(", ");288 if (i != 0) try writer.writeAll(", ");
289 const maybe_elem = try val.maybeElemValue(mod, i);
290 const elem = maybe_elem orelse return writer.writeAll("(reinterpreted data) }");
286 try print(.{291 try print(.{
287 .ty = elem_ty,292 .ty = elem_ty,
288 .val = try val.elemValue(mod, i),293 .val = elem,
289 }, writer, level - 1, mod);294 }, writer, level - 1, mod);
290 }295 }
291 if (len > max_aggregate_items) {296 if (len > max_aggregate_items) {
src/value.zig+17-12
...@@ -1520,33 +1520,38 @@ pub const Value = struct {...@@ -1520,33 +1520,38 @@ pub const Value = struct {
1520 /// Asserts the value is a single-item pointer to an array, or an array,1520 /// Asserts the value is a single-item pointer to an array, or an array,
1521 /// or an unknown-length pointer, and returns the element value at the index.1521 /// or an unknown-length pointer, and returns the element value at the index.
1522 pub fn elemValue(val: Value, mod: *Module, index: usize) Allocator.Error!Value {1522 pub fn elemValue(val: Value, mod: *Module, index: usize) Allocator.Error!Value {
1523 return (try val.maybeElemValue(mod, index)).?;
1524 }
1525
1526 /// Like `elemValue`, but returns `null` instead of asserting on failure.
1527 pub fn maybeElemValue(val: Value, mod: *Module, index: usize) Allocator.Error!?Value {
1523 return switch (val.ip_index) {1528 return switch (val.ip_index) {
1524 .none => switch (val.tag()) {1529 .none => switch (val.tag()) {
1525 .bytes => try mod.intValue(Type.u8, val.castTag(.bytes).?.data[index]),1530 .bytes => try mod.intValue(Type.u8, val.castTag(.bytes).?.data[index]),
1526 .repeated => val.castTag(.repeated).?.data,1531 .repeated => val.castTag(.repeated).?.data,
1527 .aggregate => val.castTag(.aggregate).?.data[index],1532 .aggregate => val.castTag(.aggregate).?.data[index],
1528 .slice => val.castTag(.slice).?.data.ptr.elemValue(mod, index),1533 .slice => val.castTag(.slice).?.data.ptr.maybeElemValue(mod, index),
1529 else => unreachable,1534 else => null,
1530 },1535 },
1531 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {1536 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
1532 .undef => |ty| (try mod.intern(.{1537 .undef => |ty| (try mod.intern(.{
1533 .undef = ty.toType().elemType2(mod).toIntern(),1538 .undef = ty.toType().elemType2(mod).toIntern(),
1534 })).toValue(),1539 })).toValue(),
1535 .ptr => |ptr| switch (ptr.addr) {1540 .ptr => |ptr| switch (ptr.addr) {
1536 .decl => |decl| mod.declPtr(decl).val.elemValue(mod, index),1541 .decl => |decl| mod.declPtr(decl).val.maybeElemValue(mod, index),
1537 .mut_decl => |mut_decl| (try mod.declPtr(mut_decl.decl).internValue(mod))1542 .mut_decl => |mut_decl| (try mod.declPtr(mut_decl.decl).internValue(mod))
1538 .toValue().elemValue(mod, index),1543 .toValue().maybeElemValue(mod, index),
1539 .int, .eu_payload => unreachable,1544 .int, .eu_payload => null,
1540 .opt_payload => |base| base.toValue().elemValue(mod, index),1545 .opt_payload => |base| base.toValue().maybeElemValue(mod, index),
1541 .comptime_field => |field_val| field_val.toValue().elemValue(mod, index),1546 .comptime_field => |field_val| field_val.toValue().maybeElemValue(mod, index),
1542 .elem => |elem| elem.base.toValue().elemValue(mod, index + @as(usize, @intCast(elem.index))),1547 .elem => |elem| elem.base.toValue().maybeElemValue(mod, index + @as(usize, @intCast(elem.index))),
1543 .field => |field| if (field.base.toValue().pointerDecl(mod)) |decl_index| {1548 .field => |field| if (field.base.toValue().pointerDecl(mod)) |decl_index| {
1544 const base_decl = mod.declPtr(decl_index);1549 const base_decl = mod.declPtr(decl_index);
1545 const field_val = try base_decl.val.fieldValue(mod, @as(usize, @intCast(field.index)));1550 const field_val = try base_decl.val.fieldValue(mod, @as(usize, @intCast(field.index)));
1546 return field_val.elemValue(mod, index);1551 return field_val.maybeElemValue(mod, index);
1547 } else unreachable,1552 } else null,
1548 },1553 },
1549 .opt => |opt| opt.val.toValue().elemValue(mod, index),1554 .opt => |opt| opt.val.toValue().maybeElemValue(mod, index),
1550 .aggregate => |aggregate| {1555 .aggregate => |aggregate| {
1551 const len = mod.intern_pool.aggregateTypeLen(aggregate.ty);1556 const len = mod.intern_pool.aggregateTypeLen(aggregate.ty);
1552 if (index < len) return switch (aggregate.storage) {1557 if (index < len) return switch (aggregate.storage) {
...@@ -1560,7 +1565,7 @@ pub const Value = struct {...@@ -1560,7 +1565,7 @@ pub const Value = struct {
1560 assert(index == len);1565 assert(index == len);
1561 return mod.intern_pool.indexToKey(aggregate.ty).array_type.sentinel.toValue();1566 return mod.intern_pool.indexToKey(aggregate.ty).array_type.sentinel.toValue();
1562 },1567 },
1563 else => unreachable,1568 else => null,
1564 },1569 },
1565 };1570 };
1566 }1571 }