ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.

Home Page:https://ziglang.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Erroneous `local variable is never mutated` error when using inferred many-item pointer

ssmid opened this issue · comments

commented

Zig Version

0.14.0-dev.121+ab4c461b7

Steps to Reproduce and Observed Behavior

const std = @import("std");

fn process(iovs: [*]std.posix.iovec, len: usize) void {
	for (0..len) |i| {
		iovs[i].len += 1;
	}
}

pub fn main() void {
	var buffer: [1024]u8 = undefined;
	const iov = std.posix.iovec{
		.base = &buffer,
		.len = buffer.len,
	};
	process(&.{iov}, 1);
	std.debug.print("{d}\n", .{iov.len});
}

Output:

test.zig:12:6: error: local variable is never mutated
 var iov = std.posix.iovec{
     ^~~
test.zig:12:6: note: consider using 'const'

However, when using const:

test.zig:16:10: error: expected type '[*]posix.iovec', found '*const [1]posix.iovec'
 process(&.{iov}, 1);
         ^~~~~~~
test.zig:16:10: note: cast discards const qualifier
test.zig:4:19: note: parameter type declared here
fn process(iovs: [*]std.posix.iovec, len: usize) void {
                 ~^~~~~~~~~~~~~~~~~
referenced by:
    posixCallMainAndExit: ~/bin/zig-0.14.0.121+ab4c461b7/lib/std/start.zig:505:17
    _start: ~/bin/zig-0.14.0.121+ab4c461b7/lib/std/start.zig:342:40
    remaining reference traces hidden; use '-freference-trace' to see all reference traces

Naturally happens when trying to use (posix) sendmsg().

Expected Behavior

Not fail, as iov in fact is mutated.

Not a bug. &.{iov} is not getting a pointer to iov, but rather constructing a new temporary array on the stack (which, as with all temporaries, is const) and taking a pointer to it.

You want to take a pointer to iov and convert it to a many-ptr. Try (&iov)[0..1].

commented

Your solution does work, but I'm wondering a bit because using slice syntax on a single-item pointer is not in the documentation and also seems a bit off.

It was added semi-recently (I think it's been in since 0.12.0 but I might be wrong). Slicing a *T from 0..1 converts it to a *[1]T, which can coerce to [*]T. You could equivalently use @ptrCast, but it's better to use the slicing syntax since this conversion is safe.

If the langref doesn't explain single-item pointer slicing (which wouldn't surprise me, although I can't check right now), feel free to open a PR to explain it!

commented

Just found the relevant issue proposals: #3156 #16075 #8197
This is the current text in the langref:

 Zig has two kinds of pointers: single-item and many-item.

    *T - single-item pointer to exactly one item.
        Supports deref syntax: ptr.*
        Supports pointer subtraction: ptr - ptr
    [*]T - many-item pointer to unknown number of items.
[...]

I will make a PR soon.

fwiw I would write the following as I find it more readable:

        var iov = [_]std.posix.iovec{.{
		.base = &buffer,
		.len = buffer.len,
	}};
	process(&iov, 1);

That's also a very fair point -- looking at this code properly, I'd probably also write that.