`bsn!` generates invalid code when using unqualified enum variants/generic types
- Dominant language
- Rust
- Stars
- 48.2k
- Forks
- 4.8k
- Avg merge
- 3d 22h
- Merged PRs (30d)
- 161
Description
## Bevy version and features
0.19.0 RC 1 through 3
## What you did
Trying to construct unqualified enum variants in a `bsn!`, both as components or fields:
```rust
#[derive(Component, FromTemplate)]
enum Foo {
#[default]
Variant(u32),
}
// allows unqualified reference, Rust's prelude does this for `None`/`Some` et. al
use Foo::Variant;
let _ = bsn! {
Variant(42) // ERROR: expected type, found variant
};
```
```rust
#[derive(Clone, Default)]
enum Foo {
#[default]
Default,
Variant(u32),
}
use Foo::Variant;
#[derive(Component, FromTemplate)]
struct Bar { v: Foo }
let _ = bsn! { // ERROR: no field `0` on type `Foo`
Bar { v: Variant(42) } // ERROR: expected type, found variant
};
```
Note that this affects `Some`, `Ok`, `Err` etc.
Trying to construct generic types, again either as a component or a field:
```rust
#[derive(Component, FromTemplate)]
struct Baz {
v: T,
}
let _ = bsn! {
Baz { v: 42 } // ERROR: missing generics for struct `Baz`
};
```
## What went wrong
For unqualified variants, there are two separate issues: first the generated code attempts to use a variant symbol as a type, and second it attempts to assign fields of the variant which Rust does not permit. That is, `bsn!` generates code such as
```rust
let __value = _scene
.get_or_insert_template::<
::Template // needs to be `Foo as FromTemplate`
>(_context);
__value.v.0 = 42; // needs to be `__value.v = Variant(42)`
```
For generic types, `bsn!` yields code like `` but Rust does not permit such a cast to elide generics.
Unfortunately a proper fix for these is fundamentally impossible due to current limitations in Rust itself. `bsn!`'s implementation would need access to semantic information of code surrounding the invocation; i.e. whether a symbol is an enum variant and the variant's enclosing type, or if a given type has generics. Sadly there is no mechanism for proc macros to receive such information.
## Bandaid solutions
* maintain lists of symbols that if used incorrectly issue an error and suggestion to fix, e.g. `Some(x)` suggests `Option::Some(x)`, `DespawnOnExit` suggests `DespawnOnExit::<_>`
* pro: best possible learning experience, most users will first hit this on stdlib/Bevy types, error message can give rationale and link to this issue etc
* con: breaks on user/third party types which use the same names
* maintain lists of symbols which need special casing; e.g. `Some`/`Ok`/`Err`, `DespawnOnExit`/`DespawnOnEnter`
* pro: ergonomic win
* pro: allows use of voldemort types (those that cannot be named, e.g. the concrete type of closures)
* con: poor learning experience, issue will only ever come up with user/third party types and will require research to understand core issue
Contributor guide
Assessment
This issue has not been assessed yet.