Peer type resolution drops array sentinel
Zig Version
0.12.0
Steps to Reproduce and Observed Behavior
I want to use an std.ArrayList with zero terminated arrays.
test "test" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
var list = std.ArrayList([16:0]u8).init(gpa.allocator());
defer list.deinit();
const new_buffer = try list.addOne(); // <<<<<- ERROR
_ = new_buffer; // autofix
}
Error:
C:\dev\Zig\lib\std\mem\Allocator.zig:193:41: error: expected type 'error{OutOfMemory}![][16:0]u8', found 'error{OutOfMemory}![][16]u8'
return self.allocAdvancedWithRetAddr(T, alignment, n, @returnAddress());
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\dev\Zig\lib\std\mem\Allocator.zig:193:41: note: error union payload '[][16]u8' cannot cast into error union payload '[][16:0]u8'
C:\dev\Zig\lib\std\mem\Allocator.zig:193:41: note: pointer type child '[16]u8' cannot cast into pointer type child '[16:0]u8'
C:\dev\Zig\lib\std\mem\Allocator.zig:193:41: note: destination array requires '0' sentinel
C:\dev\Zig\lib\std\mem\Allocator.zig:192:8: note: function return type declared here
) Error![]align(alignment orelse @alignOf(T)) T {
Expected Behavior
I think we should be able to allocate these in an std.ArrayList It works correctly if the array is a member of an struct:
const Foo= struct {
buff: [16:0]u8,
};
test "test with Struct" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const ptr = try gpa.allocator().alloc(Foo, 1);
defer gpa.allocator().free(ptr);
}
Seems to be an error in evaluating the return type for allocAdvancedWithRetAddr.
const std = @import("std");
pub fn main() !void {
std.debug.print("{s}\n", .{@typeName(@TypeOf(std.mem.Allocator.allocAdvancedWithRetAddr(undefined, [34:0]u8, null, 1, @returnAddress())))});
std.debug.print("{s}\n", .{@typeName(std.mem.Allocator.Error![]align(null orelse @alignOf([34:0]u8)) [34:0]u8)});
}
Output:
error{OutOfMemory}![][34]u8
error{OutOfMemory}![][34:0]u8
Here's a minimal repro of the underlying issue:
inline fn f(comptime T: type) error{E}![]T {
try g();
return undefined;
}
fn g() error{E}!void {}
comptime {
@compileLog(@typeName(@TypeOf(f([34:0]u8))));
// Compile Log Output:
// @as(*const [17:0]u8, "error{E}![][34]u8")
//
// Should be:
// @as(*const [19:0]u8, "error{E}![][34:0]u8")
}
In the context of std.mem.Allocator, f is allocAdvancedWithRetAddr and g is allocWithSizeAndAlignment.
@Vexu This seems a compiler bug instead of an std one.
Can also be replicated with another try instead of return, and a third breaks compilation:
fn g() error{E}!void {}
inline fn f() error{E}![][1:0]u8 {
try g(); //with 1 `try` : error{E}![][1:0]u8
try g(); //with 2 `try`s: error{E}![][1]u8
try g(); //with 3 `try`s: `error: incompatible types: 'error{E}![][1:0]u8' and 'error{E}![][1:0]u8'`
if (true) unreachable; //without any `try`: noreturn
}
comptime { //exact same behavior from a `test` block, so same when called in a runtime scope
@compileLog(@TypeOf(f()));
}
Normal fn don't seem affected.
I also suggest renaming the issue to reflect the reduced examples, f.e. "return type resolution error with try in inline fn".
EDIT: Forgot to mention, I didn't manage to trigger any of this using @TypeOf - not sure whether that's possible.
Minimal repro:
const a: [][2:0]u8 = undefined;
var rt = true;
export fn foo() void {
const val = if (rt) a else a;
@compileLog(@TypeOf(val));
}