hir_ty/mir.rs
1//! MIR definitions and implementation
2
3use std::{collections::hash_map::Entry, fmt::Display, iter};
4
5use crate::{
6 CallableDefId, ClosureId, Const, ConstScalar, InferenceResult, Interner, MemoryMap,
7 Substitution, TraitEnvironment, Ty, TyExt, TyKind,
8 consteval::usize_const,
9 db::HirDatabase,
10 display::{DisplayTarget, HirDisplay},
11 infer::{PointerCast, normalize},
12 lang_items::is_box,
13 mapping::ToChalk,
14};
15use base_db::Crate;
16use chalk_ir::Mutability;
17use either::Either;
18use hir_def::{
19 DefWithBodyId, FieldId, StaticId, TupleFieldId, UnionId, VariantId,
20 expr_store::Body,
21 hir::{BindingAnnotation, BindingId, Expr, ExprId, Ordering, PatId},
22};
23use la_arena::{Arena, ArenaMap, Idx, RawIdx};
24
25mod borrowck;
26mod eval;
27mod lower;
28mod monomorphization;
29mod pretty;
30
31pub use borrowck::{BorrowckResult, MutabilityReason, borrowck_query};
32pub use eval::{
33 Evaluator, MirEvalError, VTableMap, interpret_mir, pad16, render_const_using_debug_impl,
34};
35pub use lower::{MirLowerError, lower_to_mir, mir_body_for_closure_query, mir_body_query};
36pub use monomorphization::{
37 monomorphize_mir_body_bad, monomorphized_mir_body_for_closure_query,
38 monomorphized_mir_body_query,
39};
40use rustc_hash::FxHashMap;
41use smallvec::{SmallVec, smallvec};
42use stdx::{impl_from, never};
43
44pub(crate) use lower::mir_body_cycle_result;
45pub(crate) use monomorphization::monomorphized_mir_body_cycle_result;
46
47use super::consteval::{intern_const_scalar, try_const_usize};
48
49pub type BasicBlockId = Idx<BasicBlock>;
50pub type LocalId = Idx<Local>;
51
52fn return_slot() -> LocalId {
53 LocalId::from_raw(RawIdx::from(0))
54}
55
56#[derive(Debug, Clone, PartialEq, Eq)]
57pub struct Local {
58 pub ty: Ty,
59}
60
61/// An operand in MIR represents a "value" in Rust, the definition of which is undecided and part of
62/// the memory model. One proposal for a definition of values can be found [on UCG][value-def].
63///
64/// [value-def]: https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/value-domain.md
65///
66/// The most common way to create values is via loading a place. Loading a place is an operation
67/// which reads the memory of the place and converts it to a value. This is a fundamentally *typed*
68/// operation. The nature of the value produced depends on the type of the conversion. Furthermore,
69/// there may be other effects: if the type has a validity constraint loading the place might be UB
70/// if the validity constraint is not met.
71///
72/// **Needs clarification:** Ralf proposes that loading a place not have side-effects.
73/// This is what is implemented in miri today. Are these the semantics we want for MIR? Is this
74/// something we can even decide without knowing more about Rust's memory model?
75///
76/// **Needs clarification:** Is loading a place that has its variant index set well-formed? Miri
77/// currently implements it, but it seems like this may be something to check against in the
78/// validator.
79#[derive(Debug, PartialEq, Eq, Clone)]
80pub struct Operand {
81 kind: OperandKind,
82 // FIXME : This should actually just be of type `MirSpan`.
83 span: Option<MirSpan>,
84}
85
86#[derive(Debug, PartialEq, Eq, Clone)]
87pub enum OperandKind {
88 /// Creates a value by loading the given place.
89 ///
90 /// Before drop elaboration, the type of the place must be `Copy`. After drop elaboration there
91 /// is no such requirement.
92 Copy(Place),
93
94 /// Creates a value by performing loading the place, just like the `Copy` operand.
95 ///
96 /// This *may* additionally overwrite the place with `uninit` bytes, depending on how we decide
97 /// in [UCG#188]. You should not emit MIR that may attempt a subsequent second load of this
98 /// place without first re-initializing it.
99 ///
100 /// [UCG#188]: https://github.com/rust-lang/unsafe-code-guidelines/issues/188
101 Move(Place),
102 /// Constants are already semantically values, and remain unchanged.
103 Constant(Const),
104 /// NON STANDARD: This kind of operand returns an immutable reference to that static memory. Rustc
105 /// handles it with the `Constant` variant somehow.
106 Static(StaticId),
107}
108
109impl Operand {
110 fn from_concrete_const(data: Box<[u8]>, memory_map: MemoryMap<'static>, ty: Ty) -> Self {
111 Operand {
112 kind: OperandKind::Constant(intern_const_scalar(
113 ConstScalar::Bytes(data, memory_map),
114 ty,
115 )),
116 span: None,
117 }
118 }
119
120 fn from_bytes(data: Box<[u8]>, ty: Ty) -> Self {
121 Operand::from_concrete_const(data, MemoryMap::default(), ty)
122 }
123
124 fn const_zst(ty: Ty) -> Operand {
125 Self::from_bytes(Box::default(), ty)
126 }
127
128 fn from_fn(
129 db: &dyn HirDatabase,
130 func_id: hir_def::FunctionId,
131 generic_args: Substitution,
132 ) -> Operand {
133 let ty =
134 chalk_ir::TyKind::FnDef(CallableDefId::FunctionId(func_id).to_chalk(db), generic_args)
135 .intern(Interner);
136 Operand::from_bytes(Box::default(), ty)
137 }
138}
139
140#[derive(Debug, Clone, PartialEq, Eq, Hash)]
141pub enum ProjectionElem<V, T> {
142 Deref,
143 Field(Either<FieldId, TupleFieldId>),
144 // FIXME: get rid of this, and use FieldId for tuples and closures
145 ClosureField(usize),
146 Index(V),
147 ConstantIndex { offset: u64, from_end: bool },
148 Subslice { from: u64, to: u64 },
149 //Downcast(Option<Symbol>, VariantIdx),
150 OpaqueCast(T),
151}
152
153impl<V, T> ProjectionElem<V, T> {
154 pub fn projected_ty(
155 &self,
156 mut base: Ty,
157 db: &dyn HirDatabase,
158 closure_field: impl FnOnce(ClosureId, &Substitution, usize) -> Ty,
159 krate: Crate,
160 ) -> Ty {
161 // we only bail on mir building when there are type mismatches
162 // but error types may pop up resulting in us still attempting to build the mir
163 // so just propagate the error type
164 if base.is_unknown() {
165 return TyKind::Error.intern(Interner);
166 }
167
168 if matches!(base.kind(Interner), TyKind::Alias(_) | TyKind::AssociatedType(..)) {
169 base = normalize(
170 db,
171 // FIXME: we should get this from caller
172 TraitEnvironment::empty(krate),
173 base,
174 );
175 }
176 match self {
177 ProjectionElem::Deref => match &base.kind(Interner) {
178 TyKind::Raw(_, inner) | TyKind::Ref(_, _, inner) => inner.clone(),
179 TyKind::Adt(adt, subst) if is_box(db, adt.0) => {
180 subst.at(Interner, 0).assert_ty_ref(Interner).clone()
181 }
182 _ => {
183 never!(
184 "Overloaded deref on type {} is not a projection",
185 base.display(db, DisplayTarget::from_crate(db, krate))
186 );
187 TyKind::Error.intern(Interner)
188 }
189 },
190 ProjectionElem::Field(Either::Left(f)) => match base.kind(Interner) {
191 TyKind::Adt(_, subst) => {
192 db.field_types(f.parent)[f.local_id].clone().substitute(Interner, subst)
193 }
194 ty => {
195 never!("Only adt has field, found {:?}", ty);
196 TyKind::Error.intern(Interner)
197 }
198 },
199 ProjectionElem::Field(Either::Right(f)) => match &base.kind(Interner) {
200 TyKind::Tuple(_, subst) => subst
201 .as_slice(Interner)
202 .get(f.index as usize)
203 .map(|x| x.assert_ty_ref(Interner))
204 .cloned()
205 .unwrap_or_else(|| {
206 never!("Out of bound tuple field");
207 TyKind::Error.intern(Interner)
208 }),
209 ty => {
210 never!("Only tuple has tuple field: {:?}", ty);
211 TyKind::Error.intern(Interner)
212 }
213 },
214 ProjectionElem::ClosureField(f) => match &base.kind(Interner) {
215 TyKind::Closure(id, subst) => closure_field(*id, subst, *f),
216 _ => {
217 never!("Only closure has closure field");
218 TyKind::Error.intern(Interner)
219 }
220 },
221 ProjectionElem::ConstantIndex { .. } | ProjectionElem::Index(_) => {
222 match &base.kind(Interner) {
223 TyKind::Array(inner, _) | TyKind::Slice(inner) => inner.clone(),
224 _ => {
225 never!("Overloaded index is not a projection");
226 TyKind::Error.intern(Interner)
227 }
228 }
229 }
230 &ProjectionElem::Subslice { from, to } => match &base.kind(Interner) {
231 TyKind::Array(inner, c) => {
232 let next_c = usize_const(
233 db,
234 match try_const_usize(db, c) {
235 None => None,
236 Some(x) => x.checked_sub(u128::from(from + to)),
237 },
238 krate,
239 );
240 TyKind::Array(inner.clone(), next_c).intern(Interner)
241 }
242 TyKind::Slice(_) => base.clone(),
243 _ => {
244 never!("Subslice projection should only happen on slice and array");
245 TyKind::Error.intern(Interner)
246 }
247 },
248 ProjectionElem::OpaqueCast(_) => {
249 never!("We don't emit these yet");
250 TyKind::Error.intern(Interner)
251 }
252 }
253 }
254}
255
256type PlaceElem = ProjectionElem<LocalId, Ty>;
257
258#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
259pub struct ProjectionId(u32);
260
261#[derive(Debug, Clone, PartialEq, Eq)]
262pub struct ProjectionStore {
263 id_to_proj: FxHashMap<ProjectionId, Box<[PlaceElem]>>,
264 proj_to_id: FxHashMap<Box<[PlaceElem]>, ProjectionId>,
265}
266
267impl Default for ProjectionStore {
268 fn default() -> Self {
269 let mut this = Self { id_to_proj: Default::default(), proj_to_id: Default::default() };
270 // Ensure that [] will get the id 0 which is used in `ProjectionId::Empty`
271 this.intern(Box::new([]));
272 this
273 }
274}
275
276impl ProjectionStore {
277 pub fn shrink_to_fit(&mut self) {
278 self.id_to_proj.shrink_to_fit();
279 self.proj_to_id.shrink_to_fit();
280 }
281
282 pub fn intern_if_exist(&self, projection: &[PlaceElem]) -> Option<ProjectionId> {
283 self.proj_to_id.get(projection).copied()
284 }
285
286 pub fn intern(&mut self, projection: Box<[PlaceElem]>) -> ProjectionId {
287 let new_id = ProjectionId(self.proj_to_id.len() as u32);
288 match self.proj_to_id.entry(projection) {
289 Entry::Occupied(id) => *id.get(),
290 Entry::Vacant(e) => {
291 let key_clone = e.key().clone();
292 e.insert(new_id);
293 self.id_to_proj.insert(new_id, key_clone);
294 new_id
295 }
296 }
297 }
298}
299
300impl ProjectionId {
301 pub const EMPTY: ProjectionId = ProjectionId(0);
302
303 pub fn is_empty(self) -> bool {
304 self == ProjectionId::EMPTY
305 }
306
307 pub fn lookup(self, store: &ProjectionStore) -> &[PlaceElem] {
308 store.id_to_proj.get(&self).unwrap()
309 }
310
311 pub fn project(self, projection: PlaceElem, store: &mut ProjectionStore) -> ProjectionId {
312 let mut current = self.lookup(store).to_vec();
313 current.push(projection);
314 store.intern(current.into())
315 }
316}
317
318#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
319pub struct Place {
320 pub local: LocalId,
321 pub projection: ProjectionId,
322}
323
324impl Place {
325 fn is_parent(&self, child: &Place, store: &ProjectionStore) -> bool {
326 self.local == child.local
327 && child.projection.lookup(store).starts_with(self.projection.lookup(store))
328 }
329
330 /// The place itself is not included
331 fn iterate_over_parents<'a>(
332 &'a self,
333 store: &'a ProjectionStore,
334 ) -> impl Iterator<Item = Place> + 'a {
335 let projection = self.projection.lookup(store);
336 (0..projection.len()).map(|x| &projection[0..x]).filter_map(move |x| {
337 Some(Place { local: self.local, projection: store.intern_if_exist(x)? })
338 })
339 }
340
341 fn project(&self, projection: PlaceElem, store: &mut ProjectionStore) -> Place {
342 Place { local: self.local, projection: self.projection.project(projection, store) }
343 }
344}
345
346impl From<LocalId> for Place {
347 fn from(local: LocalId) -> Self {
348 Self { local, projection: ProjectionId::EMPTY }
349 }
350}
351
352#[derive(Debug, PartialEq, Eq, Clone)]
353pub enum AggregateKind {
354 /// The type is of the element
355 Array(Ty),
356 /// The type is of the tuple
357 Tuple(Ty),
358 Adt(VariantId, Substitution),
359 Union(UnionId, FieldId),
360 Closure(Ty),
361 //Coroutine(LocalDefId, SubstsRef, Movability),
362}
363
364#[derive(Debug, Clone, Hash, PartialEq, Eq)]
365pub struct SwitchTargets {
366 /// Possible values. The locations to branch to in each case
367 /// are found in the corresponding indices from the `targets` vector.
368 values: SmallVec<[u128; 1]>,
369
370 /// Possible branch sites. The last element of this vector is used
371 /// for the otherwise branch, so targets.len() == values.len() + 1
372 /// should hold.
373 //
374 // This invariant is quite non-obvious and also could be improved.
375 // One way to make this invariant is to have something like this instead:
376 //
377 // branches: Vec<(ConstInt, BasicBlock)>,
378 // otherwise: Option<BasicBlock> // exhaustive if None
379 //
380 // However we’ve decided to keep this as-is until we figure a case
381 // where some other approach seems to be strictly better than other.
382 targets: SmallVec<[BasicBlockId; 2]>,
383}
384
385impl SwitchTargets {
386 /// Creates switch targets from an iterator of values and target blocks.
387 ///
388 /// The iterator may be empty, in which case the `SwitchInt` instruction is equivalent to
389 /// `goto otherwise;`.
390 pub fn new(
391 targets: impl Iterator<Item = (u128, BasicBlockId)>,
392 otherwise: BasicBlockId,
393 ) -> Self {
394 let (values, mut targets): (SmallVec<_>, SmallVec<_>) = targets.unzip();
395 targets.push(otherwise);
396 Self { values, targets }
397 }
398
399 /// Builds a switch targets definition that jumps to `then` if the tested value equals `value`,
400 /// and to `else_` if not.
401 pub fn static_if(value: u128, then: BasicBlockId, else_: BasicBlockId) -> Self {
402 Self { values: smallvec![value], targets: smallvec![then, else_] }
403 }
404
405 /// Returns the fallback target that is jumped to when none of the values match the operand.
406 pub fn otherwise(&self) -> BasicBlockId {
407 *self.targets.last().unwrap()
408 }
409
410 /// Returns an iterator over the switch targets.
411 ///
412 /// The iterator will yield tuples containing the value and corresponding target to jump to, not
413 /// including the `otherwise` fallback target.
414 ///
415 /// Note that this may yield 0 elements. Only the `otherwise` branch is mandatory.
416 pub fn iter(&self) -> impl Iterator<Item = (u128, BasicBlockId)> + '_ {
417 iter::zip(&self.values, &self.targets).map(|(x, y)| (*x, *y))
418 }
419
420 /// Returns a slice with all possible jump targets (including the fallback target).
421 pub fn all_targets(&self) -> &[BasicBlockId] {
422 &self.targets
423 }
424
425 /// Finds the `BasicBlock` to which this `SwitchInt` will branch given the
426 /// specific value. This cannot fail, as it'll return the `otherwise`
427 /// branch if there's not a specific match for the value.
428 pub fn target_for_value(&self, value: u128) -> BasicBlockId {
429 self.iter().find_map(|(v, t)| (v == value).then_some(t)).unwrap_or_else(|| self.otherwise())
430 }
431}
432
433#[derive(Debug, PartialEq, Eq, Clone)]
434pub struct Terminator {
435 pub span: MirSpan,
436 pub kind: TerminatorKind,
437}
438
439#[derive(Debug, PartialEq, Eq, Clone)]
440pub enum TerminatorKind {
441 /// Block has one successor; we continue execution there.
442 Goto { target: BasicBlockId },
443
444 /// Switches based on the computed value.
445 ///
446 /// First, evaluates the `discr` operand. The type of the operand must be a signed or unsigned
447 /// integer, char, or bool, and must match the given type. Then, if the list of switch targets
448 /// contains the computed value, continues execution at the associated basic block. Otherwise,
449 /// continues execution at the "otherwise" basic block.
450 ///
451 /// Target values may not appear more than once.
452 SwitchInt {
453 /// The discriminant value being tested.
454 discr: Operand,
455
456 targets: SwitchTargets,
457 },
458
459 /// Indicates that the landing pad is finished and that the process should continue unwinding.
460 ///
461 /// Like a return, this marks the end of this invocation of the function.
462 ///
463 /// Only permitted in cleanup blocks. `Resume` is not permitted with `-C unwind=abort` after
464 /// deaggregation runs.
465 UnwindResume,
466
467 /// Indicates that the landing pad is finished and that the process should abort.
468 ///
469 /// Used to prevent unwinding for foreign items or with `-C unwind=abort`. Only permitted in
470 /// cleanup blocks.
471 Abort,
472
473 /// Returns from the function.
474 ///
475 /// Like function calls, the exact semantics of returns in Rust are unclear. Returning very
476 /// likely at least assigns the value currently in the return place (`_0`) to the place
477 /// specified in the associated `Call` terminator in the calling function, as if assigned via
478 /// `dest = move _0`. It might additionally do other things, like have side-effects in the
479 /// aliasing model.
480 ///
481 /// If the body is a coroutine body, this has slightly different semantics; it instead causes a
482 /// `CoroutineState::Returned(_0)` to be created (as if by an `Aggregate` rvalue) and assigned
483 /// to the return place.
484 Return,
485
486 /// Indicates a terminator that can never be reached.
487 ///
488 /// Executing this terminator is UB.
489 Unreachable,
490
491 /// The behavior of this statement differs significantly before and after drop elaboration.
492 /// After drop elaboration, `Drop` executes the drop glue for the specified place, after which
493 /// it continues execution/unwinds at the given basic blocks. It is possible that executing drop
494 /// glue is special - this would be part of Rust's memory model. (**FIXME**: due we have an
495 /// issue tracking if drop glue has any interesting semantics in addition to those of a function
496 /// call?)
497 ///
498 /// `Drop` before drop elaboration is a *conditional* execution of the drop glue. Specifically, the
499 /// `Drop` will be executed if...
500 ///
501 /// **Needs clarification**: End of that sentence. This in effect should document the exact
502 /// behavior of drop elaboration. The following sounds vaguely right, but I'm not quite sure:
503 ///
504 /// > The drop glue is executed if, among all statements executed within this `Body`, an assignment to
505 /// > the place or one of its "parents" occurred more recently than a move out of it. This does not
506 /// > consider indirect assignments.
507 Drop { place: Place, target: BasicBlockId, unwind: Option<BasicBlockId> },
508
509 /// Drops the place and assigns a new value to it.
510 ///
511 /// This first performs the exact same operation as the pre drop-elaboration `Drop` terminator;
512 /// it then additionally assigns the `value` to the `place` as if by an assignment statement.
513 /// This assignment occurs both in the unwind and the regular code paths. The semantics are best
514 /// explained by the elaboration:
515 ///
516 /// ```ignore (MIR)
517 /// BB0 {
518 /// DropAndReplace(P <- V, goto BB1, unwind BB2)
519 /// }
520 /// ```
521 ///
522 /// becomes
523 ///
524 /// ```ignore (MIR)
525 /// BB0 {
526 /// Drop(P, goto BB1, unwind BB2)
527 /// }
528 /// BB1 {
529 /// // P is now uninitialized
530 /// P <- V
531 /// }
532 /// BB2 {
533 /// // P is now uninitialized -- its dtor panicked
534 /// P <- V
535 /// }
536 /// ```
537 ///
538 /// Disallowed after drop elaboration.
539 DropAndReplace {
540 place: Place,
541 value: Operand,
542 target: BasicBlockId,
543 unwind: Option<BasicBlockId>,
544 },
545
546 /// Roughly speaking, evaluates the `func` operand and the arguments, and starts execution of
547 /// the referred to function. The operand types must match the argument types of the function.
548 /// The return place type must match the return type. The type of the `func` operand must be
549 /// callable, meaning either a function pointer, a function type, or a closure type.
550 ///
551 /// **Needs clarification**: The exact semantics of this. Current backends rely on `move`
552 /// operands not aliasing the return place. It is unclear how this is justified in MIR, see
553 /// [#71117].
554 ///
555 /// [#71117]: https://github.com/rust-lang/rust/issues/71117
556 Call {
557 /// The function that’s being called.
558 func: Operand,
559 /// Arguments the function is called with.
560 /// These are owned by the callee, which is free to modify them.
561 /// This allows the memory occupied by "by-value" arguments to be
562 /// reused across function calls without duplicating the contents.
563 args: Box<[Operand]>,
564 /// Where the returned value will be written
565 destination: Place,
566 /// Where to go after this call returns. If none, the call necessarily diverges.
567 target: Option<BasicBlockId>,
568 /// Cleanups to be done if the call unwinds.
569 cleanup: Option<BasicBlockId>,
570 /// `true` if this is from a call in HIR rather than from an overloaded
571 /// operator. True for overloaded function call.
572 from_hir_call: bool,
573 // This `Span` is the span of the function, without the dot and receiver
574 // (e.g. `foo(a, b)` in `x.foo(a, b)`
575 //fn_span: Span,
576 },
577
578 /// Evaluates the operand, which must have type `bool`. If it is not equal to `expected`,
579 /// initiates a panic. Initiating a panic corresponds to a `Call` terminator with some
580 /// unspecified constant as the function to call, all the operands stored in the `AssertMessage`
581 /// as parameters, and `None` for the destination. Keep in mind that the `cleanup` path is not
582 /// necessarily executed even in the case of a panic, for example in `-C panic=abort`. If the
583 /// assertion does not fail, execution continues at the specified basic block.
584 Assert {
585 cond: Operand,
586 expected: bool,
587 //msg: AssertMessage,
588 target: BasicBlockId,
589 cleanup: Option<BasicBlockId>,
590 },
591
592 /// Marks a suspend point.
593 ///
594 /// Like `Return` terminators in coroutine bodies, this computes `value` and then a
595 /// `CoroutineState::Yielded(value)` as if by `Aggregate` rvalue. That value is then assigned to
596 /// the return place of the function calling this one, and execution continues in the calling
597 /// function. When next invoked with the same first argument, execution of this function
598 /// continues at the `resume` basic block, with the second argument written to the `resume_arg`
599 /// place. If the coroutine is dropped before then, the `drop` basic block is invoked.
600 ///
601 /// Not permitted in bodies that are not coroutine bodies, or after coroutine lowering.
602 ///
603 /// **Needs clarification**: What about the evaluation order of the `resume_arg` and `value`?
604 Yield {
605 /// The value to return.
606 value: Operand,
607 /// Where to resume to.
608 resume: BasicBlockId,
609 /// The place to store the resume argument in.
610 resume_arg: Place,
611 /// Cleanup to be done if the coroutine is dropped at this suspend point.
612 drop: Option<BasicBlockId>,
613 },
614
615 /// Indicates the end of dropping a coroutine.
616 ///
617 /// Semantically just a `return` (from the coroutines drop glue). Only permitted in the same situations
618 /// as `yield`.
619 ///
620 /// **Needs clarification**: Is that even correct? The coroutine drop code is always confusing
621 /// to me, because it's not even really in the current body.
622 ///
623 /// **Needs clarification**: Are there type system constraints on these terminators? Should
624 /// there be a "block type" like `cleanup` blocks for them?
625 CoroutineDrop,
626
627 /// A block where control flow only ever takes one real path, but borrowck needs to be more
628 /// conservative.
629 ///
630 /// At runtime this is semantically just a goto.
631 ///
632 /// Disallowed after drop elaboration.
633 FalseEdge {
634 /// The target normal control flow will take.
635 real_target: BasicBlockId,
636 /// A block control flow could conceptually jump to, but won't in
637 /// practice.
638 imaginary_target: BasicBlockId,
639 },
640
641 /// A terminator for blocks that only take one path in reality, but where we reserve the right
642 /// to unwind in borrowck, even if it won't happen in practice. This can arise in infinite loops
643 /// with no function calls for example.
644 ///
645 /// At runtime this is semantically just a goto.
646 ///
647 /// Disallowed after drop elaboration.
648 FalseUnwind {
649 /// The target normal control flow will take.
650 real_target: BasicBlockId,
651 /// The imaginary cleanup block link. This particular path will never be taken
652 /// in practice, but in order to avoid fragility we want to always
653 /// consider it in borrowck. We don't want to accept programs which
654 /// pass borrowck only when `panic=abort` or some assertions are disabled
655 /// due to release vs. debug mode builds. This needs to be an `Option` because
656 /// of the `remove_noop_landing_pads` and `abort_unwinding_calls` passes.
657 unwind: Option<BasicBlockId>,
658 },
659}
660
661// Order of variants in this enum matter: they are used to compare borrow kinds.
662#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
663pub enum BorrowKind {
664 /// Data must be immutable and is aliasable.
665 Shared,
666
667 /// The immediately borrowed place must be immutable, but projections from
668 /// it don't need to be. For example, a shallow borrow of `a.b` doesn't
669 /// conflict with a mutable borrow of `a.b.c`.
670 ///
671 /// This is used when lowering matches: when matching on a place we want to
672 /// ensure that place have the same value from the start of the match until
673 /// an arm is selected. This prevents this code from compiling:
674 /// ```compile_fail,E0510
675 /// let mut x = &Some(0);
676 /// match *x {
677 /// None => (),
678 /// Some(_) if { x = &None; false } => (),
679 /// Some(_) => (),
680 /// }
681 /// ```
682 /// This can't be a shared borrow because mutably borrowing (*x as Some).0
683 /// should not prevent `if let None = x { ... }`, for example, because the
684 /// mutating `(*x as Some).0` can't affect the discriminant of `x`.
685 /// We can also report errors with this kind of borrow differently.
686 Shallow,
687
688 /// Data is mutable and not aliasable.
689 Mut { kind: MutBorrowKind },
690}
691
692// Order of variants in this enum matter: they are used to compare borrow kinds.
693#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
694pub enum MutBorrowKind {
695 /// Data must be immutable but not aliasable. This kind of borrow cannot currently
696 /// be expressed by the user and is used only in implicit closure bindings.
697 ClosureCapture,
698 Default,
699 /// This borrow arose from method-call auto-ref
700 /// (i.e., adjustment::Adjust::Borrow).
701 TwoPhasedBorrow,
702}
703
704impl BorrowKind {
705 fn from_hir(m: hir_def::type_ref::Mutability) -> Self {
706 match m {
707 hir_def::type_ref::Mutability::Shared => BorrowKind::Shared,
708 hir_def::type_ref::Mutability::Mut => BorrowKind::Mut { kind: MutBorrowKind::Default },
709 }
710 }
711
712 fn from_chalk(m: Mutability) -> Self {
713 match m {
714 Mutability::Not => BorrowKind::Shared,
715 Mutability::Mut => BorrowKind::Mut { kind: MutBorrowKind::Default },
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, Const),
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, Place),
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(Place),
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, Ty),
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 /// [`discriminant_ty`]: crate::ty::Ty::discriminant_ty
982 /// [#91095]: https://github.com/rust-lang/rust/issues/91095
983 /// [`discriminant_for_variant`]: crate::ty::Ty::discriminant_for_variant
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 /// Transmutes a `*mut u8` into shallow-initialized `Box<T>`.
997 ///
998 /// This is different from a normal transmute because dataflow analysis will treat the box as
999 /// initialized but its content as uninitialized. Like other pointer casts, this in general
1000 /// affects alias analysis.
1001 ShallowInitBox(Operand, Ty),
1002
1003 /// NON STANDARD: allocates memory with the type's layout, and shallow init the box with the resulting pointer.
1004 ShallowInitBoxWithAlloc(Ty),
1005
1006 /// A CopyForDeref is equivalent to a read from a place at the
1007 /// codegen level, but is treated specially by drop elaboration. When such a read happens, it
1008 /// is guaranteed (via nature of the mir_opt `Derefer` in rustc_mir_transform/src/deref_separator)
1009 /// that the only use of the returned value is a deref operation, immediately
1010 /// followed by one or more projections. Drop elaboration treats this rvalue as if the
1011 /// read never happened and just projects further. This allows simplifying various MIR
1012 /// optimizations and codegen backends that previously had to handle deref operations anywhere
1013 /// in a place.
1014 CopyForDeref(Place),
1015}
1016
1017#[derive(Debug, PartialEq, Eq, Clone)]
1018pub enum StatementKind {
1019 Assign(Place, Rvalue),
1020 FakeRead(Place),
1021 //SetDiscriminant {
1022 // place: Box<Place>,
1023 // variant_index: VariantIdx,
1024 //},
1025 Deinit(Place),
1026 StorageLive(LocalId),
1027 StorageDead(LocalId),
1028 //Retag(RetagKind, Box<Place>),
1029 //AscribeUserType(Place, UserTypeProjection, Variance),
1030 //Intrinsic(Box<NonDivergingIntrinsic>),
1031 Nop,
1032}
1033impl StatementKind {
1034 fn with_span(self, span: MirSpan) -> Statement {
1035 Statement { kind: self, span }
1036 }
1037}
1038
1039#[derive(Debug, PartialEq, Eq, Clone)]
1040pub struct Statement {
1041 pub kind: StatementKind,
1042 pub span: MirSpan,
1043}
1044
1045#[derive(Debug, Default, Clone, PartialEq, Eq)]
1046pub struct BasicBlock {
1047 /// List of statements in this block.
1048 pub statements: Vec<Statement>,
1049
1050 /// Terminator for this block.
1051 ///
1052 /// N.B., this should generally ONLY be `None` during construction.
1053 /// Therefore, you should generally access it via the
1054 /// `terminator()` or `terminator_mut()` methods. The only
1055 /// exception is that certain passes, such as `simplify_cfg`, swap
1056 /// out the terminator temporarily with `None` while they continue
1057 /// to recurse over the set of basic blocks.
1058 pub terminator: Option<Terminator>,
1059
1060 /// If true, this block lies on an unwind path. This is used
1061 /// during codegen where distinct kinds of basic blocks may be
1062 /// generated (particularly for MSVC cleanup). Unwind blocks must
1063 /// only branch to other unwind blocks.
1064 pub is_cleanup: bool,
1065}
1066
1067#[derive(Debug, Clone, PartialEq, Eq)]
1068pub struct MirBody {
1069 pub projection_store: ProjectionStore,
1070 pub basic_blocks: Arena<BasicBlock>,
1071 pub locals: Arena<Local>,
1072 pub start_block: BasicBlockId,
1073 pub owner: DefWithBodyId,
1074 pub binding_locals: ArenaMap<BindingId, LocalId>,
1075 pub param_locals: Vec<LocalId>,
1076 /// This field stores the closures directly owned by this body. It is used
1077 /// in traversing every mir body.
1078 pub closures: Vec<ClosureId>,
1079}
1080
1081impl MirBody {
1082 pub fn local_to_binding_map(&self) -> ArenaMap<LocalId, BindingId> {
1083 self.binding_locals.iter().map(|(it, y)| (*y, it)).collect()
1084 }
1085
1086 fn walk_places(&mut self, mut f: impl FnMut(&mut Place, &mut ProjectionStore)) {
1087 fn for_operand(
1088 op: &mut Operand,
1089 f: &mut impl FnMut(&mut Place, &mut ProjectionStore),
1090 store: &mut ProjectionStore,
1091 ) {
1092 match &mut op.kind {
1093 OperandKind::Copy(p) | OperandKind::Move(p) => {
1094 f(p, store);
1095 }
1096 OperandKind::Constant(_) | OperandKind::Static(_) => (),
1097 }
1098 }
1099 for (_, block) in self.basic_blocks.iter_mut() {
1100 for statement in &mut block.statements {
1101 match &mut statement.kind {
1102 StatementKind::Assign(p, r) => {
1103 f(p, &mut self.projection_store);
1104 match r {
1105 Rvalue::ShallowInitBoxWithAlloc(_) => (),
1106 Rvalue::ShallowInitBox(o, _)
1107 | Rvalue::UnaryOp(_, o)
1108 | Rvalue::Cast(_, o, _)
1109 | Rvalue::Repeat(o, _)
1110 | Rvalue::Use(o) => for_operand(o, &mut f, &mut self.projection_store),
1111 Rvalue::CopyForDeref(p)
1112 | Rvalue::Discriminant(p)
1113 | Rvalue::Len(p)
1114 | Rvalue::Ref(_, p) => f(p, &mut self.projection_store),
1115 Rvalue::CheckedBinaryOp(_, o1, o2) => {
1116 for_operand(o1, &mut f, &mut self.projection_store);
1117 for_operand(o2, &mut f, &mut self.projection_store);
1118 }
1119 Rvalue::Aggregate(_, ops) => {
1120 for op in ops.iter_mut() {
1121 for_operand(op, &mut f, &mut self.projection_store);
1122 }
1123 }
1124 Rvalue::ThreadLocalRef(n)
1125 | Rvalue::AddressOf(n)
1126 | Rvalue::BinaryOp(n)
1127 | Rvalue::NullaryOp(n) => match *n {},
1128 }
1129 }
1130 StatementKind::FakeRead(p) | StatementKind::Deinit(p) => {
1131 f(p, &mut self.projection_store)
1132 }
1133 StatementKind::StorageLive(_)
1134 | StatementKind::StorageDead(_)
1135 | StatementKind::Nop => (),
1136 }
1137 }
1138 match &mut block.terminator {
1139 Some(x) => match &mut x.kind {
1140 TerminatorKind::SwitchInt { discr, .. } => {
1141 for_operand(discr, &mut f, &mut self.projection_store)
1142 }
1143 TerminatorKind::FalseEdge { .. }
1144 | TerminatorKind::FalseUnwind { .. }
1145 | TerminatorKind::Goto { .. }
1146 | TerminatorKind::UnwindResume
1147 | TerminatorKind::CoroutineDrop
1148 | TerminatorKind::Abort
1149 | TerminatorKind::Return
1150 | TerminatorKind::Unreachable => (),
1151 TerminatorKind::Drop { place, .. } => {
1152 f(place, &mut self.projection_store);
1153 }
1154 TerminatorKind::DropAndReplace { place, value, .. } => {
1155 f(place, &mut self.projection_store);
1156 for_operand(value, &mut f, &mut self.projection_store);
1157 }
1158 TerminatorKind::Call { func, args, destination, .. } => {
1159 for_operand(func, &mut f, &mut self.projection_store);
1160 args.iter_mut()
1161 .for_each(|x| for_operand(x, &mut f, &mut self.projection_store));
1162 f(destination, &mut self.projection_store);
1163 }
1164 TerminatorKind::Assert { cond, .. } => {
1165 for_operand(cond, &mut f, &mut self.projection_store);
1166 }
1167 TerminatorKind::Yield { value, resume_arg, .. } => {
1168 for_operand(value, &mut f, &mut self.projection_store);
1169 f(resume_arg, &mut self.projection_store);
1170 }
1171 },
1172 None => (),
1173 }
1174 }
1175 }
1176
1177 fn shrink_to_fit(&mut self) {
1178 let MirBody {
1179 basic_blocks,
1180 locals,
1181 start_block: _,
1182 owner: _,
1183 binding_locals,
1184 param_locals,
1185 closures,
1186 projection_store,
1187 } = self;
1188 projection_store.shrink_to_fit();
1189 basic_blocks.shrink_to_fit();
1190 locals.shrink_to_fit();
1191 binding_locals.shrink_to_fit();
1192 param_locals.shrink_to_fit();
1193 closures.shrink_to_fit();
1194 for (_, b) in basic_blocks.iter_mut() {
1195 let BasicBlock { statements, terminator: _, is_cleanup: _ } = b;
1196 statements.shrink_to_fit();
1197 }
1198 }
1199}
1200
1201#[derive(Debug, PartialEq, Eq, Clone, Copy)]
1202pub enum MirSpan {
1203 ExprId(ExprId),
1204 PatId(PatId),
1205 BindingId(BindingId),
1206 SelfParam,
1207 Unknown,
1208}
1209
1210impl MirSpan {
1211 pub fn is_ref_span(&self, body: &Body) -> bool {
1212 match *self {
1213 MirSpan::ExprId(expr) => matches!(body[expr], Expr::Ref { .. }),
1214 // FIXME: Figure out if this is correct wrt. match ergonomics.
1215 MirSpan::BindingId(binding) => {
1216 matches!(body[binding].mode, BindingAnnotation::Ref | BindingAnnotation::RefMut)
1217 }
1218 MirSpan::PatId(_) | MirSpan::SelfParam | MirSpan::Unknown => false,
1219 }
1220 }
1221}
1222
1223impl_from!(ExprId, PatId for MirSpan);
1224
1225impl From<&ExprId> for MirSpan {
1226 fn from(value: &ExprId) -> Self {
1227 (*value).into()
1228 }
1229}