ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
33.78k stars 2.47k forks source link

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

Open DaanVandenBosch opened 1 year ago

DaanVandenBosch commented 1 year ago

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

rohlem commented 1 year ago

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:


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.

DaanVandenBosch commented 1 year ago

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.