Skip to content

Instantly share code, notes, and snippets.

@marler8997
Created December 13, 2020 18:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marler8997/9e1dd332c7a60c4caeca96931345c58d to your computer and use it in GitHub Desktop.
Save marler8997/9e1dd332c7a60c4caeca96931345c58d to your computer and use it in GitHub Desktop.

Zig Compiler implementation proposal

DISCLAIMER: This is not a language feature proposal, rather, it is a compiler implemenation proposal.

Generic-Aware Error Messages

Problem: Today, in order to provide useful error messages, sometimes we add extra code to check generic values before using them. This extra codes causes issues because it must be kept in sync with the implementation. It's sole purpose is to provide better compile error messages which adds no functionality to the code itself.

Idea: Could we provide good error messages without requiring this extra code that checks generic types?

TODO: find examples of this "check code"

Note the difference in intention when using a generic value and a concrete value. When using a concrete value, if there is a problem, the problem is with the code using the value. However, when using a generic value, the problem could be the code using the generic value, or the code that instantiated the generic type. This "check" code aims to distiguish between these kinds of errors (demonstrate what I just said with an example).

Examples

Let's look at the format function in std/fmt.zig. Suppose the user incorrectly calls it like this:

try std.fmt.format(std.io.getStdOut().writer(), "Hello, {}!", "World");

Forgetting to wrap the format arguements in a tuple with .{} is a common mistake, so it's important to provide a good error message when this happens. To address this, Zig has added some code to verify whether the argument is a struct and asserts a @compileError when it isn't:

pub fn format(
...
    const ArgsType = @TypeOf(args);
    if (@typeInfo(ArgsType) != .Struct) {
        @compileError("Expected tuple or struct argument, found " ++ @typeName(ArgsType));
    }
...

So the code above would yeild the following error:

lib/zig/std/fmt.zig:92:9: error: Expected tuple or struct argument, found *const [5:0]u8
        @compileError("Expected tuple or struct argument, found " ++ @typeName(ArgsType));
...

The code that checks the generic types is what separates "implemenation errors" from "invalid generic type from the user" errors. However, this extra "check code" can itself have errors, and means that it needs to be kept in sync with the implementation if it is to distinguish these errors correctly.

Another way the compiler could

passed in a string literal to format, then we'd get an error such as: Compile sees this: @field(args, ...).

fmt.zig:1234:23: error: no field named "_0" in '*const [N:0]u8`

This error requires the user to go into the generic code inside fmt.zig and trace back the invalid usage to the invalid type that was give to the generic function. This adds alot of work to figuring out the problem which is just that this function doesn't accept strings, it only accepts structs.

std/fmt.zig

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment