rust/compiler/rustc_middle/src/thir.rs
2022-06-09 17:00:38 +08:00

824 lines
24 KiB
Rust

//! THIR datatypes and definitions. See the [rustc dev guide] for more info.
//!
//! If you compare the THIR [`ExprKind`] to [`hir::ExprKind`], you will see it is
//! a good bit simpler. In fact, a number of the more straight-forward
//! MIR simplifications are already done in the lowering to THIR. For
//! example, method calls and overloaded operators are absent: they are
//! expected to be converted into [`ExprKind::Call`] instances.
//!
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/thir.html
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_hir as hir;
use rustc_hir::def::CtorKind;
use rustc_hir::def_id::DefId;
use rustc_hir::RangeEnd;
use rustc_index::newtype_index;
use rustc_index::vec::IndexVec;
use rustc_middle::infer::canonical::Canonical;
use rustc_middle::middle::region;
use rustc_middle::mir::interpret::AllocId;
use rustc_middle::mir::{self, BinOp, BorrowKind, FakeReadCause, Field, Mutability, UnOp};
use rustc_middle::ty::adjustment::PointerCast;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::CanonicalUserTypeAnnotation;
use rustc_middle::ty::{self, AdtDef, Ty, UpvarSubsts, UserType};
use rustc_span::{Span, Symbol, DUMMY_SP};
use rustc_target::abi::VariantIdx;
use rustc_target::asm::InlineAsmRegOrRegClass;
use std::fmt;
use std::ops::Index;
pub mod abstract_const;
pub mod visit;
newtype_index! {
/// An index to an [`Arm`] stored in [`Thir::arms`]
#[derive(HashStable)]
pub struct ArmId {
DEBUG_FORMAT = "a{}"
}
}
newtype_index! {
/// An index to an [`Expr`] stored in [`Thir::exprs`]
#[derive(HashStable)]
pub struct ExprId {
DEBUG_FORMAT = "e{}"
}
}
newtype_index! {
#[derive(HashStable)]
/// An index to a [`Stmt`] stored in [`Thir::stmts`]
pub struct StmtId {
DEBUG_FORMAT = "s{}"
}
}
macro_rules! thir_with_elements {
($($name:ident: $id:ty => $value:ty,)*) => {
/// A container for a THIR body.
///
/// This can be indexed directly by any THIR index (e.g. [`ExprId`]).
#[derive(Debug, HashStable, Clone)]
pub struct Thir<'tcx> {
$(
pub $name: IndexVec<$id, $value>,
)*
}
impl<'tcx> Thir<'tcx> {
pub fn new() -> Thir<'tcx> {
Thir {
$(
$name: IndexVec::new(),
)*
}
}
}
$(
impl<'tcx> Index<$id> for Thir<'tcx> {
type Output = $value;
fn index(&self, index: $id) -> &Self::Output {
&self.$name[index]
}
}
)*
}
}
thir_with_elements! {
arms: ArmId => Arm<'tcx>,
exprs: ExprId => Expr<'tcx>,
stmts: StmtId => Stmt<'tcx>,
}
#[derive(Copy, Clone, Debug, HashStable)]
pub enum LintLevel {
Inherited,
Explicit(hir::HirId),
}
#[derive(Clone, Debug, HashStable)]
pub struct Block {
/// Whether the block itself has a label. Used by `label: {}`
/// and `try` blocks.
///
/// This does *not* include labels on loops, e.g. `'label: loop {}`.
pub targeted_by_break: bool,
pub region_scope: region::Scope,
pub opt_destruction_scope: Option<region::Scope>,
/// The span of the block, including the opening braces,
/// the label, and the `unsafe` keyword, if present.
pub span: Span,
/// The statements in the blocK.
pub stmts: Box<[StmtId]>,
/// The trailing expression of the block, if any.
pub expr: Option<ExprId>,
pub safety_mode: BlockSafety,
}
#[derive(Clone, Debug, HashStable)]
pub struct Adt<'tcx> {
/// The ADT we're constructing.
pub adt_def: AdtDef<'tcx>,
/// The variant of the ADT.
pub variant_index: VariantIdx,
pub substs: SubstsRef<'tcx>,
/// Optional user-given substs: for something like `let x =
/// Bar::<T> { ... }`.
pub user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
pub fields: Box<[FieldExpr]>,
/// The base, e.g. `Foo {x: 1, .. base}`.
pub base: Option<FruInfo<'tcx>>,
}
#[derive(Copy, Clone, Debug, HashStable)]
pub enum BlockSafety {
Safe,
/// A compiler-generated unsafe block
BuiltinUnsafe,
/// An `unsafe` block. The `HirId` is the ID of the block.
ExplicitUnsafe(hir::HirId),
}
#[derive(Clone, Debug, HashStable)]
pub struct Stmt<'tcx> {
pub kind: StmtKind<'tcx>,
pub opt_destruction_scope: Option<region::Scope>,
}
#[derive(Clone, Debug, HashStable)]
pub enum StmtKind<'tcx> {
/// An expression with a trailing semicolon.
Expr {
/// The scope for this statement; may be used as lifetime of temporaries.
scope: region::Scope,
/// The expression being evaluated in this statement.
expr: ExprId,
},
/// A `let` binding.
Let {
/// The scope for variables bound in this `let`; it covers this and
/// all the remaining statements in the block.
remainder_scope: region::Scope,
/// The scope for the initialization itself; might be used as
/// lifetime of temporaries.
init_scope: region::Scope,
/// `let <PAT> = ...`
///
/// If a type annotation is included, it is added as an ascription pattern.
pattern: Pat<'tcx>,
/// `let pat: ty = <INIT>`
initializer: Option<ExprId>,
/// The lint level for this `let` statement.
lint_level: LintLevel,
},
}
// `Expr` is used a lot. Make sure it doesn't unintentionally get bigger.
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
rustc_data_structures::static_assert_size!(Expr<'_>, 104);
#[derive(
Clone,
Debug,
Copy,
PartialEq,
Eq,
Hash,
HashStable,
TyEncodable,
TyDecodable,
TypeFoldable
)]
pub struct LocalVarId(pub hir::HirId);
/// A THIR expression.
#[derive(Clone, Debug, HashStable)]
pub struct Expr<'tcx> {
/// The type of this expression
pub ty: Ty<'tcx>,
/// The lifetime of this expression if it should be spilled into a
/// temporary; should be `None` only if in a constant context
pub temp_lifetime: Option<region::Scope>,
/// span of the expression in the source
pub span: Span,
/// kind of expression
pub kind: ExprKind<'tcx>,
}
#[derive(Clone, Debug, HashStable)]
pub enum ExprKind<'tcx> {
/// `Scope`s are used to explicitly mark destruction scopes,
/// and to track the `HirId` of the expressions within the scope.
Scope {
region_scope: region::Scope,
lint_level: LintLevel,
value: ExprId,
},
/// A `box <value>` expression.
Box {
value: ExprId,
},
/// An `if` expression.
If {
if_then_scope: region::Scope,
cond: ExprId,
then: ExprId,
else_opt: Option<ExprId>,
},
/// A function call. Method calls and overloaded operators are converted to plain function calls.
Call {
/// The type of the function. This is often a [`FnDef`] or a [`FnPtr`].
///
/// [`FnDef`]: ty::TyKind::FnDef
/// [`FnPtr`]: ty::TyKind::FnPtr
ty: Ty<'tcx>,
/// The function itself.
fun: ExprId,
/// The arguments passed to the function.
///
/// Note: in some cases (like calling a closure), the function call `f(...args)` gets
/// rewritten as a call to a function trait method (e.g. `FnOnce::call_once(f, (...args))`).
args: Box<[ExprId]>,
/// Whether this is from an overloaded operator rather than a
/// function call from HIR. `true` for overloaded function call.
from_hir_call: bool,
/// The span of the function, without the dot and receiver
/// (e.g. `foo(a, b)` in `x.foo(a, b)`).
fn_span: Span,
},
/// A *non-overloaded* dereference.
Deref {
arg: ExprId,
},
/// A *non-overloaded* binary operation.
Binary {
op: BinOp,
lhs: ExprId,
rhs: ExprId,
},
/// A logical operation. This is distinct from `BinaryOp` because
/// the operands need to be lazily evaluated.
LogicalOp {
op: LogicalOp,
lhs: ExprId,
rhs: ExprId,
},
/// A *non-overloaded* unary operation. Note that here the deref (`*`)
/// operator is represented by `ExprKind::Deref`.
Unary {
op: UnOp,
arg: ExprId,
},
/// A cast: `<source> as <type>`. The type we cast to is the type of
/// the parent expression.
Cast {
source: ExprId,
},
Use {
source: ExprId,
}, // Use a lexpr to get a vexpr.
/// A coercion from `!` to any type.
NeverToAny {
source: ExprId,
},
/// A pointer cast. More information can be found in [`PointerCast`].
Pointer {
cast: PointerCast,
source: ExprId,
},
/// A `loop` expression.
Loop {
body: ExprId,
},
Let {
expr: ExprId,
pat: Pat<'tcx>,
},
/// A `match` expression.
Match {
scrutinee: ExprId,
arms: Box<[ArmId]>,
},
/// A block.
Block {
body: Block,
},
/// An assignment: `lhs = rhs`.
Assign {
lhs: ExprId,
rhs: ExprId,
},
/// A *non-overloaded* operation assignment, e.g. `lhs += rhs`.
AssignOp {
op: BinOp,
lhs: ExprId,
rhs: ExprId,
},
/// Access to a field of a struct, a tuple, an union, or an enum.
Field {
lhs: ExprId,
/// Variant containing the field.
variant_index: VariantIdx,
/// This can be a named (`.foo`) or unnamed (`.0`) field.
name: Field,
},
/// A *non-overloaded* indexing operation.
Index {
lhs: ExprId,
index: ExprId,
},
/// A local variable.
VarRef {
id: LocalVarId,
},
/// Used to represent upvars mentioned in a closure/generator
UpvarRef {
/// DefId of the closure/generator
closure_def_id: DefId,
/// HirId of the root variable
var_hir_id: LocalVarId,
},
/// A borrow, e.g. `&arg`.
Borrow {
borrow_kind: BorrowKind,
arg: ExprId,
},
/// A `&raw [const|mut] $place_expr` raw borrow resulting in type `*[const|mut] T`.
AddressOf {
mutability: hir::Mutability,
arg: ExprId,
},
/// A `break` expression.
Break {
label: region::Scope,
value: Option<ExprId>,
},
/// A `continue` expression.
Continue {
label: region::Scope,
},
/// A `return` expression.
Return {
value: Option<ExprId>,
},
/// An inline `const` block, e.g. `const {}`.
ConstBlock {
did: DefId,
substs: SubstsRef<'tcx>,
},
/// An array literal constructed from one repeated element, e.g. `[1; 5]`.
Repeat {
value: ExprId,
count: ty::Const<'tcx>,
},
/// An array, e.g. `[a, b, c, d]`.
Array {
fields: Box<[ExprId]>,
},
/// A tuple, e.g. `(a, b, c, d)`.
Tuple {
fields: Box<[ExprId]>,
},
/// An ADT constructor, e.g. `Foo {x: 1, y: 2}`.
Adt(Box<Adt<'tcx>>),
/// A type ascription on a place.
PlaceTypeAscription {
source: ExprId,
/// Type that the user gave to this expression
user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
},
/// A type ascription on a value, e.g. `42: i32`.
ValueTypeAscription {
source: ExprId,
/// Type that the user gave to this expression
user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
},
/// A closure definition.
Closure {
closure_id: DefId,
substs: UpvarSubsts<'tcx>,
upvars: Box<[ExprId]>,
movability: Option<hir::Movability>,
fake_reads: Vec<(ExprId, FakeReadCause, hir::HirId)>,
},
/// A literal.
Literal {
lit: &'tcx hir::Lit,
neg: bool,
},
/// For literals that don't correspond to anything in the HIR
NonHirLiteral {
lit: ty::ScalarInt,
user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
},
/// Associated constants and named constants
NamedConst {
def_id: DefId,
substs: SubstsRef<'tcx>,
user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
},
ConstParam {
param: ty::ParamConst,
def_id: DefId,
},
// FIXME improve docs for `StaticRef` by distinguishing it from `NamedConst`
/// A literal containing the address of a `static`.
///
/// This is only distinguished from `Literal` so that we can register some
/// info for diagnostics.
StaticRef {
alloc_id: AllocId,
ty: Ty<'tcx>,
def_id: DefId,
},
/// Inline assembly, i.e. `asm!()`.
InlineAsm {
template: &'tcx [InlineAsmTemplatePiece],
operands: Box<[InlineAsmOperand<'tcx>]>,
options: InlineAsmOptions,
line_spans: &'tcx [Span],
},
/// An expression taking a reference to a thread local.
ThreadLocalRef(DefId),
/// A `yield` expression.
Yield {
value: ExprId,
},
}
impl<'tcx> ExprKind<'tcx> {
pub fn zero_sized_literal(user_ty: Option<Canonical<'tcx, UserType<'tcx>>>) -> Self {
ExprKind::NonHirLiteral { lit: ty::ScalarInt::ZST, user_ty }
}
}
/// Represents the association of a field identifier and an expression.
///
/// This is used in struct constructors.
#[derive(Clone, Debug, HashStable)]
pub struct FieldExpr {
pub name: Field,
pub expr: ExprId,
}
#[derive(Clone, Debug, HashStable)]
pub struct FruInfo<'tcx> {
pub base: ExprId,
pub field_types: Box<[Ty<'tcx>]>,
}
/// A `match` arm.
#[derive(Clone, Debug, HashStable)]
pub struct Arm<'tcx> {
pub pattern: Pat<'tcx>,
pub guard: Option<Guard<'tcx>>,
pub body: ExprId,
pub lint_level: LintLevel,
pub scope: region::Scope,
pub span: Span,
}
/// A `match` guard.
#[derive(Clone, Debug, HashStable)]
pub enum Guard<'tcx> {
If(ExprId),
IfLet(Pat<'tcx>, ExprId),
}
#[derive(Copy, Clone, Debug, HashStable)]
pub enum LogicalOp {
/// The `&&` operator.
And,
/// The `||` operator.
Or,
}
#[derive(Clone, Debug, HashStable)]
pub enum InlineAsmOperand<'tcx> {
In {
reg: InlineAsmRegOrRegClass,
expr: ExprId,
},
Out {
reg: InlineAsmRegOrRegClass,
late: bool,
expr: Option<ExprId>,
},
InOut {
reg: InlineAsmRegOrRegClass,
late: bool,
expr: ExprId,
},
SplitInOut {
reg: InlineAsmRegOrRegClass,
late: bool,
in_expr: ExprId,
out_expr: Option<ExprId>,
},
Const {
value: mir::ConstantKind<'tcx>,
span: Span,
},
SymFn {
value: mir::ConstantKind<'tcx>,
span: Span,
},
SymStatic {
def_id: DefId,
},
}
#[derive(Copy, Clone, Debug, PartialEq, HashStable)]
pub enum BindingMode {
ByValue,
ByRef(BorrowKind),
}
#[derive(Clone, Debug, HashStable)]
pub struct FieldPat<'tcx> {
pub field: Field,
pub pattern: Pat<'tcx>,
}
#[derive(Clone, Debug, HashStable)]
pub struct Pat<'tcx> {
pub ty: Ty<'tcx>,
pub span: Span,
pub kind: Box<PatKind<'tcx>>,
}
impl<'tcx> Pat<'tcx> {
pub fn wildcard_from_ty(ty: Ty<'tcx>) -> Self {
Pat { ty, span: DUMMY_SP, kind: Box::new(PatKind::Wild) }
}
}
#[derive(Clone, Debug, HashStable)]
pub struct Ascription<'tcx> {
pub annotation: CanonicalUserTypeAnnotation<'tcx>,
/// Variance to use when relating the `user_ty` to the **type of the value being
/// matched**. Typically, this is `Variance::Covariant`, since the value being matched must
/// have a type that is some subtype of the ascribed type.
///
/// Note that this variance does not apply for any bindings within subpatterns. The type
/// assigned to those bindings must be exactly equal to the `user_ty` given here.
///
/// The only place where this field is not `Covariant` is when matching constants, where
/// we currently use `Contravariant` -- this is because the constant type just needs to
/// be "comparable" to the type of the input value. So, for example:
///
/// ```text
/// match x { "foo" => .. }
/// ```
///
/// requires that `&'static str <: T_x`, where `T_x` is the type of `x`. Really, we should
/// probably be checking for a `PartialEq` impl instead, but this preserves the behavior
/// of the old type-check for now. See #57280 for details.
pub variance: ty::Variance,
}
#[derive(Clone, Debug, HashStable)]
pub enum PatKind<'tcx> {
/// A wildcard pattern: `_`.
Wild,
AscribeUserType {
ascription: Ascription<'tcx>,
subpattern: Pat<'tcx>,
},
/// `x`, `ref x`, `x @ P`, etc.
Binding {
mutability: Mutability,
name: Symbol,
mode: BindingMode,
var: LocalVarId,
ty: Ty<'tcx>,
subpattern: Option<Pat<'tcx>>,
/// Is this the leftmost occurrence of the binding, i.e., is `var` the
/// `HirId` of this pattern?
is_primary: bool,
},
/// `Foo(...)` or `Foo{...}` or `Foo`, where `Foo` is a variant name from an ADT with
/// multiple variants.
Variant {
adt_def: AdtDef<'tcx>,
substs: SubstsRef<'tcx>,
variant_index: VariantIdx,
subpatterns: Vec<FieldPat<'tcx>>,
},
/// `(...)`, `Foo(...)`, `Foo{...}`, or `Foo`, where `Foo` is a variant name from an ADT with
/// a single variant.
Leaf {
subpatterns: Vec<FieldPat<'tcx>>,
},
/// `box P`, `&P`, `&mut P`, etc.
Deref {
subpattern: Pat<'tcx>,
},
/// One of the following:
/// * `&str`, which will be handled as a string pattern and thus exhaustiveness
/// checking will detect if you use the same string twice in different patterns.
/// * integer, bool, char or float, which will be handled by exhaustiveness to cover exactly
/// its own value, similar to `&str`, but these values are much simpler.
/// * Opaque constants, that must not be matched structurally. So anything that does not derive
/// `PartialEq` and `Eq`.
Constant {
value: mir::ConstantKind<'tcx>,
},
Range(PatRange<'tcx>),
/// Matches against a slice, checking the length and extracting elements.
/// irrefutable when there is a slice pattern and both `prefix` and `suffix` are empty.
/// e.g., `&[ref xs @ ..]`.
Slice {
prefix: Vec<Pat<'tcx>>,
slice: Option<Pat<'tcx>>,
suffix: Vec<Pat<'tcx>>,
},
/// Fixed match against an array; irrefutable.
Array {
prefix: Vec<Pat<'tcx>>,
slice: Option<Pat<'tcx>>,
suffix: Vec<Pat<'tcx>>,
},
/// An or-pattern, e.g. `p | q`.
/// Invariant: `pats.len() >= 2`.
Or {
pats: Vec<Pat<'tcx>>,
},
}
#[derive(Copy, Clone, Debug, PartialEq, HashStable)]
pub struct PatRange<'tcx> {
pub lo: mir::ConstantKind<'tcx>,
pub hi: mir::ConstantKind<'tcx>,
pub end: RangeEnd,
}
impl<'tcx> fmt::Display for Pat<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// Printing lists is a chore.
let mut first = true;
let mut start_or_continue = |s| {
if first {
first = false;
""
} else {
s
}
};
let mut start_or_comma = || start_or_continue(", ");
match *self.kind {
PatKind::Wild => write!(f, "_"),
PatKind::AscribeUserType { ref subpattern, .. } => write!(f, "{}: _", subpattern),
PatKind::Binding { mutability, name, mode, ref subpattern, .. } => {
let is_mut = match mode {
BindingMode::ByValue => mutability == Mutability::Mut,
BindingMode::ByRef(bk) => {
write!(f, "ref ")?;
matches!(bk, BorrowKind::Mut { .. })
}
};
if is_mut {
write!(f, "mut ")?;
}
write!(f, "{}", name)?;
if let Some(ref subpattern) = *subpattern {
write!(f, " @ {}", subpattern)?;
}
Ok(())
}
PatKind::Variant { ref subpatterns, .. } | PatKind::Leaf { ref subpatterns } => {
let variant = match *self.kind {
PatKind::Variant { adt_def, variant_index, .. } => {
Some(adt_def.variant(variant_index))
}
_ => self.ty.ty_adt_def().and_then(|adt| {
if !adt.is_enum() { Some(adt.non_enum_variant()) } else { None }
}),
};
if let Some(variant) = variant {
write!(f, "{}", variant.name)?;
// Only for Adt we can have `S {...}`,
// which we handle separately here.
if variant.ctor_kind == CtorKind::Fictive {
write!(f, " {{ ")?;
let mut printed = 0;
for p in subpatterns {
if let PatKind::Wild = *p.pattern.kind {
continue;
}
let name = variant.fields[p.field.index()].name;
write!(f, "{}{}: {}", start_or_comma(), name, p.pattern)?;
printed += 1;
}
if printed < variant.fields.len() {
write!(f, "{}..", start_or_comma())?;
}
return write!(f, " }}");
}
}
let num_fields = variant.map_or(subpatterns.len(), |v| v.fields.len());
if num_fields != 0 || variant.is_none() {
write!(f, "(")?;
for i in 0..num_fields {
write!(f, "{}", start_or_comma())?;
// Common case: the field is where we expect it.
if let Some(p) = subpatterns.get(i) {
if p.field.index() == i {
write!(f, "{}", p.pattern)?;
continue;
}
}
// Otherwise, we have to go looking for it.
if let Some(p) = subpatterns.iter().find(|p| p.field.index() == i) {
write!(f, "{}", p.pattern)?;
} else {
write!(f, "_")?;
}
}
write!(f, ")")?;
}
Ok(())
}
PatKind::Deref { ref subpattern } => {
match self.ty.kind() {
ty::Adt(def, _) if def.is_box() => write!(f, "box ")?,
ty::Ref(_, _, mutbl) => {
write!(f, "&{}", mutbl.prefix_str())?;
}
_ => bug!("{} is a bad Deref pattern type", self.ty),
}
write!(f, "{}", subpattern)
}
PatKind::Constant { value } => write!(f, "{}", value),
PatKind::Range(PatRange { lo, hi, end }) => {
write!(f, "{}", lo)?;
write!(f, "{}", end)?;
write!(f, "{}", hi)
}
PatKind::Slice { ref prefix, ref slice, ref suffix }
| PatKind::Array { ref prefix, ref slice, ref suffix } => {
write!(f, "[")?;
for p in prefix {
write!(f, "{}{}", start_or_comma(), p)?;
}
if let Some(ref slice) = *slice {
write!(f, "{}", start_or_comma())?;
match *slice.kind {
PatKind::Wild => {}
_ => write!(f, "{}", slice)?,
}
write!(f, "..")?;
}
for p in suffix {
write!(f, "{}{}", start_or_comma(), p)?;
}
write!(f, "]")
}
PatKind::Or { ref pats } => {
for pat in pats {
write!(f, "{}{}", start_or_continue(" | "), pat)?;
}
Ok(())
}
}
}
}