Skip to content

Commit

Permalink
Add wrapper type for opting out of FSA
Browse files Browse the repository at this point in the history
This can be very useful when using types from third party crates which
do not implement `FinalizerSafe`. Instead of implementing
`FinalizerSafe` on any `T` with a `!FinalizerSafe` field, we can now
ensure each problematic field uses the `FinalizeUnchecked<U>` wrapper.
This is safer than a blanket implementation.
  • Loading branch information
jacob-hughes committed Sep 14, 2023
1 parent c56e16c commit 0ea82a0
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 1 deletion.
42 changes: 42 additions & 0 deletions library/core/src/gc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,48 @@ impl<T: ?Sized> DerefMut for NonFinalizable<T> {
}
}

/// A wrapper to prevent alloy from performing Finaliser Safety Analysis (FSA)
/// on `T`.
///
/// FSA is a compile-time analysis performed by alloy which checks whether it is
/// sound to call a type's drop method by a garbage collection finaliser. It
/// works by looking at each line in T's drop method for potential soundness
/// violations.
///
/// However, where this is too strict -- and the user knows T::drop to be sound
/// -- `FinalizeUnchecked` can be used to opt-out of FSA. This is preferable to
/// implementing the `FinalizerSafe` trait for `T` as `FinalizeUnchecked`
/// applies only to individual uses of `T`.
#[unstable(feature = "gc", issue = "none")]
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct FinalizeUnchecked<T: ?Sized>(T);

impl<T> FinalizeUnchecked<T> {
pub unsafe fn new(value: T) -> Self {
FinalizeUnchecked(value)
}
}

#[unstable(feature = "gc", issue = "none")]
impl<T: ?Sized> Deref for FinalizeUnchecked<T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &T {
&self.0
}
}

#[unstable(feature = "gc", issue = "none")]
impl<T: ?Sized> DerefMut for FinalizeUnchecked<T> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut T {
&mut self.0
}
}

#[cfg(not(bootstrap))]
unsafe impl<T> FinalizerSafe for FinalizeUnchecked<T> {}

#[unstable(feature = "gc", issue = "none")]
#[cfg_attr(not(test), rustc_diagnostic_item = "ReferenceFree")]
pub auto trait ReferenceFree {}
Expand Down
4 changes: 3 additions & 1 deletion tests/ui/runtime/gc/check_finalizers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#![feature(negative_impls)]

use std::cell::Cell;
use std::gc::Gc;
use std::gc::{Gc, FinalizeUnchecked};
use std::marker::FinalizerSafe;
use std::rc::Rc;

Expand Down Expand Up @@ -61,4 +61,6 @@ fn main() {

let self_call = ShouldFail2(123 as *mut u8);
Gc::new(self_call); //~ ERROR: `self_call` cannot be safely finalized.

unsafe { Gc::new(FinalizeUnchecked::new(ShouldFail(Cell::new(123)))) };
}

0 comments on commit 0ea82a0

Please sign in to comment.