]>
Commit | Line | Data |
---|---|---|
60c5eb7d XL |
1 | //! # Categorization |
2 | //! | |
3 | //! The job of the categorization module is to analyze an expression to | |
4 | //! determine what kind of memory is used in evaluating it (for example, | |
5 | //! where dereferences occur and what kind of pointer is dereferenced; | |
6 | //! whether the memory is mutable, etc.). | |
7 | //! | |
8 | //! Categorization effectively transforms all of our expressions into | |
9 | //! expressions of the following forms (the actual enum has many more | |
10 | //! possibilities, naturally, but they are all variants of these base | |
11 | //! forms): | |
12 | //! | |
13 | //! E = rvalue // some computed rvalue | |
14 | //! | x // address of a local variable or argument | |
15 | //! | *E // deref of a ptr | |
16 | //! | E.comp // access to an interior component | |
17 | //! | |
18 | //! Imagine a routine ToAddr(Expr) that evaluates an expression and returns an | |
19 | //! address where the result is to be found. If Expr is a place, then this | |
20 | //! is the address of the place. If `Expr` is an rvalue, this is the address of | |
21 | //! some temporary spot in memory where the result is stored. | |
22 | //! | |
23 | //! Now, `cat_expr()` classifies the expression `Expr` and the address `A = ToAddr(Expr)` | |
24 | //! as follows: | |
25 | //! | |
26 | //! - `cat`: what kind of expression was this? This is a subset of the | |
27 | //! full expression forms which only includes those that we care about | |
28 | //! for the purpose of the analysis. | |
29 | //! - `mutbl`: mutability of the address `A`. | |
30 | //! - `ty`: the type of data found at the address `A`. | |
31 | //! | |
32 | //! The resulting categorization tree differs somewhat from the expressions | |
33 | //! themselves. For example, auto-derefs are explicit. Also, an index a[b] is | |
34 | //! decomposed into two operations: a dereference to reach the array data and | |
35 | //! then an index to jump forward to the relevant item. | |
36 | //! | |
37 | //! ## By-reference upvars | |
38 | //! | |
39 | //! One part of the codegen which may be non-obvious is that we translate | |
40 | //! closure upvars into the dereference of a borrowed pointer; this more closely | |
41 | //! resembles the runtime codegen. So, for example, if we had: | |
42 | //! | |
43 | //! let mut x = 3; | |
44 | //! let y = 5; | |
45 | //! let inc = || x += y; | |
46 | //! | |
47 | //! Then when we categorize `x` (*within* the closure) we would yield a | |
48 | //! result of `*x'`, effectively, where `x'` is a `Categorization::Upvar` reference | |
49 | //! tied to `x`. The type of `x'` will be a borrowed pointer. | |
50 | ||
ba9703b0 XL |
51 | use rustc_middle::ty::adjustment; |
52 | use rustc_middle::ty::fold::TypeFoldable; | |
53 | use rustc_middle::ty::{self, Ty, TyCtxt}; | |
74b04a01 | 54 | |
60c5eb7d | 55 | use rustc_data_structures::fx::FxIndexMap; |
dfeec247 XL |
56 | use rustc_hir as hir; |
57 | use rustc_hir::def::{DefKind, Res}; | |
58 | use rustc_hir::def_id::DefId; | |
59 | use rustc_hir::PatKind; | |
74b04a01 | 60 | use rustc_infer::infer::InferCtxt; |
dfeec247 | 61 | use rustc_span::Span; |
ba9703b0 | 62 | use rustc_trait_selection::infer::InferCtxtExt; |
60c5eb7d XL |
63 | |
64 | #[derive(Clone, Debug)] | |
65 | pub enum PlaceBase { | |
66 | /// A temporary variable | |
67 | Rvalue, | |
68 | /// A named `static` item | |
69 | StaticItem, | |
70 | /// A named local variable | |
71 | Local(hir::HirId), | |
72 | /// An upvar referenced by closure env | |
73 | Upvar(ty::UpvarId), | |
74 | } | |
75 | ||
76 | #[derive(Clone, Debug)] | |
77 | pub enum Projection<'tcx> { | |
78 | /// A dereference of a pointer, reference or `Box<T>` of the given type | |
79 | Deref(Ty<'tcx>), | |
80 | /// An index or a field | |
81 | Other, | |
82 | } | |
83 | ||
84 | /// A `Place` represents how a value is located in memory. | |
85 | /// | |
86 | /// This is an HIR version of `mir::Place` | |
87 | #[derive(Clone, Debug)] | |
88 | pub struct Place<'tcx> { | |
89 | /// `HirId` of the expression or pattern producing this value. | |
90 | pub hir_id: hir::HirId, | |
91 | /// The `Span` of the expression or pattern producing this value. | |
92 | pub span: Span, | |
93 | /// The type of the `Place` | |
94 | pub ty: Ty<'tcx>, | |
95 | /// The "outermost" place that holds this value. | |
96 | pub base: PlaceBase, | |
97 | /// How this place is derived from the base place. | |
98 | pub projections: Vec<Projection<'tcx>>, | |
99 | } | |
100 | ||
101 | impl<'tcx> Place<'tcx> { | |
102 | /// Returns an iterator of the types that have to be dereferenced to access | |
103 | /// the `Place`. | |
104 | /// | |
105 | /// The types are in the reverse order that they are applied. So if | |
106 | /// `x: &*const u32` and the `Place` is `**x`, then the types returned are | |
107 | ///`*const u32` then `&*const u32`. | |
dfeec247 XL |
108 | crate fn deref_tys(&self) -> impl Iterator<Item = Ty<'tcx>> + '_ { |
109 | self.projections.iter().rev().filter_map(|proj| { | |
110 | if let Projection::Deref(deref_ty) = *proj { Some(deref_ty) } else { None } | |
60c5eb7d XL |
111 | }) |
112 | } | |
113 | } | |
114 | ||
115 | crate trait HirNode { | |
116 | fn hir_id(&self) -> hir::HirId; | |
117 | fn span(&self) -> Span; | |
118 | } | |
119 | ||
dfeec247 XL |
120 | impl HirNode for hir::Expr<'_> { |
121 | fn hir_id(&self) -> hir::HirId { | |
122 | self.hir_id | |
123 | } | |
124 | fn span(&self) -> Span { | |
125 | self.span | |
126 | } | |
60c5eb7d XL |
127 | } |
128 | ||
dfeec247 XL |
129 | impl HirNode for hir::Pat<'_> { |
130 | fn hir_id(&self) -> hir::HirId { | |
131 | self.hir_id | |
132 | } | |
133 | fn span(&self) -> Span { | |
134 | self.span | |
135 | } | |
60c5eb7d XL |
136 | } |
137 | ||
138 | #[derive(Clone)] | |
139 | crate struct MemCategorizationContext<'a, 'tcx> { | |
140 | crate tables: &'a ty::TypeckTables<'tcx>, | |
141 | infcx: &'a InferCtxt<'a, 'tcx>, | |
142 | param_env: ty::ParamEnv<'tcx>, | |
143 | body_owner: DefId, | |
144 | upvars: Option<&'tcx FxIndexMap<hir::HirId, hir::Upvar>>, | |
145 | } | |
146 | ||
147 | crate type McResult<T> = Result<T, ()>; | |
148 | ||
149 | impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { | |
150 | /// Creates a `MemCategorizationContext`. | |
151 | crate fn new( | |
152 | infcx: &'a InferCtxt<'a, 'tcx>, | |
153 | param_env: ty::ParamEnv<'tcx>, | |
154 | body_owner: DefId, | |
155 | tables: &'a ty::TypeckTables<'tcx>, | |
156 | ) -> MemCategorizationContext<'a, 'tcx> { | |
157 | MemCategorizationContext { | |
158 | tables, | |
159 | infcx, | |
160 | param_env, | |
161 | body_owner, | |
162 | upvars: infcx.tcx.upvars(body_owner), | |
163 | } | |
164 | } | |
165 | ||
166 | crate fn tcx(&self) -> TyCtxt<'tcx> { | |
167 | self.infcx.tcx | |
168 | } | |
169 | ||
dfeec247 | 170 | crate fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>, span: Span) -> bool { |
60c5eb7d XL |
171 | self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) |
172 | } | |
173 | ||
174 | fn resolve_vars_if_possible<T>(&self, value: &T) -> T | |
dfeec247 XL |
175 | where |
176 | T: TypeFoldable<'tcx>, | |
60c5eb7d XL |
177 | { |
178 | self.infcx.resolve_vars_if_possible(value) | |
179 | } | |
180 | ||
181 | fn is_tainted_by_errors(&self) -> bool { | |
182 | self.infcx.is_tainted_by_errors() | |
183 | } | |
184 | ||
dfeec247 XL |
185 | fn resolve_type_vars_or_error( |
186 | &self, | |
187 | id: hir::HirId, | |
188 | ty: Option<Ty<'tcx>>, | |
189 | ) -> McResult<Ty<'tcx>> { | |
60c5eb7d XL |
190 | match ty { |
191 | Some(ty) => { | |
192 | let ty = self.resolve_vars_if_possible(&ty); | |
193 | if ty.references_error() || ty.is_ty_var() { | |
194 | debug!("resolve_type_vars_or_error: error from {:?}", ty); | |
195 | Err(()) | |
196 | } else { | |
197 | Ok(ty) | |
198 | } | |
199 | } | |
200 | // FIXME | |
201 | None if self.is_tainted_by_errors() => Err(()), | |
202 | None => { | |
dfeec247 XL |
203 | bug!( |
204 | "no type for node {}: {} in mem_categorization", | |
205 | id, | |
206 | self.tcx().hir().node_to_string(id) | |
207 | ); | |
60c5eb7d XL |
208 | } |
209 | } | |
210 | } | |
211 | ||
212 | crate fn node_ty(&self, hir_id: hir::HirId) -> McResult<Ty<'tcx>> { | |
213 | self.resolve_type_vars_or_error(hir_id, self.tables.node_type_opt(hir_id)) | |
214 | } | |
215 | ||
dfeec247 | 216 | fn expr_ty(&self, expr: &hir::Expr<'_>) -> McResult<Ty<'tcx>> { |
60c5eb7d XL |
217 | self.resolve_type_vars_or_error(expr.hir_id, self.tables.expr_ty_opt(expr)) |
218 | } | |
219 | ||
dfeec247 | 220 | crate fn expr_ty_adjusted(&self, expr: &hir::Expr<'_>) -> McResult<Ty<'tcx>> { |
60c5eb7d XL |
221 | self.resolve_type_vars_or_error(expr.hir_id, self.tables.expr_ty_adjusted_opt(expr)) |
222 | } | |
223 | ||
224 | /// Returns the type of value that this pattern matches against. | |
225 | /// Some non-obvious cases: | |
226 | /// | |
227 | /// - a `ref x` binding matches against a value of type `T` and gives | |
228 | /// `x` the type `&T`; we return `T`. | |
229 | /// - a pattern with implicit derefs (thanks to default binding | |
230 | /// modes #42640) may look like `Some(x)` but in fact have | |
231 | /// implicit deref patterns attached (e.g., it is really | |
232 | /// `&Some(x)`). In that case, we return the "outermost" type | |
233 | /// (e.g., `&Option<T>). | |
dfeec247 | 234 | crate fn pat_ty_adjusted(&self, pat: &hir::Pat<'_>) -> McResult<Ty<'tcx>> { |
60c5eb7d XL |
235 | // Check for implicit `&` types wrapping the pattern; note |
236 | // that these are never attached to binding patterns, so | |
237 | // actually this is somewhat "disjoint" from the code below | |
238 | // that aims to account for `ref x`. | |
239 | if let Some(vec) = self.tables.pat_adjustments().get(pat.hir_id) { | |
240 | if let Some(first_ty) = vec.first() { | |
241 | debug!("pat_ty(pat={:?}) found adjusted ty `{:?}`", pat, first_ty); | |
242 | return Ok(first_ty); | |
243 | } | |
244 | } | |
245 | ||
246 | self.pat_ty_unadjusted(pat) | |
247 | } | |
248 | ||
60c5eb7d | 249 | /// Like `pat_ty`, but ignores implicit `&` patterns. |
dfeec247 | 250 | fn pat_ty_unadjusted(&self, pat: &hir::Pat<'_>) -> McResult<Ty<'tcx>> { |
60c5eb7d XL |
251 | let base_ty = self.node_ty(pat.hir_id)?; |
252 | debug!("pat_ty(pat={:?}) base_ty={:?}", pat, base_ty); | |
253 | ||
254 | // This code detects whether we are looking at a `ref x`, | |
255 | // and if so, figures out what the type *being borrowed* is. | |
256 | let ret_ty = match pat.kind { | |
257 | PatKind::Binding(..) => { | |
dfeec247 XL |
258 | let bm = |
259 | *self.tables.pat_binding_modes().get(pat.hir_id).expect("missing binding mode"); | |
60c5eb7d XL |
260 | |
261 | if let ty::BindByReference(_) = bm { | |
262 | // a bind-by-ref means that the base_ty will be the type of the ident itself, | |
263 | // but what we want here is the type of the underlying value being borrowed. | |
264 | // So peel off one-level, turning the &T into T. | |
265 | match base_ty.builtin_deref(false) { | |
266 | Some(t) => t.ty, | |
267 | None => { | |
268 | debug!("By-ref binding of non-derefable type {:?}", base_ty); | |
269 | return Err(()); | |
270 | } | |
271 | } | |
272 | } else { | |
273 | base_ty | |
274 | } | |
275 | } | |
276 | _ => base_ty, | |
277 | }; | |
278 | debug!("pat_ty(pat={:?}) ret_ty={:?}", pat, ret_ty); | |
279 | ||
280 | Ok(ret_ty) | |
281 | } | |
282 | ||
dfeec247 | 283 | crate fn cat_expr(&self, expr: &hir::Expr<'_>) -> McResult<Place<'tcx>> { |
60c5eb7d XL |
284 | // This recursion helper avoids going through *too many* |
285 | // adjustments, since *only* non-overloaded deref recurses. | |
286 | fn helper<'a, 'tcx>( | |
287 | mc: &MemCategorizationContext<'a, 'tcx>, | |
dfeec247 | 288 | expr: &hir::Expr<'_>, |
60c5eb7d XL |
289 | adjustments: &[adjustment::Adjustment<'tcx>], |
290 | ) -> McResult<Place<'tcx>> { | |
291 | match adjustments.split_last() { | |
292 | None => mc.cat_expr_unadjusted(expr), | |
293 | Some((adjustment, previous)) => { | |
294 | mc.cat_expr_adjusted_with(expr, || helper(mc, expr, previous), adjustment) | |
295 | } | |
296 | } | |
297 | } | |
298 | ||
299 | helper(self, expr, self.tables.expr_adjustments(expr)) | |
300 | } | |
301 | ||
dfeec247 XL |
302 | crate fn cat_expr_adjusted( |
303 | &self, | |
304 | expr: &hir::Expr<'_>, | |
305 | previous: Place<'tcx>, | |
306 | adjustment: &adjustment::Adjustment<'tcx>, | |
307 | ) -> McResult<Place<'tcx>> { | |
60c5eb7d XL |
308 | self.cat_expr_adjusted_with(expr, || Ok(previous), adjustment) |
309 | } | |
310 | ||
dfeec247 XL |
311 | fn cat_expr_adjusted_with<F>( |
312 | &self, | |
313 | expr: &hir::Expr<'_>, | |
314 | previous: F, | |
315 | adjustment: &adjustment::Adjustment<'tcx>, | |
316 | ) -> McResult<Place<'tcx>> | |
317 | where | |
318 | F: FnOnce() -> McResult<Place<'tcx>>, | |
60c5eb7d XL |
319 | { |
320 | debug!("cat_expr_adjusted_with({:?}): {:?}", adjustment, expr); | |
321 | let target = self.resolve_vars_if_possible(&adjustment.target); | |
322 | match adjustment.kind { | |
323 | adjustment::Adjust::Deref(overloaded) => { | |
324 | // Equivalent to *expr or something similar. | |
325 | let base = if let Some(deref) = overloaded { | |
dfeec247 XL |
326 | let ref_ty = self |
327 | .tcx() | |
328 | .mk_ref(deref.region, ty::TypeAndMut { ty: target, mutbl: deref.mutbl }); | |
60c5eb7d XL |
329 | self.cat_rvalue(expr.hir_id, expr.span, ref_ty) |
330 | } else { | |
331 | previous()? | |
332 | }; | |
333 | self.cat_deref(expr, base) | |
334 | } | |
335 | ||
dfeec247 XL |
336 | adjustment::Adjust::NeverToAny |
337 | | adjustment::Adjust::Pointer(_) | |
338 | | adjustment::Adjust::Borrow(_) => { | |
60c5eb7d XL |
339 | // Result is an rvalue. |
340 | Ok(self.cat_rvalue(expr.hir_id, expr.span, target)) | |
341 | } | |
342 | } | |
343 | } | |
344 | ||
dfeec247 | 345 | crate fn cat_expr_unadjusted(&self, expr: &hir::Expr<'_>) -> McResult<Place<'tcx>> { |
60c5eb7d XL |
346 | debug!("cat_expr: id={} expr={:?}", expr.hir_id, expr); |
347 | ||
348 | let expr_ty = self.expr_ty(expr)?; | |
349 | match expr.kind { | |
dfeec247 | 350 | hir::ExprKind::Unary(hir::UnOp::UnDeref, ref e_base) => { |
60c5eb7d XL |
351 | if self.tables.is_method_call(expr) { |
352 | self.cat_overloaded_place(expr, e_base) | |
353 | } else { | |
354 | let base = self.cat_expr(&e_base)?; | |
355 | self.cat_deref(expr, base) | |
356 | } | |
357 | } | |
358 | ||
359 | hir::ExprKind::Field(ref base, _) => { | |
360 | let base = self.cat_expr(&base)?; | |
dfeec247 | 361 | debug!("cat_expr(cat_field): id={} expr={:?} base={:?}", expr.hir_id, expr, base); |
60c5eb7d XL |
362 | Ok(self.cat_projection(expr, base, expr_ty)) |
363 | } | |
364 | ||
365 | hir::ExprKind::Index(ref base, _) => { | |
366 | if self.tables.is_method_call(expr) { | |
367 | // If this is an index implemented by a method call, then it | |
368 | // will include an implicit deref of the result. | |
369 | // The call to index() returns a `&T` value, which | |
370 | // is an rvalue. That is what we will be | |
371 | // dereferencing. | |
372 | self.cat_overloaded_place(expr, base) | |
373 | } else { | |
374 | let base = self.cat_expr(&base)?; | |
375 | Ok(self.cat_projection(expr, base, expr_ty)) | |
376 | } | |
377 | } | |
378 | ||
379 | hir::ExprKind::Path(ref qpath) => { | |
380 | let res = self.tables.qpath_res(qpath, expr.hir_id); | |
381 | self.cat_res(expr.hir_id, expr.span, expr_ty, res) | |
382 | } | |
383 | ||
dfeec247 XL |
384 | hir::ExprKind::Type(ref e, _) => self.cat_expr(&e), |
385 | ||
386 | hir::ExprKind::AddrOf(..) | |
387 | | hir::ExprKind::Call(..) | |
388 | | hir::ExprKind::Assign(..) | |
389 | | hir::ExprKind::AssignOp(..) | |
390 | | hir::ExprKind::Closure(..) | |
391 | | hir::ExprKind::Ret(..) | |
392 | | hir::ExprKind::Unary(..) | |
393 | | hir::ExprKind::Yield(..) | |
394 | | hir::ExprKind::MethodCall(..) | |
395 | | hir::ExprKind::Cast(..) | |
396 | | hir::ExprKind::DropTemps(..) | |
397 | | hir::ExprKind::Array(..) | |
398 | | hir::ExprKind::Tup(..) | |
399 | | hir::ExprKind::Binary(..) | |
400 | | hir::ExprKind::Block(..) | |
401 | | hir::ExprKind::Loop(..) | |
402 | | hir::ExprKind::Match(..) | |
403 | | hir::ExprKind::Lit(..) | |
404 | | hir::ExprKind::Break(..) | |
405 | | hir::ExprKind::Continue(..) | |
406 | | hir::ExprKind::Struct(..) | |
407 | | hir::ExprKind::Repeat(..) | |
ba9703b0 | 408 | | hir::ExprKind::LlvmInlineAsm(..) |
dfeec247 XL |
409 | | hir::ExprKind::Box(..) |
410 | | hir::ExprKind::Err => Ok(self.cat_rvalue(expr.hir_id, expr.span, expr_ty)), | |
60c5eb7d XL |
411 | } |
412 | } | |
413 | ||
dfeec247 XL |
414 | crate fn cat_res( |
415 | &self, | |
416 | hir_id: hir::HirId, | |
417 | span: Span, | |
418 | expr_ty: Ty<'tcx>, | |
419 | res: Res, | |
420 | ) -> McResult<Place<'tcx>> { | |
421 | debug!("cat_res: id={:?} expr={:?} def={:?}", hir_id, expr_ty, res); | |
60c5eb7d XL |
422 | |
423 | match res { | |
ba9703b0 XL |
424 | Res::Def( |
425 | DefKind::Ctor(..) | |
426 | | DefKind::Const | |
427 | | DefKind::ConstParam | |
428 | | DefKind::AssocConst | |
429 | | DefKind::Fn | |
430 | | DefKind::AssocFn, | |
431 | _, | |
432 | ) | |
dfeec247 | 433 | | Res::SelfCtor(..) => Ok(self.cat_rvalue(hir_id, span, expr_ty)), |
60c5eb7d | 434 | |
dfeec247 XL |
435 | Res::Def(DefKind::Static, _) => Ok(Place { |
436 | hir_id, | |
437 | span, | |
438 | ty: expr_ty, | |
439 | base: PlaceBase::StaticItem, | |
440 | projections: Vec::new(), | |
441 | }), | |
60c5eb7d XL |
442 | |
443 | Res::Local(var_id) => { | |
444 | if self.upvars.map_or(false, |upvars| upvars.contains_key(&var_id)) { | |
445 | self.cat_upvar(hir_id, span, var_id) | |
446 | } else { | |
447 | Ok(Place { | |
448 | hir_id, | |
449 | span, | |
450 | ty: expr_ty, | |
451 | base: PlaceBase::Local(var_id), | |
452 | projections: Vec::new(), | |
453 | }) | |
454 | } | |
455 | } | |
456 | ||
dfeec247 | 457 | def => span_bug!(span, "unexpected definition in memory categorization: {:?}", def), |
60c5eb7d XL |
458 | } |
459 | } | |
460 | ||
461 | /// Categorize an upvar. | |
462 | /// | |
463 | /// Note: the actual upvar access contains invisible derefs of closure | |
464 | /// environment and upvar reference as appropriate. Only regionck cares | |
465 | /// about these dereferences, so we let it compute them as needed. | |
466 | fn cat_upvar( | |
467 | &self, | |
468 | hir_id: hir::HirId, | |
469 | span: Span, | |
470 | var_id: hir::HirId, | |
471 | ) -> McResult<Place<'tcx>> { | |
472 | let closure_expr_def_id = self.body_owner; | |
473 | ||
474 | let upvar_id = ty::UpvarId { | |
475 | var_path: ty::UpvarPath { hir_id: var_id }, | |
ba9703b0 | 476 | closure_expr_id: closure_expr_def_id.expect_local(), |
60c5eb7d XL |
477 | }; |
478 | let var_ty = self.node_ty(var_id)?; | |
479 | ||
480 | let ret = Place { | |
481 | hir_id, | |
482 | span, | |
483 | ty: var_ty, | |
484 | base: PlaceBase::Upvar(upvar_id), | |
485 | projections: Vec::new(), | |
486 | }; | |
487 | ||
488 | debug!("cat_upvar ret={:?}", ret); | |
489 | Ok(ret) | |
490 | } | |
491 | ||
492 | crate fn cat_rvalue(&self, hir_id: hir::HirId, span: Span, expr_ty: Ty<'tcx>) -> Place<'tcx> { | |
493 | debug!("cat_rvalue hir_id={:?}, expr_ty={:?}, span={:?}", hir_id, expr_ty, span); | |
dfeec247 XL |
494 | let ret = |
495 | Place { hir_id, span, base: PlaceBase::Rvalue, projections: Vec::new(), ty: expr_ty }; | |
60c5eb7d XL |
496 | debug!("cat_rvalue ret={:?}", ret); |
497 | ret | |
498 | } | |
499 | ||
500 | crate fn cat_projection<N: HirNode>( | |
501 | &self, | |
502 | node: &N, | |
503 | base_place: Place<'tcx>, | |
504 | ty: Ty<'tcx>, | |
505 | ) -> Place<'tcx> { | |
506 | let mut projections = base_place.projections; | |
507 | projections.push(Projection::Other); | |
508 | let ret = Place { | |
509 | hir_id: node.hir_id(), | |
510 | span: node.span(), | |
511 | ty, | |
512 | base: base_place.base, | |
513 | projections, | |
514 | }; | |
515 | debug!("cat_field ret {:?}", ret); | |
516 | ret | |
517 | } | |
518 | ||
519 | fn cat_overloaded_place( | |
520 | &self, | |
dfeec247 XL |
521 | expr: &hir::Expr<'_>, |
522 | base: &hir::Expr<'_>, | |
60c5eb7d XL |
523 | ) -> McResult<Place<'tcx>> { |
524 | debug!("cat_overloaded_place(expr={:?}, base={:?})", expr, base); | |
525 | ||
526 | // Reconstruct the output assuming it's a reference with the | |
527 | // same region and mutability as the receiver. This holds for | |
528 | // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`. | |
529 | let place_ty = self.expr_ty(expr)?; | |
530 | let base_ty = self.expr_ty_adjusted(base)?; | |
531 | ||
532 | let (region, mutbl) = match base_ty.kind { | |
533 | ty::Ref(region, _, mutbl) => (region, mutbl), | |
dfeec247 | 534 | _ => span_bug!(expr.span, "cat_overloaded_place: base is not a reference"), |
60c5eb7d | 535 | }; |
dfeec247 | 536 | let ref_ty = self.tcx().mk_ref(region, ty::TypeAndMut { ty: place_ty, mutbl }); |
60c5eb7d XL |
537 | |
538 | let base = self.cat_rvalue(expr.hir_id, expr.span, ref_ty); | |
539 | self.cat_deref(expr, base) | |
540 | } | |
541 | ||
dfeec247 | 542 | fn cat_deref(&self, node: &impl HirNode, base_place: Place<'tcx>) -> McResult<Place<'tcx>> { |
60c5eb7d XL |
543 | debug!("cat_deref: base_place={:?}", base_place); |
544 | ||
545 | let base_ty = base_place.ty; | |
546 | let deref_ty = match base_ty.builtin_deref(true) { | |
547 | Some(mt) => mt.ty, | |
548 | None => { | |
549 | debug!("explicit deref of non-derefable type: {:?}", base_ty); | |
550 | return Err(()); | |
551 | } | |
552 | }; | |
553 | let mut projections = base_place.projections; | |
554 | projections.push(Projection::Deref(base_ty)); | |
555 | ||
556 | let ret = Place { | |
557 | hir_id: node.hir_id(), | |
558 | span: node.span(), | |
559 | ty: deref_ty, | |
560 | base: base_place.base, | |
561 | projections, | |
562 | }; | |
563 | debug!("cat_deref ret {:?}", ret); | |
564 | Ok(ret) | |
565 | } | |
566 | ||
dfeec247 XL |
567 | crate fn cat_pattern<F>( |
568 | &self, | |
569 | place: Place<'tcx>, | |
570 | pat: &hir::Pat<'_>, | |
571 | mut op: F, | |
572 | ) -> McResult<()> | |
573 | where | |
574 | F: FnMut(&Place<'tcx>, &hir::Pat<'_>), | |
60c5eb7d XL |
575 | { |
576 | self.cat_pattern_(place, pat, &mut op) | |
577 | } | |
578 | ||
579 | // FIXME(#19596) This is a workaround, but there should be a better way to do this | |
dfeec247 XL |
580 | fn cat_pattern_<F>( |
581 | &self, | |
582 | mut place: Place<'tcx>, | |
583 | pat: &hir::Pat<'_>, | |
584 | op: &mut F, | |
585 | ) -> McResult<()> | |
586 | where | |
587 | F: FnMut(&Place<'tcx>, &hir::Pat<'_>), | |
60c5eb7d XL |
588 | { |
589 | // Here, `place` is the `Place` being matched and pat is the pattern it | |
590 | // is being matched against. | |
591 | // | |
592 | // In general, the way that this works is that we walk down the pattern, | |
593 | // constructing a `Place` that represents the path that will be taken | |
594 | // to reach the value being matched. | |
595 | ||
596 | debug!("cat_pattern(pat={:?}, place={:?})", pat, place); | |
597 | ||
598 | // If (pattern) adjustments are active for this pattern, adjust the `Place` correspondingly. | |
599 | // `Place`s are constructed differently from patterns. For example, in | |
600 | // | |
601 | // ``` | |
602 | // match foo { | |
603 | // &&Some(x, ) => { ... }, | |
604 | // _ => { ... }, | |
605 | // } | |
606 | // ``` | |
607 | // | |
608 | // the pattern `&&Some(x,)` is represented as `Ref { Ref { TupleStruct }}`. To build the | |
609 | // corresponding `Place` we start with the `Place` for `foo`, and then, by traversing the | |
610 | // pattern, try to answer the question: given the address of `foo`, how is `x` reached? | |
611 | // | |
612 | // `&&Some(x,)` `place_foo` | |
613 | // `&Some(x,)` `deref { place_foo}` | |
614 | // `Some(x,)` `deref { deref { place_foo }}` | |
615 | // (x,)` `field0 { deref { deref { place_foo }}}` <- resulting place | |
616 | // | |
617 | // The above example has no adjustments. If the code were instead the (after adjustments, | |
618 | // equivalent) version | |
619 | // | |
620 | // ``` | |
621 | // match foo { | |
622 | // Some(x, ) => { ... }, | |
623 | // _ => { ... }, | |
624 | // } | |
625 | // ``` | |
626 | // | |
627 | // Then we see that to get the same result, we must start with | |
628 | // `deref { deref { place_foo }}` instead of `place_foo` since the pattern is now `Some(x,)` | |
629 | // and not `&&Some(x,)`, even though its assigned type is that of `&&Some(x,)`. | |
dfeec247 | 630 | for _ in 0..self.tables.pat_adjustments().get(pat.hir_id).map(|v| v.len()).unwrap_or(0) { |
60c5eb7d XL |
631 | debug!("cat_pattern: applying adjustment to place={:?}", place); |
632 | place = self.cat_deref(pat, place)?; | |
633 | } | |
634 | let place = place; // lose mutability | |
635 | debug!("cat_pattern: applied adjustment derefs to get place={:?}", place); | |
636 | ||
637 | // Invoke the callback, but only now, after the `place` has adjusted. | |
638 | // | |
639 | // To see that this makes sense, consider `match &Some(3) { Some(x) => { ... }}`. In that | |
640 | // case, the initial `place` will be that for `&Some(3)` and the pattern is `Some(x)`. We | |
641 | // don't want to call `op` with these incompatible values. As written, what happens instead | |
642 | // is that `op` is called with the adjusted place (that for `*&Some(3)`) and the pattern | |
643 | // `Some(x)` (which matches). Recursing once more, `*&Some(3)` and the pattern `Some(x)` | |
644 | // result in the place `Downcast<Some>(*&Some(3)).0` associated to `x` and invoke `op` with | |
645 | // that (where the `ref` on `x` is implied). | |
646 | op(&place, pat); | |
647 | ||
648 | match pat.kind { | |
dfeec247 | 649 | PatKind::TupleStruct(_, ref subpats, _) | PatKind::Tuple(ref subpats, _) => { |
60c5eb7d XL |
650 | // S(p1, ..., pN) or (p1, ..., pN) |
651 | for subpat in subpats.iter() { | |
652 | let subpat_ty = self.pat_ty_adjusted(&subpat)?; | |
653 | let sub_place = self.cat_projection(pat, place.clone(), subpat_ty); | |
654 | self.cat_pattern_(sub_place, &subpat, op)?; | |
655 | } | |
656 | } | |
657 | ||
dfeec247 | 658 | PatKind::Struct(_, field_pats, _) => { |
60c5eb7d XL |
659 | // S { f1: p1, ..., fN: pN } |
660 | for fp in field_pats { | |
661 | let field_ty = self.pat_ty_adjusted(&fp.pat)?; | |
662 | let field_place = self.cat_projection(pat, place.clone(), field_ty); | |
663 | self.cat_pattern_(field_place, &fp.pat, op)?; | |
664 | } | |
665 | } | |
666 | ||
dfeec247 | 667 | PatKind::Or(pats) => { |
60c5eb7d XL |
668 | for pat in pats { |
669 | self.cat_pattern_(place.clone(), &pat, op)?; | |
670 | } | |
671 | } | |
672 | ||
673 | PatKind::Binding(.., Some(ref subpat)) => { | |
674 | self.cat_pattern_(place, &subpat, op)?; | |
675 | } | |
676 | ||
677 | PatKind::Box(ref subpat) | PatKind::Ref(ref subpat, _) => { | |
678 | // box p1, &p1, &mut p1. we can ignore the mutability of | |
679 | // PatKind::Ref since that information is already contained | |
680 | // in the type. | |
681 | let subplace = self.cat_deref(pat, place)?; | |
682 | self.cat_pattern_(subplace, &subpat, op)?; | |
683 | } | |
684 | ||
dfeec247 | 685 | PatKind::Slice(before, ref slice, after) => { |
60c5eb7d XL |
686 | let element_ty = match place.ty.builtin_index() { |
687 | Some(ty) => ty, | |
688 | None => { | |
689 | debug!("explicit index of non-indexable type {:?}", place); | |
690 | return Err(()); | |
691 | } | |
692 | }; | |
693 | let elt_place = self.cat_projection(pat, place.clone(), element_ty); | |
694 | for before_pat in before { | |
695 | self.cat_pattern_(elt_place.clone(), &before_pat, op)?; | |
696 | } | |
697 | if let Some(ref slice_pat) = *slice { | |
698 | let slice_pat_ty = self.pat_ty_adjusted(&slice_pat)?; | |
699 | let slice_place = self.cat_projection(pat, place, slice_pat_ty); | |
700 | self.cat_pattern_(slice_place, &slice_pat, op)?; | |
701 | } | |
702 | for after_pat in after { | |
703 | self.cat_pattern_(elt_place.clone(), &after_pat, op)?; | |
704 | } | |
705 | } | |
706 | ||
dfeec247 XL |
707 | PatKind::Path(_) |
708 | | PatKind::Binding(.., None) | |
709 | | PatKind::Lit(..) | |
710 | | PatKind::Range(..) | |
711 | | PatKind::Wild => { | |
60c5eb7d XL |
712 | // always ok |
713 | } | |
714 | } | |
715 | ||
716 | Ok(()) | |
717 | } | |
718 | } |