authorgravatar for rb.lymn@gmail.comRobbie Lyman <rb.lymn@gmail.com> 2026-07-12 00:53:34-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-08-05 14:45:00-07:00
logc9533046ca37a053f487f4002a999a29a96de517
tree5cf9504b4a33cef1b3d592effc22b4d3cb9e3c1a
parent75b267b7fb412dedaea7e593e2d7f66a71399147

fix: Allocator contract should allow *[len]T in more places

This commit continues work begun in #35222. Although after #35222 is now legal to call `Allocator.free` on memory of type *[len]T, code which does still does not compile. Additionally, similarly shaped footguns remain; this commit addresses those.

1 files changed, 43 insertions(+), 5 deletions(-)

lib/std/mem/Allocator.zig+43-5
......@@ -305,6 +305,25 @@ pub fn allocBytesAligned(
305305 return @alignCast(byte_ptr);
306306}
307307
308fn SliceType(comptime Pointer: type) type {
309 const info = @typeInfo(Pointer).pointer;
310 switch (info.size) {
311 .slice => return Pointer,
312 .one => {
313 const child_info = @typeInfo(info.child);
314 comptime assert(child_info == .array);
315 const sentinel_ptr: ?*const child_info.array.child = @ptrCast(@alignCast(child_info.array.sentinel_ptr));
316 return @Pointer(
317 .slice,
318 info.attrs,
319 child_info.array.child,
320 if (sentinel_ptr) |ptr| ptr.* else null,
321 );
322 },
323 else => unreachable,
324 }
325}
326
308327/// Request to modify the size of an allocation.
309328///
310329/// It is guaranteed to not move the pointer, however the allocator
......@@ -316,6 +335,10 @@ pub fn allocBytesAligned(
316335/// `new_len` may be zero, in which case the allocation is freed.
317336pub fn resize(self: Allocator, allocation: anytype, new_len: usize) bool {
318337 const slice_info = @typeInfo(@TypeOf(allocation)).pointer;
338 if (slice_info.size != .slice) {
339 const slice: SliceType(@TypeOf(allocation)) = allocation; // coerce *[len]T to []T
340 return resize(self, slice, new_len);
341 }
319342 comptime assert(slice_info.size == .slice);
320343 const T = slice_info.child;
321344 if (new_len == 0) {
......@@ -354,8 +377,12 @@ pub fn resize(self: Allocator, allocation: anytype, new_len: usize) bool {
354377/// `new_len` may be zero, in which case the allocation is freed.
355378///
356379/// If the allocation's elements' type is zero bytes sized, `allocation.len` is set to `new_len`.
357pub fn remap(self: Allocator, allocation: anytype, new_len: usize) ?@TypeOf(allocation) {
380pub fn remap(self: Allocator, allocation: anytype, new_len: usize) ?SliceType(@TypeOf(allocation)) {
358381 const slice_info = @typeInfo(@TypeOf(allocation)).pointer;
382 if (slice_info.size != .slice) {
383 const slice: SliceType(@TypeOf(allocation)) = allocation; // coerce *[len]T to []T
384 return remap(self, slice, new_len);
385 }
359386 comptime assert(slice_info.size == .slice);
360387 const T = slice_info.child;
361388
......@@ -399,7 +426,7 @@ pub fn remap(self: Allocator, allocation: anytype, new_len: usize) ?@TypeOf(allo
399426/// do the realloc more efficiently than the caller
400427/// * `resize` which returns `false` when the `Allocator` implementation cannot
401428/// change the size without relocating the allocation.
402pub fn realloc(self: Allocator, old_mem: anytype, new_n: usize) Error!@TypeOf(old_mem) {
429pub fn realloc(self: Allocator, old_mem: anytype, new_n: usize) Error!SliceType(@TypeOf(old_mem)) {
403430 return self.reallocAdvanced(old_mem, new_n, @returnAddress());
404431}
405432
......@@ -408,8 +435,12 @@ pub fn reallocAdvanced(
408435 old_mem: anytype,
409436 new_n: usize,
410437 return_address: usize,
411) Error!@TypeOf(old_mem) {
438) Error!SliceType(@TypeOf(old_mem)) {
412439 const slice_info = @typeInfo(@TypeOf(old_mem)).pointer;
440 if (slice_info.size != .slice) {
441 const slice: SliceType(@TypeOf(old_mem)) = old_mem; // coerce *[len]T to []T
442 return reallocAdvanced(self, slice, new_n, return_address);
443 }
413444 comptime assert(slice_info.size == .slice);
414445 const T = slice_info.child;
415446 if (old_mem.len == 0) {
......@@ -446,9 +477,10 @@ pub fn reallocAdvanced(
446477pub fn free(self: Allocator, memory: anytype) void {
447478 const slice_info = @typeInfo(@TypeOf(memory)).pointer;
448479 if (slice_info.size != .slice) {
449 // slicing with comptime-known start and end results in *[len]T, which may be free'd
450 comptime assert(slice_info.size == .one and @typeInfo(slice_info.child) == .array);
480 const slice: SliceType(@TypeOf(memory)) = memory; // coerce *[len]T to []T
481 return free(self, slice);
451482 }
483 comptime assert(slice_info.size == .slice);
452484 const bytes: []u8 = @ptrCast(@constCast(mem.absorbSentinel(memory)));
453485 if (bytes.len == 0) return;
454486 @memset(bytes, undefined);
......@@ -591,3 +623,9 @@ test failing {
591623 try std.testing.expectError(error.OutOfMemory, f.alloc(u8, std.math.maxInt(usize)));
592624 try std.testing.expectError(error.OutOfMemory, f.allocSentinel(u8, std.math.maxInt(usize) - 1, 0));
593625}
626
627test "free single-pointer to array" {
628 const allocator = std.testing.allocator;
629 const bytes = allocator.alloc(u32, 128) catch return error.SkipZigTest;
630 allocator.free(bytes.ptr[0..128]);
631}