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