From 0ea82a07f02119796e7f8305eea73966568cfec6 Mon Sep 17 00:00:00 2001 From: Jake Hughes Date: Thu, 14 Sep 2023 12:40:17 +0100 Subject: [PATCH] Add wrapper type for opting out of FSA 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` wrapper. This is safer than a blanket implementation. --- library/core/src/gc.rs | 42 +++++++++++++++++++++++++ tests/ui/runtime/gc/check_finalizers.rs | 4 ++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/library/core/src/gc.rs b/library/core/src/gc.rs index 04b90fac3536e..8f3ca703ac372 100644 --- a/library/core/src/gc.rs +++ b/library/core/src/gc.rs @@ -47,6 +47,48 @@ impl DerefMut for NonFinalizable { } } +/// 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); + +impl FinalizeUnchecked { + pub unsafe fn new(value: T) -> Self { + FinalizeUnchecked(value) + } +} + +#[unstable(feature = "gc", issue = "none")] +impl Deref for FinalizeUnchecked { + type Target = T; + #[inline(always)] + fn deref(&self) -> &T { + &self.0 + } +} + +#[unstable(feature = "gc", issue = "none")] +impl DerefMut for FinalizeUnchecked { + #[inline(always)] + fn deref_mut(&mut self) -> &mut T { + &mut self.0 + } +} + +#[cfg(not(bootstrap))] +unsafe impl FinalizerSafe for FinalizeUnchecked {} + #[unstable(feature = "gc", issue = "none")] #[cfg_attr(not(test), rustc_diagnostic_item = "ReferenceFree")] pub auto trait ReferenceFree {} diff --git a/tests/ui/runtime/gc/check_finalizers.rs b/tests/ui/runtime/gc/check_finalizers.rs index 6471cfefdae67..705fc7d806196 100644 --- a/tests/ui/runtime/gc/check_finalizers.rs +++ b/tests/ui/runtime/gc/check_finalizers.rs @@ -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; @@ -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)))) }; }