Skip to content

regression: cannot borrow ... as immutable because it is also borrowed as mutable #135671

Description

@BoxyUwU
[INFO] [stdout] error[E0502]: cannot borrow `*inputs` as immutable because it is also borrowed as mutable
[INFO] [stdout]    --> examples/basic.rs:267:27
[INFO] [stdout]     |
[INFO] [stdout] 263 |           if let Some(grd) = inputs[0].grad.as_mut() {
[INFO] [stdout]     |                              -------------- mutable borrow occurs here
[INFO] [stdout] 264 | /             *grd += output_grad
[INFO] [stdout] 265 | |                 * if inputs[0].val > 0.0 {
[INFO] [stdout] 266 | |                     1.0
[INFO] [stdout] 267 | |                 } else if inputs[0].val == 0.0 {
[INFO] [stdout]     | |                           ^^^^^^^^^ immutable borrow occurs here
[INFO] [stdout] ...   |
[INFO] [stdout] 270 | |                     -1.0
[INFO] [stdout] 271 | |                 };
[INFO] [stdout]     | |_________________- mutable borrow later used here

Activity

  1. added
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    T-typesRelevant to the types team, which will review and decide on the PR/issue.
    on Jan 18, 2025
  2. added
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Jan 18, 2025
  3. compiler-errors commented on Jan 18, 2025

    @compiler-errors
    Contributor

    This regressed in #133734 cc @scottmcm

    instead -- with some asterisks for arrays and &mut that need it to be done slightly differently.

    I assume that maybe there was something missing here 🤔

  4. compiler-errors commented on Jan 18, 2025

    @compiler-errors
    Contributor

    Minimal:

    struct Test {
        a: i32,
        b: i32,
    }
    
    fn main() {
        let inputs: &mut [_] = &mut [Test { a: 0, b: 0 }];
        let a = &mut inputs[0].a;
        let b = &mut inputs[0].b;
    
        *a = 0;
        *b = 1;
    }
  5. compiler-errors commented on Jan 18, 2025

    @compiler-errors
    Contributor

    I think we should probably revert #133734 (and the follow-up PR that fully removed the Len operand), and spend some more time on a solution that is both resilient to borrowck and also to miri.

    Specifically, the problem here is that the borrow-checker treated the Len operand specially (as a kind of fake borrow) which allowed it to be interleaved with mutable live mutable references pointing into the slice. The same does not apply to the RawPtr operand (&raw const) that we emit as part of the new lowering (all of this happens because we must read the slice length to emit a bounds check before slice accesses). Instead, in MIR borrowck, we currently treat RawPtr operands like borrows, so the metadata access for let b = ... is treated like an access conflict with the mutable reference of let a = ....

    After this revert, we could either think about:

    • Changing the semantics of &raw (const|mut) operand in borrowck to not act like an access
    • Emitting a new kind of special copy operand (much like CopyForDeref) that allows us to treat the access of an array for length access as disjoint.
    • Some other solution...

    However, in the mean time, I'd rather we not crunch trying to find and more importantly validate the soundness of a solution 🤔

  6. steffahn commented on Jan 18, 2025

    @steffahn
    Member

    Oh wow, that minimal repro is … just normal code very deliberately supported by borrow checking for a long time!? There was no UI test for that? I’ve even shared code examples like this in the forums before … multiple times o.O

    (One example is here, e.g. the very first code block is broken on beta. And here’s another one, the example bar2 in the playground behind the last paragraph’s link.)

    I suppose, this means I should write down some of this stuff as UI tests, right?


    By the way, tuples could be used, too… e.g. for a minimal repro without defining a struct:

    fn main() {
        let slice = &mut [(0, 0)][..];
        std::mem::swap(&mut slice[0].0, &mut slice[0].1);
    }

    And here’s an example similar to the latter one of my forum-examples linked above

    fn foo(a: &mut [(i32, i32)], i: usize, j: usize) -> (&mut i32, &mut i32) {
        (&mut a[i].0, &mut a[j].1)
    }
  7. compiler-errors commented on Jan 18, 2025

    @compiler-errors
    Contributor

    I suppose, this means I should write down some of this stuff as UI tests, right?

    @steffahn: If you want to contribute some tests that exercise disjoint borrows, feel free to. I'll review them.

  8. added a commit that references this issue on Jan 19, 2025
  9. lqd commented on Jan 19, 2025

    @lqd
    Member

    Fixed on nightly. Reopening to track beta backport.

  10. reopened this on Jan 19, 2025
  11. 6 remaining items

  12. lqd commented on Jan 29, 2025

    @lqd
    Member

    Reopening as #135709 wasn't merged.

  13. reopened this on Jan 29, 2025
  14. assigned
    and
    lqd
    and unassigned on Jan 30, 2025
  15. lqd commented on Feb 3, 2025

    @lqd
    Member

    This was already fixed on nightly. #136352 unblocked fixing it on beta. Reopening to track beta backport of #135709.

  16. reopened this on Feb 3, 2025
  17. linked a pull request that will close this issue[beta] backports #136650on Feb 6, 2025
  18. cuviper commented on Feb 7, 2025

    @cuviper
    Member

    #136650 backported the fix to beta.

  19. added a commit that references this issue on Mar 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

P-criticalCritical priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-typesRelevant to the types team, which will review and decide on the PR/issue.regression-from-stable-to-betaPerformance or correctness regression from stable to beta.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions