VTable interface corrupted pointer

I have a problem with what i think is a corrupted pointer. I have a struct with some_value which I’m trying to access in the iterators next function. But the value becomes a totally scrambled, but only when i get the iterator from the query() function it seems. I have no idea what’s going on here.

Sorry for the lengthy code snippets but i think it might be required to understand where my mistakes are.

Struct with some value:

const Archetype = @This();
some_value: usize = 0,

pub fn query(self: *Archetype, comptime Q: type) q.QueryIterator(Q) {
    var iterator = ArchtypeQueryIterator(Q).create(self);
    return iterator.iterator();
}

Query iterator interface:

pub fn QueryIterator(comptime Q: type) type {
    return struct {
        const Self = @This();
        pub const VTable = struct {
            next: *const fn (ctx: *anyopaque) ?QueryReturnType(Q),
        };

        ptr: *anyopaque,
        vtable: *const VTable,

        pub fn next(self: *Self) ?QueryReturnType(Q) {
            return self.vtable.next(self.ptr);
        }
    };
}

Interface implementation whit some prints for debugging.
When i call iterator() to create the iterator the debug prints: “iterator some_value 0” as it should. I also put _ = next(ptr) here for testing and then it always prints “next some value 0” as it should,

pub fn ArchtypeQueryIterator(comptime Q: type) type {
    return struct {
        const Self = @This();
        archetype: *Archetype,
        cursor: usize = 0,

        pub fn create(archetype: *Archetype) Self {
            return .{ .archetype = archetype };
        }

        pub fn iterator(self: *Self) q.QueryIterator(Q) {
            const ptr: *anyopaque = @ptrCast(@alignCast(self));
            // prints some_value 0 correctly
            std.debug.print("\niterator some_value {}\n", .{self.archetype.some_value});
             // When next is called called here some_value prints 0 correctly
            _ = next(ptr); 
            return .{
                .ptr = self,
                .vtable = &.{
                    .next = next,
                },
            };
        }

        pub fn next(ctx: *anyopaque) ?q.QueryReturnType(Q) {
            const self: *Self = @ptrCast(@alignCast(ctx));
            std.debug.print("\nnext some_value {}", .{self.archetype.some_value});
            return null;
        }
    };
}

When i call the iterator like this which is what i intend the usage to be, the print from `ǹext()`` becomes some random number.

test "sometest" {
    var archetype = try Archetype.init(B1, allocator);
    defer archetype.deinit();
    // ...
    var query_iter = archetype.query(qry);
    // When next is called from here some_value returns some random value, corrupted ptr?
    _ = query_iter.next();  
}
// print looks like this:
// iterator some_value 0
// next some_value 0
// next some_value 356482285568

But if i create the iterator like this, the print becomes some_value 0 as it should.

test "sometest" {
    var archetype = try Archetype.init(B1, allocator);
    defer archetype.deinit();
    // ...
    var iterator = ArchtypeQueryIterator(qry).create(&archetype);
    var query_iter = iterator.iterator();
    // When next is called here it some_value prints 0 correctly
    _ = query_iter.next();
}
// print looks like this:
// iterator some_value 0
// next some_value 0
// next some_value 0

Hey @kALLEBALIK welcome to ziggit.

The iterator or ArchtypeQueryIterator value is created inside of this query function’s stack frame, when this function returns that stack frame kind of gets lost or consumed

This pointer ptr kind of gets overwritten when the stack frame gets consumed.
Since ArchtypeQueryIterator.iterator saves pointer to Self(ArchtypeQueryIterator) as QueryIterator.ptr which is a temporary stack value created on Archetype.query function’s stack frame.

You may be can find more information by reading this issue and also this issue.

(sorry, i deleted the post by mistake while trying to edit)

2 Likes

Thanks for your welcoming and thanks for your answer :). But from my perspective i return the iterator as a value and the archtype pointer is also just a value, i don’t understand why it should be invalid after the stackframe is returned?

1 Like

you’re creating iterator on this query’s stack frame (read the comment),

you stored self (pub fn iterator(self: *Self) q.QueryIterator(Q)) as ptr which is a pointer to query’s stack frame.

Ah, i see. I was so focused on archtype pointer but it’s the pointer to self that i pass to the VTable?

Yes, the pointer to ArchtypeQueryIterator get’s invalid(which you store as the QueryIterator’s ptr and inside the vtable too) beacuse it was created inside of the temporary stack frame of Archetype.query function.

Thanks for your help. I learnt something new. That was a hard one, next time ill might catch it myself :slight_smile:

1 Like