Okay, yes in my example I now see that it can be known at comptime. But I played around with an example of my own. Turns out the compiler allows returning references to local runtime constructed anonymous structs.
const std = @import("std");
const MyThing = struct {
num: i32,
foo: []const u8,
};
fn bad(i: i32) *const MyThing {
return &.{ .num = i, .foo = "hello" };
}
fn fine(i: i32) *const MyThing {
_ = i;
return &.{ .num = 7, .foo = "hello" };
}
pub fn main() void {
const bar = bad(7);
// const bar = fine(7);
std.debug.print("hello {}\n", .{bar});
}
if you call fine() then it is comptime known and compiles and runs as expected. However if you call bad, we get a runtime crash in debug mode
⯠zig run reference_to_anonymous_struct.zig
hello .{ .num = -1431655766, .foo = { General protection exception (no address available)
/nix/store/iyv5lnq9cwlsixg357zv8zcp4pv2q7ml-zig-0.15.2/lib/zig/std/Io/Writer.zig:1358:29: 0x1142b01 in printValue__anon_23104 (std.zig)
for (value, 0..) |elem, i| {
^
/nix/store/iyv5lnq9cwlsixg357zv8zcp4pv2q7ml-zig-0.15.2/lib/zig/std/Io/Writer.zig:1333:33: 0x1142411 in printValue__anon_23069 (std.zig)
try w.printValue(ANY, options, @field(value, f.name), max_depth - 1);
^
/nix/store/iyv5lnq9cwlsixg357zv8zcp4pv2q7ml-zig-0.15.2/lib/zig/std/Io/Writer.zig:1300:34: 0x1141d33 in printValue__anon_22884 (std.zig)
return printValue(w, ANY, options, value, max_depth);
^
/nix/store/iyv5lnq9cwlsixg357zv8zcp4pv2q7ml-zig-0.15.2/lib/zig/std/Io/Writer.zig:1340:71: 0x11408c5 in printValue__anon_22823 (std.zig)
.@"enum", .@"union", .@"struct" => return w.printValue(fmt, options, value.*, max_depth),
^
/nix/store/iyv5lnq9cwlsixg357zv8zcp4pv2q7ml-zig-0.15.2/lib/zig/std/Io/Writer.zig:700:25: 0x1140064 in print__anon_22781 (std.zig)
try w.printValue(
^
/nix/store/iyv5lnq9cwlsixg357zv8zcp4pv2q7ml-zig-0.15.2/lib/zig/std/debug.zig:231:23: 0x113fbaf in print__anon_22682 (std.zig)
nosuspend bw.print(fmt, args) catch return;
^
/home/oskar/Coding/_molijoxer/zig_buggy_stuff/reference_to_anonymous_struct.zig:20:20: 0x113e884 in main (reference_to_anonymous_struct.zig)
std.debug.print("hello {}\n", .{bar});
^
/nix/store/iyv5lnq9cwlsixg357zv8zcp4pv2q7ml-zig-0.15.2/lib/zig/std/start.zig:618:22: 0x113dabd in posixCallMainAndExit (std.zig)
root.main();
^
/nix/store/iyv5lnq9cwlsixg357zv8zcp4pv2q7ml-zig-0.15.2/lib/zig/std/start.zig:232:5: 0x113d351 in _start (std.zig)
asm volatile (switch (native_arch) {
^
???:?:?: 0x0 in ??? (???)
fish: Job 1, 'zig run reference_to_anonymousâŚ' terminated by signal SIGABRT (Abort)
Quite a nasty footgun that such a small change can make the difference between a well defined program and runtime undetectable illegal behavior. So I guess what is going on is that the bad function get compiled to the equivalent of
fn bad2(i: i32) *const MyThing {
const tmp: MyThing = .{ .num = i, .foo = "hello" };
return &tmp;
}
and this is an obvious return of reference to local variable bug. I saw that there is an open issue which intends to detect these at compile time in the future and I guess it would also be able to detect these references to anonymous structs.
I was imagining that the compiler could potentially do some fancy reasoning about result location semantics of the anonymous reference and place it in the caller stack frame instead of the calleeâs stack frame. But that does not appear to be the case.