╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ print_value.zig ──────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
95 }), 96 .enum_tag => |enum_tag| { 97 const ty: Type = .fromInterned(enum_tag.ty); 98 const enum_obj = ip.loadEnumType(ty.toIntern()); 99 if (enum_obj.tagValueIndex(ip, enum_tag.int)) |tag_index| { 100 return writer.print(".{f}", .{enum_obj.field_names.get(ip)[tag_index].fmt(ip)}); 101 } 102 try writer.writeAll("@fromBackingInt("); 103 if (level == 0) return writer.writeAll("...)"); 104 try print(.fromInterned(enum_tag.int), writer, level - 1, pt, opt_sema); 105 try writer.writeAll(")"); 106 }, 107 .float => |float| switch (float.storage) { 108 inline else => |x| try writer.print("{d}", .{@as(f64, @floatCast(x))}), 109 }, 110 .slice => |slice| { 111 if (ip.isUndef(slice.ptr)) { 112 if (slice.len == .zero_usize) { 113 return writer.writeAll("&.{}"); 114 } 115 try print(.fromInterned(slice.ptr), writer, level, pt, opt_sema); 116 } else { 117 const print_contents = switch (ip.getBackingAddrTag(slice.ptr).?) { 118 .field, .arr_elem, .eu_payload, .opt_payload => unreachable, 119 .uav, .comptime_alloc, .comptime_field => true, 120 .nav, .int => false, 121 }; 122 if (print_contents) { 123 // TODO: eventually we want to load the slice as an array with `sema`, but that's 124 // currently not possible without e.g. triggering compile errors. 125 } 126 try printPtr(Value.fromInterned(slice.ptr), null, writer, level, pt, opt_sema); 127 } 128 try writer.writeAll("[0.."); 129 if (level == 0) { 130 try writer.writeAll("(...)"); 131 } else { 132 try print(Value.fromInterned(slice.len), writer, level - 1, pt, opt_sema); 133 } 134 try writer.writeAll("]"); 135 }, 136 .ptr => { 137 const print_contents = switch (ip.getBackingAddrTag(val.toIntern()).?) { 138 .field, .arr_elem, .eu_payload, .opt_payload => unreachable, 139 .uav, .comptime_alloc, .comptime_field => true, 140 .nav, .int => false, 141 }; 142 if (print_contents) { 143 // TODO: eventually we want to load the pointer with `sema`, but that's 144 // currently not possible without e.g. triggering compile errors. 145 } 146 try printPtr(val, .rvalue, writer, level, pt, opt_sema); 147 }, 148 .opt => |opt| switch (opt.val) { 149 .none => try writer.writeAll("null"), 150 else => |payload| try print(Value.fromInterned(payload), writer, level, pt, opt_sema), 151 }, 152 .aggregate => |aggregate| try printAggregate(val, aggregate, false, writer, level, pt, opt_sema), 153 .un => |un| { 154 if (level == 0) { 155 try writer.writeAll(".{ ... }"); 156 return; 157 } 158 if (un.tag == .none) { 159 const backing_ty = try val.typeOf(zcu).externUnionBackingType(pt); 160 try writer.print("@bitCast(@as({f}, ", .{backing_ty.fmt(pt)}); 161 try print(Value.fromInterned(un.val), writer, level - 1, pt, opt_sema); 162 try writer.writeAll("))"); 163 } else { 164 try writer.writeAll(".{ "); 165 try print(Value.fromInterned(un.tag), writer, level - 1, pt, opt_sema); 166 try writer.writeAll(" = "); 167 try print(Value.fromInterned(un.val), writer, level - 1, pt, opt_sema); 168 try writer.writeAll(" }"); 169 } 170 }, 171 .bitpack => |bitpack| { 172 if (level == 0) { 173 return writer.writeAll(".{ ... }"); 174 } 175 const ty: Type = .fromInterned(bitpack.ty); 176 switch (ty.zigTypeTag(zcu)) { 177 .@"struct" => { 178 if (ty.structFieldCount(zcu) == 0) { 179 return writer.writeAll(".{}"); 180 } 181 try writer.writeAll(".{ "); 182 const max_len = @min(ty.structFieldCount(zcu), max_aggregate_items); 183 for (0..max_len) |i| { 184 if (i != 0) try writer.writeAll(", "); 185 const field_name = ty.structFieldName(@intCast(i), zcu).unwrap().?; 186 try writer.print(".{f} = ", .{field_name.fmt(ip)}); 187 try print(try val.fieldValue(pt, i), writer, level - 1, pt, opt_sema); 188 } 189 try writer.writeAll(" }"); 190 return; 191 }, 192 .@"union" => switch (ty.backingIntMode(zcu)) { 193 .auto => { 194 try writer.print("@bitCast(@as({f}, ", .{ty.backingIntType(zcu).fmt(pt)}); 195 try print(.fromInterned(bitpack.backing_int_val), writer, level - 1, pt, opt_sema); 196 try writer.writeAll("))"); 197 }, 198 .explicit => { 199 try writer.writeAll("@fromBackingInt("); 200 try print(.fromInterned(bitpack.backing_int_val), writer, level - 1, pt, opt_sema); 201 try writer.writeAll(")"); 202 }, 203 }, 204 else => unreachable, 205 } 206 }, 207 .memoized_call => unreachable, 208 } 209 } 210 211 fn printAggregate( 212 val: Value, 213 aggregate: InternPool.Key.Aggregate, 214 is_ref: bool, 215 writer: *Writer, 216 level: u8, 217 pt: Zcu.PerThread, 218 opt_sema: ?*Sema, 219 ) (Writer.Error || Allocator.Error)!void { 220 if (level == 0) { 221 if (is_ref) try writer.writeByte('&'); 222 return writer.writeAll(".{ ... }"); 223 } 224 const zcu = pt.zcu; 225 const ip = &zcu.intern_pool; 226 const ty = Type.fromInterned(aggregate.ty); 227 switch (ty.zigTypeTag(zcu)) { 228 .@"struct" => if (!ty.isTuple(zcu)) { 229 if (is_ref) try writer.writeByte('&'); 230 if (ty.structFieldCount(zcu) == 0) { 231 return writer.writeAll(".{}"); 232 } 233 try writer.writeAll(".{ "); 234 const max_len = @min(ty.structFieldCount(zcu), max_aggregate_items); 235 for (0..max_len) |i| { 236 if (i != 0) try writer.writeAll(", "); 237 const field_name = ty.structFieldName(@intCast(i), zcu).unwrap().?; 238 try writer.print(".{f} = ", .{field_name.fmt(ip)}); 239 try print(try val.fieldValue(pt, i), writer, level - 1, pt, opt_sema); 240 } 241 try writer.writeAll(" }"); 242 return; 243 }, 244 .array => { 245 switch (aggregate.storage) { 246 .bytes => |bytes| string: { 247 const len = ty.arrayLenIncludingSentinel(zcu); 248 if (len == 0) break :string; 249 const slice = bytes.toSlice(if (bytes.at(len - 1, ip) == 0) len - 1 else len, ip); 250 try writer.print("\"{f}\"", .{std.zig.fmtString(slice)}); 251 if (!is_ref) try writer.writeAll(".*"); 252 return; 253 }, 254 .elems, .repeated_elem => {}, 255 } 256 switch (ty.arrayLen(zcu)) { 257 0 => { 258 if (is_ref) try writer.writeByte('&'); 259 return writer.writeAll(".{}"); 260 }, 261 1 => one_byte_str: { 262 // The repr isn't `bytes`, but we might still be able to print this as a string 263 if (ty.childType(zcu).toIntern() != .u8_type) break :one_byte_str; 264 const elem_val = Value.fromInterned(aggregate.storage.values()[0]); 265 if (elem_val.isUndef(zcu)) break :one_byte_str; 266 const byte = elem_val.toUnsignedInt(zcu); 267 try writer.print("\"{f}\"", .{std.zig.fmtString(&.{@intCast(byte)})}); 268 if (!is_ref) try writer.writeAll(".*"); 269 return; 270 }, 271 else => {}, 272 } 273 }, 274 .vector => if (ty.arrayLen(zcu) == 0) { 275 if (is_ref) try writer.writeByte('&'); 276 return writer.writeAll(".{}"); 277 }, 278 else => unreachable, 279 } 280 281 const len = ty.arrayLen(zcu); 282 283 if (is_ref) try writer.writeByte('&'); 284 switch (len) { 285 0 => try writer.writeAll(".{}"), 286 1 => { 287 try writer.writeAll(".{"); 288 try print(try val.fieldValue(pt, 0), writer, level - 1, pt, opt_sema); 289 try writer.writeByte('}'); 290 }, 291 else => { 292 try writer.writeAll(".{ "); 293 const max_len = @min(len, max_aggregate_items); 294 for (0..max_len) |i| { 295 if (i != 0) try writer.writeAll(", "); 296 try print(try val.fieldValue(pt, i), writer, level - 1, pt, opt_sema); 297 } 298 if (len > max_aggregate_items) { 299 try writer.writeAll(", ..."); 300 } 301 try writer.writeAll(" }"); 302 }, 303 } 304 } 305 306 fn printPtr( 307 ptr_val: Value, 308 /// Whether to print `derivation` as an lvalue or rvalue. If `null`, the more concise option is chosen. 309 want_kind: ?PrintPtrKind, 310 writer: *Writer, 311 level: u8, 312 pt: Zcu.PerThread, 313 opt_sema: ?*Sema, 314 ) (Writer.Error || Allocator.Error)!void { 315 const ptr = switch (pt.zcu.intern_pool.indexToKey(ptr_val.toIntern())) { 316 .undef => return writer.writeAll("undefined"), 317 .ptr => |ptr| ptr, 318 else => unreachable, 319 }; 320 321 if (ptr.base_addr == .uav) { 322 // If the value is an aggregate, we can potentially print it more nicely. 323 switch (pt.zcu.intern_pool.indexToKey(ptr.base_addr.uav.val)) { 324 .aggregate => |agg| return printAggregate( 325 Value.fromInterned(ptr.base_addr.uav.val), 326 agg, 327 true, 328 writer, 329 level, 330 pt, 331 opt_sema, 332 ), 333 else => {}, 334 } 335 } 336 337 var arena = std.heap.ArenaAllocator.init(pt.zcu.gpa); 338 defer arena.deinit(); 339 const derivation = try ptr_val.pointerDerivation(arena.allocator(), pt, opt_sema); 340 341 _ = try printPtrDerivation(derivation, writer, pt, want_kind, .{ .print_val = .{ 342 .level = level, 343 .opt_sema = opt_sema, 344 } }, 20); 345 } 346 347 const PrintPtrKind = enum { lvalue, rvalue }; 348 349 /// Print the pointer defined by `derivation` as an lvalue or an rvalue. 350 /// Returns the root derivation, which may be ignored. 351 pub fn printPtrDerivation( 352 derivation: Value.PointerDeriveStep, 353 writer: *Writer, 354 pt: Zcu.PerThread, 355 /// Whether to print `derivation` as an lvalue or rvalue. If `null`, the more concise option is chosen. 356 /// If this is `.rvalue`, the result may look like `&foo`, so it's not necessarily valid to treat it as 357 /// an atom -- e.g. `&foo.*` is distinct from `(&foo).*`. 358 want_kind: ?PrintPtrKind, 359 /// How to print the "root" of the derivation. `.print_val` will recursively print other values if needed, 360 /// e.g. for UAV refs. `.str` will just write the root as the given string. 361 root_strat: union(enum) { 362 str: []const u8, 363 print_val: struct { 364 level: u8, 365 opt_sema: ?*Sema, 366 }, 367 }, 368 /// The maximum recursion depth. We can never recurse infinitely here, but the depth can be arbitrary, 369 /// so at this depth we just write "..." to prevent stack overflow. 370 ptr_depth: u8, 371 ) !Value.PointerDeriveStep { 372 const zcu = pt.zcu; 373 const ip = &zcu.intern_pool; 374 375 if (ptr_depth == 0) { 376 const root_step = root: switch (derivation) { 377 inline .eu_payload_ptr, 378 .opt_payload_ptr, 379 .field_ptr, 380 .elem_ptr, 381 .offset_and_cast, 382 => |step| continue :root step.parent.*, 383 else => |step| break :root step, 384 }; 385 try writer.writeAll("..."); 386 return root_step; 387 } 388 389 const result_kind: PrintPtrKind = switch (derivation) { 390 .nav_ptr, 391 .uav_ptr, 392 .comptime_alloc_ptr, 393 .comptime_field_ptr, 394 .eu_payload_ptr, 395 .opt_payload_ptr, 396 .field_ptr, 397 .elem_ptr, 398 => .lvalue, 399 400 .offset_and_cast, 401 .int, 402 => .rvalue, 403 }; 404 405 const need_kind = want_kind orelse result_kind; 406 407 if (need_kind == .rvalue and result_kind == .lvalue) { 408 try writer.writeByte('&'); 409 } 410 411 // null if `derivation` is the root. 412 const root_or_null: ?Value.PointerDeriveStep = switch (derivation) { 413 .eu_payload_ptr => |info| root: { 414 try writer.writeByte('('); 415 const root = try printPtrDerivation(info.parent.*, writer, pt, .lvalue, root_strat, ptr_depth - 1); 416 try writer.writeAll(" catch unreachable)"); 417 break :root root; 418 }, 419 .opt_payload_ptr => |info| root: { 420 const root = try printPtrDerivation(info.parent.*, writer, pt, .lvalue, root_strat, ptr_depth - 1); 421 try writer.writeAll(".?"); 422 break :root root; 423 }, 424 .field_ptr => |field| root: { 425 const root = try printPtrDerivation(field.parent.*, writer, pt, null, root_strat, ptr_depth - 1); 426 const agg_ty = (try field.parent.ptrType(pt)).childType(zcu); 427 switch (agg_ty.zigTypeTag(zcu)) { 428 .@"struct" => if (agg_ty.structFieldName(field.field_idx, zcu).unwrap()) |field_name| { 429 try writer.print(".{f}", .{field_name.fmt(ip)}); 430 } else { 431 try writer.print("[{d}]", .{field.field_idx}); 432 }, 433 .@"union" => { 434 const tag_ty = agg_ty.unionTagTypeHypothetical(zcu); 435 const field_name = tag_ty.enumFieldName(field.field_idx, zcu); 436 try writer.print(".{f}", .{field_name.fmt(ip)}); 437 }, 438 .pointer => switch (field.field_idx) { 439 Value.slice_ptr_index => try writer.writeAll(".ptr"), 440 Value.slice_len_index => try writer.writeAll(".len"), 441 else => unreachable, 442 }, 443 else => unreachable, 444 } 445 break :root root; 446 }, 447 .elem_ptr => |elem| root: { 448 const root = try printPtrDerivation(elem.parent.*, writer, pt, null, root_strat, ptr_depth - 1); 449 try writer.print("[{d}]", .{elem.elem_idx}); 450 break :root root; 451 }, 452 453 .offset_and_cast => |oac| if (oac.byte_offset == 0) root: { 454 try writer.print("@as({f}, @ptrCast(", .{oac.new_ptr_ty.fmt(pt)}); 455 const root = try printPtrDerivation(oac.parent.*, writer, pt, .rvalue, root_strat, ptr_depth - 1); 456 try writer.writeAll("))"); 457 break :root root; 458 } else root: { 459 try writer.print("@as({f}, @ptrFromInt(@intFromPtr(", .{oac.new_ptr_ty.fmt(pt)}); 460 const root = try printPtrDerivation(oac.parent.*, writer, pt, .rvalue, root_strat, ptr_depth - 1); 461 try writer.print(") + {d}))", .{oac.byte_offset}); 462 break :root root; 463 }, 464 465 .int, .nav_ptr, .uav_ptr, .comptime_alloc_ptr, .comptime_field_ptr => null, 466 }; 467 468 if (root_or_null == null) switch (root_strat) { 469 .str => |x| try writer.writeAll(x), 470 .print_val => |x| switch (derivation) { 471 .int => |int| try writer.print("@as({f}, @ptrFromInt(0x{x}))", .{ int.ptr_ty.fmt(pt), int.addr }), 472 .nav_ptr => |nav| try writer.print("{f}", .{ip.getNav(nav).fqn.fmt(ip)}), 473 .uav_ptr => |uav| { 474 const ty = Value.fromInterned(uav.val).typeOf(zcu); 475 try writer.print("@as({f}, ", .{ty.fmt(pt)}); 476 if (x.level == 0) { 477 try writer.writeAll("..."); 478 } else { 479 try print(Value.fromInterned(uav.val), writer, x.level - 1, pt, x.opt_sema); 480 } 481 try writer.writeByte(')'); 482 }, 483 .comptime_alloc_ptr => |info| { 484 try writer.print("@as({f}, ", .{info.val.typeOf(zcu).fmt(pt)}); 485 if (x.level == 0) { 486 try writer.writeAll("..."); 487 } else { 488 try print(info.val, writer, x.level - 1, pt, x.opt_sema); 489 } 490 try writer.writeByte(')'); 491 }, 492 .comptime_field_ptr => |val| { 493 const ty = val.typeOf(zcu); 494 try writer.print("@as({f}, ", .{ty.fmt(pt)}); 495 if (x.level == 0) { 496 try writer.writeAll("..."); 497 } else { 498 try print(val, writer, x.level - 1, pt, x.opt_sema); 499 } 500 try writer.writeByte(')'); 501 }, 502 else => unreachable, 503 }, 504 }; 505 506 if (need_kind == .lvalue and result_kind == .rvalue) { 507 try writer.writeAll(".*"); 508 } 509 510 return root_or_null orelse derivation; 511 } 512