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

consider fixing common regression with expansion of 2-phase borrows #51915

Open
nikomatsakis opened this issue Jun 29, 2018 · 12 comments
Open

consider fixing common regression with expansion of 2-phase borrows #51915

nikomatsakis opened this issue Jun 29, 2018 · 12 comments
Labels
A-NLL Area: Non-lexical lifetimes (NLL) C-enhancement Category: An issue proposing an enhancement or a PR with one. NLL-complete Working towards the "valid code works" goal P-medium Medium priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@nikomatsakis
Copy link
Contributor

The following code works without NLL but not with NLL enabled:

https://play.rust-lang.org/?gist=9b797f941b3aa419991e15fd5a2d07a0&version=nightly&mode=debug

//#![feature(nll)]

struct S {
    a: &'static str,
}

fn f(_: &mut S, _: &str) {
}

fn main() {
    let mut s = S {
        a: "a",
    };

    f(&mut s, s.a);
}

NLL is not wrong to reject this code. This is #38899. However, it could plausibly be accepted if we expanded two-phase borrows ever so slightly... actually, I'm not sure about this exact source, or it least it wouldn't quite fit into what I had in mind.

I had in mind an expansion for arguments of type &mut, basically, so that foo(bar), if bar is an &mut that is being reborrowed, would reborrow using a 2-phase borrow.

@nikomatsakis nikomatsakis added T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. WG-compiler-nll labels Jun 29, 2018
@nikomatsakis nikomatsakis added this to the Rust 2018 Preview 2 milestone Jun 29, 2018
@nikomatsakis
Copy link
Contributor Author

Adding to the Preview 2 milestone since this affected bootstrap. We have to decide how to proceed here though.

@nikomatsakis
Copy link
Contributor Author

Notably, the original source that gave rise to this error was:

- librustc_passes: 1 error
error[E0502]: cannot borrow `*self.tables` as immutable because it is also borrowed as mutable
   --> librustc_passes/rvalue_promotion.rs:206:76
    |
206 |         euv::ExprUseVisitor::new(self, tcx, param_env, &region_scope_tree, self.tables, None)
    |         -------------------------------------------------------------------^^^^^^^^^^^-------
    |         |                        |                                         |
    |         |                        |                                         immutable borrow occurs here
    |         |                        mutable borrow occurs here
| borrow later used here

and in this case you can see that the reborrow of self is implicit.

@nikomatsakis
Copy link
Contributor Author

(The fact that the borrow is implicit is important because we need for 2-phase borrows to have a clear activation point.)

@nikomatsakis nikomatsakis added the A-NLL Area: Non-lexical lifetimes (NLL) label Jul 17, 2018
@nikomatsakis
Copy link
Contributor Author

We decided to make a final decision about this based on the feedback from EP2. Moving to RC.

@matthewjasper
Copy link
Contributor

Slightly surprisingly, the following version of the code does compile:

struct S {
    a: &'static str,
}

fn f(_: &mut S, _: &str) {
}

fn main() {
    let mut s = S {
        a: "a",
    };

    let r = &mut s;

    f(r, r.a);
}

@pnkfelix pnkfelix added the NLL-complete Working towards the "valid code works" goal label Jul 24, 2018
@nikomatsakis
Copy link
Contributor Author

Example from the extended-collections crate (v0.2.0) that seems relevant:

playground

#![feature(nll)]

use std::mem;

struct Node<T, U> {
    links_len: usize,
    entry: Entry<T, U>,
    links: [*mut Node<T, U>; 0],
}

impl<T, U> Node<T, U> {
    fn get_pointer(&self, index: usize) -> &*mut Node<T, U> {
        loop { }
    }
}

pub struct Entry<T, U> {
    pub key: T,
    pub value: U,
}

pub struct SkipMapIter<'a, T, U>
where
    T: 'a,
    U: 'a,
{
    current: &'a *mut Node<T, U>,
}

impl<'a, T, U> Iterator for SkipMapIter<'a, T, U>
where
    T: 'a + Ord,
    U: 'a,
{
    type Item = (&'a T, &'a U);

    fn next(&mut self) -> Option<Self::Item> {
        if self.current.is_null() {
            None
        } else {
            unsafe {
                let Entry { ref key, ref value } = (**self.current).entry;
                mem::replace(&mut self.current, &*(**self.current).get_pointer(0));
                Some((key, value))
            }
        }
    }
}

fn main() { }

yields

error[E0502]: cannot borrow `**self.current` as immutable because it is also borrowed as mutable
  --> src/main.rs:43:51
   |
43 |                 mem::replace(&mut self.current, &*(**self.current).get_pointer(0));
   |                 ----------------------------------^^^^^^^^^^^^^^^^----------------
   |                 |            |                    |
   |                 |            |                    immutable borrow occurs here
   |                 |            mutable borrow occurs here
   |                 borrow later used here

@nikomatsakis
Copy link
Contributor Author

That said, this code would probably not be fixed by the proposed change, because the &mut self.current is explicit.

@nikomatsakis
Copy link
Contributor Author

I think it would be helpful to have some kind of list for how often this sort of regression occurs in real life. I am currently leaning towards making no change here and instead improving the diagnostics (as described in #47349)

@nikomatsakis nikomatsakis added I-nominated I-needs-decision Issue: In need of a decision. labels Aug 27, 2018
@nikomatsakis
Copy link
Contributor Author

I'm nominating this for discussion — I'm not sure what action to take here. Leaning towards nothing, as most of the 2PB regressions we've seen in the wild would not be readily addressed by this.

@nikomatsakis
Copy link
Contributor Author

nikomatsakis commented Aug 31, 2018

In the discussion, we decided not to expand 2PB for the time being, but to consider that as future work. Link to our discussion

@nikomatsakis nikomatsakis removed I-needs-decision Issue: In need of a decision. I-nominated labels Aug 31, 2018
@nikomatsakis nikomatsakis removed this from the Rust 2018 RC milestone Aug 31, 2018
@nikomatsakis
Copy link
Contributor Author

Accordingly I am removing this from the milestone

@pnkfelix
Copy link
Member

Re-triaging for #56754. P-medium. CC #49434

@pnkfelix pnkfelix added the P-medium Medium priority label Dec 19, 2018
@Enselic Enselic added the C-enhancement Category: An issue proposing an enhancement or a PR with one. label Nov 19, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-NLL Area: Non-lexical lifetimes (NLL) C-enhancement Category: An issue proposing an enhancement or a PR with one. NLL-complete Working towards the "valid code works" goal P-medium Medium priority 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