hir_ty/mir.rs
1//! MIR definitions and implementation
2
3use std::{fmt::Display, iter};
4
5use hir_def::{
6 FieldId, LocalFieldId, StaticId, UnionId, VariantId,
7 hir::{BindingId, Expr, ExprId, Ordering, PatId},
8};
9use la_arena::{Arena, ArenaMap, Idx, RawIdx};
10use macros::{TypeFoldable, TypeVisitable};
11use rustc_ast_ir::Mutability;
12use rustc_type_ir::{
13 GenericTypeVisitable,
14 inherent::{GenericArgs as _, IntoKind, Ty as _},
15};
16use salsa::SalsaValue;
17use smallvec::{SmallVec, smallvec};
18use stdx::impl_from;
19
20use crate::{
21 CallableDefId, InferBodyId, InferenceResult, MemoryMap, ThinFxHashMap,
22 db::{HirDatabase, InternedClosureId},
23 infer::PointerCast,
24 next_solver::{
25 Allocation, AllocationData, DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv,
26 StoredAllocation, StoredConst, StoredGenericArgs, StoredTy, Ty, TyKind,
27 infer::{InferCtxt, traits::ObligationCause},
28 interned_slice,
29 obligation_ctxt::ObligationCtxt,
30 },
31};
32
33mod eval;
34mod lower;
35mod monomorphization;
36
37pub use eval::{
38 Evaluator, IsSigned, MirEvalError, VTableMap, interpret_mir, pad16,
39 render_const_using_debug_impl,
40};
41pub use lower::{
42 MirLowerError, lower_body_to_mir, lower_to_mir_with_store, mir_body_for_closure_query,
43 mir_body_query,
44};
45pub use monomorphization::{
46 monomorphized_mir_body_for_closure_query, monomorphized_mir_body_query,
47};
48
49pub type BasicBlockId = Idx<BasicBlock>;
50pub type LocalId = Idx<Local>;
51
52fn return_slot() -> LocalId {
53 LocalId::from_raw(RawIdx::from(0))
54}
55
56#[derive(Debug, Clone, PartialEq, Eq, Hash)]
57pub struct Local {
58 pub ty: StoredTy,
59}
60
61/// An operand in MIR represents a "value" in Rust, the definition of which is undecided and part of
62/// the memory model. One proposal for a definition of values can be found [on UCG][value-def].
63///
64/// [value-def]: https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/value-domain.md
65///
66/// The most common way to create values is via loading a place. Loading a place is an operation
67/// which reads the memory of the place and converts it to a value. This is a fundamentally *typed*
68/// operation. The nature of the value produced depends on the type of the conversion. Furthermore,
69/// there may be other effects: if the type has a validity constraint loading the place might be UB
70/// if the validity constraint is not met.
71///
72/// **Needs clarification:** Ralf proposes that loading a place not have side-effects.
73/// This is what is implemented in miri today. Are these the semantics we want for MIR? Is this
74/// something we can even decide without knowing more about Rust's memory model?
75///
76/// **Needs clarification:** Is loading a place that has its variant index set well-formed? Miri
77/// currently implements it, but it seems like this may be something to check against in the
78/// validator.
79#[derive(Debug, PartialEq, Eq, Clone)]
80pub struct Operand {
81 pub kind: OperandKind,
82 // FIXME : This should actually just be of type `MirSpan`.
83 span: Option<MirSpan>,
84}
85
86#[derive(Debug, PartialEq, Eq, Clone)]
87pub enum OperandKind {
88 /// Creates a value by loading the given place.
89 ///
90 /// Before drop elaboration, the type of the place must be `Copy`. After drop elaboration there
91 /// is no such requirement.
92 Copy(StoredPlace),
93
94 /// Creates a value by performing loading the place, just like the `Copy` operand.
95 ///
96 /// This *may* additionally overwrite the place with `uninit` bytes, depending on how we decide
97 /// in [UCG#188]. You should not emit MIR that may attempt a subsequent second load of this
98 /// place without first re-initializing it.
99 ///
100 /// [UCG#188]: https://github.com/rust-lang/unsafe-code-guidelines/issues/188
101 Move(StoredPlace),
102 /// Constants are already semantically values, and remain unchanged.
103 Constant {
104 konst: StoredConst,
105 ty: StoredTy,
106 },
107 Allocation {
108 allocation: StoredAllocation,
109 },
110 /// NON STANDARD: This kind of operand returns an immutable reference to that static memory. Rustc
111 /// handles it with the `Constant` variant somehow.
112 Static(StaticId),
113}
114
115impl<'db> Operand {
116 fn from_concrete_const(data: Box<[u8]>, memory_map: MemoryMap<'db>, ty: Ty<'db>) -> Self {
117 Operand {
118 kind: OperandKind::Allocation {
119 allocation: Allocation::new(AllocationData { ty, memory: data, memory_map })
120 .store(),
121 },
122 span: None,
123 }
124 }
125
126 fn from_bytes(data: Box<[u8]>, ty: Ty<'db>) -> Self {
127 Operand::from_concrete_const(data, MemoryMap::default(), ty)
128 }
129
130 fn const_zst(ty: Ty<'db>) -> Operand {
131 Self::from_bytes(Box::default(), ty)
132 }
133
134 fn from_fn(
135 db: &'db dyn HirDatabase,
136 func_id: hir_def::FunctionId,
137 generic_args: GenericArgs<'db>,
138 ) -> Operand {
139 let interner = DbInterner::new_no_crate(db);
140 let ty = Ty::new_fn_def(interner, CallableDefId::FunctionId(func_id).into(), generic_args);
141 Operand::from_bytes(Box::default(), ty)
142 }
143}
144
145/// The index of a field (whether of a struct/enum variant, tuple, or closure).
146/// For a struct/enum it converts from and to the LocalFieldId, for a tuple or closure it's simply the index.
147#[derive(Copy, Clone, PartialEq, Eq, Hash, salsa::SalsaValue, PartialOrd, Ord, Debug)]
148pub struct FieldIndex(pub u32);
149
150impl FieldIndex {
151 pub fn to_local_field_id(self) -> LocalFieldId {
152 LocalFieldId::from_raw(RawIdx::from_u32(self.0))
153 }
154}
155
156impl From<LocalFieldId> for FieldIndex {
157 fn from(value: LocalFieldId) -> Self {
158 FieldIndex(value.into_raw().into_u32())
159 }
160}
161
162#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
163pub enum ProjectionElem<V: PartialEq> {
164 Deref,
165 /// A field (e.g., `f` in `_1.f`).
166 Field(FieldIndex),
167 /// Index into a slice/array.
168 Index(V),
169 /// These indices are generated by slice patterns.
170 ConstantIndex {
171 offset: u64,
172 from_end: bool,
173 },
174 /// These indices are generated by slice patterns.
175 Subslice {
176 from: u64,
177 to: u64,
178 },
179 /// "Downcast" to a variant of an enum or a coroutine.
180 Downcast(VariantId),
181}
182
183impl<V: PartialEq> ProjectionElem<V> {
184 pub fn map<V2: PartialEq>(self, v: impl FnOnce(V) -> V2) -> ProjectionElem<V2> {
185 match self {
186 ProjectionElem::Deref => ProjectionElem::Deref,
187 ProjectionElem::Field(field_index) => ProjectionElem::Field(field_index),
188 ProjectionElem::Index(idx) => ProjectionElem::Index(v(idx)),
189 ProjectionElem::ConstantIndex { offset, from_end } => {
190 ProjectionElem::ConstantIndex { offset, from_end }
191 }
192 ProjectionElem::Subslice { from, to } => ProjectionElem::Subslice { from, to },
193 ProjectionElem::Downcast(variant_id) => ProjectionElem::Downcast(variant_id),
194 }
195 }
196
197 pub fn try_map<V2: PartialEq>(
198 self,
199 v: impl FnOnce(V) -> Option<V2>,
200 ) -> Option<ProjectionElem<V2>> {
201 Some(match self {
202 ProjectionElem::Deref => ProjectionElem::Deref,
203 ProjectionElem::Field(field_index) => ProjectionElem::Field(field_index),
204 ProjectionElem::Index(idx) => ProjectionElem::Index(v(idx)?),
205 ProjectionElem::ConstantIndex { offset, from_end } => {
206 ProjectionElem::ConstantIndex { offset, from_end }
207 }
208 ProjectionElem::Subslice { from, to } => ProjectionElem::Subslice { from, to },
209 ProjectionElem::Downcast(variant_id) => ProjectionElem::Downcast(variant_id),
210 })
211 }
212}
213
214pub type PlaceElem = ProjectionElem<LocalId>;
215
216impl<W: crate::next_solver::WorldExposer> GenericTypeVisitable<W> for PlaceElem {
217 fn generic_visit_with(&self, _: &mut W) {}
218}
219
220interned_slice!(ProjectionStorage, Projection, StoredProjection, projection, PlaceElem, PlaceElem);
221
222impl<'db> Projection<'db> {
223 pub fn project(self, projection: PlaceElem) -> Projection<'db> {
224 Projection::new_from_iter(self.as_slice().iter().copied().chain([projection]))
225 }
226}
227
228impl StoredProjection {
229 pub fn as_slice(&self) -> &[PlaceElem] {
230 self.as_ref().as_slice()
231 }
232
233 pub fn is_empty(&self) -> bool {
234 self.as_slice().is_empty()
235 }
236}
237
238#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
239pub struct Place<'db> {
240 pub local: LocalId,
241 pub projection: Projection<'db>,
242}
243
244#[derive(Debug, Clone, PartialEq, Eq, Hash)]
245pub struct StoredPlace {
246 pub local: LocalId,
247 pub projection: StoredProjection,
248}
249
250impl StoredPlace {
251 pub fn as_ref<'db>(&self) -> Place<'db> {
252 Place { local: self.local, projection: self.projection.as_ref() }
253 }
254}
255
256impl<'db> Place<'db> {
257 fn is_parent(&self, child: Place<'db>) -> bool {
258 self.local == child.local
259 && child.projection.as_slice().starts_with(self.projection.as_slice())
260 }
261
262 /// The place itself is not included
263 fn iterate_over_parents<'a>(&'a self) -> impl Iterator<Item = Place<'db>> + 'a {
264 let projection = self.projection.as_slice();
265 (0..projection.len()).map(move |x| Place {
266 local: self.local,
267 projection: Projection::new_from_slice(&projection[0..x]),
268 })
269 }
270
271 fn project(&self, projection: PlaceElem) -> Place<'db> {
272 Place { local: self.local, projection: self.projection.project(projection) }
273 }
274
275 pub fn store(&self) -> StoredPlace {
276 StoredPlace { local: self.local, projection: self.projection.store() }
277 }
278 pub fn ty(
279 &self,
280 body: &MirBody<'db>,
281 infcx: &InferCtxt<'db>,
282 env: ParamEnv<'db>,
283 ) -> PlaceTy<'db> {
284 PlaceTy::from_ty(body.locals[self.local].ty.as_ref()).multi_projection_ty(
285 infcx,
286 env,
287 self.projection.as_slice(),
288 )
289 }
290}
291
292impl<'db> From<LocalId> for Place<'db> {
293 fn from(local: LocalId) -> Self {
294 let empty: &[PlaceElem] = &[];
295 Place { local, projection: Projection::new_from_slice(empty) }
296 }
297}
298
299#[derive(Debug, PartialEq, Eq, Clone)]
300pub enum AggregateKind {
301 /// The type is of the element
302 Array(StoredTy),
303 /// The type is of the tuple
304 Tuple(StoredTy),
305 Adt(VariantId, StoredGenericArgs),
306 Union(UnionId, FieldId),
307 Closure(StoredTy),
308 //Coroutine(LocalDefId, SubstsRef, Movability),
309}
310
311#[derive(Debug, Clone, Hash, PartialEq, Eq)]
312pub struct SwitchTargets {
313 /// Possible values. The locations to branch to in each case
314 /// are found in the corresponding indices from the `targets` vector.
315 values: SmallVec<[u128; 1]>,
316
317 /// Possible branch sites. The last element of this vector is used
318 /// for the otherwise branch, so targets.len() == values.len() + 1
319 /// should hold.
320 //
321 // This invariant is quite non-obvious and also could be improved.
322 // One way to make this invariant is to have something like this instead:
323 //
324 // branches: Vec<(ConstInt, BasicBlock)>,
325 // otherwise: Option<BasicBlock> // exhaustive if None
326 //
327 // However we’ve decided to keep this as-is until we figure a case
328 // where some other approach seems to be strictly better than other.
329 targets: SmallVec<[BasicBlockId; 2]>,
330}
331
332impl SwitchTargets {
333 /// Creates switch targets from an iterator of values and target blocks.
334 ///
335 /// The iterator may be empty, in which case the `SwitchInt` instruction is equivalent to
336 /// `goto otherwise;`.
337 pub fn new(
338 targets: impl Iterator<Item = (u128, BasicBlockId)>,
339 otherwise: BasicBlockId,
340 ) -> Self {
341 let (values, mut targets): (SmallVec<_>, SmallVec<_>) = targets.unzip();
342 targets.push(otherwise);
343 Self { values, targets }
344 }
345
346 /// Builds a switch targets definition that jumps to `then` if the tested value equals `value`,
347 /// and to `else_` if not.
348 pub fn static_if(value: u128, then: BasicBlockId, else_: BasicBlockId) -> Self {
349 Self { values: smallvec![value], targets: smallvec![then, else_] }
350 }
351
352 /// Returns the fallback target that is jumped to when none of the values match the operand.
353 pub fn otherwise(&self) -> BasicBlockId {
354 *self.targets.last().unwrap()
355 }
356
357 /// Returns an iterator over the switch targets.
358 ///
359 /// The iterator will yield tuples containing the value and corresponding target to jump to, not
360 /// including the `otherwise` fallback target.
361 ///
362 /// Note that this may yield 0 elements. Only the `otherwise` branch is mandatory.
363 pub fn iter(&self) -> impl Iterator<Item = (u128, BasicBlockId)> + '_ {
364 iter::zip(&self.values, &self.targets).map(|(x, y)| (*x, *y))
365 }
366
367 /// Returns a slice with all possible jump targets (including the fallback target).
368 pub fn all_targets(&self) -> &[BasicBlockId] {
369 &self.targets
370 }
371
372 /// Finds the `BasicBlock` to which this `SwitchInt` will branch given the
373 /// specific value. This cannot fail, as it'll return the `otherwise`
374 /// branch if there's not a specific match for the value.
375 pub fn target_for_value(&self, value: u128) -> BasicBlockId {
376 self.iter().find_map(|(v, t)| (v == value).then_some(t)).unwrap_or_else(|| self.otherwise())
377 }
378}
379
380#[derive(Debug, PartialEq, Eq, Clone)]
381pub struct Terminator {
382 pub span: MirSpan,
383 pub kind: TerminatorKind,
384}
385
386#[derive(Debug, PartialEq, Eq, Clone)]
387pub enum TerminatorKind {
388 /// Block has one successor; we continue execution there.
389 Goto { target: BasicBlockId },
390
391 /// Switches based on the computed value.
392 ///
393 /// First, evaluates the `discr` operand. The type of the operand must be a signed or unsigned
394 /// integer, char, or bool, and must match the given type. Then, if the list of switch targets
395 /// contains the computed value, continues execution at the associated basic block. Otherwise,
396 /// continues execution at the "otherwise" basic block.
397 ///
398 /// Target values may not appear more than once.
399 SwitchInt {
400 /// The discriminant value being tested.
401 discr: Operand,
402
403 targets: SwitchTargets,
404 },
405
406 /// Indicates that the landing pad is finished and that the process should continue unwinding.
407 ///
408 /// Like a return, this marks the end of this invocation of the function.
409 ///
410 /// Only permitted in cleanup blocks. `Resume` is not permitted with `-C unwind=abort` after
411 /// deaggregation runs.
412 UnwindResume,
413
414 /// Indicates that the landing pad is finished and that the process should abort.
415 ///
416 /// Used to prevent unwinding for foreign items or with `-C unwind=abort`. Only permitted in
417 /// cleanup blocks.
418 Abort,
419
420 /// Returns from the function.
421 ///
422 /// Like function calls, the exact semantics of returns in Rust are unclear. Returning very
423 /// likely at least assigns the value currently in the return place (`_0`) to the place
424 /// specified in the associated `Call` terminator in the calling function, as if assigned via
425 /// `dest = move _0`. It might additionally do other things, like have side-effects in the
426 /// aliasing model.
427 ///
428 /// If the body is a coroutine body, this has slightly different semantics; it instead causes a
429 /// `CoroutineState::Returned(_0)` to be created (as if by an `Aggregate` rvalue) and assigned
430 /// to the return place.
431 Return,
432
433 /// Indicates a terminator that can never be reached.
434 ///
435 /// Executing this terminator is UB.
436 Unreachable,
437
438 /// The behavior of this statement differs significantly before and after drop elaboration.
439 /// After drop elaboration, `Drop` executes the drop glue for the specified place, after which
440 /// it continues execution/unwinds at the given basic blocks. It is possible that executing drop
441 /// glue is special - this would be part of Rust's memory model. (**FIXME**: due we have an
442 /// issue tracking if drop glue has any interesting semantics in addition to those of a function
443 /// call?)
444 ///
445 /// `Drop` before drop elaboration is a *conditional* execution of the drop glue. Specifically, the
446 /// `Drop` will be executed if...
447 ///
448 /// **Needs clarification**: End of that sentence. This in effect should document the exact
449 /// behavior of drop elaboration. The following sounds vaguely right, but I'm not quite sure:
450 ///
451 /// > The drop glue is executed if, among all statements executed within this `Body`, an assignment to
452 /// > the place or one of its "parents" occurred more recently than a move out of it. This does not
453 /// > consider indirect assignments.
454 Drop { place: StoredPlace, target: BasicBlockId, unwind: Option<BasicBlockId> },
455
456 /// Drops the place and assigns a new value to it.
457 ///
458 /// This first performs the exact same operation as the pre drop-elaboration `Drop` terminator;
459 /// it then additionally assigns the `value` to the `place` as if by an assignment statement.
460 /// This assignment occurs both in the unwind and the regular code paths. The semantics are best
461 /// explained by the elaboration:
462 ///
463 /// ```ignore (MIR)
464 /// BB0 {
465 /// DropAndReplace(P <- V, goto BB1, unwind BB2)
466 /// }
467 /// ```
468 ///
469 /// becomes
470 ///
471 /// ```ignore (MIR)
472 /// BB0 {
473 /// Drop(P, goto BB1, unwind BB2)
474 /// }
475 /// BB1 {
476 /// // P is now uninitialized
477 /// P <- V
478 /// }
479 /// BB2 {
480 /// // P is now uninitialized -- its dtor panicked
481 /// P <- V
482 /// }
483 /// ```
484 ///
485 /// Disallowed after drop elaboration.
486 DropAndReplace {
487 place: StoredPlace,
488 value: Operand,
489 target: BasicBlockId,
490 unwind: Option<BasicBlockId>,
491 },
492
493 /// Roughly speaking, evaluates the `func` operand and the arguments, and starts execution of
494 /// the referred to function. The operand types must match the argument types of the function.
495 /// The return place type must match the return type. The type of the `func` operand must be
496 /// callable, meaning either a function pointer, a function type, or a closure type.
497 ///
498 /// **Needs clarification**: The exact semantics of this. Current backends rely on `move`
499 /// operands not aliasing the return place. It is unclear how this is justified in MIR, see
500 /// [#71117].
501 ///
502 /// [#71117]: https://github.com/rust-lang/rust/issues/71117
503 Call {
504 /// The function that’s being called.
505 func: Operand,
506 /// Arguments the function is called with.
507 /// These are owned by the callee, which is free to modify them.
508 /// This allows the memory occupied by "by-value" arguments to be
509 /// reused across function calls without duplicating the contents.
510 args: Box<[Operand]>,
511 /// Where the returned value will be written
512 destination: StoredPlace,
513 /// Where to go after this call returns. If none, the call necessarily diverges.
514 target: Option<BasicBlockId>,
515 /// Cleanups to be done if the call unwinds.
516 cleanup: Option<BasicBlockId>,
517 /// `true` if this is from a call in HIR rather than from an overloaded
518 /// operator. True for overloaded function call.
519 from_hir_call: bool,
520 // This `Span` is the span of the function, without the dot and receiver
521 // (e.g. `foo(a, b)` in `x.foo(a, b)`
522 //fn_span: Span,
523 },
524
525 /// Evaluates the operand, which must have type `bool`. If it is not equal to `expected`,
526 /// initiates a panic. Initiating a panic corresponds to a `Call` terminator with some
527 /// unspecified constant as the function to call, all the operands stored in the `AssertMessage`
528 /// as parameters, and `None` for the destination. Keep in mind that the `cleanup` path is not
529 /// necessarily executed even in the case of a panic, for example in `-C panic=abort`. If the
530 /// assertion does not fail, execution continues at the specified basic block.
531 Assert {
532 cond: Operand,
533 expected: bool,
534 //msg: AssertMessage,
535 target: BasicBlockId,
536 cleanup: Option<BasicBlockId>,
537 },
538
539 /// Marks a suspend point.
540 ///
541 /// Like `Return` terminators in coroutine bodies, this computes `value` and then a
542 /// `CoroutineState::Yielded(value)` as if by `Aggregate` rvalue. That value is then assigned to
543 /// the return place of the function calling this one, and execution continues in the calling
544 /// function. When next invoked with the same first argument, execution of this function
545 /// continues at the `resume` basic block, with the second argument written to the `resume_arg`
546 /// place. If the coroutine is dropped before then, the `drop` basic block is invoked.
547 ///
548 /// Not permitted in bodies that are not coroutine bodies, or after coroutine lowering.
549 ///
550 /// **Needs clarification**: What about the evaluation order of the `resume_arg` and `value`?
551 Yield {
552 /// The value to return.
553 value: Operand,
554 /// Where to resume to.
555 resume: BasicBlockId,
556 /// The place to store the resume argument in.
557 resume_arg: StoredPlace,
558 /// Cleanup to be done if the coroutine is dropped at this suspend point.
559 drop: Option<BasicBlockId>,
560 },
561
562 /// Indicates the end of dropping a coroutine.
563 ///
564 /// Semantically just a `return` (from the coroutines drop glue). Only permitted in the same situations
565 /// as `yield`.
566 ///
567 /// **Needs clarification**: Is that even correct? The coroutine drop code is always confusing
568 /// to me, because it's not even really in the current body.
569 ///
570 /// **Needs clarification**: Are there type system constraints on these terminators? Should
571 /// there be a "block type" like `cleanup` blocks for them?
572 CoroutineDrop,
573
574 /// A block where control flow only ever takes one real path, but borrowck needs to be more
575 /// conservative.
576 ///
577 /// At runtime this is semantically just a goto.
578 ///
579 /// Disallowed after drop elaboration.
580 FalseEdge {
581 /// The target normal control flow will take.
582 real_target: BasicBlockId,
583 /// A block control flow could conceptually jump to, but won't in
584 /// practice.
585 imaginary_target: BasicBlockId,
586 },
587
588 /// A terminator for blocks that only take one path in reality, but where we reserve the right
589 /// to unwind in borrowck, even if it won't happen in practice. This can arise in infinite loops
590 /// with no function calls for example.
591 ///
592 /// At runtime this is semantically just a goto.
593 ///
594 /// Disallowed after drop elaboration.
595 FalseUnwind {
596 /// The target normal control flow will take.
597 real_target: BasicBlockId,
598 /// The imaginary cleanup block link. This particular path will never be taken
599 /// in practice, but in order to avoid fragility we want to always
600 /// consider it in borrowck. We don't want to accept programs which
601 /// pass borrowck only when `panic=abort` or some assertions are disabled
602 /// due to release vs. debug mode builds. This needs to be an `Option` because
603 /// of the `remove_noop_landing_pads` and `abort_unwinding_calls` passes.
604 unwind: Option<BasicBlockId>,
605 },
606}
607
608// Order of variants in this enum matter: they are used to compare borrow kinds.
609#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
610pub enum BorrowKind {
611 /// Data must be immutable and is aliasable.
612 Shared,
613
614 /// The immediately borrowed place must be immutable, but projections from
615 /// it don't need to be. For example, a shallow borrow of `a.b` doesn't
616 /// conflict with a mutable borrow of `a.b.c`.
617 ///
618 /// This is used when lowering matches: when matching on a place we want to
619 /// ensure that place have the same value from the start of the match until
620 /// an arm is selected. This prevents this code from compiling:
621 /// ```compile_fail,E0510
622 /// let mut x = &Some(0);
623 /// match *x {
624 /// None => (),
625 /// Some(_) if { x = &None; false } => (),
626 /// Some(_) => (),
627 /// }
628 /// ```
629 /// This can't be a shared borrow because mutably borrowing (*x as Some).0
630 /// should not prevent `if let None = x { ... }`, for example, because the
631 /// mutating `(*x as Some).0` can't affect the discriminant of `x`.
632 /// We can also report errors with this kind of borrow differently.
633 Shallow,
634
635 /// Data is mutable and not aliasable.
636 Mut { kind: MutBorrowKind },
637}
638
639// Order of variants in this enum matter: they are used to compare borrow kinds.
640#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
641pub enum MutBorrowKind {
642 /// Data must be immutable but not aliasable. This kind of borrow cannot currently
643 /// be expressed by the user and is used only in implicit closure bindings.
644 ClosureCapture,
645 Default,
646 /// This borrow arose from method-call auto-ref
647 /// (i.e., adjustment::Adjust::Borrow).
648 TwoPhasedBorrow,
649}
650
651impl BorrowKind {
652 fn from_hir_mutability(m: hir_def::type_ref::Mutability) -> Self {
653 match m {
654 hir_def::type_ref::Mutability::Shared => BorrowKind::Shared,
655 hir_def::type_ref::Mutability::Mut => BorrowKind::Mut { kind: MutBorrowKind::Default },
656 }
657 }
658
659 fn from_rustc_mutability(m: rustc_ast_ir::Mutability) -> Self {
660 match m {
661 rustc_ast_ir::Mutability::Not => BorrowKind::Shared,
662 rustc_ast_ir::Mutability::Mut => BorrowKind::Mut { kind: MutBorrowKind::Default },
663 }
664 }
665
666 fn from_hir(bk: crate::infer::closure::analysis::BorrowKind) -> Self {
667 match bk {
668 crate::closure_analysis::BorrowKind::Immutable => Self::Shared,
669 crate::closure_analysis::BorrowKind::UniqueImmutable => {
670 Self::Mut { kind: MutBorrowKind::ClosureCapture }
671 }
672 crate::closure_analysis::BorrowKind::Mutable => {
673 Self::Mut { kind: MutBorrowKind::Default }
674 }
675 }
676 }
677}
678
679#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
680pub enum UnOp {
681 /// The `!` operator for logical inversion
682 Not,
683 /// The `-` operator for negation
684 Neg,
685}
686
687#[derive(Debug, PartialEq, Eq, Clone)]
688pub enum BinOp {
689 /// The `+` operator (addition)
690 Add,
691 /// The `-` operator (subtraction)
692 Sub,
693 /// The `*` operator (multiplication)
694 Mul,
695 /// The `/` operator (division)
696 ///
697 /// Division by zero is UB, because the compiler should have inserted checks
698 /// prior to this.
699 Div,
700 /// The `%` operator (modulus)
701 ///
702 /// Using zero as the modulus (second operand) is UB, because the compiler
703 /// should have inserted checks prior to this.
704 Rem,
705 /// The `^` operator (bitwise xor)
706 BitXor,
707 /// The `&` operator (bitwise and)
708 BitAnd,
709 /// The `|` operator (bitwise or)
710 BitOr,
711 /// The `<<` operator (shift left)
712 ///
713 /// The offset is truncated to the size of the first operand before shifting.
714 Shl,
715 /// The `>>` operator (shift right)
716 ///
717 /// The offset is truncated to the size of the first operand before shifting.
718 Shr,
719 /// The `==` operator (equality)
720 Eq,
721 /// The `<` operator (less than)
722 Lt,
723 /// The `<=` operator (less than or equal to)
724 Le,
725 /// The `!=` operator (not equal to)
726 Ne,
727 /// The `>=` operator (greater than or equal to)
728 Ge,
729 /// The `>` operator (greater than)
730 Gt,
731 /// The `ptr.offset` operator
732 Offset,
733}
734
735impl BinOp {
736 fn run_compare<T: PartialEq + PartialOrd>(&self, l: T, r: T) -> bool {
737 match self {
738 BinOp::Ge => l >= r,
739 BinOp::Gt => l > r,
740 BinOp::Le => l <= r,
741 BinOp::Lt => l < r,
742 BinOp::Eq => l == r,
743 BinOp::Ne => l != r,
744 x => panic!("`run_compare` called on operator {x:?}"),
745 }
746 }
747}
748
749impl Display for BinOp {
750 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
751 f.write_str(match self {
752 BinOp::Add => "+",
753 BinOp::Sub => "-",
754 BinOp::Mul => "*",
755 BinOp::Div => "/",
756 BinOp::Rem => "%",
757 BinOp::BitXor => "^",
758 BinOp::BitAnd => "&",
759 BinOp::BitOr => "|",
760 BinOp::Shl => "<<",
761 BinOp::Shr => ">>",
762 BinOp::Eq => "==",
763 BinOp::Lt => "<",
764 BinOp::Le => "<=",
765 BinOp::Ne => "!=",
766 BinOp::Ge => ">=",
767 BinOp::Gt => ">",
768 BinOp::Offset => "`offset`",
769 })
770 }
771}
772
773impl From<hir_def::hir::ArithOp> for BinOp {
774 fn from(value: hir_def::hir::ArithOp) -> Self {
775 match value {
776 hir_def::hir::ArithOp::Add => BinOp::Add,
777 hir_def::hir::ArithOp::Mul => BinOp::Mul,
778 hir_def::hir::ArithOp::Sub => BinOp::Sub,
779 hir_def::hir::ArithOp::Div => BinOp::Div,
780 hir_def::hir::ArithOp::Rem => BinOp::Rem,
781 hir_def::hir::ArithOp::Shl => BinOp::Shl,
782 hir_def::hir::ArithOp::Shr => BinOp::Shr,
783 hir_def::hir::ArithOp::BitXor => BinOp::BitXor,
784 hir_def::hir::ArithOp::BitOr => BinOp::BitOr,
785 hir_def::hir::ArithOp::BitAnd => BinOp::BitAnd,
786 }
787 }
788}
789
790impl From<hir_def::hir::CmpOp> for BinOp {
791 fn from(value: hir_def::hir::CmpOp) -> Self {
792 match value {
793 hir_def::hir::CmpOp::Eq { negated: false } => BinOp::Eq,
794 hir_def::hir::CmpOp::Eq { negated: true } => BinOp::Ne,
795 hir_def::hir::CmpOp::Ord { ordering: Ordering::Greater, strict: false } => BinOp::Ge,
796 hir_def::hir::CmpOp::Ord { ordering: Ordering::Greater, strict: true } => BinOp::Gt,
797 hir_def::hir::CmpOp::Ord { ordering: Ordering::Less, strict: false } => BinOp::Le,
798 hir_def::hir::CmpOp::Ord { ordering: Ordering::Less, strict: true } => BinOp::Lt,
799 }
800 }
801}
802
803impl From<Operand> for Rvalue {
804 fn from(x: Operand) -> Self {
805 Self::Use(x)
806 }
807}
808
809#[derive(Debug, PartialEq, Eq, Clone)]
810pub enum CastKind {
811 /// An exposing pointer to address cast. A cast between a pointer and an integer type, or
812 /// between a function pointer and an integer type.
813 /// See the docs on `expose_addr` for more details.
814 PointerExposeAddress,
815 /// An address-to-pointer cast that picks up an exposed provenance.
816 /// See the docs on `from_exposed_addr` for more details.
817 PointerFromExposedAddress,
818 /// All sorts of pointer-to-pointer casts. Note that reference-to-raw-ptr casts are
819 /// translated into `&raw mut/const *r`, i.e., they are not actually casts.
820 PtrToPtr,
821 /// Pointer related casts that are done by coercions.
822 PointerCoercion(PointerCast),
823 /// Cast into a dyn* object.
824 DynStar,
825 IntToInt,
826 FloatToInt,
827 FloatToFloat,
828 IntToFloat,
829 FnPtrToPtr,
830}
831
832#[derive(Debug, PartialEq, Eq, Clone)]
833pub enum Rvalue {
834 /// Yields the operand unchanged
835 Use(Operand),
836
837 /// Creates an array where each element is the value of the operand.
838 ///
839 /// Corresponds to source code like `[x; 32]`.
840 Repeat(Operand, StoredConst),
841
842 /// Creates a reference of the indicated kind to the place.
843 ///
844 /// There is not much to document here, because besides the obvious parts the semantics of this
845 /// are essentially entirely a part of the aliasing model. There are many UCG issues discussing
846 /// exactly what the behavior of this operation should be.
847 ///
848 /// `Shallow` borrows are disallowed after drop lowering.
849 Ref(BorrowKind, StoredPlace),
850
851 /// Creates a pointer/reference to the given thread local.
852 ///
853 /// The yielded type is a `*mut T` if the static is mutable, otherwise if the static is extern a
854 /// `*const T`, and if neither of those apply a `&T`.
855 ///
856 /// **Note:** This is a runtime operation that actually executes code and is in this sense more
857 /// like a function call. Also, eliminating dead stores of this rvalue causes `fn main() {}` to
858 /// SIGILL for some reason that I (JakobDegen) never got a chance to look into.
859 ///
860 /// **Needs clarification**: Are there weird additional semantics here related to the runtime
861 /// nature of this operation?
862 // ThreadLocalRef(DefId),
863 ThreadLocalRef(std::convert::Infallible),
864
865 /// Creates a pointer with the indicated mutability to the place.
866 ///
867 /// This is generated by pointer casts like `&v as *const _` or raw address of expressions like
868 /// `&raw v` or `addr_of!(v)`.
869 ///
870 /// Like with references, the semantics of this operation are heavily dependent on the aliasing
871 /// model.
872 // AddressOf(Mutability, Place),
873 AddressOf(std::convert::Infallible),
874
875 /// Yields the length of the place, as a `usize`.
876 ///
877 /// If the type of the place is an array, this is the array length. For slices (`[T]`, not
878 /// `&[T]`) this accesses the place's metadata to determine the length. This rvalue is
879 /// ill-formed for places of other types.
880 Len(StoredPlace),
881
882 /// Performs essentially all of the casts that can be performed via `as`.
883 ///
884 /// This allows for casts from/to a variety of types.
885 ///
886 /// **FIXME**: Document exactly which `CastKind`s allow which types of casts. Figure out why
887 /// `ArrayToPointer` and `MutToConstPointer` are special.
888 Cast(CastKind, Operand, StoredTy),
889
890 // FIXME link to `pointer::offset` when it hits stable.
891 /// * `Offset` has the same semantics as `pointer::offset`, except that the second
892 /// parameter may be a `usize` as well.
893 /// * The comparison operations accept `bool`s, `char`s, signed or unsigned integers, floats,
894 /// raw pointers, or function pointers and return a `bool`. The types of the operands must be
895 /// matching, up to the usual caveat of the lifetimes in function pointers.
896 /// * Left and right shift operations accept signed or unsigned integers not necessarily of the
897 /// same type and return a value of the same type as their LHS. Like in Rust, the RHS is
898 /// truncated as needed.
899 /// * The `Bit*` operations accept signed integers, unsigned integers, or bools with matching
900 /// types and return a value of that type.
901 /// * The remaining operations accept signed integers, unsigned integers, or floats with
902 /// matching types and return a value of that type.
903 //BinaryOp(BinOp, Box<(Operand, Operand)>),
904 BinaryOp(std::convert::Infallible),
905
906 /// Same as `BinaryOp`, but yields `(T, bool)` with a `bool` indicating an error condition.
907 ///
908 /// When overflow checking is disabled and we are generating run-time code, the error condition
909 /// is false. Otherwise, and always during CTFE, the error condition is determined as described
910 /// below.
911 ///
912 /// For addition, subtraction, and multiplication on integers the error condition is set when
913 /// the infinite precision result would be unequal to the actual result.
914 ///
915 /// For shift operations on integers the error condition is set when the value of right-hand
916 /// side is greater than or equal to the number of bits in the type of the left-hand side, or
917 /// when the value of right-hand side is negative.
918 ///
919 /// Other combinations of types and operators are unsupported.
920 CheckedBinaryOp(BinOp, Operand, Operand),
921
922 /// Computes a value as described by the operation.
923 //NullaryOp(NullOp, Ty),
924 NullaryOp(std::convert::Infallible),
925
926 /// Exactly like `BinaryOp`, but less operands.
927 ///
928 /// Also does two's-complement arithmetic. Negation requires a signed integer or a float;
929 /// bitwise not requires a signed integer, unsigned integer, or bool. Both operation kinds
930 /// return a value with the same type as their operand.
931 UnaryOp(UnOp, Operand),
932
933 /// Computes the discriminant of the place, returning it as an integer of type
934 /// `discriminant_ty`. Returns zero for types without discriminant.
935 ///
936 /// The validity requirements for the underlying value are undecided for this rvalue, see
937 /// [#91095]. Note too that the value of the discriminant is not the same thing as the
938 /// variant index; use `discriminant_for_variant` to convert.
939 ///
940 /// [#91095]: https://github.com/rust-lang/rust/issues/91095
941 Discriminant(StoredPlace),
942
943 /// Creates an aggregate value, like a tuple or struct.
944 ///
945 /// This is needed because dataflow analysis needs to distinguish
946 /// `dest = Foo { x: ..., y: ... }` from `dest.x = ...; dest.y = ...;` in the case that `Foo`
947 /// has a destructor.
948 ///
949 /// Disallowed after deaggregation for all aggregate kinds except `Array` and `Coroutine`. After
950 /// coroutine lowering, `Coroutine` aggregate kinds are disallowed too.
951 Aggregate(AggregateKind, Box<[Operand]>),
952
953 /// A CopyForDeref is equivalent to a read from a place at the
954 /// codegen level, but is treated specially by drop elaboration. When such a read happens, it
955 /// is guaranteed (via nature of the mir_opt `Derefer` in rustc_mir_transform/src/deref_separator)
956 /// that the only use of the returned value is a deref operation, immediately
957 /// followed by one or more projections. Drop elaboration treats this rvalue as if the
958 /// read never happened and just projects further. This allows simplifying various MIR
959 /// optimizations and codegen backends that previously had to handle deref operations anywhere
960 /// in a place.
961 CopyForDeref(StoredPlace),
962}
963
964#[derive(Debug, PartialEq, Eq, Clone)]
965pub enum StatementKind {
966 Assign(StoredPlace, Rvalue),
967 FakeRead(StoredPlace),
968 //SetDiscriminant {
969 // place: Box<Place>,
970 // variant_index: VariantIdx,
971 //},
972 Deinit(StoredPlace),
973 StorageLive(LocalId),
974 StorageDead(LocalId),
975 //Retag(RetagKind, Box<Place>),
976 //AscribeUserType(Place, UserTypeProjection, Variance),
977 //Intrinsic(Box<NonDivergingIntrinsic>),
978 Nop,
979}
980impl StatementKind {
981 fn with_span(self, span: MirSpan) -> Statement {
982 Statement { kind: self, span }
983 }
984}
985
986#[derive(Debug, PartialEq, Eq, Clone)]
987pub struct Statement {
988 pub kind: StatementKind,
989 pub span: MirSpan,
990}
991
992#[derive(Debug, Default, Clone, PartialEq, Eq)]
993pub struct BasicBlock {
994 /// List of statements in this block.
995 pub statements: Vec<Statement>,
996
997 /// Terminator for this block.
998 ///
999 /// N.B., this should generally ONLY be `None` during construction.
1000 /// Therefore, you should generally access it via the
1001 /// `terminator()` or `terminator_mut()` methods. The only
1002 /// exception is that certain passes, such as `simplify_cfg`, swap
1003 /// out the terminator temporarily with `None` while they continue
1004 /// to recurse over the set of basic blocks.
1005 pub terminator: Option<Terminator>,
1006
1007 /// If true, this block lies on an unwind path. This is used
1008 /// during codegen where distinct kinds of basic blocks may be
1009 /// generated (particularly for MSVC cleanup). Unwind blocks must
1010 /// only branch to other unwind blocks.
1011 pub is_cleanup: bool,
1012}
1013
1014#[derive(Debug, Clone, PartialEq, Eq, SalsaValue)]
1015pub struct MirBody<'db> {
1016 pub basic_blocks: Arena<BasicBlock>,
1017 pub locals: Arena<Local>,
1018 pub start_block: BasicBlockId,
1019 pub owner: InferBodyId<'db>,
1020 pub binding_locals: ArenaMap<BindingId, LocalId>,
1021 pub upvar_locals: ThinFxHashMap<BindingId, Vec<(LocalId, crate::closure_analysis::Place)>>,
1022 pub param_locals: Vec<LocalId>,
1023 /// This field stores the closures directly owned by this body. It is used
1024 /// in traversing every mir body.
1025 pub closures: Vec<InternedClosureId<'db>>,
1026}
1027
1028impl MirBody<'_> {
1029 pub fn local_to_binding_map(&self) -> ArenaMap<LocalId, BindingId> {
1030 self.binding_locals.iter().map(|(it, y)| (*y, it)).collect()
1031 }
1032
1033 fn walk_places(&mut self, mut f: impl FnMut(&mut StoredPlace)) {
1034 fn for_operand(op: &mut Operand, f: &mut impl FnMut(&mut StoredPlace)) {
1035 match &mut op.kind {
1036 OperandKind::Copy(p) | OperandKind::Move(p) => {
1037 f(p);
1038 }
1039 OperandKind::Constant { .. }
1040 | OperandKind::Static(_)
1041 | OperandKind::Allocation { .. } => (),
1042 }
1043 }
1044 for (_, block) in self.basic_blocks.iter_mut() {
1045 for statement in &mut block.statements {
1046 match &mut statement.kind {
1047 StatementKind::Assign(p, r) => {
1048 f(p);
1049 match r {
1050 Rvalue::UnaryOp(_, o)
1051 | Rvalue::Cast(_, o, _)
1052 | Rvalue::Repeat(o, _)
1053 | Rvalue::Use(o) => for_operand(o, &mut f),
1054 Rvalue::CopyForDeref(p)
1055 | Rvalue::Discriminant(p)
1056 | Rvalue::Len(p)
1057 | Rvalue::Ref(_, p) => f(p),
1058 Rvalue::CheckedBinaryOp(_, o1, o2) => {
1059 for_operand(o1, &mut f);
1060 for_operand(o2, &mut f);
1061 }
1062 Rvalue::Aggregate(_, ops) => {
1063 for op in ops.iter_mut() {
1064 for_operand(op, &mut f);
1065 }
1066 }
1067 Rvalue::ThreadLocalRef(n)
1068 | Rvalue::AddressOf(n)
1069 | Rvalue::BinaryOp(n)
1070 | Rvalue::NullaryOp(n) => match *n {},
1071 }
1072 }
1073 StatementKind::FakeRead(p) | StatementKind::Deinit(p) => f(p),
1074 StatementKind::StorageLive(_)
1075 | StatementKind::StorageDead(_)
1076 | StatementKind::Nop => (),
1077 }
1078 }
1079 match &mut block.terminator {
1080 Some(x) => match &mut x.kind {
1081 TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, &mut f),
1082 TerminatorKind::FalseEdge { .. }
1083 | TerminatorKind::FalseUnwind { .. }
1084 | TerminatorKind::Goto { .. }
1085 | TerminatorKind::UnwindResume
1086 | TerminatorKind::CoroutineDrop
1087 | TerminatorKind::Abort
1088 | TerminatorKind::Return
1089 | TerminatorKind::Unreachable => (),
1090 TerminatorKind::Drop { place, .. } => {
1091 f(place);
1092 }
1093 TerminatorKind::DropAndReplace { place, value, .. } => {
1094 f(place);
1095 for_operand(value, &mut f);
1096 }
1097 TerminatorKind::Call { func, args, destination, .. } => {
1098 for_operand(func, &mut f);
1099 args.iter_mut().for_each(|x| for_operand(x, &mut f));
1100 f(destination);
1101 }
1102 TerminatorKind::Assert { cond, .. } => {
1103 for_operand(cond, &mut f);
1104 }
1105 TerminatorKind::Yield { value, resume_arg, .. } => {
1106 for_operand(value, &mut f);
1107 f(resume_arg);
1108 }
1109 },
1110 None => (),
1111 }
1112 }
1113 }
1114
1115 fn shrink_to_fit(&mut self) {
1116 let MirBody {
1117 basic_blocks,
1118 locals,
1119 start_block: _,
1120 owner: _,
1121 binding_locals,
1122 upvar_locals,
1123 param_locals,
1124 closures,
1125 } = self;
1126 basic_blocks.shrink_to_fit();
1127 locals.shrink_to_fit();
1128 binding_locals.shrink_to_fit();
1129 upvar_locals.shrink_to_fit();
1130 param_locals.shrink_to_fit();
1131 closures.shrink_to_fit();
1132 for (_, b) in basic_blocks.iter_mut() {
1133 let BasicBlock { statements, terminator: _, is_cleanup: _ } = b;
1134 statements.shrink_to_fit();
1135 }
1136 }
1137}
1138
1139#[derive(Debug, PartialEq, Eq, Clone, Copy, SalsaValue)]
1140pub enum MirSpan {
1141 ExprId(ExprId),
1142 PatId(PatId),
1143 BindingId(BindingId),
1144 SelfParam,
1145 Unknown,
1146}
1147impl_from!(ExprId, PatId for MirSpan);
1148
1149impl From<&ExprId> for MirSpan {
1150 fn from(value: &ExprId) -> Self {
1151 (*value).into()
1152 }
1153}
1154
1155impl<'tcx> Place<'tcx> {
1156 /// If this place represents a local variable like `_X` with no
1157 /// projections, return `Some(_X)`.
1158 #[inline]
1159 pub fn as_local(&self) -> Option<LocalId> {
1160 match *self {
1161 Place { local, projection } if projection.as_slice().is_empty() => Some(local),
1162 _ => None,
1163 }
1164 }
1165}
1166
1167/// To determine the type of a place, we need to keep track of the variant that has been downcast to, in order to find the correct fields.
1168/// This type does that.
1169#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable, Hash, PartialEq, Eq)]
1170pub struct PlaceTy<'db> {
1171 pub ty: Ty<'db>,
1172 /// Downcast to a particular variant of an enum or a coroutine, if included.
1173 #[type_foldable(identity)]
1174 #[type_visitable(ignore)]
1175 pub variant_id: Option<VariantId>,
1176}
1177
1178impl<'db> PlaceTy<'db> {
1179 #[inline]
1180 pub fn from_ty(ty: Ty<'db>) -> PlaceTy<'db> {
1181 PlaceTy { ty, variant_id: None }
1182 }
1183
1184 pub fn multi_projection_ty(
1185 self,
1186 infcx: &InferCtxt<'db>,
1187 env: ParamEnv<'db>,
1188 elems: &[PlaceElem],
1189 ) -> PlaceTy<'db> {
1190 elems.iter().fold(self, |place_ty, elem| place_ty.projection_ty(infcx, elem, env))
1191 }
1192
1193 fn field_ty(
1194 infcx: &InferCtxt<'db>,
1195 self_ty: Ty<'db>,
1196 variant: Option<VariantId>,
1197 f: FieldIndex,
1198 ) -> Ty<'db> {
1199 if let Some(variant_id) = variant {
1200 match self_ty.kind() {
1201 TyKind::Adt(adt_def, args) if adt_def.is_enum() => {
1202 infcx.interner.db().field_types(variant_id)[f.to_local_field_id()]
1203 .ty()
1204 .instantiate(infcx.interner, args)
1205 .skip_norm_wip()
1206 }
1207 // FIXME TyKind::Coroutine...
1208 _ => panic!("can't downcast non-adt non-coroutine type: {self_ty:?}"),
1209 }
1210 } else {
1211 match self_ty.kind() {
1212 TyKind::Adt(adt_def, args) if !adt_def.is_enum() => {
1213 let variant_id = VariantId::from_non_enum(adt_def.def_id()).unwrap();
1214 infcx.interner.db().field_types(variant_id)[f.to_local_field_id()]
1215 .ty()
1216 .instantiate(infcx.interner, args)
1217 .skip_norm_wip()
1218 }
1219 TyKind::Closure(_, args) => {
1220 args.as_closure().tupled_upvars_ty().tuple_fields()[f.0 as usize]
1221 }
1222 // FIXME TyKind::Coroutine / TyKind::CoroutineClosure...
1223 TyKind::Tuple(tys) => tys
1224 .get(f.0 as usize)
1225 .cloned()
1226 .unwrap_or_else(|| panic!("field {f:?} out of range: {self_ty:?}")),
1227 _ => panic!("can't project out of {self_ty:?}"),
1228 }
1229 }
1230 }
1231
1232 /// Convenience wrapper around `projection_ty_core` for `PlaceElem`.
1233 pub fn projection_ty<V: ::std::fmt::Debug + PartialEq>(
1234 self,
1235 infcx: &InferCtxt<'db>,
1236 elem: &ProjectionElem<V>,
1237 env: ParamEnv<'db>,
1238 ) -> PlaceTy<'db> {
1239 self.projection_ty_core(
1240 infcx.interner,
1241 elem,
1242 |ty| {
1243 if matches!(ty.kind(), TyKind::Alias(..)) {
1244 let mut ocx = ObligationCtxt::new(infcx);
1245 match ocx.structurally_normalize_ty(&ObligationCause::dummy(), env, ty) {
1246 Ok(it) => it,
1247 Err(_) => Ty::new_error(infcx.interner, ErrorGuaranteed),
1248 }
1249 } else {
1250 ty
1251 }
1252 },
1253 |self_ty, variant, field_id| Self::field_ty(infcx, self_ty, variant, field_id),
1254 )
1255 }
1256
1257 /// `place_ty.projection_ty_core(tcx, elem, |...| { ... })`
1258 /// projects `place_ty` onto `elem`, returning the appropriate
1259 /// `Ty` or downcast variant corresponding to that projection.
1260 /// The `handle_field` callback must map a `FieldIndex` to its `Ty`
1261 pub fn projection_ty_core<V: PartialEq + ::std::fmt::Debug>(
1262 self,
1263 tcx: DbInterner<'db>,
1264 elem: &ProjectionElem<V>,
1265 mut structurally_normalize: impl FnMut(Ty<'db>) -> Ty<'db>,
1266 mut handle_field: impl FnMut(Ty<'db>, Option<VariantId>, FieldIndex /*, T*/) -> Ty<'db>,
1267 ) -> PlaceTy<'db> {
1268 // we only bail on mir building when there are type mismatches
1269 // but error types may pop up resulting in us still attempting to build the mir
1270 // so just propagate the error type
1271 if self.ty.is_ty_error() {
1272 return PlaceTy::from_ty(Ty::new_error(tcx, ErrorGuaranteed));
1273 }
1274 if self.variant_id.is_some() && !matches!(elem, ProjectionElem::Field(..)) {
1275 panic!("cannot use non field projection on downcasted place")
1276 }
1277 match *elem {
1278 ProjectionElem::Deref => {
1279 let ty = structurally_normalize(self.ty).builtin_deref(true).unwrap_or_else(|| {
1280 panic!("deref projection of non-dereferenceable ty {:?}", self)
1281 });
1282 PlaceTy::from_ty(ty)
1283 }
1284 ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => {
1285 PlaceTy::from_ty(structurally_normalize(self.ty).builtin_index().unwrap())
1286 }
1287 ProjectionElem::Subslice { from, to /*, from_end*/ } => {
1288 PlaceTy::from_ty(match structurally_normalize(self.ty).kind() {
1289 TyKind::Slice(..) => self.ty,
1290 TyKind::Array(inner, _) /*if !from_end*/ => Ty::new_array_opt(tcx, inner, to.checked_sub(from).map(|x| x.into())),
1291 // TyKind::Array(inner, size) if from_end => {
1292 // let size = size
1293 // .try_to_target_usize(tcx)
1294 // .expect("expected subslice projection on fixed-size array");
1295 // let len = size - from - to;
1296 // Ty::new_array(tcx, *inner, len)
1297 // }
1298 _ => panic!("cannot subslice non-array type: `{:?}`", self),
1299 })
1300 }
1301 ProjectionElem::Downcast(index) => PlaceTy { ty: self.ty, variant_id: Some(index) },
1302 ProjectionElem::Field(f) => {
1303 PlaceTy::from_ty(handle_field(structurally_normalize(self.ty), self.variant_id, f))
1304 }
1305 }
1306 }
1307}