Rust drop check
Example 1:
// -----
#![allow(dead_code)]
#![allow(unused_assignments)]
#![allow(unused_variables)]
// -----
use std::fmt::Debug;
// -----
struct First<T> {
owned_ptr: *mut T,
}
impl<T> First<T> {
fn new(t_obj: T) -> Self {
Self {
// Put the type T object to heap and store its pointer.
owned_ptr: Box::into_raw(Box::new(t_obj)),
}
}
}
// Custom drop method is not defined for `First`.
//
// impl<T> Drop for First<T> {
// fn drop(&mut self) {
// unsafe {
// // The `Box` obj's `drop` method drops the object pointed to by
// // `self.owned_ptr`, i.e. it calls `second_obj`'s drop method.
// // Note `first_obj`'s data chain is not inferred based on this fact.
// Box::from_raw(self.owned_ptr);
// }
// }
// }
// -----
struct Second<T:Debug>(T);
// Custom drop method is defined for `Second`.
//
impl<T:Debug> Drop for Second<T> {
fn drop(&mut self) {
println!("Dropping Second: {:?}", self.0);
}
}
// -----
// This example compiles.
//
// For a default drop method, drop check analyzes it can not access a dangle
// reference on the data chain, so allows dangle references to exist on the
// data chain.
//
// For a custom drop method, drop check assumes it can potentially access a
// dangle reference on the data chain, so disallows dangle references to exist
// on the data chain. It requires T strictly outlives First<T>, i.e. all
// references on T's data chain are required to have longer lifetime so that
// there is no dangle reference.
//
// `first_obj` has default drop method, which does not call `second_obj`'s drop
// method because `owned_ptr` is just a pointer which does not incur a drop call
// on the pointed object. `&third_obj` is a dangle reference on `first_obj`'s
// data chain but can not be accessed by `first_obj`'s default drop method. This
// is allowed.
//
// `second_obj` has custom drop method, `&third_obj` is not a dangle reference
// on `second_obj`'s data chain. This is allowed.
//
fn main() {
let first_obj;
let third_obj = "test".to_owned();
let second_obj = Second(&third_obj);
first_obj = First::new(second_obj);
}
Example 2:
// -----
#![allow(dead_code)]
#![allow(unused_assignments)]
#![allow(unused_variables)]
// -----
use std::fmt::Debug;
// -----
struct First<T> {
owned_ptr: *mut T,
}
impl<T> First<T> {
fn new(t_obj: T) -> Self {
Self {
// Put the type T object to heap and store its pointer.
owned_ptr: Box::into_raw(Box::new(t_obj)),
}
}
}
// Custom drop method is not defined for `First`.
//
// impl<T> Drop for First<T> {
// fn drop(&mut self) {
// unsafe {
// // The `Box` obj's `drop` method drops the object pointed to by
// // `self.owned_ptr`, i.e. it calls `second_obj`'s drop method.
// // Note `first_obj`'s data chain is not inferred based on this fact.
// Box::from_raw(self.owned_ptr);
// }
// }
// }
// -----
struct Second<T:Debug>(T);
// Custom drop method is defined for `Second`.
//
impl<T:Debug> Drop for Second<T> {
fn drop(&mut self) {
println!("Dropping Second: {:?}", self.0);
}
}
// -----
// This example not compiles.
//
// For a default drop method, drop check analyzes it can not access a dangle
// reference on the data chain, so allows dangle references to exist on the
// data chain.
//
// For a custom drop method, drop check assumes it can potentially access a
// dangle reference on the data chain, so disallows dangle references to exist
// on the data chain. It requires T strictly outlives First<T>, i.e. all
// references on T's data chain are required to have longer lifetime so that
// there is no dangle reference.
//
// `first_obj` has default drop method, which does not call `second_obj`'s drop
// method because `owned_ptr` is just a pointer which does not incur a drop call
// on the pointed object. `&third_obj` is a dangle reference on `first_obj`'s
// data chain but can not be accessed by `first_obj`'s default drop method. This
// is allowed.
//
// `second_obj` has custom drop method, `&third_obj` is a dangle reference on
// `second_obj`'s data chain. This is not allowed.
//
fn main() {
let first_obj;
let second_obj;
let third_obj = "test".to_owned();
// Error
// ===
// `&third_obj`: borrowed value does not live long enough
// ===
second_obj = Second(&third_obj);
first_obj = First::new(second_obj);
}
// Error
// ===
// `third_obj` dropped here while still borrowed
// borrow might be used here, when `second_obj` is dropped and runs the `Drop`
// code for type `Second`
// ===
Example 3:
// -----
#![allow(dead_code)]
#![allow(unused_assignments)]
#![allow(unused_variables)]
// -----
use std::fmt::Debug;
// -----
struct First<T> {
owned_ptr: *mut T,
}
impl<T> First<T> {
fn new(t_obj: T) -> Self {
Self {
// Put the type T object to heap and store its pointer.
owned_ptr: Box::into_raw(Box::new(t_obj)),
}
}
}
// Custom drop method is defined for `First`.
//
impl<T> Drop for First<T> {
fn drop(&mut self) {
unsafe {
// The `Box` obj's `drop` method drops the object pointed to by
// `self.owned_ptr`, i.e. it calls `second_obj`'s drop method.
// Note `first_obj`'s data chain is not inferred based on this fact.
Box::from_raw(self.owned_ptr);
}
}
}
// -----
struct Second<T:Debug>(T);
// Custom drop method is not defined for `Second`.
//
// impl<T:Debug> Drop for Second<T> {
// fn drop(&mut self) {
// println!("Dropping Second: {:?}", self.0);
// }
// }
// -----
// This example not compiles.
//
// For a default drop method, drop check analyzes it can not access a dangle
// reference on the data chain, so allows dangle references to exist on the
// data chain.
//
// For a custom drop method, drop check assumes it can potentially access a
// dangle reference on the data chain, so disallows dangle references to exist
// on the data chain. It requires T strictly outlives First<T>, i.e. all
// references on T's data chain are required to have longer lifetime so that
// there is no dangle reference.
//
// `first_obj` has custom drop method, `&third_obj` is a dangle reference on
// `first_obj`'s data chain. This is not allowed, even if `first_obj`'s custom
// drop method does not actually access the dangle reference.
//
fn main() {
let first_obj;
let third_obj = "test".to_owned();
// Error:
// ===
// `&third_obj`: borrowed value does not live long enough
// ===
let second_obj = Second(&third_obj);
first_obj = First::new(second_obj);
}
// Error:
// ===
// `third_obj` dropped here while still borrowed
// borrow might be used here, when `first_obj` is dropped and runs the `Drop`
// code for type `First`
// ===
Example 4:
// -----
#![feature(dropck_eyepatch)]
#![allow(dead_code)]
#![allow(unused_assignments)]
#![allow(unused_variables)]
// -----
struct First<T> {
owned_ptr: *mut T,
}
impl<T> First<T> {
fn new(t_obj: T) -> Self {
Self {
// Put the type T object to heap and store its pointer.
owned_ptr: Box::into_raw(Box::new(t_obj)),
}
}
}
// Custom drop method is defined for `First`.
//
unsafe impl<#[may_dangle] T> Drop for First<T> {
fn drop(&mut self) {
unsafe {
// The `Box` obj's `drop` method drops the object pointed to by
// `self.owned_ptr`, i.e. it calls `second_obj`'s drop method.
// Note `first_obj`'s data chain is not inferred based on this fact.
Box::from_raw(self.owned_ptr);
}
}
}
// -----
struct Second<T>(T);
// -----
// This example not compiles.
//
// For a default drop method, drop check analyzes it can not access a dangle
// reference on the data chain, so allows dangle references to exist on the
// data chain.
//
// For a custom drop method, drop check assumes it can potentially access a
// dangle reference on the data chain, so disallows dangle references to exist
// on the data chain. It requires T strictly outlives First<T>, i.e. all
// references on T's data chain are required to have longer lifetime so that
// there is no dangle reference.
//
// `first_obj` has custom drop method, `&third_obj` is a dangle reference on
// `first_obj`'s data chain. This is not allowed, even if `first_obj`'s custom
// drop method does not actually access the dangle reference.
//
// `#[may_dangle] T` allows custom drop method to be defined for `first_obj`
// even if there are dangle references on T's data chain. It disables drop check
// on T's data chain. This opens a hole that a drop method of an object on T's
// data chain can access a dangle reference on `first_obj`'s data chain and this
// drop method gets called by the drop chain starting from `first_obj`'s custom
// drop method.
//
// `first_obj`'s custom drop method calls `second_obj`'s custom drop method.
// This not leads to an access to a dangle reference on `first_obj`'s data
// chain. It is not UB.
//
fn main() {
let first_obj;
let third_obj = "test".to_owned();
let second_obj = Second(&third_obj);
first_obj = First::new(second_obj);
}
Example 5:
// -----
#![feature(dropck_eyepatch)]
#![allow(dead_code)]
#![allow(unused_assignments)]
#![allow(unused_variables)]
// -----
use std::fmt::Debug;
// -----
struct First<T> {
owned_ptr: *mut T,
}
impl<T> First<T> {
fn new(t_obj: T) -> Self {
Self {
// Put the type T object to heap and store its pointer.
owned_ptr: Box::into_raw(Box::new(t_obj)),
}
}
}
// Custom drop method is defined for `First`.
//
unsafe impl<#[may_dangle] T> Drop for First<T> {
fn drop(&mut self) {
unsafe {
// The `Box` obj's `drop` method drops the object pointed to by
// `self.owned_ptr`, i.e. it calls `second_obj`'s drop method.
// Note `first_obj`'s data chain is not inferred based on this fact.
Box::from_raw(self.owned_ptr);
}
}
}
// -----
struct Second<T:Debug>(T);
// Custom drop method is defined for `Second`.
//
impl<T:Debug> Drop for Second<T> {
fn drop(&mut self) {
println!("Dropping Second: {:?}", self.0);
}
}
// -----
// This example compiles but is UB.
//
// For a default drop method, drop check analyzes it can not access a dangle
// reference on the data chain, so allows dangle references to exist on the
// data chain.
//
// For a custom drop method, drop check assumes it can potentially access a
// dangle reference on the data chain, so disallows dangle references to exist
// on the data chain. It requires T strictly outlives First<T>, i.e. all
// references on T's data chain are required to have longer lifetime so that
// there is no dangle reference.
//
// `first_obj` has custom drop method, `&third_obj` is a dangle reference on
// `first_obj`'s data chain. This is not allowed, even if `First`'s custom drop
// method does not actually access the dangle reference.
//
// `#[may_dangle] T` allows custom drop method to be defined for `first_obj`
// even if there are dangle references on T's data chain. It disables drop check
// on T's data chain. This opens a hole that a drop method of an object on T's
// data chain can access a dangle reference on `first_obj`'s data chain and this
// drop method gets called by the drop chain starting from `first_obj`'s custom
// drop method.
//
// `first_obj`'s custom drop method calls `second_obj`'s custom drop method.
// This leads to an access to a dangle reference on `first_obj`'s data chain.
// It is UB.
//
fn main() {
let first_obj;
let third_obj = "test".to_owned();
let second_obj = Second(&third_obj);
first_obj = First::new(second_obj);
}
Example 6:
// -----
#![feature(dropck_eyepatch)]
#![allow(dead_code)]
#![allow(unused_assignments)]
#![allow(unused_variables)]
// -----
use std::fmt::Debug;
use std::marker::PhantomData;
// -----
struct First<T> {
owned_ptr: *mut T,
ownership_marker: PhantomData<T>,
}
impl<T> First<T> {
fn new(t_obj: T) -> Self {
Self {
owned_ptr: Box::into_raw(Box::new(t_obj)),
ownership_marker: PhantomData,
}
}
}
// Custom drop method is defined for `First`.
//
unsafe impl<#[may_dangle] T> Drop for First<T> {
fn drop(&mut self) {
unsafe {
// The `Box` obj's `drop` method drops the object pointed to by
// `self.owned_ptr`, i.e. it calls `second_obj`'s drop method.
// Note `first_obj`'s data chain is not inferred based on this fact.
Box::from_raw(self.owned_ptr);
}
}
}
// -----
struct Second<T:Debug>(T);
// Custom drop method is defined for `Second`.
//
impl<T:Debug> Drop for Second<T> {
fn drop(&mut self) {
println!("Dropping Second: {:?}", self.0);
}
}
// -----
// This example not compiles.
//
// For a default drop method, drop check analyzes it can not access a dangle
// reference on the data chain, so allows dangle references to exist on the
// data chain.
//
// For a custom drop method, drop check assumes it can potentially access a
// dangle reference on the data chain, so disallows dangle references to exist
// on the data chain. It requires T strictly outlives First<T>, i.e. all
// references on T's data chain are required to have longer lifetime so that
// there is no dangle reference.
//
// `#[may_dangle] T` allows custom drop method to be defined for `first_obj`
// even if there are dangle references on T's data chain. It disables drop check
// on T's data chain. This opens a hole that a drop method of an object on T's
// data chain can access a dangle reference on `first_obj`'s data chain and this
// drop method gets called by the drop chain starting from `first_obj`'s custom
// drop method.
//
// `ownership_marker: PhantomData<T>` disallows custom drop method to be defined
// for `first_obj` if there is a drop method of an object on T's data chain that
// can potentially access a dangle reference on `first_obj`'s data chain. It
// fills the hole opened by `#[may_dangle] T`.
//
fn main() {
let first_obj;
let third_obj = "test".to_owned();
let second_obj = Second(&third_obj);
first_obj = First::new(second_obj);
}
Comments: