A better way of handling unresolvable error set is needed

In the code below, the return type of a() is not resolvable:

const std = @import("std");

fn a(f: anytype) !void {
    try f();
}

pub fn main() void {
    const f = @typeInfo(@TypeOf(a)).Fn;
    if (f.return_type) |RT| {
        const eu = @typeInfo(RT).ErrorUnion;
        const es = @typeInfo(eu.error_set);
        std.debug.print("{any}\n", .{es});
    }
}

We do not know what errors f() can return. Currently, the compiler would just error out with the message “unable to resolve inferred error set of generic function” when you call @typeInfo() on the error set. Clearly, this is a problem since currently there’s no way to detect whether the call would blow up on you or not. Given that @typeInfo().ErrorSet is an optional already, but isn’t it just returning null when the set cannot be determined?

Do you mean this, from a context of writing a library that wants to know at comptime whether a function provided to it will work and whether that functions error set will be able to be resolved automatically?

I think it may be a nice thing, to be able to react to some given function, but it also seems like it is the responsibility of the user to provide functions where the error can be inferred, or to declare the error set explicitly.

I also feel like I maybe miss some context here, can you provide some example of when and how you would want to “catch” an “can not be inferred error” and what it would look like to handle it?

My personal assumption has been so far that libraries need to declare their error sets more explicitly so that they can avoid some of this, possibly by comptime parameters or even by having the user supply an error set. Basically creating clear boundaries with well defined and explicit error sets.

But there are probably APIs that would want to keep the error set as inferred and I would imagine that these would have difficulties in knowing when the inference works and when it does not.

I guess if the library was able to detect the error and use that information to pick a different implementation, the library author and user wouldn’t be forced to abandon implicit error sets.

So I guess a part of this would be, to have precise documentation about what can be inferred and what causes that to stop working, maybe even have some way to declare that some function doesn’t cause something that makes it unable to infer. Or possibly a way to test for compile errors at comptime and use that to change what gets compiled, but that sounds like that would be difficult to implement without slowing down build times.

But I haven’t thought that deep about it.

2 Likes

This would be a good addition to the ZLS, by the way: a tool which pops up (in VScode, same principle in other editors) a lightbulb when you’ve selected a function signature, volunteering to expand the inferred error set of the function.

I’d say that the main thing keeping library authors from annotating error sets is that it’s drudgery. The easiest way I’ve found is to replace !T with error{E}!T and copy-paste the compiler error, which is an annoying way to program.

1 Like

Well, my code wouldn’t actually do anything with such a function. It’s just collecting type info that a routine might need later. It was just looking around and stepped on a landmine meant for someone else, so to speak.

As far as I know anyerror is the only error set that would cause @typeInfo().ErrorSet to be null. Is there really a huge difference between “any” and “some sort of”?