Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Macro fails to generate generic implementation unless it is with ident #47090

Open
uzytkownik opened this Issue Dec 31, 2017 · 1 comment

Comments

Projects
None yet
3 participants
@uzytkownik
Copy link

uzytkownik commented Dec 31, 2017

I've tried several ways but it looks like the only way is to 'smuggle' the ops by using ident. It seems that macros should be able to stuck path with generic arguments as idents seems counterintuitive.

macro_rules! gen_impl1 {
    ($trt:path, $arg:ty, $fr:ty, $o:ty, $f:ident) => {
        impl $trt<$arg> for $fr {
            type Output = $o;
            fn $f(self, rhs: $arg) -> Self::Output {
                loop {}
            }
        }
    };
}

macro_rules! gen_impl2 {
    ($trt:tt, $arg:ty, $fr:ty, $o:ty, $f:ident) => {
        impl $trt<$arg> for $fr {
            type Output = $o;
            fn $f(self, rhs: $arg) -> Self::Output {
                loop {}
            }
        }
    };
}

macro_rules! gen_impl3 {
    ($trt:path, $arg:ty, $fr:ty, $o:ty, $f:ident) => {
        gen_impl3!([$trt<$arg>], $fr, $o, $f);
    };
    ([$($trt:tt)*], $fr:ty, $o:ty, $f:ident) => {
        impl $($trt)* for $fr {
            type Output = $o;
            fn $f(self, rhs: $arg) -> Self::Output {
                loop {}
            }
        }
    };
}

macro_rules! gen_impl4 {
    ($trt:path, $arg:ty, $fr:ty, $o:ty, $f:ident) => {
        gen_impl3!($trt<$arg>, $fr, $o, $f);
    };
    ($trt:ty, $fr:ty, $o:ty, $f:ident) => {
        impl $trt for $fr {
            type Output = $o;
            fn $f(self, rhs: $arg) -> Self::Output {
                loop {}
            }
        }
    };
}

macro_rules! gen_impl5 {
    ($($trt:ident)::*, $arg:ty, $fr:ty, $o:ty, $f:ident) => {
        impl $($trt)::*<$arg> for $fr {
            type Output = $o;
            fn $f(self, rhs: $arg) -> Self::Output {
                loop {}
            }
        }
    }
}

macro_rules! gen_impl_ident {
    ($trt:ident, $arg:ty, $fr:ty, $o:ty, $f:ident) => {
        impl $trt<$arg> for $fr {
            type Output = $o;
            fn $f(self, rhs: $arg) -> Self::Output {
                loop {}
            }
        }
    };
}

struct Foo;
struct Bar;
struct FooBar;

// Those fails with: no rules expected the token `<`
gen_impl1!(std::ops::Add, Foo, Bar, FooBar, add);
gen_impl2!(std::ops::Add, Foo, Bar, FooBar, add);
gen_impl3!(std::ops::Add, Foo, Bar, FooBar, add);
gen_impl4!(std::ops::Add, Foo, Bar, FooBar, add);

// This works
gen_impl5!(std::ops::Add, Foo, Bar, FooBar, add);
use std::ops::Add;
gen_impl_ident!(Add, Foo, Bar, FooBar, add);
@petrochenkov

This comment has been minimized.

Copy link
Contributor

petrochenkov commented Dec 31, 2017

So the idea is to take an arbitrary path a::b::c and attach an <arg> to the end of it?
That's indeed not allowed because something like a::b::c<xxx> is already a path and after attaching <arg> it becomes a::b::c<xxx><arg>, which is not a correct path.

For this reason you have to assemble the path manually and something like gen_impl5 looks like the correct approach.
(Except that + is preferable to *, with * the macro will accept something like gen_impl5!(, Foo, Bar, FooBar, add);)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.