Skip to content

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

Open
@jonhoo

Description

@jonhoo

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);
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-NLLArea: Non-lexical lifetimes (NLL)A-borrow-checkerArea: The borrow checkerA-control-flowArea: Control flowC-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.fixed-by-poloniusCompiling with `-Zpolonius` fixes this issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions