Repository navigation
Prohibit specialized drops #8142
Description
Activity
Is there a use case for this? C++ doesn't give you a way to do it without going to the trouble of template specializing the whole type (and I've never missed it). You could just prohibit writing a
Dropimpl that isn't of the form type-constructor-applied-to-zero-or-more-type-variables. (Further prior art is that standard Haskell enforces this restriction for /all/ type class instances, and requires theFlexibleInstanceslanguage extension to lift it.)I'd be okay if this would be prohibited because this can always be worked around by adding flag variables to the value although this would be a neat feature. One use case that I was trying to use this for was to have a drop instance for an
Attribute<Enabled>type that disables it, and not for theAttribute<Disabled>type (whereAttributerefers to an OpenGL vertex attribute, andEnabled, andDisabledare phantom types.Reacted by Kornel and jgarvinInteresting, if it's not useless it might be worth keeping. :) There was also a discussion somewhere about what it means to have trait bounds on a Drop impl which seems vaguely related, but I can't find it...
This issue seems similar to issue #6971
your example
Attribute<Disabled>makes me think of c_vec which has two modes, either it owns the buffer or it doesn't.Nominating, this is a pretty serious problem.
accepted for "first major release", P-high.
Updated text to describe our planned fix.
I would perhaps like to fix this issue in the near-term by just putting
#[unsafe_destructor]behind a feature gate.- added a commit that references this issue
on Jun 20, 2014 - added a commit that references this issue
on Jun 20, 2014 11 remaining items
This blocks removing the
unsafe_destructorfeature gate.calling 1.0 polish unless discussion of #22196 changes my opinion.
okay I think this is the last thing I think is blocking #22196, so while it is categorized as "1.0 polish", it really would be good to get it in for the beta. Looking at it now.
I have something plausible put together; the main thing remaining is dealing with the fallout of either adding or removing the appropriate bounds everywhere in
libstdin order to placate the pass.The pass I have put together seems to handle type-constraints properly, but it does not yet properly deal with region constraints.
For a little while I was not sure if this would be a problem, but I did eventually come up with a test case illustrating why one must ensure region constraints are indeed dealt with properly:
#![feature(unsafe_destructor)] use std::cell::Cell; struct InvalidateOnDrop { orig_value: &'static str, value: &'static str } // Note: Definition has no constraint relating 'b and 'a ... struct P<'c, 'b:'c, 'a> { x: &'c Cell<&'b InvalidateOnDrop>, y: &'a InvalidateOnDrop } // ... but the Drop impl says 'a outlives 'b, and thus is // able to copy `self.y` into `self.x`. Havoc ensues below. #[unsafe_destructor] impl<'c, 'b:'c, 'a:'b> Drop for P<'c, 'b, 'a> { fn drop(&mut self) { self.x.set(self.y); } } #[allow(non_snake_case)] fn InvalidateOnDrop(s: &'static str) -> InvalidateOnDrop { InvalidateOnDrop { orig_value: s, value: s, } } impl Drop for InvalidateOnDrop { fn drop(&mut self) { self.value = "invalidated"; } } struct Loud<'l1, 'l2:'l1> { name: &'static str, value: &'l1 Cell<&'l2 InvalidateOnDrop> } #[unsafe_destructor] impl<'l1, 'l2> Drop for Loud<'l1, 'l2> { fn drop(&mut self) { let orig = self.value.get().orig_value; let val = self.value.get().value; if orig == val { println!("dropping Loud {} pointing to {}", self.name, val); } else { println!("dropping Loud {} pointing to {} (orig: {})", self.name, val, orig); } } } fn main() { let b = InvalidateOnDrop("b"); let c = Cell::new(&b); let _l1 = Loud { name: "l1", value: &c }; let a = InvalidateOnDrop("a"); let _p = P { x: &c, y: &a }; let _l2 = Loud { name: "l2", value: &c }; println!("Hello World"); }
The above prints out (if you are lucky and other data corruption does not happen first):
Hello World dropping Loud l2 pointing to b dropping Loud l1 pointing to invalidated (orig: a)thus illustrating that the
Louddestructor observed a value in a post-drop state. badness.ah sweet, I think I actually figured out how to do this (thank goodness I have
compare_method.rsto look at).And it took me way less time that it did for me to come up with above example -- I just needed the motivation!
(Update: spoke too soon: My check is rejecting too many programs. Time to read
compare_method.rsmore carefully.)
UPDATE:
The plan for drops, as I recall, was to prohibit "specialized" drops, but this never got implemented. The idea is to require that impls of
Dropmeet special criteria such that theSelfparameter must (a) be a nominal type (struct or enum); (b) have fresh type parameters for each type parameter of the type with (c) no additional bounds beyond those declared in the type declaration. Note that we will have to permit type bounds in type declarations, as well, which is to some extent a separate issue (also required for DST).For example, this is legal:
but this is not
nor the example below.
ORIGINAL:
The following code prints
Dropping!. This is wrong.