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

Seemingly incorrect borrow analysis of disjoint code paths #95582

Closed
redgoldlace opened this issue Apr 2, 2022 · 1 comment
Closed

Seemingly incorrect borrow analysis of disjoint code paths #95582

redgoldlace opened this issue Apr 2, 2022 · 1 comment
Labels
C-bug Category: This is a bug.

Comments

@redgoldlace
Copy link

More specifically, it appears that disjoint code paths are being "merged" somewhere down the track, which results in a confusing error that doesn't really make sense.

I was able to find a fairly simple min repro:

fn main() {
    let mut foobles = Example::default();
    let cool_text = foobles.get_or_insert_foo("Hello");

    println!("{}, world!", cool_text);
}

#[derive(Default)]
struct Example {
    foo: Option<String>,
}

impl Example {
    fn get_or_insert_foo(&mut self, value: &'static str) -> &mut String {
        if let Some(foo) = self.foo.as_mut() {
            return foo;
        }

        self.foo = Some(value.to_owned());
        self.foo.as_mut().unwrap()
    }
}

I expected this code to compile. and print Hello, world! to standard output.

Instead, a confusing error is presented:

error[E0506]: cannot assign to `self.foo` because it is borrowed
  --> src\main.rs:19:9
   |
14 |     fn get_or_insert_foo(&mut self, value: &str) -> &mut String {
   |                          - let's call the lifetime of this reference `'1`
15 |         if let Some(foo) = self.foo.as_mut() {
   |                            ----------------- borrow of `self.foo` occurs here
16 |             return foo;
   |                    --- returning this value requires that `self.foo` is borrowed for `'1`
...
19 |         self.foo = Some(value.to_owned());
   |         ^^^^^^^^ assignment to borrowed `self.foo` occurs here

error[E0499]: cannot borrow `self.foo` as mutable more than once at a time
  --> src\main.rs:20:9
   |
14 |     fn get_or_insert_foo(&mut self, value: &str) -> &mut String {
   |                          - let's call the lifetime of this reference `'1`
15 |         if let Some(foo) = self.foo.as_mut() {
   |                            ----------------- first mutable borrow occurs here
16 |             return foo;
   |                    --- returning this value requires that `self.foo` is borrowed for `'1`
...
20 |         self.foo.as_mut().unwrap()
   |         ^^^^^^^^^^^^^^^^^ second mutable borrow occurs here

Some errors have detailed explanations: E0499, E0506.
For more information about an error, try `rustc --explain E0499`.
error: could not compile `rustc-disjoint-borrow` due to 2 previous errors

I've tried a few variations of something similar to this now, and have been able to trigger this error using both match and if let. I assume that's because the latter is desugared to the former, but it still struck me as odd.

It's also important for me to note that the fluff from main doesn't affect the error.

Meta

rustc --version --verbose:

rustc 1.61.0-nightly (0677edc86 2022-03-31)
binary: rustc
commit-hash: 0677edc86e342f333d4828b0ee1ef395a4e70fe5
commit-date: 2022-03-31
host: x86_64-pc-windows-gnu
release: 1.61.0-nightly
LLVM version: 14.0.0

I've also tried this on the latest stable release, to no avail. This issue seems to be present as far back as Rust 1.40 as well, but I haven't checked any further than that. I did a search through open/previous issues but wasn't able to find much - apologies if I'm rehashing something that's been brought up before!

Unfortunately I can't provide a backtrace (since the code doesn't compile) and the logging output from rustc is far too gargantuan to put here. Sorry :/

@redgoldlace redgoldlace added the C-bug Category: This is a bug. label Apr 2, 2022
@ehuss
Copy link
Contributor

ehuss commented Apr 2, 2022

I believe this is a known issue that is fixed with polonius (on nightly, you can run with -Zpolonius). I think #54663 has more information about this situation.

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

2 participants