Not safe to free unallocated sentinel-terminated slices?

I remember reading that it’s OK to call free on a slice that has not been allocated, as long as its .len == 0, and indeed it is, but not when the slice is sentinel terminated:

var name: [:0]const u8 = &.{};
//var name: []const u8 = &.{}; // This works

var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocr = gpa.allocator();

allocr.free(name); // This does NOT work with [:0] slice

name = allocr.dupeZ(u8, "My name") catch @panic("Out Of Memory");
//name = allocr.dupe(u8, "My name") catch @panic("Out Of Memory"); // This works
defer allocr.free(name);

std.debug.print("Name is \"{s}\"\n", .{ name } );

In std/mem/Allocator.zig, in pub fn free I can see that for sentinel-terminated slices, the function does not short-circuit itself:

pub fn free(self: Allocator, memory: anytype) void {
    const Slice = @typeInfo(@TypeOf(memory)).pointer;
    const bytes = mem.sliceAsBytes(memory);
    const bytes_len = bytes.len + if (Slice.sentinel != null) @sizeOf(Slice.child) else 0;
    if (bytes_len == 0) return;
    const non_const_ptr = @constCast(bytes.ptr);
    // TODO: https://github.com/ziglang/zig/issues/4298
    @memset(non_const_ptr[0..bytes_len], undefined);
    self.rawFree(non_const_ptr[0..bytes_len], log2a(Slice.alignment), @returnAddress());
}

In my case ([:0]const u8), bytes_len is 1 if I understand the logic correctly, and if (bytes_len == 0) return; is not triggered.

Is this the intended behavior? Maybe I misunderstand the way sentinel-terminated slices work?

Update: just for the record - the obvious solution is to check slice len before calling free, this then works with sentinel-terminated and simple slices:

if (name.len > 0) // Sentinel-terminated slices are not safe to `free` willy-nilly
    allocr.free(name); // This works with [:0] slice and with [] slices

Yes, I believe it is intended.

When you allocate a zero element slice with sentinel, the size of sentinel is allocated and a free is required.

This statement is correct:

Why do you want to depend on something like that? Why don’t you always match free with your alloc?

When the slice is a field of a struct that is being passed around, and you don’t know whether it was allocated already or not, it is convenient if you can just call free on it without checking first when you want to allocate + copy (dupe) a new value.

This works just fine with regular slices, but does not with sentinel-terminated slices, which rubs me the wrong way, that’s all.