paholg / typenum

Compile time numbers in Rust.
https://crates.io/crates/typenum
Other
513 stars 45 forks source link

Trait implementation works when specifying types directly but not as power #208

Open seftontycho opened 5 months ago

seftontycho commented 5 months ago

The following code compiles correctly when the following types are defined as:

pub type MANTISSA_ONE = U8388608;
pub type MANTISSA_TWO = U16777216;

But not when they are defined as:

pub type MANTISSA_ONE = <U2 as Pow<U23>>::Output;
pub type MANTISSA_TWO = <U2 as Pow<U24>>::Output;

Any idea what is going on here? Sorry for the spam and thank you for your time (this is my 3rd issue/question in the last week).

The following is as minimal as I could make it to still reproduce:

#![allow(dead_code)]

use std::ops::{Add, Div, Mul, Sub};

use private::IsLessPrivate;
use typenum::*;

pub type MANTISSA_ONE = <U2 as Pow<U23>>::Output;
pub type MANTISSA_TWO = <U2 as Pow<U24>>::Output;

pub struct MyStruct<T>(T);

pub trait TraitA<T> {
    type Output;
}

impl<M1, M2> TraitA<MyStruct<M2>> for MyStruct<M1>
where
    M1: Unsigned + Add<M2> + Mul<M2>,
    M2: Unsigned,
    Sum<M1, M2>: Unsigned
        + Cmp<MANTISSA_ONE>
        + Cmp<MANTISSA_TWO>
        + IsLessPrivate<MANTISSA_ONE, Compare<Sum<M1, M2>, MANTISSA_ONE>>,
    MyStruct<Prod<M1, M2>>: TraitB<Le<Sum<M1, M2>, MANTISSA_ONE>, True>,
{
    type Output = TraitBOut<MyStruct<Prod<M1, M2>>, Le<Sum<M1, M2>, MANTISSA_ONE>, True>;
}

pub trait TraitB<LHint: Bit, RHint: Bit> {
    type Output;
}

pub type TraitBOut<T, LHint, RHint> = <T as TraitB<LHint, RHint>>::Output;

impl<M> TraitB<True, False> for MyStruct<M>
where
    M: Unsigned + TraitBInner<op!(U1 + (MANTISSA_ONE / M)), False>,
    op!(U1 + (MANTISSA_ONE / M)): Unsigned,

    MANTISSA_ONE: Div<M>,
    U1: Add<Quot<MANTISSA_ONE, M>>,
{
    type Output = TraitBInnerOut<M, op!(U1 + (MANTISSA_ONE / M)), False>;
}

impl<M> TraitB<False, True> for MyStruct<M>
where
    M: Unsigned + Div<MANTISSA_TWO> + TraitBInner<op!(U1 + (M / MANTISSA_TWO)), True>,
    op!(U1 + (M / MANTISSA_TWO)): Unsigned,
    U1: Add<Quot<M, MANTISSA_TWO>>,
{
    type Output = TraitBInnerOut<M, op!(U1 + (M / MANTISSA_TWO)), True>;
}

// This has two different behaviours depending on the type of `Hint`
pub trait TraitBInner<U: Unsigned, Hint: Bit> {
    type Output;
}

pub type TraitBInnerOut<T, U, Hint> = <T as TraitBInner<U, Hint>>::Output;

impl<M, U> TraitBInner<U, True> for MyStruct<M>
where
    M: Unsigned + Add<U>,
    U: Unsigned,
{
    type Output = MyStruct<Sum<M, U>>;
}

impl<M, U> TraitBInner<U, False> for MyStruct<M>
where
    M: Unsigned + Sub<U>,
    U: Unsigned,
{
    type Output = MyStruct<Diff<M, U>>;
}

fn main() {
    type A = MyStruct<U1>;
    type B = MyStruct<U2>;
    type C = <A as TraitA<B>>::Output;
}
seftontycho commented 5 months ago

I have also noticed that while cargo run produces errors during compilation, compilation never ends. I believe I may have gotten stuck in unbounded recursion. This would also explain why my editor kept crashing my pc.

I can't see where the issue is however.

paholg commented 5 months ago

Strangely, cargo check succeeds, while cargo build fails and hangs. This definitely looks like a rust bug, I'd report there.

seftontycho commented 5 months ago

Both seem to crash for me. I will open an issue with rust anyway as I'm not sure it should hang.