Description
Roughly speaking, #[may_dangle] on a generic parameter in an unsafe Drop
impl promises that the drop behavior does not interact with data of that type other than possibly by dropping it.
To what extent does this restrict the Drop
impl from delegating to other functions that are not annotated with #[may_dangle]?
Canonical example:
#![feature(dropck_eyepatch)]
struct Struct<'a> {
field: &'a u8,
}
unsafe impl<#[may_dangle] 'a> Drop for Struct<'a> {
fn drop(&mut self) {
// may_dangle promises that we do not do the following during the course of Drop:
// println!("{}", *self.field);
self.no_read_field(); // is this UB?
}
}
impl<'a> Struct<'a> {
fn no_read_field(&self) -> usize {
// DO NOT: println!("{}", *self.field);
std::mem::size_of::<Self>()
}
}
fn main() {
fn f<'a>(s: &'a mut Struct<'a>) -> Struct<'a> {
Struct { field: s.field }
}
let mut s = Struct { field: &0 };
f(&mut s); // borrowed value does not live long enough without may_dangle
}
Such code appears to be widespread among uses of #[may_dangle] in the standard library and ecosystem.
My concern is that if inside of no_read_field
we have a reference/pointer that is understood to be dereferenceable
by the compiler backend as insinuated by #77, in theory the backend would be free to insert a spurious read of *self.field
into no_read_field
if it so desires. During a Drop
in which 'a is already dangling, that seems "bad".
Is it necessary for no_read_field
to understand 'a to be may_dangle as well?—
unsafe impl<#[may_dangle] 'a> Struct<'a> {
fn no_read_field(&self) -> usize {
// DO NOT: println!("{}", *self.field);
std::mem::size_of::<Self>()
}
}
Real world instance from standard library code:
unsafe impl<#[may_dangle] T> Drop for LinkedList<T>
- calls
self.pop_front_node()
- which juggles some values of type
Node<T>
, directly containing a field of typeT
If you imagine a spurious read of that T
(suppose T=&u8
) inside pop_front_node
this is analogous to the example above.
I found vaguely related discussions at #84 and #268 but this seems to me a pretty different case that I haven't found discussed. The salient example in both of those other issues is along the lines of #268 (comment) where a value somewhere contains an invalid bit pattern for its type, whereas the current issue involving may_dangle is an issue even if that never happens.