Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign up`#[may_dangle]`, a refined dropck escape hatch (tracking issue for RFC 1327) #34761
Comments
apasel422
added
the
B-RFC-approved
label
Jul 11, 2016
Manishearth
added a commit
to Manishearth/rust
that referenced
this issue
Sep 30, 2016
Manishearth
added a commit
to Manishearth/rust
that referenced
this issue
Oct 1, 2016
pnkfelix
self-assigned this
Oct 3, 2016
This comment has been minimized.
This comment has been minimized.
|
see also #28498 |
nikomatsakis
referenced this issue
Oct 10, 2016
Open
Nonparametric dropck (tracking issue for RFC 1238) #28498
eddyb
added a commit
to eddyb/rust
that referenced
this issue
Oct 18, 2016
eddyb
added a commit
to eddyb/rust
that referenced
this issue
Oct 19, 2016
eddyb
added a commit
to eddyb/rust
that referenced
this issue
Oct 19, 2016
This comment has been minimized.
This comment has been minimized.
apasel422
referenced this issue
Dec 28, 2016
Merged
Replace uses of `#[unsafe_destructor_blind_to_params]` with `#[may_dangle]` #38664
GuillaumeGomez
pushed a commit
to GuillaumeGomez/rust
that referenced
this issue
Jan 4, 2017
GuillaumeGomez
added a commit
to GuillaumeGomez/rust
that referenced
this issue
Jan 4, 2017
steveklabnik
added a commit
to steveklabnik/rust
that referenced
this issue
Jan 4, 2017
frewsxcv
added a commit
to frewsxcv/rust
that referenced
this issue
Jan 9, 2017
frewsxcv
added a commit
to frewsxcv/rust
that referenced
this issue
Jan 9, 2017
sanxiyn
added a commit
to sanxiyn/rust
that referenced
this issue
Jan 10, 2017
apasel422
referenced this issue
Jan 10, 2017
Merged
Deprecate `#[unsafe_destructor_blind_to_params]` #38970
GuillaumeGomez
added a commit
to GuillaumeGomez/rust
that referenced
this issue
Jan 19, 2017
apasel422
referenced this issue
Jan 19, 2017
Merged
Update nomicon to describe `#[may_dangle]` #39196
bors
added a commit
that referenced
this issue
Jan 30, 2017
GuillaumeGomez
added a commit
to GuillaumeGomez/rust
that referenced
this issue
Feb 2, 2017
This comment has been minimized.
This comment has been minimized.
|
I have to say I find |
brson
added
the
B-unstable
label
Mar 1, 2017
Mark-Simulacrum
added
the
C-tracking-issue
label
Jul 22, 2017
This comment has been minimized.
This comment has been minimized.
asajeffrey
commented
Feb 1, 2018
|
An issue that came up in the context of Josephine is asajeffrey/josephine#52. This is mainly an issue for the interaction between the drop checker and untagged unions (#32836 (comment)) but does have impact on the code guidelines for when The issue is the text "When used on a type, e.g. |
This comment has been minimized.
This comment has been minimized.
|
|
This comment has been minimized.
This comment has been minimized.
|
Yes, he knows, that's not what he's asking. The part of the equation that's unsafe and whose contract is in question is |
This comment has been minimized.
This comment has been minimized.
Personally I don't consider My mental model for fn foo<'a, T>(x: &'a i32, y: T)then there are some implicit conditions ( Right now this is all specialized to be just about |
This comment has been minimized.
This comment has been minimized.
|
First, why would adding Second, what even would be the purpose of adding |
This comment has been minimized.
This comment has been minimized.
I didn't say that. All I did is answer the question (quoted in my reply) of whether
No idea. @asajeffrey quite specifically asked about this above, so I gave my 2 cents. Or did I entirely misunderstand their question...? |
asajeffrey
referenced this issue
Feb 2, 2018
Open
Untagged unions (tracking issue for RFC 1444) #32836
This comment has been minimized.
This comment has been minimized.
asajeffrey
commented
Feb 2, 2018
|
Hmm, good point @RalfJung, perhaps my model of I'd been thinking about it as specific to the drop checker, in that code such as: fn foo(x: T) {
// ... do stuff with x that doesn't transfer ownership
}informally desugars to: fn foo(mut x: T) {
// ... do stuff with x that doesn't transfer ownership
T::drop(&mut x); // if T implements Drop
mem::forget(x);
}but that But there may be a way of thinking about it more generally, as loosening the requirement that in @RalfJung there certainly are ways of reading the text as allowing Also, your example at #32836 (comment) is interesting. Slightly messed around with, it's: struct Transmuted<T> {
data: Vec<u8>,
marker: PhantomData<T>,
}
impl<T> Transmuted<T> {
fn new(x: T) { ... horrible mem::transmute to store the byte representation of x in data }
}
impl<T> Deref for Transmuted<T> {
type Target = T;
fn deref(&self) -> &T { ... more horrible transmuting ... }
}
impl<#[may_dangle] T> Drop for Transmuted<T> {
fn drop(&mut self) { ... what can we do with data here? ... }
}We're not allowed to dereference data as a |
This comment has been minimized.
This comment has been minimized.
|
(Quoting from another thread but I think it better fits here)
If the code says
I think we'd all like to better understand what it takes to make this a usable, modular and ideally safe concept. :)
Except, of course, we know it was a Actually most methods that work for any generic (And this is where I realized that I can explain the failure of the parametricity-based argument in terms of my Of course, it turned out this does not work. The counterexample involves a type like struct S<T> { x: Option<T>, f: fn (T) }And of course, |
This comment has been minimized.
This comment has been minimized.
Thanks a lot for this! I came up with my model mostly because I didn't see any model defined anywhere, so this is very useful. What I'm currently struggling with is reconciling "but that &mut x borrow impacts the borrow-checker, and may_dangle tells the drop-checker to pretend it's not there" with the fact that unsafe impl <#[may_dangle] T, A: Allocator> Drop for AllocVec<T, A> {
fn drop(&mut AllocVec<T, A>) {
// can rely on A being alive, but not T
}
}then how is that reconciled by the borrow checker at the drop call site? I like your desugaring into drop-wth-borrow + drop(&mut x);
drop(&mut x);
mem::forget(x);which is clearly not the case. What I think I'd like to add to the picture is where exactly the lifetimes end. In a usual situation, we may have something like this: // Assume some outer lifetime `'a`
let v : Vec<&'a i32> = Vec::new(); // lifetime of this variable: 'v
// ...
// The vector "dies" during the drop call, so we have to end the lifetime *before*
// calling drop.
EndLifetime('v);
drop(&mut v); mem::forget(v);
EndLifetime('a);Everything is fine here as However, if we have cyclic references between elements in the Vec (or with your let v : Vec<&'v i32> = Vec::new(); // lifetime of this variable: 'v
// It's literally cyclic! The type of this variable refers to its own lifetime.
EndLifetime('v); // As usual end the lifetime of a variable before it is dropped
drop(&mut v); // Uh-oh. 'v is dead and yet we are calling Vec<&'v i32>::drop. *explosion sound*Because the type of this variable involves its own lifetime, there just is no way to end that lifetime and call the destructor in any order. So Rust rejects this, unless it knows that This scales to let v : AllocVec<&'v i32, Allocator<'a>>but we cannot have a let v : AllocVec<&'v i32, Allocator<'v>>So, I guess I just repeated my point about WF checks from the perspective of the drop call site: The actual Please let me know what you think... I'm kind of making this up as I go here; I've had plenty of fuzzy thoughts for a while but this is the first time I'm writing them down. |
This comment has been minimized.
This comment has been minimized.
asajeffrey
commented
Feb 2, 2018
|
Trying to make my slightly incoherent examples more precise... The kind of thing I'm worried about is something like: struct Bar<'a>(*const String, PhantomData<&'a()>);
impl<'a> Bar<'a> {
fn new(x: &'a String) -> Bar<'a> { Bar(x, PhantomData) }
}
impl<'a> Deref for Bar<'a> {
Target = String;
fn deref(&self) -> &String { unsafe { &*self.0 } }
}
unsafe impl<#[may_dangle] 'a> Drop for Bar<'a> {
fn drop(&mut self) { unsafe { println!("{}", &*self.0) } }
}AFAICT a) without the But it's unsafe, since it's printing a string that might have been reclaimed. This is what I'm meaning about which type (e.g. for |
This comment has been minimized.
This comment has been minimized.
asajeffrey
commented
Feb 2, 2018
|
OK, the desugaring is a bit inaccurate, instead of: T::drop(&mut x);
T::drop(&mut x);
mem::forget(x);it should be something like: if !x.drop_flag { T::drop(&mut x); x.drop_flag = true; }
if !x.drop_flag { T::drop(&mut x); x.drop_flag = true; }
mem::forget(x);but I don't think this affects the typing. Putting in the explicit lifetimes is interesting, as it makes clear the difference between the usual desugaring: T<'a>::drop(&mut x);
EndLifetime('a);
mem::forget(x);and the one that EndLifetime('a);
T<'static>::drop(&mut x);
mem::forget(x);that is a |
This comment has been minimized.
This comment has been minimized.
The implementation of |
This comment has been minimized.
This comment has been minimized.
asajeffrey
commented
Feb 3, 2018
|
@arielb1 indeed, the issue is one of the definition of the |
This comment has been minimized.
This comment has been minimized.
I disagree. The wording in the RFC is
Notice it doesn't say "static". Semantically speaking, your So, your code violates my reading of the
I wasn't talking about the drop flag. My comment about the type being a lie refers to the fact that
This should be |
This comment has been minimized.
This comment has been minimized.
|
So one version of the invariant josephine wants is basically this:
That makes josephine sound: after a place is borrowed for The invariant also probably holds in "specified pre-1.20 Rust": in safe code, if a place is memory-overwritten, then (up to the OTOH, that invariant is never something we tried to guarantee. In fact, a struct Cyclic<A, T> {
cycle: RefCell<Option<Rc<Cyclic<T, A>, A>>,
data: T
}
fn foo() {
let arena = make_arena();
let rc1 = arena <- Cyclic {
cycle: Default::default(),
data: Pin::new(),
};
// make a cycle
*rc1.cycle.borrow_mut() = Some(rc1.clone());
rc1.data.pin();
// at end-of-scope, no destructors are run, but the arena (and its containing Rc) are free-ed.
} |
This comment has been minimized.
This comment has been minimized.
|
What is blocking stabilization of |
This comment has been minimized.
This comment has been minimized.
|
@Centril nothing really... |
This comment has been minimized.
This comment has been minimized.
|
What can we do to stabilise |
This comment has been minimized.
This comment has been minimized.
|
@nox I would guess: make a PR that stabilizes it and ask the lang team to do a FCP. |
This comment has been minimized.
This comment has been minimized.
Best thing: create a new issue for just that stabilization. Describe briefly the behavior you want to stabilize (as there is no RFC we need a bit more explanation, but then this is very simple so that seems easy enough). Ideally, show pointers to some tests that include that behavior. I would particularly be interested in tests that show that we properly error on unrecognized attributes (like To be clear: I think we can stabilize this. =) |
This comment has been minimized.
This comment has been minimized.
Unknown attributes are easy, but this is much harder because known attributes are generally accepted in any weird positions with any syntax without validation, e.g. we already accept a lot of things we don't expect fn main() {
#[inline(x = "y")] // OK
let x = 10;
}Fixing this is somewhere in my queue though! Maybe I'll even get to it this year. |

pnkfelix commentedJul 11, 2016
•
edited
Tracking issue for rust-lang/rfcs#1327
generic_param_attrsfeature): #34764#[may_dangle]attribute for lifetime/type formal parameters onunsafe impl<...> Drop(dropck_eyepatchfeature)#[unsafe_destructor_blind_to_params]attribute in libstd with#[may_dangle]#38664#[may_dangle]instead of#[unsafe_destructor_blind_to_params]#39196#[unsafe_destructor_blind_to_params]attribute (with warning cycle before removal).#[unsafe_destructor_blind_to_params]with[may_dangle]before we can actually remove the former.