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

RFC Legal double reference #2268

Closed
wants to merge 12 commits into
base: master
from

Conversation

Projects
None yet
8 participants
@Dynisious

Dynisious commented Dec 27, 2017

A proposal for allowing the creation of immutable references to a value WHILE there is an existing mutable reference when the immutable reference will be dropped before the next use of the mutable reference.
This change will mean that you can write Safe Rust without having to worry about the borrow checker as much and make writing functional Rust code much easier.

Rendered

Dynisious added some commits Oct 18, 2017

Imply Option
My RFC for adding some function implimentation for the Option type akin to the `implies` operation from predicate logic.
Formatting
Formatted poorly displayed code and pseudo-code to display correctly and be legible.
Spelling Corrections
Fixed some spelling errors spotted by @Centril
Improved Explanations
Expanded some areas of the RFC to better explain some areas which were confusing contributors.
Syntax Error
My definitions in the summary didn't have semicolons. Not exactly an earth shattering issue but my as well fix it up.
More Syntax
More syntax fixing, I'm tired and it's 33 Celsius here.
RFC Legal Double Reference
Added the Legal Double Reference RFC.

@Centril Centril added the T-lang label Dec 27, 2017

@TimNN

This comment has been minimized.

Show comment
Hide comment
@TimNN

TimNN Dec 27, 2017

Contributor

I don't think that can (easily, if at all) be done without making currently safe code unsound.

Consider for example the following code (which, AFAIK, is safe in current rust -- edit: or not, see below):

use std::{mem, ptr};

fn tmp_move<T, F: FnOnce(T) -> T>(val: &mut T, f: F) {
    let v = unsafe { ptr::read(val) };
    let v = f(v);
    unsafe { ptr::write(val, v) };
}

fn main() {
    let mut b = Box::new(0u32);
    tmp_move(&mut b, |v| {
        mem::drop(v);
        
        // The following line would be allowed by this RFC, however 
        // would cause a use-after-free.
        // println!("{}", &b);
        
        Box::new(1u32)
    });
}

