Rust's `generic_const_exprs` feature and "cycle detected when building an abstract representation..."

48 views Asked by At

This is a problem when implementing a bitfield macro in Rust.

pub trait Bits {
    const BITS: usize; // How many bits this field has

    type Underlying; // Minimum underlying uint (u8, u16, ...) to store this field

    fn from_arbint(from: arbitrary_int::UInt<Self::Underlying, { Self::BITS }>) -> Self;
}

This code uses an arbitrary precision integer library: arbitrary-int. Trait Bits is used to describe:

  1. How many bits this field has.
  2. Minimum underlying uint type to store this field, like u8, u16, .. etc.
  3. How to convert an arbitrary_int::UInt<T, BITS> to this field.

For example, for a bool type:

impl Bits for bool {
    const BITS: usize = 1;

    type Underlying = u8;

    fn from_arbint(from: arbitrary_int::UInt<Self::Underlying, { Self::BITS }>) -> Self {
        from.value() == 1
    }
}

For array type it becomes a bit more complicated:

// Used to calculate the minimun uint type to store bits 
pub trait CalcUint {
    type Uint;
}
impl CalcUint for [(); 1] {
    type Uint = u8;
}
...
impl CalcUint for [(); 9] {
    type Uint = u16;
}
...
impl<T, const N: usize> Bits for [T; N]
where
    T: Bits + Default + Copy,
    [(); T::BITS * N]: CalcUint,
{
    const BITS: usize = T::BITS * N;

    type Underlying = <[(); T::BITS * N] as CalcUint>::Uint;

    fn from_arbint(from: arbitrary_int::UInt<Self::Underlying, { Self::BITS }>) -> Self {
        todo!()
    }
}

Then the compiler report error at { Self::BITS } in from_arbint:

error[E0391]: cycle detected when building an abstract representation for `<impl at src/lib.rs:53:1: 56:33>::from_arbint::{constant#0}`
  --> src/lib.rs:62:64
   |
62 |     fn from_arbint(from: arbitrary_int::UInt<Self::Underlying, { Self::BITS }>) -> Self {
   |                                                                ^^^^^^^^^^^^^^
   |
note: ...which requires building THIR for `<impl at src/lib.rs:53:1: 56:33>::from_arbint::{constant#0}`...
  --> src/lib.rs:62:64
   |
62 |     fn from_arbint(from: arbitrary_int::UInt<Self::Underlying, { Self::BITS }>) -> Self {
   |                                                                ^^^^^^^^^^^^^^
note: ...which requires type-checking `<impl at src/lib.rs:53:1: 56:33>::from_arbint::{constant#0}`...
  --> src/lib.rs:62:72
   |
62 |     fn from_arbint(from: arbitrary_int::UInt<Self::Underlying, { Self::BITS }>) -> Self {
   |                                                                        ^^^^
   = note: ...which requires evaluating trait selection obligation `[T; N]: Bits`...
   = note: ...which again requires building an abstract representation for `<impl at src/lib.rs:53:1: 56:33>::from_arbint::{constant#0}`, completing the cycle
note: cycle used when checking that `<impl at src/lib.rs:53:1: 56:33>` is well-formed
  --> src/lib.rs:53:1
   |
53 | / impl<T, const N: usize> Bits for [T; N]
54 | | where
55 | |     T: Bits + Default + Copy,
56 | |     [(); T::BITS * N]: CalcUint,
   | |________________________________^
   = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information

Can someone help to explain why is this happening and how to fix it? Thanks!

0

There are 0 answers