diff --git a/compiler/rustc_mir_transform/src/check_finalizers.rs b/compiler/rustc_mir_transform/src/check_finalizers.rs index e65e67069010b..f3bd9cd551589 100644 --- a/compiler/rustc_mir_transform/src/check_finalizers.rs +++ b/compiler/rustc_mir_transform/src/check_finalizers.rs @@ -1,5 +1,6 @@ #![allow(rustc::untranslatable_diagnostic)] #![allow(rustc::diagnostic_outside_of_impl)] +use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_middle::mir::visit::PlaceContext; @@ -20,8 +21,8 @@ enum FinalizerErrorKind<'tcx> { NotSendAndSync(Span), /// Does not implement `FinalizerSafe` NotFinalizerSafe(Ty<'tcx>, Span), - /// Does not implement `ReferenceFree` - NotReferenceFree, + /// Contains a field projection where one of the projection elements is a reference. + UnsoundReference(Ty<'tcx>, Span), /// Uses a trait object whose concrete type is unknown UnknownTraitObject, /// Calls a function whose definition is unavailable, so we can't be certain it's safe. @@ -70,19 +71,19 @@ impl<'tcx> FinalizerErrorKind<'tcx> { err.help("`Gc` runs finalizers on a separate thread, so drop methods\nmust only use values whose types implement `FinalizerSafe`."); err.span_label( cx.ctor, - format!("`Gc::new` requires that it implements the `FinalizeSafe` trait.",), + format!( + "`Gc::new` requires that {ty} implements the `FinalizeSafe` trait.", + ), ); } } - Self::NotReferenceFree => { + Self::UnsoundReference(ty, span) => { + err.span_label(*span, "caused by the expression here in `fn drop(&mut)` because"); err.span_label( - cx.arg, - "contains a reference (&) which is not safe to be used in a finalizer.", - ); - err.span_label( - cx.ctor, - format!("`Gc::new` requires finalizable types to be reference free.",), + *span, + format!("it is a reference ({ty}) which is not safe to use in a finalizer."), ); + err.help("`Gc` may run finalizers after the valid lifetime of this reference."); } Self::MissingFnDef => { err.span_label(cx.arg, "contains a function call which may be unsafe."); @@ -152,10 +153,6 @@ impl<'tcx> FinalizationCtxt<'tcx> { return Ok(()); } - if !self.is_reference_free(ty) { - return Err(vec![FinalizerErrorKind::NotReferenceFree]); - } - if self.is_send(ty) && self.is_sync(ty) && self.is_finalizer_safe(ty) { return Ok(()); } @@ -265,16 +262,6 @@ impl<'tcx> FinalizationCtxt<'tcx> { .must_apply_modulo_regions(); } - fn is_reference_free(&self, ty: Ty<'tcx>) -> bool { - let t = self.tcx.get_diagnostic_item(sym::ReferenceFree).unwrap(); - return self - .tcx - .infer_ctxt() - .build() - .type_implements_trait(t, [ty], self.param_env) - .must_apply_modulo_regions(); - } - fn is_copy(&self, ty: Ty<'tcx>) -> bool { ty.is_copy_modulo_regions(self.tcx, self.param_env) } @@ -316,23 +303,73 @@ impl<'tcx> FinalizationCtxt<'tcx> { } return false; } + + /// For a given projection, extract the 'useful' type which needs checking for finalizer safety. + /// + /// Simplifying somewhat, a projection is a way of peeking into a place. For FSA, the + /// projections that are interesting to us are struct/enum fields, and slice/array indices. When + /// we find these, we want to extract the type of the field or slice/array element for further + /// analysis. This is best explained with an example, the following shows the projection, and + /// what type would be returned: + /// + /// a[i] -> typeof(a[i]) + /// a.b[i] -> typeof(a.b[i]) + /// a.b -> typeof(b) + /// a.b.c -> typeof(c) + /// + /// In practice, this means that the type of the last projection is extracted and returned. + fn extract_projection_ty( + &self, + body: &Body<'tcx>, + base: PlaceRef<'tcx>, + elem: ProjectionElem>, + ) -> Option> { + match elem { + ProjectionElem::Field(_, ty) => Some(ty), + ProjectionElem::Index(_) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } => { + let array_ty = match base.last_projection() { + Some((last_base, last_elem)) => { + last_base.ty(body, self.tcx).projection_ty(self.tcx, last_elem).ty + } + None => base.ty(body, self.tcx).ty, + }; + match array_ty.kind() { + ty::Array(ty, ..) | ty::Slice(ty) => Some(*ty), + _ => None, + } + } + _ => None, + } + } } struct DropMethodChecker<'a, 'tcx> { body: &'a Body<'tcx>, cx: &'a FinalizationCtxt<'tcx>, errors: Vec>, + error_locs: FxHashSet, } impl<'a, 'tcx> DropMethodChecker<'a, 'tcx> { fn new(body: &'a Body<'tcx>, fctxt: &'a FinalizationCtxt<'tcx>) -> Self { - Self { body, cx: fctxt, errors: Vec::new() } + Self { body, cx: fctxt, errors: Vec::new(), error_locs: FxHashSet::default() } } fn check(mut self) -> Result<(), Vec>> { self.visit_body(self.body); if self.errors.is_empty() { Ok(()) } else { Err(self.errors) } } + + fn push_error(&mut self, location: Location, error: FinalizerErrorKind<'tcx>) { + if self.error_locs.contains(&location) { + return; + } + + self.errors.push(error); + self.error_locs.insert(location); + } } impl<'a, 'tcx> Visitor<'tcx> for DropMethodChecker<'a, 'tcx> { @@ -342,18 +379,32 @@ impl<'a, 'tcx> Visitor<'tcx> for DropMethodChecker<'a, 'tcx> { context: PlaceContext, location: Location, ) { - for (_, proj) in place_ref.iter_projections() { - match proj { - ProjectionElem::Field(_, ty) => { - let span = self.body.source_info(location).span; - if !self.cx.is_send(ty) || !self.cx.is_sync(ty) { - self.errors.push(FinalizerErrorKind::NotSendAndSync(span)); - } - if !self.cx.is_finalizer_safe(ty) { - self.errors.push(FinalizerErrorKind::NotFinalizerSafe(ty, span)); - } - } - _ => (), + // A single projection can be comprised of other 'inner' projections (e.g. self.a.b.c), so + // this loop ensures that the types of each intermediate projection is extracted and then + // checked. + for ty in place_ref + .iter_projections() + .filter_map(|(base, elem)| self.cx.extract_projection_ty(self.body, base, elem)) + { + let span = self.body.source_info(location).span; + if !self.cx.is_send(ty) || !self.cx.is_sync(ty) { + self.push_error(location, FinalizerErrorKind::NotSendAndSync(span)); + break; + } + if ty.is_ref() { + // Unfortunately, we can't relax this constraint to allow static refs for two + // reasons: + // 1. When this MIR transformation is called, all lifetimes have already + // been erased by borrow-checker. + // 2. Unsafe code can and does transmute lifetimes up to 'static then use + // runtime properties to ensure that the reference is valid. FSA would + // not catch this and could allow unsound programs. + self.push_error(location, FinalizerErrorKind::UnsoundReference(ty, span)); + break; + } + if !self.cx.is_finalizer_safe(ty) { + self.push_error(location, FinalizerErrorKind::NotFinalizerSafe(ty, span)); + break; } } self.super_projection(place_ref, context, location); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 888cf266abf17..2aa7f331359cf 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -284,7 +284,6 @@ symbols! { RefCell, RefCellRef, RefCellRefMut, - ReferenceFree, Relaxed, Release, Result, diff --git a/library/core/src/gc.rs b/library/core/src/gc.rs index 6c77d9d897524..47a2ba95e8efa 100644 --- a/library/core/src/gc.rs +++ b/library/core/src/gc.rs @@ -47,13 +47,6 @@ impl DerefMut for NonFinalizable { } } -#[unstable(feature = "gc", issue = "none")] -#[cfg_attr(not(test), rustc_diagnostic_item = "ReferenceFree")] -pub unsafe auto trait ReferenceFree {} - -impl !ReferenceFree for &T {} -impl !ReferenceFree for &mut T {} - /// A wrapper to prevent alloy from performing Finaliser Safety Analysis (FSA) /// on `T`. /// @@ -96,6 +89,3 @@ impl DerefMut for FinalizeUnchecked { #[cfg(not(bootstrap))] unsafe impl FinalizerSafe for FinalizeUnchecked {} - -#[cfg(not(bootstrap))] -unsafe impl ReferenceFree for FinalizeUnchecked {} diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 4c84b5e6108b5..50b4238a9bdc3 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -623,6 +623,11 @@ impl !FinalizerSafe for *const T {} #[unstable(feature = "gc", issue = "none")] impl !FinalizerSafe for *mut T {} +#[unstable(feature = "gc", issue = "none")] +impl !FinalizerSafe for &T {} +#[unstable(feature = "gc", issue = "none")] +impl !FinalizerSafe for &mut T {} + /// Zero-sized type used to mark things that "act like" they own a `T`. /// /// Adding a `PhantomData` field to your type tells the compiler that your diff --git a/library/std/src/gc.rs b/library/std/src/gc.rs index f11d75cb503bd..1aab18f9cbba2 100644 --- a/library/std/src/gc.rs +++ b/library/std/src/gc.rs @@ -560,7 +560,7 @@ impl GcBox> { #[cfg(not(no_global_oom_handling))] #[unstable(feature = "gc", issue = "none")] -impl Default for Gc { +impl Default for Gc { /// Creates a new `Gc`, with the `Default` value for `T`. /// /// # Examples diff --git a/tests/rustdoc/empty-section.rs b/tests/rustdoc/empty-section.rs index e0140b315cad0..db455f7106dd8 100644 --- a/tests/rustdoc/empty-section.rs +++ b/tests/rustdoc/empty-section.rs @@ -12,4 +12,3 @@ impl !FinalizerSafe for Foo {} impl !std::marker::Unpin for Foo {} impl !std::panic::RefUnwindSafe for Foo {} impl !std::panic::UnwindSafe for Foo {} -unsafe impl std::gc::ReferenceFree for Foo {} diff --git a/tests/ui/runtime/gc/check_finalizers.rs b/tests/ui/runtime/gc/check_finalizers.rs index 8c9916fbd059f..8d83007208183 100644 --- a/tests/ui/runtime/gc/check_finalizers.rs +++ b/tests/ui/runtime/gc/check_finalizers.rs @@ -70,7 +70,6 @@ fn main() { Gc::new(ShouldFail(Cell::new(123))); //~^ ERROR: `ShouldFail(Cell::new(123))` has a drop method which cannot be safely finalized. - //~^^ ERROR: `ShouldFail(Cell::new(123))` has a drop method which cannot be safely finalized. let gcfields = HasGcFields(Gc::new(123)); Gc::new(gcfields); diff --git a/tests/ui/runtime/gc/check_finalizers.stderr b/tests/ui/runtime/gc/check_finalizers.stderr index 20c9de2b0b140..924878c6d1977 100644 --- a/tests/ui/runtime/gc/check_finalizers.stderr +++ b/tests/ui/runtime/gc/check_finalizers.stderr @@ -13,23 +13,8 @@ LL | Gc::new(ShouldFail(Cell::new(123))); = help: `Gc` runs finalizers on a separate thread, so drop methods must only use values whose types implement `Send` + `Sync`. -error: `ShouldFail(Cell::new(123))` has a drop method which cannot be safely finalized. - --> $DIR/check_finalizers.rs:71:13 - | -LL | self.0.replace(456); - | ------ - | | - | caused by the expression in `fn drop(&mut)` here because - | it uses a type which is not safe to use in a finalizer. -... -LL | Gc::new(ShouldFail(Cell::new(123))); - | --------^^^^^^^^^^^^^^^^^^^^^^^^^^- `Gc::new` requires that it implements the `FinalizeSafe` trait. - | - = help: `Gc` runs finalizers on a separate thread, so drop methods - must only use values whose types implement `FinalizerSafe`. - error: `gcfields` has a drop method which cannot be safely finalized. - --> $DIR/check_finalizers.rs:76:13 + --> $DIR/check_finalizers.rs:75:13 | LL | println!("Boom {}", self.0); | ------ @@ -41,13 +26,13 @@ LL | Gc::new(gcfields); | --------^^^^^^^^- Finalizers cannot safely dereference other `Gc`s, because they might have already been finalised. error: `self_call` has a drop method which cannot be safely finalized. - --> $DIR/check_finalizers.rs:80:13 + --> $DIR/check_finalizers.rs:79:13 | LL | Gc::new(self_call); | ^^^^^^^^^ contains a function call which may be unsafe. error: `not_threadsafe` has a drop method which cannot be safely finalized. - --> $DIR/check_finalizers.rs:84:13 + --> $DIR/check_finalizers.rs:83:13 | LL | println!("Boom {}", self.0.0); | -------- @@ -61,5 +46,5 @@ LL | Gc::new(not_threadsafe); = help: `Gc` runs finalizers on a separate thread, so drop methods must only use values whose types implement `Send` + `Sync`. -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/static/gc/fsa/auxiliary/types.rs b/tests/ui/static/gc/fsa/auxiliary/types.rs new file mode 100644 index 0000000000000..e20d8033516b3 --- /dev/null +++ b/tests/ui/static/gc/fsa/auxiliary/types.rs @@ -0,0 +1,44 @@ +#[inline(never)] +fn use_val(x: T) { + dbg!("{:?}", x); +} + +#[derive(Debug)] +struct HasNestedRef<'a> { + a: &'a u64, + b: u64, + c: HasRef<'a>, +} + +#[derive(Debug)] +struct HasRef<'a> { + a: &'a u64, + b: u64, + c: [&'a u64; 2] +} + +impl<'a> HasRef<'a> { + #[inline(never)] + fn new(a: &'a u64, b: u64, c: [&'a u64; 2]) -> Self { + Self { a, b, c } + } + + #[inline(always)] + fn new_inlined(a: &'a u64, b: u64, c: [&'a u64; 2]) -> Self { + Self { a, b, c } + } +} + +impl<'a> std::default::Default for HasRef<'a> { + #[inline(never)] + fn default() -> Self { + Self { a: &1, b: 1, c: [&1, &2] } + } +} + +impl<'a> std::default::Default for HasNestedRef<'a> { + #[inline(never)] + fn default() -> Self { + Self { a: &1, b: 1, c: HasRef::default() } + } +} diff --git a/tests/ui/static/gc/fsa/references.rs b/tests/ui/static/gc/fsa/references.rs new file mode 100644 index 0000000000000..bba4ff8d917c2 --- /dev/null +++ b/tests/ui/static/gc/fsa/references.rs @@ -0,0 +1,31 @@ +#![feature(gc)] +#![feature(negative_impls)] +#![allow(dead_code)] +#![allow(unused_variables)] +include!{"./auxiliary/types.rs"} + +impl<'a> Drop for HasRef<'a> { + fn drop(&mut self) { + use_val(self.a); // should fail + use_val(self.b); // should pass + use_val(self.c[0]); // should fail + + let a = self.a; // should fail + let b = self.b; // should pass + let c = self.c; + use_val(c[1]); // should fail + + // should pass, as not a field projection + let c = &1; + use_val(c); + } +} + +fn main() { + std::gc::Gc::new(HasRef::default()); + //~^ ERROR: `HasRef::default()` has a drop method which cannot be safely finalized. + //~^^ ERROR: `HasRef::default()` has a drop method which cannot be safely finalized. + //~^^^ ERROR: `HasRef::default()` has a drop method which cannot be safely finalized. + //~^^^^ ERROR: `HasRef::default()` has a drop method which cannot be safely finalized. + //~^^^^^ ERROR: `HasRef::default()` has a drop method which cannot be safely finalized. +} diff --git a/tests/ui/static/gc/fsa/references.stderr b/tests/ui/static/gc/fsa/references.stderr new file mode 100644 index 0000000000000..fb929b6f40ba4 --- /dev/null +++ b/tests/ui/static/gc/fsa/references.stderr @@ -0,0 +1,74 @@ +error: `HasRef::default()` has a drop method which cannot be safely finalized. + --> $DIR/references.rs:25:22 + | +LL | use_val(self.a); // should fail + | ------ + | | + | caused by the expression here in `fn drop(&mut)` because + | it is a reference (&u64) which is not safe to use in a finalizer. +... +LL | std::gc::Gc::new(HasRef::default()); + | ^^^^^^^^^^^^^^^^^ + | + = help: `Gc` may run finalizers after the valid lifetime of this reference. + +error: `HasRef::default()` has a drop method which cannot be safely finalized. + --> $DIR/references.rs:25:22 + | +LL | use_val(self.c[0]); // should fail + | --------- + | | + | caused by the expression in `fn drop(&mut)` here because + | it uses a type which is not safe to use in a finalizer. +... +LL | std::gc::Gc::new(HasRef::default()); + | -----------------^^^^^^^^^^^^^^^^^- `Gc::new` requires that [&u64; 2] implements the `FinalizeSafe` trait. + | + = help: `Gc` runs finalizers on a separate thread, so drop methods + must only use values whose types implement `FinalizerSafe`. + +error: `HasRef::default()` has a drop method which cannot be safely finalized. + --> $DIR/references.rs:25:22 + | +LL | let a = self.a; // should fail + | ------ + | | + | caused by the expression here in `fn drop(&mut)` because + | it is a reference (&u64) which is not safe to use in a finalizer. +... +LL | std::gc::Gc::new(HasRef::default()); + | ^^^^^^^^^^^^^^^^^ + | + = help: `Gc` may run finalizers after the valid lifetime of this reference. + +error: `HasRef::default()` has a drop method which cannot be safely finalized. + --> $DIR/references.rs:25:22 + | +LL | let c = self.c; + | ------ + | | + | caused by the expression in `fn drop(&mut)` here because + | it uses a type which is not safe to use in a finalizer. +... +LL | std::gc::Gc::new(HasRef::default()); + | -----------------^^^^^^^^^^^^^^^^^- `Gc::new` requires that [&u64; 2] implements the `FinalizeSafe` trait. + | + = help: `Gc` runs finalizers on a separate thread, so drop methods + must only use values whose types implement `FinalizerSafe`. + +error: `HasRef::default()` has a drop method which cannot be safely finalized. + --> $DIR/references.rs:25:22 + | +LL | use_val(c[1]); // should fail + | ---- + | | + | caused by the expression here in `fn drop(&mut)` because + | it is a reference (&u64) which is not safe to use in a finalizer. +... +LL | std::gc::Gc::new(HasRef::default()); + | ^^^^^^^^^^^^^^^^^ + | + = help: `Gc` may run finalizers after the valid lifetime of this reference. + +error: aborting due to 5 previous errors + diff --git a/tests/ui/static/gc/fsa/references_nested.rs b/tests/ui/static/gc/fsa/references_nested.rs new file mode 100644 index 0000000000000..5316a878e12c3 --- /dev/null +++ b/tests/ui/static/gc/fsa/references_nested.rs @@ -0,0 +1,44 @@ +#![feature(gc)] +#![feature(negative_impls)] +#![allow(dead_code)] +#![allow(unused_variables)] +include!{"./auxiliary/types.rs"} + +impl<'a> Drop for HasNestedRef<'a> { + fn drop(&mut self) { + use_val(self.a); // should fail + use_val(self.b); // should pass + + // Project through to the nested `HasRef` struct. + use_val(self.c.a); // should fail + use_val(self.c.b); // should pass + + let a = self.a; // should fail + let b = self.b; // should pass + + let ca = self.a; // should fail + let cb = self.b; // should pass + + // should pass, as not a field projection + let d = &1; + use_val(d); + + let e = HasRef::default(); + // Should fail as it is a field projection. Ideally this should be allowed because these + // references are not fields on the `self` type. However, FSA is not sophisticated enough to + // make this distinction. + use_val(e.a); + // Should pass + use_val(e.b); + } +} + +fn main() { + std::gc::Gc::new(HasNestedRef::default()); + //~^ ERROR: `HasNestedRef::default()` has a drop method which cannot be safely finalized. + //~^^ ERROR: `HasNestedRef::default()` has a drop method which cannot be safely finalized. + //~^^^ ERROR: `HasNestedRef::default()` has a drop method which cannot be safely finalized. + //~^^^^ ERROR: `HasNestedRef::default()` has a drop method which cannot be safely finalized. + //~^^^^^ ERROR: `HasNestedRef::default()` has a drop method which cannot be safely finalized. + //~^^^^^^ERROR: `HasNestedRef::default()` has a drop method which cannot be safely finalized. +} diff --git a/tests/ui/static/gc/fsa/references_nested.stderr b/tests/ui/static/gc/fsa/references_nested.stderr new file mode 100644 index 0000000000000..4fc7afb37979e --- /dev/null +++ b/tests/ui/static/gc/fsa/references_nested.stderr @@ -0,0 +1,88 @@ +error: `HasNestedRef::default()` has a drop method which cannot be safely finalized. + --> $DIR/references_nested.rs:37:22 + | +LL | use_val(self.a); // should fail + | ------ + | | + | caused by the expression here in `fn drop(&mut)` because + | it is a reference (&u64) which is not safe to use in a finalizer. +... +LL | std::gc::Gc::new(HasNestedRef::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `Gc` may run finalizers after the valid lifetime of this reference. + +error: `HasNestedRef::default()` has a drop method which cannot be safely finalized. + --> $DIR/references_nested.rs:37:22 + | +LL | use_val(self.c.a); // should fail + | -------- + | | + | caused by the expression in `fn drop(&mut)` here because + | it uses a type which is not safe to use in a finalizer. +... +LL | std::gc::Gc::new(HasNestedRef::default()); + | -----------------^^^^^^^^^^^^^^^^^^^^^^^- `Gc::new` requires that HasRef<'_> implements the `FinalizeSafe` trait. + | + = help: `Gc` runs finalizers on a separate thread, so drop methods + must only use values whose types implement `FinalizerSafe`. + +error: `HasNestedRef::default()` has a drop method which cannot be safely finalized. + --> $DIR/references_nested.rs:37:22 + | +LL | use_val(self.c.b); // should pass + | -------- + | | + | caused by the expression in `fn drop(&mut)` here because + | it uses a type which is not safe to use in a finalizer. +... +LL | std::gc::Gc::new(HasNestedRef::default()); + | -----------------^^^^^^^^^^^^^^^^^^^^^^^- `Gc::new` requires that HasRef<'_> implements the `FinalizeSafe` trait. + | + = help: `Gc` runs finalizers on a separate thread, so drop methods + must only use values whose types implement `FinalizerSafe`. + +error: `HasNestedRef::default()` has a drop method which cannot be safely finalized. + --> $DIR/references_nested.rs:37:22 + | +LL | let a = self.a; // should fail + | ------ + | | + | caused by the expression here in `fn drop(&mut)` because + | it is a reference (&u64) which is not safe to use in a finalizer. +... +LL | std::gc::Gc::new(HasNestedRef::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `Gc` may run finalizers after the valid lifetime of this reference. + +error: `HasNestedRef::default()` has a drop method which cannot be safely finalized. + --> $DIR/references_nested.rs:37:22 + | +LL | let ca = self.a; // should fail + | ------ + | | + | caused by the expression here in `fn drop(&mut)` because + | it is a reference (&u64) which is not safe to use in a finalizer. +... +LL | std::gc::Gc::new(HasNestedRef::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `Gc` may run finalizers after the valid lifetime of this reference. + +error: `HasNestedRef::default()` has a drop method which cannot be safely finalized. + --> $DIR/references_nested.rs:37:22 + | +LL | use_val(e.a); + | --- + | | + | caused by the expression here in `fn drop(&mut)` because + | it is a reference (&u64) which is not safe to use in a finalizer. +... +LL | std::gc::Gc::new(HasNestedRef::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `Gc` may run finalizers after the valid lifetime of this reference. + +error: aborting due to 6 previous errors + diff --git a/tests/ui/static/gc/fsa/stdlib_errors.stderr b/tests/ui/static/gc/fsa/stdlib_errors.stderr index 8c3a24db1e0fa..241ce9934a67a 100644 --- a/tests/ui/static/gc/fsa/stdlib_errors.stderr +++ b/tests/ui/static/gc/fsa/stdlib_errors.stderr @@ -18,7 +18,7 @@ LL | println!("Boom {}", self.1.0); // deref `Unsafe` | it uses a type which is not safe to use in a finalizer. ... LL | Gc::new(t); - | --------^- `Gc::new` requires that it implements the `FinalizeSafe` trait. + | --------^- `Gc::new` requires that Unsafe implements the `FinalizeSafe` trait. | = help: `Gc` runs finalizers on a separate thread, so drop methods must only use values whose types implement `FinalizerSafe`.