Updating a field in a comptime struct (part 2)

entropy has set in within this post, so let’s start anew. more important, i now have a trivial, self-contained example that illustrates the underlying issue i encountered:

const std = @import("std");

const S = struct {
    s2: S2,
};

const S2 = struct {
    p: u32 = 10,
};

pub fn main() void {
    const s = comptime @constCast(&std.mem.zeroInit(S, .{}));
    std.log.debug("\n", .{});
    std.log.debug("{d}", .{s.s2.p});
    s.s2.p = 20;
    std.log.debug("{d}", .{s.s2.p});
    std.log.debug("{any}", .{s});
}

output is then:

debug: 10
debug: 10
debug: main.S{ .s2 = main.S2{ .p = 20 } }

strange, in that the object itself shows .p = 20 after the update; but reading s.s2.p directly returns the initial value…

REMOVING the comptime qualification from the initial value of s leads to the output you would expect…

incidently, this problem would not occur with a “single” struct containing p; it’s the nesting of S2 within S that causes the issue…

without this nesting, the compiler would in fact display an error message about updating p inside of a constant… but in the example above, things are not so obvious…

so why do i see s.s2.p displayed as 10 when in fact the container s shows field s2.p having the value of 20???

@mlugg @kristoff – here’s the output from running under 0.13.0:

debug: 

debug: 10
Segmentation fault at address 0xec268c
C:\tools\zig-windows-x86_64-0.13.0\lib\std\start.zig:363:53: 0xe41013 in WinStartup (zig-play.exe.obj)
    std.os.windows.ntdll.RtlExitUserProcess(callMain());
                                                    ^
???:?:?: 0x7ff84da67373 in ??? (KERNEL32.DLL)
???:?:?: 0x7ff84f95cc90 in ??? (ntdll.dll)
run
└─ run zig-play failure
error: the following command exited with error code 3:
C:\Users\biosb\zig\zig-play\zig-out\bin\zig-play.exe
Build Summary: 5/7 steps succeeded; 1 failed (disable with --summary none)
run transitive failure
└─ run zig-play failure
error: the following build command failed with exit code 1:
C:\Users\biosb\zig\zig-play\.zig-cache\o\c6bf77fa964b22f54b0b4a2190073d0a\build.exe C:\tools\zig-windows-x86_64-0.13.0\zig.exe C:\Users\biosb\zig\zig-play C:\Users\biosb\zig\zig-play\.zig-cache C:\Users\biosb\AppData\Local\zig --seed 0xb6e57b6b -Zd8dff6a5aa024aab run

no compiler errors, but clearly a runtime failure…

0.11.0, 0.13.0, and master all produce segfaults, which is the expected behavior. Only 0.12.0 and 0.12.1 act weird.

my small example is presumably “wrong”, in that assigning to s.s2.p is clearly problematic…

the fact that it (sort of) worked in 0.12.0 is a fluke, IMHO…

ideally, the compiler would flag this at compile-time rather than faulting at run-time…

the workaround in my actual use-case will involve a rework of my S2 – which currently must be comptime due to type-valued fields and/or methods that return comptime-only structs…

Apart from the debug output, I read this as being equivalent to:

const p : u32 = 10;
p = 20;

This clearly shoud be a compile error, no?

I think nesting in structs, getting the default value of 10 through zeroInit and even demanding the calculation of the value to be comptime should have no effect on the validity of then trying to change a thing which is const.

1 Like

The compile-time safety was provided by having the temporary value value be const so that pointers to it become *const. The constCast let you bypass the type system and in this case it proved to be a bad cast.

My guess is that in older versions of Zig the value would not get interned and remained in areas of memory that were still mutable at runtime.

Maybe this also has something to do with your main example (the one from the original post): could it be that a const value (maybe because it gets created as a temporary value) somehow gets mutated in a illegal but not-properly-checked way causing the inconsistency that you reported originally?

yes!!! my (hopefully improving) understanding of these nuances are probably the root-cause of ALL of my immediate issues :wink: