Aoik

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);
}
Prev Post:
Next Post:

Comments:

Reply to: