summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_middle/src/thir.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_middle/src/thir.rs')
-rw-r--r--compiler/rustc_middle/src/thir.rs821
1 files changed, 821 insertions, 0 deletions
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs
new file mode 100644
index 000000000..b856af1d8
--- /dev/null
+++ b/compiler/rustc_middle/src/thir.rs
@@ -0,0 +1,821 @@
+//! 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 rustc_span::def_id::LocalDefId;
+use std::fmt;
+use std::ops::Index;
+
+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>,
+
+ /// `let pat: ty = <INIT> else { <ELSE> }
+ else_block: Option<Block>,
+
+ /// The lint level for this `let` statement.
+ lint_level: LintLevel,
+ },
+}
+
+#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable)]
+#[derive(TypeFoldable, TypeVisitable)]
+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: LocalDefId,
+ 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>>>,
+ },
+ /// A literal of a ZST type.
+ ZstLiteral {
+ 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,
+ },
+}
+
+/// 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(())
+ }
+ }
+ }
+}
+
+// Some nodes are used a lot. Make sure they don't unintentionally get bigger.
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+mod size_asserts {
+ use super::*;
+ // These are in alphabetical order, which is easy to maintain.
+ rustc_data_structures::static_assert_size!(Block, 56);
+ rustc_data_structures::static_assert_size!(Expr<'_>, 104);
+ rustc_data_structures::static_assert_size!(Pat<'_>, 24);
+ rustc_data_structures::static_assert_size!(Stmt<'_>, 120);
+}