In the process of migrating my code to 0.14.x I have run into a new error that I don’t understand:
pointer arithmetic requires element type 'void' to have runtime bits
It’s happening when I am passing a [*]void to a function after some pointer arithmetic. The memory.permanent_storage variable is a ?[*]void and the function is expecting a [*]void.
My current workaround is to use @intFromPtr and @ptrFromInt (which I prefer and have been transitioning to anyway):
It’s because the compiler doesn’t know the size of void, and so doesn’t know the correct amount to offset the pointer by. If you have a [*]u32, and you add 1 to it, the compiler knows that you actually mean to increase the address by 4 bytes, so you get to the next element. It can’t do this with void because it isn’t a “sized” type.
The @ptrFromInt(@intFromPtr works because because you’re taking the byte address of the pointer and increasing it by a specific byte amount.
Judging by your code, it seems that you’re trying to implement type erasure of State. A type erased pointer in zig is usually typed with anyopaque instead of void.
So in your case you could have permanent_storage: ?[*]anyopaque, and then @ptrCast it when you know the type:
const state_storage: [*]State = @ptrCast(memory.permanent_storage.?);
state_storage += 1; // Increases the address by sizeof State
This is nicer than the @ptrFromInt approach, but they both should do the same thing.
This error seem to be added with introduction of pointer subtraction. Subtracting 2 pointers of *void would give infinity which is not representable with integers
This is actually not correct as a many-item pointer to anyopaque is not allowed. You can however have a *anyopaque and then cast to a many-item pointer of a concrete type:
var x = [_]u16{ 10, 20, 30 };
const p: *anyopaque = @ptrCast(&x);
std.debug.print("p: {*}\n", .{p});
var s: [*]u16 = @ptrCast(@alignCast(p));
s += 1;
std.debug.print("s: {*}\n", .{s});
std.debug.print("s[0]: {d}", .{s[0]});
$ zig run test.zig
p: anyopaque@7ffe0f0fd6f2
s: u16@7ffe0f0fd6f4
s[0]: 20
Interesting, that makes sense but in that case I’m surprised that it was working fine on 0.13.0.
I should have included some more of the example, I am using that as a manual memory arena which contains all sorts of data, so having it be of type [*]State wouldn’t work here. The line before added a State to the arena, the pointer arithmetic is there to provide the next available address.
This makes sense to me, I used void because that is what it was in the C version I am following but I’ve suspected that I should be using anyopaque rather than void. I’ll give that a try when I continue on the migration.
Zig void != C void. Same is true for a number of other things, such as packed. There are often similarities for things using the same term (e.g. for), but these are two different languages, with different semantics. Sometimes the terms come mainly from other domains (such as type theory), not necessarily C.