As I understand the RFC, the currently commented println! would be allowed, but would cause a use-after-free. The problem I see, is that unsafe code can currently rely on the promise "immutable XOR mutable borrow" to be safe, which would no longer be true with the proposed semantics. (Also, tools like crater won't really be able to test which crates would be affected by this change).

Contributor

TimNN commented Dec 27, 2017

I don't think that can (easily, if at all) be done without making currently safe code unsound.

Consider for example the following code (which, AFAIK, is safe in current rust -- edit: or not, see below):

use std::{mem, ptr};

fn tmp_move<T, F: FnOnce(T) -> T>(val: &mut T, f: F) {
    let v = unsafe { ptr::read(val) };
    let v = f(v);
    unsafe { ptr::write(val, v) };
}

fn main() {
    let mut b = Box::new(0u32);
    tmp_move(&mut b, |v| {
        mem::drop(v);
        
        // The following line would be allowed by this RFC, however 
        // would cause a use-after-free.
        // println!("{}", &b);
        
        Box::new(1u32)
    });
}

As I understand the RFC, the currently commented println! would be allowed, but would cause a use-after-free. The problem I see, is that unsafe code can currently rely on the promise "immutable XOR mutable borrow" to be safe, which would no longer be true with the proposed semantics. (Also, tools like crater won't really be able to test which crates would be affected by this change).

@Centril

This comment has been minimized.

Show comment
Hide comment
@Centril

Centril Dec 27, 2017

Contributor

@TimNN A nit: tmp_move as currently written is not safe. You can cause double drops. Other than that, your point is well made.

Contributor

Centril commented Dec 27, 2017

@TimNN A nit: tmp_move as currently written is not safe. You can cause double drops. Other than that, your point is well made.

@TimNN

This comment has been minimized.

Show comment
Hide comment
@TimNN

TimNN Dec 27, 2017

Contributor

@Centril: Good catch! (I checked that tmp_move was not UnwindSafe but didn't consider the double-drop issue). Anyway, I guess you can fix the double-drop by causing a double-panic.

There is also probably a nicer way to illustrate the problem than my original code example, but that was the first thing I could come up with 😄

Contributor

TimNN commented Dec 27, 2017

@Centril: Good catch! (I checked that tmp_move was not UnwindSafe but didn't consider the double-drop issue). Anyway, I guess you can fix the double-drop by causing a double-panic.

There is also probably a nicer way to illustrate the problem than my original code example, but that was the first thing I could come up with 😄

@eddyb

This comment has been minimized.

Show comment
Hide comment
@eddyb

eddyb Dec 27, 2017

Member

cc @nikomatsakis to whom I argued for something similar a while back, but this proposal might be missing a way to avoid data races. I believe we used to allow reading from a borrowed variable a while ago, but that didn't play well when another thread actually held the reference.

Member

eddyb commented Dec 27, 2017

cc @nikomatsakis to whom I argued for something similar a while back, but this proposal might be missing a way to avoid data races. I believe we used to allow reading from a borrowed variable a while ago, but that didn't play well when another thread actually held the reference.

let c = a.len(); //E0502
//Do more stuff with b.
});
```

This comment has been minimized.

@ExpHP

ExpHP Dec 28, 2017

This snippet has far greater issues than a being immutably borrowed inside the closure:

  1. Closures begin borrowing from their environment as soon as they are created, not just when they are called. In other words, calling a.len() inside the closure does not cause it to be immutably borrowed each iteration, but rather, it is borrowed once, and this borrow lasts for the entire duration of the for_each loop.
  2. Similar is true for the mutable borrow. iter_mut() returns a struct that holds a &mut [T]. It holds this reference for the entire duration of the loop.
  3. A function like iter_mut is currently allowed to do things that put a in an invalid state (i.e. where len() could invoke undefined behavior), because it knows that it has unique access to that object until the IterMut struct is dropped.
@ExpHP

ExpHP Dec 28, 2017

This snippet has far greater issues than a being immutably borrowed inside the closure:

  1. Closures begin borrowing from their environment as soon as they are created, not just when they are called. In other words, calling a.len() inside the closure does not cause it to be immutably borrowed each iteration, but rather, it is borrowed once, and this borrow lasts for the entire duration of the for_each loop.
  2. Similar is true for the mutable borrow. iter_mut() returns a struct that holds a &mut [T]. It holds this reference for the entire duration of the loop.
  3. A function like iter_mut is currently allowed to do things that put a in an invalid state (i.e. where len() could invoke undefined behavior), because it knows that it has unique access to that object until the IterMut struct is dropped.
@nikomatsakis

This comment has been minimized.

Show comment
Hide comment
@nikomatsakis

nikomatsakis Jan 25, 2018

Contributor

@rfcbot fcp close

If I understood this RFC correctly, it is very similar to @arielb1's prior proposal of Ref2Φ. I believe that the discussion of Ref2 from the nested method calls RFC is basically still apt. In short, it may be a direction that we want to go eventually, but let's move cautiously, because it could make borrows much harder to understand without a big increase in expressiveness.

In any case, I think that now is not the time to be making these sorts of deeper changes to borrowing. We're still working on integrating NLL and two-phase-borrows, which is going to be a big change in how Rust feels. I am reluctant to tinker more with borrows until we have a string feeling for how that will play out.

Therefore, I move to close this RFC.

Thanks @Dynisious for the suggestion, however! I think this is something we may well want to revisit in the future.

Contributor

nikomatsakis commented Jan 25, 2018

@rfcbot fcp close

If I understood this RFC correctly, it is very similar to @arielb1's prior proposal of Ref2Φ. I believe that the discussion of Ref2 from the nested method calls RFC is basically still apt. In short, it may be a direction that we want to go eventually, but let's move cautiously, because it could make borrows much harder to understand without a big increase in expressiveness.

In any case, I think that now is not the time to be making these sorts of deeper changes to borrowing. We're still working on integrating NLL and two-phase-borrows, which is going to be a big change in how Rust feels. I am reluctant to tinker more with borrows until we have a string feeling for how that will play out.

Therefore, I move to close this RFC.

Thanks @Dynisious for the suggestion, however! I think this is something we may well want to revisit in the future.

@rfcbot

This comment has been minimized.

Show comment
Hide comment
@rfcbot

rfcbot Jan 25, 2018

Team member @nikomatsakis has proposed to close this. The next step is review by the rest of the tagged teams:

No concerns currently listed.

Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

rfcbot commented Jan 25, 2018

Team member @nikomatsakis has proposed to close this. The next step is review by the rest of the tagged teams:

No concerns currently listed.

Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@rfcbot

This comment has been minimized.

Show comment
Hide comment
@rfcbot

rfcbot Jan 31, 2018

🔔 This is now entering its final comment period, as per the review above. 🔔

rfcbot commented Jan 31, 2018

🔔 This is now entering its final comment period, as per the review above. 🔔

@rfcbot

This comment has been minimized.

Show comment
Hide comment
@rfcbot

rfcbot Jan 31, 2018

🔔 This is now entering its final comment period, as per the review above. 🔔

rfcbot commented Jan 31, 2018

🔔 This is now entering its final comment period, as per the review above. 🔔

@rfcbot

This comment has been minimized.

Show comment
Hide comment
@rfcbot

rfcbot Feb 10, 2018

The final comment period is now complete.

rfcbot commented Feb 10, 2018

The final comment period is now complete.

@aturon

This comment has been minimized.

Show comment
Hide comment
@aturon

aturon Feb 14, 2018

Member

Closing, though open to revisiting later, as per @nikomatsakis's summary. Thanks @Dynisious!

Member

aturon commented Feb 14, 2018

Closing, though open to revisiting later, as per @nikomatsakis's summary. Thanks @Dynisious!

@aturon aturon closed this Feb 14, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment