I remember (no guarantee that it’s correct) that once I was able to compile a program that could take an anytype value and access a “random” field. It felt like depending on which structure I passed, it could work in one case and panic in another. However, this lasting effect could be due to ZLS could not infer a tricky chain of type interrelations.
I tried to reproduce this behaviour but couldn’t trick the compiler:
This program compiles successfully but when executed produces zsh: trace trap zig run file.zig with a return code of 133. Anyway, I wanted the line I commented out to work (which is good that it doesn’t).
My understanding is that in “low-level” languages like C or Zig, the .field access is a result of pointer arithmetic—we calculate sizes of structs and the necessary shifts for accessing particular fields based on their type sizes. So, if there is an example where Zig allows compiling access to a field without knowing its type and size, it kind of ruins my world . It would be very helpful to have some background or examples that provide insight into how this accessing works (or, in case it doesn’t, why) .
It’s not compiling. The SIGTRAP is being triggered by the compiler. You can’t have a slice of anyopaque, because it doesn’t have a known size. Switch Field to []*anyopaque and it works. Field acces is indeed just pointer arithmetic, it’s trivial to access any field in any weird way that you wish.
const T = struct{
f: u32,
};
const U = struct{
f: u16,
g: u16,
};
const t: T = undefined;
const u: *U = @ptrCast(&t);
// u.f access the first two bytes of t.f
We kind of do this when we use std.mem.sliceAsBytes. The entire struct is seen as just a bunch of bytes. When doing aliasing, you have to be careful with alignment and padding. Also, in Zig, structs that are not extern don’t have a defined layout.
This is related to a common technique referred to as “coalesced access” and it’s used to vectorize GPU loads on numeric arrays. They’ll define a struct with four data members (or less, we’ll it Vec4 and give it members w, x, y, z). They’ll then cast the head of the array to to a *Vec4 and index into every 4th (starting from zero) data element in the array. This can lead to big speedups because it will coalesce the memory access and can force the machine to do less independent copy operations.
It’s all just bytes - what you call them is a matter of convention and how you access them is a matter of size and alignment
It’s not compiling. The SIGTRAP is being triggered by the compiler.
To be honest, I’m not sure why, if it is obvious that having a slice of anyopaque is not allowed, Zig doesn’t throw a compilation error. Also, I’m unsure about where exactly the SIGTRAP happens.
Regarding the example. Thank you for showing how to cast pointers (didn’t reach that part yet:)). I couldn’t get it work though with t being const:
Yes, that would have been ideal, but zig is a work in progress.
Oof, this looks like a miscompilation to me. I’ve never seen this error before. I believe packed structs are still kind of iffy. I think the packed struct is interacting with the pointer in an unexpected way. I think the compiler is seeing that t is comptime-known and replacing it with a u0, and the fields are being transformed into global constants. This should have been transparent to us. But it looks like u didn’t get the memo and is actually trying to access the fields. When you change it to var, the compiler can’t do this transformation, which is why it works.
Type reflection is all comptime. Zig is statically-typed. An anytype parameter does not make a run-time function that works for any type, it makes a different run-time function for every kind of type you use the function with. E.g. if I have:
These are calling two completely different functions at run-time! One takes two u64’s, the other takes two i32’s. (Of course for this trivial example the compiler could inline both functions, but for non-trivial code, my point stands)
Thank you. I understand. But I’m not sure how this understanding explains the error: dereference of '*align(1:0:1) const u4' exceeds bounds of containing decl of type 'u0' in the example above…