zig
zig copied to clipboard
Multiple orelse fail with compile time error under certain circumstances
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
isnull
, then the expressiona orelse b
will evaluate to the valueb
and when the valueb
isnull
, then the subsequent expressionorelse 0
will evaluate to the value `0'. - when the value
a
isnull
, then the expressiona orelse b
will evaluate to the valueb
and when the valueb
is nonnull
, then the subsequent expressionorelse 0
will be short circuited. - when the value
a
is nonnull
, then the expressiona orelse b
will evaluate to the valuea
and the subsequent expressionorelse 0
will be short circuited.
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;
}
a orelse b orelse 0
is parsed as (a orelse b) orelse 0
you want a orelse (b orelse 0)
, #15108 proposes changing that.