zig icon indicating copy to clipboard operation
zig copied to clipboard

incorrect parsing of error set merge operator

Open Fri3dNstuff opened this issue 1 year ago • 2 comments

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}).

Fri3dNstuff avatar May 07 '24 15:05 Fri3dNstuff

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.

rohlem avatar May 07 '24 16:05 rohlem

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 avatar May 09 '24 19:05 Pyrolistical

@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...

Fri3dNstuff avatar May 10 '24 03:05 Fri3dNstuff