Asserting function signatures at compile time in Zig

Feel the power of comptime

As a way of introducing myself to Zig, I've been writing a virtual machine based on the LC-3 ISA. In Zig, there are few fun features baked in but overall it feels like a focused attempt at a modern C while maintaining simplicity.

An insight that Zig has made apparent to me, through comptime, is that by exposing what should be known at compile time as a feature in the language itself you can now combine this with other primitives for interesting effects. The popular result of this being generics. It reminds me of macro libraries that combine peculiar parts of a language to create what seems like new features.

In writing the VM, I needed a way of asserting that the functions I wrote to virtualize the assembly operations all abided by the same signature. And I wanted to assert this at compile time. By combining comptime with Zig's builtin reflection functions, I was able to write a simple function that allowed me check signature details of a function:

fn assert_instr_fn(comptime instr_fn: type) void {
try std.testing.expect(@typeInfo(instr_fn).Fn.args.len == 1);
try std.testing.expect(@typeInfo(instr_fn).Fn.args[0].arg_type.? == u16);
}

comptime {
assert_instr_fn(@TypeOf(stOp));
assert_instr_fn(@TypeOf(trapOp));
// ...more compile time assertions...
}

The errors aren't pretty when they happen but they force me to look at what went wrong and I can potentially use a catch to give more descriptive errors.

An alternative I realized later is that if I have a canonical reference function I can simplify the function to assert against that type:

fn assert_instr_fn(comptime instr_fn: type) void {
try std.testing.expectEqual(@typeInfo(instr_fn), @typeInfo(@TypeOf(canonicalReferenceFn)));
}

If you see any inaccuracies, typos or have comments, please feel welcomed to reach out @mdaverde.