zig icon indicating copy to clipboard operation
zig copied to clipboard

std/json: parse* cannot handle `?noreturn`

Open deanveloper opened this issue 1 year ago • 5 comments

Zig Version

0.12.0-dev.2928+6fddc9cd3

Steps to Reproduce and Observed Behavior

Minimum reproducible example:

const std = @import("std");
const json = std.json;

pub const Foo = struct {
    can_only_be_null: ?noreturn,
};

pub fn main() !void {
    const foo = Foo{ .can_only_be_null = null };
    try std.json.stringify(foo, .{}, std.io.getStdOut().writer());

    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const bar = try std.json.parseFromSlice(Foo, gpa.allocator(), "{\"can_only_be_null\":null}", .{});

    std.debug.print("{}", .{bar});
}

Gives the compile-time error:

An error occurred:
/usr/local/bin/lib/std/json/static.zig:506:17: error: Unable to parse into type 'noreturn'

Expected Behavior

While noreturn on its own can't be parsed into, ?noreturn and fn (T: type) type { return union(enum) { x: SomeType, y: T }} (where T is noreturn) are useful. For instance, see Discord's Role Tags Structure, which with an fn Omittable(T) helper, can be represented as Omittable(?noreturn)

Workaround for now can be to use enum{} instead of noreturn, which both represent a type that has 0 possible values. Another workaround is of course to just make a custom type with its own jsonParse and jsonStringify methods.

deanveloper avatar Mar 19 '24 17:03 deanveloper

should specify that Discord's Role Tags Structure is incredibly cursed, but alas, I must parse it. And it'd be very convenient to use Omittable(?noreturn) 😄

deanveloper avatar Mar 19 '24 18:03 deanveloper

Does Omittable(@TypeOf(null)) not work for this usecase? @TypeOf(null) gives you a zero sized type that can only be the value null:

test {
    const S = struct {
        f: @TypeOf(null),
    };
    const s = S{ .f = null };
    try std.testing.expectEqual(null, s.f);
}

const std = @import("std");

Hejsil avatar Mar 19 '24 21:03 Hejsil

On a sidenote, this is kinda funny:

src/main.zig:7:44: error: no size available for type '@TypeOf(null)'
    try std.testing.expectEqual(0, @sizeOf(@TypeOf(null)));

But @sizeOf(S) return 0. Seems like a bug. Is there an issue for this?

Hejsil avatar Mar 19 '24 21:03 Hejsil

Aah no, you cannot have fields of type @TypeOf(null) at runtime. My bad:

test {
    const S = struct {
        f: @TypeOf(null),
    };
    var s = S{ .f = null };
    try std.testing.expectEqual(0, @sizeOf(@TypeOf(S)));
    try std.testing.expectEqual(null, s.f);

    s = s;
}

const std = @import("std");
src/main.zig:5:9: error: variable of type 'main.test_0.S' must be const or comptime
    var s = S{ .f = null };
        ^
src/main.zig:3:12: note: struct requires comptime because of this field
        f: @TypeOf(null),

Not sure when this changed

Hejsil avatar Mar 19 '24 21:03 Hejsil

@Hejsil I also think of @TypeOf(null) as a runtime-valid type, but it seems like the opposite was invalidly assumed at some point when stage2 was written. For arguments and return values this has been reported and fixed before: https://github.com/ziglang/zig/pull/16104. I haven't found an open issue about using it as struct field, but IMO there shouldn't be any disparity between those usages.

Regarding @sizeOf on comptime-only types erroring, that's proposal https://github.com/ziglang/zig/issues/4211 ; currently the langref states that 0 is returned.

rohlem avatar Mar 20 '24 19:03 rohlem