zig icon indicating copy to clipboard operation
zig copied to clipboard

Inferred error sets + generic interfaces = stage2 crashes

Open ibokuri opened this issue 2 years ago • 2 comments

Zig Version

0.10.0-dev.3978+4fd4c733d

Steps to Reproduce

Run the following program using the stage2 compiler (and a recent version of Zig).

const std = @import("std");

// This interface is basically std.io.Writer, except the interface type
// and interface function are nested within a namespace to make
// things easier for implementations.
fn Writer(
    comptime Context: type,
    comptime O: type,
    comptime E: type,
    comptime writeFn: fn (Context, anytype) E!O,
) type {
    return struct {
        pub const W = struct {
            context: Context,

            pub fn write(self: @This(), value: anytype) E!O {
                return writeFn(self.context, value);
            }
        };

        pub fn writer(self: Context) W {
            return .{ .context = self };
        }
    };
}

const MyWriter = struct {
    pub usingnamespace Writer(@This(), Ok, Error, write);

    const Ok = void;
    const Error = error{ Foo, Bar };

    // Changing the return type to `Error!Ok` fixes everything.
    pub fn write(_: @This(), value: anytype) !Ok {
        std.debug.print("{}\n", .{value});
    }
};

pub fn main() !void {
    var mw = MyWriter{};
    const w = mw.writer();

    try w.write(true);
}

Expected Behavior

$ zig build run
true

Actual Behavior

$ zig build run
thread 3989124 panic: attempt to unwrap error: GenericPoison
Unable to dump stack trace: debug info stripped
error: test...
error: The following command terminated unexpectedly:
/usr/local/bin/zig/zig build-exe /Users/jason/Projects/Personal/test/src/main.zig --cache-dir /Users/jason/Projects/Personal/test/zig-cache --global-cache-dir /Users/jason/.cache/zig --name test --pkg-begin getty /Users/jason/Projects/Personal/getty/src/lib.zig --pkg-end --pkg-begin json /Users/jason/Projects/Personal/json/src/lib.zig --pkg-begin getty /Users/jason/Projects/Personal/getty/src/lib.zig --pkg-end --pkg-begin concepts /Users/jason/Projects/Personal/test/.gyro/concepts-ibokuri-github.com-05c73681/pkg/src/lib.zig --pkg-end --pkg-end --enable-cache
error: the following build command failed with exit code 6:
/Users/jason/Projects/Personal/test/zig-cache/o/73cf47bc28a8ebaa9cf2c9de60989768/build /usr/local/bin/zig/zig /Users/jason/Projects/Personal/test /Users/jason/Projects/Personal/test/zig-cache /Users/jason/.cache/zig run

The problem seems to only come up when the interface type (W) is returned within a namespace instead of as-is (e.g., std.io.Writer doesn't run into this issue since the interface type's returned directly). For some reason, having the interface type nested like this causes stage2 compilations to crash if a method implementation provided to the interface:

  1. Takes an anytype parameter and
  2. Uses an inferred error set in its return type.

This behavior doesn't come up when building with -fstage1 and wasn't an issue for me in stage2 until I upgraded this morning.

ibokuri avatar Sep 10 '22 16:09 ibokuri

This code is triggering an assertion

thread 26160 panic: reached unreachable code
/home/david/dev/official-zig/src/Module.zig:5563:36: 0x55d0af82fe4c in Module.analyzeFnBody (zig2)
            error.GenericPoison => unreachable,
                                   ^
