Skip to content
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

Inferred error sets + generic interfaces = stage2 crashes #12806

Closed
ibokuri opened this issue Sep 10, 2022 · 6 comments
Closed

Inferred error sets + generic interfaces = stage2 crashes #12806

ibokuri opened this issue Sep 10, 2022 · 6 comments
Labels
bug Observed behavior contradicts documented or intended behavior frontend Tokenization, parsing, AstGen, Sema, and Liveness.
Milestone

Comments

@ibokuri
Copy link
Contributor

ibokuri commented Sep 10, 2022

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 ibokuri added the bug Observed behavior contradicts documented or intended behavior label Sep 10, 2022
@Vexu Vexu added the frontend Tokenization, parsing, AstGen, Sema, and Liveness. label Sep 10, 2022
@Vexu Vexu added this to the 0.10.0 milestone Sep 10, 2022
@davidgm94
Copy link
Contributor

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;

@ibokuri
Copy link
Contributor Author

ibokuri commented Oct 3, 2022

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

@andrewrk andrewrk modified the milestones: 0.10.0, 0.10.1 Oct 12, 2022
@ibokuri ibokuri changed the title Inferred error sets + generic interfaces = stage2 compilations that run forever Inferred error sets + generic interfaces = stage2 crashes Oct 21, 2022
@andrewrk andrewrk modified the milestones: 0.10.1, 0.11.0 Jan 10, 2023
@travisstaloch
Copy link
Sponsor Contributor

travisstaloch commented Mar 17, 2023

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

@perillo
Copy link
Contributor

perillo commented Jan 30, 2024

@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.

@nektro
Copy link
Contributor

nektro commented Jan 30, 2024

I'd agree it's correct personally

@Vexu
Copy link
Member

Vexu commented Jan 30, 2024

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 Vexu closed this as completed Jan 30, 2024
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 frontend Tokenization, parsing, AstGen, Sema, and Liveness.
Projects
None yet
Development

No branches or pull requests

7 participants