LucioleMaléfique
LucioleMaléfique

Reputation: 770

"expected value, found module `self`" in macro recursive call argument

I'm reworking my rust ecs lib, and I'm re-writing the component iterator.

The main idea is to be able to iterate over any tuples of components, mutably or not, with invocation of my iteration macro like so:

for (pos: &mut Position, vel: &Velocity) in component_iterator!(compnents; mut Position, Velocity) {
    Position += Velocity;
}

where Position and Velocity are structs.

The way I'm parsing the macro input is with recursive calls, that re order the input and allows me generate code one component at the time.

The macro will generate a struct, implement the Iterator trait on it and return an instance of it. the issue comes from the macro implementing the Iterator trait.

Warning, lots of code coming in, I'll try to keep it short : Here is one of the four conditions I am parsing :

macro_rules! impl_iterator_inner {
    // we are adding a non terminal non mutable component.
    (
        impl<'a> Iterator for MacroGeneratedComponentIterator<'a> {
            type Item = ($($item_out:tt)*);
            fn next(&mut self) -> Option<Self::Item> {
                loop {
                    match self.current_component {
                        ($($match_out:tt)*)
                        MacroGeneratedComponentsEnum::EndOfIterator => {
                            let result = ($($result_out:tt)*);
                            self.current_entity += 1;
                            self.current_component = macro_generated_reset();
                            return Some(result);
                        }
                    }
                }
            }
        }, $comp:ident, $($rest:tt)+
    ) => {
        paste::paste! {           
            impl_iterator_inner!(
                impl<'a> Iterator for MacroGeneratedComponentIterator<'a> {
                    type Item = ($($item_out)* &'a $comp,);
                    fn next(&mut self) -> Option<Self::Item> {
                        loop {
                            match self.current_component {
                                (
                                    $($match_out)*
                                    MacroGeneratedComponentsEnum::$comp => {
                                        while {
                                            let elem = &self.[<$comp:snake>].peek()?;
                                            if elem.index > self.current_entity {
                                                self.current_entity = elem.index; // update the current entity
                                                self.current_component = macro_generated_reset();
                                                false
                                            } else if elem.index == self.current_entity {
                                                match self.active_entities.get(self.current_entity)? {
                                                    true => {
                                                        self.current_entity += 1; // current entity is inactive, go to next one
                                                        self.current_component = macro_generated_reset();
                                                    }
                                                    false => self.current_component = macro_generated_return_next(self.current_component),
                                                }
                                                false // stop iteration
                                            } else { true /* keep iterating */ }
                                        } {
                                            self.[<$comp:snake>].next();
                                        }
                                    }
                                )
                                MacroGeneratedComponentsEnum::EndOfIterator => {
                                    let result = (
                                        (
                                            $($result_out)*
                                            &self.[<$comp:snake>].next()?.elem,
                                        )
                                    );
                                    self.current_entity += 1;
                                    self.current_component = macro_generated_reset();
                                    return Some(result);
                                }
                            }
                        }
                    }
                }, $($rest)+
            );
        }
    };
}

in parameter, I'll take the whole impl, the next component and rest. Based on the current component, I will call the macro again with only the rest, adding to the impl the code for the current component. I have more of these, that treat mut $comp:ident for mutable components, and the terminaison cases, where I return the whole impl.

I need these recursive calls, because I can't find another way to properly treat the mut Component input, and the code generation is done at different places, and the macros have to be hygienic.

Now, to my problem :

When I'm trying this on a single component, I'm only reading the terminaison case (which is very similar to the shown case, and with no rest in the params) it works fine. But the moment I have a single recursive call, I'm getting an error :

"expected value, found module self" at every instance of the self keyword in my macro invocation. I though you could put anything in macro invocations, as long as it was parsed ?

I don't understand it as I'm generating a whole impl, so the &mut self is properly defined in the function ?

The issue can be explored more in detail here : https://github.com/VirgileHenry/Foundry (warning, lot of code in progress and refactor, so some examples won't work.)

What is going on here ?

EDIT :

Here is a minimal example reproducing the problem : I'm using recursive macro calls to generate an impl for a struct, and it throws the same error : https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=369340ee70847e631eef219883938827

Upvotes: 1

Views: 713

Answers (1)

Jmb
Jmb

Reputation: 23329

This is due to macro hygiene. The self value inside the macro is not accessible from the outside, and conversely the self value from the caller is not accessible from inside the macro. You can work around it by capturing self in a macro argument:

macro_rules! impl_macro {
    (
        impl MyTrait for MyStruct {
            fn draw(&mut $self:ident) {
                ($($out:tt)*)
            }
        }, $to_draw:expr, $($rest:tt)+
    ) => {
        impl_macro!(
            impl MyTrait for MyStruct {
                fn draw(&mut $self) {
                    (
                        $($out)*
                        print!("self data : {}", $self.data);
                        println!("{:?}", $to_draw);
                    )
                }
            }, $($rest)+
        );
    };
    (
        impl MyTrait for MyStruct {
            fn draw(&mut $self:ident) {
                ($($out:tt)*)
            }
        }, $to_draw:expr
    ) => {
        impl MyTrait for MyStruct {
            fn draw(&mut $self) {
                $($out)*
                print!("self data : {}", $self.data);
                println!("{:?}", $to_draw);
            }
        }
    };
}

struct MyStruct {
    pub data: u8,
}

trait MyTrait {
    fn draw(&mut self);
}

fn main() {
    impl_macro!(
        impl MyTrait for MyStruct {
            fn draw(&mut self) {
                ()
            }
        }, "first draw", "second", 12
    );

    let mut st = MyStruct { data: 0 };
    st.draw();
}

Playground

See also How to call methods on self in macros?

Upvotes: 2

Related Questions