@@ -137,18 +137,20 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
137
137
mut context : PlaceContext ,
138
138
location : Location ,
139
139
) {
140
- debug ! ( "visit_place(place={:?}, context={:?})" , place, context) ;
140
+ let mir:: Place { local, projection } = * place;
141
+
142
+ self . super_projection ( local, projection, context, location) ;
141
143
142
144
// Non-uses do not force locals onto the stack.
143
145
if !context. is_use ( ) {
144
146
return ;
145
147
}
146
148
147
- let mir :: Place { local , projection } = * place ;
149
+ let is_consume = is_consume ( context ) ;
148
150
149
151
// Reads from ZSTs do not require memory accesses and do not count when determining what
150
152
// needs to live on the stack.
151
- if is_consume ( context ) {
153
+ if is_consume {
152
154
let ty = place. ty ( self . fx . mir , self . fx . cx . tcx ( ) ) . ty ;
153
155
let ty = self . fx . monomorphize ( & ty) ;
154
156
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>
157
159
}
158
160
}
159
161
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
+ }
174
166
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) ;
181
168
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) ;
185
174
}
186
175
187
- has_disqualifying_projection = true ;
176
+ return ;
188
177
}
189
178
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 ( ..) ) ) {
191
187
self . not_ssa ( local) ;
188
+ return ;
192
189
}
193
190
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
+ }
195
209
}
196
210
197
211
fn visit_local ( & mut self , & local: & mir:: Local , context : PlaceContext , location : Location ) {
0 commit comments