//! This module describes hir-level representation of expressions. //! //! This representaion is: //! //! 1. Identity-based. Each expression has an `id`, so we can distinguish //! between different `1` in `1 + 1`. //! 2. Independent of syntax. Though syntactic provenance information can be //! attached separately via id-based side map. //! 3. Unresolved. Paths are stored as sequences of names, and not as defs the //! names refer to. //! 4. Desugared. There's no `if let`. //! //! See also a neighboring `body` module. use hir_expand::name::Name; use ra_arena::{impl_arena_id, RawId}; use crate::{ builtin_type::{BuiltinFloat, BuiltinInt}, path::{GenericArgs, Path}, type_ref::{Mutability, TypeRef}, }; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ExprId(RawId); impl_arena_id!(ExprId); impl ExprId { pub fn dummy() -> ExprId { ExprId((!0).into()) } } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PatId(RawId); impl_arena_id!(PatId); #[derive(Debug, Clone, Eq, PartialEq)] pub enum Literal { String(String), ByteString(Vec), Char(char), Bool(bool), Int(u64, Option), Float(u64, Option), // FIXME: f64 is not Eq } #[derive(Debug, Clone, Eq, PartialEq)] pub enum Expr { /// This is produced if syntax tree does not have a required expression piece. Missing, Path(Path), If { condition: ExprId, then_branch: ExprId, else_branch: Option, }, Block { statements: Vec, tail: Option, }, Loop { body: ExprId, }, While { condition: ExprId, body: ExprId, }, For { iterable: ExprId, pat: PatId, body: ExprId, }, Call { callee: ExprId, args: Vec, }, MethodCall { receiver: ExprId, method_name: Name, args: Vec, generic_args: Option, }, Match { expr: ExprId, arms: Vec, }, Continue, Break { expr: Option, }, Return { expr: Option, }, RecordLit { path: Option, fields: Vec, spread: Option, }, Field { expr: ExprId, name: Name, }, Await { expr: ExprId, }, Try { expr: ExprId, }, TryBlock { body: ExprId, }, Cast { expr: ExprId, type_ref: TypeRef, }, Ref { expr: ExprId, mutability: Mutability, }, Box { expr: ExprId, }, UnaryOp { expr: ExprId, op: UnaryOp, }, BinaryOp { lhs: ExprId, rhs: ExprId, op: Option, }, RangeFull, RangeFrom { lhs: ExprId, }, RangeTo { rhs: ExprId, }, Range { lhs: ExprId, rhs: ExprId, }, RangeToInclusive { rhs: ExprId, }, RangeInclusive { lhs: ExprId, rhs: ExprId, }, Index { base: ExprId, index: ExprId, }, Lambda { args: Vec, arg_types: Vec>, body: ExprId, }, Tuple { exprs: Vec, }, Array(Array), Literal(Literal), } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum BinaryOp { LogicOp(LogicOp), ArithOp(ArithOp), CmpOp(CmpOp), Assignment { op: Option }, } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum LogicOp { And, Or, } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum CmpOp { Eq { negated: bool }, Ord { ordering: Ordering, strict: bool }, } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum Ordering { Less, Greater, } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum ArithOp { Add, Mul, Sub, Div, Rem, Shl, Shr, BitXor, BitOr, BitAnd, } pub use ra_syntax::ast::PrefixOp as UnaryOp; #[derive(Debug, Clone, Eq, PartialEq)] pub enum Array { ElementList(Vec), Repeat { initializer: ExprId, repeat: ExprId }, } #[derive(Debug, Clone, Eq, PartialEq)] pub struct MatchArm { pub pats: Vec, pub guard: Option, pub expr: ExprId, } #[derive(Debug, Clone, Eq, PartialEq)] pub struct RecordLitField { pub name: Name, pub expr: ExprId, } #[derive(Debug, Clone, Eq, PartialEq)] pub enum Statement { Let { pat: PatId, type_ref: Option, initializer: Option }, Expr(ExprId), } impl Expr { pub fn walk_child_exprs(&self, mut f: impl FnMut(ExprId)) { match self { Expr::Missing => {} Expr::Path(_) => {} Expr::If { condition, then_branch, else_branch } => { f(*condition); f(*then_branch); if let Some(else_branch) = else_branch { f(*else_branch); } } Expr::Block { statements, tail } => { for stmt in statements { match stmt { Statement::Let { initializer, .. } => { if let Some(expr) = initializer { f(*expr); } } Statement::Expr(e) => f(*e), } } if let Some(expr) = tail { f(*expr); } } Expr::TryBlock { body } => f(*body), Expr::Loop { body } => f(*body), Expr::While { condition, body } => { f(*condition); f(*body); } Expr::For { iterable, body, .. } => { f(*iterable); f(*body); } Expr::Call { callee, args } => { f(*callee); for arg in args { f(*arg); } } Expr::MethodCall { receiver, args, .. } => { f(*receiver); for arg in args { f(*arg); } } Expr::Match { expr, arms } => { f(*expr); for arm in arms { f(arm.expr); } } Expr::Continue => {} Expr::Break { expr } | Expr::Return { expr } => { if let Some(expr) = expr { f(*expr); } } Expr::RecordLit { fields, spread, .. } => { for field in fields { f(field.expr); } if let Some(expr) = spread { f(*expr); } } Expr::Lambda { body, .. } => { f(*body); } Expr::BinaryOp { lhs, rhs, .. } | Expr::Range { lhs, rhs } | Expr::RangeInclusive { lhs, rhs } => { f(*lhs); f(*rhs); } Expr::Index { base, index } => { f(*base); f(*index); } Expr::RangeFull => {} Expr::RangeFrom { lhs: expr } | Expr::RangeTo { rhs: expr } | Expr::RangeToInclusive { rhs: expr } | Expr::Field { expr, .. } | Expr::Await { expr } | Expr::Try { expr } | Expr::Cast { expr, .. } | Expr::Ref { expr, .. } | Expr::UnaryOp { expr, .. } | Expr::Box { expr } => { f(*expr); } Expr::Tuple { exprs } => { for expr in exprs { f(*expr); } } Expr::Array(a) => match a { Array::ElementList(exprs) => { for expr in exprs { f(*expr); } } Array::Repeat { initializer, repeat } => { f(*initializer); f(*repeat) } }, Expr::Literal(_) => {} } } } /// Explicit binding annotations given in the HIR for a binding. Note /// that this is not the final binding *mode* that we infer after type /// inference. #[derive(Clone, PartialEq, Eq, Debug, Copy)] pub enum BindingAnnotation { /// No binding annotation given: this means that the final binding mode /// will depend on whether we have skipped through a `&` reference /// when matching. For example, the `x` in `Some(x)` will have binding /// mode `None`; if you do `let Some(x) = &Some(22)`, it will /// ultimately be inferred to be by-reference. Unannotated, /// Annotated with `mut x` -- could be either ref or not, similar to `None`. Mutable, /// Annotated as `ref`, like `ref x` Ref, /// Annotated as `ref mut x`. RefMut, } impl BindingAnnotation { pub fn new(is_mutable: bool, is_ref: bool) -> Self { match (is_mutable, is_ref) { (true, true) => BindingAnnotation::RefMut, (false, true) => BindingAnnotation::Ref, (true, false) => BindingAnnotation::Mutable, (false, false) => BindingAnnotation::Unannotated, } } } #[derive(Debug, Clone, Eq, PartialEq)] pub struct RecordFieldPat { pub name: Name, pub pat: PatId, } /// Close relative to rustc's hir::PatKind #[derive(Debug, Clone, Eq, PartialEq)] pub enum Pat { Missing, Wild, Tuple(Vec), Record { path: Option, args: Vec, // FIXME: 'ellipsis' option }, Range { start: ExprId, end: ExprId, }, Slice { prefix: Vec, rest: Option, suffix: Vec, }, Path(Path), Lit(ExprId), Bind { mode: BindingAnnotation, name: Name, subpat: Option, }, TupleStruct { path: Option, args: Vec, }, Ref { pat: PatId, mutability: Mutability, }, } impl Pat { pub fn walk_child_pats(&self, mut f: impl FnMut(PatId)) { match self { Pat::Range { .. } | Pat::Lit(..) | Pat::Path(..) | Pat::Wild | Pat::Missing => {} Pat::Bind { subpat, .. } => { subpat.iter().copied().for_each(f); } Pat::Tuple(args) | Pat::TupleStruct { args, .. } => { args.iter().copied().for_each(f); } Pat::Ref { pat, .. } => f(*pat), Pat::Slice { prefix, rest, suffix } => { let total_iter = prefix.iter().chain(rest.iter()).chain(suffix.iter()); total_iter.copied().for_each(f); } Pat::Record { args, .. } => { args.iter().map(|f| f.pat).for_each(f); } } } }