taiki-e / derive_utils

A procedural macro helper for easily writing custom derives for enums.
https://docs.rs/derive_utils
Apache License 2.0
31 stars 2 forks source link

error[E0275]: overflow evaluating the requirement `B: Iterator` #47

Closed erikbrinkman closed 9 months ago

erikbrinkman commented 1 year ago

This library seems to enter an infinite loop when trying to analyze recursive structures, even if the manual implementation works. I'm not super familiar with proc macros, so looking around a bit didn't turn up an obvious fix, but here's a reproduceable error:

use iter_enum::Iterator;
use std::vec::IntoIter;

#[derive(Iterator)]
pub enum A {
    A(Box<B>),
}

#[derive(Iterator)]
pub enum B {
    C(IntoIter<i32>),
    B(A),
}

and the corresponding implementation that works that I assumed the macro would generate:

impl Iterator for A {
    type Item = <B as Iterator>::Item;

    fn next(&mut self) -> Option<Self::Item> {
        match self {
            A::A(b) => b.next(),
        }
    }
}

impl Iterator for B {
    type Item = <IntoIter<i32> as Iterator>::Item;

    fn next(&mut self) -> Option<Self::Item> {
        match self {
            B::C(i) => i.next(),
            B::B(a) => a.next(),
        }
    }
}
taiki-e commented 1 year ago

Could you provide output from cargo-expand (and the full error message for it)?

My guess is that the compiler cannot handle the Iterator bounds generated for the field types. https://github.com/taiki-e/iter-enum/blob/b4701422711834496824be87b24c492cfb000ae3/tests/expand/iterator.expanded.rs#L7-L9

If the field contains generics, these bounds are definitely needed, but it is okay to skip the generation of these bounds if the field does not contain generics.

erikbrinkman commented 1 year ago

Here's the output of cargo expand I haven't had time yet to think about the extra bounds

#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
use iter_enum::Iterator;
use std::vec::IntoIter;
pub enum A {
    A(Box<B>),
}
impl ::core::iter::Iterator for A
where
    Box<B>: ::core::iter::Iterator,
{
    type Item = <Box<B> as ::core::iter::Iterator>::Item;
    #[inline]
    fn next(&mut self) -> ::core::option::Option<Self::Item> {
        match self {
            A::A(x) => ::core::iter::Iterator::next(x),
        }
    }
    #[inline]
    fn size_hint(&self) -> (usize, ::core::option::Option<usize>) {
        match self {
            A::A(x) => ::core::iter::Iterator::size_hint(x),
        }
    }
    #[inline]
    fn count(self) -> usize {
        match self {
            A::A(x) => ::core::iter::Iterator::count(x),
        }
    }
    #[inline]
    fn last(self) -> ::core::option::Option<Self::Item> {
        match self {
            A::A(x) => ::core::iter::Iterator::last(x),
        }
    }
    #[inline]
    fn nth(&mut self, n: usize) -> ::core::option::Option<Self::Item> {
        match self {
            A::A(x) => ::core::iter::Iterator::nth(x, n),
        }
    }
    #[inline]
    #[must_use = "if you really need to exhaust the iterator, consider `.for_each(drop)` instead"]
    fn collect<__U: ::core::iter::FromIterator<Self::Item>>(self) -> __U {
        match self {
            A::A(x) => ::core::iter::Iterator::collect(x),
        }
    }
    #[inline]
    fn partition<__U, __F>(self, f: __F) -> (__U, __U)
    where
        __U: ::core::default::Default + ::core::iter::Extend<Self::Item>,
        __F: ::core::ops::FnMut(&Self::Item) -> bool,
    {
        match self {
            A::A(x) => ::core::iter::Iterator::partition(x, f),
        }
    }
    #[inline]
    fn fold<__U, __F>(self, init: __U, f: __F) -> __U
    where
        __F: ::core::ops::FnMut(__U, Self::Item) -> __U,
    {
        match self {
            A::A(x) => ::core::iter::Iterator::fold(x, init, f),
        }
    }
    #[inline]
    fn all<__F>(&mut self, f: __F) -> bool
    where
        __F: ::core::ops::FnMut(Self::Item) -> bool,
    {
        match self {
            A::A(x) => ::core::iter::Iterator::all(x, f),
        }
    }
    #[inline]
    fn any<__F>(&mut self, f: __F) -> bool
    where
        __F: ::core::ops::FnMut(Self::Item) -> bool,
    {
        match self {
            A::A(x) => ::core::iter::Iterator::any(x, f),
        }
    }
    #[inline]
    fn find<__P>(&mut self, predicate: __P) -> ::core::option::Option<Self::Item>
    where
        __P: ::core::ops::FnMut(&Self::Item) -> bool,
    {
        match self {
            A::A(x) => ::core::iter::Iterator::find(x, predicate),
        }
    }
    #[inline]
    fn find_map<__U, __F>(&mut self, f: __F) -> ::core::option::Option<__U>
    where
        __F: ::core::ops::FnMut(Self::Item) -> ::core::option::Option<__U>,
    {
        match self {
            A::A(x) => ::core::iter::Iterator::find_map(x, f),
        }
    }
    #[inline]
    fn position<__P>(&mut self, predicate: __P) -> ::core::option::Option<usize>
    where
        __P: ::core::ops::FnMut(Self::Item) -> bool,
    {
        match self {
            A::A(x) => ::core::iter::Iterator::position(x, predicate),
        }
    }
}
pub enum B {
    C(IntoIter<i32>),
    B(A),
}
impl ::core::iter::Iterator for B
where
    IntoIter<i32>: ::core::iter::Iterator,
    A: ::core::iter::Iterator<Item = <IntoIter<i32> as ::core::iter::Iterator>::Item>,
{
    type Item = <IntoIter<i32> as ::core::iter::Iterator>::Item;
    #[inline]
    fn next(&mut self) -> ::core::option::Option<Self::Item> {
        match self {
            B::C(x) => ::core::iter::Iterator::next(x),
            B::B(x) => ::core::iter::Iterator::next(x),
        }
    }
    #[inline]
    fn size_hint(&self) -> (usize, ::core::option::Option<usize>) {
        match self {
            B::C(x) => ::core::iter::Iterator::size_hint(x),
            B::B(x) => ::core::iter::Iterator::size_hint(x),
        }
    }
    #[inline]
    fn count(self) -> usize {
        match self {
            B::C(x) => ::core::iter::Iterator::count(x),
            B::B(x) => ::core::iter::Iterator::count(x),
        }
    }
    #[inline]
    fn last(self) -> ::core::option::Option<Self::Item> {
        match self {
            B::C(x) => ::core::iter::Iterator::last(x),
            B::B(x) => ::core::iter::Iterator::last(x),
        }
    }
    #[inline]
    fn nth(&mut self, n: usize) -> ::core::option::Option<Self::Item> {
        match self {
            B::C(x) => ::core::iter::Iterator::nth(x, n),
            B::B(x) => ::core::iter::Iterator::nth(x, n),
        }
    }
    #[inline]
    #[must_use = "if you really need to exhaust the iterator, consider `.for_each(drop)` instead"]
    fn collect<__U: ::core::iter::FromIterator<Self::Item>>(self) -> __U {
        match self {
            B::C(x) => ::core::iter::Iterator::collect(x),
            B::B(x) => ::core::iter::Iterator::collect(x),
        }
    }
    #[inline]
    fn partition<__U, __F>(self, f: __F) -> (__U, __U)
    where
        __U: ::core::default::Default + ::core::iter::Extend<Self::Item>,
        __F: ::core::ops::FnMut(&Self::Item) -> bool,
    {
        match self {
            B::C(x) => ::core::iter::Iterator::partition(x, f),
            B::B(x) => ::core::iter::Iterator::partition(x, f),
        }
    }
    #[inline]
    fn fold<__U, __F>(self, init: __U, f: __F) -> __U
    where
        __F: ::core::ops::FnMut(__U, Self::Item) -> __U,
    {
        match self {
            B::C(x) => ::core::iter::Iterator::fold(x, init, f),
            B::B(x) => ::core::iter::Iterator::fold(x, init, f),
        }
    }
    #[inline]
    fn all<__F>(&mut self, f: __F) -> bool
    where
        __F: ::core::ops::FnMut(Self::Item) -> bool,
    {
        match self {
            B::C(x) => ::core::iter::Iterator::all(x, f),
            B::B(x) => ::core::iter::Iterator::all(x, f),
        }
    }
    #[inline]
    fn any<__F>(&mut self, f: __F) -> bool
    where
        __F: ::core::ops::FnMut(Self::Item) -> bool,
    {
        match self {
            B::C(x) => ::core::iter::Iterator::any(x, f),
            B::B(x) => ::core::iter::Iterator::any(x, f),
        }
    }
    #[inline]
    fn find<__P>(&mut self, predicate: __P) -> ::core::option::Option<Self::Item>
    where
        __P: ::core::ops::FnMut(&Self::Item) -> bool,
    {
        match self {
            B::C(x) => ::core::iter::Iterator::find(x, predicate),
            B::B(x) => ::core::iter::Iterator::find(x, predicate),
        }
    }
    #[inline]
    fn find_map<__U, __F>(&mut self, f: __F) -> ::core::option::Option<__U>
    where
        __F: ::core::ops::FnMut(Self::Item) -> ::core::option::Option<__U>,
    {
        match self {
            B::C(x) => ::core::iter::Iterator::find_map(x, f),
            B::B(x) => ::core::iter::Iterator::find_map(x, f),
        }
    }
    #[inline]
    fn position<__P>(&mut self, predicate: __P) -> ::core::option::Option<usize>
    where
        __P: ::core::ops::FnMut(Self::Item) -> bool,
    {
        match self {
            B::C(x) => ::core::iter::Iterator::position(x, predicate),
            B::B(x) => ::core::iter::Iterator::position(x, predicate),
        }
    }
}
taiki-e commented 1 year ago

Thanks, I tried the output of cargo-expand in the playground, and removing the following bound seems to fix the error. So I think the way described in https://github.com/taiki-e/derive_utils/issues/47 (skip the generation of the trait bound) will work.

    A: ::core::iter::Iterator<Item = <IntoIter<i32> as ::core::iter::Iterator>::Item>,
taiki-e commented 9 months ago

Fixed in derive_utils 0.14.0 & iter-enum 1.1.2.