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

[Merged by Bors] - Add attribute to ignore fields of derived labels #5366

Closed
wants to merge 7 commits into from

Conversation

JoJoJet
Copy link
Member

@JoJoJet JoJoJet commented Jul 18, 2022

Objective

Fixes #5362

Solution

Add the attribute #[label(ignore_fields)] for *Label types.

#[derive(SystemLabel)]
pub enum MyLabel {
    One,

    // Previously this was not allowed since labels cannot contain data.
    #[system_label(ignore_fields)]
    Two(PhantomData<usize>),
}

Notes

This label makes it possible for equality to behave differently depending on whether or not you are treating the type as a label. For example:

#[derive(SystemLabel, PartialEq, Eq)]
#[system_label(ignore_fields)]
pub struct Foo(usize);

If you compare it as a label, it will ignore the wrapped fields as the user requested. But if you compare it as a Foo, the derive will incorrectly compare the inner fields. I see a few solutions

  1. Do nothing. This is technically intended behavior, but I think we should do our best to prevent footguns.
  2. Generate impls of PartialEq and Eq along with the #[derive(Label)] macros. This is a breaking change as it requires all users to remove these derives from their types.
  3. Only allow PhantomData to be used with ignore_fields -- seems needlessly prescriptive.

Changelog

  • Added the ignore_fields attribute to the derive macros for *Label types.
  • Added an example showing off different forms of the derive macro.

