why do user defined types in zig need to be const? - zig

In case I need to declare a struct in Zig I have to prefix it with a const
const Arith = struct {
x: i32,
y: i32,
fn add(self: *Arith) i32 {
return self.x + self.y;
}
};
test "struct test" {
var testArith = Arith{
.x = 9,
.y = 9,
};
expect(testArith.add() == 18);
}
But it can be initialized both ways as var and const so why does the type declaration need a constant keyword when it only matters whether the instance of the struct is const or not?

Need to be const because the order of evaluation in the root scope is undefined and because the variables of type type only can live in the compiler (has no memory representation, the compiler is unable to produce a binary representation of it). But you can use var inside other scopes:
comptime {
var T = struct { value: u64 };
const x: T = .{ .value = 3 };
T = struct { ok: bool };
const y: T = .{ .ok = true };
#compileLog(x.value); // <- 3
#compileLog(y.ok); // <- true
}
Run this code
In the rest of the answer, I explain in detail.
Const
const Arith = struct {...};
Creates a constant variable of a inferred type. In this case, the variable Arith has type type:
const Arith = struct {...};
comptime {
#compileLog(#TypeOf(Arith)); // <- type
}
Run this code
This is the same as declare the variable as:
const Arith: type = struct {...};
Var
You also can create a variable with var
Examples:
comptime {
var Arith = struct {...};
}
comptime {
var Arith: type = struct {...};
}
fn main() !void {
comptime var Arith = struct {...};
}
fn main() !void {
var Arith: type = struct {...};
}
fn main() !void {
comptime var Arith: type = struct {...};
}
Because is a variable you can modify it:
comptime {
var T = u64;
T = bool;
#compileLog(T); // <-- bool
}
Run this code
Comptime Types
There is types that can only live in the compiler, like: type or structs that have a field of type anytype or other comptime type.
In the case of type, this make the compiler interpret var x: type as comptime var x: type.
Then, consider the following code:
var T = struct { value: u64 }; // <- Compiler error
comptime {
const x: T = .{ .value = 3 };
}
error: variable of type 'type' must be constant
because the order of evaluation in the root scope is undefined, the compiler forces to create a global variable inside the binary, but the type type has no memory representation. So, the compiler raises an error.

Since Arith is of the type type it has to be declared a constant because the compiler expects it to be. This can be checked by changing the type declaration to this and running the program
var Arith = struct {
x: i32,
y: i32,
fn add(self: *Arith) i32 {
return self.x + self.y;
}
};
which will result in the error error: variable of type 'type' must be constant
Also in Zig we need either a const or a var preceeding a name otherwise it is considered an invalid token.

Related

How to typecast fixed size byte array as struct?

I want to reinterpret a stack allocated byte array as a stack allocated (statically guaranteed) struct without doing any work - just to tell the compiler that "Yes, I promise they are the same size and anything". How do I do that?
I tried transmute, but it doesn't compile.
fn from_u8_fixed_size_array<T>(arr: [u8; size_of::<T>()]) -> T {
unsafe { mem::transmute(arr) }
}
cannot transmute between types of different sizes, or dependently-sized types E0512
Note: source type: `[u8; _]` (this type does not have a fixed size)
Note: target type: `T` (this type does not have a fixed size)
There is also this variant of such a function, that compiles, but it requires T to be Copy:
fn from_u8_fixed_size_array(arr: [u8; size_of::<T>()]) -> T {
unsafe { *(&arr as *const [u8; size_of::<T>()] as *const T) }
}
With Rust 1.64 I have a compilation error on [u8; size_of::<T>()] (cannot perform const operation using T).
I tried with a const generic parameter but the problem is still the same (I cannot introduce a where clause to constrain this constant to match size_of::<T>()).
Since the array is passed by value and the result is a value, some bytes have to be copied ; this implies a kind of memcpy().
I suggest using a slice instead of an array and checking the size at runtime.
If you are ready to deal with undefined behaviour, you might consider the second version which does not copy anything: it just reinterprets the storage as is.
I'm not certain I would do that, however...
Edit
The original code was compiled with nightly and a specific feature.
We can simply use transmute_copy() to get the array by value and emit a value.
And, I think the functions themselves should be qualified with unsafe instead of just some of their operations, because nothing guaranties (statically) that these conversions are correct.
#![feature(generic_const_exprs)] // nightly required
unsafe fn from_u8_slice_v1<T>(arr: &[u8]) -> T {
let mut result = std::mem::MaybeUninit::<T>::uninit();
let src = &arr[0] as *const u8;
let dst = result.as_mut_ptr() as *mut u8;
let count = std::mem::size_of::<T>();
assert_eq!(count, arr.len());
std::ptr::copy_nonoverlapping(src, dst, count);
result.assume_init()
}
unsafe fn from_u8_slice_v2<T>(arr: &[u8]) -> &T {
let size = std::mem::size_of::<T>();
let align = std::mem::align_of::<T>();
assert_eq!(size, arr.len());
let addr = &arr[0] as *const _ as usize;
assert_eq!(addr % align, 0);
&*(addr as *const T) // probably UB
}
unsafe fn from_u8_fixed_size_array<T>(
arr: [u8; std::mem::size_of::<T>()]
) -> T {
std::mem::transmute_copy(&arr)
}
fn main() {
let a = [1, 2];
println!("{:?}", a);
let i1 = unsafe { from_u8_slice_v1::<i16>(&a) };
println!("{:?}", i1);
let i2 = unsafe { from_u8_slice_v2::<i16>(&a) };
println!("{:?}", i2);
let i3 = unsafe { from_u8_fixed_size_array::<i16>(a) };
println!("{:?}", i3);
}
/*
[1, 2]
513
513
513
*/

