1 //! This module describes hir-level representation of expressions.
2 //!
3 //! This representation is:
4 //!
5 //! 1. Identity-based. Each expression has an `id`, so we can distinguish
6 //! between different `1` in `1 + 1`.
7 //! 2. Independent of syntax. Though syntactic provenance information can be
8 //! attached separately via id-based side map.
9 //! 3. Unresolved. Paths are stored as sequences of names, and not as defs the
10 //! names refer to.
11 //! 4. Desugared. There's no `if let`.
12 //!
13 //! See also a neighboring `body` module.
14
15 pub mod type_ref;
16
17 use std::fmt;
18
19 use hir_expand::name::Name;
20 use intern::Interned;
21 use la_arena::{Idx, RawIdx};
22 use smallvec::SmallVec;
23 use syntax::ast;
24
25 use crate::{
26 builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
27 path::{GenericArgs, Path},
28 type_ref::{Mutability, Rawness, TypeRef},
29 BlockId, ConstBlockId,
30 };
31
32 pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp};
33
34 pub type BindingId = Idx<Binding>;
35
36 pub type ExprId = Idx<Expr>;
37
38 /// FIXME: this is a hacky function which should be removed
dummy_expr_id() -> ExprId39 pub(crate) fn dummy_expr_id() -> ExprId {
40 ExprId::from_raw(RawIdx::from(u32::MAX))
41 }
42
43 pub type PatId = Idx<Pat>;
44
45 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
46 pub enum ExprOrPatId {
47 ExprId(ExprId),
48 PatId(PatId),
49 }
50 stdx::impl_from!(ExprId, PatId for ExprOrPatId);
51
52 #[derive(Debug, Clone, Eq, PartialEq)]
53 pub struct Label {
54 pub name: Name,
55 }
56 pub type LabelId = Idx<Label>;
57
58 // We convert float values into bits and that's how we don't need to deal with f32 and f64.
59 // For PartialEq, bits comparison should work, as ordering is not important
60 // https://github.com/rust-lang/rust-analyzer/issues/12380#issuecomment-1137284360
61 #[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
62 pub struct FloatTypeWrapper(u64);
63
64 impl FloatTypeWrapper {
new(value: f64) -> Self65 pub fn new(value: f64) -> Self {
66 Self(value.to_bits())
67 }
68
into_f64(self) -> f6469 pub fn into_f64(self) -> f64 {
70 f64::from_bits(self.0)
71 }
72
into_f32(self) -> f3273 pub fn into_f32(self) -> f32 {
74 f64::from_bits(self.0) as f32
75 }
76 }
77
78 impl fmt::Display for FloatTypeWrapper {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 write!(f, "{:?}", f64::from_bits(self.0))
81 }
82 }
83
84 #[derive(Debug, Clone, Eq, PartialEq)]
85 pub enum Literal {
86 String(Box<str>),
87 ByteString(Box<[u8]>),
88 CString(Box<str>),
89 Char(char),
90 Bool(bool),
91 Int(i128, Option<BuiltinInt>),
92 Uint(u128, Option<BuiltinUint>),
93 // Here we are using a wrapper around float because f32 and f64 do not implement Eq, so they
94 // could not be used directly here, to understand how the wrapper works go to definition of
95 // FloatTypeWrapper
96 Float(FloatTypeWrapper, Option<BuiltinFloat>),
97 }
98
99 #[derive(Debug, Clone, Eq, PartialEq)]
100 /// Used in range patterns.
101 pub enum LiteralOrConst {
102 Literal(Literal),
103 Const(Path),
104 }
105
106 impl Literal {
negate(self) -> Option<Self>107 pub fn negate(self) -> Option<Self> {
108 if let Literal::Int(i, k) = self {
109 Some(Literal::Int(-i, k))
110 } else {
111 None
112 }
113 }
114 }
115
116 impl From<ast::LiteralKind> for Literal {
from(ast_lit_kind: ast::LiteralKind) -> Self117 fn from(ast_lit_kind: ast::LiteralKind) -> Self {
118 use ast::LiteralKind;
119 match ast_lit_kind {
120 // FIXME: these should have actual values filled in, but unsure on perf impact
121 LiteralKind::IntNumber(lit) => {
122 if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
123 Literal::Float(
124 FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())),
125 builtin,
126 )
127 } else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinUint::from_suffix) {
128 Literal::Uint(lit.value().unwrap_or(0), builtin)
129 } else {
130 let builtin = lit.suffix().and_then(BuiltinInt::from_suffix);
131 Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
132 }
133 }
134 LiteralKind::FloatNumber(lit) => {
135 let ty = lit.suffix().and_then(BuiltinFloat::from_suffix);
136 Literal::Float(FloatTypeWrapper::new(lit.value().unwrap_or(Default::default())), ty)
137 }
138 LiteralKind::ByteString(bs) => {
139 let text = bs.value().map(Box::from).unwrap_or_else(Default::default);
140 Literal::ByteString(text)
141 }
142 LiteralKind::String(s) => {
143 let text = s.value().map(Box::from).unwrap_or_else(Default::default);
144 Literal::String(text)
145 }
146 LiteralKind::CString(s) => {
147 let text = s.value().map(Box::from).unwrap_or_else(Default::default);
148 Literal::CString(text)
149 }
150 LiteralKind::Byte(b) => {
151 Literal::Uint(b.value().unwrap_or_default() as u128, Some(BuiltinUint::U8))
152 }
153 LiteralKind::Char(c) => Literal::Char(c.value().unwrap_or_default()),
154 LiteralKind::Bool(val) => Literal::Bool(val),
155 }
156 }
157 }
158
159 #[derive(Debug, Clone, Eq, PartialEq)]
160 pub enum Expr {
161 /// This is produced if the syntax tree does not have a required expression piece.
162 Missing,
163 Path(Path),
164 If {
165 condition: ExprId,
166 then_branch: ExprId,
167 else_branch: Option<ExprId>,
168 },
169 Let {
170 pat: PatId,
171 expr: ExprId,
172 },
173 Block {
174 id: Option<BlockId>,
175 statements: Box<[Statement]>,
176 tail: Option<ExprId>,
177 label: Option<LabelId>,
178 },
179 Async {
180 id: Option<BlockId>,
181 statements: Box<[Statement]>,
182 tail: Option<ExprId>,
183 },
184 Const(ConstBlockId),
185 Unsafe {
186 id: Option<BlockId>,
187 statements: Box<[Statement]>,
188 tail: Option<ExprId>,
189 },
190 Loop {
191 body: ExprId,
192 label: Option<LabelId>,
193 },
194 While {
195 condition: ExprId,
196 body: ExprId,
197 label: Option<LabelId>,
198 },
199 Call {
200 callee: ExprId,
201 args: Box<[ExprId]>,
202 is_assignee_expr: bool,
203 },
204 MethodCall {
205 receiver: ExprId,
206 method_name: Name,
207 args: Box<[ExprId]>,
208 generic_args: Option<Box<GenericArgs>>,
209 },
210 Match {
211 expr: ExprId,
212 arms: Box<[MatchArm]>,
213 },
214 Continue {
215 label: Option<LabelId>,
216 },
217 Break {
218 expr: Option<ExprId>,
219 label: Option<LabelId>,
220 },
221 Return {
222 expr: Option<ExprId>,
223 },
224 Yield {
225 expr: Option<ExprId>,
226 },
227 Yeet {
228 expr: Option<ExprId>,
229 },
230 RecordLit {
231 path: Option<Box<Path>>,
232 fields: Box<[RecordLitField]>,
233 spread: Option<ExprId>,
234 ellipsis: bool,
235 is_assignee_expr: bool,
236 },
237 Field {
238 expr: ExprId,
239 name: Name,
240 },
241 Await {
242 expr: ExprId,
243 },
244 Cast {
245 expr: ExprId,
246 type_ref: Interned<TypeRef>,
247 },
248 Ref {
249 expr: ExprId,
250 rawness: Rawness,
251 mutability: Mutability,
252 },
253 Box {
254 expr: ExprId,
255 },
256 UnaryOp {
257 expr: ExprId,
258 op: UnaryOp,
259 },
260 BinaryOp {
261 lhs: ExprId,
262 rhs: ExprId,
263 op: Option<BinaryOp>,
264 },
265 Range {
266 lhs: Option<ExprId>,
267 rhs: Option<ExprId>,
268 range_type: RangeOp,
269 },
270 Index {
271 base: ExprId,
272 index: ExprId,
273 },
274 Closure {
275 args: Box<[PatId]>,
276 arg_types: Box<[Option<Interned<TypeRef>>]>,
277 ret_type: Option<Interned<TypeRef>>,
278 body: ExprId,
279 closure_kind: ClosureKind,
280 capture_by: CaptureBy,
281 },
282 Tuple {
283 exprs: Box<[ExprId]>,
284 is_assignee_expr: bool,
285 },
286 Array(Array),
287 Literal(Literal),
288 Underscore,
289 }
290
291 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
292 pub enum ClosureKind {
293 Closure,
294 Generator(Movability),
295 Async,
296 }
297
298 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
299 pub enum CaptureBy {
300 /// `move |x| y + x`.
301 Value,
302 /// `move` keyword was not specified.
303 Ref,
304 }
305
306 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
307 pub enum Movability {
308 Static,
309 Movable,
310 }
311
312 #[derive(Debug, Clone, Eq, PartialEq)]
313 pub enum Array {
314 ElementList { elements: Box<[ExprId]>, is_assignee_expr: bool },
315 Repeat { initializer: ExprId, repeat: ExprId },
316 }
317
318 #[derive(Debug, Clone, Eq, PartialEq)]
319 pub struct MatchArm {
320 pub pat: PatId,
321 pub guard: Option<ExprId>,
322 pub expr: ExprId,
323 }
324
325 #[derive(Debug, Clone, Eq, PartialEq)]
326 pub struct RecordLitField {
327 pub name: Name,
328 pub expr: ExprId,
329 }
330
331 #[derive(Debug, Clone, Eq, PartialEq)]
332 pub enum Statement {
333 Let {
334 pat: PatId,
335 type_ref: Option<Interned<TypeRef>>,
336 initializer: Option<ExprId>,
337 else_branch: Option<ExprId>,
338 },
339 Expr {
340 expr: ExprId,
341 has_semi: bool,
342 },
343 }
344
345 impl Expr {
walk_child_exprs(&self, mut f: impl FnMut(ExprId))346 pub fn walk_child_exprs(&self, mut f: impl FnMut(ExprId)) {
347 match self {
348 Expr::Missing => {}
349 Expr::Path(_) => {}
350 Expr::If { condition, then_branch, else_branch } => {
351 f(*condition);
352 f(*then_branch);
353 if let &Some(else_branch) = else_branch {
354 f(else_branch);
355 }
356 }
357 Expr::Let { expr, .. } => {
358 f(*expr);
359 }
360 Expr::Const(_) => (),
361 Expr::Block { statements, tail, .. }
362 | Expr::Unsafe { statements, tail, .. }
363 | Expr::Async { statements, tail, .. } => {
364 for stmt in statements.iter() {
365 match stmt {
366 Statement::Let { initializer, else_branch, .. } => {
367 if let &Some(expr) = initializer {
368 f(expr);
369 }
370 if let &Some(expr) = else_branch {
371 f(expr);
372 }
373 }
374 Statement::Expr { expr: expression, .. } => f(*expression),
375 }
376 }
377 if let &Some(expr) = tail {
378 f(expr);
379 }
380 }
381 Expr::Loop { body, .. } => f(*body),
382 Expr::While { condition, body, .. } => {
383 f(*condition);
384 f(*body);
385 }
386 Expr::Call { callee, args, .. } => {
387 f(*callee);
388 args.iter().copied().for_each(f);
389 }
390 Expr::MethodCall { receiver, args, .. } => {
391 f(*receiver);
392 args.iter().copied().for_each(f);
393 }
394 Expr::Match { expr, arms } => {
395 f(*expr);
396 arms.iter().map(|arm| arm.expr).for_each(f);
397 }
398 Expr::Continue { .. } => {}
399 Expr::Break { expr, .. }
400 | Expr::Return { expr }
401 | Expr::Yield { expr }
402 | Expr::Yeet { expr } => {
403 if let &Some(expr) = expr {
404 f(expr);
405 }
406 }
407 Expr::RecordLit { fields, spread, .. } => {
408 for field in fields.iter() {
409 f(field.expr);
410 }
411 if let &Some(expr) = spread {
412 f(expr);
413 }
414 }
415 Expr::Closure { body, .. } => {
416 f(*body);
417 }
418 Expr::BinaryOp { lhs, rhs, .. } => {
419 f(*lhs);
420 f(*rhs);
421 }
422 Expr::Range { lhs, rhs, .. } => {
423 if let &Some(lhs) = rhs {
424 f(lhs);
425 }
426 if let &Some(rhs) = lhs {
427 f(rhs);
428 }
429 }
430 Expr::Index { base, index } => {
431 f(*base);
432 f(*index);
433 }
434 Expr::Field { expr, .. }
435 | Expr::Await { expr }
436 | Expr::Cast { expr, .. }
437 | Expr::Ref { expr, .. }
438 | Expr::UnaryOp { expr, .. }
439 | Expr::Box { expr } => {
440 f(*expr);
441 }
442 Expr::Tuple { exprs, .. } => exprs.iter().copied().for_each(f),
443 Expr::Array(a) => match a {
444 Array::ElementList { elements, .. } => elements.iter().copied().for_each(f),
445 Array::Repeat { initializer, repeat } => {
446 f(*initializer);
447 f(*repeat)
448 }
449 },
450 Expr::Literal(_) => {}
451 Expr::Underscore => {}
452 }
453 }
454 }
455
456 /// Explicit binding annotations given in the HIR for a binding. Note
457 /// that this is not the final binding *mode* that we infer after type
458 /// inference.
459 #[derive(Clone, PartialEq, Eq, Debug, Copy)]
460 pub enum BindingAnnotation {
461 /// No binding annotation given: this means that the final binding mode
462 /// will depend on whether we have skipped through a `&` reference
463 /// when matching. For example, the `x` in `Some(x)` will have binding
464 /// mode `None`; if you do `let Some(x) = &Some(22)`, it will
465 /// ultimately be inferred to be by-reference.
466 Unannotated,
467
468 /// Annotated with `mut x` -- could be either ref or not, similar to `None`.
469 Mutable,
470
471 /// Annotated as `ref`, like `ref x`
472 Ref,
473
474 /// Annotated as `ref mut x`.
475 RefMut,
476 }
477
478 impl BindingAnnotation {
new(is_mutable: bool, is_ref: bool) -> Self479 pub fn new(is_mutable: bool, is_ref: bool) -> Self {
480 match (is_mutable, is_ref) {
481 (true, true) => BindingAnnotation::RefMut,
482 (false, true) => BindingAnnotation::Ref,
483 (true, false) => BindingAnnotation::Mutable,
484 (false, false) => BindingAnnotation::Unannotated,
485 }
486 }
487 }
488
489 #[derive(Debug, Clone, Eq, PartialEq)]
490 pub enum BindingProblems {
491 /// https://doc.rust-lang.org/stable/error_codes/E0416.html
492 BoundMoreThanOnce,
493 /// https://doc.rust-lang.org/stable/error_codes/E0409.html
494 BoundInconsistently,
495 /// https://doc.rust-lang.org/stable/error_codes/E0408.html
496 NotBoundAcrossAll,
497 }
498
499 #[derive(Debug, Clone, Eq, PartialEq)]
500 pub struct Binding {
501 pub name: Name,
502 pub mode: BindingAnnotation,
503 pub definitions: SmallVec<[PatId; 1]>,
504 pub problems: Option<BindingProblems>,
505 }
506
507 #[derive(Debug, Clone, Eq, PartialEq)]
508 pub struct RecordFieldPat {
509 pub name: Name,
510 pub pat: PatId,
511 }
512
513 /// Close relative to rustc's hir::PatKind
514 #[derive(Debug, Clone, Eq, PartialEq)]
515 pub enum Pat {
516 Missing,
517 Wild,
518 Tuple { args: Box<[PatId]>, ellipsis: Option<usize> },
519 Or(Box<[PatId]>),
520 Record { path: Option<Box<Path>>, args: Box<[RecordFieldPat]>, ellipsis: bool },
521 Range { start: Option<Box<LiteralOrConst>>, end: Option<Box<LiteralOrConst>> },
522 Slice { prefix: Box<[PatId]>, slice: Option<PatId>, suffix: Box<[PatId]> },
523 Path(Box<Path>),
524 Lit(ExprId),
525 Bind { id: BindingId, subpat: Option<PatId> },
526 TupleStruct { path: Option<Box<Path>>, args: Box<[PatId]>, ellipsis: Option<usize> },
527 Ref { pat: PatId, mutability: Mutability },
528 Box { inner: PatId },
529 ConstBlock(ExprId),
530 }
531
532 impl Pat {
walk_child_pats(&self, mut f: impl FnMut(PatId))533 pub fn walk_child_pats(&self, mut f: impl FnMut(PatId)) {
534 match self {
535 Pat::Range { .. }
536 | Pat::Lit(..)
537 | Pat::Path(..)
538 | Pat::ConstBlock(..)
539 | Pat::Wild
540 | Pat::Missing => {}
541 Pat::Bind { subpat, .. } => {
542 subpat.iter().copied().for_each(f);
543 }
544 Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => {
545 args.iter().copied().for_each(f);
546 }
547 Pat::Ref { pat, .. } => f(*pat),
548 Pat::Slice { prefix, slice, suffix } => {
549 let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter());
550 total_iter.copied().for_each(f);
551 }
552 Pat::Record { args, .. } => {
553 args.iter().map(|f| f.pat).for_each(f);
554 }
555 Pat::Box { inner } => f(*inner),
556 }
557 }
558 }
559