zakarumych / edict

Other
82 stars 6 forks source link

Unexpected result with spawn and query tracked #9

Closed akhilman closed 2 years ago

akhilman commented 2 years ago

I'm writing unit tests for for change tracking and got unexpected result. Here is simple snipped:

use edict::prelude::*;

#[derive(Debug)]
struct Foo();

fn main() {
    let mut world = World::default();

    world.spawn((Foo(),));
    let mut tracks = world.tracks_now();  // <- This tracks created between two inserts
    world.spawn((Foo(),));

    dbg!(&tracks);
    let result: Vec<_> = world.query::<(&Foo,)>().tracked_into_iter(&mut tracks).map(|x| x.clone()).collect();
    dbg!(&result);
    // println!("Tracks: {:?}", );
}

This code shows that both entities are modified:

    Finished dev [unoptimized + debuginfo] target(s) in 0.02s
     Running `target/debug/check_edict`
[src/main.rs:13] &tracks = Tracks {
    epoch: 1,
}
[src/main.rs:15] &result = [
    (
        EntityId {
            gen: 2,
            id: 0,
        },
        (
            Foo,
        ),
    ),
    (
        EntityId {
            gen: 2,
            id: 1,
        },
        (
            Foo,
        ),
    ),
]

Is this a bug or I'm doing something wrong?

akhilman commented 2 years ago

I have exactly the same result with world.query::<(Modified<&Foo>,)>()

akhilman commented 2 years ago

This is wired. Seems fetch.skip_item(...) always calls the generic method defined the by trait itself, not by defined by the concrete type trait implementation. Here is gdb's stacktrace:

#0  edict::query::Fetch::skip_item<(edict::query::modified::ModifiedFetchRead<check_edict::Foo>)> (self=0x7fffffffcf38, idx=0) at ../edict/src/query/mod.rs:60
#1  0x000055555556081a in edict::query::{impl#5}::next<(edict::query::modified::Modified<&check_edict::Foo>), ()> (self=0x7fffffffcf18) at ../edict/src/query/mod.rs:568
#2  0x0000555555561f93 in core::iter::adapters::map::{impl#2}::next<(edict::entity::id::EntityId, (&check_edict::Foo)), edict::query::QueryTrackedIter<(edict::query::modified::Modified<&check_edict::Foo>), ()>, check_edict::main::{closure_env#0}> (self=0x7fffffffcf18) at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/core/src/iter/adapters/map.rs:103
#3  0x000055555557181b in alloc::vec::spec_from_iter_nested::{impl#0}::from_iter<(edict::entity::id::EntityId, (&check_edict::Foo)), core::iter::adapters::map::Map<edict::query::QueryTrackedIter<(edict::query::modified::Modified<&check_edict::Foo>), ()>, check_edict::main::{closure_env#0}>> (iterator=...) at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/alloc/src/vec/spec_from_iter_nested.rs:26
#4  0x0000555555571f1c in alloc::vec::spec_from_iter::{impl#0}::from_iter<(edict::entity::id::EntityId, (&check_edict::Foo)), core::iter::adapters::map::Map<edict::query::QueryTrackedIter<(edict::query::modified::Modified<&check_edict::Foo>), ()>, check_edict::main::{closure_env#0}>> (iterator=...) at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/alloc/src/vec/spec_from_iter.rs:33
#5  0x00005555555739ce in alloc::vec::{impl#17}::from_iter<(edict::entity::id::EntityId, (&check_edict::Foo)), core::iter::adapters::map::Map<edict::query::QueryTrackedIter<(edict::query::modified::Modified<&check_edict::Foo>), ()>, check_edict::main::{closure_env#0}>> (iter=...) at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/alloc/src/vec/mod.rs:2552
#6  0x00005555555623ec in core::iter::traits::iterator::Iterator::collect<core::iter::adapters::map::Map<edict::query::QueryTrackedIter<(edict::query::modified::Modified<&check_edict::Foo>), ()>, check_edict::main::{closure_env#0}>, alloc::vec::Vec<(edict::entity::id::EntityId, (&check_edict::Foo)), alloc::alloc::Global>> (self=...) at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/core/src/iter/traits/iterator.rs:1778
#7  0x000055555556ba4b in check_edict::main () at src/main.rs:14
akhilman commented 2 years ago

All works as expected if we call skip_item directly, it is not:

-                    if !unsafe { self.fetch.skip_item(idx) } {
+                    if !unsafe { Q::Fetch::skip_item(&self.fetch, idx) } {
akhilman commented 2 years ago

The cause of this problem is that Fetch implementation for tuple's Query does not implement skip_item, skip_chunk methods. https://github.com/zakarumych/edict/blob/e1b4de9cfd2aa2ada66bccd83df730983fa636d9/src/query/mod.rs#L244

I'll fix this issue.