]>
Commit | Line | Data |
---|---|---|
9fa01778 | 1 | //! A different sort of visitor for walking fn bodies. Unlike the |
1a4d82fc JJ |
2 | //! normal visitor, which just walks the entire body in one shot, the |
3 | //! `ExprUseVisitor` determines how expressions are being used. | |
4 | ||
1a4d82fc | 5 | pub use self::ConsumeMode::*; |
1a4d82fc | 6 | |
60c5eb7d | 7 | // Export these here so that Clippy can use them. |
6a06907d | 8 | pub use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection}; |
60c5eb7d | 9 | |
6a06907d | 10 | use rustc_data_structures::fx::FxIndexMap; |
dfeec247 XL |
11 | use rustc_hir as hir; |
12 | use rustc_hir::def::Res; | |
f9f354fc | 13 | use rustc_hir::def_id::LocalDefId; |
dfeec247 | 14 | use rustc_hir::PatKind; |
3dfed10e | 15 | use rustc_index::vec::Idx; |
74b04a01 | 16 | use rustc_infer::infer::InferCtxt; |
3dfed10e | 17 | use rustc_middle::hir::place::ProjectionKind; |
6a06907d | 18 | use rustc_middle::mir::FakeReadCause; |
ba9703b0 | 19 | use rustc_middle::ty::{self, adjustment, TyCtxt}; |
3dfed10e | 20 | use rustc_target::abi::VariantIdx; |
60c5eb7d XL |
21 | |
22 | use crate::mem_categorization as mc; | |
1a4d82fc JJ |
23 | |
24 | /////////////////////////////////////////////////////////////////////////// | |
25 | // The Delegate trait | |
26 | ||
27 | /// This trait defines the callbacks you can expect to receive when | |
28 | /// employing the ExprUseVisitor. | |
29 | pub trait Delegate<'tcx> { | |
60c5eb7d | 30 | // The value found at `place` is either copied or moved, depending |
29967ef6 XL |
31 | // on `mode`. Where `diag_expr_id` is the id used for diagnostics for `place`. |
32 | // | |
33 | // The parameter `diag_expr_id` indicates the HIR id that ought to be used for | |
34 | // diagnostics. Around pattern matching such as `let pat = expr`, the diagnostic | |
35 | // id will be the id of the expression `expr` but the place itself will have | |
36 | // the id of the binding in the pattern `pat`. | |
37 | fn consume( | |
38 | &mut self, | |
39 | place_with_id: &PlaceWithHirId<'tcx>, | |
40 | diag_expr_id: hir::HirId, | |
41 | mode: ConsumeMode, | |
42 | ); | |
1a4d82fc | 43 | |
60c5eb7d | 44 | // The value found at `place` is being borrowed with kind `bk`. |
29967ef6 XL |
45 | // `diag_expr_id` is the id used for diagnostics (see `consume` for more details). |
46 | fn borrow( | |
47 | &mut self, | |
48 | place_with_id: &PlaceWithHirId<'tcx>, | |
49 | diag_expr_id: hir::HirId, | |
50 | bk: ty::BorrowKind, | |
51 | ); | |
52 | ||
53 | // The path at `assignee_place` is being assigned to. | |
54 | // `diag_expr_id` is the id used for diagnostics (see `consume` for more details). | |
55 | fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId); | |
6a06907d XL |
56 | |
57 | // The `place` should be a fake read because of specified `cause`. | |
58 | fn fake_read(&mut self, place: Place<'tcx>, cause: FakeReadCause, diag_expr_id: hir::HirId); | |
1a4d82fc JJ |
59 | } |
60 | ||
c34b1796 | 61 | #[derive(Copy, Clone, PartialEq, Debug)] |
1a4d82fc | 62 | pub enum ConsumeMode { |
dfeec247 XL |
63 | Copy, // reference to x where x has a type that copies |
64 | Move, // reference to x where x has a type that moves | |
1a4d82fc JJ |
65 | } |
66 | ||
c34b1796 | 67 | #[derive(Copy, Clone, PartialEq, Debug)] |
1a4d82fc JJ |
68 | pub enum MutateMode { |
69 | Init, | |
70 | JustWrite, // x = y | |
71 | WriteAndRead, // x += y | |
72 | } | |
73 | ||
1a4d82fc JJ |
74 | /////////////////////////////////////////////////////////////////////////// |
75 | // The ExprUseVisitor type | |
76 | // | |
041b39d2 | 77 | // This is the code that actually walks the tree. |
dc9dc135 XL |
78 | pub struct ExprUseVisitor<'a, 'tcx> { |
79 | mc: mc::MemCategorizationContext<'a, 'tcx>, | |
fc512014 | 80 | body_owner: LocalDefId, |
0531ce1d | 81 | delegate: &'a mut dyn Delegate<'tcx>, |
1a4d82fc JJ |
82 | } |
83 | ||
041b39d2 | 84 | // If the MC results in an error, it's because the type check |
1a4d82fc JJ |
85 | // failed (or will fail, when the error is uncovered and reported |
86 | // during writeback). In this case, we just ignore this part of the | |
87 | // code. | |
88 | // | |
89 | // Note that this macro appears similar to try!(), but, unlike try!(), | |
90 | // it does not propagate the error. | |
91 | macro_rules! return_if_err { | |
dfeec247 | 92 | ($inp: expr) => { |
1a4d82fc JJ |
93 | match $inp { |
94 | Ok(v) => v, | |
c1a9b12d SL |
95 | Err(()) => { |
96 | debug!("mc reported err"); | |
dfeec247 | 97 | return; |
c1a9b12d | 98 | } |
1a4d82fc | 99 | } |
dfeec247 | 100 | }; |
1a4d82fc JJ |
101 | } |
102 | ||
dc9dc135 | 103 | impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { |
abe05a73 XL |
104 | /// Creates the ExprUseVisitor, configuring it with the various options provided: |
105 | /// | |
106 | /// - `delegate` -- who receives the callbacks | |
107 | /// - `param_env` --- parameter environment for trait lookups (esp. pertaining to `Copy`) | |
3dfed10e | 108 | /// - `typeck_results` --- typeck results for the code being analyzed |
dc9dc135 | 109 | pub fn new( |
dc9dc135 XL |
110 | delegate: &'a mut (dyn Delegate<'tcx> + 'a), |
111 | infcx: &'a InferCtxt<'a, 'tcx>, | |
f9f354fc | 112 | body_owner: LocalDefId, |
dc9dc135 | 113 | param_env: ty::ParamEnv<'tcx>, |
3dfed10e | 114 | typeck_results: &'a ty::TypeckResults<'tcx>, |
dc9dc135 | 115 | ) -> Self { |
a7813a04 | 116 | ExprUseVisitor { |
3dfed10e | 117 | mc: mc::MemCategorizationContext::new(infcx, param_env, body_owner, typeck_results), |
fc512014 | 118 | body_owner, |
7cac9316 | 119 | delegate, |
a7813a04 | 120 | } |
1a4d82fc JJ |
121 | } |
122 | ||
dfeec247 | 123 | pub fn consume_body(&mut self, body: &hir::Body<'_>) { |
cc61c64b XL |
124 | debug!("consume_body(body={:?})", body); |
125 | ||
dfeec247 | 126 | for param in body.params { |
e1599b0c XL |
127 | let param_ty = return_if_err!(self.mc.pat_ty_adjusted(¶m.pat)); |
128 | debug!("consume_body: param_ty = {:?}", param_ty); | |
1a4d82fc | 129 | |
60c5eb7d | 130 | let param_place = self.mc.cat_rvalue(param.hir_id, param.pat.span, param_ty); |
1a4d82fc | 131 | |
60c5eb7d | 132 | self.walk_irrefutable_pat(¶m_place, ¶m.pat); |
1a4d82fc | 133 | } |
32a655c1 SL |
134 | |
135 | self.consume_expr(&body.value); | |
1a4d82fc JJ |
136 | } |
137 | ||
dc9dc135 | 138 | fn tcx(&self) -> TyCtxt<'tcx> { |
60c5eb7d | 139 | self.mc.tcx() |
1a4d82fc JJ |
140 | } |
141 | ||
29967ef6 | 142 | fn delegate_consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) { |
f035d41b | 143 | debug!("delegate_consume(place_with_id={:?})", place_with_id); |
85aaf69f | 144 | |
f035d41b | 145 | let mode = copy_or_move(&self.mc, place_with_id); |
29967ef6 | 146 | self.delegate.consume(place_with_id, diag_expr_id, mode); |
1a4d82fc JJ |
147 | } |
148 | ||
dfeec247 | 149 | fn consume_exprs(&mut self, exprs: &[hir::Expr<'_>]) { |
85aaf69f | 150 | for expr in exprs { |
7453a54e | 151 | self.consume_expr(&expr); |
1a4d82fc JJ |
152 | } |
153 | } | |
154 | ||
dfeec247 | 155 | pub fn consume_expr(&mut self, expr: &hir::Expr<'_>) { |
62682a34 | 156 | debug!("consume_expr(expr={:?})", expr); |
1a4d82fc | 157 | |
f035d41b | 158 | let place_with_id = return_if_err!(self.mc.cat_expr(expr)); |
29967ef6 | 159 | self.delegate_consume(&place_with_id, place_with_id.hir_id); |
1a4d82fc JJ |
160 | self.walk_expr(expr); |
161 | } | |
162 | ||
dfeec247 | 163 | fn mutate_expr(&mut self, expr: &hir::Expr<'_>) { |
f035d41b | 164 | let place_with_id = return_if_err!(self.mc.cat_expr(expr)); |
29967ef6 | 165 | self.delegate.mutate(&place_with_id, place_with_id.hir_id); |
1a4d82fc JJ |
166 | self.walk_expr(expr); |
167 | } | |
168 | ||
dfeec247 | 169 | fn borrow_expr(&mut self, expr: &hir::Expr<'_>, bk: ty::BorrowKind) { |
e74abb32 | 170 | debug!("borrow_expr(expr={:?}, bk={:?})", expr, bk); |
1a4d82fc | 171 | |
f035d41b | 172 | let place_with_id = return_if_err!(self.mc.cat_expr(expr)); |
29967ef6 | 173 | self.delegate.borrow(&place_with_id, place_with_id.hir_id, bk); |
1a4d82fc | 174 | |
1a4d82fc JJ |
175 | self.walk_expr(expr) |
176 | } | |
177 | ||
dfeec247 | 178 | fn select_from_expr(&mut self, expr: &hir::Expr<'_>) { |
1a4d82fc JJ |
179 | self.walk_expr(expr) |
180 | } | |
181 | ||
dfeec247 | 182 | pub fn walk_expr(&mut self, expr: &hir::Expr<'_>) { |
62682a34 | 183 | debug!("walk_expr(expr={:?})", expr); |
1a4d82fc JJ |
184 | |
185 | self.walk_adjustment(expr); | |
186 | ||
e74abb32 | 187 | match expr.kind { |
dfeec247 | 188 | hir::ExprKind::Path(_) => {} |
1a4d82fc | 189 | |
dfeec247 | 190 | hir::ExprKind::Type(ref subexpr, _) => self.walk_expr(subexpr), |
9cc50fc6 | 191 | |
6a06907d | 192 | hir::ExprKind::Unary(hir::UnOp::Deref, ref base) => { |
dfeec247 | 193 | // *base |
e74abb32 | 194 | self.select_from_expr(base); |
1a4d82fc JJ |
195 | } |
196 | ||
dfeec247 XL |
197 | hir::ExprKind::Field(ref base, _) => { |
198 | // base.f | |
e74abb32 | 199 | self.select_from_expr(base); |
1a4d82fc JJ |
200 | } |
201 | ||
dfeec247 XL |
202 | hir::ExprKind::Index(ref lhs, ref rhs) => { |
203 | // lhs[rhs] | |
e74abb32 XL |
204 | self.select_from_expr(lhs); |
205 | self.consume_expr(rhs); | |
1a4d82fc JJ |
206 | } |
207 | ||
dfeec247 XL |
208 | hir::ExprKind::Call(ref callee, ref args) => { |
209 | // callee(args) | |
210 | self.consume_expr(callee); | |
1a4d82fc JJ |
211 | self.consume_exprs(args); |
212 | } | |
213 | ||
f035d41b | 214 | hir::ExprKind::MethodCall(.., ref args, _) => { |
dfeec247 | 215 | // callee.m(args) |
1a4d82fc JJ |
216 | self.consume_exprs(args); |
217 | } | |
218 | ||
8faf50e0 | 219 | hir::ExprKind::Struct(_, ref fields, ref opt_with) => { |
9e0c209e | 220 | self.walk_struct_expr(fields, opt_with); |
1a4d82fc JJ |
221 | } |
222 | ||
8faf50e0 | 223 | hir::ExprKind::Tup(ref exprs) => { |
1a4d82fc JJ |
224 | self.consume_exprs(exprs); |
225 | } | |
226 | ||
5869c6ff XL |
227 | hir::ExprKind::If(ref cond_expr, ref then_expr, ref opt_else_expr) => { |
228 | self.consume_expr(&cond_expr); | |
229 | self.consume_expr(&then_expr); | |
230 | if let Some(ref else_expr) = *opt_else_expr { | |
231 | self.consume_expr(&else_expr); | |
232 | } | |
233 | } | |
234 | ||
dfeec247 | 235 | hir::ExprKind::Match(ref discr, arms, _) => { |
60c5eb7d | 236 | let discr_place = return_if_err!(self.mc.cat_expr(&discr)); |
6a06907d XL |
237 | |
238 | // Matching should not always be considered a use of the place, hence | |
239 | // discr does not necessarily need to be borrowed. | |
240 | // We only want to borrow discr if the pattern contain something other | |
241 | // than wildcards. | |
242 | let ExprUseVisitor { ref mc, body_owner: _, delegate: _ } = *self; | |
243 | let mut needs_to_be_read = false; | |
244 | for arm in arms.iter() { | |
245 | return_if_err!(mc.cat_pattern(discr_place.clone(), &arm.pat, |place, pat| { | |
246 | match &pat.kind { | |
247 | PatKind::Binding(.., opt_sub_pat) => { | |
248 | // If the opt_sub_pat is None, than the binding does not count as | |
249 | // a wildcard for the purpose of borrowing discr. | |
250 | if opt_sub_pat.is_none() { | |
251 | needs_to_be_read = true; | |
252 | } | |
253 | } | |
254 | PatKind::TupleStruct(..) | |
255 | | PatKind::Path(..) | |
256 | | PatKind::Struct(..) | |
257 | | PatKind::Tuple(..) => { | |
258 | // If the PatKind is a TupleStruct, Struct or Tuple then we want to check | |
259 | // whether the Variant is a MultiVariant or a SingleVariant. We only want | |
260 | // to borrow discr if it is a MultiVariant. | |
261 | // If it is a SingleVariant and creates a binding we will handle that when | |
262 | // this callback gets called again. | |
263 | if let ty::Adt(def, _) = place.place.base_ty.kind() { | |
264 | if def.variants.len() > 1 { | |
265 | needs_to_be_read = true; | |
266 | } | |
267 | } | |
268 | } | |
269 | PatKind::Lit(_) => { | |
270 | // If the PatKind is a Lit then we want | |
271 | // to borrow discr. | |
272 | needs_to_be_read = true; | |
273 | } | |
274 | _ => {} | |
275 | } | |
276 | })); | |
277 | } | |
278 | ||
279 | if needs_to_be_read { | |
280 | self.borrow_expr(&discr, ty::ImmBorrow); | |
281 | } else { | |
282 | self.delegate.fake_read( | |
283 | discr_place.place.clone(), | |
284 | FakeReadCause::ForMatchedPlace, | |
285 | discr_place.hir_id, | |
286 | ); | |
287 | ||
288 | // We always want to walk the discriminant. We want to make sure, for instance, | |
289 | // that the discriminant has been initialized. | |
290 | self.walk_expr(&discr); | |
291 | } | |
1a4d82fc JJ |
292 | |
293 | // treatment of the discriminant is handled while walking the arms. | |
85aaf69f | 294 | for arm in arms { |
60c5eb7d | 295 | self.walk_arm(&discr_place, arm); |
1a4d82fc JJ |
296 | } |
297 | } | |
298 | ||
8faf50e0 | 299 | hir::ExprKind::Array(ref exprs) => { |
1a4d82fc JJ |
300 | self.consume_exprs(exprs); |
301 | } | |
302 | ||
dfeec247 XL |
303 | hir::ExprKind::AddrOf(_, m, ref base) => { |
304 | // &base | |
1a4d82fc JJ |
305 | // make sure that the thing we are pointing out stays valid |
306 | // for the lifetime `scope_r` of the resulting ptr: | |
e74abb32 XL |
307 | let bk = ty::BorrowKind::from_mutbl(m); |
308 | self.borrow_expr(&base, bk); | |
1a4d82fc JJ |
309 | } |
310 | ||
f9f354fc | 311 | hir::ExprKind::InlineAsm(ref asm) => { |
fc512014 | 312 | for (op, _op_sp) in asm.operands { |
f9f354fc XL |
313 | match op { |
314 | hir::InlineAsmOperand::In { expr, .. } | |
315 | | hir::InlineAsmOperand::Const { expr, .. } | |
316 | | hir::InlineAsmOperand::Sym { expr, .. } => self.consume_expr(expr), | |
317 | hir::InlineAsmOperand::Out { expr, .. } => { | |
318 | if let Some(expr) = expr { | |
319 | self.mutate_expr(expr); | |
320 | } | |
321 | } | |
322 | hir::InlineAsmOperand::InOut { expr, .. } => { | |
323 | self.mutate_expr(expr); | |
324 | } | |
325 | hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { | |
326 | self.consume_expr(in_expr); | |
327 | if let Some(out_expr) = out_expr { | |
328 | self.mutate_expr(out_expr); | |
329 | } | |
330 | } | |
331 | } | |
332 | } | |
333 | } | |
334 | ||
ba9703b0 | 335 | hir::ExprKind::LlvmInlineAsm(ref ia) => { |
dfeec247 | 336 | for (o, output) in ia.inner.outputs.iter().zip(ia.outputs_exprs) { |
54a0048b SL |
337 | if o.is_indirect { |
338 | self.consume_expr(output); | |
9cc50fc6 | 339 | } else { |
e74abb32 | 340 | self.mutate_expr(output); |
9cc50fc6 | 341 | } |
1a4d82fc | 342 | } |
60c5eb7d | 343 | self.consume_exprs(&ia.inputs_exprs); |
1a4d82fc JJ |
344 | } |
345 | ||
29967ef6 XL |
346 | hir::ExprKind::Continue(..) |
347 | | hir::ExprKind::Lit(..) | |
348 | | hir::ExprKind::ConstBlock(..) | |
349 | | hir::ExprKind::Err => {} | |
1a4d82fc | 350 | |
5869c6ff | 351 | hir::ExprKind::Loop(ref blk, ..) => { |
e74abb32 | 352 | self.walk_block(blk); |
1a4d82fc JJ |
353 | } |
354 | ||
8faf50e0 | 355 | hir::ExprKind::Unary(_, ref lhs) => { |
e74abb32 | 356 | self.consume_expr(lhs); |
1a4d82fc JJ |
357 | } |
358 | ||
8faf50e0 | 359 | hir::ExprKind::Binary(_, ref lhs, ref rhs) => { |
e74abb32 XL |
360 | self.consume_expr(lhs); |
361 | self.consume_expr(rhs); | |
1a4d82fc JJ |
362 | } |
363 | ||
8faf50e0 | 364 | hir::ExprKind::Block(ref blk, _) => { |
e74abb32 | 365 | self.walk_block(blk); |
1a4d82fc JJ |
366 | } |
367 | ||
8faf50e0 | 368 | hir::ExprKind::Break(_, ref opt_expr) | hir::ExprKind::Ret(ref opt_expr) => { |
85aaf69f | 369 | if let Some(ref expr) = *opt_expr { |
e74abb32 | 370 | self.consume_expr(expr); |
1a4d82fc JJ |
371 | } |
372 | } | |
373 | ||
dfeec247 | 374 | hir::ExprKind::Assign(ref lhs, ref rhs, _) => { |
e74abb32 XL |
375 | self.mutate_expr(lhs); |
376 | self.consume_expr(rhs); | |
1a4d82fc JJ |
377 | } |
378 | ||
8faf50e0 | 379 | hir::ExprKind::Cast(ref base, _) => { |
e74abb32 | 380 | self.consume_expr(base); |
1a4d82fc JJ |
381 | } |
382 | ||
48663c56 | 383 | hir::ExprKind::DropTemps(ref expr) => { |
e74abb32 | 384 | self.consume_expr(expr); |
48663c56 XL |
385 | } |
386 | ||
8faf50e0 | 387 | hir::ExprKind::AssignOp(_, ref lhs, ref rhs) => { |
3dfed10e | 388 | if self.mc.typeck_results.is_method_call(expr) { |
7cac9316 XL |
389 | self.consume_expr(lhs); |
390 | } else { | |
e74abb32 | 391 | self.mutate_expr(lhs); |
b039eaaf | 392 | } |
e74abb32 | 393 | self.consume_expr(rhs); |
1a4d82fc JJ |
394 | } |
395 | ||
8faf50e0 | 396 | hir::ExprKind::Repeat(ref base, _) => { |
e74abb32 | 397 | self.consume_expr(base); |
1a4d82fc JJ |
398 | } |
399 | ||
fc512014 XL |
400 | hir::ExprKind::Closure(..) => { |
401 | self.walk_captures(expr); | |
1a4d82fc JJ |
402 | } |
403 | ||
8faf50e0 | 404 | hir::ExprKind::Box(ref base) => { |
e74abb32 | 405 | self.consume_expr(base); |
1a4d82fc | 406 | } |
ea8adc8c | 407 | |
dc9dc135 | 408 | hir::ExprKind::Yield(ref value, _) => { |
e74abb32 | 409 | self.consume_expr(value); |
ea8adc8c | 410 | } |
1a4d82fc JJ |
411 | } |
412 | } | |
413 | ||
dfeec247 | 414 | fn walk_stmt(&mut self, stmt: &hir::Stmt<'_>) { |
e74abb32 | 415 | match stmt.kind { |
9fa01778 XL |
416 | hir::StmtKind::Local(ref local) => { |
417 | self.walk_local(&local); | |
418 | } | |
1a4d82fc | 419 | |
9fa01778 | 420 | hir::StmtKind::Item(_) => { |
e1599b0c | 421 | // We don't visit nested items in this visitor, |
9fa01778 | 422 | // only the fn body we were given. |
1a4d82fc JJ |
423 | } |
424 | ||
dfeec247 | 425 | hir::StmtKind::Expr(ref expr) | hir::StmtKind::Semi(ref expr) => { |
7453a54e | 426 | self.consume_expr(&expr); |
1a4d82fc | 427 | } |
1a4d82fc JJ |
428 | } |
429 | } | |
430 | ||
dfeec247 | 431 | fn walk_local(&mut self, local: &hir::Local<'_>) { |
e74abb32 XL |
432 | if let Some(ref expr) = local.init { |
433 | // Variable declarations with | |
434 | // initializers are considered | |
435 | // "assigns", which is handled by | |
436 | // `walk_pat`: | |
437 | self.walk_expr(&expr); | |
60c5eb7d XL |
438 | let init_place = return_if_err!(self.mc.cat_expr(&expr)); |
439 | self.walk_irrefutable_pat(&init_place, &local.pat); | |
1a4d82fc JJ |
440 | } |
441 | } | |
442 | ||
443 | /// Indicates that the value of `blk` will be consumed, meaning either copied or moved | |
444 | /// depending on its type. | |
dfeec247 | 445 | fn walk_block(&mut self, blk: &hir::Block<'_>) { |
532ac7d7 | 446 | debug!("walk_block(blk.hir_id={})", blk.hir_id); |
1a4d82fc | 447 | |
dfeec247 | 448 | for stmt in blk.stmts { |
92a42be0 | 449 | self.walk_stmt(stmt); |
1a4d82fc JJ |
450 | } |
451 | ||
85aaf69f | 452 | if let Some(ref tail_expr) = blk.expr { |
7453a54e | 453 | self.consume_expr(&tail_expr); |
1a4d82fc JJ |
454 | } |
455 | } | |
456 | ||
dfeec247 XL |
457 | fn walk_struct_expr( |
458 | &mut self, | |
6a06907d | 459 | fields: &[hir::ExprField<'_>], |
dfeec247 XL |
460 | opt_with: &Option<&'hir hir::Expr<'_>>, |
461 | ) { | |
1a4d82fc | 462 | // Consume the expressions supplying values for each field. |
85aaf69f | 463 | for field in fields { |
7453a54e | 464 | self.consume_expr(&field.expr); |
1a4d82fc JJ |
465 | } |
466 | ||
467 | let with_expr = match *opt_with { | |
468 | Some(ref w) => &**w, | |
dfeec247 XL |
469 | None => { |
470 | return; | |
471 | } | |
1a4d82fc JJ |
472 | }; |
473 | ||
60c5eb7d | 474 | let with_place = return_if_err!(self.mc.cat_expr(&with_expr)); |
1a4d82fc JJ |
475 | |
476 | // Select just those fields of the `with` | |
477 | // expression that will actually be used | |
1b1a35ee | 478 | match with_place.place.ty().kind() { |
b7449926 | 479 | ty::Adt(adt, substs) if adt.is_struct() => { |
9e0c209e | 480 | // Consume those fields of the with expression that are needed. |
83c7162d | 481 | for (f_index, with_field) in adt.non_enum_variant().fields.iter().enumerate() { |
3dfed10e XL |
482 | let is_mentioned = fields.iter().any(|f| { |
483 | self.tcx().field_index(f.hir_id, self.mc.typeck_results) == f_index | |
484 | }); | |
83c7162d | 485 | if !is_mentioned { |
60c5eb7d | 486 | let field_place = self.mc.cat_projection( |
9e0c209e | 487 | &*with_expr, |
60c5eb7d XL |
488 | with_place.clone(), |
489 | with_field.ty(self.tcx(), substs), | |
3dfed10e | 490 | ProjectionKind::Field(f_index as u32, VariantIdx::new(0)), |
9e0c209e | 491 | ); |
29967ef6 | 492 | self.delegate_consume(&field_place, field_place.hir_id); |
9e0c209e | 493 | } |
1a4d82fc | 494 | } |
1a4d82fc | 495 | } |
9e0c209e SL |
496 | _ => { |
497 | // the base expression should always evaluate to a | |
498 | // struct; however, when EUV is run during typeck, it | |
499 | // may not. This will generate an error earlier in typeck, | |
500 | // so we can just ignore it. | |
501 | if !self.tcx().sess.has_errors() { | |
dfeec247 | 502 | span_bug!(with_expr.span, "with expression doesn't evaluate to a struct"); |
9e0c209e | 503 | } |
1a4d82fc | 504 | } |
9e0c209e | 505 | } |
1a4d82fc JJ |
506 | |
507 | // walk the with expression so that complex expressions | |
508 | // are properly handled. | |
509 | self.walk_expr(with_expr); | |
1a4d82fc JJ |
510 | } |
511 | ||
512 | // Invoke the appropriate delegate calls for anything that gets | |
513 | // consumed or borrowed as part of the automatic adjustment | |
514 | // process. | |
dfeec247 | 515 | fn walk_adjustment(&mut self, expr: &hir::Expr<'_>) { |
3dfed10e | 516 | let adjustments = self.mc.typeck_results.expr_adjustments(expr); |
f035d41b | 517 | let mut place_with_id = return_if_err!(self.mc.cat_expr_unadjusted(expr)); |
7cac9316 XL |
518 | for adjustment in adjustments { |
519 | debug!("walk_adjustment expr={:?} adj={:?}", expr, adjustment); | |
c30ab7b3 | 520 | match adjustment.kind { |
dfeec247 | 521 | adjustment::Adjust::NeverToAny | adjustment::Adjust::Pointer(_) => { |
9346a6ac AL |
522 | // Creating a closure/fn-pointer or unsizing consumes |
523 | // the input and stores it into the resulting rvalue. | |
29967ef6 | 524 | self.delegate_consume(&place_with_id, place_with_id.hir_id); |
9346a6ac | 525 | } |
c30ab7b3 | 526 | |
7cac9316 XL |
527 | adjustment::Adjust::Deref(None) => {} |
528 | ||
529 | // Autoderefs for overloaded Deref calls in fact reference | |
530 | // their receiver. That is, if we have `(*x)` where `x` | |
531 | // is of type `Rc<T>`, then this in fact is equivalent to | |
532 | // `x.deref()`. Since `deref()` is declared with `&self`, | |
533 | // this is an autoref of `x`. | |
534 | adjustment::Adjust::Deref(Some(ref deref)) => { | |
535 | let bk = ty::BorrowKind::from_mutbl(deref.mutbl); | |
29967ef6 | 536 | self.delegate.borrow(&place_with_id, place_with_id.hir_id, bk); |
1a4d82fc | 537 | } |
1a4d82fc | 538 | |
7cac9316 | 539 | adjustment::Adjust::Borrow(ref autoref) => { |
f035d41b | 540 | self.walk_autoref(expr, &place_with_id, autoref); |
7cac9316 | 541 | } |
1a4d82fc | 542 | } |
f035d41b XL |
543 | place_with_id = |
544 | return_if_err!(self.mc.cat_expr_adjusted(expr, place_with_id, &adjustment)); | |
1a4d82fc JJ |
545 | } |
546 | } | |
547 | ||
7cac9316 | 548 | /// Walks the autoref `autoref` applied to the autoderef'd |
60c5eb7d | 549 | /// `expr`. `base_place` is the mem-categorized form of `expr` |
7cac9316 | 550 | /// after all relevant autoderefs have occurred. |
dfeec247 XL |
551 | fn walk_autoref( |
552 | &mut self, | |
553 | expr: &hir::Expr<'_>, | |
3dfed10e | 554 | base_place: &PlaceWithHirId<'tcx>, |
dfeec247 XL |
555 | autoref: &adjustment::AutoBorrow<'tcx>, |
556 | ) { | |
557 | debug!( | |
558 | "walk_autoref(expr.hir_id={} base_place={:?} autoref={:?})", | |
559 | expr.hir_id, base_place, autoref | |
560 | ); | |
1a4d82fc | 561 | |
9346a6ac | 562 | match *autoref { |
e74abb32 | 563 | adjustment::AutoBorrow::Ref(_, m) => { |
29967ef6 XL |
564 | self.delegate.borrow( |
565 | base_place, | |
566 | base_place.hir_id, | |
567 | ty::BorrowKind::from_mutbl(m.into()), | |
568 | ); | |
1a4d82fc | 569 | } |
9346a6ac | 570 | |
c30ab7b3 | 571 | adjustment::AutoBorrow::RawPtr(m) => { |
dfeec247 | 572 | debug!("walk_autoref: expr.hir_id={} base_place={:?}", expr.hir_id, base_place); |
1a4d82fc | 573 | |
29967ef6 | 574 | self.delegate.borrow(base_place, base_place.hir_id, ty::BorrowKind::from_mutbl(m)); |
e74abb32 | 575 | } |
1a4d82fc | 576 | } |
1a4d82fc JJ |
577 | } |
578 | ||
f035d41b | 579 | fn walk_arm(&mut self, discr_place: &PlaceWithHirId<'tcx>, arm: &hir::Arm<'_>) { |
6a06907d XL |
580 | self.delegate.fake_read( |
581 | discr_place.place.clone(), | |
582 | FakeReadCause::ForMatchedPlace, | |
583 | discr_place.hir_id, | |
584 | ); | |
60c5eb7d | 585 | self.walk_pat(discr_place, &arm.pat); |
1a4d82fc | 586 | |
0bf4aa26 XL |
587 | if let Some(hir::Guard::If(ref e)) = arm.guard { |
588 | self.consume_expr(e) | |
1a4d82fc JJ |
589 | } |
590 | ||
7453a54e | 591 | self.consume_expr(&arm.body); |
1a4d82fc JJ |
592 | } |
593 | ||
9fa01778 XL |
594 | /// Walks a pat that occurs in isolation (i.e., top-level of fn argument or |
595 | /// let binding, and *not* a match arm or nested pat.) | |
f035d41b | 596 | fn walk_irrefutable_pat(&mut self, discr_place: &PlaceWithHirId<'tcx>, pat: &hir::Pat<'_>) { |
6a06907d XL |
597 | self.delegate.fake_read( |
598 | discr_place.place.clone(), | |
599 | FakeReadCause::ForLet, | |
600 | discr_place.hir_id, | |
601 | ); | |
60c5eb7d | 602 | self.walk_pat(discr_place, pat); |
1a4d82fc JJ |
603 | } |
604 | ||
e74abb32 | 605 | /// The core driver for walking a pattern |
f035d41b | 606 | fn walk_pat(&mut self, discr_place: &PlaceWithHirId<'tcx>, pat: &hir::Pat<'_>) { |
60c5eb7d | 607 | debug!("walk_pat(discr_place={:?}, pat={:?})", discr_place, pat); |
1a4d82fc | 608 | |
8faf50e0 | 609 | let tcx = self.tcx(); |
fc512014 | 610 | let ExprUseVisitor { ref mc, body_owner: _, ref mut delegate } = *self; |
60c5eb7d | 611 | return_if_err!(mc.cat_pattern(discr_place.clone(), pat, |place, pat| { |
e74abb32 | 612 | if let PatKind::Binding(_, canonical_id, ..) = pat.kind { |
dfeec247 | 613 | debug!("walk_pat: binding place={:?} pat={:?}", place, pat,); |
3dfed10e XL |
614 | if let Some(bm) = |
615 | mc.typeck_results.extract_binding_mode(tcx.sess, pat.hir_id, pat.span) | |
616 | { | |
8faf50e0 XL |
617 | debug!("walk_pat: pat.hir_id={:?} bm={:?}", pat.hir_id, bm); |
618 | ||
619 | // pat_ty: the type of the binding being produced. | |
620 | let pat_ty = return_if_err!(mc.node_ty(pat.hir_id)); | |
621 | debug!("walk_pat: pat_ty={:?}", pat_ty); | |
622 | ||
623 | // Each match binding is effectively an assignment to the | |
624 | // binding being produced. | |
48663c56 | 625 | let def = Res::Local(canonical_id); |
60c5eb7d | 626 | if let Ok(ref binding_place) = mc.cat_res(pat.hir_id, pat.span, pat_ty, def) { |
29967ef6 | 627 | delegate.mutate(binding_place, binding_place.hir_id); |
8faf50e0 | 628 | } |
5bcae85e | 629 | |
8faf50e0 | 630 | // It is also a borrow or copy/move of the value being matched. |
29967ef6 XL |
631 | // In a cases of pattern like `let pat = upvar`, don't use the span |
632 | // of the pattern, as this just looks confusing, instead use the span | |
633 | // of the discriminant. | |
8faf50e0 XL |
634 | match bm { |
635 | ty::BindByReference(m) => { | |
e74abb32 | 636 | let bk = ty::BorrowKind::from_mutbl(m); |
29967ef6 | 637 | delegate.borrow(place, discr_place.hir_id, bk); |
8faf50e0 XL |
638 | } |
639 | ty::BindByValue(..) => { | |
29967ef6 | 640 | let mode = copy_or_move(mc, &place); |
8faf50e0 | 641 | debug!("walk_pat binding consuming pat"); |
29967ef6 | 642 | delegate.consume(place, discr_place.hir_id, mode); |
c1a9b12d | 643 | } |
1a4d82fc | 644 | } |
1a4d82fc JJ |
645 | } |
646 | } | |
647 | })); | |
1a4d82fc JJ |
648 | } |
649 | ||
fc512014 XL |
650 | /// Handle the case where the current body contains a closure. |
651 | /// | |
652 | /// When the current body being handled is a closure, then we must make sure that | |
653 | /// - The parent closure only captures Places from the nested closure that are not local to it. | |
654 | /// | |
655 | /// In the following example the closures `c` only captures `p.x`` even though `incr` | |
656 | /// is a capture of the nested closure | |
657 | /// | |
658 | /// ```rust,ignore(cannot-test-this-because-pseduo-code) | |
659 | /// let p = ..; | |
660 | /// let c = || { | |
661 | /// let incr = 10; | |
662 | /// let nested = || p.x += incr; | |
663 | /// } | |
664 | /// ``` | |
665 | /// | |
666 | /// - When reporting the Place back to the Delegate, ensure that the UpvarId uses the enclosing | |
667 | /// closure as the DefId. | |
668 | fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>) { | |
6a06907d XL |
669 | fn upvar_is_local_variable( |
670 | upvars: Option<&'tcx FxIndexMap<hir::HirId, hir::Upvar>>, | |
671 | upvar_id: &hir::HirId, | |
672 | body_owner_is_closure: bool, | |
673 | ) -> bool { | |
674 | upvars.map(|upvars| !upvars.contains_key(upvar_id)).unwrap_or(body_owner_is_closure) | |
675 | } | |
676 | ||
62682a34 | 677 | debug!("walk_captures({:?})", closure_expr); |
1a4d82fc | 678 | |
fc512014 XL |
679 | let closure_def_id = self.tcx().hir().local_def_id(closure_expr.hir_id).to_def_id(); |
680 | let upvars = self.tcx().upvars_mentioned(self.body_owner); | |
681 | ||
682 | // For purposes of this function, generator and closures are equivalent. | |
5869c6ff XL |
683 | let body_owner_is_closure = matches!( |
684 | self.tcx().type_of(self.body_owner.to_def_id()).kind(), | |
685 | ty::Closure(..) | ty::Generator(..) | |
686 | ); | |
fc512014 | 687 | |
6a06907d XL |
688 | // If we have a nested closure, we want to include the fake reads present in the nested closure. |
689 | if let Some(fake_reads) = self.mc.typeck_results.closure_fake_reads.get(&closure_def_id) { | |
690 | for (fake_read, cause, hir_id) in fake_reads.iter() { | |
691 | match fake_read.base { | |
692 | PlaceBase::Upvar(upvar_id) => { | |
693 | if upvar_is_local_variable( | |
694 | upvars, | |
695 | &upvar_id.var_path.hir_id, | |
696 | body_owner_is_closure, | |
697 | ) { | |
698 | // The nested closure might be fake reading the current (enclosing) closure's local variables. | |
699 | // The only places we want to fake read before creating the parent closure are the ones that | |
700 | // are not local to it/ defined by it. | |
701 | // | |
702 | // ```rust,ignore(cannot-test-this-because-pseduo-code) | |
703 | // let v1 = (0, 1); | |
704 | // let c = || { // fake reads: v1 | |
705 | // let v2 = (0, 1); | |
706 | // let e = || { // fake reads: v1, v2 | |
707 | // let (_, t1) = v1; | |
708 | // let (_, t2) = v2; | |
709 | // } | |
710 | // } | |
711 | // ``` | |
712 | // This check is performed when visiting the body of the outermost closure (`c`) and ensures | |
713 | // that we don't add a fake read of v2 in c. | |
714 | continue; | |
715 | } | |
716 | } | |
717 | _ => { | |
718 | bug!( | |
719 | "Do not know how to get HirId out of Rvalue and StaticItem {:?}", | |
720 | fake_read.base | |
721 | ); | |
722 | } | |
723 | }; | |
724 | self.delegate.fake_read(fake_read.clone(), *cause, *hir_id); | |
725 | } | |
726 | } | |
727 | ||
fc512014 XL |
728 | if let Some(min_captures) = self.mc.typeck_results.closure_min_captures.get(&closure_def_id) |
729 | { | |
730 | for (var_hir_id, min_list) in min_captures.iter() { | |
731 | if upvars.map_or(body_owner_is_closure, |upvars| !upvars.contains_key(var_hir_id)) { | |
732 | // The nested closure might be capturing the current (enclosing) closure's local variables. | |
733 | // We check if the root variable is ever mentioned within the enclosing closure, if not | |
734 | // then for the current body (if it's a closure) these aren't captures, we will ignore them. | |
735 | continue; | |
736 | } | |
737 | for captured_place in min_list { | |
738 | let place = &captured_place.place; | |
739 | let capture_info = captured_place.info; | |
740 | ||
741 | let place_base = if body_owner_is_closure { | |
742 | // Mark the place to be captured by the enclosing closure | |
743 | PlaceBase::Upvar(ty::UpvarId::new(*var_hir_id, self.body_owner)) | |
744 | } else { | |
745 | // If the body owner isn't a closure then the variable must | |
746 | // be a local variable | |
747 | PlaceBase::Local(*var_hir_id) | |
748 | }; | |
749 | let place_with_id = PlaceWithHirId::new( | |
5869c6ff | 750 | capture_info.path_expr_id.unwrap_or(closure_expr.hir_id), |
fc512014 XL |
751 | place.base_ty, |
752 | place_base, | |
753 | place.projections.clone(), | |
754 | ); | |
755 | ||
756 | match capture_info.capture_kind { | |
757 | ty::UpvarCapture::ByValue(_) => { | |
758 | let mode = copy_or_move(&self.mc, &place_with_id); | |
759 | self.delegate.consume(&place_with_id, place_with_id.hir_id, mode); | |
760 | } | |
761 | ty::UpvarCapture::ByRef(upvar_borrow) => { | |
762 | self.delegate.borrow( | |
763 | &place_with_id, | |
764 | place_with_id.hir_id, | |
765 | upvar_borrow.kind, | |
766 | ); | |
767 | } | |
85aaf69f | 768 | } |
1a4d82fc JJ |
769 | } |
770 | } | |
48663c56 | 771 | } |
1a4d82fc | 772 | } |
1a4d82fc JJ |
773 | } |
774 | ||
dc9dc135 XL |
775 | fn copy_or_move<'a, 'tcx>( |
776 | mc: &mc::MemCategorizationContext<'a, 'tcx>, | |
f035d41b | 777 | place_with_id: &PlaceWithHirId<'tcx>, |
dc9dc135 | 778 | ) -> ConsumeMode { |
f035d41b XL |
779 | if !mc.type_is_copy_modulo_regions( |
780 | place_with_id.place.ty(), | |
781 | mc.tcx().hir().span(place_with_id.hir_id), | |
782 | ) { | |
783 | Move | |
784 | } else { | |
785 | Copy | |
786 | } | |
1a4d82fc | 787 | } |