remi-dupre / aoc

A macro which defines a handful main for the Advent of Code
Apache License 2.0
35 stars 7 forks source link

[Bug] Recursion Limit #15

Open Stian108 opened 3 years ago

Stian108 commented 3 years ago

First of all thanks for the awesome project.

While doing day 22 today I bumped into a problem. If there are too many solutions in total the macro expansion hits a recursion limit. Here's a MWE exemplifying the issue.

aoc_main::main! {
    year 2020;
    // 60 parts
    day1 => p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p;
    day2 => p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p;
}

mod day1 {
    pub fn p(_: &str) -> &str {
        ""
    }
}

Resulting in the error:

error: recursion limit reached while expanding `$crate::format_args!`
 --> src/main.rs:1:1
  |
1 | / aoc_main::main! {
2 | |     year 2020;
3 | |     // 60 parts
4 | |     day1 => p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p;
5 | |     day2 => p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p,p;
6 | | }
  | |_^
  |
  = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`aoc_bug`)
  = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

The proposed fix by using #![recursion_limit="256"] works, but maybe there's a way to fix this in the crate.

Cheers.

remi-dupre commented 1 year ago

Hi! Thanks for your issue.

This seems like a tough one, I've just had a quick look for now. I expected to find some kind of recursive call to a macro_rules, which doesn't seem to be the case. So maybe this comes from a fundamental limitation in macro_rules or that I'm using them wrong (I'll have to read a bit of documentation).

If this happens to be a limitation in what macro_rules can achieve I might dive in a sort of v2 for this crate that would rely on proc macros. However I might not find the motivation to do that and it might not be such a good idea because proc macros based on syn / quote are known to take quite a bit of time to compile :confused:

rbtcollins commented 1 year ago

There is recursion in the macros today. This is a breakdown of the call tree for my local reproduction of this error

Using this input:

aoc_main::main! {
    year 2022;

    day10 : generate => part_1, part_2,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1,part_1;
    // day3             => part_1, part_2; // no generator, a &str is passed
}

https://github.com/remi-dupre/aoc/blob/bb08c555a403e56d86caec7f00bf51ec33187dbc/src/lib.rs#L154 (in main! calling base_main!)

https://github.com/remi-dupre/aoc/blob/bb08c555a403e56d86caec7f00bf51ec33187dbc/src/lib.rs#L117 (in base_main! calling parse!)

https://github.com/remi-dupre/aoc/blob/bb08c555a403e56d86caec7f00bf51ec33187dbc/src/parse/mod.rs#L90 (in parse! calling parse!)

https://github.com/remi-dupre/aoc/blob/bb08c555a403e56d86caec7f00bf51ec33187dbc/src/parse/mod.rs#L26 (in parse! calling parse!)

https://github.com/remi-dupre/aoc/blob/bb08c555a403e56d86caec7f00bf51ec33187dbc/src/parse/mod.rs#L64 (in parse! calling parse!)

https://github.com/remi-dupre/aoc/blob/bb08c555a403e56d86caec7f00bf51ec33187dbc/src/parse/mod.rs#L75 (in parse! calling parse!)

rbtcollins commented 1 year ago

New test with the in-tree example modified to include a new day at the end, and we see this: The entry that doesn't recurse indefinitely is: lib:154 lib:117 parse 90 With repeating section parse 26: handling 'day ...' with generator but creates ' sol' and feeds to parse parse 64 handling 'sol ...' but creates 'post_sol ...' parse 84 handling 'post_sol ...' but creates 'day ...' parse 15 handling 'day ...' with default generator parse 52 handling 'sol ...' creates 'post_sol ...' and feeds to parse parse 84 as above parse 37 handling 'day ...' with fallible solution parse 64 sol -> post_sol parse 84 post_sol -> day parse 26 generator day -> sol parse 64 sol -> post_sol

rbtcollins commented 1 year ago

Ok so having had a bit of a think; I think the recursion around each solution is quite easily fixable by a bit of macro golf.

Match $ ( $e:expr ) , * => $( gen_sol!($gen, $e) ); )*

And possibly main can be rephrased like that too.

remi-dupre commented 1 year ago

Yeah indeed, my first look was not accurate, thanks for investigating.

My hope currently is to rely much less on macros by finishing https://github.com/remi-dupre/aoc/pull/29. If I merge this PR the macro will be very short and will just generate a small code to initialize an helper struct with the list of solutions.

If I fail to make that relevant I will definitely use you solution :+1:

rbtcollins commented 11 months ago

AOC time is sneaking up on us fast. Do you need help to get ready? :)

remi-dupre commented 11 months ago

Well sadly I'm not spending any time on AOC this year :confused:

However, I think #29 was kind of ready to go, if one or two people would try it and confirm it solves this problem with no side effect I would be more confident to merge it :shrug: