Retrieving a field from a struct after updating its value

unfortunately, i’m unable to create a “small” self-contained example of the issue… but here’s a small snip from my codebase that illustrates problem…

    em.print("AppBut = {any}\n", .{AppBut});
    em.print("before: Edge = {any}\n", .{AppBut.Edge});
    em.print("before: Edge._upath = {s}\n", .{AppBut.Edge._upath});
    AppBut.Edge._upath = "foo";
    em.print("after:  Edge = {any}\n", .{AppBut.Edge});
    em.print("after:  Edge._upath = {s}\n", .{AppBut.Edge._upath});

output is as follows:

debug: AppBut = em.core.em.lang.em.unitScope_H(em.core.em.utils.ButtonT.em.em__generateS("ti.distro.cc23xx/BoardC__AppBut"[0..31]))

debug: before: Edge = em.core.em.lang.em.Proxy_H(em.core.em.lang.em.unitScope_H(em.core.em.hal.GpioEdgeI.em)){ ._upath = { 101, 109, 46, 104, 97, 108, 47, 71, 112, 105, 111, 69, 100, 103, 101, 73 } }

debug: before: Edge._upath = em.hal/GpioEdgeI

debug: after:  Edge = em.core.em.lang.em.Proxy_H(em.core.em.lang.em.unitScope_H(em.core.em.hal.GpioEdgeI.em)){ ._upath = { 102, 111, 111 } }

debug: after:  Edge._upath = em.hal/GpioEdgeI

suffice it to say that AppBut.Edge is a struct with a single field named _upath of type []const u8…; AppBut itself is a comptime struct returned by a generator function… [[ sorry again for the added layers of complexity :confused: ]]

the snip above assigns AppBut.Edge._upath = "foo", with the state of the struct printed before/after the assignment…

what i can’t understand is WHY the 4rd print output shows “foo” { 102, 111, 111 } as the current ._upath value, whereas the 5th print output shows the original (string-formatted) value of this field (which was “em.hal/GpioEdgeI”)…

oddly enough, i have a method defined on @TypeOf(AppBut.Edge) that actually DOES see the updated value of ._upath when invoked later on; it’s only when i attempt to select ._upath from the outside that i see what in fact was the ORIGINAL value of this field…

i realize the “fog” of my codebase can be a challenge here, and we’re operating somewhat in the abstract… any insights into WHAT circumstances in general could cause this anomaly might turn on a light for me :bulb:

2 Likes

Can you show the result for @compileLog(AppBut), @compileLog(AppBut.Edge), and @compileLog(AppBut.Edge._upath)? That would tell us whether the variables are runtime or comptime.

2 Likes
@as(type, em.core.em.lang.em.unitScope_H(em.core.em.utils.ButtonT.em.em__generateS("ti.distro.cc23xx/BoardC__AppBut"[0..31])))
@as(*em.core.em.lang.em.Proxy_H(em.core.em.lang.em.unitScope_H(em.core.em.hal.GpioEdgeI.em)), @as(*em.core.em.utils.ButtonT.em.EM__CONFIG, @ptrCast(@as(em.core.em.utils.ButtonT.em.EM__CONFIG, .{ .em__upath = "ti.distro.cc23xx/BoardC__AppBut"[0..31], .Edge = .{ ._upath = &.{ ... }[0..(...)] }, .debounceF = .{ ._val = .{ ... } } }))).Edge)
@as([]const u8, "em.hal/GpioEdgeI"[0..16])

help me understand what i’m seeing here…

Okay, so everything is comptime… I tried replicating the error but only managed to segfault the compiler with the following code:

const std = @import("std");

const StructA = struct {
    T: type = void,
    ptr: *StructB,
};

const StructB = struct {
    string: []const u8,
};

pub fn main() void {
    var b = StructB{ .string = "Hello" };
    const a = StructA{ .ptr = &b };
    std.debug.print("{s}\n", .{a.ptr.string});
    a.ptr.string = "world";
    std.debug.print("{s}\n", .{a.ptr.string});
}

The 0.11.0 compiler would actually identify the issue in the code above and error out. 0.12.0 and up (including master) would run it inspite of the defect and crash. Maybe you can try compiling your code with 0.11.0? Just shooting in the dark here. No idea what’s behind what you’re seeing.

1 Like

funny thing, because as i was frantically tweaking my code to fix things, i would often segfault…

so what IS the issue in the small example above that 0.11.0 would identify???

and to increase my own understanding, what did the @compileLog output actually mean???

i couldn’t build ANYTHING under 0.13.0 and segfaulted inside std/array_list_zig… since i DO want to move forward from 0.12.0 (where all is well, modulo the issue in the OP), i’ll need to do some more troubleshooting…