@JoJoJet JoJoJet marked this pull request as ready for review July 18, 2022 12:13
#[derive(SystemLabel)]
pub enum GenericLabel<T> {
One,
#[system_label(ignore_fields)]
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
#[system_label(ignore_fields)]
#[system_label(ignore)]

Bikeshedding: I think this is marginally clearer, less verbose and more idiomatic.

Copy link
Member Author

Choose a reason for hiding this comment

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

To me, that implies that we're ignoring the entire variant, but we're only ignoring the fields.

Copy link
Member

@alice-i-cecile alice-i-cecile Jul 18, 2022

Choose a reason for hiding this comment

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

Oh I see 🤔 Yeah I think you're right, but we should make sure to make this clear in the docs.

Weird to think that you could set your enum label to the phantomdata variant...

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.

Some nits, but I'm on board with this solution and the underlying code implementation looks correct.

@alice-i-cecile alice-i-cecile added A-ECS Entities, components, systems, and events C-Bug An unexpected or incorrect behavior labels Jul 18, 2022
@alice-i-cecile alice-i-cecile added this to the Bevy 0.8 milestone Jul 18, 2022
@alice-i-cecile
Copy link
Member

Adding to the 0.8 milestone; I think it's important that we have a migration path here.

@alice-i-cecile
Copy link
Member

@IceSentry this plus #4957 will have to be added to the migration guide.


fn main() {
// Unit labels are always equal.
assert_eq!(UnitLabel.as_label(), UnitLabel.as_label());
Copy link
Member

Choose a reason for hiding this comment

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

These tests are critical, but won't be run in CI. I actually think that these should be moved to doc tests on SystemLabel.

Copy link
Member Author

Choose a reason for hiding this comment

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

I agree, but we can't actually add doctests to label types until #5367

@alice-i-cecile alice-i-cecile added the S-Blocked This cannot move forward until something else changes label Jul 18, 2022
@JoJoJet JoJoJet changed the title Optionally ignore label fields Add attribute to ignore fields of derived labels Jul 19, 2022
Copy link
Member

@cart cart left a comment

Choose a reason for hiding this comment

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

All of this additional complexity around ensuring labels don't have values makes me want to revisit #4957. All of this weirdness could be avoided if we just let labels have normal values, be normal structs, and use the derived Hash/PartialEq impls. I think that would be easier to document / understand for users.

Likewise, the benchmarks used to justify #4957 don't account for enums / longer names in the comparison, which I suspect might paint that impl in a worse light. Given the runtime cost scales with the struct name length, we should be benching real world names / enum lengths like VisibilitySystems::UpdateOrthographicFrusta, not the unit struct DummyLabel. And ideally we benchmark more complicated schedules with multiple labels. I'm a bit wary of things being optimized out when code only contains instance of a thing (ex: hashes, comparisons, collections, etc).

Too late to rethink for 0.8 (and i think we should merge this to make migrations smoother), but I think the @bevyengine/ecs-team should give this one last pass for 0.9 to do a final weighing of the pros and cons.

I'm not fully convinced one way or the other (and i do really like the elimination of boxing + removing the need for some trait impls in the api), but this doesn't feel clear cut to me yet.

@cart
Copy link
Member

cart commented Jul 19, 2022

bors r+

bors bot pushed a commit that referenced this pull request Jul 19, 2022
# Objective

Fixes #5362 

## Solution

Add the attribute `#[label(ignore_fields)]` for `*Label` types.

```rust
#[derive(SystemLabel)]
pub enum MyLabel {
    One,

    // Previously this was not allowed since labels cannot contain data.
    #[system_label(ignore_fields)]
    Two(PhantomData<usize>),
}
```

## Notes

This label makes it possible for equality to behave differently depending on whether or not you are treating the type as a label. For example:

```rust
#[derive(SystemLabel, PartialEq, Eq)]
#[system_label(ignore_fields)]
pub struct Foo(usize);
```

If you compare it as a label, it will ignore the wrapped fields as the user requested. But if you compare it as a `Foo`, the derive will incorrectly compare the inner fields. I see a few solutions

1. Do nothing. This is technically intended behavior, but I think we should do our best to prevent footguns.
2. Generate impls of `PartialEq` and `Eq` along with the `#[derive(Label)]` macros. This is a breaking change as it requires all users to remove these derives from their types.
3. Only allow `PhantomData` to be used with `ignore_fields` -- seems needlessly prescriptive.

---

## Changelog

* Added the `ignore_fields` attribute to the derive macros for `*Label` types.
* Added an example showing off different forms of the derive macro.

<!--
## Migration Guide

> This section is optional. If there are no breaking changes, you can delete this section.

- If this PR is a breaking change (relative to the last release of Bevy), describe how a user might need to migrate their code to support these changes
- Simply adding new functionality is not a breaking change.
- Fixing behavior that was definitely a bug, rather than a questionable design choice is not a breaking change.
-->
@bors bors bot changed the title Add attribute to ignore fields of derived labels [Merged by Bors] - Add attribute to ignore fields of derived labels Jul 19, 2022
@bors bors bot closed this Jul 19, 2022
@JoJoJet
Copy link
Member Author

JoJoJet commented Jul 19, 2022

Likewise, the benchmarks used to justify #4957 don't account for enums / longer names in the comparison, which I suspect might paint that impl in a worse light. Given the runtime cost scales with the struct name length, we should be benching real world names / enum lengths like VisibilitySystems::UpdateOrthographicFrusta, not the unit struct DummyLabel. And ideally we benchmark more complicated schedules with multiple labels. I'm a bit wary of things being optimized out when code only contains instance of a thing (ex: hashes, comparisons, collections, etc).

Note that on this branch I've optimized comparisons by storing a "key" (u64) along with the TypeId -- this means comparing labels is effectively the same as comparing u128s. This is blocked by #4341, though.

@cart
Copy link
Member

cart commented Jul 19, 2022

Ooh that would definitely close the gap / increase my comfort. I think thats worth pursuing.

@alice-i-cecile
Copy link
Member

I agree; I like the new PR quite a bit and it alleviates some of my worries. And, obviously, I think that #4341 is a good idea (especially with the autolabelling).

bors bot pushed a commit that referenced this pull request Jul 20, 2022
# Objective

I noticed while working on #5366 that the documentation for label types wasn't working correctly. Having experimented with this for a few weeks, I believe that generating docs in macros is more effort than it's worth.

## Solution

Add more boilerplate, copy-paste and edit the docs across types. This also lets us add custom doctests for specific types. Also, we don't need `concat_idents` as a dependency anymore.
inodentry pushed a commit to IyesGames/bevy that referenced this pull request Aug 8, 2022
# Objective

Fixes bevyengine#5362 

## Solution

Add the attribute `#[label(ignore_fields)]` for `*Label` types.

```rust
#[derive(SystemLabel)]
pub enum MyLabel {
    One,

    // Previously this was not allowed since labels cannot contain data.
    #[system_label(ignore_fields)]
    Two(PhantomData<usize>),
}
```

## Notes

This label makes it possible for equality to behave differently depending on whether or not you are treating the type as a label. For example:

```rust
#[derive(SystemLabel, PartialEq, Eq)]
#[system_label(ignore_fields)]
pub struct Foo(usize);
```

If you compare it as a label, it will ignore the wrapped fields as the user requested. But if you compare it as a `Foo`, the derive will incorrectly compare the inner fields. I see a few solutions

1. Do nothing. This is technically intended behavior, but I think we should do our best to prevent footguns.
2. Generate impls of `PartialEq` and `Eq` along with the `#[derive(Label)]` macros. This is a breaking change as it requires all users to remove these derives from their types.
3. Only allow `PhantomData` to be used with `ignore_fields` -- seems needlessly prescriptive.

---

## Changelog

* Added the `ignore_fields` attribute to the derive macros for `*Label` types.
* Added an example showing off different forms of the derive macro.

<!--
## Migration Guide

> This section is optional. If there are no breaking changes, you can delete this section.

- If this PR is a breaking change (relative to the last release of Bevy), describe how a user might need to migrate their code to support these changes
- Simply adding new functionality is not a breaking change.
- Fixing behavior that was definitely a bug, rather than a questionable design choice is not a breaking change.
-->
inodentry pushed a commit to IyesGames/bevy that referenced this pull request Aug 8, 2022
# Objective

I noticed while working on bevyengine#5366 that the documentation for label types wasn't working correctly. Having experimented with this for a few weeks, I believe that generating docs in macros is more effort than it's worth.

## Solution

Add more boilerplate, copy-paste and edit the docs across types. This also lets us add custom doctests for specific types. Also, we don't need `concat_idents` as a dependency anymore.
james7132 pushed a commit to james7132/bevy that referenced this pull request Oct 28, 2022
# Objective

Fixes bevyengine#5362 

## Solution

Add the attribute `#[label(ignore_fields)]` for `*Label` types.

```rust
#[derive(SystemLabel)]
pub enum MyLabel {
    One,

    // Previously this was not allowed since labels cannot contain data.
    #[system_label(ignore_fields)]
    Two(PhantomData<usize>),
}
```

## Notes

This label makes it possible for equality to behave differently depending on whether or not you are treating the type as a label. For example:

```rust
#[derive(SystemLabel, PartialEq, Eq)]
#[system_label(ignore_fields)]
pub struct Foo(usize);
```

If you compare it as a label, it will ignore the wrapped fields as the user requested. But if you compare it as a `Foo`, the derive will incorrectly compare the inner fields. I see a few solutions

1. Do nothing. This is technically intended behavior, but I think we should do our best to prevent footguns.
2. Generate impls of `PartialEq` and `Eq` along with the `#[derive(Label)]` macros. This is a breaking change as it requires all users to remove these derives from their types.
3. Only allow `PhantomData` to be used with `ignore_fields` -- seems needlessly prescriptive.

---

## Changelog

* Added the `ignore_fields` attribute to the derive macros for `*Label` types.
* Added an example showing off different forms of the derive macro.

<!--
## Migration Guide

> This section is optional. If there are no breaking changes, you can delete this section.

- If this PR is a breaking change (relative to the last release of Bevy), describe how a user might need to migrate their code to support these changes
- Simply adding new functionality is not a breaking change.
- Fixing behavior that was definitely a bug, rather than a questionable design choice is not a breaking change.
-->
james7132 pushed a commit to james7132/bevy that referenced this pull request Oct 28, 2022
# Objective

I noticed while working on bevyengine#5366 that the documentation for label types wasn't working correctly. Having experimented with this for a few weeks, I believe that generating docs in macros is more effort than it's worth.

## Solution

Add more boilerplate, copy-paste and edit the docs across types. This also lets us add custom doctests for specific types. Also, we don't need `concat_idents` as a dependency anymore.
ItsDoot pushed a commit to ItsDoot/bevy that referenced this pull request Feb 1, 2023
# Objective

Fixes bevyengine#5362 

## Solution

Add the attribute `#[label(ignore_fields)]` for `*Label` types.

```rust
#[derive(SystemLabel)]
pub enum MyLabel {
    One,

    // Previously this was not allowed since labels cannot contain data.
    #[system_label(ignore_fields)]
    Two(PhantomData<usize>),
}
```

## Notes

This label makes it possible for equality to behave differently depending on whether or not you are treating the type as a label. For example:

```rust
#[derive(SystemLabel, PartialEq, Eq)]
#[system_label(ignore_fields)]
pub struct Foo(usize);
```

If you compare it as a label, it will ignore the wrapped fields as the user requested. But if you compare it as a `Foo`, the derive will incorrectly compare the inner fields. I see a few solutions

1. Do nothing. This is technically intended behavior, but I think we should do our best to prevent footguns.
2. Generate impls of `PartialEq` and `Eq` along with the `#[derive(Label)]` macros. This is a breaking change as it requires all users to remove these derives from their types.
3. Only allow `PhantomData` to be used with `ignore_fields` -- seems needlessly prescriptive.

---

## Changelog

* Added the `ignore_fields` attribute to the derive macros for `*Label` types.
* Added an example showing off different forms of the derive macro.

<!--
## Migration Guide

> This section is optional. If there are no breaking changes, you can delete this section.

- If this PR is a breaking change (relative to the last release of Bevy), describe how a user might need to migrate their code to support these changes
- Simply adding new functionality is not a breaking change.
- Fixing behavior that was definitely a bug, rather than a questionable design choice is not a breaking change.
-->
ItsDoot pushed a commit to ItsDoot/bevy that referenced this pull request Feb 1, 2023
# Objective

I noticed while working on bevyengine#5366 that the documentation for label types wasn't working correctly. Having experimented with this for a few weeks, I believe that generating docs in macros is more effort than it's worth.

## Solution

Add more boilerplate, copy-paste and edit the docs across types. This also lets us add custom doctests for specific types. Also, we don't need `concat_idents` as a dependency anymore.
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-Bug An unexpected or incorrect behavior S-Blocked This cannot move forward until something else changes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

SystemLabel proc-macro no longer allows for generic enums
4 participants