55use crate :: fold:: { FallibleTypeFolder , TypeFoldable } ;
66use crate :: visit:: { TypeVisitable , TypeVisitor } ;
77use crate :: { ConstKind , FloatTy , InferTy , IntTy , Interner , UintTy , UniverseIndex } ;
8- use rustc_data_structures:: functor:: IdFunctor ;
98use rustc_data_structures:: sync:: Lrc ;
109use rustc_index:: { Idx , IndexVec } ;
1110
1211use core:: fmt;
1312use std:: marker:: PhantomData ;
13+ use std:: mem;
1414use std:: ops:: ControlFlow ;
1515
1616///////////////////////////////////////////////////////////////////////////
@@ -108,8 +108,39 @@ impl<I: Interner, T: TypeVisitable<I>, E: TypeVisitable<I>> TypeVisitable<I> for
108108}
109109
110110impl < I : Interner , T : TypeFoldable < I > > TypeFoldable < I > for Lrc < T > {
111- fn try_fold_with < F : FallibleTypeFolder < I > > ( self , folder : & mut F ) -> Result < Self , F :: Error > {
112- self . try_map_id ( |value| value. try_fold_with ( folder) )
111+ fn try_fold_with < F : FallibleTypeFolder < I > > ( mut self , folder : & mut F ) -> Result < Self , F :: Error > {
112+ // We merely want to replace the contained `T`, if at all possible,
113+ // so that we don't needlessly allocate a new `Lrc` or indeed clone
114+ // the contained type.
115+ unsafe {
116+ // First step is to ensure that we have a unique reference to
117+ // the contained type, which `Lrc::make_mut` will accomplish (by
118+ // allocating a new `Lrc` and cloning the `T` only if required).
119+ // This is done *before* casting to `Lrc<ManuallyDrop<T>>` so that
120+ // panicking during `make_mut` does not leak the `T`.
121+ Lrc :: make_mut ( & mut self ) ;
122+
123+ // Casting to `Lrc<ManuallyDrop<T>>` is safe because `ManuallyDrop`
124+ // is `repr(transparent)`.
125+ let ptr = Lrc :: into_raw ( self ) . cast :: < mem:: ManuallyDrop < T > > ( ) ;
126+ let mut unique = Lrc :: from_raw ( ptr) ;
127+
128+ // Call to `Lrc::make_mut` above guarantees that `unique` is the
129+ // sole reference to the contained value, so we can avoid doing
130+ // a checked `get_mut` here.
131+ let slot = Lrc :: get_mut_unchecked ( & mut unique) ;
132+
133+ // Semantically move the contained type out from `unique`, fold
134+ // it, then move the folded value back into `unique`. Should
135+ // folding fail, `ManuallyDrop` ensures that the "moved-out"
136+ // value is not re-dropped.
137+ let owned = mem:: ManuallyDrop :: take ( slot) ;
138+ let folded = owned. try_fold_with ( folder) ?;
139+ * slot = mem:: ManuallyDrop :: new ( folded) ;
140+
141+ // Cast back to `Lrc<T>`.
142+ Ok ( Lrc :: from_raw ( Lrc :: into_raw ( unique) . cast ( ) ) )
143+ }
113144 }
114145}
115146
@@ -120,8 +151,9 @@ impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Lrc<T> {
120151}
121152
122153impl < I : Interner , T : TypeFoldable < I > > TypeFoldable < I > for Box < T > {
123- fn try_fold_with < F : FallibleTypeFolder < I > > ( self , folder : & mut F ) -> Result < Self , F :: Error > {
124- self . try_map_id ( |value| value. try_fold_with ( folder) )
154+ fn try_fold_with < F : FallibleTypeFolder < I > > ( mut self , folder : & mut F ) -> Result < Self , F :: Error > {
155+ * self = ( * self ) . try_fold_with ( folder) ?;
156+ Ok ( self )
125157 }
126158}
127159
@@ -133,7 +165,7 @@ impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Box<T> {
133165
134166impl < I : Interner , T : TypeFoldable < I > > TypeFoldable < I > for Vec < T > {
135167 fn try_fold_with < F : FallibleTypeFolder < I > > ( self , folder : & mut F ) -> Result < Self , F :: Error > {
136- self . try_map_id ( |t| t. try_fold_with ( folder) )
168+ self . into_iter ( ) . map ( |t| t. try_fold_with ( folder) ) . collect ( )
137169 }
138170}
139171
@@ -161,7 +193,7 @@ impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Box<[T]> {
161193
162194impl < I : Interner , T : TypeFoldable < I > , Ix : Idx > TypeFoldable < I > for IndexVec < Ix , T > {
163195 fn try_fold_with < F : FallibleTypeFolder < I > > ( self , folder : & mut F ) -> Result < Self , F :: Error > {
164- self . try_map_id ( |x| x . try_fold_with ( folder) )
196+ self . raw . try_fold_with ( folder) . map ( IndexVec :: from_raw )
165197 }
166198}
167199
0 commit comments