diff options
| author | Veikka Tuominen <git@vexu.eu> | 2022-03-21 14:48:47 +0200 |
|---|---|---|
| committer | Veikka Tuominen <git@vexu.eu> | 2022-03-21 15:03:42 +0200 |
| commit | a31fe0ff12270ba2f957c2a957941a23f2143ad5 (patch) | |
| tree | 3381f01ab6a370f0718c24732adf90cb03370a20 | |
| parent | 3d8d6c0a6d7a29396725467672023b5ec3adbce6 (diff) | |
| download | zig-a31fe0ff12270ba2f957c2a957941a23f2143ad5.tar.gz zig-a31fe0ff12270ba2f957c2a957941a23f2143ad5.zip | |
stage2: add way to print values with types
| -rw-r--r-- | src/Sema.zig | 16 | ||||
| -rw-r--r-- | src/TypedValue.zig | 281 | ||||
| -rw-r--r-- | src/arch/aarch64/CodeGen.zig | 2 | ||||
| -rw-r--r-- | src/arch/x86_64/CodeGen.zig | 2 | ||||
| -rw-r--r-- | src/codegen.zig | 2 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 2 | ||||
| -rw-r--r-- | src/print_air.zig | 2 | ||||
| -rw-r--r-- | src/type.zig | 12 | ||||
| -rw-r--r-- | src/value.zig | 20 |
9 files changed, 318 insertions, 21 deletions
diff --git a/src/Sema.zig b/src/Sema.zig index e7c6ae0719..7674121ba6 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1867,7 +1867,7 @@ fn createTypeName( const arg_val = sema.resolveConstMaybeUndefVal(block, .unneeded, arg) catch unreachable; if (arg_i != 0) try buf.appendSlice(","); - try buf.writer().print("{}", .{arg_val}); + try buf.writer().print("{}", .{arg_val.fmtValue(sema.typeOf(arg))}); arg_i += 1; continue; @@ -3673,7 +3673,7 @@ fn zirCompileLog( const arg = sema.resolveInst(arg_ref); const arg_ty = sema.typeOf(arg); if (try sema.resolveMaybeUndefVal(block, src, arg)) |val| { - try writer.print("@as({}, {})", .{ arg_ty, val }); + try writer.print("@as({}, {})", .{ arg_ty, val.fmtValue(arg_ty) }); } else { try writer.print("@as({}, [runtime value])", .{arg_ty}); } @@ -5670,7 +5670,7 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A block, src, "enum '{}' has no tag with value {}", - .{ dest_ty, int_val }, + .{ dest_ty, int_val.fmtValue(sema.typeOf(operand)) }, ); errdefer msg.destroy(sema.gpa); try sema.mod.errNoteNonLazy( @@ -7900,7 +7900,7 @@ fn validateSwitchItemEnum( block, src, "enum '{}' has no tag with value '{}'", - .{ item_tv.ty, item_tv.val }, + .{ item_tv.ty, item_tv.val.fmtValue(item_tv.ty) }, ); errdefer msg.destroy(sema.gpa); try sema.mod.errNoteNonLazy( @@ -17507,7 +17507,7 @@ fn coerce( const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse break :float; if (val.floatHasFraction()) { - return sema.fail(block, inst_src, "fractional component prevents float value {} from coercion to type '{}'", .{ val, dest_ty }); + return sema.fail(block, inst_src, "fractional component prevents float value {} from coercion to type '{}'", .{ val.fmtValue(inst_ty), dest_ty }); } const result_val = val.floatToInt(sema.arena, dest_ty, target) catch |err| switch (err) { error.FloatCannotFit => { @@ -17521,7 +17521,7 @@ fn coerce( if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| { // comptime known integer to other number if (!val.intFitsInType(dest_ty, target)) { - return sema.fail(block, inst_src, "type {} cannot represent integer value {}", .{ dest_ty, val }); + return sema.fail(block, inst_src, "type {} cannot represent integer value {}", .{ dest_ty, val.fmtValue(inst_ty) }); } return try sema.addConstant(dest_ty, val); } @@ -17556,7 +17556,7 @@ fn coerce( block, inst_src, "type {} cannot represent float value {}", - .{ dest_ty, val }, + .{ dest_ty, val.fmtValue(inst_ty) }, ); } return try sema.addConstant(dest_ty, result_val); @@ -18850,7 +18850,7 @@ fn coerceEnumToUnion( const field_index = union_obj.tag_ty.enumTagFieldIndex(val) orelse { const msg = msg: { const msg = try sema.errMsg(block, inst_src, "union {} has no tag with value {}", .{ - union_ty, val, + union_ty, val.fmtValue(tag_ty), }); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, union_ty); diff --git a/src/TypedValue.zig b/src/TypedValue.zig index 1fa4813a34..fc5ef45991 100644 --- a/src/TypedValue.zig +++ b/src/TypedValue.zig @@ -42,3 +42,284 @@ pub fn hash(tv: TypedValue, hasher: *std.hash.Wyhash) void { pub fn enumToInt(tv: TypedValue, buffer: *Value.Payload.U64) Value { return tv.val.enumToInt(tv.ty, buffer); } + +const max_aggregate_items = 100; + +pub fn format( + tv: TypedValue, + comptime fmt: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + comptime std.debug.assert(fmt.len == 0); + return tv.print(options, writer, 3); +} + +pub fn print( + tv: TypedValue, + options: std.fmt.FormatOptions, + writer: anytype, + level: u8, +) @TypeOf(writer).Error!void { + var val = tv.val; + var ty = tv.ty; + while (true) switch (val.tag()) { + .u1_type => return writer.writeAll("u1"), + .u8_type => return writer.writeAll("u8"), + .i8_type => return writer.writeAll("i8"), + .u16_type => return writer.writeAll("u16"), + .i16_type => return writer.writeAll("i16"), + .u32_type => return writer.writeAll("u32"), + .i32_type => return writer.writeAll("i32"), + .u64_type => return writer.writeAll("u64"), + .i64_type => return writer.writeAll("i64"), + .u128_type => return writer.writeAll("u128"), + .i128_type => return writer.writeAll("i128"), + .isize_type => return writer.writeAll("isize"), + .usize_type => return writer.writeAll("usize"), + .c_short_type => return writer.writeAll("c_short"), + .c_ushort_type => return writer.writeAll("c_ushort"), + .c_int_type => return writer.writeAll("c_int"), + .c_uint_type => return writer.writeAll("c_uint"), + .c_long_type => return writer.writeAll("c_long"), + .c_ulong_type => return writer.writeAll("c_ulong"), + .c_longlong_type => return writer.writeAll("c_longlong"), + .c_ulonglong_type => return writer.writeAll("c_ulonglong"), + .c_longdouble_type => return writer.writeAll("c_longdouble"), + .f16_type => return writer.writeAll("f16"), + .f32_type => return writer.writeAll("f32"), + .f64_type => return writer.writeAll("f64"), + .f80_type => return writer.writeAll("f80"), + .f128_type => return writer.writeAll("f128"), + .anyopaque_type => return writer.writeAll("anyopaque"), + .bool_type => return writer.writeAll("bool"), + .void_type => return writer.writeAll("void"), + .type_type => return writer.writeAll("type"), + .anyerror_type => return writer.writeAll("anyerror"), + .comptime_int_type => return writer.writeAll("comptime_int"), + .comptime_float_type => return writer.writeAll("comptime_float"), + .noreturn_type => return writer.writeAll("noreturn"), + .null_type => return writer.writeAll("@Type(.Null)"), + .undefined_type => return writer.writeAll("@Type(.Undefined)"), + .fn_noreturn_no_args_type => return writer.writeAll("fn() noreturn"), + .fn_void_no_args_type => return writer.writeAll("fn() void"), + .fn_naked_noreturn_no_args_type => return writer.writeAll("fn() callconv(.Naked) noreturn"), + .fn_ccc_void_no_args_type => return writer.writeAll("fn() callconv(.C) void"), + .single_const_pointer_to_comptime_int_type => return writer.writeAll("*const comptime_int"), + .anyframe_type => return writer.writeAll("anyframe"), + .const_slice_u8_type => return writer.writeAll("[]const u8"), + .const_slice_u8_sentinel_0_type => return writer.writeAll("[:0]const u8"), + .anyerror_void_error_union_type => return writer.writeAll("anyerror!void"), + + .enum_literal_type => return writer.writeAll("@Type(.EnumLiteral)"), + .manyptr_u8_type => return writer.writeAll("[*]u8"), + .manyptr_const_u8_type => return writer.writeAll("[*]const u8"), + .manyptr_const_u8_sentinel_0_type => return writer.writeAll("[*:0]const u8"), + .atomic_order_type => return writer.writeAll("std.builtin.AtomicOrder"), + .atomic_rmw_op_type => return writer.writeAll("std.builtin.AtomicRmwOp"), + .calling_convention_type => return writer.writeAll("std.builtin.CallingConvention"), + .address_space_type => return writer.writeAll("std.builtin.AddressSpace"), + .float_mode_type => return writer.writeAll("std.builtin.FloatMode"), + .reduce_op_type => return writer.writeAll("std.builtin.ReduceOp"), + .call_options_type => return writer.writeAll("std.builtin.CallOptions"), + .prefetch_options_type => return writer.writeAll("std.builtin.PrefetchOptions"), + .export_options_type => return writer.writeAll("std.builtin.ExportOptions"), + .extern_options_type => return writer.writeAll("std.builtin.ExternOptions"), + .type_info_type => return writer.writeAll("std.builtin.Type"), + + .empty_struct_value => return writer.writeAll(".{}"), + .aggregate => { + if (level == 0) { + return writer.writeAll(".{ ... }"); + } + const vals = val.castTag(.aggregate).?.data; + if (ty.zigTypeTag() == .Struct) { + try writer.writeAll(".{ "); + const struct_fields = ty.structFields(); + const max_len = std.math.min(struct_fields.count(), max_aggregate_items); + + const field_names = struct_fields.keys(); + const fields = struct_fields.values(); + + var i: u32 = 0; + while (i < max_len) : (i += 1) { + if (i != 0) try writer.writeAll(", "); + try writer.print(".{s} = ", .{field_names[i]}); + try print(.{ + .ty = fields[i].ty, + .val = vals[i], + }, options, writer, level - 1); + } + return writer.writeAll(" }"); + } else { + try writer.writeAll(".{ "); + const elem_ty = ty.elemType2(); + const max_len = std.math.min(ty.arrayLen(), max_aggregate_items); + + var i: u32 = 0; + while (i < max_len) : (i += 1) { + if (i != 0) try writer.writeAll(", "); + try print(.{ + .ty = elem_ty, + .val = vals[i], + }, options, writer, level - 1); + } + return writer.writeAll(" }"); + } + }, + .@"union" => { + if (level == 0) { + return writer.writeAll(".{ ... }"); + } + const union_val = val.castTag(.@"union").?.data; + try writer.writeAll(".{ "); + + try print(.{ + .ty = ty.unionTagType().?, + .val = union_val.tag, + }, options, writer, level - 1); + try writer.writeAll(" = "); + try print(.{ + .ty = ty.unionFieldType(union_val.tag), + .val = union_val.val, + }, options, writer, level - 1); + + return writer.writeAll(" }"); + }, + .null_value => return writer.writeAll("null"), + .undef => return writer.writeAll("undefined"), + .zero => return writer.writeAll("0"), + .one => return writer.writeAll("1"), + .void_value => return writer.writeAll("{}"), + .unreachable_value => return writer.writeAll("unreachable"), + .the_only_possible_value => { + val = ty.onePossibleValue().?; + }, + .bool_true => return writer.writeAll("true"), + .bool_false => return writer.writeAll("false"), + .ty => return val.castTag(.ty).?.data.format("", options, writer), + .int_type => { + const int_type = val.castTag(.int_type).?.data; + return writer.print("{s}{d}", .{ + if (int_type.signed) "s" else "u", + int_type.bits, + }); + }, + .int_u64 => return std.fmt.formatIntValue(val.castTag(.int_u64).?.data, "", options, writer), + .int_i64 => return std.fmt.formatIntValue(val.castTag(.int_i64).?.data, "", options, writer), + .int_big_positive => return writer.print("{}", .{val.castTag(.int_big_positive).?.asBigInt()}), + .int_big_negative => return writer.print("{}", .{val.castTag(.int_big_negative).?.asBigInt()}), + .function => return writer.print("(function '{s}')", .{val.castTag(.function).?.data.owner_decl.name}), + .extern_fn => return writer.writeAll("(extern function)"), + .variable => return writer.writeAll("(variable)"), + .decl_ref_mut => { + const decl = val.castTag(.decl_ref_mut).?.data.decl; + if (level == 0) { + return writer.print("(decl ref mut '{s}')", .{decl.name}); + } + return print(.{ + .ty = decl.ty, + .val = decl.val, + }, options, writer, level - 1); + }, + .decl_ref => { + const decl = val.castTag(.decl_ref).?.data; + if (level == 0) { + return writer.print("(decl ref '{s}')", .{decl.name}); + } + return print(.{ + .ty = decl.ty, + .val = decl.val, + }, options, writer, level - 1); + }, + .elem_ptr => { + const elem_ptr = val.castTag(.elem_ptr).?.data; + try writer.writeAll("&"); + try print(.{ + .ty = elem_ptr.elem_ty, + .val = elem_ptr.array_ptr, + }, options, writer, level - 1); + return writer.print("[{}]", .{elem_ptr.index}); + }, + .field_ptr => { + const field_ptr = val.castTag(.field_ptr).?.data; + try writer.writeAll("&"); + try print(.{ + .ty = field_ptr.container_ty, + .val = field_ptr.container_ptr, + }, options, writer, level - 1); + + if (field_ptr.container_ty.zigTypeTag() == .Struct) { + const field_name = field_ptr.container_ty.structFields().keys()[field_ptr.field_index]; + return writer.print(".{s}", .{field_name}); + } else if (field_ptr.container_ty.zigTypeTag() == .Union) { + const field_name = field_ptr.container_ty.unionFields().keys()[field_ptr.field_index]; + return writer.print(".{s}", .{field_name}); + } else unreachable; + }, + .empty_array => return writer.writeAll(".{}"), + .enum_literal => return writer.print(".{}", .{std.zig.fmtId(val.castTag(.enum_literal).?.data)}), + .enum_field_index => { + return writer.print(".{s}", .{ty.enumFieldName(val.castTag(.enum_field_index).?.data)}); + }, + .bytes => return writer.print("\"{}\"", .{std.zig.fmtEscapes(val.castTag(.bytes).?.data)}), + .repeated => { + if (level == 0) { + return writer.writeAll(".{ ... }"); + } + var i: u32 = 0; + try writer.writeAll(".{ "); + const elem_tv = TypedValue{ + .ty = ty.elemType2(), + .val = val.castTag(.repeated).?.data, + }; + while (i < max_aggregate_items) : (i += 1) { + if (i != 0) try writer.writeAll(", "); + try print(elem_tv, options, writer, level - 1); + } + return writer.writeAll(" }"); + }, + .empty_array_sentinel => { + if (level == 0) { + return writer.writeAll(".{ (sentinel) }"); + } + try writer.writeAll(".{ "); + try print(.{ + .ty = ty.elemType2(), + .val = ty.sentinel().?, + }, options, writer, level - 1); + return writer.writeAll(" }"); + }, + .slice => return writer.writeAll("(slice)"), + .float_16 => return writer.print("{}", .{val.castTag(.float_16).?.data}), + .float_32 => return writer.print("{}", .{val.castTag(.float_32).?.data}), + .float_64 => return writer.print("{}", .{val.castTag(.float_64).?.data}), + .float_80 => return writer.print("{}", .{val.castTag(.float_80).?.data}), + .float_128 => return writer.print("{}", .{val.castTag(.float_128).?.data}), + .@"error" => return writer.print("error.{s}", .{val.castTag(.@"error").?.data.name}), + .eu_payload => { + val = val.castTag(.eu_payload).?.data; + }, + .opt_payload => { + val = val.castTag(.opt_payload).?.data; + }, + .eu_payload_ptr => { + try writer.writeAll("&"); + val = val.castTag(.eu_payload_ptr).?.data.container_ptr; + }, + .opt_payload_ptr => { + try writer.writeAll("&"); + val = val.castTag(.opt_payload_ptr).?.data.container_ptr; + }, + + // TODO these should not appear in this function + .inferred_alloc => return writer.writeAll("(inferred allocation value)"), + .inferred_alloc_comptime => return writer.writeAll("(inferred comptime allocation value)"), + .bound_fn => { + const bound_func = val.castTag(.bound_fn).?.data; + return writer.print("(bound_fn %{}(%{})", .{ bound_func.func_inst, bound_func.arg0_inst }); + }, + .generic_poison_type => return writer.writeAll("(generic poison type)"), + .generic_poison => return writer.writeAll("(generic poison)"), + }; +} diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 9a732e9fd0..c9121c8859 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -3873,7 +3873,7 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa } fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { - log.debug("lowerUnnamedConst: ty = {}, val = {}", .{ tv.ty, tv.val }); + log.debug("lowerUnnamedConst: ty = {}, val = {}", .{ tv.ty, tv.val.fmtDebug() }); const local_sym_index = self.bin_file.lowerUnnamedConst(tv, self.mod_fn.owner_decl) catch |err| { return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)}); }; diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index d90ec90c28..37d93f870c 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -5735,7 +5735,7 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa } fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { - log.debug("lowerUnnamedConst: ty = {}, val = {}", .{ tv.ty, tv.val }); + log.debug("lowerUnnamedConst: ty = {}, val = {}", .{ tv.ty, tv.val.fmtDebug() }); const local_sym_index = self.bin_file.lowerUnnamedConst(tv, self.mod_fn.owner_decl) catch |err| { return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)}); }; diff --git a/src/codegen.zig b/src/codegen.zig index 5d61095ff5..95b145e901 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -165,7 +165,7 @@ pub fn generateSymbol( const target = bin_file.options.target; const endian = target.cpu.arch.endian(); - log.debug("generateSymbol: ty = {}, val = {}", .{ typed_value.ty, typed_value.val }); + log.debug("generateSymbol: ty = {}, val = {}", .{ typed_value.ty, typed_value.val.fmtDebug() }); if (typed_value.val.isUndefDeep()) { const abi_size = try math.cast(usize, typed_value.ty.abiSize(target)); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 2d5b38eb17..8eb1eba3f6 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1676,7 +1676,7 @@ pub const DeclGen = struct { const decl = dg.decl; assert(decl.has_tv); - log.debug("gen: {s} type: {}, value: {}", .{ decl.name, decl.ty, decl.val }); + log.debug("gen: {s} type: {}, value: {}", .{ decl.name, decl.ty, decl.val.fmtDebug() }); if (decl.val.castTag(.function)) |func_payload| { _ = func_payload; diff --git a/src/print_air.zig b/src/print_air.zig index a9605d5dcf..fb970e3b08 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -491,7 +491,7 @@ const Writer = struct { fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; const val = w.air.values[ty_pl.payload]; - try s.print("{}, {}", .{ w.air.getRefType(ty_pl.ty), val }); + try s.print("{}, {}", .{ w.air.getRefType(ty_pl.ty), val.fmtDebug() }); } fn writeAssembly(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { diff --git a/src/type.zig b/src/type.zig index a29dc19f8d..6d27150412 100644 --- a/src/type.zig +++ b/src/type.zig @@ -1633,7 +1633,7 @@ pub const Type = extern union { }, .array_sentinel => { const payload = ty.castTag(.array_sentinel).?.data; - try writer.print("[{d}:{}]", .{ payload.len, payload.sentinel }); + try writer.print("[{d}:{}]", .{ payload.len, payload.sentinel.fmtValue(payload.elem_type) }); ty = payload.elem_type; continue; }, @@ -1648,8 +1648,7 @@ pub const Type = extern union { } try field_ty.format("", .{}, writer); if (val.tag() != .unreachable_value) { - try writer.writeAll(" = "); - try val.format("", .{}, writer); + try writer.print(" = {}", .{val.fmtValue(field_ty)}); } } try writer.writeAll("}"); @@ -1668,8 +1667,7 @@ pub const Type = extern union { try writer.writeAll(": "); try field_ty.format("", .{}, writer); if (val.tag() != .unreachable_value) { - try writer.writeAll(" = "); - try val.format("", .{}, writer); + try writer.print(" = {}", .{val.fmtValue(field_ty)}); } } try writer.writeAll("}"); @@ -1754,8 +1752,8 @@ pub const Type = extern union { const payload = ty.castTag(.pointer).?.data; if (payload.sentinel) |some| switch (payload.size) { .One, .C => unreachable, - .Many => try writer.print("[*:{}]", .{some}), - .Slice => try writer.print("[:{}]", .{some}), + .Many => try writer.print("[*:{}]", .{some.fmtValue(payload.pointee_type)}), + .Slice => try writer.print("[:{}]", .{some.fmtValue(payload.pointee_type)}), } else switch (payload.size) { .One => try writer.writeAll("*"), .Many => try writer.writeAll("[*]"), diff --git a/src/value.zig b/src/value.zig index 269b13b099..76ab20c7ab 100644 --- a/src/value.zig +++ b/src/value.zig @@ -600,9 +600,17 @@ pub const Value = extern union { return Value{ .ptr_otherwise = &new_payload.base }; } + pub fn format(val: Value, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { + _ = val; + _ = fmt; + _ = options; + _ = writer; + @compileError("do not use format values directly; use either fmtDebug or fmtValue"); + } + /// TODO this should become a debug dump() function. In order to print values in a meaningful way /// we also need access to the type. - pub fn format( + pub fn dump( start_val: Value, comptime fmt: []const u8, options: std.fmt.FormatOptions, @@ -766,6 +774,16 @@ pub const Value = extern union { }; } + pub fn fmtDebug(val: Value) std.fmt.Formatter(dump) { + return .{ .data = val }; + } + + const TypedValue = @import("TypedValue.zig"); + + pub fn fmtValue(val: Value, ty: Type) std.fmt.Formatter(TypedValue.format) { + return .{ .data = .{ .ty = ty, .val = val } }; + } + /// Asserts that the value is representable as an array of bytes. /// Copies the value into a freshly allocated slice of memory, which is owned by the caller. pub fn toAllocatedBytes(val: Value, ty: Type, allocator: Allocator) ![]u8 { |
