From 30793c1e819550288a9a17ddeccac06be85b61a2 Mon Sep 17 00:00:00 2001 From: LeSeulArtichaut Date: Mon, 14 Jun 2021 17:39:25 +0200 Subject: [PATCH] Add documentation for various THIR structs --- compiler/rustc_middle/src/thir.rs | 144 ++++++++++++++++++++++-------- 1 file changed, 109 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 6b2e542ee7051..cdefc9effa1e9 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -1,3 +1,13 @@ +//! 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; @@ -24,6 +34,7 @@ use std::fmt; use std::ops::Index; newtype_index! { + /// An index to an [`Arm`] stored in [`Thir::arms`] #[derive(HashStable)] pub struct ArmId { DEBUG_FORMAT = "a{}" @@ -31,6 +42,7 @@ newtype_index! { } newtype_index! { + /// An index to an [`Expr`] stored in [`Thir::exprs`] #[derive(HashStable)] pub struct ExprId { DEBUG_FORMAT = "e{}" @@ -39,6 +51,7 @@ newtype_index! { newtype_index! { #[derive(HashStable)] + /// An index to a [`Stmt`] stored in [`Thir::stmts`] pub struct StmtId { DEBUG_FORMAT = "s{}" } @@ -46,6 +59,9 @@ newtype_index! { 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)] pub struct Thir<'tcx> { $( @@ -88,18 +104,28 @@ pub enum LintLevel { #[derive(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, + /// 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, pub safety_mode: BlockSafety, } #[derive(Debug, HashStable)] pub struct Adt<'tcx> { + /// The ADT we're constructing. pub adt_def: &'tcx AdtDef, + /// The variant of the ADT. pub variant_index: VariantIdx, pub substs: SubstsRef<'tcx>, @@ -108,13 +134,16 @@ pub struct Adt<'tcx> { pub user_ty: Option>>, pub fields: Box<[FieldExpr]>, + /// The base, e.g. `Foo {x: 1, .. base}`. pub base: Option>, } #[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), } @@ -126,32 +155,34 @@ pub struct Stmt<'tcx> { #[derive(Debug, HashStable)] pub enum StmtKind<'tcx> { + /// An expression with a trailing semicolon. Expr { - /// scope for this statement; may be used as lifetime of temporaries + /// The scope for this statement; may be used as lifetime of temporaries. scope: region::Scope, - /// expression being evaluated in this statement + /// The expression being evaluated in this statement. expr: ExprId, }, + /// A `let` binding. Let { - /// scope for variables bound in this let; covers this and - /// remaining statements in block + /// The scope for variables bound in this `let`; it covers this and + /// all the remaining statements in the block. remainder_scope: region::Scope, - /// scope for the initialization itself; might be used as - /// lifetime of temporaries + /// The scope for the initialization itself; might be used as + /// lifetime of temporaries. init_scope: region::Scope, /// `let = ...` /// - /// if a type is included, it is added as an ascription pattern + /// If a type annotation is included, it is added as an ascription pattern. pattern: Pat<'tcx>, - /// let pat: ty = ... + /// `let pat: ty = ` initializer: Option, - /// the lint level for this let-statement + /// The lint level for this `let` statement. lint_level: LintLevel, }, } @@ -160,27 +191,14 @@ pub enum StmtKind<'tcx> { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(Expr<'_>, 104); -/// The Thir trait implementor lowers their expressions (`&'tcx H::Expr`) -/// into instances of this `Expr` enum. This lowering can be done -/// basically as lazily or as eagerly as desired: every recursive -/// reference to an expression in this enum is an `ExprId`, which -/// may in turn be another instance of this enum (boxed), or else an -/// unlowered `&'tcx H::Expr`. Note that instances of `Expr` are very -/// short-lived. They are created by `Thir::to_expr`, analyzed and -/// converted into MIR, and then discarded. -/// -/// If you compare `Expr` to the full compiler AST, 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 impl of `Thir`. For -/// example, method calls and overloaded operators are absent: they are -/// expected to be converted into `Expr::Call` instances. +/// A THIR expression. #[derive(Debug, HashStable)] pub struct Expr<'tcx> { - /// type of this expression + /// The type of this expression pub ty: Ty<'tcx>, - /// lifetime of this expression if it should be spilled into a - /// temporary; should be None only if in a constant context + /// 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, /// span of the expression in the source @@ -192,88 +210,120 @@ pub struct Expr<'tcx> { #[derive(Debug, HashStable)] pub enum ExprKind<'tcx> { + /// `Scope`s are used to explicitely 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 ` expression. Box { value: ExprId, }, + /// An `if` expression. If { cond: ExprId, then: ExprId, else_opt: Option, }, + /// 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 a call in HIR, rather than from an overloaded - /// operator. `true` for overloaded function call. + /// Whether this is from an overloaded operator rather than a + /// function call from HIR. `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)` + /// 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, - }, // NOT overloaded! + }, + /// A *non-overloaded* binary operation. Binary { op: BinOp, lhs: ExprId, rhs: ExprId, - }, // NOT overloaded! + }, + /// A logical operation. This is distinct from `BinaryOp` because + /// the operands need to be lazily evaluated. LogicalOp { op: LogicalOp, lhs: ExprId, rhs: ExprId, - }, // NOT overloaded! - // LogicalOp is distinct from BinaryOp because of lazy evaluation of the operands. + }, + /// A *non-overloaded* unary operation. Note that here the deref (`*`) + /// operator is represented by `ExprKind::Deref`. Unary { op: UnOp, arg: ExprId, - }, // NOT overloaded! + }, + /// A cast: ` as `. 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, }, + /// 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 struct or tuple field. Field { lhs: ExprId, + /// 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: hir::HirId, }, @@ -285,6 +335,7 @@ pub enum ExprKind<'tcx> { /// HirId of the root variable var_hir_id: hir::HirId, }, + /// A borrow, e.g. `&arg`. Borrow { borrow_kind: BorrowKind, arg: ExprId, @@ -294,40 +345,51 @@ pub enum ExprKind<'tcx> { mutability: hir::Mutability, arg: ExprId, }, + /// A `break` expression. Break { label: region::Scope, value: Option, }, + /// A `continue` expression. Continue { label: region::Scope, }, + /// A `return` expression. Return { value: Option, }, + /// An inline `const` block, e.g. `const {}`. ConstBlock { value: &'tcx Const<'tcx>, }, + /// An array literal constructed from one repeated element, e.g. `[1; 5]`. Repeat { value: ExprId, count: &'tcx 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>), + /// A type ascription on a place. PlaceTypeAscription { source: ExprId, /// Type that the user gave to this expression user_ty: Option>>, }, + /// A type ascription on a value, e.g. `42: i32`. ValueTypeAscription { source: ExprId, /// Type that the user gave to this expression user_ty: Option>>, }, + /// A closure definition. Closure { closure_id: DefId, substs: UpvarSubsts<'tcx>, @@ -335,6 +397,7 @@ pub enum ExprKind<'tcx> { movability: Option, fake_reads: Vec<(ExprId, FakeReadCause, hir::HirId)>, }, + /// A literal. Literal { literal: &'tcx Const<'tcx>, user_ty: Option>>, @@ -351,6 +414,7 @@ pub enum ExprKind<'tcx> { literal: &'tcx Const<'tcx>, def_id: DefId, }, + /// Inline assembly, i.e. `asm!()`. InlineAsm { template: &'tcx [InlineAsmTemplatePiece], operands: Box<[InlineAsmOperand<'tcx>]>, @@ -359,16 +423,21 @@ pub enum ExprKind<'tcx> { }, /// An expression taking a reference to a thread local. ThreadLocalRef(DefId), + /// Inline LLVM assembly, i.e. `llvm_asm!()`. LlvmInlineAsm { asm: &'tcx hir::LlvmInlineAsmInner, outputs: Box<[ExprId]>, inputs: Box<[ExprId]>, }, + /// A `yield` expression. Yield { value: ExprId, }, } +/// Represents the association of a field identifier and an expression. +/// +/// This is used in struct constructors. #[derive(Debug, HashStable)] pub struct FieldExpr { pub name: Field, @@ -381,6 +450,7 @@ pub struct FruInfo<'tcx> { pub field_types: Box<[Ty<'tcx>]>, } +/// A `match` arm. #[derive(Debug, HashStable)] pub struct Arm<'tcx> { pub pattern: Pat<'tcx>, @@ -391,6 +461,7 @@ pub struct Arm<'tcx> { pub span: Span, } +/// A `match` guard. #[derive(Debug, HashStable)] pub enum Guard<'tcx> { If(ExprId), @@ -399,7 +470,9 @@ pub enum Guard<'tcx> { #[derive(Copy, Clone, Debug, HashStable)] pub enum LogicalOp { + /// The `&&` operator. And, + /// The `||` operator. Or, } @@ -516,6 +589,7 @@ pub struct Ascription<'tcx> { #[derive(Clone, Debug, PartialEq, HashStable)] pub enum PatKind<'tcx> { + /// A wildward pattern: `_`. Wild, AscribeUserType {