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

Type inference through structs containing types #1993

Open
dnut opened this issue Aug 7, 2024 · 0 comments
Open

Type inference through structs containing types #1993

dnut opened this issue Aug 7, 2024 · 0 comments
Labels
enhancement New feature or request

Comments

@dnut
Copy link

dnut commented Aug 7, 2024

Sometimes it is useful to express a combination of generic types as a struct. For example, I use a struct like this in a key-value database that supports arbitrary types.

const ColumnFamily = struct {
    Key: type,
    Value: type,
    name: []const u8,
};

I wrote some generic functions that accept a ColumnFamily as an input, and use its contained types as the types of the parameters and return of the function. A simplified example:

pub fn get(db: Database, cf: ColumnFamily, key: cf.Key) ?cf.Value {
    return db.getStore(cf)).get(key);
}

This works great in zig, but zls has trouble inferring the type of any variable whose type is specified via ColumnFamily.

What Does Work?

Note: I have measured "successful type inference by zls" by checking that VS code shows a type hint for the variable.

ZLS has no trouble inferring types through layers of indirection, as long as they are supported forms of indirection.

type variable

You can assign a concrete type to a variable T of type type, and zls will successfully infer the concrete type of any variables that have their type specified as T

test "indirect type inference" {
    const T: type = u8;
    const start: T = 123;
    const item = start; // zls infers item as u8
    try std.testing.expect(u8 == @TypeOf(item));
}

generic function with type parameter

ZLS can infer the type returned from a generic function when the type is passed as a parameter to the function.

fn identity(comptime T: type, item: T) T {
    return item;
}

test "double indirect type inference via type param" {
    const T: type = u8;
    const item = identity(T, 123); // zls infers item as u8
    try std.testing.expect(u8 == @TypeOf(item));
}

generic function with anytype

The same also works when the parameter's type is inferred rather than being specified with a type parameter.

fn anyIdentity(item: anytype) @TypeOf(item) {
    return item;
}

test "indirect type inferece via anytype fn" {
    const start: u8 = 123;
    const item = anyIdentity(start); // zls infers item as u8
    try std.testing.expect(u8 == @TypeOf(item));
}

What Doesn't Work?

If all you do is wrap a type in a struct and directly specify it as the type of a variable (similar to the first example, but now with a wrapper struct), ZLS cannot infer the type:

const TypeSpec = struct { T: type };

test TypeSpec {
    const spec = TypeSpec{ .T = u8 };
    const start: spec.T = 123;
    const item = start;  // zls is unable to infer item's type here
    try std.testing.expect(u8 == @TypeOf(item));
}

Likewise, it does not work when passed through a function, either.

const TypeSpec = struct { T: type };

fn specIdentity(comptime spec: TypeSpec, item: spec.T) spec.T {
    return item;
}

test specIdentity {
    const spec = TypeSpec{ .T = u8 };
    const item = specIdentity(spec, 123);  // zls is unable to infer item's type here
    try std.testing.expect(u8 == @TypeOf(item));
}
@dnut dnut added the enhancement New feature or request label Aug 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant