Skip to content
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

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

Open
glandium opened this issue Jul 28, 2017 · 6 comments
Open
Labels
A-closures Area: closures (`|args| { .. }`) C-bug Category: This is a bug. 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.

@ChrisDenton ChrisDenton added the needs-triage-legacy Old issue that were never triaged. Remove this label once the issue has been sufficiently triaged. label Jul 16, 2023
@Enselic Enselic added A-closures Area: closures (`|args| { .. }`) T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. and removed needs-triage-legacy Old issue that were never triaged. Remove this label once the issue has been sufficiently triaged. 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-closures Area: closures (`|args| { .. }`) C-bug Category: This is a bug. 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

9 participants