zig icon indicating copy to clipboard operation
zig copied to clipboard

Can't convert a slice to a sentinel-terminated slice at comptime

Open DaanVandenBosch opened this issue 1 year ago • 2 comments

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".

DaanVandenBosch avatar Jun 20 '23 13:06 DaanVandenBosch

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 index b of x.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 by c. (=> 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.

rohlem avatar Jun 20 '23 18:06 rohlem

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.

DaanVandenBosch avatar Jun 20 '23 18:06 DaanVandenBosch