Lokathor / bytemuck

A crate for mucking around with piles of bytes
https://docs.rs/bytemuck
Apache License 2.0
697 stars 77 forks source link

Potential rustc bug related to deriving ByteEq #217

Closed LilyIsTrans closed 3 months ago

LilyIsTrans commented 8 months ago

Right off the bat, here's the minimum reproducible example: Cargo.toml:

[package]
name = "bug_rep"
version = "0.1.0"
edition = "2021"

[dependencies]
bytemuck = { version = "1.14.0", features = ["derive"] }
#[derive(bytemuck::ByteEq)]
pub struct Magnitude<const BYTES: usize>([u8; 0]);

The bug still occurs identically if swapping out bytemuck::ByteEq for bytemuck::ByteHash.

The error from cargo check is:

error[E0107]: missing generics for struct `Magnitude`
 --> src/lib.rs:2:12
  |
2 | pub struct Magnitude<const BYTES: usize>([u8; 0]);
  |            ^^^^^^^^^ expected 1 generic argument
  |
note: struct defined here, with 1 generic parameter: `BYTES`
 --> src/lib.rs:2:12
  |
2 | pub struct Magnitude<const BYTES: usize>([u8; 0]);
  |            ^^^^^^^^^ ------------------
help: add missing generic argument
  |
2 | pub struct Magnitude<BYTES><const BYTES: usize>([u8; 0]);
  |                     +++++++

For more information about this error, try `rustc --explain E0107`.
error: could not compile `bug_rep` (lib) due to 2 previous errors

Following it's inane suggestion, unsurprisingly, does not fix the issue.

I got this from the rather more reasonable code:

[package]
name = "magnitude"
version = "0.1.0"
edition = "2021"

[dependencies]
bytemuck = { version = "1.14.0", features = ["derive", "min_const_generics"] }
#[derive(Clone, Copy, bytemuck::Zeroable, bytemuck::Pod, bytemuck::ByteEq, bytemuck::ByteHash)]
#[repr(transparent)]
pub struct Magnitude<const BYTES: usize>([u8; BYTES]);

Which gives an identical error message other than that it shows the longer source code.

    Checking magnitude v0.1.0 (/home/lily/dev/magnitude)
error[E0107]: missing generics for struct `Magnitude`
 --> src/static_precision/mod.rs:3:12
  |
3 | pub struct Magnitude<const BYTES: usize>([u8; BYTES]);
  |            ^^^^^^^^^ expected 1 generic argument
  |
note: struct defined here, with 1 generic parameter: `BYTES`
 --> src/static_precision/mod.rs:3:12
  |
3 | pub struct Magnitude<const BYTES: usize>([u8; BYTES]);
  |            ^^^^^^^^^ ------------------
help: add missing generic argument
  |
3 | pub struct Magnitude<BYTES><const BYTES: usize>([u8; BYTES]);
  |                     +++++++

For more information about this error, try `rustc --explain E0107`.
error: could not compile `magnitude` (lib) due to 3 previous errors

Considering how inane the suggestion from rustc is, I suspect this may actually be a compiler bug, but I want to get a second opinion before I go opening an issue claiming a compiler error, especially for code involving macros I didn't write.

zachs18 commented 8 months ago

(cargo expand output on the example given above)

#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
pub struct Magnitude<const BYTES : usize>([u8; 0]);
impl ::core::cmp::PartialEq for Magnitude {
    #[inline]
    #[must_use]
    fn eq(&self, other: &Self) -> bool {
        ::bytemuck::bytes_of(self) == ::bytemuck::bytes_of(other)
    }
}
impl ::core::cmp::Eq for Magnitude {}

This isn't a compiler bug, the ByteEq derive macro doesn't currently emit generics on the PartialEq and Eq impls (which it probably should, or should emit an error that it doesn't support generics).

zachs18 commented 8 months ago

Opened a PR to allow deriving ByteEq and ByteHash for types with generics.

Note that ByteEq and ByteHash require the type implement NoUninit, which cannot currently be derived for generic structs, and must be manually verified to be correct and implemented.