dtolnay / proc-macro-workshop

Learn to write Rust procedural macros  [Rust Latam conference, Montevideo Uruguay, March 2019]
Apache License 2.0
4k stars 1.01k forks source link

Full path in compiler error for bitfield 04 #55

Closed robamu closed 1 year ago

robamu commented 1 year ago

I am trying to solve 04 right now. There is just a small thing left where I was wondering how this was done in the reference implementation.

My error output currently looks like this:

EXPECTED:
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
error[E0277]: the trait bound `bitfield::checks::SevenMod8: bitfield::checks::TotalSizeIsMultipleOfEightBits` is not satisfied
  --> tests/04-multiple-of-8bits.rs:53:1
   |
53 | #[bitfield]
   | ^^^^^^^^^^^ the trait `bitfield::checks::TotalSizeIsMultipleOfEightBits` is not implemented for `bitfield::checks::SevenMod8`
   |
   = help: the trait `bitfield::checks::TotalSizeIsMultipleOfEightBits` is implemented for `bitfield::checks::ZeroMod8`
   = note: this error originates in the attribute macro `bitfield` (in Nightly builds, run with -Z macro-backtrace for more info)
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈

ACTUAL OUTPUT:
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
error[E0277]: the trait bound `SevenMod8: TotalSizeIsMultipleOfEightsBits` is not satisfied
   --> tests/04-multiple-of-8bits.rs:53:1
    |
53  | #[bitfield]
    | ^^^^^^^^^^^ the trait `TotalSizeIsMultipleOfEightsBits` is not implemented for `SevenMod8`
    |
    = help: the trait `TotalSizeIsMultipleOfEightsBits` is implemented for `ZeroMod8`
note: required by a bound in `width_check`
   --> src/lib.rs
    |
    |     pub fn width_check<T: TotalSizeIsMultipleOfEightsBits>() {}
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `width_check`
    = note: this error originates in the attribute macro `bitfield` (in Nightly builds, run with -Z macro-backtrace for more info)
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈

I am happy with this, but I was curious why my output does not have the full paths as the reference implementation had and what to consider to get those full paths.

Kind Regards Robin

dtolnay commented 1 year ago

Obviously either output is fine so you can replace 04-multiple-of-8bits.stderr if you like yours.

I checked and my reference implementation does past the current test, on current stable as well as nightly. According to cargo expand here is exactly what it expands to for 04-multiple-of-8bits.rs:

#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
use bitfield::*;
type A = B1;
type B = B3;
type C = B4;
type D = B23;
#[repr(C)]
pub struct NotQuiteFourBytes {
    data: [u8; {
        0 + <A as bitfield::Specifier>::BITS as usize
            + <B as bitfield::Specifier>::BITS as usize
            + <C as bitfield::Specifier>::BITS as usize
            + <D as bitfield::Specifier>::BITS as usize
    } / 8],
}
impl NotQuiteFourBytes {
    pub fn new() -> Self {
        let _: bitfield::MultipleOfEight<
            [(); {
                0 + <A as bitfield::Specifier>::BITS as usize
                    + <B as bitfield::Specifier>::BITS as usize
                    + <C as bitfield::Specifier>::BITS as usize
                    + <D as bitfield::Specifier>::BITS as usize
            } % 8],
        >;
        NotQuiteFourBytes {
            data: [0; {
                0 + <A as bitfield::Specifier>::BITS as usize
                    + <B as bitfield::Specifier>::BITS as usize
                    + <C as bitfield::Specifier>::BITS as usize
                    + <D as bitfield::Specifier>::BITS as usize
            } / 8],
        }
    }
}
impl NotQuiteFourBytes {
    fn get(&self, offset: usize, width: u8) -> u64 {
        let mut val = 0;
        for i in 0..(width as usize) {
            let offset = i + offset;
            let byte_index = offset / 8;
            let bit_offset = offset % 8;
            let byte = self.data[byte_index];
            let mask = 1 << bit_offset;
            if byte & mask == mask {
                val |= 1 << i;
            }
        }
        val
    }
    fn set(&mut self, offset: usize, width: u8, val: u64) {
        for i in 0..(width as usize) {
            let mask = 1 << i;
            let val_bit_is_set = val & mask == mask;
            let offset = i + offset;
            let byte_index = offset / 8;
            let bit_offset = offset % 8;
            let byte = &mut self.data[byte_index];
            let mask = 1 << bit_offset;
            if val_bit_is_set {
                *byte |= mask;
            } else {
                *byte &= !mask;
            }
        }
    }
    pub fn get_a(&self) -> <A as bitfield::Specifier>::GetterType {
        let val = self.get(0, <A as bitfield::Specifier>::BITS);
        <A as bitfield::Specifier>::from_u64(val)
    }
    pub fn set_a(&mut self, val: <A as bitfield::Specifier>::SetterType) {
        let val = <A as bitfield::Specifier>::into_u64(val);
        debug_assert!(val <= bitfield::max::<A>());
        self.set(0, <A as bitfield::Specifier>::BITS, val);
    }
    pub fn get_b(&self) -> <B as bitfield::Specifier>::GetterType {
        let val = self
            .get(
                0 + <A as bitfield::Specifier>::BITS as usize,
                <B as bitfield::Specifier>::BITS,
            );
        <B as bitfield::Specifier>::from_u64(val)
    }
    pub fn set_b(&mut self, val: <B as bitfield::Specifier>::SetterType) {
        let val = <B as bitfield::Specifier>::into_u64(val);
        debug_assert!(val <= bitfield::max::<A>());
        self.set(
            0 + <A as bitfield::Specifier>::BITS as usize,
            <B as bitfield::Specifier>::BITS,
            val,
        );
    }
    pub fn get_c(&self) -> <C as bitfield::Specifier>::GetterType {
        let val = self
            .get(
                0 + <A as bitfield::Specifier>::BITS as usize
                    + <B as bitfield::Specifier>::BITS as usize,
                <C as bitfield::Specifier>::BITS,
            );
        <C as bitfield::Specifier>::from_u64(val)
    }
    pub fn set_c(&mut self, val: <C as bitfield::Specifier>::SetterType) {
        let val = <C as bitfield::Specifier>::into_u64(val);
        debug_assert!(val <= bitfield::max::<A>());
        self.set(
            0 + <A as bitfield::Specifier>::BITS as usize
                + <B as bitfield::Specifier>::BITS as usize,
            <C as bitfield::Specifier>::BITS,
            val,
        );
    }
    pub fn get_d(&self) -> <D as bitfield::Specifier>::GetterType {
        let val = self
            .get(
                0 + <A as bitfield::Specifier>::BITS as usize
                    + <B as bitfield::Specifier>::BITS as usize
                    + <C as bitfield::Specifier>::BITS as usize,
                <D as bitfield::Specifier>::BITS,
            );
        <D as bitfield::Specifier>::from_u64(val)
    }
    pub fn set_d(&mut self, val: <D as bitfield::Specifier>::SetterType) {
        let val = <D as bitfield::Specifier>::into_u64(val);
        debug_assert!(val <= bitfield::max::<A>());
        self.set(
            0 + <A as bitfield::Specifier>::BITS as usize
                + <B as bitfield::Specifier>::BITS as usize
                + <C as bitfield::Specifier>::BITS as usize,
            <D as bitfield::Specifier>::BITS,
            val,
        );
    }
}
impl std::fmt::Debug for NotQuiteFourBytes {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        f.debug_struct("NotQuiteFourBytes")
            .field("a", &self.get_a())
            .field("b", &self.get_b())
            .field("c", &self.get_c())
            .field("d", &self.get_d())
            .finish()
    }
}
fn main() {}
robamu commented 1 year ago

