master
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.ComptimeBreak, error.ComptimeReturn => unreachable,
29 error.AnalysisFail => unreachable, // TODO: re-evaluate when we use `sema` more fully
30 error.Canceled => @panic("TODO"), // pls stop returning this error mlugg
31 else => |e| return e,
32 };
33}
34
35pub fn format(ctx: FormatContext, writer: *Writer) Writer.Error!void {
36 std.debug.assert(ctx.opt_sema == null);
37 return print(ctx.val, writer, ctx.depth, ctx.pt, null) catch |err| switch (err) {
38 error.OutOfMemory => @panic("OOM"), // We're not allowed to return this from a format function
39 error.ComptimeBreak, error.ComptimeReturn, error.AnalysisFail => unreachable,
40 error.Canceled => @panic("TODO"), // pls stop returning this error mlugg
41 else => |e| return e,
42 };
43}
44
45pub fn print(
46 val: Value,
47 writer: *Writer,
48 level: u8,
49 pt: Zcu.PerThread,
50 opt_sema: ?*Sema,
51) (Writer.Error || Zcu.CompileError)!void {
52 const zcu = pt.zcu;
53 const ip = &zcu.intern_pool;
54 switch (ip.indexToKey(val.toIntern())) {
55 .int_type,
56 .ptr_type,
57 .array_type,
58 .vector_type,
59 .opt_type,
60 .anyframe_type,
61 .error_union_type,
62 .simple_type,
63 .struct_type,
64 .tuple_type,
65 .union_type,
66 .opaque_type,
67 .enum_type,
68 .func_type,
69 .error_set_type,
70 .inferred_error_set_type,
71 => try Type.print(val.toType(), writer, pt, null),
72 .undef => try writer.writeAll("undefined"),
73 .simple_value => |simple_value| switch (simple_value) {
74 .void => try writer.writeAll("{}"),
75 .empty_tuple => try writer.writeAll(".{}"),
76 else => try writer.writeAll(@tagName(simple_value)),
77 },
78 .variable => try writer.writeAll("(variable)"),
79 .@"extern" => |e| try writer.print("(extern '{f}')", .{e.name.fmt(ip)}),
80 .func => |func| try writer.print("(function '{f}')", .{ip.getNav(func.owner_nav).name.fmt(ip)}),
81 .int => |int| switch (int.storage) {
82 inline .u64, .i64 => |x| try writer.print("{d}", .{x}),
83 .big_int => |x| try writer.print("{d}", .{x}),
84 .lazy_align => |ty| if (opt_sema != null) {
85 const a = try Type.fromInterned(ty).abiAlignmentSema(pt);
86 try writer.print("{d}", .{a.toByteUnits() orelse 0});
87 } else try writer.print("@alignOf({f})", .{Type.fromInterned(ty).fmt(pt)}),
88 .lazy_size => |ty| if (opt_sema != null) {
89 const s = try Type.fromInterned(ty).abiSizeSema(pt);
90 try writer.print("{d}", .{s});
91 } else try writer.print("@sizeOf({f})", .{Type.fromInterned(ty).fmt(pt)}),
92 },
93 .err => |err| try writer.print("error.{f}", .{
94 err.name.fmt(ip),
95 }),
96 .error_union => |error_union| switch (error_union.val) {
97 .err_name => |err_name| try writer.print("error.{f}", .{
98 err_name.fmt(ip),
99 }),
100 .payload => |payload| try print(Value.fromInterned(payload), writer, level, pt, opt_sema),
101 },
102 .enum_literal => |enum_literal| try writer.print(".{f}", .{
103 enum_literal.fmt(ip),
104 }),
105 .enum_tag => |enum_tag| {
106 const enum_type = ip.loadEnumType(val.typeOf(zcu).toIntern());
107 if (enum_type.tagValueIndex(ip, val.toIntern())) |tag_index| {
108 return writer.print(".{f}", .{enum_type.names.get(ip)[tag_index].fmt(ip)});
109 }
110 if (level == 0) {
111 return writer.writeAll("@enumFromInt(...)");
112 }
113 try writer.writeAll("@enumFromInt(");
114 try print(Value.fromInterned(enum_tag.int), writer, level - 1, pt, opt_sema);
115 try writer.writeAll(")");
116 },
117 .empty_enum_value => try writer.writeAll("(empty enum value)"),
118 .float => |float| switch (float.storage) {
119 inline else => |x| try writer.print("{d}", .{@as(f64, @floatCast(x))}),
120 },
121 .slice => |slice| {
122 if (ip.isUndef(slice.ptr)) {
123 if (slice.len == .zero_usize) {
124 return writer.writeAll("&.{}");
125 }
126 try print(.fromInterned(slice.ptr), writer, level - 1, pt, opt_sema);
127 } else {
128 const print_contents = switch (ip.getBackingAddrTag(slice.ptr).?) {
129 .field, .arr_elem, .eu_payload, .opt_payload => unreachable,
130 .uav, .comptime_alloc, .comptime_field => true,
131 .nav, .int => false,
132 };
133 if (print_contents) {
134 // TODO: eventually we want to load the slice as an array with `sema`, but that's
135 // currently not possible without e.g. triggering compile errors.
136 }
137 try printPtr(Value.fromInterned(slice.ptr), null, writer, level, pt, opt_sema);
138 }
139 try writer.writeAll("[0..");
140 if (level == 0) {
141 try writer.writeAll("(...)");
142 } else {
143 try print(Value.fromInterned(slice.len), writer, level - 1, pt, opt_sema);
144 }
145 try writer.writeAll("]");
146 },
147 .ptr => {
148 const print_contents = switch (ip.getBackingAddrTag(val.toIntern()).?) {
149 .field, .arr_elem, .eu_payload, .opt_payload => unreachable,
150 .uav, .comptime_alloc, .comptime_field => true,
151 .nav, .int => false,
152 };
153 if (print_contents) {
154 // TODO: eventually we want to load the pointer with `sema`, but that's
155 // currently not possible without e.g. triggering compile errors.
156 }
157 try printPtr(val, .rvalue, writer, level, pt, opt_sema);
158 },
159 .opt => |opt| switch (opt.val) {
160 .none => try writer.writeAll("null"),
161 else => |payload| try print(Value.fromInterned(payload), writer, level, pt, opt_sema),
162 },
163 .aggregate => |aggregate| try printAggregate(val, aggregate, false, writer, level, pt, opt_sema),
164 .un => |un| {
165 if (level == 0) {
166 try writer.writeAll(".{ ... }");
167 return;
168 }
169 if (un.tag == .none) {
170 const backing_ty = try val.typeOf(zcu).unionBackingType(pt);
171 try writer.print("@bitCast(@as({f}, ", .{backing_ty.fmt(pt)});
172 try print(Value.fromInterned(un.val), writer, level - 1, pt, opt_sema);
173 try writer.writeAll("))");
174 } else {
175 try writer.writeAll(".{ ");
176 try print(Value.fromInterned(un.tag), writer, level - 1, pt, opt_sema);
177 try writer.writeAll(" = ");
178 try print(Value.fromInterned(un.val), writer, level - 1, pt, opt_sema);
179 try writer.writeAll(" }");
180 }
181 },
182 .memoized_call => unreachable,
183 }
184}
185
186fn printAggregate(
187 val: Value,
188 aggregate: InternPool.Key.Aggregate,
189 is_ref: bool,
190 writer: *Writer,
191 level: u8,
192 pt: Zcu.PerThread,
193 opt_sema: ?*Sema,
194) (Writer.Error || Zcu.CompileError)!void {
195 if (level == 0) {
196 if (is_ref) try writer.writeByte('&');
197 return writer.writeAll(".{ ... }");
198 }
199 const zcu = pt.zcu;
200 const ip = &zcu.intern_pool;
201 const ty = Type.fromInterned(aggregate.ty);
202 switch (ty.zigTypeTag(zcu)) {
203 .@"struct" => if (!ty.isTuple(zcu)) {
204 if (is_ref) try writer.writeByte('&');
205 if (ty.structFieldCount(zcu) == 0) {
206 return writer.writeAll(".{}");
207 }
208 try writer.writeAll(".{ ");
209 const max_len = @min(ty.structFieldCount(zcu), max_aggregate_items);
210 for (0..max_len) |i| {
211 if (i != 0) try writer.writeAll(", ");
212 const field_name = ty.structFieldName(@intCast(i), zcu).unwrap().?;
213 try writer.print(".{f} = ", .{field_name.fmt(ip)});
214 try print(try val.fieldValue(pt, i), writer, level - 1, pt, opt_sema);
215 }
216 try writer.writeAll(" }");
217 return;
218 },
219 .array => {
220 switch (aggregate.storage) {
221 .bytes => |bytes| string: {
222 const len = ty.arrayLenIncludingSentinel(zcu);
223 if (len == 0) break :string;
224 const slice = bytes.toSlice(if (bytes.at(len - 1, ip) == 0) len - 1 else len, ip);
225 try writer.print("\"{f}\"", .{std.zig.fmtString(slice)});
226 if (!is_ref) try writer.writeAll(".*");
227 return;
228 },
229 .elems, .repeated_elem => {},
230 }
231 switch (ty.arrayLen(zcu)) {
232 0 => {
233 if (is_ref) try writer.writeByte('&');
234 return writer.writeAll(".{}");
235 },
236 1 => one_byte_str: {
237 // The repr isn't `bytes`, but we might still be able to print this as a string
238 if (ty.childType(zcu).toIntern() != .u8_type) break :one_byte_str;
239 const elem_val = Value.fromInterned(aggregate.storage.values()[0]);
240 if (elem_val.isUndef(zcu)) break :one_byte_str;
241 const byte = elem_val.toUnsignedInt(zcu);
242 try writer.print("\"{f}\"", .{std.zig.fmtString(&.{@intCast(byte)})});
243 if (!is_ref) try writer.writeAll(".*");
244 return;
245 },
246 else => {},
247 }
248 },
249 .vector => if (ty.arrayLen(zcu) == 0) {
250 if (is_ref) try writer.writeByte('&');
251 return writer.writeAll(".{}");
252 },
253 else => unreachable,
254 }
255
256 const len = ty.arrayLen(zcu);
257
258 if (is_ref) try writer.writeByte('&');
259 try writer.writeAll(".{ ");
260
261 const max_len = @min(len, max_aggregate_items);
262 for (0..max_len) |i| {
263 if (i != 0) try writer.writeAll(", ");
264 try print(try val.fieldValue(pt, i), writer, level - 1, pt, opt_sema);
265 }
266 if (len > max_aggregate_items) {
267 try writer.writeAll(", ...");
268 }
269 return writer.writeAll(" }");
270}
271
272fn printPtr(
273 ptr_val: Value,
274 /// Whether to print `derivation` as an lvalue or rvalue. If `null`, the more concise option is chosen.
275 want_kind: ?PrintPtrKind,
276 writer: *Writer,
277 level: u8,
278 pt: Zcu.PerThread,
279 opt_sema: ?*Sema,
280) (Writer.Error || Zcu.CompileError)!void {
281 const ptr = switch (pt.zcu.intern_pool.indexToKey(ptr_val.toIntern())) {
282 .undef => return writer.writeAll("undefined"),
283 .ptr => |ptr| ptr,
284 else => unreachable,
285 };
286
287 if (ptr.base_addr == .uav) {
288 // If the value is an aggregate, we can potentially print it more nicely.
289 switch (pt.zcu.intern_pool.indexToKey(ptr.base_addr.uav.val)) {
290 .aggregate => |agg| return printAggregate(
291 Value.fromInterned(ptr.base_addr.uav.val),
292 agg,
293 true,
294 writer,
295 level,
296 pt,
297 opt_sema,
298 ),
299 else => {},
300 }
301 }
302
303 var arena = std.heap.ArenaAllocator.init(pt.zcu.gpa);
304 defer arena.deinit();
305 const derivation = if (opt_sema) |sema|
306 try ptr_val.pointerDerivationAdvanced(arena.allocator(), pt, true, sema)
307 else
308 try ptr_val.pointerDerivationAdvanced(arena.allocator(), pt, false, null);
309
310 _ = try printPtrDerivation(derivation, writer, pt, want_kind, .{ .print_val = .{
311 .level = level,
312 .opt_sema = opt_sema,
313 } }, 20);
314}
315
316const PrintPtrKind = enum { lvalue, rvalue };
317
318/// Print the pointer defined by `derivation` as an lvalue or an rvalue.
319/// Returns the root derivation, which may be ignored.
320pub fn printPtrDerivation(
321 derivation: Value.PointerDeriveStep,
322 writer: *Writer,
323 pt: Zcu.PerThread,
324 /// Whether to print `derivation` as an lvalue or rvalue. If `null`, the more concise option is chosen.
325 /// If this is `.rvalue`, the result may look like `&foo`, so it's not necessarily valid to treat it as
326 /// an atom -- e.g. `&foo.*` is distinct from `(&foo).*`.
327 want_kind: ?PrintPtrKind,
328 /// How to print the "root" of the derivation. `.print_val` will recursively print other values if needed,
329 /// e.g. for UAV refs. `.str` will just write the root as the given string.
330 root_strat: union(enum) {
331 str: []const u8,
332 print_val: struct {
333 level: u8,
334 opt_sema: ?*Sema,
335 },
336 },
337 /// The maximum recursion depth. We can never recurse infinitely here, but the depth can be arbitrary,
338 /// so at this depth we just write "..." to prevent stack overflow.
339 ptr_depth: u8,
340) !Value.PointerDeriveStep {
341 const zcu = pt.zcu;
342 const ip = &zcu.intern_pool;
343
344 if (ptr_depth == 0) {
345 const root_step = root: switch (derivation) {
346 inline .eu_payload_ptr,
347 .opt_payload_ptr,
348 .field_ptr,
349 .elem_ptr,
350 .offset_and_cast,
351 => |step| continue :root step.parent.*,
352 else => |step| break :root step,
353 };
354 try writer.writeAll("...");
355 return root_step;
356 }
357
358 const result_kind: PrintPtrKind = switch (derivation) {
359 .nav_ptr,
360 .uav_ptr,
361 .comptime_alloc_ptr,
362 .comptime_field_ptr,
363 .eu_payload_ptr,
364 .opt_payload_ptr,
365 .field_ptr,
366 .elem_ptr,
367 => .lvalue,
368
369 .offset_and_cast,
370 .int,
371 => .rvalue,
372 };
373
374 const need_kind = want_kind orelse result_kind;
375
376 if (need_kind == .rvalue and result_kind == .lvalue) {
377 try writer.writeByte('&');
378 }
379
380 // null if `derivation` is the root.
381 const root_or_null: ?Value.PointerDeriveStep = switch (derivation) {
382 .eu_payload_ptr => |info| root: {
383 try writer.writeByte('(');
384 const root = try printPtrDerivation(info.parent.*, writer, pt, .lvalue, root_strat, ptr_depth - 1);
385 try writer.writeAll(" catch unreachable)");
386 break :root root;
387 },
388 .opt_payload_ptr => |info| root: {
389 const root = try printPtrDerivation(info.parent.*, writer, pt, .lvalue, root_strat, ptr_depth - 1);
390 try writer.writeAll(".?");
391 break :root root;
392 },
393 .field_ptr => |field| root: {
394 const root = try printPtrDerivation(field.parent.*, writer, pt, null, root_strat, ptr_depth - 1);
395 const agg_ty = (try field.parent.ptrType(pt)).childType(zcu);
396 switch (agg_ty.zigTypeTag(zcu)) {
397 .@"struct" => if (agg_ty.structFieldName(field.field_idx, zcu).unwrap()) |field_name| {
398 try writer.print(".{f}", .{field_name.fmt(ip)});
399 } else {
400 try writer.print("[{d}]", .{field.field_idx});
401 },
402 .@"union" => {
403 const tag_ty = agg_ty.unionTagTypeHypothetical(zcu);
404 const field_name = tag_ty.enumFieldName(field.field_idx, zcu);
405 try writer.print(".{f}", .{field_name.fmt(ip)});
406 },
407 .pointer => switch (field.field_idx) {
408 Value.slice_ptr_index => try writer.writeAll(".ptr"),
409 Value.slice_len_index => try writer.writeAll(".len"),
410 else => unreachable,
411 },
412 else => unreachable,
413 }
414 break :root root;
415 },
416 .elem_ptr => |elem| root: {
417 const root = try printPtrDerivation(elem.parent.*, writer, pt, null, root_strat, ptr_depth - 1);
418 try writer.print("[{d}]", .{elem.elem_idx});
419 break :root root;
420 },
421
422 .offset_and_cast => |oac| if (oac.byte_offset == 0) root: {
423 try writer.print("@as({f}, @ptrCast(", .{oac.new_ptr_ty.fmt(pt)});
424 const root = try printPtrDerivation(oac.parent.*, writer, pt, .rvalue, root_strat, ptr_depth - 1);
425 try writer.writeAll("))");
426 break :root root;
427 } else root: {
428 try writer.print("@as({f}, @ptrFromInt(@intFromPtr(", .{oac.new_ptr_ty.fmt(pt)});
429 const root = try printPtrDerivation(oac.parent.*, writer, pt, .rvalue, root_strat, ptr_depth - 1);
430 try writer.print(") + {d}))", .{oac.byte_offset});
431 break :root root;
432 },
433
434 .int, .nav_ptr, .uav_ptr, .comptime_alloc_ptr, .comptime_field_ptr => null,
435 };
436
437 if (root_or_null == null) switch (root_strat) {
438 .str => |x| try writer.writeAll(x),
439 .print_val => |x| switch (derivation) {
440 .int => |int| try writer.print("@as({f}, @ptrFromInt(0x{x}))", .{ int.ptr_ty.fmt(pt), int.addr }),
441 .nav_ptr => |nav| try writer.print("{f}", .{ip.getNav(nav).fqn.fmt(ip)}),
442 .uav_ptr => |uav| {
443 const ty = Value.fromInterned(uav.val).typeOf(zcu);
444 try writer.print("@as({f}, ", .{ty.fmt(pt)});
445 try print(Value.fromInterned(uav.val), writer, x.level - 1, pt, x.opt_sema);
446 try writer.writeByte(')');
447 },
448 .comptime_alloc_ptr => |info| {
449 try writer.print("@as({f}, ", .{info.val.typeOf(zcu).fmt(pt)});
450 try print(info.val, writer, x.level - 1, pt, x.opt_sema);
451 try writer.writeByte(')');
452 },
453 .comptime_field_ptr => |val| {
454 const ty = val.typeOf(zcu);
455 try writer.print("@as({f}, ", .{ty.fmt(pt)});
456 try print(val, writer, x.level - 1, pt, x.opt_sema);
457 try writer.writeByte(')');
458 },
459 else => unreachable,
460 },
461 };
462
463 if (need_kind == .lvalue and result_kind == .rvalue) {
464 try writer.writeAll(".*");
465 }
466
467 return root_or_null orelse derivation;
468}