Skip to content
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

no error on invalid try before anon struct #21991

Closed
travisstaloch opened this issue Nov 15, 2024 · 3 comments · Fixed by #22707
Closed

no error on invalid try before anon struct #21991

travisstaloch opened this issue Nov 15, 2024 · 3 comments · Fixed by #22707
Assignees
Labels
bug Observed behavior contradicts documented or intended behavior frontend Tokenization, parsing, AstGen, Sema, and Liveness. regression It worked in a previous version of Zig, but stopped working.
Milestone

Comments

@travisstaloch
Copy link
Contributor

Zig Version

0.14.0-dev.2126+e27b4647d

Steps to Reproduce and Observed Behavior

The compiler doesn't report an error message here and these 3 tests all pass.

const std = @import("std");

test {
    const S = struct { a: u8 };
    const s: S = try .{ .a = 0 }; // try makes no sense here
    try std.testing.expectEqual(0, s.a);
}

I originally noticed this while refactoring some code and forgot to remove try from a switch statement, similar to these

test {
    const S = struct { a: u8 };
    const s: S = switch (0) {
        0 => try .{ .a = 0 }, // try makes no sense here
        else => unreachable,
    };
    try std.testing.expectEqual(0, s.a);
}

test {
    const S = struct { a: u8 };
    const s: S = if (true)
        try .{ .a = 0 } // try makes no sense here
    else
        unreachable;

    try std.testing.expectEqual(0, s.a);
}

Expected Behavior

I would expect an error message like this:

test {
    const S = struct { a: u8 };
    const s = try S{ .a = 0 };
    try std.testing.expectEqual(0, s.a);
}
$ zig test /tmp/tmp.zig
/tmp/tmp.zig:5:23: error: expected error union type, found 'tmp.test_0.S'
    const s: S = try S{ .a = 0 };
                     ~^~~~~~~~~~
/tmp/tmp.zig:4:15: note: struct declared here
    const S = struct { a: u8 };
              ^~~~~~~~~~~~~~~~
@travisstaloch travisstaloch added the bug Observed behavior contradicts documented or intended behavior label Nov 15, 2024
@Vexu Vexu added the frontend Tokenization, parsing, AstGen, Sema, and Liveness. label Nov 15, 2024
@Vexu Vexu added this to the 0.14.0 milestone Nov 15, 2024
@pierrelgol
Copy link

I can reproduce this with 0.14.0-dev.2245+4fc295dc0. In fact its more silly than that this compiles just fine.

const std = @import("std");

test {
    const S = struct { a: u8 };
    const s: S = try try try try try try try try try try .{ .a = 0 }; // try makes no sense here
    try std.testing.expectEqual(0, s.a);
}

pub fn main() void {}

@mlugg
Copy link
Member

mlugg commented Nov 18, 2024

This is caused by a combination of two things:

  • try provides a result type to its operand, in this case anyerror!S
  • .{ ... } aggregate initialization syntax can "strip off" error unions from the result type; this is important for e.g. return .{ ... } in errorable functions

I think this issue indicates that try should not provide a result type; that is, #19777 should be reverted. The concrete reasoning is that if the operand to try is not already an error union, it is incorrect to use try, but providing a result type to the operand masks this by applying a coercion which always turns the operand into an error union. (As a side note, this issue also shows that the current implementation of this is clearly incorrect, because this error union result type for the try operand isn't applied in simpler cases, e.g. const x: u8 = try 1, hence exposing an implementation detail.)

It's still useful to be able to use try in conjunction with Decl Literals, so that can be allowed as a syntactic special case.

@mathieu-suen-sonarsource

Maybe a naive question but why not disallowing the syntax try <Literal value> ?

@mlugg mlugg added the regression It worked in a previous version of Zig, but stopped working. label Jan 31, 2025
@mlugg mlugg self-assigned this Jan 31, 2025
mlugg added a commit to mlugg/zig that referenced this issue Feb 1, 2025
This commit effectively reverts 9e683f0, and hence un-accepts ziglang#19777.
While nice in theory, this proposal turned out to have a few problems.

Firstly, supplying a result type implicitly coerces the operand to this
type -- that's the main point of result types! But for `try`, this is
actually a bad idea; we want a redundant `try` to be a compile error,
not to silently coerce the non-error value to an error union. In
practice, this didn't always happen, because the implementation was
buggy anyway; but when it did, it was really quite silly. For instance,
`try try ... try .{ ... }` was an accepted expression, with the inner
initializer being initially coerced to `E!E!...E!T`.

Secondly, the result type inference here didn't play nicely with
`return`. If you write `return try`, the operand would actually receive
a result type of `E!E!T`, since the `return` gave a result type of `E!T`
and the `try` wrapped it in *another* error union. More generally, the
problem here is that `try` doesn't know when it should or shouldn't
nest error unions. This occasionally broke code which looked like it
should work.

So, this commit prevents `try` from propagating result types through to
its operand. A key motivation for the original proposal here was decl
literals; so, as a special case, `try .foo(...)` is still an allowed
syntax form, caught by AstGen and specially lowered. This does open the
doors to allowing other special cases for decl literals in future, such
as `.foo(...) catch ...`, but those proposals are for another time.

Resolves: ziglang#21991
Resolves: ziglang#22633
@mlugg mlugg closed this as completed in 3924f17 Feb 1, 2025
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 frontend Tokenization, parsing, AstGen, Sema, and Liveness. regression It worked in a previous version of Zig, but stopped working.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants