-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Fix fallout from #57667 #58021
Fix fallout from #57667 #58021
Conversation
src/libsyntax/ptr.rs
Outdated
drop(Box::from_raw(p)); | ||
// Free the allocated memory only, | ||
// do not drop T as it was ptr::read above. | ||
drop(Box::from_raw(p as *mut ManuallyDrop<T>)); |
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.
cc @RalfJung this is a clever trick I didn't consider!
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.
Yeah I think this should work. I don't like the raw ptr cast though, it's so easy to get those wrong -- but I also don't know another way here.
r=me if @RalfJung can confirm it's a correct approach |
Do you suggestions on alternatives to raw pointer cast? If not, I guess the code should be merged as-is. |
This might be a shot in the dark but would something like the following work (and deal with the FIXME): pub fn filter_map<F>(self, f: F) -> Option<P<T>> where
F: FnOnce(T) -> Option<T>,
{
unsafe {
// Transmute to `Box<MaybeUninit<T>>` so if `f` `panic`s or returns
// `None` the `Box` will be freed but not the inner T.
let mut maybe_box: Box<MaybeUninit<T>> = mem::transmute(self.ptr);
let x = f(ptr::read(maybe_box.as_ptr()))?;
maybe_box.set(x);
Some(P { ptr: mem::transmute(maybe_box) })
}
} |
I think @ollie27's suggestion is pretty good, any opinions/alternatives regarding the use of transmute? |
It seems to me that using |
@RalfJung the snippet above uses MaybeUninit::set. |
I guess this would work: pub fn filter_map<F>(self, f: F) -> Option<P<T>> where
F: FnOnce(T) -> Option<T>,
{
unsafe {
// Transmute to `Box<ManuallyDrop<T>>` so if `f` `panic`s or returns
// `None` the `Box` will be freed but not the inner T.
let mut manaully_drop_box: Box<ManuallyDrop<T>> = mem::transmute(self.ptr);
let x = f(ManuallyDrop::take(&mut manaully_drop_box))?;
*manaully_drop_box = ManuallyDrop::new(x);
Some(P { ptr: mem::transmute(manaully_drop_box) })
}
} |
Yeah, something like that. Either way though I think it'd be good to avoid "open" |
ping from triage @ishitatsuyuki @eddyb what's the update on this? |
r? @RalfJung |
@ishitatsuyuki could you update this to one of the proposed panic-safe schemes? If not I can r+ this, because it is an improvement, but the other version is even better. |
Actually I invented one that is drastically simpler, based on ManuallyDrop. The pains are with the |
Not sure if I like changing But it seems like some local private conversion functions between |
I spent some effort and wrote things again. Hopefully this is what you wanted. |
src/liballoc/boxed.rs
Outdated
@@ -270,6 +270,20 @@ impl<T: ?Sized> Box<T> { | |||
// additional requirements. | |||
unsafe { Pin::new_unchecked(boxed) } | |||
} | |||
|
|||
/// Converts a `Box<T>` into a `Box<ManuallyDrop<T>>` | |||
#[unstable(feature = "box_manually_drop", issue = "0")] |
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 meant local helper functions in the P
module... this also works but now you have to create a tracking issue and add it here.
src/libsyntax/ptr.rs
Outdated
|
||
// Recreate self from the raw pointer. | ||
P { ptr: Box::from_raw(p) } | ||
let x = f(ManuallyDrop::take(&mut manually_drop_box)); |
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.
Can you use into_inner
instead of take
?
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 don't think so, that would reallocate (or at least consume) the Box which defeats the point of P.
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.
It would move out of the box and then later move back into it, why would it reallocate? This code does not reallocate either.
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.
Oh wow. Box
turns out to be even more magical than I realized. In this playground example shows that not only we can move a value out of Box
without a DerefMove
trait, but the compiler tracks such moves like for local variables and plain struct fields. Attempting to use the box before its contents are reassigned (if *x = …
is removed) results in a compile time error[E0382]: use of moved value: `x`
. But the box is still there and can be used again after its contents are reassigned.
And this is panic-safe too. In the assembly for https://play.rust-lang.org/?version=nightly&mode=release&edition=2018&gist=d4907435865f18159268fc36a8546016 we see that foo
calls box_free
but not drop_t
.
So I think @RalfJung’s suggestion is the way to go.
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.
Bonus possibly-unexpected detail:
Lines 290 to 295 in f22dca0
#[stable(feature = "rust1", since = "1.0.0")] | |
unsafe impl<#[may_dangle] T: ?Sized> Drop for Box<T> { | |
fn drop(&mut self) { | |
// FIXME: Do nothing, drop is currently performed by compiler. | |
} | |
} |
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.
Wow, I was aware of Box
special-casing, but I do not recall being able to reinitialize the Box
and reuse it!
@nikomatsakis @pnkfelix Do you happen to know when that might've happened?
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 believe this probably happened as part of the NLL transition. We decided to stop hobbling the treatment of Box
during that discussion.
I will do the tracking issue thing tomorrow. |
@rust-lang/libs any advise on how to proceed here -- should we add two new unstable methods with a tracking issue, or instead add two local private helper functions in |
Per #58021 (comment) it looks like the one use case for those methods can be solved with plain |
I hadn't even thought so far, but you are right... this proper move tracking for boxes entirely sidesteps the need for |
(Short on time this week, will update this weekend) |
☀️ Test successful - checks-travis, status-appveyor |
Discussed in this week's T-compiler meeting. Approved for beta-backport. (In parallel, @pnkfelix wants to make a regression test. But than need not hold up the backport.) |
I was careless and misread the patch, thinking that this was some |
[beta] Rollup backports Cherry-picked: * #58021: Fix fallout from #57667 * #59599: Updated RELEASES.md for 1.34.0 * #59587: Remove #[doc(hidden)] from Error::type_id * #58994: Hide deprecation warnings inside derive expansions * #58015: Expand docs for `TryFrom` and `TryInto`. * #59770: ci: pin android emulator to 28.0.23 * #59704: ci: Update FreeBSD tarball downloads * #59257: Update CI configuration for building Redox libraries * #59724: Function arguments should never get promoted r? @ghost
[beta] Rollup backports Cherry-picked: * #58021: Fix fallout from #57667 * #59599: Updated RELEASES.md for 1.34.0 * #59587: Remove #[doc(hidden)] from Error::type_id * #58994: Hide deprecation warnings inside derive expansions * #58015: Expand docs for `TryFrom` and `TryInto`. * #59770: ci: pin android emulator to 28.0.23 * #59704: ci: Update FreeBSD tarball downloads * #59257: Update CI configuration for building Redox libraries * #59724: Function arguments should never get promoted * #59499: Fix broken download link in the armhf-gnu image * #58330: Add rustdoc JS non-std tests * #58848: Prevent cache issues on version updates r? @ghost
PR #57667 fixed a memory leak in
P::filter_map
(whereP
is the special pointer type used in the compiler's AST).but @eddyb pointed out a bug in it: #57667 (comment)