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}