@@ -13,6 +13,7 @@ use rustc_hir::def_id::CRATE_DEF_INDEX;
13
13
use rustc_index:: vec:: { Idx , IndexVec } ;
14
14
15
15
use std:: fmt;
16
+ use std:: mem:: ManuallyDrop ;
16
17
use std:: ops:: ControlFlow ;
17
18
use std:: rc:: Rc ;
18
19
use std:: sync:: Arc ;
@@ -732,11 +733,41 @@ EnumTypeFoldableImpl! {
732
733
733
734
impl < ' tcx , T : TypeFoldable < ' tcx > > TypeFoldable < ' tcx > for Rc < T > {
734
735
fn try_super_fold_with < F : FallibleTypeFolder < ' tcx > > (
735
- self ,
736
+ mut self ,
736
737
folder : & mut F ,
737
738
) -> 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
+ }
740
771
}
741
772
742
773
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> {
746
777
747
778
impl < ' tcx , T : TypeFoldable < ' tcx > > TypeFoldable < ' tcx > for Arc < T > {
748
779
fn try_super_fold_with < F : FallibleTypeFolder < ' tcx > > (
749
- self ,
780
+ mut self ,
750
781
folder : & mut F ,
751
782
) -> 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
+ }
754
815
}
755
816
756
817
fn super_visit_with < V : TypeVisitor < ' tcx > > ( & self , visitor : & mut V ) -> ControlFlow < V :: BreakTy > {
0 commit comments