]>
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; | |
9ffffee4 | 54 | use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; |
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; |
74b04a01 | 62 | use rustc_infer::infer::InferCtxt; |
dfeec247 | 63 | use rustc_span::Span; |
353b0b11 | 64 | use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT}; |
ba9703b0 | 65 | use rustc_trait_selection::infer::InferCtxtExt; |
60c5eb7d | 66 | |
923072b8 | 67 | pub(crate) trait HirNode { |
60c5eb7d XL |
68 | fn hir_id(&self) -> hir::HirId; |
69 | fn span(&self) -> Span; | |
70 | } | |
71 | ||
dfeec247 XL |
72 | impl HirNode for hir::Expr<'_> { |
73 | fn hir_id(&self) -> hir::HirId { | |
74 | self.hir_id | |
75 | } | |
76 | fn span(&self) -> Span { | |
77 | self.span | |
78 | } | |
60c5eb7d XL |
79 | } |
80 | ||
dfeec247 XL |
81 | impl HirNode for hir::Pat<'_> { |
82 | fn hir_id(&self) -> hir::HirId { | |
83 | self.hir_id | |
84 | } | |
85 | fn span(&self) -> Span { | |
86 | self.span | |
87 | } | |
60c5eb7d XL |
88 | } |
89 | ||
90 | #[derive(Clone)] | |
923072b8 FG |
91 | pub(crate) struct MemCategorizationContext<'a, 'tcx> { |
92 | pub(crate) typeck_results: &'a ty::TypeckResults<'tcx>, | |
2b03887a | 93 | infcx: &'a InferCtxt<'tcx>, |
60c5eb7d | 94 | param_env: ty::ParamEnv<'tcx>, |
f9f354fc | 95 | body_owner: LocalDefId, |
60c5eb7d XL |
96 | upvars: Option<&'tcx FxIndexMap<hir::HirId, hir::Upvar>>, |
97 | } | |
98 | ||
923072b8 | 99 | pub(crate) type McResult<T> = Result<T, ()>; |
60c5eb7d XL |
100 | |
101 | impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { | |
102 | /// Creates a `MemCategorizationContext`. | |
923072b8 | 103 | pub(crate) fn new( |
2b03887a | 104 | infcx: &'a InferCtxt<'tcx>, |
60c5eb7d | 105 | param_env: ty::ParamEnv<'tcx>, |
f9f354fc | 106 | body_owner: LocalDefId, |
3dfed10e | 107 | typeck_results: &'a ty::TypeckResults<'tcx>, |
60c5eb7d XL |
108 | ) -> MemCategorizationContext<'a, 'tcx> { |
109 | MemCategorizationContext { | |
3dfed10e | 110 | typeck_results, |
60c5eb7d XL |
111 | infcx, |
112 | param_env, | |
113 | body_owner, | |
f9f354fc | 114 | upvars: infcx.tcx.upvars_mentioned(body_owner), |
60c5eb7d XL |
115 | } |
116 | } | |
117 | ||
923072b8 | 118 | pub(crate) fn tcx(&self) -> TyCtxt<'tcx> { |
60c5eb7d XL |
119 | self.infcx.tcx |
120 | } | |
121 | ||
353b0b11 FG |
122 | pub(crate) fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool { |
123 | self.infcx.type_is_copy_modulo_regions(self.param_env, ty) | |
60c5eb7d XL |
124 | } |
125 | ||
fc512014 | 126 | fn resolve_vars_if_possible<T>(&self, value: T) -> T |
dfeec247 | 127 | where |
9ffffee4 | 128 | T: TypeFoldable<TyCtxt<'tcx>>, |
60c5eb7d XL |
129 | { |
130 | self.infcx.resolve_vars_if_possible(value) | |
131 | } | |
132 | ||
133 | fn is_tainted_by_errors(&self) -> bool { | |
487cf647 | 134 | self.infcx.tainted_by_errors().is_some() |
60c5eb7d XL |
135 | } |
136 | ||
dfeec247 XL |
137 | fn resolve_type_vars_or_error( |
138 | &self, | |
139 | id: hir::HirId, | |
140 | ty: Option<Ty<'tcx>>, | |
141 | ) -> McResult<Ty<'tcx>> { | |
60c5eb7d XL |
142 | match ty { |
143 | Some(ty) => { | |
fc512014 | 144 | let ty = self.resolve_vars_if_possible(ty); |
60c5eb7d XL |
145 | if ty.references_error() || ty.is_ty_var() { |
146 | debug!("resolve_type_vars_or_error: error from {:?}", ty); | |
147 | Err(()) | |
148 | } else { | |
149 | Ok(ty) | |
150 | } | |
151 | } | |
152 | // FIXME | |
153 | None if self.is_tainted_by_errors() => Err(()), | |
154 | None => { | |
dfeec247 | 155 | bug!( |
9ffffee4 | 156 | "no type for node {} in mem_categorization", |
dfeec247 XL |
157 | self.tcx().hir().node_to_string(id) |
158 | ); | |
60c5eb7d XL |
159 | } |
160 | } | |
161 | } | |
162 | ||
923072b8 | 163 | pub(crate) fn node_ty(&self, hir_id: hir::HirId) -> McResult<Ty<'tcx>> { |
3dfed10e | 164 | self.resolve_type_vars_or_error(hir_id, self.typeck_results.node_type_opt(hir_id)) |
60c5eb7d XL |
165 | } |
166 | ||
dfeec247 | 167 | fn expr_ty(&self, expr: &hir::Expr<'_>) -> McResult<Ty<'tcx>> { |
3dfed10e | 168 | self.resolve_type_vars_or_error(expr.hir_id, self.typeck_results.expr_ty_opt(expr)) |
60c5eb7d XL |
169 | } |
170 | ||
923072b8 | 171 | pub(crate) fn expr_ty_adjusted(&self, expr: &hir::Expr<'_>) -> McResult<Ty<'tcx>> { |
3dfed10e | 172 | self.resolve_type_vars_or_error(expr.hir_id, self.typeck_results.expr_ty_adjusted_opt(expr)) |
60c5eb7d XL |
173 | } |
174 | ||
175 | /// Returns the type of value that this pattern matches against. | |
176 | /// Some non-obvious cases: | |
177 | /// | |
178 | /// - a `ref x` binding matches against a value of type `T` and gives | |
179 | /// `x` the type `&T`; we return `T`. | |
180 | /// - a pattern with implicit derefs (thanks to default binding | |
181 | /// modes #42640) may look like `Some(x)` but in fact have | |
182 | /// implicit deref patterns attached (e.g., it is really | |
183 | /// `&Some(x)`). In that case, we return the "outermost" type | |
2b03887a | 184 | /// (e.g., `&Option<T>`). |
923072b8 | 185 | pub(crate) fn pat_ty_adjusted(&self, pat: &hir::Pat<'_>) -> McResult<Ty<'tcx>> { |
60c5eb7d XL |
186 | // Check for implicit `&` types wrapping the pattern; note |
187 | // that these are never attached to binding patterns, so | |
188 | // actually this is somewhat "disjoint" from the code below | |
189 | // that aims to account for `ref x`. | |
3dfed10e | 190 | if let Some(vec) = self.typeck_results.pat_adjustments().get(pat.hir_id) { |
60c5eb7d XL |
191 | if let Some(first_ty) = vec.first() { |
192 | debug!("pat_ty(pat={:?}) found adjusted ty `{:?}`", pat, first_ty); | |
5099ac24 | 193 | return Ok(*first_ty); |
60c5eb7d XL |
194 | } |
195 | } | |
196 | ||
197 | self.pat_ty_unadjusted(pat) | |
198 | } | |
199 | ||
60c5eb7d | 200 | /// Like `pat_ty`, but ignores implicit `&` patterns. |
dfeec247 | 201 | fn pat_ty_unadjusted(&self, pat: &hir::Pat<'_>) -> McResult<Ty<'tcx>> { |
60c5eb7d XL |
202 | let base_ty = self.node_ty(pat.hir_id)?; |
203 | debug!("pat_ty(pat={:?}) base_ty={:?}", pat, base_ty); | |
204 | ||
205 | // This code detects whether we are looking at a `ref x`, | |
206 | // and if so, figures out what the type *being borrowed* is. | |
207 | let ret_ty = match pat.kind { | |
208 | PatKind::Binding(..) => { | |
3dfed10e XL |
209 | let bm = *self |
210 | .typeck_results | |
211 | .pat_binding_modes() | |
212 | .get(pat.hir_id) | |
213 | .expect("missing binding mode"); | |
60c5eb7d XL |
214 | |
215 | if let ty::BindByReference(_) = bm { | |
216 | // a bind-by-ref means that the base_ty will be the type of the ident itself, | |
217 | // but what we want here is the type of the underlying value being borrowed. | |
218 | // So peel off one-level, turning the &T into T. | |
219 | match base_ty.builtin_deref(false) { | |
220 | Some(t) => t.ty, | |
221 | None => { | |
222 | debug!("By-ref binding of non-derefable type {:?}", base_ty); | |
223 | return Err(()); | |
224 | } | |
225 | } | |
226 | } else { | |
227 | base_ty | |
228 | } | |
229 | } | |
230 | _ => base_ty, | |
231 | }; | |
232 | debug!("pat_ty(pat={:?}) ret_ty={:?}", pat, ret_ty); | |
233 | ||
234 | Ok(ret_ty) | |
235 | } | |
236 | ||
923072b8 | 237 | pub(crate) fn cat_expr(&self, expr: &hir::Expr<'_>) -> McResult<PlaceWithHirId<'tcx>> { |
60c5eb7d XL |
238 | // This recursion helper avoids going through *too many* |
239 | // adjustments, since *only* non-overloaded deref recurses. | |
240 | fn helper<'a, 'tcx>( | |
241 | mc: &MemCategorizationContext<'a, 'tcx>, | |
dfeec247 | 242 | expr: &hir::Expr<'_>, |
60c5eb7d | 243 | adjustments: &[adjustment::Adjustment<'tcx>], |
f035d41b | 244 | ) -> McResult<PlaceWithHirId<'tcx>> { |
60c5eb7d XL |
245 | match adjustments.split_last() { |
246 | None => mc.cat_expr_unadjusted(expr), | |
247 | Some((adjustment, previous)) => { | |
248 | mc.cat_expr_adjusted_with(expr, || helper(mc, expr, previous), adjustment) | |
249 | } | |
250 | } | |
251 | } | |
252 | ||
3dfed10e | 253 | helper(self, expr, self.typeck_results.expr_adjustments(expr)) |
60c5eb7d XL |
254 | } |
255 | ||
923072b8 | 256 | pub(crate) fn cat_expr_adjusted( |
dfeec247 XL |
257 | &self, |
258 | expr: &hir::Expr<'_>, | |
f035d41b | 259 | previous: PlaceWithHirId<'tcx>, |
dfeec247 | 260 | adjustment: &adjustment::Adjustment<'tcx>, |
f035d41b | 261 | ) -> McResult<PlaceWithHirId<'tcx>> { |
60c5eb7d XL |
262 | self.cat_expr_adjusted_with(expr, || Ok(previous), adjustment) |
263 | } | |
264 | ||
2b03887a | 265 | #[instrument(level = "debug", skip(self, previous))] |
dfeec247 XL |
266 | fn cat_expr_adjusted_with<F>( |
267 | &self, | |
268 | expr: &hir::Expr<'_>, | |
269 | previous: F, | |
270 | adjustment: &adjustment::Adjustment<'tcx>, | |
f035d41b | 271 | ) -> McResult<PlaceWithHirId<'tcx>> |
dfeec247 | 272 | where |
f035d41b | 273 | F: FnOnce() -> McResult<PlaceWithHirId<'tcx>>, |
60c5eb7d | 274 | { |
fc512014 | 275 | let target = self.resolve_vars_if_possible(adjustment.target); |
60c5eb7d XL |
276 | match adjustment.kind { |
277 | adjustment::Adjust::Deref(overloaded) => { | |
278 | // Equivalent to *expr or something similar. | |
279 | let base = if let Some(deref) = overloaded { | |
dfeec247 XL |
280 | let ref_ty = self |
281 | .tcx() | |
282 | .mk_ref(deref.region, ty::TypeAndMut { ty: target, mutbl: deref.mutbl }); | |
60c5eb7d XL |
283 | self.cat_rvalue(expr.hir_id, expr.span, ref_ty) |
284 | } else { | |
285 | previous()? | |
286 | }; | |
287 | self.cat_deref(expr, base) | |
288 | } | |
289 | ||
dfeec247 XL |
290 | adjustment::Adjust::NeverToAny |
291 | | adjustment::Adjust::Pointer(_) | |
2b03887a FG |
292 | | adjustment::Adjust::Borrow(_) |
293 | | adjustment::Adjust::DynStar => { | |
60c5eb7d XL |
294 | // Result is an rvalue. |
295 | Ok(self.cat_rvalue(expr.hir_id, expr.span, target)) | |
296 | } | |
297 | } | |
298 | } | |
299 | ||
2b03887a | 300 | #[instrument(level = "debug", skip(self))] |
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, | |
353b0b11 | 333 | ProjectionKind::Field(field_idx, FIRST_VARIANT), |
3dfed10e | 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(..) |
49aad941 | 384 | | hir::ExprKind::OffsetOf(..) |
9ffffee4 | 385 | | hir::ExprKind::Err(_) => Ok(self.cat_rvalue(expr.hir_id, expr.span, expr_ty)), |
60c5eb7d XL |
386 | } |
387 | } | |
388 | ||
2b03887a | 389 | #[instrument(level = "debug", skip(self, span))] |
923072b8 | 390 | pub(crate) fn cat_res( |
dfeec247 XL |
391 | &self, |
392 | hir_id: hir::HirId, | |
393 | span: Span, | |
394 | expr_ty: Ty<'tcx>, | |
395 | res: Res, | |
f035d41b | 396 | ) -> McResult<PlaceWithHirId<'tcx>> { |
60c5eb7d | 397 | match res { |
ba9703b0 XL |
398 | Res::Def( |
399 | DefKind::Ctor(..) | |
400 | | DefKind::Const | |
401 | | DefKind::ConstParam | |
402 | | DefKind::AssocConst | |
403 | | DefKind::Fn | |
404 | | DefKind::AssocFn, | |
405 | _, | |
406 | ) | |
dfeec247 | 407 | | Res::SelfCtor(..) => Ok(self.cat_rvalue(hir_id, span, expr_ty)), |
60c5eb7d | 408 | |
5e7ed085 | 409 | Res::Def(DefKind::Static(_), _) => { |
f035d41b XL |
410 | Ok(PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::StaticItem, Vec::new())) |
411 | } | |
60c5eb7d XL |
412 | |
413 | Res::Local(var_id) => { | |
49aad941 | 414 | if self.upvars.is_some_and(|upvars| upvars.contains_key(&var_id)) { |
f035d41b | 415 | self.cat_upvar(hir_id, var_id) |
60c5eb7d | 416 | } else { |
f035d41b | 417 | Ok(PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Local(var_id), Vec::new())) |
60c5eb7d XL |
418 | } |
419 | } | |
420 | ||
dfeec247 | 421 | def => span_bug!(span, "unexpected definition in memory categorization: {:?}", def), |
60c5eb7d XL |
422 | } |
423 | } | |
424 | ||
425 | /// Categorize an upvar. | |
426 | /// | |
427 | /// Note: the actual upvar access contains invisible derefs of closure | |
428 | /// environment and upvar reference as appropriate. Only regionck cares | |
429 | /// about these dereferences, so we let it compute them as needed. | |
f035d41b | 430 | fn cat_upvar(&self, hir_id: hir::HirId, var_id: hir::HirId) -> McResult<PlaceWithHirId<'tcx>> { |
60c5eb7d XL |
431 | let closure_expr_def_id = self.body_owner; |
432 | ||
433 | let upvar_id = ty::UpvarId { | |
434 | var_path: ty::UpvarPath { hir_id: var_id }, | |
f9f354fc | 435 | closure_expr_id: closure_expr_def_id, |
60c5eb7d XL |
436 | }; |
437 | let var_ty = self.node_ty(var_id)?; | |
438 | ||
f035d41b | 439 | let ret = PlaceWithHirId::new(hir_id, var_ty, PlaceBase::Upvar(upvar_id), Vec::new()); |
60c5eb7d XL |
440 | |
441 | debug!("cat_upvar ret={:?}", ret); | |
442 | Ok(ret) | |
443 | } | |
444 | ||
923072b8 | 445 | pub(crate) fn cat_rvalue( |
f035d41b XL |
446 | &self, |
447 | hir_id: hir::HirId, | |
448 | span: Span, | |
449 | expr_ty: Ty<'tcx>, | |
450 | ) -> PlaceWithHirId<'tcx> { | |
60c5eb7d | 451 | debug!("cat_rvalue hir_id={:?}, expr_ty={:?}, span={:?}", hir_id, expr_ty, span); |
f035d41b | 452 | let ret = PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Rvalue, Vec::new()); |
60c5eb7d XL |
453 | debug!("cat_rvalue ret={:?}", ret); |
454 | ret | |
455 | } | |
456 | ||
923072b8 | 457 | pub(crate) fn cat_projection<N: HirNode>( |
60c5eb7d XL |
458 | &self, |
459 | node: &N, | |
f035d41b | 460 | base_place: PlaceWithHirId<'tcx>, |
60c5eb7d | 461 | ty: Ty<'tcx>, |
3dfed10e | 462 | kind: ProjectionKind, |
f035d41b XL |
463 | ) -> PlaceWithHirId<'tcx> { |
464 | let mut projections = base_place.place.projections; | |
5869c6ff | 465 | projections.push(Projection { kind, ty }); |
f035d41b XL |
466 | let ret = PlaceWithHirId::new( |
467 | node.hir_id(), | |
468 | base_place.place.base_ty, | |
469 | base_place.place.base, | |
60c5eb7d | 470 | projections, |
f035d41b | 471 | ); |
60c5eb7d XL |
472 | debug!("cat_field ret {:?}", ret); |
473 | ret | |
474 | } | |
475 | ||
2b03887a | 476 | #[instrument(level = "debug", skip(self))] |
60c5eb7d XL |
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 | // Reconstruct the output assuming it's a reference with the |
483 | // same region and mutability as the receiver. This holds for | |
484 | // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`. | |
485 | let place_ty = self.expr_ty(expr)?; | |
486 | let base_ty = self.expr_ty_adjusted(base)?; | |
487 | ||
5e7ed085 FG |
488 | let ty::Ref(region, _, mutbl) = *base_ty.kind() else { |
489 | span_bug!(expr.span, "cat_overloaded_place: base is not a reference"); | |
60c5eb7d | 490 | }; |
dfeec247 | 491 | let ref_ty = self.tcx().mk_ref(region, ty::TypeAndMut { ty: place_ty, mutbl }); |
60c5eb7d XL |
492 | |
493 | let base = self.cat_rvalue(expr.hir_id, expr.span, ref_ty); | |
494 | self.cat_deref(expr, base) | |
495 | } | |
496 | ||
2b03887a | 497 | #[instrument(level = "debug", skip(self, node))] |
f035d41b XL |
498 | fn cat_deref( |
499 | &self, | |
500 | node: &impl HirNode, | |
501 | base_place: PlaceWithHirId<'tcx>, | |
502 | ) -> McResult<PlaceWithHirId<'tcx>> { | |
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 | ||
923072b8 | 524 | pub(crate) fn cat_pattern<F>( |
dfeec247 | 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(..) | |
2b03887a FG |
561 | | Res::SelfTyParam { .. } |
562 | | Res::SelfTyAlias { .. } => { | |
3dfed10e | 563 | // Structs and Unions have only have one variant. |
353b0b11 | 564 | Ok(FIRST_VARIANT) |
3dfed10e XL |
565 | } |
566 | _ => bug!("expected ADT path, found={:?}", res), | |
567 | } | |
568 | } | |
569 | ||
570 | /// Returns the total number of fields in an ADT variant used within a pattern. | |
571 | /// Here `pat_hir_id` is the HirId of the pattern itself. | |
572 | fn total_fields_in_adt_variant( | |
573 | &self, | |
574 | pat_hir_id: hir::HirId, | |
575 | variant_index: VariantIdx, | |
576 | span: Span, | |
577 | ) -> McResult<usize> { | |
578 | let ty = self.typeck_results.node_type(pat_hir_id); | |
1b1a35ee | 579 | match ty.kind() { |
5e7ed085 | 580 | ty::Adt(adt_def, _) => Ok(adt_def.variant(variant_index).fields.len()), |
3dfed10e XL |
581 | _ => { |
582 | self.tcx() | |
583 | .sess | |
584 | .delay_span_bug(span, "struct or tuple struct pattern not applied to an ADT"); | |
585 | Err(()) | |
586 | } | |
587 | } | |
588 | } | |
589 | ||
590 | /// Returns the total number of fields in a tuple used within a Tuple pattern. | |
591 | /// Here `pat_hir_id` is the HirId of the pattern itself. | |
592 | fn total_fields_in_tuple(&self, pat_hir_id: hir::HirId, span: Span) -> McResult<usize> { | |
593 | let ty = self.typeck_results.node_type(pat_hir_id); | |
1b1a35ee | 594 | match ty.kind() { |
3dfed10e XL |
595 | ty::Tuple(substs) => Ok(substs.len()), |
596 | _ => { | |
597 | self.tcx().sess.delay_span_bug(span, "tuple pattern not applied to a tuple"); | |
598 | Err(()) | |
599 | } | |
600 | } | |
601 | } | |
602 | ||
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 }}` | |
9ffffee4 | 638 | // `(x,)` `field0 { deref { deref { place_foo }}}` <- resulting place |
60c5eb7d XL |
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)?; |
353b0b11 FG |
678 | let projection_kind = |
679 | ProjectionKind::Field(FieldIdx::from_usize(i), FIRST_VARIANT); | |
3dfed10e XL |
680 | let sub_place = |
681 | self.cat_projection(pat, place_with_id.clone(), subpat_ty, projection_kind); | |
c295e0f8 | 682 | self.cat_pattern_(sub_place, subpat, op)?; |
60c5eb7d XL |
683 | } |
684 | } | |
685 | ||
c295e0f8 | 686 | PatKind::TupleStruct(ref qpath, subpats, dots_pos) => { |
3dfed10e XL |
687 | // S(p1, ..., pN) |
688 | let variant_index = self.variant_index_for_adt(qpath, pat.hir_id, pat.span)?; | |
689 | let total_fields = | |
690 | self.total_fields_in_adt_variant(pat.hir_id, variant_index, pat.span)?; | |
691 | ||
692 | for (i, subpat) in subpats.iter().enumerate_and_adjust(total_fields, dots_pos) { | |
c295e0f8 | 693 | let subpat_ty = self.pat_ty_adjusted(subpat)?; |
353b0b11 FG |
694 | let projection_kind = |
695 | ProjectionKind::Field(FieldIdx::from_usize(i), variant_index); | |
3dfed10e XL |
696 | let sub_place = |
697 | self.cat_projection(pat, place_with_id.clone(), subpat_ty, projection_kind); | |
c295e0f8 | 698 | self.cat_pattern_(sub_place, subpat, op)?; |
3dfed10e XL |
699 | } |
700 | } | |
701 | ||
702 | PatKind::Struct(ref qpath, field_pats, _) => { | |
60c5eb7d | 703 | // S { f1: p1, ..., fN: pN } |
3dfed10e XL |
704 | |
705 | let variant_index = self.variant_index_for_adt(qpath, pat.hir_id, pat.span)?; | |
706 | ||
60c5eb7d | 707 | for fp in field_pats { |
c295e0f8 | 708 | let field_ty = self.pat_ty_adjusted(fp.pat)?; |
3dfed10e XL |
709 | let field_index = self |
710 | .typeck_results | |
711 | .field_indices() | |
712 | .get(fp.hir_id) | |
713 | .cloned() | |
714 | .expect("no index for a field"); | |
715 | ||
716 | let field_place = self.cat_projection( | |
717 | pat, | |
718 | place_with_id.clone(), | |
719 | field_ty, | |
353b0b11 | 720 | ProjectionKind::Field(field_index, variant_index), |
3dfed10e | 721 | ); |
c295e0f8 | 722 | self.cat_pattern_(field_place, fp.pat, op)?; |
60c5eb7d XL |
723 | } |
724 | } | |
725 | ||
dfeec247 | 726 | PatKind::Or(pats) => { |
60c5eb7d | 727 | for pat in pats { |
c295e0f8 | 728 | self.cat_pattern_(place_with_id.clone(), pat, op)?; |
60c5eb7d XL |
729 | } |
730 | } | |
731 | ||
732 | PatKind::Binding(.., Some(ref subpat)) => { | |
c295e0f8 | 733 | self.cat_pattern_(place_with_id, subpat, op)?; |
60c5eb7d XL |
734 | } |
735 | ||
736 | PatKind::Box(ref subpat) | PatKind::Ref(ref subpat, _) => { | |
9c376795 | 737 | // box p1, &p1, &mut p1. we can ignore the mutability of |
60c5eb7d XL |
738 | // PatKind::Ref since that information is already contained |
739 | // in the type. | |
f035d41b | 740 | let subplace = self.cat_deref(pat, place_with_id)?; |
c295e0f8 | 741 | self.cat_pattern_(subplace, subpat, op)?; |
60c5eb7d XL |
742 | } |
743 | ||
dfeec247 | 744 | PatKind::Slice(before, ref slice, after) => { |
5e7ed085 FG |
745 | let Some(element_ty) = place_with_id.place.ty().builtin_index() else { |
746 | debug!("explicit index of non-indexable type {:?}", place_with_id); | |
747 | return Err(()); | |
60c5eb7d | 748 | }; |
3dfed10e XL |
749 | let elt_place = self.cat_projection( |
750 | pat, | |
751 | place_with_id.clone(), | |
752 | element_ty, | |
753 | ProjectionKind::Index, | |
754 | ); | |
60c5eb7d | 755 | for before_pat in before { |
c295e0f8 | 756 | self.cat_pattern_(elt_place.clone(), before_pat, op)?; |
60c5eb7d XL |
757 | } |
758 | if let Some(ref slice_pat) = *slice { | |
c295e0f8 | 759 | let slice_pat_ty = self.pat_ty_adjusted(slice_pat)?; |
3dfed10e XL |
760 | let slice_place = self.cat_projection( |
761 | pat, | |
762 | place_with_id, | |
763 | slice_pat_ty, | |
764 | ProjectionKind::Subslice, | |
765 | ); | |
c295e0f8 | 766 | self.cat_pattern_(slice_place, slice_pat, op)?; |
60c5eb7d XL |
767 | } |
768 | for after_pat in after { | |
c295e0f8 | 769 | self.cat_pattern_(elt_place.clone(), after_pat, op)?; |
60c5eb7d XL |
770 | } |
771 | } | |
772 | ||
dfeec247 XL |
773 | PatKind::Path(_) |
774 | | PatKind::Binding(.., None) | |
775 | | PatKind::Lit(..) | |
776 | | PatKind::Range(..) | |
777 | | PatKind::Wild => { | |
60c5eb7d XL |
778 | // always ok |
779 | } | |
780 | } | |
781 | ||
782 | Ok(()) | |
783 | } | |
784 | } |