LLVM: how can I get the struct name that the StoreInst operates on?

recently I am trying to monitor the struct accessment using LLVM.
I can print the StoreInst using the SI.dump() as
i32* getelementptr inbounds (%struct._Datatype, %struct._Datatype* #monitor_memory, i32 0, i32 0)
But in the pass code, I can't find a method for StoreInst to get just the Struct name, which I want to use to do the compare laterly.
if (type->getStructName() == "struct._Datatype") {
....
}
Can someone help me? Thanks!
the code fragment is here:
for(BasicBlock &B : F) {
for(Instruction &I : B) {
StoreInst * store = nullptr;
builder->SetInsertPoint(&I);
if((store = dyn_cast<StoreInst>(&I)) != nullptr) {
Value * value = store->getValueOperand(); // Getting the value to be stored at given address
Value * address = store->getPointerOperand(); // Getting source address
Type * type = store->getPointerOperandType(); // the type that I get is i32*, but I expect it to be struct._Datatype.
}
}
}
the Datatype defined as
typedef struct _Datatype {
int a;
int b;
} Datatype;
Names are generally obtained using getName() in LLVM, for the kinds of objects that may have names. The StructType class has a getName(), so all you need is
cast<StructType>(type)->getName()

Why do structs share the same address when created, and have different addresses from creating when dropped

I am attempting to log a structs address when creating the struct and when it is dropped, when I run the below code not only do both structs log the same address, both structs log a different address when being dropped. Is there a correct way to do this?
struct TestStruct {
val: i32
}
impl TestStruct {
fn new(val: i32) -> Self {
let x = TestStruct{val};
println!("creating struct {:p}", &x as *const _);
x
}
}
impl Drop for TestStruct {
fn drop(&mut self) {
println!("destroying struct {:p}", &self as *const _)
}
}
fn main() {
let s1 = TestStruct::new(1);
let s2 = TestStruct::new(2);
}
Output:
creating struct 0x7ffef1f96e44
creating struct 0x7ffef1f96e44
destroying struct 0x7ffef1f96e38
destroying struct 0x7ffef1f96e38
In new() you're printing the address of x, when new() returns x is moved, so that is no longer the actual address, which is why you see the same address repeated.
See also "Is a returned value moved or not?".
In drop(), you are actually printing the address of the &Self and not Self itself. You need to change &self as *const _ to just self as self is already a reference. Now it correctly prints the two different addresses.
If you then instead try to print the address of s1 and s2 in main() then the addresses match.
impl TestStruct {
fn new(val: i32) -> Self {
let x = TestStruct { val };
x
}
}
impl Drop for TestStruct {
fn drop(&mut self) {
println!("destroying struct {:p}", self);
}
}
fn main() {
let s1 = TestStruct::new(1);
println!("creating struct {:p}", &s1);
let s2 = TestStruct::new(2);
println!("creating struct {:p}", &s2);
}
Output:
creating struct 0xb8682ff59c <- s1
creating struct 0xb8682ff5f4 <- s2
destroying struct 0xb8682ff5f4 <- s2
destroying struct 0xb8682ff59c <- s1

Generation of types in zig (zig language)

Is it possible to create a comptime function in zig that would generate a new struct type? The function would receive an array of strings and an array of types. The strings are the names of subsequent struct fields.
This has been implemented now as https://github.com/ziglang/zig/pull/6099
const builtin = #import("std").builtin;
const A = #Type(.{
.Struct = .{
.layout = .Auto,
.fields = &[_]builtin.TypeInfo.StructField{
.{ .name = "one", .field_type = i32, .default_value = null, .is_comptime = false, .alignment = 0 },
},
.decls = &[_]builtin.TypeInfo.Declaration{},
.is_tuple = false,
},
});
test "" {
const a: A = .{ .one = 25 };
}
The TypeInfo struct is defined here.
Partially. This has been long proposed at https://github.com/ziglang/zig/issues/383
You can only do so with fields, not with custom decls.

Getting error on host_processor_sets swift 3

I don't know why i am getting this error
var list = [ProcessInfo]()
var psets = processor_set_name_array_t.allocate(capacity: 1)
var pcnt: mach_msg_type_number_t = 0
var result = host_processor_sets(machHost, &psets, &pcnt)
the last statement gives :
cannot pass immutable value of type as inout argument
The function host_processor_sets is imported into Swift as:
func host_processor_sets(
_ host_priv: host_priv_t,
_ processor_sets: UnsafeMutablePointer<processor_set_name_array_t?>!,
_ processor_setsCnt: UnsafeMutablePointer<mach_msg_type_number_t>!
) -> kern_return_t
(Taken from the Quick Help of Xcode.)
When you find an UnsafeMutablePointer<T>! in imported C-function, you usually declare a variable of type T and pass it as an inout parameter.
So, you need to declare variables of processor_set_name_array_t? and mach_msg_type_number_t.
var psets: processor_set_name_array_t? = nil
var pcnt: mach_msg_type_number_t = 0
var result = host_processor_sets(machHost, &psets, &pcnt)

Resources