/home/david/dev/official-zig/src/Module.zig:4291:40: 0x55d0af81096a in Module.ensureFuncBodyAnalyzed (zig2)
            var air = mod.analyzeFnBody(func, sema_arena) catch |err| switch (err) {
                                       ^
/home/david/dev/official-zig/src/Sema.zig:26405:36: 0x55d0aff5d773 in Sema.ensureFuncBodyAnalyzed (zig2)
    sema.mod.ensureFuncBodyAnalyzed(func) catch |err| {
                                   ^
/home/david/dev/official-zig/src/Sema.zig:28530:40: 0x55d0afe8770f in Sema.resolveInferredErrorSet (zig2)
        try sema.ensureFuncBodyAnalyzed(ies.func);
                                       ^
/home/david/dev/official-zig/src/Sema.zig:24197:45: 0x55d0aff51652 in Sema.coerceInMemoryAllowedErrorSets (zig2)
            try sema.resolveInferredErrorSet(block, src_src, src_data);
                                            ^
/home/david/dev/official-zig/src/Sema.zig:24037:55: 0x55d0afd18b26 in Sema.coerceInMemoryAllowed (zig2)
        return try sema.coerceInMemoryAllowedErrorSets(block, dest_ty, src_ty, dest_src, src_src);
                                                      ^
/home/david/dev/official-zig/src/Sema.zig:24032:46: 0x55d0afd189d7 in Sema.coerceInMemoryAllowed (zig2)
        return try sema.coerceInMemoryAllowed(block, dest_ty.errorUnionSet(), src_ty.errorUnionSet(), dest_is_mut, target, dest_src, src_src);
                                             ^
/home/david/dev/official-zig/src/Sema.zig:24299:50: 0x55d0aff4faba in Sema.coerceInMemoryAllowedFns (zig2)
        const rt = try sema.coerceInMemoryAllowed(block, dest_info.return_type, src_info.return_type, false, target, dest_src, src_src);
                                                 ^
/home/david/dev/official-zig/src/Sema.zig:24017:49: 0x55d0afd185f9 in Sema.coerceInMemoryAllowed (zig2)
        return try sema.coerceInMemoryAllowedFns(block, dest_ty, src_ty, target, dest_src, src_src);
                                                ^
/home/david/dev/official-zig/src/Sema.zig:23005:58: 0x55d0afa4dfdb in Sema.coerceExtra (zig2)
    var in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src);
                                                         ^
/home/david/dev/official-zig/src/Sema.zig:22960:28: 0x55d0afa4cde9 in Sema.coerce (zig2)
    return sema.coerceExtra(block, dest_ty_unresolved, inst, inst_src, .{}) catch |err| switch (err) {
                           ^
/home/david/dev/official-zig/src/Sema.zig:6353:47: 0x55d0b007f413 in Sema.analyzeInlineCallArg (zig2)
            const casted_arg = try sema.coerce(arg_block, param_ty, uncasted_arg, arg_src);
                                              ^
/home/david/dev/official-zig/src/Sema.zig:6055:38: 0x55d0afe74849 in Sema.analyzeCall (zig2)
            sema.analyzeInlineCallArg(
                                     ^
/home/david/dev/official-zig/src/Sema.zig:5711:28: 0x55d0afbdd39c in Sema.zirCall (zig2)
    return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src);
                           ^
/home/david/dev/official-zig/src/Sema.zig:727:62: 0x55d0afa38fb5 in Sema.analyzeBodyInner (zig2)
            .call                         => try sema.zirCall(block, inst),
                                                             ^
/home/david/dev/official-zig/src/Sema.zig:628:45: 0x55d0afa33c1c in Sema.analyzeBodyBreak (zig2)
    const break_inst = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
                                            ^
/home/david/dev/official-zig/src/Module.zig:4571:50: 0x55d0af8376df in Module.semaDecl (zig2)
    const result_ref = (try sema.analyzeBodyBreak(&block_scope, body)).?.operand;
                                                 ^
/home/david/dev/official-zig/src/Module.zig:4205:38: 0x55d0af811bbb in Module.ensureDeclAnalyzed (zig2)
    const type_changed = mod.semaDecl(decl_index) catch |err| switch (err) {
                                     ^
/home/david/dev/official-zig/src/Sema.zig:26394:32: 0x55d0afe9e35f in Sema.ensureDeclAnalyzed (zig2)
    sema.mod.ensureDeclAnalyzed(decl_index) catch |err| {
                               ^
/home/david/dev/official-zig/src/Sema.zig:5521:44: 0x55d0afeacf3e in Sema.lookupInNamespace (zig2)
                try sema.ensureDeclAnalyzed(sub_usingnamespace_decl_index);
                                           ^
/home/david/dev/official-zig/src/Sema.zig:22004:35: 0x55d0afe9de5d in Sema.namespaceLookup (zig2)
    if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl_index| {
                                  ^
/home/david/dev/official-zig/src/Sema.zig:22029:43: 0x55d0afc8c6d6 in Sema.namespaceLookupRef (zig2)
    const decl = (try sema.namespaceLookup(block, src, namespace, decl_name)) orelse return null;
                                          ^
/home/david/dev/official-zig/src/Sema.zig:21906:48: 0x55d0afe81206 in Sema.fieldCallBind (zig2)
                if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| {
                                               ^
/home/david/dev/official-zig/src/Sema.zig:8521:30: 0x55d0afbe65df in Sema.zirFieldCallBind (zig2)
    return sema.fieldCallBind(block, src, object_ptr, field_name, field_name_src);
                             ^
/home/david/dev/official-zig/src/Sema.zig:759:71: 0x55d0afa3a971 in Sema.analyzeBodyInner (zig2)
            .field_call_bind              => try sema.zirFieldCallBind(block, inst),
                                                                      ^
/home/david/dev/official-zig/src/Sema.zig:611:30: 0x55d0afa2ca9a in Sema.analyzeBody (zig2)
    _ = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
                             ^
/home/david/dev/official-zig/src/Module.zig:5591:21: 0x55d0af830043 in Module.analyzeFnBody (zig2)
    sema.analyzeBody(&inner_block, fn_info.body) catch |err| switch (err) {
                    ^
/home/david/dev/official-zig/src/Module.zig:4291:40: 0x55d0af81096a in Module.ensureFuncBodyAnalyzed (zig2)
            var air = mod.analyzeFnBody(func, sema_arena) catch |err| switch (err) {
                                       ^
/home/david/dev/official-zig/src/Sema.zig:26405:36: 0x55d0aff5d773 in Sema.ensureFuncBodyAnalyzed (zig2)
    sema.mod.ensureFuncBodyAnalyzed(func) catch |err| {
                                   ^
/home/david/dev/official-zig/src/Sema.zig:28530:40: 0x55d0afe8770f in Sema.resolveInferredErrorSet (zig2)
        try sema.ensureFuncBodyAnalyzed(ies.func);
                                       ^
/home/david/dev/official-zig/src/Sema.zig:26684:49: 0x55d0afc7fc59 in Sema.analyzeIsNonErrComptimeOnly (zig2)
                try sema.resolveInferredErrorSet(block, src, ies);
                                                ^
/home/david/dev/official-zig/src/Sema.zig:26711:56: 0x55d0afe87045 in Sema.analyzeIsNonErr (zig2)
    const result = try sema.analyzeIsNonErrComptimeOnly(block, src, operand);
                                                       ^
/home/david/dev/official-zig/src/Sema.zig:15296:32: 0x55d0afbeb640 in Sema.zirIsNonErr (zig2)
    return sema.analyzeIsNonErr(block, inst_data.src(), operand);
                               ^
/home/david/dev/official-zig/src/Sema.zig:770:66: 0x55d0afa3b212 in Sema.analyzeBodyInner (zig2)
            .is_non_err                   => try sema.zirIsNonErr(block, inst),
                                                                 ^
/home/david/dev/official-zig/src/Sema.zig:4972:34: 0x55d0afe98f56 in Sema.resolveBlockBody (zig2)
        if (sema.analyzeBodyInner(child_block, body)) |_| {
                                 ^
/home/david/dev/official-zig/src/Sema.zig:4955:33: 0x55d0afc7c1dd in Sema.zirBlock (zig2)
    return sema.resolveBlockBody(parent_block, src, &child_block, body, inst, &label.merges);
                                ^
/home/david/dev/official-zig/src/Sema.zig:1250:69: 0x55d0afa45c75 in Sema.analyzeBodyInner (zig2)
                if (!block.is_comptime) break :blk try sema.zirBlock(block, inst);
                                                                    ^
/home/david/dev/official-zig/src/Sema.zig:4972:34: 0x55d0afe98f56 in Sema.resolveBlockBody (zig2)
        if (sema.analyzeBodyInner(child_block, body)) |_| {
                                 ^
/home/david/dev/official-zig/src/Sema.zig:9877:49: 0x55d0afbf6fc8 in Sema.zirSwitchBlock (zig2)
                    return sema.resolveBlockBody(block, src, &child_block, body, inst, merges);
                                                ^
/home/david/dev/official-zig/src/Sema.zig:792:69: 0x55d0afa3c358 in Sema.analyzeBodyInner (zig2)
            .switch_block                 => try sema.zirSwitchBlock(block, inst),
                                                                    ^
/home/david/dev/official-zig/src/Sema.zig:611:30: 0x55d0afa2ca9a in Sema.analyzeBody (zig2)
    _ = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
                             ^
/home/david/dev/official-zig/src/Sema.zig:6173:33: 0x55d0afe768dd in Sema.analyzeCall (zig2)
                sema.analyzeBody(&child_block, fn_info.body) catch |err| switch (err) {
                                ^
/home/david/dev/official-zig/src/Sema.zig:19793:28: 0x55d0afc2d923 in Sema.zirBuiltinCall (zig2)
    return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src);
                           ^
/home/david/dev/official-zig/src/Sema.zig:848:69: 0x55d0afa3ef9b in Sema.analyzeBodyInner (zig2)
            .builtin_call                 => try sema.zirBuiltinCall(block, inst),
                                                                    ^
/home/david/dev/official-zig/src/Sema.zig:611:30: 0x55d0afa2ca9a in Sema.analyzeBody (zig2)
    _ = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
                             ^
/home/david/dev/official-zig/src/Sema.zig:6173:33: 0x55d0afe768dd in Sema.analyzeCall (zig2)
                sema.analyzeBody(&child_block, fn_info.body) catch |err| switch (err) {
                                ^
/home/david/dev/official-zig/src/Sema.zig:5711:28: 0x55d0afbdd39c in Sema.zirCall (zig2)
    return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src);
                           ^
/home/david/dev/official-zig/src/Sema.zig:727:62: 0x55d0afa38fb5 in Sema.analyzeBodyInner (zig2)
            .call                         => try sema.zirCall(block, inst),
                                                             ^
/home/david/dev/official-zig/src/Sema.zig:611:30: 0x55d0afa2ca9a in Sema.analyzeBody (zig2)
    _ = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
                             ^
/home/david/dev/official-zig/src/Sema.zig:6173:33: 0x55d0afe768dd in Sema.analyzeCall (zig2)
                sema.analyzeBody(&child_block, fn_info.body) catch |err| switch (err) {
                                ^
/home/david/dev/official-zig/src/Sema.zig:19793:28: 0x55d0afc2d923 in Sema.zirBuiltinCall (zig2)
    return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src);
                           ^
/home/david/dev/official-zig/src/Sema.zig:848:69: 0x55d0afa3ef9b in Sema.analyzeBodyInner (zig2)
            .builtin_call                 => try sema.zirBuiltinCall(block, inst),
                                                                    ^
/home/david/dev/official-zig/src/Sema.zig:628:45: 0x55d0afa33c1c in Sema.analyzeBodyBreak (zig2)
    const break_inst = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
                                            ^
/home/david/dev/official-zig/src/Sema.zig:593:50: 0x55d0afe70860 in Sema.resolveBody (zig2)
    const break_data = (try sema.analyzeBodyBreak(block, body)) orelse
                                                 ^
/home/david/dev/official-zig/src/Sema.zig:5704:46: 0x55d0afbddab5 in Sema.zirCall (zig2)
        const resolved = try sema.resolveBody(block, args_body[arg_start..arg_end], inst);
                                             ^
/home/david/dev/official-zig/src/Sema.zig:727:62: 0x55d0afa38fb5 in Sema.analyzeBodyInner (zig2)
            .call                         => try sema.zirCall(block, inst),
                                                             ^
/home/david/dev/official-zig/src/Sema.zig:611:30: 0x55d0afa2ca9a in Sema.analyzeBody (zig2)
    _ = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
                             ^
/home/david/dev/official-zig/src/Module.zig:5591:21: 0x55d0af830043 in Module.analyzeFnBody (zig2)
    sema.analyzeBody(&inner_block, fn_info.body) catch |err| switch (err) {
                    ^
/home/david/dev/official-zig/src/Module.zig:4291:40: 0x55d0af81096a in Module.ensureFuncBodyAnalyzed (zig2)
            var air = mod.analyzeFnBody(func, sema_arena) catch |err| switch (err) {
                                       ^
/home/david/dev/official-zig/src/Compilation.zig:3047:42: 0x55d0af50071c in Compilation.processOneJob (zig2)
            module.ensureFuncBodyAnalyzed(func) catch |err| switch (err) {
                                         ^
/home/david/dev/official-zig/src/Compilation.zig:2985:30: 0x55d0af4ee46f in Compilation.performAllTheWork (zig2)
            try processOneJob(comp, work_item);
                             ^
/home/david/dev/official-zig/src/Compilation.zig:2325:31: 0x55d0af4e6b1d in Compilation.update (zig2)
    try comp.performAllTheWork(main_progress_node);
                              ^
/home/david/dev/official-zig/src/main.zig:3304:20: 0x55d0af4720ff in main.updateModule (zig2)
    try comp.update();
                   ^
/home/david/dev/official-zig/src/main.zig:2989:17: 0x55d0af3bf1e4 in main.buildOutputType (zig2)
    updateModule(gpa, comp, hook) catch |err| switch (err) {
                ^
/home/david/dev/official-zig/src/main.zig:238:31: 0x55d0af3627df in main.mainArgs (zig2)
        return buildOutputType(gpa, arena, args, .run);
                              ^
/home/david/dev/official-zig/src/stage1.zig:48:24: 0x55d0af361e59 in main (zig2)
        stage2.mainArgs(gpa, arena, args) catch unreachable;

david4r4 avatar Sep 30 '22 04:09 david4r4

I'm getting that now too. Is that the correct behavior do you think or?

ibokuri avatar Oct 03 '22 16:10 ibokuri

i ran into a similar issue. not 100% sure it is the same but seems related. here is a reproduction:

const std = @import("std");

fn foo(a: anytype) !void {
    if (a == 0) return error.A;
    return error.B;
}

const Error = error{ A, B };

test {
    const info = @typeInfo(@TypeOf(foo));
    const ret_type = info.Fn.return_type.?;
    const error_set = @typeInfo(ret_type).ErrorUnion.error_set;
    _ = Error || error_set;
}

This also crashes the compiler. Althought not from this repro, a similar debug error trace was shared in discord by @nektro

travisstaloch avatar Mar 17 '23 09:03 travisstaloch

@ibokuri 0.12.0-dev.2341+92211135 now returns an error message:

test.zig:28:51: error: unable to resolve inferred error set of generic function
    pub usingnamespace Writer(@This(), Ok, Error, write);
                                                  ^~~~~
test.zig:34:9: note: generic function declared here
    pub fn write(_: @This(), value: anytype) !Ok {
    ~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I'm not sure if this the correct behavior.

perillo avatar Jan 30 '24 13:01 perillo

I'd agree it's correct personally

nektro avatar Jan 30 '24 16:01 nektro

Closing as duplicate of #14991

@perillo it's nice that you're checking these old issues but just commenting that it is fixed is not all that useful if you don't also point to a test that covers it or add a new one.

Vexu avatar Jan 30 '24 19:01 Vexu