Skip to content

wrong branching of if(optional) in anonymous list literal #4491

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
jakwings opened this issue Feb 17, 2020 · 4 comments
Closed

wrong branching of if(optional) in anonymous list literal #4491

jakwings opened this issue Feb 17, 2020 · 4 comments
Labels
bug Observed behavior contradicts documented or intended behavior miscompilation The compiler reports success but produces semantically incorrect code. stage1 The process of building from source via WebAssembly and the C backend.
Milestone

Comments

@jakwings
Copy link

jakwings commented Feb 17, 2020

version 5e37fc0 (master)

const std = @import("std");

fn testFmt(expected: var, actual: var) !void {
    var buf1: [512]u8 = undefined;
    var buf2: [512]u8 = undefined;
    const result1 = try std.fmt.bufPrint(buf1[0..], "{}", .{expected});
    const result2 = try std.fmt.bufPrint(buf2[0..], "{}", .{actual.@"0"});
    if (std.mem.eql(u8, result1, result2)) return;

    std.debug.warn("\n====== expected this output: =========\n", .{});
    std.debug.warn("{}", .{result1});
    std.debug.warn("\n======== instead found this: =========\n", .{});
    std.debug.warn("{}", .{result2});
    std.debug.warn("\n======================================\n", .{});
    return error.TestFailure;
}
fn foo(bar: var) void {}

test "wrong branching of if(optional) in anonymous list literal" {
    const T = usize;
    var x: ?T = null;
    const a: T = 1;
    const b: T = 2;
    const c: T = 3;

    // all pass
    _ = if (x != null) x.? else b;
    _ = if (x) |xx| xx else b;
    _ = if (x) |_| x.? else b;
    _ = if (x) |_| unreachable else b;

    // all pass
    std.testing.expectEqual(b, if (x != null) x.? else b);
    std.testing.expectEqual(b, if (x != null) a else b);
    std.testing.expectEqual(b, if (x) |xx| xx else b);
    std.testing.expectEqual(b, if (x) |xx| a else b);
    std.testing.expectEqual(b, if (x) |_| x.? else b);
    std.testing.expectEqual(b, if (x) |_| a else b);
    std.testing.expectEqual(b, if (x) |_| unreachable else b);

    // all pass
    std.testing.expectEqual([_]T{b}, [_]T{if (x != null) x.? else b});
    std.testing.expectEqual([_]T{b}, [_]T{if (x != null) a else b});
    std.testing.expectEqual([_]T{b}, [_]T{if (x) |xx| xx else b});
    std.testing.expectEqual([_]T{b}, [_]T{if (x) |xx| a else b});
    std.testing.expectEqual([_]T{b}, [_]T{if (x) |_| x.? else b});
    std.testing.expectEqual([_]T{b}, [_]T{if (x) |_| a else b});
    std.testing.expectEqual([_]T{b}, [_]T{if (x) |_| unreachable else b});

    // useful hint? error: cannot store runtime value in compile time variable
    //foo(.{x orelse b});

    try testFmt(b, .{if (x != null) x.? else b});
    try testFmt(b, .{if (x != null) a else b}); // failure
    try testFmt(b, .{if (x) |xx| xx else b});
    try testFmt(b, .{if (x) |xx| a else b}); // failure
    try testFmt(b, .{if (x) |_| x.? else b});
    try testFmt(b, .{if (x) |_| a else b}); // failure
    try testFmt(b, .{if (x) |_| unreachable else b});

    x = c;
    // all pass
    try testFmt(c, .{if (x != null) x.? else b});
    try testFmt(c, .{if (x) |xx| xx else b});
    try testFmt(c, .{if (x) |_| x.? else b});
    try testFmt(a, .{if (x) |_| a else b});
    try testFmt(a, .{if (x) |_| a else unreachable});
}
@andrewrk andrewrk added miscompilation The compiler reports success but produces semantically incorrect code. stage1 The process of building from source via WebAssembly and the C backend. bug Observed behavior contradicts documented or intended behavior labels Feb 18, 2020
@andrewrk andrewrk added this to the 0.6.0 milestone Feb 18, 2020
@jakwings
Copy link
Author

More cases:

test "wrong branching of if(runtime false) inside anonymous list literal" {
    const T = i32;
    var x: T = 0;
    const a: T = 0;
    const b: T = 1;

    try testFmt(a, .{if (true) a else b});
    try testFmt(b, .{if (false) a else b});
    try testFmt(a, .{if (yes(true)) a else b});
    try testFmt(b, .{if (no(false)) a else b}); // wrong
    try testFmt(b, .{if (no(false)) unreachable else b});
    //try testFmt(b, .{if (no(false)) a else unreachable}); // panic
    try testFmt(a, .{if (x == a) a else unreachable});
    try testFmt(b, .{if (x != a) unreachable else b});
    try testFmt(b, .{if (x == b) unreachable else b});
    try testFmt(a, .{if (x != b) a else unreachable});
    try testFmt(a, .{if (x == a) a else b});
    try testFmt(b, .{if (x != a) a else b}); // wrong
    try testFmt(b, .{if (x == b) a else b}); // wrong
    try testFmt(a, .{if (x != b) a else b});
    try testFmt(a, .{switch (x) { b => unreachable, a => a, else => unreachable }});
    try testFmt(a, .{switch (x) { b => b, a => a, else => unreachable }}); // wrong
    try testFmt(a, .{switch (x) { b => unreachable, a => a, else => b }}); // wrong
    try testFmt(a, .{switch (x) { a => a, b => unreachable, else => unreachable }});
    try testFmt(a, .{switch (x) { a => a, b => unreachable, else => b }}); // wrong
    try testFmt(a, .{switch (x) { a => a, b => b, else => unreachable }});
    try testFmt(a, .{switch (x) { a => a, b => b, else => b }}); // wrong
}
fn yes(t: var) bool {
    return true;
}
fn no(t: var) bool {
    return false;
}

@ghost
Copy link

ghost commented Mar 17, 2020

const std = @import("std");

pub fn main() void {
    var x = false;
    std.debug.warn("{}\n", .{if (x) true else false}); // wrong
    std.debug.warn("{}\n", .{if (!x) false else true}); // right
}
true
false

Is it always taking the first branch?

@andrewrk andrewrk modified the milestones: 0.6.0, 0.7.0 Apr 13, 2020
@andrewrk andrewrk modified the milestones: 0.7.0, 0.8.0 Oct 17, 2020
@andrewrk andrewrk modified the milestones: 0.8.0, 0.9.0 Nov 6, 2020
@giatorta
Copy link

const std = @import("std");

pub fn main() void {
var x = false;
std.debug.warn("{}\n", .{if (x) true else false}); // wrong
std.debug.warn("{}\n", .{if (!x) false else true}); // right
}


true
false

just a small addition: putting "1>2" (resp. "1<2") instead of "x" (resp. "!x") in the if condition works correctly, i.e. prints "false" twice

@Vexu
Copy link
Member

Vexu commented Dec 28, 2022

Duplicate of #3882

@Vexu Vexu marked this as a duplicate of #3882 Dec 28, 2022
@Vexu Vexu closed this as completed Dec 28, 2022
@andrewrk andrewrk modified the milestones: 0.12.0, 0.11.0 Dec 28, 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 miscompilation The compiler reports success but produces semantically incorrect code. stage1 The process of building from source via WebAssembly and the C backend.
Projects
None yet
Development

No branches or pull requests

4 participants