zig
zig copied to clipboard
Can't convert a slice to a sentinel-terminated slice at comptime
Zig Version
0.11.0-dev.3724+32cb9462f
Steps to Reproduce and Observed Behavior
zig run
the following code:
const std = @import("std");
pub fn main() void {
const fields = switch (@typeInfo(TestStruct)) {
.Struct => |info| info.fields,
else => @compileError("Error."),
};
inline for (fields) |field| {
const n = field.name[0.. :0]; // Problematic line.
std.debug.print("Field name: {0s}", .{n});
}
}
const TestStruct = struct {
someField: bool,
};
Output:
src\main.zig:1:1: error: slice end index 9 exceeds bounds of containing decl of type '[9]u8'
const std = @import("std");
^~~~~
referenced by:
callMain: C:\Users\Daan\bin\zig\lib\std\start.zig:598:17
initEventLoopAndCallMain: C:\Users\Daan\bin\zig\lib\std\start.zig:542:34
remaining reference traces hidden; use '-freference-trace' to see all reference traces
The line marked with a comment seems to be the problematic line because the field name length is 9 and changing the slice conversion to [0..field.name.len :0]
gives a similar error but now the position of the error is a bit more sensible:
src\main.zig:10:43: error: slice end index 9 exceeds bounds of containing decl of type '[9]u8'
const n = field.name[0..field.name.len :0]; // Problematic line.
~~~~~~~~~~^~~~
referenced by:
callMain: C:\Users\Daan\bin\zig\lib\std\start.zig:598:17
initEventLoopAndCallMain: C:\Users\Daan\bin\zig\lib\std\start.zig:542:34
remaining reference traces hidden; use '-freference-trace' to see all reference traces
If it helps, the actual code from which this was simplified worked with zig from a week or two ago (don't know the exact date, I deleted the previous version of zig before installing a newer version, have learned my lesson ;-)).
Expected Behavior
The code should simply compile and run, printing "Field name: someField".
Pretty sure this is a direct result of #16072 .
Note that the sentinel-terminated slicing syntax x[a .. b :c]
asserts the sentinel of a slice (or array) is already present in-place - it doesn't change or provide that sentinel. Therefore:
- If the slice (or array) is not already sentinel-terminated, asserting
c
at indexb
ofx.len
is (the way I understand it) accessing the slice (or array) out-of-bounds. This is illegal behavior. (=> should be a compile error, as is the case in your example, or a runtime panic) - If the slice (or array) already has a different sentinel value, the assertion that
c
is present fails. This is also illegal behavior. - If the slice (or array) already has sentinel value
c
, the operation yields a pointer to array or a slice terminated byc
. (=> With regard to the sentinel value, this is a no-op.)
Once #16072 is fixed, simply using field.name
directly (or via const n = field.name;
) should already work.
As a workaround until then, you need to copy the contents over into a one-byte-larger new buffer.
Playing around with this I didn't find a convenient way to convert a length n+1
array into a sentinel-terminated length n
array via the type system currently.
Maybe someone else knows a shorter way; if not, that's probably an area for potential language improvements.
This is what I came up with:
// use as addZ(slice.len, slice[0..].*) - slice must be comptime-known
pub fn addZ(comptime length: usize, value: [length]u8) [length:0]u8 {
var terminated_value: [length:0]u8 = undefined;
terminated_value[length] = 0;
@memcpy(&terminated_value, &value);
return terminated_value;
}
The original error output pointing to file location :1:1
looks like a separate bug in compile error generation logic that should be fixed as well though.
That wasn't clear to me, but I see the documentation directly states it. Thanks for pointing it out, figuring out the real problem and giving me a workaround!
The error message is confusing though. Following your explanation, I understand where this "low level" message comes from, but a clearer, more high level message would be nice.