#Validating mutable references

21 messages · Page 1 of 1 (latest)

inland lotus
#

I'm working on a Bevy system validator macro. In Bevy there is a ECS and a Query system parameter which can be used to query for entities and their components. But you can't borrow components mutably twice. So I need to check that. I already parsed all query arguments into a Vec<(Vec<QueryArg>, Vec<QueryFilter>)>. QueryFilter isn't relevant, so I'm gonna talk about QueryArg, which looks like this:

enum QueryArg {
    Entity,
    MutRef(TypePath),
    Ref(TypePath),
}

I need to check if theres an instance of MutRef, and if there is disallow all other instances of MutRef and Ref with the same TypePath. How could I achieve this?

dusty rune
#

Also, do you want to ensure that if a MutRef exists, there's also no Ref with the same path?

inland lotus
dusty rune
# inland lotus compare among all mutrefs in all vectors.
#[derive(PartialEq, Eq, Clone, Copy)]
enum PathKind {
  Shared,
  Mutable,
}

let mut seen = HashMap::new();
for elem in vec_of_vecs.iter().flatten() {
  match elem {
    QueryArg::MutRef(path) if seen.insert(path, PathKind::Mutable).is_some() => return Err(Error::ConflictingRefMut),
    QueryArg::Ref(path) if *seen.entry(path).or_insert(PathKind::Shared) == PathKind::Mutable => return Err(Error::ConflictingRefMut),
    _ => {}
  }
}
Ok(())
```I took the liberty of also erroring if a `Ref` path conflicts with a `RefMut` path, rather than only finding conflicts among `RefMut`s
inland lotus
#

interesting

inland lotus
# dusty rune ```rs #[derive(PartialEq, Eq, Clone, Copy)] enum PathKind { Shared, Mutable,...

While this works amazingly (thank you btw), I now need to consider the QueryFilters. And not compare among all vectors.
QueryFilter would look something like this:

enum QueryFilter {
  With(TypePath),
  Without(TypePath)
}

The QueryFilter tells the query to only include entity with/without this component.

We must enforce borrowing rules on the query based on all this.

Here are some examples:

// Valid
Query<&mut Transform>

// Invalid
Query<(&mut Transform, &Transform)>
Query<(&mut Transform, &mut Transform)>

Query<&mut Transform> Query<&Transform>
Query<&mut Transform> Query<&mut Transform>

Query<&mut Transform, With<MyComponent>> Query<&Transform>
Query<&mut Transform, With<MyComponent>> Query<&mut Transform>

// Valid
Query<&mut Transform, With<MyComponent>> Query<&Transform, Without<MyComponent>>
Query<&mut Transform, With<MyComponent>> Query<&mut Transform, Without<MyComponent>>

Query<&mut Transform, (With<A>, Without<B>)> Query<&Transform, (With<B>, Without<A>)>
Query<&mut Transform, (With<A>, Without<B>)> Query<&mut Transform, (With<B>, Without<A>)>
dusty rune
#

That is, if there cannot possibly exist a component that both filters would accept

inland lotus
#

Query<&Transform, With<MyComponent>> + Query<&Transform, With<MyComponent>> is valid because there are no mutable references

dusty rune
#

First idea: come up with a way to group queries according to their filters: Two queries fall in the same group if and only if there exists at least one component that is accepted by both of their filters. After this, we should, if I'm right, only need to run the algorithm from the previous step, the one that ignores filters, once per group.

#

The idea is that queries in different groups cannot possibly "overlap" because their filters won't let them

inland lotus
#

thats interesting

dusty rune
#

I do not promise it actually works, but it might?

inland lotus
#

problem is more generic queries will need to be in multiple groups

#

i.e. Query<_> is in the Query<_, With<A>> group and the Query<_, With<B>> group

dusty rune
#

That might duplicate our work some, but should be feasible

#

Of course, even just determining whether the intersection of two filters is disjoint is a problem I have no idea how to start solving

#

Given what you're doing appears to be Bevy's query disjointness analysis, maybe you could ask their own discord about it

inland lotus
#

I know Bevy panics when a Query is invalid, so I'm looking into the source code to see how its done