Skip to content

Commit 1796de7

Browse files
committed
Auto merge of #91353 - eggyal:reuse-rcs-during-folding, r=lcnr
Avoid cloning refcounted types during folding Addresses FIXME comment created in #78313 r? `@lcnr`
2 parents 06a6674 + 5920a1d commit 1796de7

File tree

2 files changed

+68
-6
lines changed

2 files changed

+68
-6
lines changed

compiler/rustc_middle/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#![feature(derive_default_enum)]
3434
#![feature(discriminant_kind)]
3535
#![feature(exhaustive_patterns)]
36+
#![feature(get_mut_unchecked)]
3637
#![feature(if_let_guard)]
3738
#![feature(map_first_last)]
3839
#![feature(never_type)]

compiler/rustc_middle/src/ty/structural_impls.rs

+67-6
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use rustc_hir::def_id::CRATE_DEF_INDEX;
1313
use rustc_index::vec::{Idx, IndexVec};
1414

1515
use std::fmt;
16+
use std::mem::ManuallyDrop;
1617
use std::ops::ControlFlow;
1718
use std::rc::Rc;
1819
use std::sync::Arc;
@@ -732,11 +733,41 @@ EnumTypeFoldableImpl! {
732733

733734
impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Rc<T> {
734735
fn try_super_fold_with<F: FallibleTypeFolder<'tcx>>(
735-
self,
736+
mut self,
736737
folder: &mut F,
737738
) -> Result<Self, F::Error> {
738-
// FIXME: Reuse the `Rc` here.
739-
(*self).clone().try_fold_with(folder).map(Rc::new)
739+
// We merely want to replace the contained `T`, if at all possible,
740+
// so that we don't needlessly allocate a new `Rc` or indeed clone
741+
// the contained type.
742+
unsafe {
743+
// First step is to ensure that we have a unique reference to
744+
// the contained type, which `Rc::make_mut` will accomplish (by
745+
// allocating a new `Rc` and cloning the `T` only if required).
746+
// This is done *before* casting to `Rc<ManuallyDrop<T>>` so that
747+
// panicking during `make_mut` does not leak the `T`.
748+
Rc::make_mut(&mut self);
749+
750+
// Casting to `Rc<ManuallyDrop<T>>` is safe because `ManuallyDrop`
751+
// is `repr(transparent)`.
752+
let ptr = Rc::into_raw(self).cast::<ManuallyDrop<T>>();
753+
let mut unique = Rc::from_raw(ptr);
754+
755+
// Call to `Rc::make_mut` above guarantees that `unique` is the
756+
// sole reference to the contained value, so we can avoid doing
757+
// a checked `get_mut` here.
758+
let slot = Rc::get_mut_unchecked(&mut unique);
759+
760+
// Semantically move the contained type out from `unique`, fold
761+
// it, then move the folded value back into `unique`. Should
762+
// folding fail, `ManuallyDrop` ensures that the "moved-out"
763+
// value is not re-dropped.
764+
let owned = ManuallyDrop::take(slot);
765+
let folded = owned.try_fold_with(folder)?;
766+
*slot = ManuallyDrop::new(folded);
767+
768+
// Cast back to `Rc<T>`.
769+
Ok(Rc::from_raw(Rc::into_raw(unique).cast()))
770+
}
740771
}
741772

742773
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
@@ -746,11 +777,41 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Rc<T> {
746777

747778
impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Arc<T> {
748779
fn try_super_fold_with<F: FallibleTypeFolder<'tcx>>(
749-
self,
780+
mut self,
750781
folder: &mut F,
751782
) -> Result<Self, F::Error> {
752-
// FIXME: Reuse the `Arc` here.
753-
(*self).clone().try_fold_with(folder).map(Arc::new)
783+
// We merely want to replace the contained `T`, if at all possible,
784+
// so that we don't needlessly allocate a new `Arc` or indeed clone
785+
// the contained type.
786+
unsafe {
787+
// First step is to ensure that we have a unique reference to
788+
// the contained type, which `Arc::make_mut` will accomplish (by
789+
// allocating a new `Arc` and cloning the `T` only if required).
790+
// This is done *before* casting to `Arc<ManuallyDrop<T>>` so that
791+
// panicking during `make_mut` does not leak the `T`.
792+
Arc::make_mut(&mut self);
793+
794+
// Casting to `Arc<ManuallyDrop<T>>` is safe because `ManuallyDrop`
795+
// is `repr(transparent)`.
796+
let ptr = Arc::into_raw(self).cast::<ManuallyDrop<T>>();
797+
let mut unique = Arc::from_raw(ptr);
798+
799+
// Call to `Arc::make_mut` above guarantees that `unique` is the
800+
// sole reference to the contained value, so we can avoid doing
801+
// a checked `get_mut` here.
802+
let slot = Arc::get_mut_unchecked(&mut unique);
803+
804+
// Semantically move the contained type out from `unique`, fold
805+
// it, then move the folded value back into `unique`. Should
806+
// folding fail, `ManuallyDrop` ensures that the "moved-out"
807+
// value is not re-dropped.
808+
let owned = ManuallyDrop::take(slot);
809+
let folded = owned.try_fold_with(folder)?;
810+
*slot = ManuallyDrop::new(folded);
811+
812+
// Cast back to `Arc<T>`.
813+
Ok(Arc::from_raw(Arc::into_raw(unique).cast()))
814+
}
754815
}
755816

756817
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {

0 commit comments

Comments
 (0)