1//! This type exists only for legacy purposes, and will be removed in the future.
2//! It is a thin wrapper around a `Value` which also, redundantly, stores its `Type`.
3
4const std = @import("std");
5const Type = @import("Type.zig");
6const Value = @import("Value.zig");
7const Zcu = @import("Zcu.zig");
8const Sema = @import("Sema.zig");
9const InternPool = @import("InternPool.zig");
10const Allocator = std.mem.Allocator;
11const Target = std.Target;
12const Writer = std.Io.Writer;
13
14const max_aggregate_items = 100;
15const max_string_len = 256;
16
17pub const FormatContext = struct {
18 val: Value,
19 pt: Zcu.PerThread,
20 opt_sema: ?*Sema,
21 depth: u8,
22};
23
24pub fn formatSema(ctx: FormatContext, writer: *Writer) Writer.Error!void {
25 const sema = ctx.opt_sema.?;
26 return print(ctx.val, writer, ctx.depth, ctx.pt, sema) catch |err| switch (err) {
27 error.OutOfMemory => @panic("OOM"), // We're not allowed to return this from a format function
28 error.WriteFailed => |e| return e,
29 };
30}
31
32pub fn format(ctx: FormatContext, writer: *Writer) Writer.Error!void {
33 std.debug.assert(ctx.opt_sema == null);
34 return print(ctx.val, writer, ctx.depth, ctx.pt, null) catch |err| switch (err) {
35 error.OutOfMemory => @panic("OOM"), // We're not allowed to return this from a format function
36 error.WriteFailed => |e| return e,
37 };
38}
39
40pub fn print(
41 val: Value,
42 writer: *Writer,
43 level: u8,
44 pt: Zcu.PerThread,
45 opt_sema: ?*Sema,
46) (Writer.Error || Allocator.Error)!void {
47 const zcu = pt.zcu;
48 const ip = &zcu.intern_pool;
49 switch (ip.indexToKey(val.toIntern())) {
50 .int_type,
51 .ptr_type,
52 .array_type,
53 .vector_type,
54 .opt_type,
55 .anyframe_type,
56 .error_union_type,
57 .simple_type,
58 .struct_type,
59 .tuple_type,
60 .union_type,
61 .opaque_type,
62 .enum_type,
63 .spirv_type,
64 .func_type,
65 .error_set_type,
66 .inferred_error_set_type,
67 => try Type.print(val.toType(), writer, pt, null),
68 .undef => try writer.writeAll("undefined"),
69 .simple_value => |simple_value| switch (simple_value) {
70 .void => try writer.writeAll("{}"),
71
72 .null,
73 .true,
74 .false,
75 .@"unreachable",
76 => try writer.writeAll(@tagName(simple_value)),
77 },
78 .@"extern" => |e| try writer.print("(extern '{f}')", .{e.name.fmt(ip)}),
79 .func => |func| try writer.print("(function '{f}')", .{ip.getNav(func.owner_nav).name.fmt(ip)}),
80 .int => |int| switch (int.storage) {
81 inline .u64, .i64 => |x| try writer.print("{d}", .{x}),
82 .big_int => |x| try writer.print("{d}", .{x}),
83 },
84 .err => |err| try writer.print("error.{f}", .{
85 err.name.fmt(ip),
86 }),
87 .error_union => |error_union| switch (error_union.val) {
88 .err_name => |err_name| try writer.print("error.{f}", .{
89 err_name.fmt(ip),
90 }),
91 .payload => |payload| try print(Value.fromInterned(payload), writer, level, pt, opt_sema),
92 },
93 .enum_literal => |enum_literal| try writer.print(".{f}", .{
94 enum_literal.fmt(ip),
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
211fn 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
306fn 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
347const 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.
351pub 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}