]>
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): | |
04454e1e FG |
12 | //! ```ignore (not-rust) |
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 | //! ``` | |
60c5eb7d XL |
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 | |
923072b8 | 68 | pub(crate) trait HirNode { |
60c5eb7d XL |
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)] | |
923072b8 FG |
92 | pub(crate) struct MemCategorizationContext<'a, 'tcx> { |
93 | pub(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 | ||
923072b8 | 100 | pub(crate) type McResult<T> = Result<T, ()>; |
60c5eb7d XL |
101 | |
102 | impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { | |
103 | /// Creates a `MemCategorizationContext`. | |
923072b8 | 104 | pub(crate) fn new( |
60c5eb7d XL |
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 | ||
923072b8 | 119 | pub(crate) fn tcx(&self) -> TyCtxt<'tcx> { |
60c5eb7d XL |
120 | self.infcx.tcx |
121 | } | |
122 | ||
923072b8 | 123 | pub(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 | ||
923072b8 | 165 | pub(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 | ||
923072b8 | 173 | pub(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>). | |
923072b8 | 187 | pub(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 | ||
923072b8 | 239 | pub(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 | ||
923072b8 | 258 | pub(crate) fn cat_expr_adjusted( |
dfeec247 XL |
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 | ||
923072b8 FG |
301 | pub(crate) fn cat_expr_unadjusted( |
302 | &self, | |
303 | expr: &hir::Expr<'_>, | |
304 | ) -> McResult<PlaceWithHirId<'tcx>> { | |
60c5eb7d XL |
305 | debug!("cat_expr: id={} expr={:?}", expr.hir_id, expr); |
306 | ||
307 | let expr_ty = self.expr_ty(expr)?; | |
308 | match expr.kind { | |
6a06907d | 309 | hir::ExprKind::Unary(hir::UnOp::Deref, ref e_base) => { |
3dfed10e | 310 | if self.typeck_results.is_method_call(expr) { |
60c5eb7d XL |
311 | self.cat_overloaded_place(expr, e_base) |
312 | } else { | |
c295e0f8 | 313 | let base = self.cat_expr(e_base)?; |
60c5eb7d XL |
314 | self.cat_deref(expr, base) |
315 | } | |
316 | } | |
317 | ||
318 | hir::ExprKind::Field(ref base, _) => { | |
c295e0f8 | 319 | let base = self.cat_expr(base)?; |
dfeec247 | 320 | debug!("cat_expr(cat_field): id={} expr={:?} base={:?}", expr.hir_id, expr, base); |
3dfed10e XL |
321 | |
322 | let field_idx = self | |
323 | .typeck_results | |
324 | .field_indices() | |
325 | .get(expr.hir_id) | |
326 | .cloned() | |
327 | .expect("Field index not found"); | |
328 | ||
329 | Ok(self.cat_projection( | |
330 | expr, | |
331 | base, | |
332 | expr_ty, | |
333 | ProjectionKind::Field(field_idx as u32, VariantIdx::new(0)), | |
334 | )) | |
60c5eb7d XL |
335 | } |
336 | ||
337 | hir::ExprKind::Index(ref base, _) => { | |
3dfed10e | 338 | if self.typeck_results.is_method_call(expr) { |
60c5eb7d XL |
339 | // If this is an index implemented by a method call, then it |
340 | // will include an implicit deref of the result. | |
341 | // The call to index() returns a `&T` value, which | |
342 | // is an rvalue. That is what we will be | |
343 | // dereferencing. | |
344 | self.cat_overloaded_place(expr, base) | |
345 | } else { | |
c295e0f8 | 346 | let base = self.cat_expr(base)?; |
3dfed10e | 347 | Ok(self.cat_projection(expr, base, expr_ty, ProjectionKind::Index)) |
60c5eb7d XL |
348 | } |
349 | } | |
350 | ||
351 | hir::ExprKind::Path(ref qpath) => { | |
3dfed10e | 352 | let res = self.typeck_results.qpath_res(qpath, expr.hir_id); |
60c5eb7d XL |
353 | self.cat_res(expr.hir_id, expr.span, expr_ty, res) |
354 | } | |
355 | ||
c295e0f8 | 356 | hir::ExprKind::Type(ref e, _) => self.cat_expr(e), |
dfeec247 XL |
357 | |
358 | hir::ExprKind::AddrOf(..) | |
359 | | hir::ExprKind::Call(..) | |
360 | | hir::ExprKind::Assign(..) | |
361 | | hir::ExprKind::AssignOp(..) | |
923072b8 | 362 | | hir::ExprKind::Closure { .. } |
dfeec247 XL |
363 | | hir::ExprKind::Ret(..) |
364 | | hir::ExprKind::Unary(..) | |
365 | | hir::ExprKind::Yield(..) | |
366 | | hir::ExprKind::MethodCall(..) | |
367 | | hir::ExprKind::Cast(..) | |
368 | | hir::ExprKind::DropTemps(..) | |
369 | | hir::ExprKind::Array(..) | |
5869c6ff | 370 | | hir::ExprKind::If(..) |
dfeec247 XL |
371 | | hir::ExprKind::Tup(..) |
372 | | hir::ExprKind::Binary(..) | |
373 | | hir::ExprKind::Block(..) | |
94222f64 | 374 | | hir::ExprKind::Let(..) |
dfeec247 XL |
375 | | hir::ExprKind::Loop(..) |
376 | | hir::ExprKind::Match(..) | |
377 | | hir::ExprKind::Lit(..) | |
29967ef6 | 378 | | hir::ExprKind::ConstBlock(..) |
dfeec247 XL |
379 | | hir::ExprKind::Break(..) |
380 | | hir::ExprKind::Continue(..) | |
381 | | hir::ExprKind::Struct(..) | |
382 | | hir::ExprKind::Repeat(..) | |
f9f354fc | 383 | | hir::ExprKind::InlineAsm(..) |
dfeec247 XL |
384 | | hir::ExprKind::Box(..) |
385 | | hir::ExprKind::Err => Ok(self.cat_rvalue(expr.hir_id, expr.span, expr_ty)), | |
60c5eb7d XL |
386 | } |
387 | } | |
388 | ||
923072b8 | 389 | pub(crate) fn cat_res( |
dfeec247 XL |
390 | &self, |
391 | hir_id: hir::HirId, | |
392 | span: Span, | |
393 | expr_ty: Ty<'tcx>, | |
394 | res: Res, | |
f035d41b | 395 | ) -> McResult<PlaceWithHirId<'tcx>> { |
dfeec247 | 396 | debug!("cat_res: id={:?} expr={:?} def={:?}", hir_id, expr_ty, res); |
60c5eb7d XL |
397 | |
398 | match res { | |
ba9703b0 XL |
399 | Res::Def( |
400 | DefKind::Ctor(..) | |
401 | | DefKind::Const | |
402 | | DefKind::ConstParam | |
403 | | DefKind::AssocConst | |
404 | | DefKind::Fn | |
405 | | DefKind::AssocFn, | |
406 | _, | |
407 | ) | |
dfeec247 | 408 | | Res::SelfCtor(..) => Ok(self.cat_rvalue(hir_id, span, expr_ty)), |
60c5eb7d | 409 | |
5e7ed085 | 410 | Res::Def(DefKind::Static(_), _) => { |
f035d41b XL |
411 | Ok(PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::StaticItem, Vec::new())) |
412 | } | |
60c5eb7d XL |
413 | |
414 | Res::Local(var_id) => { | |
415 | if self.upvars.map_or(false, |upvars| upvars.contains_key(&var_id)) { | |
f035d41b | 416 | self.cat_upvar(hir_id, var_id) |
60c5eb7d | 417 | } else { |
f035d41b | 418 | Ok(PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Local(var_id), Vec::new())) |
60c5eb7d XL |
419 | } |
420 | } | |
421 | ||
dfeec247 | 422 | def => span_bug!(span, "unexpected definition in memory categorization: {:?}", def), |
60c5eb7d XL |
423 | } |
424 | } | |
425 | ||
426 | /// Categorize an upvar. | |
427 | /// | |
428 | /// Note: the actual upvar access contains invisible derefs of closure | |
429 | /// environment and upvar reference as appropriate. Only regionck cares | |
430 | /// about these dereferences, so we let it compute them as needed. | |
f035d41b | 431 | fn cat_upvar(&self, hir_id: hir::HirId, var_id: hir::HirId) -> McResult<PlaceWithHirId<'tcx>> { |
60c5eb7d XL |
432 | let closure_expr_def_id = self.body_owner; |
433 | ||
434 | let upvar_id = ty::UpvarId { | |
435 | var_path: ty::UpvarPath { hir_id: var_id }, | |
f9f354fc | 436 | closure_expr_id: closure_expr_def_id, |
60c5eb7d XL |
437 | }; |
438 | let var_ty = self.node_ty(var_id)?; | |
439 | ||
f035d41b | 440 | let ret = PlaceWithHirId::new(hir_id, var_ty, PlaceBase::Upvar(upvar_id), Vec::new()); |
60c5eb7d XL |
441 | |
442 | debug!("cat_upvar ret={:?}", ret); | |
443 | Ok(ret) | |
444 | } | |
445 | ||
923072b8 | 446 | pub(crate) fn cat_rvalue( |
f035d41b XL |
447 | &self, |
448 | hir_id: hir::HirId, | |
449 | span: Span, | |
450 | expr_ty: Ty<'tcx>, | |
451 | ) -> PlaceWithHirId<'tcx> { | |
60c5eb7d | 452 | debug!("cat_rvalue hir_id={:?}, expr_ty={:?}, span={:?}", hir_id, expr_ty, span); |
f035d41b | 453 | let ret = PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Rvalue, Vec::new()); |
60c5eb7d XL |
454 | debug!("cat_rvalue ret={:?}", ret); |
455 | ret | |
456 | } | |
457 | ||
923072b8 | 458 | pub(crate) fn cat_projection<N: HirNode>( |
60c5eb7d XL |
459 | &self, |
460 | node: &N, | |
f035d41b | 461 | base_place: PlaceWithHirId<'tcx>, |
60c5eb7d | 462 | ty: Ty<'tcx>, |
3dfed10e | 463 | kind: ProjectionKind, |
f035d41b XL |
464 | ) -> PlaceWithHirId<'tcx> { |
465 | let mut projections = base_place.place.projections; | |
5869c6ff | 466 | projections.push(Projection { kind, ty }); |
f035d41b XL |
467 | let ret = PlaceWithHirId::new( |
468 | node.hir_id(), | |
469 | base_place.place.base_ty, | |
470 | base_place.place.base, | |
60c5eb7d | 471 | projections, |
f035d41b | 472 | ); |
60c5eb7d XL |
473 | debug!("cat_field ret {:?}", ret); |
474 | ret | |
475 | } | |
476 | ||
477 | fn cat_overloaded_place( | |
478 | &self, | |
dfeec247 XL |
479 | expr: &hir::Expr<'_>, |
480 | base: &hir::Expr<'_>, | |
f035d41b | 481 | ) -> McResult<PlaceWithHirId<'tcx>> { |
60c5eb7d XL |
482 | debug!("cat_overloaded_place(expr={:?}, base={:?})", expr, base); |
483 | ||
484 | // Reconstruct the output assuming it's a reference with the | |
485 | // same region and mutability as the receiver. This holds for | |
486 | // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`. | |
487 | let place_ty = self.expr_ty(expr)?; | |
488 | let base_ty = self.expr_ty_adjusted(base)?; | |
489 | ||
5e7ed085 FG |
490 | let ty::Ref(region, _, mutbl) = *base_ty.kind() else { |
491 | span_bug!(expr.span, "cat_overloaded_place: base is not a reference"); | |
60c5eb7d | 492 | }; |
dfeec247 | 493 | let ref_ty = self.tcx().mk_ref(region, ty::TypeAndMut { ty: place_ty, mutbl }); |
60c5eb7d XL |
494 | |
495 | let base = self.cat_rvalue(expr.hir_id, expr.span, ref_ty); | |
496 | self.cat_deref(expr, base) | |
497 | } | |
498 | ||
f035d41b XL |
499 | fn cat_deref( |
500 | &self, | |
501 | node: &impl HirNode, | |
502 | base_place: PlaceWithHirId<'tcx>, | |
503 | ) -> McResult<PlaceWithHirId<'tcx>> { | |
60c5eb7d XL |
504 | debug!("cat_deref: base_place={:?}", base_place); |
505 | ||
f035d41b XL |
506 | let base_curr_ty = base_place.place.ty(); |
507 | let deref_ty = match base_curr_ty.builtin_deref(true) { | |
60c5eb7d XL |
508 | Some(mt) => mt.ty, |
509 | None => { | |
f035d41b | 510 | debug!("explicit deref of non-derefable type: {:?}", base_curr_ty); |
60c5eb7d XL |
511 | return Err(()); |
512 | } | |
513 | }; | |
f035d41b XL |
514 | let mut projections = base_place.place.projections; |
515 | projections.push(Projection { kind: ProjectionKind::Deref, ty: deref_ty }); | |
516 | ||
517 | let ret = PlaceWithHirId::new( | |
518 | node.hir_id(), | |
519 | base_place.place.base_ty, | |
520 | base_place.place.base, | |
60c5eb7d | 521 | projections, |
f035d41b | 522 | ); |
60c5eb7d XL |
523 | debug!("cat_deref ret {:?}", ret); |
524 | Ok(ret) | |
525 | } | |
526 | ||
923072b8 | 527 | pub(crate) fn cat_pattern<F>( |
dfeec247 | 528 | &self, |
f035d41b | 529 | place: PlaceWithHirId<'tcx>, |
dfeec247 XL |
530 | pat: &hir::Pat<'_>, |
531 | mut op: F, | |
532 | ) -> McResult<()> | |
533 | where | |
f035d41b | 534 | F: FnMut(&PlaceWithHirId<'tcx>, &hir::Pat<'_>), |
60c5eb7d XL |
535 | { |
536 | self.cat_pattern_(place, pat, &mut op) | |
537 | } | |
538 | ||
3dfed10e XL |
539 | /// Returns the variant index for an ADT used within a Struct or TupleStruct pattern |
540 | /// Here `pat_hir_id` is the HirId of the pattern itself. | |
541 | fn variant_index_for_adt( | |
542 | &self, | |
543 | qpath: &hir::QPath<'_>, | |
544 | pat_hir_id: hir::HirId, | |
545 | span: Span, | |
546 | ) -> McResult<VariantIdx> { | |
547 | let res = self.typeck_results.qpath_res(qpath, pat_hir_id); | |
548 | let ty = self.typeck_results.node_type(pat_hir_id); | |
5e7ed085 FG |
549 | let ty::Adt(adt_def, _) = ty.kind() else { |
550 | self.tcx() | |
551 | .sess | |
552 | .delay_span_bug(span, "struct or tuple struct pattern not applied to an ADT"); | |
553 | return Err(()); | |
3dfed10e XL |
554 | }; |
555 | ||
556 | match res { | |
557 | Res::Def(DefKind::Variant, variant_id) => Ok(adt_def.variant_index_with_id(variant_id)), | |
558 | Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_id) => { | |
559 | Ok(adt_def.variant_index_with_ctor_id(variant_ctor_id)) | |
560 | } | |
561 | Res::Def(DefKind::Ctor(CtorOf::Struct, ..), _) | |
562 | | Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _) | |
563 | | Res::SelfCtor(..) | |
5099ac24 | 564 | | Res::SelfTy { .. } => { |
3dfed10e XL |
565 | // Structs and Unions have only have one variant. |
566 | Ok(VariantIdx::new(0)) | |
567 | } | |
568 | _ => bug!("expected ADT path, found={:?}", res), | |
569 | } | |
570 | } | |
571 | ||
572 | /// Returns the total number of fields in an ADT variant used within a pattern. | |
573 | /// Here `pat_hir_id` is the HirId of the pattern itself. | |
574 | fn total_fields_in_adt_variant( | |
575 | &self, | |
576 | pat_hir_id: hir::HirId, | |
577 | variant_index: VariantIdx, | |
578 | span: Span, | |
579 | ) -> McResult<usize> { | |
580 | let ty = self.typeck_results.node_type(pat_hir_id); | |
1b1a35ee | 581 | match ty.kind() { |
5e7ed085 | 582 | ty::Adt(adt_def, _) => Ok(adt_def.variant(variant_index).fields.len()), |
3dfed10e XL |
583 | _ => { |
584 | self.tcx() | |
585 | .sess | |
586 | .delay_span_bug(span, "struct or tuple struct pattern not applied to an ADT"); | |
587 | Err(()) | |
588 | } | |
589 | } | |
590 | } | |
591 | ||
592 | /// Returns the total number of fields in a tuple used within a Tuple pattern. | |
593 | /// Here `pat_hir_id` is the HirId of the pattern itself. | |
594 | fn total_fields_in_tuple(&self, pat_hir_id: hir::HirId, span: Span) -> McResult<usize> { | |
595 | let ty = self.typeck_results.node_type(pat_hir_id); | |
1b1a35ee | 596 | match ty.kind() { |
3dfed10e XL |
597 | ty::Tuple(substs) => Ok(substs.len()), |
598 | _ => { | |
599 | self.tcx().sess.delay_span_bug(span, "tuple pattern not applied to a tuple"); | |
600 | Err(()) | |
601 | } | |
602 | } | |
603 | } | |
604 | ||
60c5eb7d | 605 | // FIXME(#19596) This is a workaround, but there should be a better way to do this |
dfeec247 XL |
606 | fn cat_pattern_<F>( |
607 | &self, | |
f035d41b | 608 | mut place_with_id: PlaceWithHirId<'tcx>, |
dfeec247 XL |
609 | pat: &hir::Pat<'_>, |
610 | op: &mut F, | |
611 | ) -> McResult<()> | |
612 | where | |
f035d41b | 613 | F: FnMut(&PlaceWithHirId<'tcx>, &hir::Pat<'_>), |
60c5eb7d | 614 | { |
f035d41b | 615 | // Here, `place` is the `PlaceWithHirId` being matched and pat is the pattern it |
60c5eb7d XL |
616 | // is being matched against. |
617 | // | |
618 | // In general, the way that this works is that we walk down the pattern, | |
f035d41b | 619 | // constructing a `PlaceWithHirId` that represents the path that will be taken |
60c5eb7d XL |
620 | // to reach the value being matched. |
621 | ||
f035d41b | 622 | debug!("cat_pattern(pat={:?}, place_with_id={:?})", pat, place_with_id); |
60c5eb7d | 623 | |
f035d41b XL |
624 | // If (pattern) adjustments are active for this pattern, adjust the `PlaceWithHirId` correspondingly. |
625 | // `PlaceWithHirId`s are constructed differently from patterns. For example, in | |
60c5eb7d XL |
626 | // |
627 | // ``` | |
628 | // match foo { | |
629 | // &&Some(x, ) => { ... }, | |
630 | // _ => { ... }, | |
631 | // } | |
632 | // ``` | |
633 | // | |
634 | // the pattern `&&Some(x,)` is represented as `Ref { Ref { TupleStruct }}`. To build the | |
f035d41b | 635 | // corresponding `PlaceWithHirId` we start with the `PlaceWithHirId` for `foo`, and then, by traversing the |
60c5eb7d XL |
636 | // pattern, try to answer the question: given the address of `foo`, how is `x` reached? |
637 | // | |
638 | // `&&Some(x,)` `place_foo` | |
639 | // `&Some(x,)` `deref { place_foo}` | |
640 | // `Some(x,)` `deref { deref { place_foo }}` | |
641 | // (x,)` `field0 { deref { deref { place_foo }}}` <- resulting place | |
642 | // | |
643 | // The above example has no adjustments. If the code were instead the (after adjustments, | |
644 | // equivalent) version | |
645 | // | |
646 | // ``` | |
647 | // match foo { | |
648 | // Some(x, ) => { ... }, | |
649 | // _ => { ... }, | |
650 | // } | |
651 | // ``` | |
652 | // | |
653 | // Then we see that to get the same result, we must start with | |
654 | // `deref { deref { place_foo }}` instead of `place_foo` since the pattern is now `Some(x,)` | |
655 | // and not `&&Some(x,)`, even though its assigned type is that of `&&Some(x,)`. | |
5869c6ff | 656 | for _ in 0..self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(0, |v| v.len()) { |
f035d41b XL |
657 | debug!("cat_pattern: applying adjustment to place_with_id={:?}", place_with_id); |
658 | place_with_id = self.cat_deref(pat, place_with_id)?; | |
60c5eb7d | 659 | } |
f035d41b XL |
660 | let place_with_id = place_with_id; // lose mutability |
661 | debug!("cat_pattern: applied adjustment derefs to get place_with_id={:?}", place_with_id); | |
60c5eb7d | 662 | |
f035d41b | 663 | // Invoke the callback, but only now, after the `place_with_id` has adjusted. |
60c5eb7d XL |
664 | // |
665 | // To see that this makes sense, consider `match &Some(3) { Some(x) => { ... }}`. In that | |
f035d41b | 666 | // case, the initial `place_with_id` will be that for `&Some(3)` and the pattern is `Some(x)`. We |
60c5eb7d XL |
667 | // don't want to call `op` with these incompatible values. As written, what happens instead |
668 | // is that `op` is called with the adjusted place (that for `*&Some(3)`) and the pattern | |
669 | // `Some(x)` (which matches). Recursing once more, `*&Some(3)` and the pattern `Some(x)` | |
670 | // result in the place `Downcast<Some>(*&Some(3)).0` associated to `x` and invoke `op` with | |
671 | // that (where the `ref` on `x` is implied). | |
f035d41b | 672 | op(&place_with_id, pat); |
60c5eb7d XL |
673 | |
674 | match pat.kind { | |
c295e0f8 | 675 | PatKind::Tuple(subpats, dots_pos) => { |
3dfed10e XL |
676 | // (p1, ..., pN) |
677 | let total_fields = self.total_fields_in_tuple(pat.hir_id, pat.span)?; | |
678 | ||
679 | for (i, subpat) in subpats.iter().enumerate_and_adjust(total_fields, dots_pos) { | |
c295e0f8 | 680 | let subpat_ty = self.pat_ty_adjusted(subpat)?; |
3dfed10e XL |
681 | let projection_kind = ProjectionKind::Field(i as u32, VariantIdx::new(0)); |
682 | let sub_place = | |
683 | self.cat_projection(pat, place_with_id.clone(), subpat_ty, projection_kind); | |
c295e0f8 | 684 | self.cat_pattern_(sub_place, subpat, op)?; |
60c5eb7d XL |
685 | } |
686 | } | |
687 | ||
c295e0f8 | 688 | PatKind::TupleStruct(ref qpath, subpats, dots_pos) => { |
3dfed10e XL |
689 | // S(p1, ..., pN) |
690 | let variant_index = self.variant_index_for_adt(qpath, pat.hir_id, pat.span)?; | |
691 | let total_fields = | |
692 | self.total_fields_in_adt_variant(pat.hir_id, variant_index, pat.span)?; | |
693 | ||
694 | for (i, subpat) in subpats.iter().enumerate_and_adjust(total_fields, dots_pos) { | |
c295e0f8 | 695 | let subpat_ty = self.pat_ty_adjusted(subpat)?; |
3dfed10e XL |
696 | let projection_kind = ProjectionKind::Field(i as u32, variant_index); |
697 | let sub_place = | |
698 | self.cat_projection(pat, place_with_id.clone(), subpat_ty, projection_kind); | |
c295e0f8 | 699 | self.cat_pattern_(sub_place, subpat, op)?; |
3dfed10e XL |
700 | } |
701 | } | |
702 | ||
703 | PatKind::Struct(ref qpath, field_pats, _) => { | |
60c5eb7d | 704 | // S { f1: p1, ..., fN: pN } |
3dfed10e XL |
705 | |
706 | let variant_index = self.variant_index_for_adt(qpath, pat.hir_id, pat.span)?; | |
707 | ||
60c5eb7d | 708 | for fp in field_pats { |
c295e0f8 | 709 | let field_ty = self.pat_ty_adjusted(fp.pat)?; |
3dfed10e XL |
710 | let field_index = self |
711 | .typeck_results | |
712 | .field_indices() | |
713 | .get(fp.hir_id) | |
714 | .cloned() | |
715 | .expect("no index for a field"); | |
716 | ||
717 | let field_place = self.cat_projection( | |
718 | pat, | |
719 | place_with_id.clone(), | |
720 | field_ty, | |
721 | ProjectionKind::Field(field_index as u32, variant_index), | |
722 | ); | |
c295e0f8 | 723 | self.cat_pattern_(field_place, fp.pat, op)?; |
60c5eb7d XL |
724 | } |
725 | } | |
726 | ||
dfeec247 | 727 | PatKind::Or(pats) => { |
60c5eb7d | 728 | for pat in pats { |
c295e0f8 | 729 | self.cat_pattern_(place_with_id.clone(), pat, op)?; |
60c5eb7d XL |
730 | } |
731 | } | |
732 | ||
733 | PatKind::Binding(.., Some(ref subpat)) => { | |
c295e0f8 | 734 | self.cat_pattern_(place_with_id, subpat, op)?; |
60c5eb7d XL |
735 | } |
736 | ||
737 | PatKind::Box(ref subpat) | PatKind::Ref(ref subpat, _) => { | |
738 | // box p1, &p1, &mut p1. we can ignore the mutability of | |
739 | // PatKind::Ref since that information is already contained | |
740 | // in the type. | |
f035d41b | 741 | let subplace = self.cat_deref(pat, place_with_id)?; |
c295e0f8 | 742 | self.cat_pattern_(subplace, subpat, op)?; |
60c5eb7d XL |
743 | } |
744 | ||
dfeec247 | 745 | PatKind::Slice(before, ref slice, after) => { |
5e7ed085 FG |
746 | let Some(element_ty) = place_with_id.place.ty().builtin_index() else { |
747 | debug!("explicit index of non-indexable type {:?}", place_with_id); | |
748 | return Err(()); | |
60c5eb7d | 749 | }; |
3dfed10e XL |
750 | let elt_place = self.cat_projection( |
751 | pat, | |
752 | place_with_id.clone(), | |
753 | element_ty, | |
754 | ProjectionKind::Index, | |
755 | ); | |
60c5eb7d | 756 | for before_pat in before { |
c295e0f8 | 757 | self.cat_pattern_(elt_place.clone(), before_pat, op)?; |
60c5eb7d XL |
758 | } |
759 | if let Some(ref slice_pat) = *slice { | |
c295e0f8 | 760 | let slice_pat_ty = self.pat_ty_adjusted(slice_pat)?; |
3dfed10e XL |
761 | let slice_place = self.cat_projection( |
762 | pat, | |
763 | place_with_id, | |
764 | slice_pat_ty, | |
765 | ProjectionKind::Subslice, | |
766 | ); | |
c295e0f8 | 767 | self.cat_pattern_(slice_place, slice_pat, op)?; |
60c5eb7d XL |
768 | } |
769 | for after_pat in after { | |
c295e0f8 | 770 | self.cat_pattern_(elt_place.clone(), after_pat, op)?; |
60c5eb7d XL |
771 | } |
772 | } | |
773 | ||
dfeec247 XL |
774 | PatKind::Path(_) |
775 | | PatKind::Binding(.., None) | |
776 | | PatKind::Lit(..) | |
777 | | PatKind::Range(..) | |
778 | | PatKind::Wild => { | |
60c5eb7d XL |
779 | // always ok |
780 | } | |
781 | } | |
782 | ||
783 | Ok(()) | |
784 | } | |
785 | } |