0.11.0 was also a non-starter, but for very different reasons… i’m less motivated to address this, however…

i could probably tweak my current 0.12.0 codebase to cause a segfault – if that would help…

referring to this post, all of this code is part of an upstream meta-program which executes natively on the host… strange as it may seem, performance is not all that important; and i’ve already moved some computation from comptime to “runtime”, as i’m still a little shy about the former…

unfortunately, my struct either contains a type-valued field or else has a type-valued method…

The error from 0.11.0 is:

test.zig:14:31: error: unable to resolve comptime value
    const a = StructA{ .ptr = &b };
                              ^~
test.zig:14:31: note: initializer of comptime only struct must be comptime-known

Because b is a local variable, its location is not comptime known (we don’t have the stack pointer).

@compileLog() will print “[runtime value]” if a variable is runtime:

const StructA = struct {
    T: type = void,
    ptr: *StructB,
};

const StructB = struct {
    string: []const u8,
};

var b = StructB{ .string = "Hello" };

pub fn main() void {
    const a = StructA{ .ptr = &b };
    @compileLog(a.ptr.string);
    @compileLog(a);
}
Compile Log Output:
@as([]const u8, [runtime value])
@as(test.StructA, .{ .T = void, .ptr = test.b })

If I do this instead:

const StructA = struct {
    T: type = void,
    ptr: *StructB,
};

const StructB = struct {
    string: []const u8,
};

pub fn main() void {
    comptime var b = StructB{ .string = "Hello" };
    const a = StructA{ .ptr = &b };
    @compileLog(a.ptr.string);
    @compileLog(a);
}

I get this:

Compile Log Output:
@as([]const u8, "Hello"[0..5])
@as(test.StructA, .{ .T = void, .ptr = @as(test.StructB, .{ .string = &.{ ... }[0..(...)] }) })

My guess was there was some sort of mix-up between runtime and comptime in your code. One thing to try is to stick a T: type = void into Edge so that it’s a comptime-only struct too.

adding a T field actually made things “worse”, in that the assignment of “foo” to .upath had no effect on this field’s value INSIDE the struct as well…

hmmmm :thinking:

I’m baffled by the @ptrCast(). Since _upath is []const u8, it has be in a struct and not a extern struct. That doesn’t have a well-defined layout so comptime dereferencing shouldn’t work at all. For example, the following does not compile:

const std = @import("std");

const StructA = struct {
    T: type = void,
    ptr: *StructB,
};

const StructB = struct {
    string: []const u8,
};

const StructC = struct {
    string: []const u8,
};

pub fn main() void {
    comptime var c = StructC{ .string = "world" };
    const a = StructA{ .ptr = @ptrCast(&c) };
    std.debug.print("{s}\n", .{a.ptr.string});
}
test.zig:19:37: error: comptime dereference requires 'test.StructB' to have a well-defined layout
    std.debug.print("{s}\n", .{a.ptr.string});
                               ~~~~~^~~~~~~
test.zig:8:17: note: struct declared here
const StructB = struct {

if compiler segfaults and differing behavior between versions are in the mix of things you’re observing, I would guess this is compiler bug land, pinging @mlugg since this seems relevant to his area of interest

@chung-leong, thanks for pointing out that compiler segfault – I’ll take a look now.

@biosbob, it’s unfortunately difficult to figure out what’s going on without more details. Given that you said you were often crashing the compiler, chances are you’re hitting a compiler bug which would always crash in a Debug/ReleaseSafe compiler, but sometimes makes it through and does weird UB things in ReleaseFast. What platform are you building on? I might be able to send you a ReleaseSafe compiler build (sans LLVM, so you’ll have to build with -fno-emit-bin or something) so you can see if you can get us a compiler crash report.

1 Like

I’m baffled by the @ptrCast().

Assuming you mean the @ptrCast in the @compileLog output (that’s the only one I see in this thread), don’t read into that too much! The printing logic for comptime-known pointers is, ahem, not quite right currently (my own fault!) and can print some quite bizarre results at times.

1 Like

@mlugg – to be clear, the anomalous behavior and/or the crashes are occurring in my “upstream meta-program” which is built for DEBUG… in my OP (where the crash did not occur), inspection of the output generated by this upstream program immediately pointed to an issue; adding the print calls simply confirmed the weirdness…

I just want to confirm that the segfault did occur using compilation. Both zig build-exe test.zig and zig run test.zig end in a crash.

@mlugg, i’m on windows-86_64… as i mentioned, however, i’m not seeing any crashes for the example in the OP…

related question: is there some “dump” of an intermediate data structure used by the compiler which i could emit with some special flags???