]>
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 | |
f9f354fc | 33 | //! themselves. For example, auto-derefs are explicit. Also, an index `a[b]` is |
60c5eb7d XL |
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 | ||
3dfed10e | 51 | use rustc_middle::hir::place::*; |
ba9703b0 XL |
52 | use rustc_middle::ty::adjustment; |
53 | use rustc_middle::ty::fold::TypeFoldable; | |
54 | use rustc_middle::ty::{self, Ty, TyCtxt}; | |
74b04a01 | 55 | |
60c5eb7d | 56 | use rustc_data_structures::fx::FxIndexMap; |
dfeec247 | 57 | use rustc_hir as hir; |
3dfed10e | 58 | use rustc_hir::def::{CtorOf, DefKind, Res}; |
f9f354fc | 59 | use rustc_hir::def_id::LocalDefId; |
3dfed10e | 60 | use rustc_hir::pat_util::EnumerateAndAdjustIterator; |
dfeec247 | 61 | use rustc_hir::PatKind; |
3dfed10e | 62 | use rustc_index::vec::Idx; |
74b04a01 | 63 | use rustc_infer::infer::InferCtxt; |
dfeec247 | 64 | use rustc_span::Span; |
3dfed10e | 65 | use rustc_target::abi::VariantIdx; |
ba9703b0 | 66 | use rustc_trait_selection::infer::InferCtxtExt; |
60c5eb7d | 67 | |
60c5eb7d XL |
68 | crate trait HirNode { |
69 | fn hir_id(&self) -> hir::HirId; | |
70 | fn span(&self) -> Span; | |
71 | } | |
72 | ||
dfeec247 XL |
73 | impl HirNode for hir::Expr<'_> { |
74 | fn hir_id(&self) -> hir::HirId { | |
75 | self.hir_id | |
76 | } | |
77 | fn span(&self) -> Span { | |
78 | self.span | |
79 | } | |
60c5eb7d XL |
80 | } |
81 | ||
dfeec247 XL |
82 | impl HirNode for hir::Pat<'_> { |
83 | fn hir_id(&self) -> hir::HirId { | |
84 | self.hir_id | |
85 | } | |
86 | fn span(&self) -> Span { | |
87 | self.span | |
88 | } | |
60c5eb7d XL |
89 | } |
90 | ||
91 | #[derive(Clone)] | |
92 | crate struct MemCategorizationContext<'a, 'tcx> { | |
3dfed10e | 93 | crate typeck_results: &'a ty::TypeckResults<'tcx>, |
60c5eb7d XL |
94 | infcx: &'a InferCtxt<'a, 'tcx>, |
95 | param_env: ty::ParamEnv<'tcx>, | |
f9f354fc | 96 | body_owner: LocalDefId, |
60c5eb7d XL |
97 | upvars: Option<&'tcx FxIndexMap<hir::HirId, hir::Upvar>>, |
98 | } | |
99 | ||
100 | crate type McResult<T> = Result<T, ()>; | |
101 | ||
102 | impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { | |
103 | /// Creates a `MemCategorizationContext`. | |
104 | crate fn new( | |
105 | infcx: &'a InferCtxt<'a, 'tcx>, | |
106 | param_env: ty::ParamEnv<'tcx>, | |
f9f354fc | 107 | body_owner: LocalDefId, |
3dfed10e | 108 | typeck_results: &'a ty::TypeckResults<'tcx>, |
60c5eb7d XL |
109 | ) -> MemCategorizationContext<'a, 'tcx> { |
110 | MemCategorizationContext { | |
3dfed10e | 111 | typeck_results, |
60c5eb7d XL |
112 | infcx, |
113 | param_env, | |
114 | body_owner, | |
f9f354fc | 115 | upvars: infcx.tcx.upvars_mentioned(body_owner), |
60c5eb7d XL |
116 | } |
117 | } | |
118 | ||
119 | crate fn tcx(&self) -> TyCtxt<'tcx> { | |
120 | self.infcx.tcx | |
121 | } | |
122 | ||
dfeec247 | 123 | crate fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>, span: Span) -> bool { |
60c5eb7d XL |
124 | self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) |
125 | } | |
126 | ||
fc512014 | 127 | fn resolve_vars_if_possible<T>(&self, value: T) -> T |
dfeec247 XL |
128 | where |
129 | T: TypeFoldable<'tcx>, | |
60c5eb7d XL |
130 | { |
131 | self.infcx.resolve_vars_if_possible(value) | |
132 | } | |
133 | ||
134 | fn is_tainted_by_errors(&self) -> bool { | |
135 | self.infcx.is_tainted_by_errors() | |
136 | } | |
137 | ||
dfeec247 XL |
138 | fn resolve_type_vars_or_error( |
139 | &self, | |
140 | id: hir::HirId, | |
141 | ty: Option<Ty<'tcx>>, | |
142 | ) -> McResult<Ty<'tcx>> { | |
60c5eb7d XL |
143 | match ty { |
144 | Some(ty) => { | |
fc512014 | 145 | let ty = self.resolve_vars_if_possible(ty); |
60c5eb7d XL |
146 | if ty.references_error() || ty.is_ty_var() { |
147 | debug!("resolve_type_vars_or_error: error from {:?}", ty); | |
148 | Err(()) | |
149 | } else { | |
150 | Ok(ty) | |
151 | } | |
152 | } | |
153 | // FIXME | |
154 | None if self.is_tainted_by_errors() => Err(()), | |
155 | None => { | |
dfeec247 XL |
156 | bug!( |
157 | "no type for node {}: {} in mem_categorization", | |
158 | id, | |
159 | self.tcx().hir().node_to_string(id) | |
160 | ); | |
60c5eb7d XL |
161 | } |
162 | } | |
163 | } | |
164 | ||
165 | crate fn node_ty(&self, hir_id: hir::HirId) -> McResult<Ty<'tcx>> { | |
3dfed10e | 166 | self.resolve_type_vars_or_error(hir_id, self.typeck_results.node_type_opt(hir_id)) |
60c5eb7d XL |
167 | } |
168 | ||
dfeec247 | 169 | fn expr_ty(&self, expr: &hir::Expr<'_>) -> McResult<Ty<'tcx>> { |
3dfed10e | 170 | self.resolve_type_vars_or_error(expr.hir_id, self.typeck_results.expr_ty_opt(expr)) |
60c5eb7d XL |
171 | } |
172 | ||
dfeec247 | 173 | crate fn expr_ty_adjusted(&self, expr: &hir::Expr<'_>) -> McResult<Ty<'tcx>> { |
3dfed10e | 174 | self.resolve_type_vars_or_error(expr.hir_id, self.typeck_results.expr_ty_adjusted_opt(expr)) |
60c5eb7d XL |
175 | } |
176 | ||
177 | /// Returns the type of value that this pattern matches against. | |
178 | /// Some non-obvious cases: | |
179 | /// | |
180 | /// - a `ref x` binding matches against a value of type `T` and gives | |
181 | /// `x` the type `&T`; we return `T`. | |
182 | /// - a pattern with implicit derefs (thanks to default binding | |
183 | /// modes #42640) may look like `Some(x)` but in fact have | |
184 | /// implicit deref patterns attached (e.g., it is really | |
185 | /// `&Some(x)`). In that case, we return the "outermost" type | |
186 | /// (e.g., `&Option<T>). | |
dfeec247 | 187 | crate fn pat_ty_adjusted(&self, pat: &hir::Pat<'_>) -> McResult<Ty<'tcx>> { |
60c5eb7d XL |
188 | // Check for implicit `&` types wrapping the pattern; note |
189 | // that these are never attached to binding patterns, so | |
190 | // actually this is somewhat "disjoint" from the code below | |
191 | // that aims to account for `ref x`. | |
3dfed10e | 192 | if let Some(vec) = self.typeck_results.pat_adjustments().get(pat.hir_id) { |
60c5eb7d XL |
193 | if let Some(first_ty) = vec.first() { |
194 | debug!("pat_ty(pat={:?}) found adjusted ty `{:?}`", pat, first_ty); | |
5099ac24 | 195 | return Ok(*first_ty); |
60c5eb7d XL |
196 | } |
197 | } | |
198 | ||
199 | self.pat_ty_unadjusted(pat) | |
200 | } | |
201 | ||
60c5eb7d | 202 | /// Like `pat_ty`, but ignores implicit `&` patterns. |
dfeec247 | 203 | fn pat_ty_unadjusted(&self, pat: &hir::Pat<'_>) -> McResult<Ty<'tcx>> { |
60c5eb7d XL |
204 | let base_ty = self.node_ty(pat.hir_id)?; |
205 | debug!("pat_ty(pat={:?}) base_ty={:?}", pat, base_ty); | |
206 | ||
207 | // This code detects whether we are looking at a `ref x`, | |
208 | // and if so, figures out what the type *being borrowed* is. | |
209 | let ret_ty = match pat.kind { | |
210 | PatKind::Binding(..) => { | |
3dfed10e XL |
211 | let bm = *self |
212 | .typeck_results | |
213 | .pat_binding_modes() | |
214 | .get(pat.hir_id) | |
215 | .expect("missing binding mode"); | |
60c5eb7d XL |
216 | |
217 | if let ty::BindByReference(_) = bm { | |
218 | // a bind-by-ref means that the base_ty will be the type of the ident itself, | |
219 | // but what we want here is the type of the underlying value being borrowed. | |
220 | // So peel off one-level, turning the &T into T. | |
221 | match base_ty.builtin_deref(false) { | |
222 | Some(t) => t.ty, | |
223 | None => { | |
224 | debug!("By-ref binding of non-derefable type {:?}", base_ty); | |
225 | return Err(()); | |
226 | } | |
227 | } | |
228 | } else { | |
229 | base_ty | |
230 | } | |
231 | } | |
232 | _ => base_ty, | |
233 | }; | |
234 | debug!("pat_ty(pat={:?}) ret_ty={:?}", pat, ret_ty); | |
235 | ||
236 | Ok(ret_ty) | |
237 | } | |
238 | ||
f035d41b | 239 | crate fn cat_expr(&self, expr: &hir::Expr<'_>) -> McResult<PlaceWithHirId<'tcx>> { |
60c5eb7d XL |
240 | // This recursion helper avoids going through *too many* |
241 | // adjustments, since *only* non-overloaded deref recurses. | |
242 | fn helper<'a, 'tcx>( | |
243 | mc: &MemCategorizationContext<'a, 'tcx>, | |
dfeec247 | 244 | expr: &hir::Expr<'_>, |
60c5eb7d | 245 | adjustments: &[adjustment::Adjustment<'tcx>], |
f035d41b | 246 | ) -> McResult<PlaceWithHirId<'tcx>> { |
60c5eb7d XL |
247 | match adjustments.split_last() { |
248 | None => mc.cat_expr_unadjusted(expr), | |
249 | Some((adjustment, previous)) => { | |
250 | mc.cat_expr_adjusted_with(expr, || helper(mc, expr, previous), adjustment) | |
251 | } | |
252 | } | |
253 | } | |
254 | ||
3dfed10e | 255 | helper(self, expr, self.typeck_results.expr_adjustments(expr)) |
60c5eb7d XL |
256 | } |
257 | ||
dfeec247 XL |
258 | crate fn cat_expr_adjusted( |
259 | &self, | |
260 | expr: &hir::Expr<'_>, | |
f035d41b | 261 | previous: PlaceWithHirId<'tcx>, |
dfeec247 | 262 | adjustment: &adjustment::Adjustment<'tcx>, |
f035d41b | 263 | ) -> McResult<PlaceWithHirId<'tcx>> { |
60c5eb7d XL |
264 | self.cat_expr_adjusted_with(expr, || Ok(previous), adjustment) |
265 | } | |
266 | ||
dfeec247 XL |
267 | fn cat_expr_adjusted_with<F>( |
268 | &self, | |
269 | expr: &hir::Expr<'_>, | |
270 | previous: F, | |
271 | adjustment: &adjustment::Adjustment<'tcx>, | |
f035d41b | 272 | ) -> McResult<PlaceWithHirId<'tcx>> |
dfeec247 | 273 | where |
f035d41b | 274 | F: FnOnce() -> McResult<PlaceWithHirId<'tcx>>, |
60c5eb7d XL |
275 | { |
276 | debug!("cat_expr_adjusted_with({:?}): {:?}", adjustment, expr); | |
fc512014 | 277 | let target = self.resolve_vars_if_possible(adjustment.target); |
60c5eb7d XL |
278 | match adjustment.kind { |
279 | adjustment::Adjust::Deref(overloaded) => { | |
280 | // Equivalent to *expr or something similar. | |
281 | let base = if let Some(deref) = overloaded { | |
dfeec247 XL |
282 | let ref_ty = self |
283 | .tcx() | |
284 | .mk_ref(deref.region, ty::TypeAndMut { ty: target, mutbl: deref.mutbl }); | |
60c5eb7d XL |
285 | self.cat_rvalue(expr.hir_id, expr.span, ref_ty) |
286 | } else { | |
287 | previous()? | |
288 | }; | |
289 | self.cat_deref(expr, base) | |
290 | } | |
291 | ||
dfeec247 XL |
292 | adjustment::Adjust::NeverToAny |
293 | | adjustment::Adjust::Pointer(_) | |
294 | | adjustment::Adjust::Borrow(_) => { | |
60c5eb7d XL |
295 | // Result is an rvalue. |
296 | Ok(self.cat_rvalue(expr.hir_id, expr.span, target)) | |
297 | } | |
298 | } | |
299 | } | |
300 | ||
f035d41b | 301 | crate fn cat_expr_unadjusted(&self, expr: &hir::Expr<'_>) -> McResult<PlaceWithHirId<'tcx>> { |
60c5eb7d XL |
302 | debug!("cat_expr: id={} expr={:?}", expr.hir_id, expr); |
303 | ||
304 | let expr_ty = self.expr_ty(expr)?; | |
305 | match expr.kind { | |
6a06907d | 306 | hir::ExprKind::Unary(hir::UnOp::Deref, ref e_base) => { |
3dfed10e | 307 | if self.typeck_results.is_method_call(expr) { |
60c5eb7d XL |
308 | self.cat_overloaded_place(expr, e_base) |
309 | } else { | |
c295e0f8 | 310 | let base = self.cat_expr(e_base)?; |
60c5eb7d XL |
311 | self.cat_deref(expr, base) |
312 | } | |
313 | } | |
314 | ||
315 | hir::ExprKind::Field(ref base, _) => { | |
c295e0f8 | 316 | let base = self.cat_expr(base)?; |
dfeec247 | 317 | debug!("cat_expr(cat_field): id={} expr={:?} base={:?}", expr.hir_id, expr, base); |
3dfed10e XL |
318 | |
319 | let field_idx = self | |
320 | .typeck_results | |
321 | .field_indices() | |
322 | .get(expr.hir_id) | |
323 | .cloned() | |
324 | .expect("Field index not found"); | |
325 | ||
326 | Ok(self.cat_projection( | |
327 | expr, | |
328 | base, | |
329 | expr_ty, | |
330 | ProjectionKind::Field(field_idx as u32, VariantIdx::new(0)), | |
331 | )) | |
60c5eb7d XL |
332 | } |
333 | ||
334 | hir::ExprKind::Index(ref base, _) => { | |
3dfed10e | 335 | if self.typeck_results.is_method_call(expr) { |
60c5eb7d XL |
336 | // If this is an index implemented by a method call, then it |
337 | // will include an implicit deref of the result. | |
338 | // The call to index() returns a `&T` value, which | |
339 | // is an rvalue. That is what we will be | |
340 | // dereferencing. | |
341 | self.cat_overloaded_place(expr, base) | |
342 | } else { | |
c295e0f8 | 343 | let base = self.cat_expr(base)?; |
3dfed10e | 344 | Ok(self.cat_projection(expr, base, expr_ty, ProjectionKind::Index)) |
60c5eb7d XL |
345 | } |
346 | } | |
347 | ||
348 | hir::ExprKind::Path(ref qpath) => { | |
3dfed10e | 349 | let res = self.typeck_results.qpath_res(qpath, expr.hir_id); |
60c5eb7d XL |
350 | self.cat_res(expr.hir_id, expr.span, expr_ty, res) |
351 | } | |
352 | ||
c295e0f8 | 353 | hir::ExprKind::Type(ref e, _) => self.cat_expr(e), |
dfeec247 XL |
354 | |
355 | hir::ExprKind::AddrOf(..) | |
356 | | hir::ExprKind::Call(..) | |
357 | | hir::ExprKind::Assign(..) | |
358 | | hir::ExprKind::AssignOp(..) | |
359 | | hir::ExprKind::Closure(..) | |
360 | | hir::ExprKind::Ret(..) | |
361 | | hir::ExprKind::Unary(..) | |
362 | | hir::ExprKind::Yield(..) | |
363 | | hir::ExprKind::MethodCall(..) | |
364 | | hir::ExprKind::Cast(..) | |
365 | | hir::ExprKind::DropTemps(..) | |
366 | | hir::ExprKind::Array(..) | |
5869c6ff | 367 | | hir::ExprKind::If(..) |
dfeec247 XL |
368 | | hir::ExprKind::Tup(..) |
369 | | hir::ExprKind::Binary(..) | |
370 | | hir::ExprKind::Block(..) | |
94222f64 | 371 | | hir::ExprKind::Let(..) |
dfeec247 XL |
372 | | hir::ExprKind::Loop(..) |
373 | | hir::ExprKind::Match(..) | |
374 | | hir::ExprKind::Lit(..) | |
29967ef6 | 375 | | hir::ExprKind::ConstBlock(..) |
dfeec247 XL |
376 | | hir::ExprKind::Break(..) |
377 | | hir::ExprKind::Continue(..) | |
378 | | hir::ExprKind::Struct(..) | |
379 | | hir::ExprKind::Repeat(..) | |
f9f354fc | 380 | | hir::ExprKind::InlineAsm(..) |
dfeec247 XL |
381 | | hir::ExprKind::Box(..) |
382 | | hir::ExprKind::Err => Ok(self.cat_rvalue(expr.hir_id, expr.span, expr_ty)), | |
60c5eb7d XL |
383 | } |
384 | } | |
385 | ||
dfeec247 XL |
386 | crate fn cat_res( |
387 | &self, | |
388 | hir_id: hir::HirId, | |
389 | span: Span, | |
390 | expr_ty: Ty<'tcx>, | |
391 | res: Res, | |
f035d41b | 392 | ) -> McResult<PlaceWithHirId<'tcx>> { |
dfeec247 | 393 | debug!("cat_res: id={:?} expr={:?} def={:?}", hir_id, expr_ty, res); |
60c5eb7d XL |
394 | |
395 | match res { | |
ba9703b0 XL |
396 | Res::Def( |
397 | DefKind::Ctor(..) | |
398 | | DefKind::Const | |
399 | | DefKind::ConstParam | |
400 | | DefKind::AssocConst | |
401 | | DefKind::Fn | |
402 | | DefKind::AssocFn, | |
403 | _, | |
404 | ) | |
dfeec247 | 405 | | Res::SelfCtor(..) => Ok(self.cat_rvalue(hir_id, span, expr_ty)), |
60c5eb7d | 406 | |
5e7ed085 | 407 | Res::Def(DefKind::Static(_), _) => { |
f035d41b XL |
408 | Ok(PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::StaticItem, Vec::new())) |
409 | } | |
60c5eb7d XL |
410 | |
411 | Res::Local(var_id) => { | |
412 | if self.upvars.map_or(false, |upvars| upvars.contains_key(&var_id)) { | |
f035d41b | 413 | self.cat_upvar(hir_id, var_id) |
60c5eb7d | 414 | } else { |
f035d41b | 415 | Ok(PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Local(var_id), Vec::new())) |
60c5eb7d XL |
416 | } |
417 | } | |
418 | ||
dfeec247 | 419 | def => span_bug!(span, "unexpected definition in memory categorization: {:?}", def), |
60c5eb7d XL |
420 | } |
421 | } | |
422 | ||
423 | /// Categorize an upvar. | |
424 | /// | |
425 | /// Note: the actual upvar access contains invisible derefs of closure | |
426 | /// environment and upvar reference as appropriate. Only regionck cares | |
427 | /// about these dereferences, so we let it compute them as needed. | |
f035d41b | 428 | fn cat_upvar(&self, hir_id: hir::HirId, var_id: hir::HirId) -> McResult<PlaceWithHirId<'tcx>> { |
60c5eb7d XL |
429 | let closure_expr_def_id = self.body_owner; |
430 | ||
431 | let upvar_id = ty::UpvarId { | |
432 | var_path: ty::UpvarPath { hir_id: var_id }, | |
f9f354fc | 433 | closure_expr_id: closure_expr_def_id, |
60c5eb7d XL |
434 | }; |
435 | let var_ty = self.node_ty(var_id)?; | |
436 | ||
f035d41b | 437 | let ret = PlaceWithHirId::new(hir_id, var_ty, PlaceBase::Upvar(upvar_id), Vec::new()); |
60c5eb7d XL |
438 | |
439 | debug!("cat_upvar ret={:?}", ret); | |
440 | Ok(ret) | |
441 | } | |
442 | ||
f035d41b XL |
443 | crate fn cat_rvalue( |
444 | &self, | |
445 | hir_id: hir::HirId, | |
446 | span: Span, | |
447 | expr_ty: Ty<'tcx>, | |
448 | ) -> PlaceWithHirId<'tcx> { | |
60c5eb7d | 449 | debug!("cat_rvalue hir_id={:?}, expr_ty={:?}, span={:?}", hir_id, expr_ty, span); |
f035d41b | 450 | let ret = PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Rvalue, Vec::new()); |
60c5eb7d XL |
451 | debug!("cat_rvalue ret={:?}", ret); |
452 | ret | |
453 | } | |
454 | ||
455 | crate fn cat_projection<N: HirNode>( | |
456 | &self, | |
457 | node: &N, | |
f035d41b | 458 | base_place: PlaceWithHirId<'tcx>, |
60c5eb7d | 459 | ty: Ty<'tcx>, |
3dfed10e | 460 | kind: ProjectionKind, |
f035d41b XL |
461 | ) -> PlaceWithHirId<'tcx> { |
462 | let mut projections = base_place.place.projections; | |
5869c6ff | 463 | projections.push(Projection { kind, ty }); |
f035d41b XL |
464 | let ret = PlaceWithHirId::new( |
465 | node.hir_id(), | |
466 | base_place.place.base_ty, | |
467 | base_place.place.base, | |
60c5eb7d | 468 | projections, |
f035d41b | 469 | ); |
60c5eb7d XL |
470 | debug!("cat_field ret {:?}", ret); |
471 | ret | |
472 | } | |
473 | ||
474 | fn cat_overloaded_place( | |
475 | &self, | |
dfeec247 XL |
476 | expr: &hir::Expr<'_>, |
477 | base: &hir::Expr<'_>, | |
f035d41b | 478 | ) -> McResult<PlaceWithHirId<'tcx>> { |
60c5eb7d XL |
479 | debug!("cat_overloaded_place(expr={:?}, base={:?})", expr, base); |
480 | ||
481 | // Reconstruct the output assuming it's a reference with the | |
482 | // same region and mutability as the receiver. This holds for | |
483 | // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`. | |
484 | let place_ty = self.expr_ty(expr)?; | |
485 | let base_ty = self.expr_ty_adjusted(base)?; | |
486 | ||
5e7ed085 FG |
487 | let ty::Ref(region, _, mutbl) = *base_ty.kind() else { |
488 | span_bug!(expr.span, "cat_overloaded_place: base is not a reference"); | |
60c5eb7d | 489 | }; |
dfeec247 | 490 | let ref_ty = self.tcx().mk_ref(region, ty::TypeAndMut { ty: place_ty, mutbl }); |
60c5eb7d XL |
491 | |
492 | let base = self.cat_rvalue(expr.hir_id, expr.span, ref_ty); | |
493 | self.cat_deref(expr, base) | |
494 | } | |
495 | ||
f035d41b XL |
496 | fn cat_deref( |
497 | &self, | |
498 | node: &impl HirNode, | |
499 | base_place: PlaceWithHirId<'tcx>, | |
500 | ) -> McResult<PlaceWithHirId<'tcx>> { | |
60c5eb7d XL |
501 | debug!("cat_deref: base_place={:?}", base_place); |
502 | ||
f035d41b XL |
503 | let base_curr_ty = base_place.place.ty(); |
504 | let deref_ty = match base_curr_ty.builtin_deref(true) { | |
60c5eb7d XL |
505 | Some(mt) => mt.ty, |
506 | None => { | |
f035d41b | 507 | debug!("explicit deref of non-derefable type: {:?}", base_curr_ty); |
60c5eb7d XL |
508 | return Err(()); |
509 | } | |
510 | }; | |
f035d41b XL |
511 | let mut projections = base_place.place.projections; |
512 | projections.push(Projection { kind: ProjectionKind::Deref, ty: deref_ty }); | |
513 | ||
514 | let ret = PlaceWithHirId::new( | |
515 | node.hir_id(), | |
516 | base_place.place.base_ty, | |
517 | base_place.place.base, | |
60c5eb7d | 518 | projections, |
f035d41b | 519 | ); |
60c5eb7d XL |
520 | debug!("cat_deref ret {:?}", ret); |
521 | Ok(ret) | |
522 | } | |
523 | ||
dfeec247 XL |
524 | crate fn cat_pattern<F>( |
525 | &self, | |
f035d41b | 526 | place: PlaceWithHirId<'tcx>, |
dfeec247 XL |
527 | pat: &hir::Pat<'_>, |
528 | mut op: F, | |
529 | ) -> McResult<()> | |
530 | where | |
f035d41b | 531 | F: FnMut(&PlaceWithHirId<'tcx>, &hir::Pat<'_>), |
60c5eb7d XL |
532 | { |
533 | self.cat_pattern_(place, pat, &mut op) | |
534 | } | |
535 | ||
3dfed10e XL |
536 | /// Returns the variant index for an ADT used within a Struct or TupleStruct pattern |
537 | /// Here `pat_hir_id` is the HirId of the pattern itself. | |
538 | fn variant_index_for_adt( | |
539 | &self, | |
540 | qpath: &hir::QPath<'_>, | |
541 | pat_hir_id: hir::HirId, | |
542 | span: Span, | |
543 | ) -> McResult<VariantIdx> { | |
544 | let res = self.typeck_results.qpath_res(qpath, pat_hir_id); | |
545 | let ty = self.typeck_results.node_type(pat_hir_id); | |
5e7ed085 FG |
546 | let ty::Adt(adt_def, _) = ty.kind() else { |
547 | self.tcx() | |
548 | .sess | |
549 | .delay_span_bug(span, "struct or tuple struct pattern not applied to an ADT"); | |
550 | return Err(()); | |
3dfed10e XL |
551 | }; |
552 | ||
553 | match res { | |
554 | Res::Def(DefKind::Variant, variant_id) => Ok(adt_def.variant_index_with_id(variant_id)), | |
555 | Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_id) => { | |
556 | Ok(adt_def.variant_index_with_ctor_id(variant_ctor_id)) | |
557 | } | |
558 | Res::Def(DefKind::Ctor(CtorOf::Struct, ..), _) | |
559 | | Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _) | |
560 | | Res::SelfCtor(..) | |
5099ac24 | 561 | | Res::SelfTy { .. } => { |
3dfed10e XL |
562 | // Structs and Unions have only have one variant. |
563 | Ok(VariantIdx::new(0)) | |
564 | } | |
565 | _ => bug!("expected ADT path, found={:?}", res), | |
566 | } | |
567 | } | |
568 | ||
569 | /// Returns the total number of fields in an ADT variant used within a pattern. | |
570 | /// Here `pat_hir_id` is the HirId of the pattern itself. | |
571 | fn total_fields_in_adt_variant( | |
572 | &self, | |
573 | pat_hir_id: hir::HirId, | |
574 | variant_index: VariantIdx, | |
575 | span: Span, | |
576 | ) -> McResult<usize> { | |
577 | let ty = self.typeck_results.node_type(pat_hir_id); | |
1b1a35ee | 578 | match ty.kind() { |
5e7ed085 | 579 | ty::Adt(adt_def, _) => Ok(adt_def.variant(variant_index).fields.len()), |
3dfed10e XL |
580 | _ => { |
581 | self.tcx() | |
582 | .sess | |
583 | .delay_span_bug(span, "struct or tuple struct pattern not applied to an ADT"); | |
584 | Err(()) | |
585 | } | |
586 | } | |
587 | } | |
588 | ||
589 | /// Returns the total number of fields in a tuple used within a Tuple pattern. | |
590 | /// Here `pat_hir_id` is the HirId of the pattern itself. | |
591 | fn total_fields_in_tuple(&self, pat_hir_id: hir::HirId, span: Span) -> McResult<usize> { | |
592 | let ty = self.typeck_results.node_type(pat_hir_id); | |
1b1a35ee | 593 | match ty.kind() { |
3dfed10e XL |
594 | ty::Tuple(substs) => Ok(substs.len()), |
595 | _ => { | |
596 | self.tcx().sess.delay_span_bug(span, "tuple pattern not applied to a tuple"); | |
597 | Err(()) | |
598 | } | |
599 | } | |
600 | } | |
601 | ||
60c5eb7d | 602 | // FIXME(#19596) This is a workaround, but there should be a better way to do this |
dfeec247 XL |
603 | fn cat_pattern_<F>( |
604 | &self, | |
f035d41b | 605 | mut place_with_id: PlaceWithHirId<'tcx>, |
dfeec247 XL |
606 | pat: &hir::Pat<'_>, |
607 | op: &mut F, | |
608 | ) -> McResult<()> | |
609 | where | |
f035d41b | 610 | F: FnMut(&PlaceWithHirId<'tcx>, &hir::Pat<'_>), |
60c5eb7d | 611 | { |
f035d41b | 612 | // Here, `place` is the `PlaceWithHirId` being matched and pat is the pattern it |
60c5eb7d XL |
613 | // is being matched against. |
614 | // | |
615 | // In general, the way that this works is that we walk down the pattern, | |
f035d41b | 616 | // constructing a `PlaceWithHirId` that represents the path that will be taken |
60c5eb7d XL |
617 | // to reach the value being matched. |
618 | ||
f035d41b | 619 | debug!("cat_pattern(pat={:?}, place_with_id={:?})", pat, place_with_id); |
60c5eb7d | 620 | |
f035d41b XL |
621 | // If (pattern) adjustments are active for this pattern, adjust the `PlaceWithHirId` correspondingly. |
622 | // `PlaceWithHirId`s are constructed differently from patterns. For example, in | |
60c5eb7d XL |
623 | // |
624 | // ``` | |
625 | // match foo { | |
626 | // &&Some(x, ) => { ... }, | |
627 | // _ => { ... }, | |
628 | // } | |
629 | // ``` | |
630 | // | |
631 | // the pattern `&&Some(x,)` is represented as `Ref { Ref { TupleStruct }}`. To build the | |
f035d41b | 632 | // corresponding `PlaceWithHirId` we start with the `PlaceWithHirId` for `foo`, and then, by traversing the |
60c5eb7d XL |
633 | // pattern, try to answer the question: given the address of `foo`, how is `x` reached? |
634 | // | |
635 | // `&&Some(x,)` `place_foo` | |
636 | // `&Some(x,)` `deref { place_foo}` | |
637 | // `Some(x,)` `deref { deref { place_foo }}` | |
638 | // (x,)` `field0 { deref { deref { place_foo }}}` <- resulting place | |
639 | // | |
640 | // The above example has no adjustments. If the code were instead the (after adjustments, | |
641 | // equivalent) version | |
642 | // | |
643 | // ``` | |
644 | // match foo { | |
645 | // Some(x, ) => { ... }, | |
646 | // _ => { ... }, | |
647 | // } | |
648 | // ``` | |
649 | // | |
650 | // Then we see that to get the same result, we must start with | |
651 | // `deref { deref { place_foo }}` instead of `place_foo` since the pattern is now `Some(x,)` | |
652 | // and not `&&Some(x,)`, even though its assigned type is that of `&&Some(x,)`. | |
5869c6ff | 653 | for _ in 0..self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(0, |v| v.len()) { |
f035d41b XL |
654 | debug!("cat_pattern: applying adjustment to place_with_id={:?}", place_with_id); |
655 | place_with_id = self.cat_deref(pat, place_with_id)?; | |
60c5eb7d | 656 | } |
f035d41b XL |
657 | let place_with_id = place_with_id; // lose mutability |
658 | debug!("cat_pattern: applied adjustment derefs to get place_with_id={:?}", place_with_id); | |
60c5eb7d | 659 | |
f035d41b | 660 | // Invoke the callback, but only now, after the `place_with_id` has adjusted. |
60c5eb7d XL |
661 | // |
662 | // To see that this makes sense, consider `match &Some(3) { Some(x) => { ... }}`. In that | |
f035d41b | 663 | // case, the initial `place_with_id` will be that for `&Some(3)` and the pattern is `Some(x)`. We |
60c5eb7d XL |
664 | // don't want to call `op` with these incompatible values. As written, what happens instead |
665 | // is that `op` is called with the adjusted place (that for `*&Some(3)`) and the pattern | |
666 | // `Some(x)` (which matches). Recursing once more, `*&Some(3)` and the pattern `Some(x)` | |
667 | // result in the place `Downcast<Some>(*&Some(3)).0` associated to `x` and invoke `op` with | |
668 | // that (where the `ref` on `x` is implied). | |
f035d41b | 669 | op(&place_with_id, pat); |
60c5eb7d XL |
670 | |
671 | match pat.kind { | |
c295e0f8 | 672 | PatKind::Tuple(subpats, dots_pos) => { |
3dfed10e XL |
673 | // (p1, ..., pN) |
674 | let total_fields = self.total_fields_in_tuple(pat.hir_id, pat.span)?; | |
675 | ||
676 | for (i, subpat) in subpats.iter().enumerate_and_adjust(total_fields, dots_pos) { | |
c295e0f8 | 677 | let subpat_ty = self.pat_ty_adjusted(subpat)?; |
3dfed10e XL |
678 | let projection_kind = ProjectionKind::Field(i as u32, VariantIdx::new(0)); |
679 | let sub_place = | |
680 | self.cat_projection(pat, place_with_id.clone(), subpat_ty, projection_kind); | |
c295e0f8 | 681 | self.cat_pattern_(sub_place, subpat, op)?; |
60c5eb7d XL |
682 | } |
683 | } | |
684 | ||
c295e0f8 | 685 | PatKind::TupleStruct(ref qpath, subpats, dots_pos) => { |
3dfed10e XL |
686 | // S(p1, ..., pN) |
687 | let variant_index = self.variant_index_for_adt(qpath, pat.hir_id, pat.span)?; | |
688 | let total_fields = | |
689 | self.total_fields_in_adt_variant(pat.hir_id, variant_index, pat.span)?; | |
690 | ||
691 | for (i, subpat) in subpats.iter().enumerate_and_adjust(total_fields, dots_pos) { | |
c295e0f8 | 692 | let subpat_ty = self.pat_ty_adjusted(subpat)?; |
3dfed10e XL |
693 | let projection_kind = ProjectionKind::Field(i as u32, variant_index); |
694 | let sub_place = | |
695 | self.cat_projection(pat, place_with_id.clone(), subpat_ty, projection_kind); | |
c295e0f8 | 696 | self.cat_pattern_(sub_place, subpat, op)?; |
3dfed10e XL |
697 | } |
698 | } | |
699 | ||
700 | PatKind::Struct(ref qpath, field_pats, _) => { | |
60c5eb7d | 701 | // S { f1: p1, ..., fN: pN } |
3dfed10e XL |
702 | |
703 | let variant_index = self.variant_index_for_adt(qpath, pat.hir_id, pat.span)?; | |
704 | ||
60c5eb7d | 705 | for fp in field_pats { |
c295e0f8 | 706 | let field_ty = self.pat_ty_adjusted(fp.pat)?; |
3dfed10e XL |
707 | let field_index = self |
708 | .typeck_results | |
709 | .field_indices() | |
710 | .get(fp.hir_id) | |
711 | .cloned() | |
712 | .expect("no index for a field"); | |
713 | ||
714 | let field_place = self.cat_projection( | |
715 | pat, | |
716 | place_with_id.clone(), | |
717 | field_ty, | |
718 | ProjectionKind::Field(field_index as u32, variant_index), | |
719 | ); | |
c295e0f8 | 720 | self.cat_pattern_(field_place, fp.pat, op)?; |
60c5eb7d XL |
721 | } |
722 | } | |
723 | ||
dfeec247 | 724 | PatKind::Or(pats) => { |
60c5eb7d | 725 | for pat in pats { |
c295e0f8 | 726 | self.cat_pattern_(place_with_id.clone(), pat, op)?; |
60c5eb7d XL |
727 | } |
728 | } | |
729 | ||
730 | PatKind::Binding(.., Some(ref subpat)) => { | |
c295e0f8 | 731 | self.cat_pattern_(place_with_id, subpat, op)?; |
60c5eb7d XL |
732 | } |
733 | ||
734 | PatKind::Box(ref subpat) | PatKind::Ref(ref subpat, _) => { | |
735 | // box p1, &p1, &mut p1. we can ignore the mutability of | |
736 | // PatKind::Ref since that information is already contained | |
737 | // in the type. | |
f035d41b | 738 | let subplace = self.cat_deref(pat, place_with_id)?; |
c295e0f8 | 739 | self.cat_pattern_(subplace, subpat, op)?; |
60c5eb7d XL |
740 | } |
741 | ||
dfeec247 | 742 | PatKind::Slice(before, ref slice, after) => { |
5e7ed085 FG |
743 | let Some(element_ty) = place_with_id.place.ty().builtin_index() else { |
744 | debug!("explicit index of non-indexable type {:?}", place_with_id); | |
745 | return Err(()); | |
60c5eb7d | 746 | }; |
3dfed10e XL |
747 | let elt_place = self.cat_projection( |
748 | pat, | |
749 | place_with_id.clone(), | |
750 | element_ty, | |
751 | ProjectionKind::Index, | |
752 | ); | |
60c5eb7d | 753 | for before_pat in before { |
c295e0f8 | 754 | self.cat_pattern_(elt_place.clone(), before_pat, op)?; |
60c5eb7d XL |
755 | } |
756 | if let Some(ref slice_pat) = *slice { | |
c295e0f8 | 757 | let slice_pat_ty = self.pat_ty_adjusted(slice_pat)?; |
3dfed10e XL |
758 | let slice_place = self.cat_projection( |
759 | pat, | |
760 | place_with_id, | |
761 | slice_pat_ty, | |
762 | ProjectionKind::Subslice, | |
763 | ); | |
c295e0f8 | 764 | self.cat_pattern_(slice_place, slice_pat, op)?; |
60c5eb7d XL |
765 | } |
766 | for after_pat in after { | |
c295e0f8 | 767 | self.cat_pattern_(elt_place.clone(), after_pat, op)?; |
60c5eb7d XL |
768 | } |
769 | } | |
770 | ||
dfeec247 XL |
771 | PatKind::Path(_) |
772 | | PatKind::Binding(.., None) | |
773 | | PatKind::Lit(..) | |
774 | | PatKind::Range(..) | |
775 | | PatKind::Wild => { | |
60c5eb7d XL |
776 | // always ok |
777 | } | |
778 | } | |
779 | ||
780 | Ok(()) | |
781 | } | |
782 | } |