Hi,
I’d like to reassure myself, that my assumptions about comptime and loop unrolling are correct.
Let’s say we have this (pointless) code:
const std = @import("std");
const Container = struct {
uu1: u1 = 1,
uu2: u2 = 2,
uu3: u3 = 4,
pub fn getPointer(self: *Container, T: type) *T {
const fields = std.meta.fields(Container);
inline for (fields) |fld| {
if (fld.type == T) {
return &@field(self, fld.name);
}
}
}
};
pub fn main() !void {
var c = Container{};
const uu1 = c.getPointer(u1);
try std.testing.expectEqual(1, uu1.*);
const uu2 = c.getPointer(u2);
try std.testing.expectEqual(2, uu2.*);
const uu3 = c.getPointer(u3);
try std.testing.expectEqual(4, uu3.*);
}
I would assume, that the compiler internally creates three functions for each type we call getPointer() for. So internally this would produce something like:
const Container = struct {
[...]
pub fn u1_getPointer(self: *Container, T: u1) *u1 { [...] }
pub fn u2_getPointer(self: *Container, T: u2) *u2 { [...] }
pub fn u3_getPointer(self: *Container, T: u3) *u3 { [...] }
};
And secondly the ‘inline for’ loop will internally be unrolled into (e: substituting fld.type and fld.name) :
pub fn getPointer(self: *Container, T: type) *T {
if (@TypeOf(Container.uu1) == T) {
return &@field(self, 'uu1');
}
if (@TypeOf(Container.uu2) == T) {
return &@field(self, 'uu2');
}
if (@TypeOf(Container.uu3) == T) {
return &@field(self, 'uu3');
}
}
And if we take both these assumptions together, we get internally three functions with something like:
pub fn u1_getPointer(self: *Container, T: u1) *u1 {
if (@TypeOf(Container.uu1) == u1) { // <- true
return &@field(self, 'uu1');
}
if (@TypeOf(Container.uu2) == u1) { // <- false
return &@field(self, 'uu2');
}
if (@TypeOf(Container.uu3) == u1) { // <- false
return &@field(self, 'uu3');
}
}
// The same for u2 and u3
If that is correct so far: when the compiler optimizes these functions, are the false if statements optimized away? Since the values are comptime known, I would expect the compiler to be clever enough to generate code for the container as if we wrote this:
const Container = struct {
uu1: u1 = 1,
uu2: u2 = 2,
uu3: u3 = 4,
pub fn u1_getPointer(self: *Container) *u1 {
return &self.uu1;
}
pub fn u2_getPointer(self: *Container) *u2 {
return &self.uu2;
}
pub fn u3_getPointer(self: *Container) *u3 {
return &self.uu3;
}
};
Are these ideas about comptime and optimizations correct?
Thanks!