auto_enums icon indicating copy to clipboard operation
auto_enums copied to clipboard

Add support for derive `Into<Target>`

Open vic1707 opened this issue 10 months ago • 11 comments

Fixes #163

Currently a WIP, the implementation works but isn't pretty nor clean. Refactor in progress, mostly moving code around, adding the feature gate, add tests, remove the 'convert' as a default feature etc...

If you already have suggestions @taiki-e I'll gladly read them ! Especially around the manual use of derive_utils::EnumImpl, I think derive_utils doesn't handle generic traits for now right ?

vic1707 avatar Feb 12 '25 13:02 vic1707

I think derive_utils doesn't handle generic traits for now right ?

It is supported. I think it just needs to do the same thing as any other core::convert traits:

https://github.com/taiki-e/auto_enums/blob/main/src/derive/core/convert.rs

taiki-e avatar Feb 12 '25 15:02 taiki-e

You're right, I was too focused on doing #[auto_enum(Into<u64>)] instead of #[auto_enum(Into)] and making the impl generic.

I tried that approach with great success regarding codegen. but no success regarding compilation With said approach

#[auto_enum(Into)]
fn a(x: u8) -> impl Into<u64> {
    match x {
        0 => 8_u8,
        1 => 16_u16,
        2 => 32_u32,
        _ => false
    }
}

generates

fn a(x: u8) -> impl Into<u64> {
    #[allow(non_camel_case_types)]
    enum __Enum6219237852123227426<__Variant0, __Variant1, __Variant2, __Variant3> {     
        __Variant0(__Variant0),
        __Variant1(__Variant1),
        __Variant2(__Variant2),
        __Variant3(__Variant3),
    }
    #[automatically_derived]
    impl<__Variant0, __Variant1, __Variant2, __Variant3, __T> ::core::convert::Into<__T>
    for __Enum6219237852123227426<__Variant0, __Variant1, __Variant2, __Variant3>        
    where
        __Variant0: ::core::convert::Into<__T>,
        __Variant1: ::core::convert::Into<__T>,
        __Variant2: ::core::convert::Into<__T>,
        __Variant3: ::core::convert::Into<__T>,
    {
        #[inline]
        fn into(self) -> __T {
            match self {
                __Enum6219237852123227426::__Variant0(x) => {
                    <__Variant0 as ::core::convert::Into<__T>>::into(x)
                }
                __Enum6219237852123227426::__Variant1(x) => {
                    <__Variant1 as ::core::convert::Into<__T>>::into(x)
                }
                __Enum6219237852123227426::__Variant2(x) => {
                    <__Variant2 as ::core::convert::Into<__T>>::into(x)
                }
                __Enum6219237852123227426::__Variant3(x) => {
                    <__Variant3 as ::core::convert::Into<__T>>::into(x)
                }
            }
        }
    }
    match x {
        0 => __Enum6219237852123227426::__Variant0(8_u8),
        1 => __Enum6219237852123227426::__Variant1(16_u16),
        2 => __Enum6219237852123227426::__Variant2(32_u32),
        _ => __Enum6219237852123227426::__Variant3(false),
    }
}

Which triggers a compile error

error[E0119]: conflicting implementations of trait `std::convert::Into<_>` for type `a::__Enum6219237852123227426<_, _, _, _>`
  --> examples\t.rs:10:1
   |
10 | #[auto_enum(Into)]
   | ^^^^^^^^^^^^^^^^^^
   |
   = note: conflicting implementation in crate `core`:
           - impl<T, U> std::convert::Into<U> for T
             where U: std::convert::From<T>;
   = note: this error originates in the attribute macro `::auto_enums::enum_derive` (in Nightly builds, run with -Z macro-backtrace for more info)

Because the enum itself could replace __T Therefore I think we will still need to take the target type as input to generate a specific impl 🤔 Another idea I have would be to generate a From implementation instead but we would need much more code I think.

On a nicer note this experimentation gave me hope for a TryFrom generation 🙃

used code gen
pub(crate) mod into {
    use crate::derive::prelude::*;

    pub(crate) const NAME: &[&str] = &["Into"];

