Skip to content

fails to compile return of optional in certain conditionals #10601

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
MichaelBelousov opened this issue Jan 16, 2022 · 4 comments
Closed

fails to compile return of optional in certain conditionals #10601

MichaelBelousov opened this issue Jan 16, 2022 · 4 comments
Labels
bug Observed behavior contradicts documented or intended behavior

Comments

@MichaelBelousov
Copy link

MichaelBelousov commented Jan 16, 2022

Zig Version

0.10.0-dev.290+3901b6fb0

Steps to Reproduce

  1. write the following file (NOTE: an edit below contains a smaller reproduction):
// works if I make it return not optional
fn f(use: bool, param: u32) ?u32 {
    var val: u32 = 0;
    return switch (use) {
        true => (               // SwitchProng
            if (param == 1)
                @as(u32, 2)     // Then
            else if (val == 3)  // Else
                    @as(u32, 4) // Then1 ; here is where a return value address is created
                else
                    @as(u32, 5) // Else2 ; here is where the return value address from the other branch is mistakenly consumed
        ),
        else =>                 // SwitchElse
            @as(u32, 6)
    };
}

pub fn main() void {
    _ = f(true, 7);
}
  1. build it, I use:
zig build-exe file.zig

Expected Behavior

for it to compile as valid code

Actual Behavior

the compiler crashes:

broken LLVM module found: Instruction does not dominate all uses!
  %19 = getelementptr inbounds %"?u32", %"?u32"* %0, i32 0, i32 0
  store i32 5, i32* %19, align 4
Instruction does not dominate all uses!
  %19 = getelementptr inbounds %"?u32", %"?u32"* %0, i32 0, i32 0
  store i32 5, i32* %19, align 4
Instruction does not dominate all uses!
  %19 = getelementptr inbounds %"?u32", %"?u32"* %0, i32 0, i32 0
  store i32 %20, i32* %19, align 4

This is a bug in the Zig compiler.thread 2653385 panic: 
???:?:?: 0x55c02598cad7 in ??? (???)
???:?:?: 0x55c026924719 in ??? (???)
???:?:?: 0x55c02694b9e9 in ??? (???)
???:?:?: 0x55c026950b6f in ??? (???)
???:?:?: 0x55c026920273 in ??? (???)
???:?:?: 0x55c025d8fde4 in ??? (???)
???:?:?: 0x55c025d5068a in ??? (???)
???:?:?: 0x55c025b24205 in ??? (???)
???:?:?: 0x55c025b17432 in ??? (???)
???:?:?: 0x55c025b12fd1 in ??? (???)
???:?:?: 0x55c025aa15ff in ??? (???)
???:?:?: 0x55c0259bb486 in ??? (???)
???:?:?: 0x55c02598b097 in ??? (???)
???:?:?: 0x55c02598aaa9 in ??? (???)

EDIT: smaller reproduction:

fn f(param: u32) ?u32 {
    return if (param == 1) @as(u32, 2)
    else if (param == 2) @as(u32, 4)
    else if (param == 3) @as(u32, 5)
    else @as(u32, 6);
}

pub fn main() void { _ = f(7); }
@MichaelBelousov MichaelBelousov added the bug Observed behavior contradicts documented or intended behavior label Jan 16, 2022
@MichaelBelousov
Copy link
Author

MichaelBelousov commented Jan 16, 2022

I analyzed the llvm IR, but failed to debug further than that. Here is the relevant IR logs:

ZIR:

// same as "Then1" in in llvm-ir
Then_74:
    #79 | SrcResetResult        | (unknown)   | - | ResetResult(none)
    #80 | SrcConst              | type        | 2 | u32
    #81 | SrcEndExpr            | (unknown)   | - | EndExpr(result=none,value=#80)
    #82 | SrcResetResult        | (unknown)   | - | ResetResult(cast(ty=#80))
    #83 | SrcConst              | comptime_int| 2 | 4
    #84 | SrcEndExpr            | (unknown)   | - | EndExpr(result=cast(ty=#80),value=#83)
    #85 | SrcImplicitCast       | (unknown)   | 2 | @implicitCast(#83)result=cast(ty=#80)
    #86 | SrcEndExpr            | (unknown)   | - | EndExpr(result=peer(next=$Else_75),value=#85)
    #87 | SrcBr                 | (unknown)   | - | goto $EndIf_76 // comptime = #73
// same as "Else2" in in llvm-ir
// seems to be exactly the same as the then clause, so the issue appears to be
// in generating the 'optional return' AIR, aka must be in an ir_analyze function
// it's in analyze_fn_ir/
Else_75:
    #88 | SrcResetResult        | (unknown)   | - | ResetResult(none)
    #89 | SrcConst              | type        | 2 | u32
    #90 | SrcEndExpr            | (unknown)   | - | EndExpr(result=none,value=#89)
    #91 | SrcResetResult        | (unknown)   | - | ResetResult(cast(ty=#89))
    #92 | SrcConst              | comptime_int| 2 | 5
    #93 | SrcEndExpr            | (unknown)   | - | EndExpr(result=cast(ty=#89),value=#92)
    #94 | SrcImplicitCast       | (unknown)   | 2 | @implicitCast(#92)result=cast(ty=#89)
    #95 | SrcEndExpr            | (unknown)   | - | EndExpr(result=peer(next=$EndIf_76),value=#94)
    #96 | SrcBr                 | (unknown)   | - | goto $EndIf_76 // comptime = #73

