Change:
I tried this code: I expected running to produce an error like: It does, but takes time exponential in the number of calls, although doesn't appear to use any extra memory. In the time take looks like: I believe it's also exponential in the number of calls in . The reason I had code like this was because I was using nom's parser combinators which results in lots of nested closures. I spotted it because this caused rust-analyzer to hang because cargo clippy hung (it looks like check was the issue), I had to kill clippy to fix it. I bisected this (with cargo-bisect-rustc) to find the regression in , which contained the following bors commits: merge of - Centril:rollup-rzvry15, r=Centril merge of - tmandry:rollup-0k6jiik, r=tmandry merge of - Manishearth:clippyup, r=Manishearth merge of - tanriol:explain_borrow-use-context-dominators, r=nagisa merge of - ehuss:update-cargo-books, r=alexcrichton modify labels: +regression-from-stable-to-stable -regression-untriaged
what version of rust are you using? Is this also present on nightly?
Good question! This repros on the latest nightly () and on stable (1.53.0 2021-06-17).
Regression means that it was linear, or exponential, but smaller?
I didn't looks to still be exponential just much quicker, and with a smaller base (~2 vs ~14):
looks related
Bisected to innocent-looking commit . Removing the caching from in master does indeed fix the issue. I haven't looked at the how or why or where that causes (other) problems though. cc
Hmm, the only thing that looks expensive there is the hashing - I wonder if it's hashing over and over again without ever getting a cache hit? Hashing has a fixed cost, but can be an arbitrary length:
cuts down on time for this testcase and others with invalid recursive opaque types by bailing out early if a recursion is found. This fixes the regression but the check time is still exponential in the recursion: #wraps|time (s) ---|--- 23|9.2 24|18.3 25|36.6 Not sure if that matters in practice though.
Assigning priority as discussed in the of the Prioritization Working Group. label -I-prioritize +P-high
That's the same as before the regression, right? I definitely think that's a change worth making :) I expect the doubling will have a completely different cause.
Yes, same ballpark.

Discussion:
expanded_cache: FxHashMap::default(), primary_def_id: Some(def_id), found_recursion: false, <ins> found_any_recursion: false, </ins> check_recursion: true, tcx: self, };

expanded_cache: FxHashMap<(DefId, SubstsRef<'tcx>), Ty<'tcx>>, primary_def_id: Option<DefId>, found_recursion: bool, <ins> found_any_recursion: bool, </ins> /// Whether or not to check for recursive opaque types. /// This is `true` when we're explicitly checking for opaque type /// recursion, and 'false' otherwise to avoid unnecessary work.

impl<'tcx> OpaqueTypeExpander<'tcx> { fn expand_opaque_ty(&mut self, def_id: DefId, substs: SubstsRef<'tcx>) -> Option<Ty<'tcx>> { <del> if self.found_recursion { </del> <ins> if self.found_any_recursion { </ins> return None; } let substs = substs.fold_with(self);

} else { // If another opaque type that we contain is recursive, then it // will report the error, so we don't have to. <ins> self.found_any_recursion = true; </ins> self.found_recursion = def_id == *self.primary_def_id.as_ref().unwrap(); None }

expanded_cache: FxHashMap::default(), primary_def_id: None, found_recursion: false, <ins> found_any_recursion: false, </ins> check_recursion: false, tcx, };

<ins> fn bar() -> impl Fn() { wrap(wrap(wrap(wrap(foo())))) } fn foo() -> impl Fn() { //~^ WARNING 5:1: 5:22: function cannot return without recursing [unconditional_recursion] //~| ERROR 5:13: 5:22: cannot resolve opaque type [E0720] wrap(wrap(wrap(wrap(wrap(wrap(wrap(foo()))))))) } fn wrap(f: impl Fn()) -> impl Fn() { move || f() } fn main() { } </ins>

<ins> warning: function cannot return without recursing --> $DIR/issue-87450.rs:5:1 | LL | fn foo() -> impl Fn() { | ^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing ... LL |     wrap(wrap(wrap(wrap(wrap(wrap(wrap(foo()))))))) |                                        ----- recursive call site | = note: `#[warn(unconditional_recursion)]` on by default = help: a `loop` may express intention better if this is on purpose error[E0720]: cannot resolve opaque type --> $DIR/issue-87450.rs:5:13 | LL | fn foo() -> impl Fn() { |             ^^^^^^^^^ recursive opaque type ... LL |     wrap(wrap(wrap(wrap(wrap(wrap(wrap(foo()))))))) |     ----------------------------------------------- returning here with type `impl Fn<()>` ... LL | fn wrap(f: impl Fn()) -> impl Fn() { |                          --------- returning this opaque type `impl Fn<()>` error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0720`. </ins>