Ah okay.. many segments look similar. Maybe it is something with the specific mod 8 check? This is how my expansion looks

#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
use bitfield::*;
type A = B1;
type B = B3;
type C = B4;
type D = B23;
#[repr(C)]
pub struct NotQuiteFourBytes {
    raw_data: [u8; (<A as Specifier>::BITS + <B as Specifier>::BITS
        + <C as Specifier>::BITS + <D as Specifier>::BITS) / 8],
}
impl NotQuiteFourBytes {
    const OFFSET_A: usize = 0;
    const OFFSET_B: usize = Self::OFFSET_A + <A as bitfield::Specifier>::BITS;
    const OFFSET_C: usize = Self::OFFSET_B + <B as bitfield::Specifier>::BITS;
    const OFFSET_D: usize = Self::OFFSET_C + <C as bitfield::Specifier>::BITS;
    const FULL_LEN_MOD_EIGHT: usize = (<A as Specifier>::BITS + <B as Specifier>::BITS
        + <C as Specifier>::BITS + <D as Specifier>::BITS) % 8;
    pub fn new() -> Self {
        bitfield::checks::width_check::<
            <bitfield::checks::NumDummy<
                { Self::FULL_LEN_MOD_EIGHT },
            > as bitfield::checks::NumToGeneric>::GENERIC,
        >();
        Self {
            raw_data: [0; (<A as Specifier>::BITS + <B as Specifier>::BITS
                + <C as Specifier>::BITS + <D as Specifier>::BITS) / 8],
        }
    }
    pub fn raw_data(&self) -> &[u8] {
        self.raw_data.as_ref()
    }
    pub fn set_a(&mut self, val: <A as bitfield::Specifier>::UTYPE) {
        <A as bitfield::Specifier>::write_to_bytes(
            val,
            Self::OFFSET_A,
            self.raw_data.as_mut(),
        );
    }
    pub fn set_b(&mut self, val: <B as bitfield::Specifier>::UTYPE) {
        <B as bitfield::Specifier>::write_to_bytes(
            val,
            Self::OFFSET_B,
            self.raw_data.as_mut(),
        );
    }
    pub fn set_c(&mut self, val: <C as bitfield::Specifier>::UTYPE) {
        <C as bitfield::Specifier>::write_to_bytes(
            val,
            Self::OFFSET_C,
            self.raw_data.as_mut(),
        );
    }
    pub fn set_d(&mut self, val: <D as bitfield::Specifier>::UTYPE) {
        <D as bitfield::Specifier>::write_to_bytes(
            val,
            Self::OFFSET_D,
            self.raw_data.as_mut(),
        );
    }
    pub fn get_a(&self) -> <A as bitfield::Specifier>::UTYPE {
        <A as bitfield::Specifier>::read_from_bytes(
            Self::OFFSET_A,
            self.raw_data.as_ref(),
        )
    }
    pub fn get_b(&self) -> <B as bitfield::Specifier>::UTYPE {
        <B as bitfield::Specifier>::read_from_bytes(
            Self::OFFSET_B,
            self.raw_data.as_ref(),
        )
    }
    pub fn get_c(&self) -> <C as bitfield::Specifier>::UTYPE {
        <C as bitfield::Specifier>::read_from_bytes(
            Self::OFFSET_C,
            self.raw_data.as_ref(),
        )
    }
    pub fn get_d(&self) -> <D as bitfield::Specifier>::UTYPE {
        <D as bitfield::Specifier>::read_from_bytes(
            Self::OFFSET_D,
            self.raw_data.as_ref(),
        )
    }
}
fn main() {}

I used an empty generic function to do the check. The bitfield::MultipleOfEight looks like magic to me, or/and I am unfamiliar with that syntax for generics. Is it a struct?

Kind Regards Robin