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