Skip to content

reached the recursion limit while instantiating function_name::<[closure]> #43520

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
glandium opened this issue Jul 28, 2017 · 7 comments
Open
Labels
A-closures Area: Closures (`|…| { … }`) C-bug Category: This is a bug. I-monomorphization Issue: An error at monomorphization time. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@glandium
Copy link
Contributor

Here is a shortened version of the code I hit this with:
https://play.rust-lang.org/?gist=01e76f65024cbf57bb018547932aaef2&version=stable

The error message is everything but enlightening as to what is going wrong, and from a programmer perspective, there is nothing in there that should hit a recursion limit.

@bluss had this to say on irc:

So the first call is Arguments::from with F = U1 (some unique closure type for the initial closure) which recursively calls from with F = U2 (new type for || None) which calls from with F = U3 (new type for || None) And U2 and U3 are different because they are compiled in functions with different type parameters? U2 is compiled with F=U1 and U3 is compiled with F = U2
I think we can test if that's true
with this we have a closure with an anonymous type still, but that doesn't depend on any type parameter. It's Self::nop() in this code. https://play.rust-lang.org/?gist=c82188fa9cf9c592b1956f6a5b5babe4&version=nightly
so yeah, rustc could be a lot smarter in this case

Note that I worked around it by just manually inlining the recursion, which is also shorter.

@Mark-Simulacrum Mark-Simulacrum added the C-bug Category: This is a bug. label Jul 28, 2017
@aylei
Copy link

aylei commented Feb 10, 2019

I've got the same issue here: https://play.rust-lang.org/?gist=5253e1adb845862f26029cbf0423085c (I was trying to test if a given binary tree is a validate binary search tree)

@LukasKalbertodt
Copy link
Member

LukasKalbertodt commented Feb 10, 2019

Minimal example:

fn foo<F: Fn()>(x: bool, _: F) {
    if x {
        foo(false, || {})
    }
}

fn main() {
    foo(true, || {});
}

Error:

error: reached the recursion limit while instantiating `foo::<[closure@src/main.rs:3:20: 3:25]>`
 --> src/main.rs:1:1
  |
1 | / fn foo<F: Fn()>(x: bool, _: F) {
2 | |     if x {
3 | |         foo(false, || {})
4 | |     }
5 | | }
  | |_^

The error is gone if we use a function instead of a closure:

fn dummy() {}

fn foo<F: Fn()>(x: bool, _: F) {
    if x {
        foo(false, dummy)
    }
}

@Spoonbender
Copy link

Triage: no change

@sfzhu93
Copy link
Contributor

sfzhu93 commented Oct 4, 2022

This seems to be only a naming problem here, considering the following Go code can compile:

package main

type EmptyFunc func()

type FuncInterface interface {
	EmptyFunc
}

func bar(f EmptyFunc) EmptyFunc { return f }

func foo[T FuncInterface](flag bool, fp T) {
	if flag {
		foo(false, bar(func() {}))
	}
}

func main() {
	foo(false, bar(func() {}))
}

https://gotipplay.golang.org/p/k5ngVKP0XKE

but this code cannot:

fn bar<F: Fn()>(f: F) -> F {
    f
}

fn foo<F: Fn()>(x: bool, _: F) {
    if x {
        foo(false, bar(|| {}))
    }
}

fn main() {
    foo(true, bar(|| {}));
}

Also it is related to #77664

@CAD97
Copy link
Contributor

CAD97 commented Nov 29, 2022

considering the following Go code can compile

Go's generics are not monomorphized. Or more specifically, they're monomorphized per "GC shape," so in Rust terms you only have one instantiation foo::<fn()> rather than the actual recursive monomorphization of foo::<[closure@main]> calling foo::<[closure@foo::<[closure@main]>]> calling foo::<[closure@foo::<[closure@foo::<[closure@main]>]>]> etc.

@sfzhu93
Copy link
Contributor

sfzhu93 commented Nov 29, 2022

I checked the Go example's objdump result:
func foo[T FuncInterface](flag bool, fp T) { is represented as <main.foo[go.shape.func()_0]>:. Looks like they figure out the GC shape at an early stage, and thus make the recursive monomorphization end early.

Go's generics are not monomorphized. Or more specifically, they're monomorphized per "GC shape," so in Rust terms you only have one instantiation foo::<fn()> rather than the actual recursive monomorphization of foo::<[closure@main]> calling foo::<[closure@foo::<[closure@main]>]> calling foo::<[closure@foo::<[closure@foo::<[closure@main]>]>]> etc.

We didn't observe reuse of monomorphized methods from real-world benchmarks in our recent paper. My understanding is, GC shape is an optimization similar to the contributions from Polymorphization Working Group. But, from this example, looks like the GC shape recognition happens eariler than Rust, which could be helpful in solving cases like this.

@Enselic Enselic added A-closures Area: Closures (`|…| { … }`) T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. and removed needs-triage-legacy labels Aug 17, 2023
d1ngd0 added a commit to d1ngd0/dapt that referenced this issue Apr 19, 2024
rust-lang/rust#43520 there is a bug, that took
my morning away from me.
@rusty-snake
Copy link
Contributor

For all readers who run into this not so helpful error message during cargo build of cargo check passed code: Do some Box<dyn ???> indirection to get out of it.

@fmease fmease added the I-monomorphization Issue: An error at monomorphization time. label Feb 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-closures Area: Closures (`|…| { … }`) C-bug Category: This is a bug. I-monomorphization Issue: An error at monomorphization time. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests