I propose to add a builtin function that looks something like this: @illegal(x: bool) void
EDIT: actually i meant the following signature: @illegal(x: anytype) void
It asserts that the given expression triggers safety checked illegal behavior.
It’s a no-op in unchecked built modes.
Furthermore, this builtin could have arguments to indicate which type of illegal behaviour should trigger, in which function …
I’m not sure if an idea similiar to this one was already discussed, please give a link in that case.
no, my proposal does not assert that the expression is true, but that the expression itself triggers illegal behaviour somewhere. (the argument x could actually be of any type)
to document behaviour. just like you do with normal asserts, but to ensure expected illegal beviour is actually illegal.
eg. in a test:
// this documents that key must be absent from the map for a call to insert_unique(key, val) to be legal.
map.insert(key, value1); @illegal(map.insert_unique(key, value2));
// this documents that array.get_last() is illegal behaviour if array.length() == 0
array.clear();
assert(array.length() == 0); @illegal(array.get_last())
In these cases I would do assert(map.contains(key)) or assert(array.items.len == 0). Do you have situations, where you would use @illegal(), where an assert is not possible.
No, because for @illegal to work there the respective safety checks / assertions must already be implemented.
However I argue that this builtin would make expressing some intent simpler without duplicating the assertion code.
assertions check that code that is supposed to be legal is legal and is semantically correct. @illegal() checks that code that is supposed to be illegal is illegal.
ie. assertions check normal code and @illegal checks the assertion.
you could think of them as meta assertions. dont know if this makes sense though.
one use case i have in mind:
currently api users have to read documentation or internal source code to know the assumptions of the api. violating these assumptions would be illegal behaviour.
with my proposed builtin is is possible to document those assumptions with code tests from the outside.
again im unsure about the usefulness.
Should be optimized (in unchecked release modes) with the assumption it will never receive such input.
You want automated tests to verify that this assumption is being communicated to the compiler.
That’s a very specific set of requirements, so I don’t think it would come up often. But I think you can do it with everything already available in zig:
Let’s say you need a function that takes two integers and does a very complex mathematical calculation that could potentially overflow, but you know it never will.
Write the function with unreachable in the appropriate places
fn addCannotOverflow(comptime T: type, a: T, b: T) T) {
if (T == comptime_int) return a + b;
const ov = @addWithOverflow(a, b);
if (ov[1] != 0) unreachable;
return ov[0];
}
Replace each unreachable with return #### an error if your choice. It doesn’t matter which.
/// Returns the sum of a and b. Returns an error on overflow.
fn add(comptime T: type, a: T, b: T) (error{Overflow}!T) {
if (T == comptime_int) return a + b;
const ov = @addWithOverflow(a, b);
if (ov[1] != 0) return error.Overflow;
return ov[0];
}
Now you can write tests to verify that it returns an error: