move MIR syntax into a dedicated file and ping some people whenever it changes
This commit is contained in:
parent
baf382e63c
commit
ab01a73151
File diff suppressed because it is too large
Load Diff
1141
compiler/rustc_middle/src/mir/syntax.rs
Normal file
1141
compiler/rustc_middle/src/mir/syntax.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,16 +1,12 @@
|
||||
use crate::mir;
|
||||
use crate::mir::interpret::Scalar;
|
||||
use crate::ty::{self, Ty, TyCtxt};
|
||||
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
use super::{
|
||||
AssertMessage, BasicBlock, InlineAsmOperand, Operand, Place, SourceInfo, Successors,
|
||||
SuccessorsMut,
|
||||
};
|
||||
use super::{BasicBlock, InlineAsmOperand, Operand, SourceInfo, TerminatorKind};
|
||||
use rustc_ast::InlineAsmTemplatePiece;
|
||||
pub use rustc_ast::Mutability;
|
||||
use rustc_macros::HashStable;
|
||||
use rustc_span::Span;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::{self, Debug, Formatter, Write};
|
||||
use std::iter;
|
||||
@ -106,278 +102,16 @@ fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
|
||||
impl<'a> ExactSizeIterator for SwitchTargetsIter<'a> {}
|
||||
|
||||
/// A note on unwinding: Panics may occur during the execution of some terminators. Depending on the
|
||||
/// `-C panic` flag, this may either cause the program to abort or the call stack to unwind. Such
|
||||
/// terminators have a `cleanup: Option<BasicBlock>` field on them. If stack unwinding occurs, then
|
||||
/// once the current function is reached, execution continues at the given basic block, if any. If
|
||||
/// `cleanup` is `None` then no cleanup is performed, and the stack continues unwinding. This is
|
||||
/// equivalent to the execution of a `Resume` terminator.
|
||||
///
|
||||
/// The basic block pointed to by a `cleanup` field must have its `cleanup` flag set. `cleanup`
|
||||
/// basic blocks have a couple restrictions:
|
||||
/// 1. All `cleanup` fields in them must be `None`.
|
||||
/// 2. `Return` terminators are not allowed in them. `Abort` and `Unwind` terminators are.
|
||||
/// 3. All other basic blocks (in the current body) that are reachable from `cleanup` basic blocks
|
||||
/// must also be `cleanup`. This is a part of the type system and checked statically, so it is
|
||||
/// still an error to have such an edge in the CFG even if it's known that it won't be taken at
|
||||
/// runtime.
|
||||
#[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)]
|
||||
pub enum TerminatorKind<'tcx> {
|
||||
/// Block has one successor; we continue execution there.
|
||||
Goto { target: BasicBlock },
|
||||
|
||||
/// Switches based on the computed value.
|
||||
///
|
||||
/// First, evaluates the `discr` operand. The type of the operand must be a signed or unsigned
|
||||
/// integer, char, or bool, and must match the given type. Then, if the list of switch targets
|
||||
/// contains the computed value, continues execution at the associated basic block. Otherwise,
|
||||
/// continues execution at the "otherwise" basic block.
|
||||
///
|
||||
/// Target values may not appear more than once.
|
||||
SwitchInt {
|
||||
/// The discriminant value being tested.
|
||||
discr: Operand<'tcx>,
|
||||
|
||||
/// The type of value being tested.
|
||||
/// This is always the same as the type of `discr`.
|
||||
/// FIXME: remove this redundant information. Currently, it is relied on by pretty-printing.
|
||||
switch_ty: Ty<'tcx>,
|
||||
|
||||
targets: SwitchTargets,
|
||||
},
|
||||
|
||||
/// Indicates that the landing pad is finished and that the process should continue unwinding.
|
||||
///
|
||||
/// Like a return, this marks the end of this invocation of the function.
|
||||
///
|
||||
/// Only permitted in cleanup blocks. `Resume` is not permitted with `-C unwind=abort` after
|
||||
/// deaggregation runs.
|
||||
Resume,
|
||||
|
||||
/// Indicates that the landing pad is finished and that the process should abort.
|
||||
///
|
||||
/// Used to prevent unwinding for foreign items or with `-C unwind=abort`. Only permitted in
|
||||
/// cleanup blocks.
|
||||
Abort,
|
||||
|
||||
/// Returns from the function.
|
||||
///
|
||||
/// Like function calls, the exact semantics of returns in Rust are unclear. Returning very
|
||||
/// likely at least assigns the value currently in the return place (`_0`) to the place
|
||||
/// specified in the associated `Call` terminator in the calling function, as if assigned via
|
||||
/// `dest = move _0`. It might additionally do other things, like have side-effects in the
|
||||
/// aliasing model.
|
||||
///
|
||||
/// If the body is a generator body, this has slightly different semantics; it instead causes a
|
||||
/// `GeneratorState::Returned(_0)` to be created (as if by an `Aggregate` rvalue) and assigned
|
||||
/// to the return place.
|
||||
Return,
|
||||
|
||||
/// Indicates a terminator that can never be reached.
|
||||
///
|
||||
/// Executing this terminator is UB.
|
||||
Unreachable,
|
||||
|
||||
/// The behavior of this statement differs significantly before and after drop elaboration.
|
||||
/// After drop elaboration, `Drop` executes the drop glue for the specified place, after which
|
||||
/// it continues execution/unwinds at the given basic blocks. It is possible that executing drop
|
||||
/// glue is special - this would be part of Rust's memory model. (**FIXME**: due we have an
|
||||
/// issue tracking if drop glue has any interesting semantics in addition to those of a function
|
||||
/// call?)
|
||||
///
|
||||
/// `Drop` before drop elaboration is a *conditional* execution of the drop glue. Specifically, the
|
||||
/// `Drop` will be executed if...
|
||||
///
|
||||
/// **Needs clarification**: End of that sentence. This in effect should document the exact
|
||||
/// behavior of drop elaboration. The following sounds vaguely right, but I'm not quite sure:
|
||||
///
|
||||
/// > The drop glue is executed if, among all statements executed within this `Body`, an assignment to
|
||||
/// > the place or one of its "parents" occurred more recently than a move out of it. This does not
|
||||
/// > consider indirect assignments.
|
||||
Drop { place: Place<'tcx>, target: BasicBlock, unwind: Option<BasicBlock> },
|
||||
|
||||
/// Drops the place and assigns a new value to it.
|
||||
///
|
||||
/// This first performs the exact same operation as the pre drop-elaboration `Drop` terminator;
|
||||
/// it then additionally assigns the `value` to the `place` as if by an assignment statement.
|
||||
/// This assignment occurs both in the unwind and the regular code paths. The semantics are best
|
||||
/// explained by the elaboration:
|
||||
///
|
||||
/// ```ignore (MIR)
|
||||
/// BB0 {
|
||||
/// DropAndReplace(P <- V, goto BB1, unwind BB2)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// becomes
|
||||
///
|
||||
/// ```ignore (MIR)
|
||||
/// BB0 {
|
||||
/// Drop(P, goto BB1, unwind BB2)
|
||||
/// }
|
||||
/// BB1 {
|
||||
/// // P is now uninitialized
|
||||
/// P <- V
|
||||
/// }
|
||||
/// BB2 {
|
||||
/// // P is now uninitialized -- its dtor panicked
|
||||
/// P <- V
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Disallowed after drop elaboration.
|
||||
DropAndReplace {
|
||||
place: Place<'tcx>,
|
||||
value: Operand<'tcx>,
|
||||
target: BasicBlock,
|
||||
unwind: Option<BasicBlock>,
|
||||
},
|
||||
|
||||
/// Roughly speaking, evaluates the `func` operand and the arguments, and starts execution of
|
||||
/// the referred to function. The operand types must match the argument types of the function.
|
||||
/// The return place type must match the return type. The type of the `func` operand must be
|
||||
/// callable, meaning either a function pointer, a function type, or a closure type.
|
||||
///
|
||||
/// **Needs clarification**: The exact semantics of this. Current backends rely on `move`
|
||||
/// operands not aliasing the return place. It is unclear how this is justified in MIR, see
|
||||
/// [#71117].
|
||||
///
|
||||
/// [#71117]: https://github.com/rust-lang/rust/issues/71117
|
||||
Call {
|
||||
/// The function that’s being called.
|
||||
func: Operand<'tcx>,
|
||||
/// Arguments the function is called with.
|
||||
/// These are owned by the callee, which is free to modify them.
|
||||
/// This allows the memory occupied by "by-value" arguments to be
|
||||
/// reused across function calls without duplicating the contents.
|
||||
args: Vec<Operand<'tcx>>,
|
||||
/// Where the returned value will be written
|
||||
destination: Place<'tcx>,
|
||||
/// Where to go after this call returns. If none, the call necessarily diverges.
|
||||
target: Option<BasicBlock>,
|
||||
/// Cleanups to be done if the call unwinds.
|
||||
cleanup: Option<BasicBlock>,
|
||||
/// `true` if this is from a call in HIR rather than from an overloaded
|
||||
/// operator. True for overloaded function call.
|
||||
from_hir_call: bool,
|
||||
/// This `Span` is the span of the function, without the dot and receiver
|
||||
/// (e.g. `foo(a, b)` in `x.foo(a, b)`
|
||||
fn_span: Span,
|
||||
},
|
||||
|
||||
/// Evaluates the operand, which must have type `bool`. If it is not equal to `expected`,
|
||||
/// initiates a panic. Initiating a panic corresponds to a `Call` terminator with some
|
||||
/// unspecified constant as the function to call, all the operands stored in the `AssertMessage`
|
||||
/// as parameters, and `None` for the destination. Keep in mind that the `cleanup` path is not
|
||||
/// necessarily executed even in the case of a panic, for example in `-C panic=abort`. If the
|
||||
/// assertion does not fail, execution continues at the specified basic block.
|
||||
Assert {
|
||||
cond: Operand<'tcx>,
|
||||
expected: bool,
|
||||
msg: AssertMessage<'tcx>,
|
||||
target: BasicBlock,
|
||||
cleanup: Option<BasicBlock>,
|
||||
},
|
||||
|
||||
/// Marks a suspend point.
|
||||
///
|
||||
/// Like `Return` terminators in generator bodies, this computes `value` and then a
|
||||
/// `GeneratorState::Yielded(value)` as if by `Aggregate` rvalue. That value is then assigned to
|
||||
/// the return place of the function calling this one, and execution continues in the calling
|
||||
/// function. When next invoked with the same first argument, execution of this function
|
||||
/// continues at the `resume` basic block, with the second argument written to the `resume_arg`
|
||||
/// place. If the generator is dropped before then, the `drop` basic block is invoked.
|
||||
///
|
||||
/// Not permitted in bodies that are not generator bodies, or after generator lowering.
|
||||
///
|
||||
/// **Needs clarification**: What about the evaluation order of the `resume_arg` and `value`?
|
||||
Yield {
|
||||
/// The value to return.
|
||||
value: Operand<'tcx>,
|
||||
/// Where to resume to.
|
||||
resume: BasicBlock,
|
||||
/// The place to store the resume argument in.
|
||||
resume_arg: Place<'tcx>,
|
||||
/// Cleanup to be done if the generator is dropped at this suspend point.
|
||||
drop: Option<BasicBlock>,
|
||||
},
|
||||
|
||||
/// Indicates the end of dropping a generator.
|
||||
///
|
||||
/// Semantically just a `return` (from the generators drop glue). Only permitted in the same situations
|
||||
/// as `yield`.
|
||||
///
|
||||
/// **Needs clarification**: Is that even correct? The generator drop code is always confusing
|
||||
/// to me, because it's not even really in the current body.
|
||||
///
|
||||
/// **Needs clarification**: Are there type system constraints on these terminators? Should
|
||||
/// there be a "block type" like `cleanup` blocks for them?
|
||||
GeneratorDrop,
|
||||
|
||||
/// A block where control flow only ever takes one real path, but borrowck needs to be more
|
||||
/// conservative.
|
||||
///
|
||||
/// At runtime this is semantically just a goto.
|
||||
///
|
||||
/// Disallowed after drop elaboration.
|
||||
FalseEdge {
|
||||
/// The target normal control flow will take.
|
||||
real_target: BasicBlock,
|
||||
/// A block control flow could conceptually jump to, but won't in
|
||||
/// practice.
|
||||
imaginary_target: BasicBlock,
|
||||
},
|
||||
|
||||
/// A terminator for blocks that only take one path in reality, but where we reserve the right
|
||||
/// to unwind in borrowck, even if it won't happen in practice. This can arise in infinite loops
|
||||
/// with no function calls for example.
|
||||
///
|
||||
/// At runtime this is semantically just a goto.
|
||||
///
|
||||
/// Disallowed after drop elaboration.
|
||||
FalseUnwind {
|
||||
/// The target normal control flow will take.
|
||||
real_target: BasicBlock,
|
||||
/// The imaginary cleanup block link. This particular path will never be taken
|
||||
/// in practice, but in order to avoid fragility we want to always
|
||||
/// consider it in borrowck. We don't want to accept programs which
|
||||
/// pass borrowck only when `panic=abort` or some assertions are disabled
|
||||
/// due to release vs. debug mode builds. This needs to be an `Option` because
|
||||
/// of the `remove_noop_landing_pads` and `abort_unwinding_calls` passes.
|
||||
unwind: Option<BasicBlock>,
|
||||
},
|
||||
|
||||
/// Block ends with an inline assembly block. This is a terminator since
|
||||
/// inline assembly is allowed to diverge.
|
||||
InlineAsm {
|
||||
/// The template for the inline assembly, with placeholders.
|
||||
template: &'tcx [InlineAsmTemplatePiece],
|
||||
|
||||
/// The operands for the inline assembly, as `Operand`s or `Place`s.
|
||||
operands: Vec<InlineAsmOperand<'tcx>>,
|
||||
|
||||
/// Miscellaneous options for the inline assembly.
|
||||
options: InlineAsmOptions,
|
||||
|
||||
/// Source spans for each line of the inline assembly code. These are
|
||||
/// used to map assembler errors back to the line in the source code.
|
||||
line_spans: &'tcx [Span],
|
||||
|
||||
/// Destination block after the inline assembly returns, unless it is
|
||||
/// diverging (InlineAsmOptions::NORETURN).
|
||||
destination: Option<BasicBlock>,
|
||||
|
||||
/// Cleanup to be done if the inline assembly unwinds. This is present
|
||||
/// if and only if InlineAsmOptions::MAY_UNWIND is set.
|
||||
cleanup: Option<BasicBlock>,
|
||||
},
|
||||
}
|
||||
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)]
|
||||
pub struct Terminator<'tcx> {
|
||||
pub source_info: SourceInfo,
|
||||
pub kind: TerminatorKind<'tcx>,
|
||||
}
|
||||
|
||||
pub type Successors<'a> = impl Iterator<Item = BasicBlock> + 'a;
|
||||
pub type SuccessorsMut<'a> =
|
||||
iter::Chain<std::option::IntoIter<&'a mut BasicBlock>, slice::IterMut<'a, BasicBlock>>;
|
||||
|
||||
impl<'tcx> Terminator<'tcx> {
|
||||
pub fn successors(&self) -> Successors<'_> {
|
||||
self.kind.successors()
|
||||
|
@ -316,7 +316,12 @@ cc = ["@ehuss"]
|
||||
cc = ["@rust-lang/clippy"]
|
||||
|
||||
[mentions."src/tools/miri"]
|
||||
message = "The Miri submodule was changed"
|
||||
cc = ["@rust-lang/miri"]
|
||||
|
||||
[mentions."src/tools/rustfmt"]
|
||||
cc = ["@rust-lang/rustfmt"]
|
||||
|
||||
[mentions."compiler/rustc_middle/src/mir/syntax.rs"]
|
||||
message = "This PR changes MIR"
|
||||
cc = ["@oli-obk", "@RalfJung", "@JakobDegen"]
|
||||
|
Loading…
Reference in New Issue
Block a user