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

Can't return reference to passed-in referent that is assigned in a loop #97901

Open
jonhoo opened this issue Jun 8, 2022 · 2 comments
Open
Labels
A-borrow-checker Area: The borrow checker A-control-flow Area: Control flow A-NLL Area: Non-lexical lifetimes (NLL) C-bug Category: This is a bug. fixed-by-polonius Compiling with `-Zpolonius` fixes this issue. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@jonhoo
Copy link
Contributor

jonhoo commented Jun 8, 2022

I tried this code:

struct C;

fn inout<'cfg>(c_out: &'cfg mut C) -> &'cfg C {
    loop {
        *c_out = C;
        let r = &*c_out;
        if true {
            break r;
        }
    }
}

fn main() {
    let mut c = C;
    inout(&mut c);
}

I expected to see this happen: it compiles and runs

Instead, this happened: it fails to compile with the error:

error[E0506]: cannot assign to `*c_out` because it is borrowed
 --> src/main.rs:5:9
  |
3 | fn inout<'cfg>(c_out: &'cfg mut C) -> &'cfg C {
  |          ---- lifetime `'cfg` defined here
4 |     loop {
5 |         *c_out = C;
  |         ^^^^^^^^^^ assignment to borrowed `*c_out` occurs here
6 |         let r = &*c_out;
  |                 ------- borrow of `*c_out` occurs here
7 |         if true {
8 |             break r;
  |                   - returning this value requires that `*c_out` is borrowed for `'cfg`

It's worth noting that the code builds fine if the break is unconditional.

Even though the reproduction is somewhat different, this feels related to #92984

Meta

Happens on 1.61.0 stable, beta, and nightly.

Nightly Rust version from playground:

1.63.0-nightly
2022-06-07 5435ed6916a59e8d5acb

Hard mode

If you're looking for test cases, may also be worth including this one, which "launders" the lifetime through a for<'a>:

struct C;

fn inout<'cfg, F>(c_out: &'cfg mut C, f: F) -> &'cfg C
where
    for<'c> F: Fn(&'c C) -> &'c C,
{
    loop {
        *c_out = C;
        let c = &*c_out;
        let r = f(c);
        if true {
            break r;
        }
    }
}

fn main() {
    let mut c = C;
    inout(&mut c, |c| c);
}
@jonhoo jonhoo added the C-bug Category: This is a bug. label Jun 8, 2022
@ChayimFriedman2
Copy link
Contributor

Looks like the known problem of MIR borrowck with control flow. It compiles with polonius.

@jonhoo
Copy link
Contributor Author

jonhoo commented Jun 9, 2022

Ah, hello #21906/#51545/#54663/#58910 my old friend.

@fmease fmease added A-borrow-checker Area: The borrow checker T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. A-control-flow Area: Control flow fixed-by-polonius Compiling with `-Zpolonius` fixes this issue. A-NLL Area: Non-lexical lifetimes (NLL) and removed needs-triage-legacy labels Jan 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-borrow-checker Area: The borrow checker A-control-flow Area: Control flow A-NLL Area: Non-lexical lifetimes (NLL) C-bug Category: This is a bug. fixed-by-polonius Compiling with `-Zpolonius` fixes this issue. 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

4 participants