Skip to content

std/json: parse* cannot handle ?noreturn #19356

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
deanveloper opened this issue Mar 19, 2024 · 7 comments
Open

std/json: parse* cannot handle ?noreturn #19356

deanveloper opened this issue Mar 19, 2024 · 7 comments
Labels
bug Observed behavior contradicts documented or intended behavior standard library This issue involves writing Zig code for the standard library.
Milestone

Comments

@deanveloper
Copy link

deanveloper commented Mar 19, 2024

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 deanveloper added the bug Observed behavior contradicts documented or intended behavior label Mar 19, 2024
@deanveloper
Copy link
Author

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) 😄

@Vexu Vexu added the standard library This issue involves writing Zig code for the standard library. label Mar 19, 2024
@Vexu Vexu added this to the 0.13.0 milestone Mar 19, 2024
@Hejsil
Copy link
Contributor

Hejsil commented Mar 19, 2024

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
Copy link
Contributor

Hejsil commented Mar 19, 2024

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
Copy link
Contributor

Hejsil commented Mar 19, 2024

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

@rohlem
Copy link
Contributor

rohlem commented Mar 20, 2024

@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: #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 #4211 ; currently the langref states that 0 is returned (behavior which is imo misleading/ a source for errors).

@rohlem
Copy link
Contributor

rohlem commented Jun 20, 2024

Correction to my last comment:
I just found out I misread #16104 several times over - the description says "null and undefined are [...] comptime-only values.".
The following still fails, seemingly intentionally:

// compile error: 
// argument to function being called at comptime must be comptime-known;
// function returns a comptime-only type '@TypeOf(null)'
pub fn foo(value: bool) @TypeOf(null) {
    _ = value;
    return null;
}
fn exe(a: anytype) void {
    @compileLog(foo(a));
}
test exe {
    exe(true);
}

I still see no fundamental problem with the idea, and think it would be nicer if this were allowed, i.e. @TypeOf(null) were a 0-bit runtime type.
EDIT: As a crutch, you can sometimes use an optional with a stub payload type: const Null = ?enum{};.
Imo this reduces code clarity over @TypeOf(null) though, and doesn't integrate as nicely (needs separate checking code).

@deanveloper
Copy link
Author

Yup, I've been using Omittable(?enum{}) for now. It's definitely a strange use-case, you can blame discord's wacky API for this issue ahaha.

@andrewrk andrewrk modified the milestones: 0.14.0, 0.16.0 Feb 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Observed behavior contradicts documented or intended behavior standard library This issue involves writing Zig code for the standard library.
Projects
None yet
Development

No branches or pull requests

5 participants