@@ -13,6 +13,7 @@ use rustc_hir::def_id::CRATE_DEF_INDEX;
1313use rustc_index:: vec:: { Idx , IndexVec } ;
1414
1515use std:: fmt;
16+ use std:: mem:: ManuallyDrop ;
1617use std:: ops:: ControlFlow ;
1718use std:: rc:: Rc ;
1819use std:: sync:: Arc ;
@@ -732,11 +733,41 @@ EnumTypeFoldableImpl! {
732733
733734impl < ' 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
747778impl < ' 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