resumed portion of analysis log:

~ /home/mike/personal/sizr/sizr-format-zig/src/code.zig:9:30
~ #84 | SrcEndExpr            | (unknown)   | - | EndExpr(result=cast(ty=#80),value=#83)
-> #118| GenConst              | void        | 0 | {}
~ /home/mike/personal/sizr/sizr-format-zig/src/code.zig:9:21
~ #85 | SrcImplicitCast       | (unknown)   | 2 | @implicitCast(#83)result=cast(ty=#80)
-> #119| GenConst              | u32         | 0 | 4
~ /home/mike/personal/sizr/sizr-format-zig/src/code.zig:9:21
~ #86 | SrcEndExpr            | (unknown)   | - | EndExpr(result=peer(next=$Else_75),value=#85)
-> #121| GenConst              | void        | 0 | {}
~ /home/mike/personal/sizr/sizr-format-zig/src/code.zig:8:18
~ #87 | SrcBr                 | (unknown)   | - | goto $EndIf_76 // comptime = #73
append new bb Then_85
resume (6,5) Else_75 #93
-> (noreturn)
~ /home/mike/personal/sizr/sizr-format-zig/src/code.zig:11:30
~ #93 | SrcEndExpr            | (unknown)   | - | EndExpr(result=cast(ty=#89),value=#92)
-> #126| GenConst              | void        | 0 | {}
~ /home/mike/personal/sizr/sizr-format-zig/src/code.zig:11:21
~ #94 | SrcImplicitCast       | (unknown)   | 2 | @implicitCast(#92)result=cast(ty=#89)
-> #127| GenConst              | u32         | 0 | 5
~ /home/mike/personal/sizr/sizr-format-zig/src/code.zig:11:21
~ #95 | SrcEndExpr            | (unknown)   | - | EndExpr(result=peer(next=$EndIf_76),value=#94)
-> #129| GenConst              | void        | 0 | {}
~ /home/mike/personal/sizr/sizr-format-zig/src/code.zig:8:18
~ #96 | SrcBr                 | (unknown)   | - | goto $EndIf_76 // comptime = #73
append new bb Else_86

AIR which is equivalent to the outputted LLVM IR (has the same undominated instructions on one branch)

Then_85:
    #115| GenOptionalUnwrapPtr  | *u32        | - | &#1.*.? // no safety
    #117| GenStorePtr           | void        | - | *#115 = #116
    :116| GenConst              | u32         | 1 | 4
    #120| GenStorePtr           | void        | - | *#115 = #119
    :119| GenConst              | u32         | 2 | 4
    #122| GenBr                 | noreturn    | - | goto $EndIf_100
Else_86:
// it looks like this corresponds exactly to the llvm labels
// #115 is the same as %19, being reused even though it isn't
// dominated
    #125| GenStorePtr           | void        | - | *#115 = #124
    :124| GenConst              | u32         | 1 | 5
    #128| GenStorePtr           | void        | - | *#115 = #127
    :127| GenConst              | u32         | 2 | 5
    #130| GenBr                 | noreturn    | - | goto $EndIf_100

produced LLVM IR

Then1:                                            ; preds = %Else
  %18 = getelementptr inbounds %"?u32", %"?u32"* %0, i32 0, i32 1
  store i1 true, i1* %18, align 1
  %19 = getelementptr inbounds %"?u32", %"?u32"* %0, i32 0, i32 0
  store i32 4, i32* %19, align 4
  store i32 4, i32* %19, align 4
  br label %EndIf

Else2:                                            ; preds = %Else
  store i32 5, i32* %19, align 4
  store i32 5, i32* %19, align 4
  br label %EndIf

I was able to compile the output llvm ir with llc by adding a separate getelementptr instruction to the Else2 branch

@MichaelBelousov MichaelBelousov changed the title fails to compile nested if inside switch fails to compile optional return in nested if inside switch Jan 16, 2022
@MichaelBelousov MichaelBelousov changed the title fails to compile optional return in nested if inside switch fails to compile return of optional in nested if inside switch Jan 16, 2022
@MichaelBelousov
Copy link
Author

MichaelBelousov commented Jan 16, 2022

If someone who can fix this faster than me isn't available, I can work on a pull request slowly.

@MichaelBelousov MichaelBelousov changed the title fails to compile return of optional in nested if inside switch fails to compile return of optional in certain conditionals Jan 23, 2022
@MichaelBelousov
Copy link
Author

MichaelBelousov commented Jan 23, 2022

here's an additional smaller reproduction:

fn f(param: u32) ?u32 {
    return if (param == 1) @as(u32, 2)
    else if (param == 2) @as(u32, 4)
    else if (param == 3) @as(u32, 5)
    else @as(u32, 6);
}

pub fn main() void { _ = f(7); }

I much better understand what's going on in the compiler that triggers this, but not enough yet to produce a fix. still slowly reading through the analysis code to figure it out

@Vexu
Copy link
Member

Vexu commented Jan 29, 2022

Can be worked around by casting to ?u32, duplicate of #3750, fixed in stage2.

@Vexu Vexu closed this as completed Jan 29, 2022
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
Projects
None yet
Development

No branches or pull requests

2 participants