@@ -159,8 +159,7 @@ pub enum StackPopCleanup {
159159pub struct LocalState < ' tcx , Prov : Provenance = AllocId > {
160160 pub value : LocalValue < Prov > ,
161161 /// Don't modify if `Some`, this is only used to prevent computing the layout twice.
162- /// Layout needs to be computed lazily because ConstProp wants to run on frames where we can't
163- /// compute the layout of all locals.
162+ /// Avoids computing the layout of locals that are never actually initialized.
164163 pub layout : Cell < Option < TyAndLayout < ' tcx > > > ,
165164}
166165
@@ -919,15 +918,72 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
919918 ) -> InterpResult < ' tcx > {
920919 trace ! ( "{:?} is now live" , local) ;
921920
922- let layout = self . layout_of_local ( self . frame ( ) , local, None ) ?;
923- let local_val = LocalValue :: Live ( if layout. is_sized ( ) {
924- assert ! ( matches!( meta, MemPlaceMeta :: None ) ) ; // we're dropping the metadata
925- // Just make this an efficient immediate.
926- Operand :: Immediate ( Immediate :: Uninit )
921+ // We avoid `ty.is_trivially_sized` since that (a) cannot assume WF, so it recurses through
922+ // all fields of a tuple, and (b) does something expensive for ADTs.
923+ fn is_very_trivially_sized ( ty : Ty < ' _ > ) -> bool {
924+ match ty. kind ( ) {
925+ ty:: Infer ( ty:: IntVar ( _) | ty:: FloatVar ( _) )
926+ | ty:: Uint ( _)
927+ | ty:: Int ( _)
928+ | ty:: Bool
929+ | ty:: Float ( _)
930+ | ty:: FnDef ( ..)
931+ | ty:: FnPtr ( _)
932+ | ty:: RawPtr ( ..)
933+ | ty:: Char
934+ | ty:: Ref ( ..)
935+ | ty:: Generator ( ..)
936+ | ty:: GeneratorWitness ( ..)
937+ | ty:: GeneratorWitnessMIR ( ..)
938+ | ty:: Array ( ..)
939+ | ty:: Closure ( ..)
940+ | ty:: Never
941+ | ty:: Error ( _) => true ,
942+
943+ ty:: Str | ty:: Slice ( _) | ty:: Dynamic ( ..) | ty:: Foreign ( ..) => false ,
944+
945+ ty:: Tuple ( tys) => tys. last ( ) . iter ( ) . all ( |ty| is_very_trivially_sized ( * * ty) ) ,
946+
947+ // We don't want to do any queries, so there is not much we can do with ADTs.
948+ ty:: Adt ( ..) => false ,
949+
950+ ty:: Alias ( ..) | ty:: Param ( _) | ty:: Placeholder ( ..) => false ,
951+
952+ ty:: Infer ( ty:: TyVar ( _) ) => false ,
953+
954+ ty:: Bound ( ..)
955+ | ty:: Infer ( ty:: FreshTy ( _) | ty:: FreshIntTy ( _) | ty:: FreshFloatTy ( _) ) => {
956+ bug ! ( "`is_very_trivially_sized` applied to unexpected type: {:?}" , ty)
957+ }
958+ }
959+ }
960+
961+ // This is a hot function, we avoid computing the layout when possible.
962+ // `unsized_` will be `None` for sized types and `Some(layout)` for unsized types.
963+ let unsized_ = if is_very_trivially_sized ( self . body ( ) . local_decls [ local] . ty ) {
964+ None
927965 } else {
928- // Need to allocate some memory.
966+ // We need the layout.
967+ let layout = self . layout_of_local ( self . frame ( ) , local, None ) ?;
968+ if layout. is_sized ( ) { None } else { Some ( layout) }
969+ } ;
970+
971+ let local_val = LocalValue :: Live ( if let Some ( layout) = unsized_ {
972+ if !meta. has_meta ( ) {
973+ throw_unsup ! ( UnsizedLocal ) ;
974+ }
975+ // Need to allocate some memory, since `Immediate::Uninit` cannot be unsized.
929976 let dest_place = self . allocate_dyn ( layout, MemoryKind :: Stack , meta) ?;
930977 Operand :: Indirect ( * dest_place)
978+ } else {
979+ assert ! ( !meta. has_meta( ) ) ; // we're dropping the metadata
980+ // Just make this an efficient immediate.
981+ // Note that not calling `layout_of` here does have one real consequence:
982+ // if the type is too big, we'll only notice this when the local is actually initialized,
983+ // which is a bit too late -- we should ideally notice this alreayd here, when the memory
984+ // is conceptually allocated. But given how rare that error is and that this is a hot function,
985+ // we accept this downside for now.
986+ Operand :: Immediate ( Immediate :: Uninit )
931987 } ) ;
932988
933989 // StorageLive expects the local to be dead, and marks it live.
@@ -939,13 +995,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
939995 }
940996
941997 /// Mark a storage as live, killing the previous content.
998+ #[ inline( always) ]
942999 pub fn storage_live ( & mut self , local : mir:: Local ) -> InterpResult < ' tcx > {
943- trace ! ( "{:?} is now live" , local) ;
944-
945- if self . layout_of_local ( self . frame ( ) , local, None ) ?. is_unsized ( ) {
946- throw_unsup ! ( UnsizedLocal ) ;
947- }
948-
9491000 self . storage_live_dyn ( local, MemPlaceMeta :: None )
9501001 }
9511002
0 commit comments