Region inference fails for closure parameter

9aaf496
Opened by Brian Koropoff at 2018-12-05 20:29:29

Test

enum Chain<'a:'b, 'b> {
    Base(&'a int),
    Rec(&'a int, &'b Chain<'a,'b>)
}

fn call_with_rec<'a, 'b>(c: Chain<'a, 'b>,
                         x: &'a int,
                         f: <'c>|Chain<'a, 'c>|) {
    f(Rec(x, &c))
}

fn main() {
    let x = 0i;
    let y = 0i;

    call_with_rec(Base(&x), &y, |mut inner| {
        loop {
            match inner {
                Base(&x) => {
                    println!("Base({})", x);
                    break;
                },
                Rec(&x, &p) => {
                    println!("Rec({})", x);
                    inner = p;
                }
            }
        }
    });
}

Output

test.rs:18:19: 18:24 error: cannot infer an appropriate lifetime due to conflicting requirements
test.rs:18             match inner {
                             ^~~~~
test.rs:18:19: 18:24 note: first, the lifetime cannot outlive the expression at 18:18...
test.rs:18             match inner {
                             ^~~~~
test.rs:18:19: 18:24 note: ...so type `Chain<'_,'_>` of expression is valid during the expression
test.rs:18             match inner {
                             ^~~~~
test.rs:16:45: 29:6 note: but, the lifetime must be valid for an anonymous lifetime defined on the block at 16:44...
test.rs:16     call_with_rec(Base(&x), &y, |mut inner| {
test.rs:17         loop {
test.rs:18             match inner {
test.rs:19                 Base(&x) => {
test.rs:20                     println!("Base({})", x);
test.rs:21                     break;
           ...
test.rs:25:21: 25:26 note: ...so that the reference type `Chain<'_,'_>` does not outlive the data it points at
test.rs:25                     inner = p;
                               ^~~~~

Workaround

The program compiles successfully if the closure is converted into a named function.

enum Chain<'a:'b, 'b> {
    Base(&'a int),
    Rec(&'a int, &'b Chain<'a,'b>)
}

fn call_with_rec<'a, 'b>(c: Chain<'a, 'b>,
                         x: &'a int,
                         f: <'c>|Chain<'a, 'c>|) {
    f(Rec(x, &c))
}

fn helper<'a, 'b>(mut inner: Chain<'a, 'b>) {
    loop {
        match inner {
            Base(&x) => {
                println!("Base({})", x);
                break;
            },
            Rec(&x, &p) => {
                println!("Rec({})", x);
                inner = p;
            }
        }
    }
}

fn main() {
    let x = 0i;
    let y = 0i;

    call_with_rec(Base(&x), &y, helper);
}

cc @nikomatsakis

I'm not sure if inference is intended to work in this scenario. It's not particularly serious since there's an easy workaround.

  1. I'm not sure if this is still relevant with the closure changes or not. @nikomatsakis ?

    Steve Klabnik at 2015-01-27 17:07:40

  2. I updated the code to Rust 1.0 as well as i could. Seems to produce the same errors. (playpen link)

    enum Chain<'a:'b, 'b> {
        Base(&'a i32),
        Rec(&'a i32, &'b Chain<'a,'b>)
    }
    
    fn call_with_rec<'a, 'b, F>(c: Chain<'a, 'b>,
                             x: &'a i32,
                             f: F) where F: for<'c> Fn(Chain<'a, 'c>) {
        f(Chain::Rec(x, &c))
    }
    
    fn main() {
        let x = 0;
        let y = 0;
    
        call_with_rec(Chain::Base(&x), &y, |mut inner| {
            loop {
                match inner {
                    Chain::Base(&x) => {
                        println!("Base({})", x);
                        break;
                    },
                    Chain::Rec(&x, &p) => {
                        println!("Rec({})", x);
                        inner = p;
                    }
                }
            }
        });
    }
    

    bluss at 2015-05-30 09:24:45

  3. The updated version now fails with 'cannot move out of borrowed content', which feels right.

    @bkoropoff if you're still seeing this in some form, please let me know, but closing until then.

    Steve Klabnik at 2016-11-29 21:02:13

  4. hmm. At this bug was originally filed, didn't we infer Copy for data types? Which would mean the appropriate update to 1.0 code would include a #[derive(Copy, Clone)] ?

    Felix S Klock II at 2018-12-05 20:25:34

  5. having said that, this attempt to update the code in the aforementioned manner also does not seem to replicate the bug anymore. Huzzah?

    play

    use self::Chain::{Base, Rec};
    
    #[derive(Copy, Clone)]
    enum Chain<'a:'b, 'b> {
        Base(&'a i32),
        Rec(&'a i32, &'b Chain<'a,'b>)
    }
    
    fn call_with_rec<'a, 'b, F: for <'c> Fn(Chain<'a, 'c>)>(c: Chain<'a, 'b>,
                             x: &'a i32,
                             f: F) {
        f(Rec(x, &c))
    }
    
    fn helper<'a, 'b>(mut inner: Chain<'a, 'b>) {
        loop {
            match inner {
                Base(&x) => {
                    println!("Base({})", x);
                    break;
                },
                Rec(&x, &p) => {
                    println!("Rec({})", x);
                    inner = p;
                }
            }
        }
    }
    
    fn main() {
        let x = 0i32;
        let y = 0i32;
    
        call_with_rec(Base(&x), &y, helper);
    
        call_with_rec(Base(&x), &y, |mut inner| {
           loop {
                match inner {
                    Base(&x) => {
                        println!("Base({})", x);
                        break;
                    },
                    Rec(&x, &p) => {
                        println!("Rec({})", x);
                        inner = p;
                    }
                }
            }
        })
    }
    

    Felix S Klock II at 2018-12-05 20:28:02