zig icon indicating copy to clipboard operation
zig copied to clipboard

Multiple orelse fail with compile time error under certain circumstances

Open houghtonap opened this issue 9 months ago • 1 comments

Zig Version

0.12.0

Steps to Reproduce and Observed Behavior

The following code produces the compile time error message under certain circumstances. I think I can see the compiler's reasoning for the compile time error, but it is not the behaviour I would expect from the expression. On the two lines the compiler issues errors on, the value of a is non null, so the expression a orelse b evaluates to a u32, then the subsequent expression orelse 0 fails because the prior expression evaluated to a u32 instead of a ?u32.

Command executed and compiler results

zig test test-orelse.zig
test-orelse.zig:24:31: error: expected optional type, found 'u32'       
    const c: u32 = a orelse b orelse 0; // fails with compile time error; adding parenthesis around (a orelse b) orelse 0 has no effect
                   ~~~~~~~~~~~^~~~~~~~
test-orelse.zig:41:31: error: expected optional type, found 'u32'       
    const c: u32 = a orelse b orelse 0; // fails with compile time error; adding parenthesis around (a orelse b) orelse 0 has no effect
                   ~~~~~~~~~~~^~~~~~~~

Source code

const std = @import("std");
const debug = std.debug;
const print = debug.print;

test "[orelse a==null and b==null]" {
    const a: ?u32 = null;
    const b: ?u32 = null;
    const c: u32 = a orelse b orelse 0;
    print("\na = {?d}\nb = {?d}\nc = {?d}\n", .{ a, b, c });
    return;
}

test "[orelse a==null and b!=null]" {
    const a: ?u32 = null;
    const b: ?u32 = 2;
    const c: u32 = a orelse b orelse 0;
    print("\na = {?d}\nb = {?d}\nc = {?d}\n", .{ a, b, c });
    return;
}

test "[orelse a!=null and b==null]" {
    const a: ?u32 = 1;
    const b: ?u32 = null;
    const c: u32 = a orelse b orelse 0; // fails with compile time error; adding parenthesis around (a orelse b) orelse 0 has no effect
    print("\na = {?d}\nb = {?d}\nc = {?d}\n", .{ a, b, c });
    return;
}

test "[orelse a!=null and b==null workaround]" {
    const a: ?u32 = 1;
    const b: ?u32 = null;
    const c: ?u32 = a orelse b;
    const d: u32 = c orelse 0;
    print("\na = {?d}\nb = {?d}\nd = {?d}\n", .{ a, b, d });
    return;
}

test "[orelse a!=null and b!=null]" {
    const a: ?u32 = 1;
    const b: ?u32 = 2;
    const c: u32 = a orelse b orelse 0; // fails with compile time error; adding parenthesis around (a orelse b) orelse 0 has no effect
    print("\na = {?d}\nb = {?d}\nc = {?d}\n", .{ a, b, c });
    return;
}

test "[orelse a!=null and b!=null workaround]" {
    const a: ?u32 = 1;
    const b: ?u32 = 2;
    const c: ?u32 = a orelse b;
    const d: u32 = c orelse 0;
    print("\na = {?d}\nb = {?d}\nd = {?d}\n", .{ a, b, d });
    return;
}

Expected Behavior

Given the expression a orelse b orelse 0 my expectation is:

  • when the value a is null, then the expression a orelse b will evaluate to the value b and when the value b is null, then the subsequent expression orelse 0 will evaluate to the value `0'.
  • when the value a is null, then the expression a orelse b will evaluate to the value b and when the value b is non null, then the subsequent expression orelse 0 will be short circuited.
  • when the value a is non null, then the expression a orelse b will evaluate to the value a and the subsequent expression orelse 0 will be short circuited.

houghtonap avatar May 07 '24 11:05 houghtonap

I don't think this is an issue. This is caused by the compiler knowing a more specific type for const. IMO it is working as intended. This compiles fine,

test "[orelse a!=null and b==null]" {
    var a: ?u32 = 1;
    _ = &a;
    var b: ?u32 = null;
    _ = &b;
    const c: u32 = a orelse b orelse 0;
    print("\na = {?d}\nb = {?d}\nc = {?d}\n", .{ a, b, c });
    return;
}

test "[orelse a!=null and b!=null]" {
    var a: ?u32 = 1;
    _ = &a;
    var b: ?u32 = 2;
    _ = &b;
    const c: u32 = a orelse b orelse 0;
    print("\na = {?d}\nb = {?d}\nc = {?d}\n", .{ a, b, c });
    return;
}

Pyrolistical avatar May 08 '24 22:05 Pyrolistical

a orelse b orelse 0 is parsed as (a orelse b) orelse 0 you want a orelse (b orelse 0), #15108 proposes changing that.

Vexu avatar May 31 '24 17:05 Vexu