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

Stack overflow in compiler involving closures #22638

Closed
frewsxcv opened this issue Feb 21, 2015 · 5 comments
Closed

Stack overflow in compiler involving closures #22638

frewsxcv opened this issue Feb 21, 2015 · 5 comments
Labels
A-closures Area: Closures (`|…| { … }`) E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️

Comments

@frewsxcv
Copy link
Member

@jimtla pasted his code he was working with on IRC and I simplified it a bit:

#[derive(Clone)]
struct A (B);

impl A {
    pub fn matches<F: Fn()>(&self, f: &F) {
        let &A(ref term) = self;
        term.matches(f);
    }
}

#[derive(Clone)]
enum B {
    Variant1,
    Variant2(C),
}

impl B {
    pub fn matches<F: Fn()>(&self, f: &F) {
        match self {
            &B::Variant2(ref factor) => {
                factor.matches(&|| ())
            }
            _ => unreachable!("")
        }
    }
}

#[derive(Clone)]
struct C (D);

impl C {
    pub fn matches<F: Fn()>(&self, f: &F) {
        let &C(ref base) = self;
        base.matches(&|| {
            C(base.clone()).matches(f)
        })
    }
}

#[derive(Clone)]
struct D (Box<A>);

impl D {
    pub fn matches<F: Fn()>(&self, f: &F) {
        let &D(ref a) = self;
        a.matches(f)
    }
}

pub fn matches() {
    A(B::Variant1).matches(&(|| ()))
}

fn main() {}
/t/m/h/src (master|…) $ rustc lib.rs
lib.rs:18:36: 18:37 warning: unused variable: `f`, #[warn(unused_variables)] on by default
lib.rs:18     pub fn matches<F: Fn()>(&self, f: &F) {
                                             ^

thread 'rustc' has overflowed its stack
fish: Job 1, 'rustc lib.rs ' terminated by signal SIGILL (Illegal instruction)

This might be a duplicate of #21410

@kmcallister kmcallister changed the title Stack overflow involving closures Stack overflow in compiler involving closures Feb 21, 2015
@kmcallister kmcallister added the A-closures Area: Closures (`|…| { … }`) label Feb 21, 2015
@ebfull
Copy link
Contributor

ebfull commented Mar 21, 2015

I'm getting a similar stack overflow with closures. (I think this shouldn't be able to monomorphize?)

extern crate threadpool;

use std::sync::{Arc,Mutex};
use threadpool::ScopedPool;

fn test<'pool, F: FnMut(usize) + Send + Sync + 'pool>(mut callback: F, depth: usize, pool: Arc<Mutex<ScopedPool<'pool>>>) {
    if depth == 0 {
        callback(100);
    } else {
        struct Foo<F: FnMut(usize)> {
            callback: F
        }

        let foo = Arc::new(Mutex::new(Foo{callback: move |u| {
            callback(u);
        }}));

        for _ in 0..50 {
            struct Bar<F: FnMut(usize)> {
                callback: F
            }

            let foo = foo.clone();

            let bar = Arc::new(Mutex::new(Bar {
                callback: move |u| {
                    let foo = &mut *foo.lock().unwrap();

                    (foo.callback)(u);
                }
            }));

            for _ in 0..5 {
                let descending_pool = pool.clone();
                let pool = pool.lock().unwrap();
                let bar = bar.clone();
                pool.execute(move || {
                    test(move |n| {
                        let bar = &mut *bar.lock().unwrap();

                        (bar.callback)(n);
                    }, depth - 1, descending_pool);
                });
            }
        }
    }
}

fn main() {
    let pool = Arc::new(Mutex::new(ScopedPool::new(4)));

    test(|num| {
        println!("woohoo {}", num);
    }, 1, pool);
}
thread 'rustc' has overflowed its stack

@VladimirZhilyakov
Copy link

This code doesn't compile too.

struct Button<T: FnMut()> {
    f: Option<T>,
    title: &'static str,
}

impl<T: FnMut()> Button<T> {
    fn new() -> Button<T>
    { Button { f: None, title: "ClickMe" } }

    fn set(&mut self, f: T)
    { self.f = Some(f); }

    fn title(&mut self, title: &'static str)
    { self.title = title; }

    fn click(&mut self) {
        match self.f {
            Some(ref mut f) => f(),
            None => ()
        }
    }
}

fn main() {
    let mut btn = Button::new();
    btn.set(|| btn.title("Done"));
    btn.click();
    btn.click();
}

@jdm jdm added the I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ label Apr 12, 2015
@frewsxcv
Copy link
Member Author

Status update: my code no longer has an ICE, but instead has:

<anon>:5:5: 8:6 error: reached the recursion limit during monomorphization
<anon>:5     pub fn matches<F: Fn()>(&self, f: &F) {
<anon>:6         let &A(ref term) = self;
<anon>:7         term.matches(f);
<anon>:8     }
playpen: application terminated with error code 101

@ebfull
Copy link
Contributor

ebfull commented Apr 26, 2015

@frewsxcv That's good. Most of these code snippets should result in that sort of error. The type inference is hiding a recursive type definition which will necessarily arise during monomorphization. These are impossible to express statically.

By the way, here's a reduced example of the stack overflow:

fn main() {
    let mut test = None;
    test = Some(|| test.unwrap());
}

I think because it's otherwise impossible to express recursive type definitions in code, a lot of different areas of the compiler assume this will not happen. But with closures it can right now; we should probably check for that somewhere.

@pmarcelll
Copy link
Contributor

This seems to be fixed in nightly.

@alexcrichton alexcrichton added the E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. label Sep 1, 2015
bors added a commit that referenced this issue Sep 10, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-closures Area: Closures (`|…| { … }`) E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️
Projects
None yet
Development

No branches or pull requests

7 participants