Skip to content

Compiler crash: runtime-conditional break/return inside inline loop #2727

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

Closed
CurtisFenner opened this issue Jun 22, 2019 · 14 comments
Closed
Labels
bug Observed behavior contradicts documented or intended behavior stage1 The process of building from source via WebAssembly and the C backend.
Milestone

Comments

@CurtisFenner
Copy link

I'm building using zig 0.4.0+fcc0728 on 64 bit Windows 10, built from source in Release mode.

The following file run through zig test causes Zig to exit with no output and only empty files written to the zig-cache:

fn crash() !void {
    return error.Crash;
}

test "A problem happens" {
    inline for ([_]u8{ 0, 1 }) |x| {
        if (crash()) {} else |z| {
            return;
        }
    }
}

The exit code, according to an echo %errorlevel%, is -1073741819. The Windows Event Viewer has the following log:

Exception code: 0xc0000005
Fault offset: 0x0000000001d20690
Faulting process id: 0xac8

The test input is seemingly minimal. It doesn't crash with only one/zero elements, it doesn't crash if the return is removed, replaced with other code, or moved to the other branch (though break also causes it). It doesn't crash with a regular if/else. It doesn't crash if replaced with try or with catch.

@SamTebbs33
Copy link
Contributor

I can't run this one windows but on Linux I get

Program received signal SIGSEGV, Segmentation fault.
0x000055555a509e30 in llvm::BasicBlock::getContext() const ()
(gdb) bt
#0  0x000055555a509e30 in llvm::BasicBlock::getContext() const ()
#1  0x000055555a5a2439 in llvm::BranchInst::BranchInst(llvm::BasicBlock*, llvm::BasicBlock*, llvm::Value*, llvm::Instruction*) ()
#2  0x000055555a537408 in LLVMBuildCondBr ()
#3  0x000055555645943b in ir_render_cond_br (g=0x55555c5ff840, executable=0x55555c65cfb8, cond_br_instruction=0x55555c87dc80) at /home/sam/repos/zig/src/codegen.cpp:3281
#4  0x0000555556463122 in ir_render_instruction (g=0x55555c5ff840, executable=0x55555c65cfb8, instruction=0x55555c87dc80) at /home/sam/repos/zig/src/codegen.cpp:5631
#5  0x0000555556463b43 in ir_render (g=0x55555c5ff840, fn_entry=0x55555c65ce60) at /home/sam/repos/zig/src/codegen.cpp:5813
#6  0x0000555556468f55 in do_code_gen (g=0x55555c5ff840) at /home/sam/repos/zig/src/codegen.cpp:7027
#7  0x00005555564723b0 in codegen_build_and_link (g=0x55555c5ff840) at /home/sam/repos/zig/src/codegen.cpp:9615
#8  0x000055555644a91a in main (argc=3, argv=0x7fffffffe868) at /home/sam/repos/zig/src/main.cpp:1190

@CurtisFenner
Copy link
Author

Thanks for confirming it's not Windows specific!

@CurtisFenner CurtisFenner changed the title zig test exits unsuccessfully in inline for loop over array of at least two elements with error-union-else on Windows zig test exits unsuccessfully in inline for loop over array of at least two elements with error-union-else Jun 22, 2019
@andrewrk andrewrk added this to the 0.5.0 milestone Jun 27, 2019
@andrewrk andrewrk added the bug Observed behavior contradicts documented or intended behavior label Jun 27, 2019
@ntgraff
Copy link

ntgraff commented Aug 2, 2019

I am having this same issue in the following code snippet, so I'm leaving it here to see if it is useful at all. The backtrace is basically the same.

// T is any type
const info = switch (@typeInfo(T)) {
    .Struct => |i| i,
    else => @compileError("not a struct"),
};

// json is std.json.ValueTree
const json_object = switch (json.root) {
    .Object => |o| o,
    else => return error.JsonNotObject,
};

inline for (info.fields) |field| {
    // This if causes the segfault when `T` has more than one field,
    // but works when there is one field, or if the else branch does not have a return
    const field_value = if (json_object.getValue(field.name)) |value| value else {
        return error.JsonMissingField;
    };
}

It seems that this happens when there is a chance of an early return within an inline for, probably related (or the same issue) as #2908.

zig version is 0.4.0+ceec2393c

@CurtisFenner
Copy link
Author

CurtisFenner commented Sep 9, 2019

This also appears to happen with a regular non-error switch:

const ResultType = union(enum) {
    Alpha: u32,
    Beta: u32,
};

test "foo" {
    inline for ([2]u32{ 1, 7 }) |field| {
        var result = ResultType{ .Alpha = 7 };
        switch (result) {
            .Alpha => {},
            .Beta => {},
        }
    }
}

zig version 0.4.0+9a18db8

@CurtisFenner
Copy link
Author

CurtisFenner commented Sep 9, 2019

Interestingly, adding an else unreachable in a regular if also causes a crash:

const ResultType = struct {
    tag: u2,
    Alpha: u32,
    Beta: u32,
};

test "foo" {
    inline for ([2]u32{ 1, 7 }) |field| {
        var result = ResultType{ .tag = 1, .Alpha = 7, .Beta = undefined };
        if (result.tag == 1) {} else unreachable;
    }
}

so perhaps this has something to do with the 'exhaustiveness' of these ifs.

@pixelherodev
Copy link
Contributor

I haven't done any real work on the compiler, but would be more than willing to set aside time to look into this.

Any tips on how to get started looking into a bug like this?

@andrewrk andrewrk added the stage1 The process of building from source via WebAssembly and the C backend. label Jan 8, 2020
@andrewrk
Copy link
Member

andrewrk commented Jan 8, 2020

@pixelherodev this might a particularly difficult bug to start with as a contributor.

Here's where we are collecting tips on how to do stage1 ir.cpp work: https://github.com/ziglang/zig/wiki/FAQ#are-there-any-good-examples-of-advanced-internals-development-with-zig-specifically-stage1-bug-fixes

You might look for label:"contributor friendly" label:stage1 for starters.


Confirmed that this is still an issue in master branch.

@andrewrk andrewrk modified the milestones: 0.6.0, 0.7.0 Jan 8, 2020
@pixelherodev
Copy link
Contributor

pixelherodev commented Jan 19, 2020

Can also confirm that this happens in an inline while, not just inline for, and only when there's a possibility of early branching (so a somewhat usable workaround is to set a bool and check it after the inline while.

Interestingly, despite using comptime comparisons, if I remove the inline from the while, it works flawlessly.

comptime var index = 0;
while (index < @memberCount(RefactorSettings)) : (index += 1) {
    ...
}

works, but if the while loop is made inline, the compiler crashes.

@pixelherodev
Copy link
Contributor

I take that back; of course it doesn't work, and I'm surprised it compiled.

It works if the only argument you use is the first one - that is, if index doesn't need to increment. Incrementing has, of course, no effect at runtime, so anything else, even just an invalid argument that's not in the structure, and it completely fails.

@travisstaloch
Copy link
Contributor

another repro on latest master 0.5.0+c522699f2 (Mon 20 Jan 2020)

test "inline while break bug repro" {
    inline for ([2]u8{ 0, 0 }) |_| {
        var x = false;
        if (x) {} else break;
    }
}

produces

$ zig test test1.zig 
Code Generation [1/918] Segmentation fault at address 0x0

Adding the else break triggers the segfault. if (x) break; does not segfault.

@ghost
Copy link

ghost commented May 31, 2021

Here's a reproduction involving a labelled block:

pub fn main() void {
    blk: {
        inline for ("hello") |_| {
            var x = true;
            if (x) break :blk;
        }
    }
}

@ghost
Copy link

ghost commented May 31, 2021

I think this issue should be renamed to something more general like "Compiler crash: runtime-conditional break/return inside inline loop"

@Vexu Vexu changed the title zig test exits unsuccessfully in inline for loop over array of at least two elements with error-union-else Compiler crash: runtime-conditional break/return inside inline loop Jun 3, 2021
@billzez
Copy link
Contributor

billzez commented Oct 4, 2021

I'm hitting this too. I first hit it with serialization code with an inline for, and I stripped it down to code that's nearly identical to above examples:

pub fn t() bool { return true; }

pub fn main() void {
    comptime var i: usize = 0;
    inline while (i < 2) : (i += 1) {
        if (t()) {} else return;
    }
}

@david-vanderson
Copy link
Contributor

Seems to be fixed somewhere between 0.10.0-dev.4060+61aaef0b0 and 0.10.0-dev.4179+884979278

wooster0 added a commit to wooster0/zig that referenced this issue Dec 7, 2022
wooster0 added a commit to wooster0/zig that referenced this issue Dec 7, 2022
wooster0 added a commit to wooster0/zig that referenced this issue Dec 7, 2022
wooster0 added a commit to wooster0/zig that referenced this issue Dec 7, 2022
wooster0 added a commit to wooster0/zig that referenced this issue Dec 8, 2022
wooster0 added a commit to wooster0/zig that referenced this issue Dec 9, 2022
@andrewrk andrewrk modified the milestones: 0.12.0, 0.11.0 Dec 10, 2022
kcbanner pushed a commit to kcbanner/zig that referenced this issue Dec 10, 2022
TUSF pushed a commit to TUSF/zig that referenced this issue May 9, 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 stage1 The process of building from source via WebAssembly and the C backend.
Projects
None yet
Development

No branches or pull requests

8 participants