-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Add more methods in Cell for non-Copy types #39715
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
Closed
Closed
Changes from 1 commit
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -186,6 +186,7 @@ use fmt::{self, Debug, Display}; | |
use marker::Unsize; | ||
use mem; | ||
use ops::{Deref, DerefMut, CoerceUnsized}; | ||
use ptr; | ||
|
||
/// A mutable memory location. | ||
/// | ||
|
@@ -280,6 +281,19 @@ unsafe impl<T> Send for Cell<T> where T: Send {} | |
#[stable(feature = "rust1", since = "1.0.0")] | ||
impl<T> !Sync for Cell<T> {} | ||
|
||
#[unstable(feature = "move_cell", issue = "39264")] | ||
impl<T:Clone> Clone for Cell<T> { | ||
#[inline] | ||
default fn clone(&self) -> Cell<T> { | ||
let temp = unsafe { mem::uninitialized() }; | ||
let inner = self.replace(temp); | ||
// If `clone` panics, let it crash. | ||
let another = inner.clone(); | ||
self.set(inner); | ||
Cell::new(another) | ||
} | ||
} | ||
|
||
#[stable(feature = "rust1", since = "1.0.0")] | ||
impl<T:Copy> Clone for Cell<T> { | ||
#[inline] | ||
|
@@ -289,14 +303,26 @@ impl<T:Copy> Clone for Cell<T> { | |
} | ||
|
||
#[stable(feature = "rust1", since = "1.0.0")] | ||
impl<T:Default + Copy> Default for Cell<T> { | ||
impl<T:Default> Default for Cell<T> { | ||
/// Creates a `Cell<T>`, with the `Default` value for T. | ||
#[inline] | ||
fn default() -> Cell<T> { | ||
Cell::new(Default::default()) | ||
} | ||
} | ||
|
||
#[unstable(feature = "move_cell", issue = "39264")] | ||
impl<T:PartialEq> PartialEq for Cell<T> { | ||
#[inline] | ||
default fn eq(&self, other: &Cell<T>) -> bool { | ||
if ptr::eq(self, other) { | ||
true | ||
} else { | ||
unsafe { Cell::take_and_restore(self, other, |l, r| l == r) } | ||
} | ||
} | ||
} | ||
|
||
#[stable(feature = "rust1", since = "1.0.0")] | ||
impl<T:PartialEq + Copy> PartialEq for Cell<T> { | ||
#[inline] | ||
|
@@ -305,9 +331,60 @@ impl<T:PartialEq + Copy> PartialEq for Cell<T> { | |
} | ||
} | ||
|
||
#[unstable(feature = "move_cell", issue = "39264")] | ||
impl<T:Eq> Eq for Cell<T> {} | ||
|
||
#[stable(feature = "cell_eq", since = "1.2.0")] | ||
impl<T:Eq + Copy> Eq for Cell<T> {} | ||
|
||
#[unstable(feature = "move_cell", issue = "39264")] | ||
impl<T:PartialOrd> PartialOrd for Cell<T> { | ||
#[inline] | ||
default fn partial_cmp(&self, other: &Cell<T>) -> Option<Ordering> { | ||
if ptr::eq(self, other) { | ||
Some(Ordering::Equal) | ||
} else { | ||
unsafe { Cell::take_and_restore(self, other, |l, r| l.partial_cmp(r)) } | ||
} | ||
} | ||
|
||
#[inline] | ||
default fn lt(&self, other: &Cell<T>) -> bool { | ||
if ptr::eq(self, other) { | ||
true | ||
} else { | ||
unsafe { Cell::take_and_restore(self, other, |l, r| l < r) } | ||
} | ||
} | ||
|
||
#[inline] | ||
default fn le(&self, other: &Cell<T>) -> bool { | ||
if ptr::eq(self, other) { | ||
true | ||
} else { | ||
unsafe { Cell::take_and_restore(self, other, |l, r| l <= r) } | ||
} | ||
} | ||
|
||
#[inline] | ||
default fn gt(&self, other: &Cell<T>) -> bool { | ||
if ptr::eq(self, other) { | ||
true | ||
} else { | ||
unsafe { Cell::take_and_restore(self, other, |l, r| l > r) } | ||
} | ||
} | ||
|
||
#[inline] | ||
default fn ge(&self, other: &Cell<T>) -> bool { | ||
if ptr::eq(self, other) { | ||
true | ||
} else { | ||
unsafe { Cell::take_and_restore(self, other, |l, r| l >= r) } | ||
} | ||
} | ||
} | ||
|
||
#[stable(feature = "cell_ord", since = "1.10.0")] | ||
impl<T:PartialOrd + Copy> PartialOrd for Cell<T> { | ||
#[inline] | ||
|
@@ -336,6 +413,18 @@ impl<T:PartialOrd + Copy> PartialOrd for Cell<T> { | |
} | ||
} | ||
|
||
#[unstable(feature = "move_cell", issue = "39264")] | ||
impl<T:Ord> Ord for Cell<T> { | ||
#[inline] | ||
default fn cmp(&self, other: &Cell<T>) -> Ordering { | ||
if ptr::eq(self, other) { | ||
Ordering::Equal | ||
} else { | ||
unsafe { Cell::take_and_restore(self, other, |l, r| l.cmp(r)) } | ||
} | ||
} | ||
} | ||
|
||
#[stable(feature = "cell_ord", since = "1.10.0")] | ||
impl<T:Ord + Copy> Ord for Cell<T> { | ||
#[inline] | ||
|
@@ -345,7 +434,7 @@ impl<T:Ord + Copy> Ord for Cell<T> { | |
} | ||
|
||
#[stable(feature = "cell_from", since = "1.12.0")] | ||
impl<T: Copy> From<T> for Cell<T> { | ||
impl<T> From<T> for Cell<T> { | ||
fn from(t: T) -> Cell<T> { | ||
Cell::new(t) | ||
} | ||
|
@@ -422,6 +511,24 @@ impl<T> Cell<T> { | |
pub fn into_inner(self) -> T { | ||
unsafe { self.value.into_inner() } | ||
} | ||
|
||
/// Private function used for implementing other methods when `T` is not `Copy`. | ||
/// | ||
/// This is `unsafe` because `left` and `right` may point to the same object. | ||
#[unstable(feature = "move_cell", issue = "39264")] | ||
unsafe fn take_and_restore<Result, F>(left: &Self, right: &Self, f: F) -> Result | ||
where F: FnOnce(&T, &T) -> Result | ||
{ | ||
let temp_left = mem::uninitialized(); | ||
let temp_right = mem::uninitialized(); | ||
let lhs = left.replace(temp_left); | ||
let rhs = right.replace(temp_right); | ||
// If panic happens in `f`, just let it crash. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the record: same UB issue here as in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you. Updated. |
||
let result = f(&lhs, &rhs); | ||
left.set(lhs); | ||
right.set(rhs); | ||
return result; | ||
} | ||
} | ||
|
||
impl<T: Default> Cell<T> { | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this super unsafe? If a panic occurs here,
*self
will be dropped (not in this scope, but later during stack unwinding) which will drop uninitialized memory which will cause UB.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AssertUnwindSafe
is notunsafe
, and for good reason. Safe code usingAssertUnwindSafe
unwisely may get very undesirable behavior, but not UB.AssertUnwindSafe
is irrelevant, as this problem occurs without anycatch_unwind
calls. In fact, a well-placedcatch_unwind
can stop a panic in thisclone
call from dropping*self
(by stopping the unwinding before it gets to the stack frame where theCell
would be dropped).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, you are right. I need to figure out a solution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@F001: You'll probably want to use something similar to the
Hole
structure described in the nomicon, which writes a valid value back into the original cell in case of a panic.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or rather, is there a reason, you can't just use
self.value.get().clone()
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@TimNN @F001 I haven't thought through the details but https://users.rust-lang.org/t/why-does-cell-require-copy-instead-of-clone/5769/3?u=rkruppe seems relevant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rkruppe: Thanks for the link! After having a quick read through it and the comments on the original RFC, I think that safely implementing the methods proposed by this PR is fundamentally incompatible with "MoveCells", since we are not allowed to call any method on the contained Type, as I understand it. (This is not a problem for "CopyCells" since we can create a new copy of the value without calling any methods on it).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your comments!
@TimNN I don't agree that these methods are fundamentally incompatible with "MoveCells".
Cell
is not intended to prohibit methods of the contained type. The most important guarantee is "disallowing any reference (mutable or immutable) to the data contained in the cell."There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with that statement. However I think exactly this is happening here: As soon as you call
clone
orpartial_eq
, you provide those methods with a reference to the data in the cell, which then allows for bad things to happen, as can be seen by the example in the post linked by @rkruppe.