incorrect parsing of error set merge operator
Zig Version
0.13.0-dev.73+db890dbae
Steps to Reproduce and Observed Behavior
The following pieces of code fail zig ast-check (and thus compilation):
fn foo() error{A} || error{A} {
return error.A;
}
bad_parser.zig:1:19: error: expected ';' or block after function prototype
fn foo() error{A} || error{A} {
^~
const bar: error{A} || error{A} = error.A;
bad_parser.zig:1:21: error: expected ';' after declaration
const bar: error{A} || error{A} = error.A;
^~
field: error{A} || error{A},
bad_parser.zig:1:17: error: expected ',' after field
field: error{A} || error{A},
^~
Expected Behavior
All of the examples above should compile correctly.
It seems like the problem is with Zig's grammar itself (the implementation adheres to the specification, the specification is "wrong"). The construct TypeExpr in grammar.y does not handle error set merges.
I'm actually interested to hear why are Expr and TypeExpr distinct in the first place: types in Zig can be created from arbitrary expressions, every TypeExpr is a valid Expr, and every Expr surrounded in parentheses is a valid TypeExpr.
Fixing the bug is not urgent at all, since, as I mentioned, a parenthesised Expr is a TypeExpr - all of the compilation errors above are fixed if we replace error{A} || error{A} with (error{A} || error{A}).
I'm actually interested to hear why are Expr and TypeExpr distinct in the first place
I would have expected that to be a way to enforce the fact that the comptime unary operator is disallowed in known-comptime grammar contexts (https://github.com/ziglang/zig/issues/8364).
However if I understand correctly the chain TypeExpr -> ErrorUnionExpr -> SuffixExpr -> PrimaryTypeExpr -> KEYWORD_comptime shows that the grammar doesn't currently model this detail.
Would this also allow us to inline the operator inline in the return type?
// status quo
fn foo() (error{A} || error{B})!void {}
// would this issue allow
fn foo() error{A} || error{B}!void {}
@Pyrolistical probably not; according to the language reference the precedence of the a!b operator is higher than a || b, this means that the expression would be parsed as error{A} || (error{B}!void).
It might be nice to rethink the precedence of the type operators, IMO it would be more useful (code will require less parentheses) if a || b had higher precedence than a!b - but this is outside the scope of this issue...