    pub(crate) fn derive(_cx: &Context, data: &Data) -> Result<TokenStream> {
        Ok(derive_trait(data, &parse_quote!(::core::convert::Into), None, parse_quote! {
            trait Into<__T> {
                #[inline]
                fn into(self) -> __T;
            }
        }))
    }
}

vic1707 avatar Feb 12 '25 17:02 vic1707

Still need to update the changelog and see if i missed some docs but the implementation looks good to me let me know if it looks good too you too @taiki-e 🙃

vic1707 avatar Feb 14 '25 15:02 vic1707

Out of curiosity I tried a From implementation

fn a(x: u8) -> impl Into<u64> {
	enum __Enum6219237852123227426<
		__Variant0,
		__Variant1,
		__Variant2,
		__Variant3,
	> {
		__Variant0(__Variant0),
		__Variant1(__Variant1),
		__Variant2(__Variant2),
		__Variant3(__Variant3),
	}
	impl<__Variant0, __Variant1, __Variant2, __Variant3, __T>
		::core::convert::From<
			__Enum6219237852123227426<
				__Variant0,
				__Variant1,
				__Variant2,
				__Variant3,
			>,
		> for __T
	where
        __T: ::core::convert::From<__Variant0>,
        __T: ::core::convert::From<__Variant1>,
        __T: ::core::convert::From<__Variant2>,
        __T: ::core::convert::From<__Variant3>,
	{
		#[inline]
		fn from(
			enum_value: __Enum6219237852123227426<
				__Variant0,
				__Variant1,
				__Variant2,
				__Variant3,
			>,
		) -> __T {
			match enum_value {
				__Enum6219237852123227426::__Variant0(x) => x.into(),
				__Enum6219237852123227426::__Variant1(x) => x.into(),
				__Enum6219237852123227426::__Variant2(x) => x.into(),
				__Enum6219237852123227426::__Variant3(x) => x.into(),
			}
		}
	}
	match x {
		0 => __Enum6219237852123227426::__Variant0(8_u8),
		1 => __Enum6219237852123227426::__Variant1(16_u16),
		2 => __Enum6219237852123227426::__Variant2(32_u32),
		_ => __Enum6219237852123227426::__Variant3(false),
	}
}

fn main() {
    let a: u64 = a(12).into();
    println!("{a}");
}

But this seems to make rustc go crazy as I never get any compile error but also no program, no compilation, rust-analyzer is also unresponsive xD Using only 2 variant gives a compile error after a lot of struggling

vic1707 avatar Feb 18 '25 22:02 vic1707

Hello @taiki-e I rebased the PR, I think it's ready to be reviewed and merged 👍

vic1707 avatar Sep 24 '25 08:09 vic1707

We good 👍 @taiki-e

vic1707 avatar Sep 30 '25 17:09 vic1707

Could you add a test case to tests/auto_enum.rs to check that this is actually working? This PR currently has tests for cases where compilation fails, but not for cases where compilation succeeds.

taiki-e avatar Oct 01 '25 12:10 taiki-e

Sorry about that, took care of the other comments but managed to forgot that one. Test is written, passes when loaded from the ide without any issue but I can't get the cli cargo +nightly test --all-features --all to pass, I currently have no idea why. Will take a look at it again asap

vic1707 avatar Oct 01 '25 17:10 vic1707

~~The test seems to happen twice, once "Into" gets parsed but then another one only parses "Into" resulting in an error, I don't get why that second occurence appears~~

~~Seems like, the derive isn't passed properly to enum_derive or something like that, I'm kinda lost~~

~~It is but twice? Still lost but moving forward~~

~~Issue seems to come from context.rs line 355 let derive = args.iter().chain(traits); Into appears twice, once as Into<i64>, once as Into~~

Comes from the type_analysis feature, checking this out rn

vic1707 avatar Oct 01 '25 17:10 vic1707

Ok, I think I got it, I encourage you to double/triple check the type_analysis changes as I'm really not sure I understood its inner workings correctly

vic1707 avatar Oct 01 '25 20:10 vic1707