Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve type inference in DynSystemParam::downcast() by making the type parameter match the return value. #15103

Conversation

chescock
Copy link
Contributor

@chescock chescock commented Sep 8, 2024

Objective

Right now, DynSystemParam::downcast() always requires the type parameter to be specified with a turbofish. Make it so that it can be inferred from the use of the return value, like:

fn expects_res_a(mut param: DynSystemParam) {
    let res: Res<A> = param.downcast().unwrap();
}

Solution

The reason this doesn't currently work is that the type parameter is a 'static version of the SystemParam so that it can be used with Any::downcast_mut(). Change the method signature so that the type parameter matches the return type, and use T::Item<'static, 'static> to get the 'static version. That means we wind up returning a T::Item<'static, 'static>::Item<'w, 's>, so constrain that to be equal to T. That works with every SystemParam implementation, since they have T::Item == T up to lifetimes.

@alice-i-cecile alice-i-cecile added A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use D-Straightforward Simple bug fixes and API improvements, docs, test and examples S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Sep 8, 2024
@alice-i-cecile
Copy link
Member

@re0312 are you able to review this?

Copy link
Member

@alice-i-cecile alice-i-cecile left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have a standalone test for type inference here? I think it'll make this much clearer if it breaks due to future changes.

@@ -1948,7 +1950,10 @@ impl<'w, 's> DynSystemParam<'w, 's> {

/// Returns the inner system param if it is the correct type.
/// This consumes the dyn param, so the returned param can have its original world and state lifetimes.
pub fn downcast<T: SystemParam + 'static>(self) -> Option<T::Item<'w, 's>> {
pub fn downcast<T: SystemParam>(self) -> Option<T>
where
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This where clause could probably use a doc comment: it's very complex.

@alice-i-cecile alice-i-cecile added S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Sep 9, 2024
Copy link
Contributor

@bushrat011899 bushrat011899 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a nice ergonomics change for the API, but I agree it needs a comment explaining the where clause since it's quite challenging to parse on first glance.

For consistency, the type signature and function body of is should also be updated.

// Before
pub fn is<T: SystemParam + 'static>(&self) -> bool {
    self.state.is::<ParamState<T>>()
}

// After
pub fn is<T: SystemParam>(&self) -> bool
where
    T::Item<'static, 'static>: SystemParam<Item<'w, 's> = T> + 'static,
{
    self.state.is::<ParamState<T::Item<'static, 'static>>>()
}

Comment on lines 548 to 552
let local = *p0.downcast_mut::<Local<usize>>().unwrap();
let query_count = p1.downcast_mut::<Query<()>>().unwrap().iter().count();
let _entities = p2.downcast_mut::<&Entities>().unwrap();
assert!(p0.downcast_mut::<Query<()>>().is_none());
local + query_count
let local: Local<usize> = p0.downcast_mut().unwrap();
let query: Query<()> = p1.downcast_mut().unwrap();
let _entities: &Entities = p2.downcast_mut().unwrap();
*local + query.iter().count()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand that this change you're proposing allows for the variable type to infer the type parameter on downcast/_mut, but this change feels rather pointless. In the case of query_count, I'd even say it's a regression to move away from turbofish syntax.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I had this here to check that inference actually worked, but @alice-i-cecile's suggestion to make a dedicated test for that means I can put this back.

Comment on lines -1897 to +1899
/// let foo: Res<A> = param.downcast::<Res<A>>().unwrap();
/// let res = param.downcast_mut::<Res<A>>().unwrap();
/// // The type parameter can be left out if it can be determined from use.
/// let res: Res<A> = param.downcast().unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice documentation, clearly shows the benefit this PR adds.

Copy link
Contributor

@re0312 re0312 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might not be very familiar with this part, but it is nice to see improvements of ergonomics.

@chescock
Copy link
Contributor Author

For consistency, the type signature and function body of is should also be updated.

Hmm, I had been thinking that it would be simpler to leave it with the simpler type signature, but I think you're right that being consistent is more clear. I'll change it to match.

@chescock chescock added S-Needs-Review Needs reviewer attention (from anyone!) to move forward and removed S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged labels Sep 12, 2024
@alice-i-cecile alice-i-cecile added S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Sep 12, 2024
@alice-i-cecile alice-i-cecile added this pull request to the merge queue Sep 16, 2024
Merged via the queue into bevyengine:main with commit 382917f Sep 16, 2024
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use D-Straightforward Simple bug fixes and API improvements, docs, test and examples S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants