Skip to content

Inconsistent borrow check in async function #74068

Open

Description

@Nugine

I tried this code:

use std::collections::HashMap;

fn f1(map: &mut HashMap<i32, String>, k: i32) -> &String {
    if let Some(s) = map.get(&k) {
        return s;
    }
    map.insert(k, k.to_string());
    map.get(&k).unwrap()
}

async fn f2(map: &mut HashMap<i32, String>, k: i32) -> &String {
    if let Some(s) = map.get(&k) {
        return s;
    }
    map.insert(k, k.to_string());
    map.get(&k).unwrap()
}

f2 seems to be correct but can not be compiled.

   Compiling playground v0.0.1 (/playground)
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable
  --> src/lib.rs:15:5
   |
12 |     if let Some(s) = map.get(&k) {
   |                      --- immutable borrow occurs here
13 |         return s;
   |                - returning this value requires that `*map` is borrowed for `'1`
14 |     }
15 |     map.insert(k, k.to_string());
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
16 |     map.get(&k).unwrap()
17 | }
   | - return type of generator is &'1 std::string::String

error: aborting due to previous error

For more information about this error, try `rustc --explain E0502`.
error: could not compile `playground`.

To learn more, run the command again with --verbose.

Meta

According to the playground, the problem exists on 1.44.1 and 1.46.0-nightly (2020-07-04 0cd7ff7ddfb75a38dca8).

Other versions may be the same.

Activity

  1. added
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    on Jul 5, 2020
  2. SNCPlay42 commented on Jul 5, 2020

    @SNCPlay42
    Contributor

    If f2 is commented out then f1 errors the same way (playground):

       Compiling playground v0.0.1 (/playground)
    error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable
     --> src/lib.rs:7:5
      |
    3 | fn f1(map: &mut HashMap<i32, String>, k: i32) -> &String {
      |            - let's call the lifetime of this reference `'1`
    4 |     if let Some(s) = map.get(&k) {
      |                      --- immutable borrow occurs here
    5 |         return s;
      |                - returning this value requires that `*map` is borrowed for `'1`
    6 |     }
    7 |     map.insert(k, k.to_string());
      |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
    
    error: aborting due to previous error
    
    For more information about this error, try `rustc --explain E0502`.
    error: could not compile `playground`.
    
    To learn more, run the command again with --verbose.
    

    In rustc 1.42.0 both errors are reported simultaneously (godbolt).

    I'm not sure whether this is a bug or not - rustc need not report everything wrong with a crate in one go, but if it can report these simultaneously then it probably should.

  3. SNCPlay42 commented on Jul 5, 2020

    @SNCPlay42
    Contributor

    cargo bisect-rustc results for when the compiler stopped reporting both errors simultaneously:

    searched nightlies: from nightly-2020-02-06 to nightly-2020-07-05
    regressed nightly: nightly-2020-02-16
    searched commits: from 433aae9 to 61d9231
    regressed commit: 19288dd

  4. Nugine commented on Jul 6, 2020

    @Nugine
    ContributorAuthor
  5. tmandry commented on Jul 7, 2020

    @tmandry
    Member

    Thanks for digging into this, @SNCPlay42! This doesn't appear to be async-related, so removing that tag.

  6. added
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    and removed on Jul 7, 2020
  7. nikomatsakis commented on Jul 7, 2020

    @nikomatsakis
    Contributor

    If @SNCPlay42's bisection is correct (thanks!), then this was triggered by #67681 (cc @matthewjasper). It is indeed true that the code example is not expected to compile, though you could make it compile by either using entry API or adding something like

    if map.contains(..) {
       if let Some(...) = ... {
          ...
       }
    }

    I'm not sure exactly what caused this to occur, but it makes sense that #67681 might be related since that affected how we manage opaque type inference (and the async fn implicitly uses an opaque return type).

  8. matthewjasper commented on Jul 7, 2020

    @matthewjasper
    Contributor

    The error is being suppressed because rustc_typeck::check_crate is (effectively) aborting compilation early.

  9. added and removed
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    on Jul 8, 2020
  10. spastorino commented on Jul 8, 2020

    @spastorino
    Member

    Prioritized as P-medium as per Zulip discussion.

  11. compiler-errors commented on Dec 31, 2024

    @compiler-errors
    Contributor

    Both errors are reported now. This is a polonius problem, I believe.

  12. added
    NLL-poloniusIssues related for using Polonius in the borrow checker
    fixed-by-poloniusCompiling with `-Zpolonius` fixes this issue.
    on Dec 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-borrow-checkerArea: The borrow checkerC-bugCategory: This is a bug.NLL-poloniusIssues related for using Polonius in the borrow checkerP-mediumMedium priorityT-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