@@ -137,18 +137,20 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
137137 mut context : PlaceContext ,
138138 location : Location ,
139139 ) {
140- debug ! ( "visit_place(place={:?}, context={:?})" , place, context) ;
140+ let mir:: Place { local, projection } = * place;
141+
142+ self . super_projection ( local, projection, context, location) ;
141143
142144 // Non-uses do not force locals onto the stack.
143145 if !context. is_use ( ) {
144146 return ;
145147 }
146148
147- let mir :: Place { local , projection } = * place ;
149+ let is_consume = is_consume ( context ) ;
148150
149151 // Reads from ZSTs do not require memory accesses and do not count when determining what
150152 // needs to live on the stack.
151- if is_consume ( context ) {
153+ if is_consume {
152154 let ty = place. ty ( self . fx . mir , self . fx . cx . tcx ( ) ) . ty ;
153155 let ty = self . fx . monomorphize ( & ty) ;
154156 let span = self . fx . mir . local_decls [ local] . source_info . span ;
@@ -157,41 +159,53 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
157159 }
158160 }
159161
160- let mut has_disqualifying_projection = false ;
161-
162- let mut projection: & [ _ ] = projection. as_ref ( ) ;
163- while let [ ref proj_base @ .., elem] = * projection {
164- projection = proj_base;
165- self . super_projection_elem ( local, proj_base, elem, context, location) ;
166-
167- // Projections like `(*x)[12]` are allowed but not `*(x[12])`, since a `Deref` of a
168- // local acts like a `Copy` of that local.
169- if let mir:: PlaceElem :: Deref = elem {
170- has_disqualifying_projection = false ;
171- context = PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: Copy ) ;
172- continue ;
173- }
162+ let is_indirect = place. is_indirect ( ) ;
163+ if is_indirect {
164+ context = PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: Copy ) ;
165+ }
174166
175- // Ignoring `Deref`s, the only allowed projections are reads of scalar fields.
176- if is_consume ( context) && matches ! ( elem, mir:: ProjectionElem :: Field ( ..) ) {
177- let base_ty = mir:: Place :: ty_from ( local, proj_base, self . fx . mir , self . fx . cx . tcx ( ) ) ;
178- let base_ty = self . fx . monomorphize ( & base_ty) ;
179- let span = self . fx . mir . local_decls [ local] . source_info . span ;
180- let layout = self . fx . cx . spanned_layout_of ( base_ty. ty , span) ;
167+ self . visit_local ( & local, context, location) ;
181168
182- if !ty_requires_alloca ( self . fx , layout) {
183- continue ;
184- }
169+ // In any context besides a simple read or pointer deref, any projections whatsoever force
170+ // a value onto the stack.
171+ if !is_consume && !is_indirect {
172+ if !projection. is_empty ( ) {
173+ self . not_ssa ( local) ;
185174 }
186175
187- has_disqualifying_projection = true ;
176+ return ;
188177 }
189178
190- if has_disqualifying_projection {
179+ // Only projections inside a `Deref` can disqualify a local from being placed on the stack.
180+ // In other words, `(*x)[idx]` does not disqualify `x` but `*(x[idx])` does.
181+ let first_deref = projection. iter ( ) . position ( |elem| matches ! ( elem, mir:: PlaceElem :: Deref ) ) ;
182+ let projections_on_base_local = & projection[ ..first_deref. unwrap_or ( projection. len ( ) ) ] ;
183+
184+ // Only field projections are allowed. We check this before checking the layout of each
185+ // projection below since computing layouts is relatively expensive.
186+ if !projections_on_base_local. iter ( ) . all ( |elem| matches ! ( elem, mir:: PlaceElem :: Field ( ..) ) ) {
191187 self . not_ssa ( local) ;
188+ return ;
192189 }
193190
194- self . visit_local ( & local, context, location) ;
191+ // Ensure that each field being projected through is handled correctly.
192+ for ( i, elem) in projections_on_base_local. iter ( ) . enumerate ( ) {
193+ assert ! ( matches!( elem, mir:: PlaceElem :: Field ( ..) ) ) ;
194+
195+ // The inclusive range here means we check every projection prefix but the empty one.
196+ // This is okay since the type of each local is checked in `non_ssa_locals`.
197+ let base = & projection[ ..=i] ;
198+
199+ let base_ty = mir:: Place :: ty_from ( local, base, self . fx . mir , self . fx . cx . tcx ( ) ) ;
200+ let base_ty = self . fx . monomorphize ( & base_ty) ;
201+ let span = self . fx . mir . local_decls [ local] . source_info . span ;
202+ let layout = self . fx . cx . spanned_layout_of ( base_ty. ty , span) ;
203+
204+ if ty_requires_alloca ( self . fx , layout) {
205+ self . not_ssa ( local) ;
206+ return ;
207+ }
208+ }
195209 }
196210
197211 fn visit_local ( & mut self , & local: & mir:: Local , context : PlaceContext , location : Location ) {
0 commit comments