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

Lambda argument lifetime is tied to lifetime of unrelated iterator #92093

Closed
starptr opened this issue Dec 19, 2021 · 6 comments
Closed

Lambda argument lifetime is tied to lifetime of unrelated iterator #92093

starptr opened this issue Dec 19, 2021 · 6 comments
Labels
C-bug Category: This is a bug.

Comments

@starptr
Copy link

starptr commented Dec 19, 2021

It looks like the lifetime of the argument in str_to_i32 is tied to the line iterator for some reason, even though they can have different lifetimes:

use std::io::{BufRead, BufReader, Result};

fn main() -> Result<()> {
    let input = "\n28, 36, 1, 3, 9 \n 69, 420, 21, 22, 23".as_bytes();
    let reader = BufReader::new(input);

    let str_to_i32 = |str| i32::from_str_radix(str, 10).unwrap();

    let mut lines = reader.lines().map(|wrapped| wrapped.unwrap());

    for _i in 0..1 {
        let line = lines.next().unwrap();
        // NOTE: line that errors
        let _nums = line.split_whitespace().map(str_to_i32);
    }
    Ok(())
}

I expect this code to compile. Instead, rustc complains that

error[E0597]: `line` does not live long enough
  --> test.rs:14:21
   |
14 |         let _nums = line.split_whitespace().map(str_to_i32);
   |                     ^^^^^^^^^^^^^^^^^^^^^^^     ---------- borrow later used here
   |                     |
   |                     borrowed value does not live long enough
15 |     }
   |     - `line` dropped here while still borrowed

error: aborting due to previous error

Meta

rustc --version --verbose:

rustc 1.57.0 (f1edd0429 2021-11-29)
binary: rustc
commit-hash: f1edd0429582dd29cccacaf50fd134b05593bd9c
commit-date: 2021-11-29
host: x86_64-unknown-linux-gnu
release: 1.57.0
LLVM version: 13.0.0

Same behavior on both beta and nightly.

(Backtrace is irrelevant here.)

@starptr starptr added the C-bug Category: This is a bug. label Dec 19, 2021
@nrabulinski
Copy link
Contributor

Worth noting that explicitly declaring the closure as |str: &str| ... compiles, which leads me to believe the issue is with rust inferring the argument type as &'a str where 'a is the concrete lifetime of the first line.

@nrabulinski
Copy link
Contributor

This is the minimal example I could come up with https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=3703eb374998f226f08fc76b922d112e

@Patrick-Poitras
Copy link
Contributor

Patrick-Poitras commented Dec 19, 2021

I'm playing around with getting familiar with the MIR, and I have generated it for both a failing version

fn main() {
    let foo = |a| a+a;
    for _ in 0..0 {
        let x = 5;
        foo(&x);
    }
}

and a working version which substitutes

let foo = |a: &i32| a + a;

My understanding of the MIR is extremely rudimentary, but by diffing the only difference in the first MIR files, other than storage allocation, is where in the failing version you have this:

StorageLive(_1);                 // scope 0 at .\issue-92093\src\main.rs:2:9: 2:12
_1 = [closure@.\issue-92093\src\main.rs:2:15: 2:24]; // scope 0 at .\issue-92093\src\main.rs:2:15:
[compiler_fork.zip](https://github.com/rust-lang/rust/files/7741199/compiler_fork.zip)
[mir_compare.zip](https://github.com/rust-lang/rust/files/7741203/mir_compare.zip)
 2:24
                                 // closure
                                 // + def_id: DefId(0:4 ~ main[8185]::main::{closure#0})
                                 // + substs: [
                                 //     i8,
                                 //     extern "rust-call" fn((&i32,)) -> i32,
                                 //     (),
                                 // ]

in the working version you have this: version of the "extern rust-call" line

for<'r> extern "rust-call" fn((&'r i32,)) -> i32,

The closures themselves don't have massive differences in the MIR other than allocations and the variable ranges changing.

It seems that the bug is happening in the code that figures out what the lifetime of the closure is. This confirms what we had already figured out, but doesn't add much else.

Additionally, I can add that it fails during/after the SimplifyCfg step in the MIR generation. I don't know enough to say if that's relevant or not.

All the files are attached in the zip, for both the working version and the non-working version, if someone with more knowledge about the MIR wants to have a look.

@Patrick-Poitras
Copy link
Contributor

mir_compare.zip

@Patrick-Poitras
Copy link
Contributor

@rustbot label +I-prioritize

@rustbot rustbot added the I-prioritize Issue: Indicates that prioritization has been requested for this issue. label Dec 20, 2021
@csmoe
Copy link
Member

csmoe commented Dec 21, 2021

Duplicate of #76627
cc #86921

@csmoe csmoe closed this as completed Dec 21, 2021
@rustbot rustbot removed the I-prioritize Issue: Indicates that prioritization has been requested for this issue. label Dec 21, 2021
@csmoe csmoe marked this as a duplicate and then as not a duplicate of #86921 Dec 21, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug.
Projects
None yet
Development

No branches or pull requests

5 participants