]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_hir_typeck/src/mem_categorization.rs
New upstream version 1.71.1+dfsg1
[rustc.git] / compiler / rustc_hir_typeck / src / mem_categorization.rs
CommitLineData
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 51use rustc_middle::hir::place::*;
ba9703b0
XL
52use rustc_middle::ty::adjustment;
53use rustc_middle::ty::fold::TypeFoldable;
9ffffee4 54use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
74b04a01 55
60c5eb7d 56use rustc_data_structures::fx::FxIndexMap;
dfeec247 57use rustc_hir as hir;
3dfed10e 58use rustc_hir::def::{CtorOf, DefKind, Res};
f9f354fc 59use rustc_hir::def_id::LocalDefId;
3dfed10e 60use rustc_hir::pat_util::EnumerateAndAdjustIterator;
dfeec247 61use rustc_hir::PatKind;
74b04a01 62use rustc_infer::infer::InferCtxt;
dfeec247 63use rustc_span::Span;
353b0b11 64use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT};
ba9703b0 65use rustc_trait_selection::infer::InferCtxtExt;
60c5eb7d 66
923072b8 67pub(crate) trait HirNode {
60c5eb7d
XL
68 fn hir_id(&self) -> hir::HirId;
69 fn span(&self) -> Span;
70}
71
dfeec247
XL
72impl 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
81impl 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
91pub(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 99pub(crate) type McResult<T> = Result<T, ()>;
60c5eb7d
XL
100
101impl<'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}