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

proposal: enum switch narrowing #12863

Open
nektro opened this issue Sep 15, 2022 · 11 comments
Open

proposal: enum switch narrowing #12863

nektro opened this issue Sep 15, 2022 · 11 comments
Labels
proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Milestone

Comments

@nektro
Copy link
Contributor

nektro commented Sep 15, 2022

Zig Version

0.10.0-dev.3998+c25ce5bba

Steps to Reproduce

const std = @import("std");

const E = enum {
    a,
    b,
    c,
    d,
};

pub fn main() !void {
    var e: E = .a;

    switch (e) {
        .a => {},
        .b => {},
        else => |narrow| {
            switch (narrow) {
                .c => {},
                .d => {},
            }
        },
    }
}

Expected Behavior

successful build / run

Actual Behavior

test.zig:17:13: error: switch must handle all possibilities
            switch (narrow) {
            ^~~~~~
test.zig:4:5: note: unhandled enumeration value: 'a'
    a,
    ^
test.zig:5:5: note: unhandled enumeration value: 'b'
    b,
    ^
test.zig:3:11: note: enum 'test.E' declared here
const E = enum {
          ^~~~
./test.zig:17:13: error: enumeration value 'E.a' not handled in switch
            switch (narrow) {
            ^
./test.zig:17:13: error: enumeration value 'E.b' not handled in switch
            switch (narrow) {
            ^
@nektro nektro added the bug Observed behavior contradicts documented or intended behavior label Sep 15, 2022
@nektro nektro changed the title switch narrowing does not work with enums proposal: enum switch narrowing Sep 16, 2022
@andrewrk andrewrk added proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. and removed bug Observed behavior contradicts documented or intended behavior labels Sep 16, 2022
@andrewrk andrewrk added this to the 0.11.0 milestone Sep 16, 2022
@andrewrk
Copy link
Member

Related: #7224

@InKryption
Copy link
Contributor

what would @TypeOf(narrow) be?

@nektro
Copy link
Contributor Author

nektro commented Sep 16, 2022

a custom version of E with .a and .b removed, similar to how it works with error sets. since its a subset it coerces to E either implicitly or through @as

to get the non-narrowed version you would reference full or maybe switch (foo()) |bar| { could work too to get the E version

@tauoverpi
Copy link
Contributor

tauoverpi commented Sep 16, 2022

This could also work for integer ranges along with certain forms of if and while which guarantee the same. If e was const the capture could be alided as the switch narrows any const value it branches on (as was briefly discussed at the milan party). Also, if there is only a single value within the branch (captured or e is const) then that value is comptime known.

Though this would mean tracking range information within the type system (a bit like the any range integer proposal).

@InKryption
Copy link
Contributor

a custom version of E with .a and .b removed, similar to how it works with error sets. since its a subset it coerces to E either implicitly or through @as

Right, and what would @typeInfo(@TypeOf(narrow)) look like? Would this introduce a new class of type like an "enum subset"? If it did, maybe type info could be like:

pub const EnumSubset = struct {
    parent: type,
    field_indexes: []const usize,
};

where field_indexes are indexes into @typeInfo(info.parent).Enum.values.
This could also be useful beyond just narrowing.

@nektro
Copy link
Contributor Author

nektro commented Sep 16, 2022

no it would be as if the enum had been defined inline enum { c, d }

the other way to implement this, is to keep it of type E but then infer .a, .b => unreachable, in the inner switch

@InKryption
Copy link
Contributor

no it would be as if the enum had been defined inline enum { c, d }

But then how do you implement the coercion from the narrowed type to the super-type?

@nektro
Copy link
Contributor Author

nektro commented Sep 16, 2022

either implicitly or through @as

@InKryption
Copy link
Contributor

InKryption commented Sep 16, 2022

either implicitly or through @as

This doesn't really answer the question. If narrow is just a normal enum type that happens to have near-identical fields, then as far as type coercion is concerned, enums aren't allowed to just coerce to other enums, even if they overlap.

const Foo = enum { a, b, c };
const FooSub = enum { a, b };

comptime {
    _ = @as(Foo, FooSub.a); // error: expected type 'main.Foo', found 'main.FooSub'
}

@nektro
Copy link
Contributor Author

nektro commented Sep 16, 2022

ah, interesting catch. then implementation option b would probably be better, so long as the compile error was informative

edit: similar to the error you get with this code:

const E = enum { a, b };
test {
    var foo: E = .a;
    switch (foo) {
        .a => {},
        .a => {},
        .b => {},
    }
}
test.zig:6:10: error: duplicate switch value
        .a => {},
        ~^
test.zig:5:10: note: previous value here
        .a => {},
        ~^

but handling .a in the inner switch would say, note: already handled here pointing to the top switch since in that scope it knows .a and .b are unreachable if you're switching on narrow

@mitchellh
Copy link
Contributor

mitchellh commented Sep 26, 2024

I'd like to see this applied to inline cases as well. Example:

const E = enum { a, b, c };
var e: E = .a;
switch (e) {
  .a => {},
  inline .b, .c => |_, tag| switch (tag) {
    .b, .c => {},
  },
}

Today this is an error that tag is not exhaustive. I solve this with an else => comptime unreachable today.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Projects
None yet
Development

No branches or pull requests

5 participants