rust-lang / rust-lang/rust

Infinite recursion in type resolution for unused type

Open
#134,719 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

C-bug E-needs-mcve fixed-by-next-solver I-hang T-types
Dominant language
Rust
Stars
119k
Forks
16.2k
PR merge metrics
PR metrics pending

Description

Example:

use std::collections::HashMap;
use std::marker::PhantomData;

trait Resource {}

struct Res<'w, T: ?Sized + Resource>(&'w T);

impl<'w, 'a, T: Resource> IntoIterator for &'a Res<'w, T>
where
    &'a T: IntoIterator,
{
    type Item = <&'a T as IntoIterator>::Item;
    type IntoIter = <&'a T as IntoIterator>::IntoIter;

    fn into_iter(self) -> Self::IntoIter {
        self.0.into_iter()
    }
}

trait Matcher<T> {}

impl<'a, A, B, U: Matcher<&'a A>, V: Matcher<&'a B>> Matcher<(&'a A, &'a B)> for (U, V) {}

struct EqMatcher<T>(PhantomData<T>);

impl<T> Matcher<T> for EqMatcher<T> {}

struct ContainerMatcher<T>(Box<dyn Matcher<T>>);

impl<T, ContainerT> Matcher<ContainerT> for ContainerMatcher<T> where
    ContainerT: IntoIterator<Item = T>
{
}

#[test]
fn it_works() {
    // todo just to get the types to work.
    let matcher: Box<(EqMatcher<&u32>, ContainerMatcher<(&u32, &u32)>)> = todo!();
    // This is converting the Box into a Box<dyn Matcher>
    let bad_matcher = ContainerMatcher(matcher);

    let blah = HashMap::<u32, HashMap<u32, u32>>::default();
    fn check<T>(val: T, matcher: impl Matcher<T>) {}
    check(&blah, bad_matcher);
}

As a short summary: I am implementing a trait for one struct to operate on anything that impls IntoIterator. In addition this trait "distributes" to elements in a pair. I then try and merge these two properties to construct bad_matcher. For context, this happens as a result of merging the googletest and bevy_ecs crates.

I expected to see this happen: The test to compile.

Instead, this happened: Failed to compile due to infinite recursion of the Res type (which is completely unused).

error[E0275]: overflow evaluating the requirement `&_: IntoIterator`
  --> src\lib.rs:38:40
   |
38 |     let bad_matcher = ContainerMatcher(matcher);
   |                                        ^^^^^^^
   |
   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`bevy_googletest`)
note: required for `&Res<'_, _>` to implement `IntoIterator`
  --> src\lib.rs:8:27
   |
8  | impl<'w, 'a, T: Resource> IntoIterator for &'a Res<'w, T>
   |                           ^^^^^^^^^^^^     ^^^^^^^^^^^^^^
9  | where
10 |     &'a T: IntoIterator,
   |            ------------ unsatisfied trait bound introduced here
   = note: 123 redundant requirements hidden
   = note: required for `&Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, ...>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` to implem
ent `IntoIterator`
note: required for `ContainerMatcher<(&u32, &u32)>` to implement `Matcher<&Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<
'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res
<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Re
s<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, _>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
  --> src\lib.rs:30:21
   |
30 | impl<T, ContainerT> Matcher<ContainerT> for ContainerMatcher<T> where
   |                     ^^^^^^^^^^^^^^^^^^^     ^^^^^^^^^^^^^^^^^^^
31 |     ContainerT: IntoIterator<Item = T>
   |                              -------- unsatisfied trait bound introduced here
   = note: 1 redundant requirement hidden
   = note: required for `(EqMatcher, ContainerMatcher<(&u32, &u32)>)` to implement `Matcher<(&u32, &Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res
<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Re
s<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, R
es<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, _>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>)>`
   = note: required for the cast from `Box<(EqMatcher, ContainerMatcher<(&u32, &u32)>)>` to `Box<dyn Matcher<(&u32, &Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Res<'_, Re
s<'_, Res<'_, Res<'_, ...>>>>>>>>>>>>>>>>>>>>>>>>>>>>)>>`
   = note: the full name for the type has been written to 'E:\Content\bevy_googletest\target\debug\deps\bevy_googletest-3fef145a913b892a.long-type-9581655154092012888.txt'
   = note: consider using `--verbose` to print the full type name to the console
   = note: the full name for the type has been written to 'E:\Content\bevy_googletest\target\debug\deps\bevy_googletest-3fef145a913b892a.long-type-1543903567385044461.txt'
   = note: consider using `--verbose` to print the full type name to the console
Meta

rustc --version --verbose:

rustc 1.83.0 (90b35a623 2024-11-26)
binary: rustc
commit-hash: 90b35a6239c3d8bdabc530a6a0816f7ff89a0aaf
commit-date: 2024-11-26
host: x86_64-pc-windows-msvc
release: 1.83.0
LLVM version: 19.1.1

This seems to be fixed with -Znext-solver.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the minimal example in src/lib.rs with rustc 1.83.0, then compare the default solver with -Znext-solver. Inspect the emitted long-type files and the overflow chain to determine why the unused Res type enters trait evaluation. Done should include a regression test showing the example compiles without recursive type-resolution overflow.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.