1pub mod format_args;
18pub mod generics;
19pub mod type_ref;
20
21use std::{fmt, mem};
22
23use hir_expand::{MacroDefId, name::Name};
24use intern::Symbol;
25use la_arena::{Idx, RawIdx};
26use rustc_apfloat::ieee::{Double, Half, Quad, Single};
27use syntax::ast;
28use thin_vec::ThinVec;
29use type_ref::TypeRefId;
30
31use crate::{
32 BlockId,
33 builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
34 expr_store::{
35 HygieneId,
36 path::{GenericArgs, Path},
37 },
38 type_ref::{Mutability, Rawness},
39};
40
41pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp};
42
43pub type BindingId = Idx<Binding>;
44
45pub type ExprId = Idx<Expr>;
46
47pub type PatId = Idx<Pat>;
48
49#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
50pub enum ExprOrPatId {
51 ExprId(ExprId),
52 PatId(PatId),
53}
54
55impl ExprOrPatId {
56 pub fn as_expr(self) -> Option<ExprId> {
57 match self {
58 Self::ExprId(v) => Some(v),
59 _ => None,
60 }
61 }
62
63 pub fn is_expr(&self) -> bool {
64 matches!(self, Self::ExprId(_))
65 }
66
67 pub fn as_pat(self) -> Option<PatId> {
68 match self {
69 Self::PatId(v) => Some(v),
70 _ => None,
71 }
72 }
73
74 pub fn is_pat(&self) -> bool {
75 matches!(self, Self::PatId(_))
76 }
77}
78
79#[derive(Copy, Clone, Hash, PartialEq, Eq, salsa::SalsaValue)]
80pub struct ExprOrPatIdPacked(u32);
81
82const _: () = assert!(mem::size_of::<ExprOrPatIdPacked>() == mem::size_of::<u32>());
83
84impl ExprOrPatIdPacked {
85 const PAT_BIT: u32 = 1 << (u32::BITS - 1);
86 const INDEX_MASK: u32 = !Self::PAT_BIT;
87
88 pub fn unpack(self) -> ExprOrPatId {
89 match self.is_expr() {
90 true => ExprOrPatId::ExprId(ExprId::from_raw(RawIdx::from_u32(self.0))),
91 false => {
92 ExprOrPatId::PatId(PatId::from_raw(RawIdx::from_u32(self.0 & Self::INDEX_MASK)))
93 }
94 }
95 }
96
97 #[inline]
98 pub fn as_expr(self) -> Option<ExprId> {
99 self.is_expr().then(|| ExprId::from_raw(RawIdx::from_u32(self.0)))
100 }
101
102 #[inline]
103 pub fn is_expr(&self) -> bool {
104 self.0 & Self::PAT_BIT == 0
105 }
106
107 #[inline]
108 pub fn as_pat(self) -> Option<PatId> {
109 self.is_pat().then(|| PatId::from_raw(RawIdx::from_u32(self.0 & Self::INDEX_MASK)))
110 }
111
112 #[inline]
113 pub fn is_pat(&self) -> bool {
114 self.0 & Self::PAT_BIT != 0
115 }
116}
117
118impl fmt::Debug for ExprOrPatIdPacked {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 match self.unpack() {
121 ExprOrPatId::ExprId(id) => f.debug_tuple("ExprId").field(&id).finish(),
122 ExprOrPatId::PatId(id) => f.debug_tuple("PatId").field(&id).finish(),
123 }
124 }
125}
126
127impl From<ExprId> for ExprOrPatIdPacked {
128 fn from(value: ExprId) -> Self {
129 let value = value.into_raw().into_u32();
130 debug_assert_eq!(value & Self::PAT_BIT, 0);
132 Self(value)
133 }
134}
135
136impl From<PatId> for ExprOrPatId {
137 fn from(value: PatId) -> Self {
138 ExprOrPatId::PatId(value)
139 }
140}
141impl From<ExprId> for ExprOrPatId {
142 fn from(value: ExprId) -> Self {
143 ExprOrPatId::ExprId(value)
144 }
145}
146
147impl From<PatId> for ExprOrPatIdPacked {
148 fn from(value: PatId) -> Self {
149 let value = value.into_raw().into_u32();
150 debug_assert_eq!(value & Self::PAT_BIT, 0);
152 Self(value | Self::PAT_BIT)
153 }
154}
155
156#[derive(Debug, Clone, Eq, PartialEq)]
157pub struct Label {
158 pub name: Name,
159}
160pub type LabelId = Idx<Label>;
161
162#[derive(Debug, Clone, Eq, PartialEq)]
166pub struct FloatTypeWrapper(Symbol);
167
168impl FloatTypeWrapper {
170 pub fn new(sym: Symbol) -> Self {
171 Self(sym)
172 }
173
174 pub fn to_f128(&self) -> Quad {
175 self.0.as_str().parse().unwrap_or_default()
176 }
177
178 pub fn to_f64(&self) -> Double {
179 self.0.as_str().parse().unwrap_or_default()
180 }
181
182 pub fn to_f32(&self) -> Single {
183 self.0.as_str().parse().unwrap_or_default()
184 }
185
186 pub fn to_f16(&self) -> Half {
187 self.0.as_str().parse().unwrap_or_default()
188 }
189}
190
191impl fmt::Display for FloatTypeWrapper {
192 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193 f.write_str(self.0.as_str())
194 }
195}
196
197#[derive(Debug, Clone, Eq, PartialEq)]
198pub enum Literal {
199 String(Symbol),
200 ByteString(Box<[u8]>),
201 CString(Box<[u8]>),
202 Char(char),
203 Bool(bool),
204 Int(i128, Option<BuiltinInt>),
205 Uint(u128, Option<BuiltinUint>),
206 Float(FloatTypeWrapper, Option<BuiltinFloat>),
210}
211
212#[derive(Debug, Clone, Eq, PartialEq)]
213pub enum LiteralOrConst {
215 Literal(Literal),
216 Const(PatId),
217}
218
219impl Literal {
220 pub fn negate(self) -> Option<Self> {
221 if let Literal::Int(i, k) = self { Some(Literal::Int(-i, k)) } else { None }
222 }
223}
224
225impl From<ast::LiteralKind> for Literal {
226 fn from(ast_lit_kind: ast::LiteralKind) -> Self {
227 use ast::LiteralKind;
228 match ast_lit_kind {
229 LiteralKind::IntNumber(lit) => {
230 if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
231 Literal::Float(
232 FloatTypeWrapper::new(Symbol::intern(&lit.value_string())),
233 builtin,
234 )
235 } else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinUint::from_suffix) {
236 Literal::Uint(lit.value().unwrap_or(0), builtin)
237 } else {
238 let builtin = lit.suffix().and_then(BuiltinInt::from_suffix);
239 Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
240 }
241 }
242 LiteralKind::FloatNumber(lit) => {
243 let ty = lit.suffix().and_then(BuiltinFloat::from_suffix);
244 Literal::Float(FloatTypeWrapper::new(Symbol::intern(&lit.value_string())), ty)
245 }
246 LiteralKind::ByteString(bs) => {
247 let text = bs.value().map_or_else(|_| Default::default(), Box::from);
248 Literal::ByteString(text)
249 }
250 LiteralKind::String(s) => {
251 let text = s.value().map_or_else(|_| Symbol::empty(), |it| Symbol::intern(&it));
252 Literal::String(text)
253 }
254 LiteralKind::CString(s) => {
255 let text = s.value().map_or_else(|_| Default::default(), Box::from);
256 Literal::CString(text)
257 }
258 LiteralKind::Byte(b) => {
259 Literal::Uint(b.value().unwrap_or_default() as u128, Some(BuiltinUint::U8))
260 }
261 LiteralKind::Char(c) => Literal::Char(c.value().unwrap_or_default()),
262 LiteralKind::Bool(val) => Literal::Bool(val),
263 }
264 }
265}
266
267#[derive(Debug, Clone, Eq, PartialEq, Copy)]
268pub enum RecordSpread {
269 None,
270 FieldDefaults,
271 Expr(ExprId),
272}
273
274#[derive(Debug, Clone, Copy, Eq, PartialEq)]
275pub enum Unsafe {
276 Yes,
277 No,
278}
279
280#[derive(Debug, Clone, Eq, PartialEq)]
281pub enum Expr {
282 Missing,
284 Path(Path),
285 If {
286 condition: ExprId,
287 then_branch: ExprId,
288 else_branch: Option<ExprId>,
289 },
290 Let {
291 pat: PatId,
292 expr: ExprId,
293 },
294 Block {
295 id: Option<BlockId>,
296 statements: Box<[Statement]>,
297 tail: Option<ExprId>,
298 label: Option<LabelId>,
299 unsafe_: Unsafe,
300 },
301 Const(ExprId),
302 Loop {
303 body: ExprId,
304 label: Option<LabelId>,
305 source: LoopSource,
306 },
307 Call {
308 callee: ExprId,
309 args: Box<[ExprId]>,
310 },
311 MethodCall {
312 receiver: ExprId,
313 method_name: Name,
314 args: Box<[ExprId]>,
315 generic_args: Option<Box<GenericArgs>>,
316 },
317 Match {
318 expr: ExprId,
319 arms: Box<[MatchArm]>,
320 },
321 Continue {
322 label: Option<LabelId>,
323 },
324 Break {
325 expr: Option<ExprId>,
326 label: Option<LabelId>,
327 },
328 Return {
329 expr: Option<ExprId>,
330 },
331 Become {
332 expr: ExprId,
333 },
334 Yield {
335 expr: Option<ExprId>,
336 },
337 Yeet {
338 expr: Option<ExprId>,
339 },
340 RecordLit {
341 path: Path,
342 fields: ThinVec<RecordLitField>,
343 spread: RecordSpread,
344 },
345 Field {
346 expr: ExprId,
347 name: Name,
348 },
349 Await {
350 expr: ExprId,
351 },
352 Cast {
353 expr: ExprId,
354 type_ref: TypeRefId,
355 },
356 Ref {
357 expr: ExprId,
358 rawness: Rawness,
359 mutability: Mutability,
360 },
361 UnaryOp {
362 expr: ExprId,
363 op: UnaryOp,
364 },
365 BinaryOp {
367 lhs: ExprId,
368 rhs: ExprId,
369 op: Option<BinaryOp>,
370 },
371 Assignment {
373 target: PatId,
374 value: ExprId,
375 },
376 Index {
377 base: ExprId,
378 index: ExprId,
379 },
380 Closure {
381 args: Box<[PatId]>,
382 arg_types: Box<[Option<TypeRefId>]>,
383 ret_type: Option<TypeRefId>,
384 body: ExprId,
385 closure_kind: ClosureKind,
386 capture_by: CaptureBy,
387 },
388 Tuple {
389 exprs: Box<[ExprId]>,
390 },
391 Array(Array),
392 Literal(Literal),
393 Underscore,
394 OffsetOf(OffsetOf),
395 InlineAsm(InlineAsm),
396 IncludeBytes,
397}
398
399#[cfg(target_pointer_width = "64")]
400const _: () = assert!(std::mem::size_of::<Expr>() == 48);
401
402impl Expr {
403 pub fn precedence(&self) -> ast::prec::ExprPrecedence {
404 use ast::prec::ExprPrecedence;
405
406 match self {
407 Expr::Array(_)
408 | Expr::InlineAsm(_)
409 | Expr::Block { .. }
410 | Expr::Const(_)
411 | Expr::If { .. }
412 | Expr::Literal(_)
413 | Expr::Loop { .. }
414 | Expr::Match { .. }
415 | Expr::Missing
416 | Expr::Path(_)
417 | Expr::RecordLit { .. }
418 | Expr::Tuple { .. }
419 | Expr::OffsetOf(_)
420 | Expr::Underscore
421 | Expr::IncludeBytes => ExprPrecedence::Unambiguous,
422
423 Expr::Await { .. }
424 | Expr::Call { .. }
425 | Expr::Field { .. }
426 | Expr::Index { .. }
427 | Expr::MethodCall { .. } => ExprPrecedence::Postfix,
428
429 Expr::Let { .. } | Expr::UnaryOp { .. } | Expr::Ref { .. } => ExprPrecedence::Prefix,
430
431 Expr::Cast { .. } => ExprPrecedence::Cast,
432
433 Expr::BinaryOp { op, .. } => match op {
434 None => ExprPrecedence::Unambiguous,
435 Some(BinaryOp::LogicOp(LogicOp::Or)) => ExprPrecedence::LOr,
436 Some(BinaryOp::LogicOp(LogicOp::And)) => ExprPrecedence::LAnd,
437 Some(BinaryOp::CmpOp(_)) => ExprPrecedence::Compare,
438 Some(BinaryOp::Assignment { .. }) => ExprPrecedence::Assign,
439 Some(BinaryOp::ArithOp(arith_op)) => match arith_op {
440 ArithOp::Add | ArithOp::Sub => ExprPrecedence::Sum,
441 ArithOp::Mul | ArithOp::Div | ArithOp::Rem => ExprPrecedence::Product,
442 ArithOp::Shl | ArithOp::Shr => ExprPrecedence::Shift,
443 ArithOp::BitXor => ExprPrecedence::BitXor,
444 ArithOp::BitOr => ExprPrecedence::BitOr,
445 ArithOp::BitAnd => ExprPrecedence::BitAnd,
446 },
447 },
448
449 Expr::Assignment { .. } => ExprPrecedence::Assign,
450
451 Expr::Become { .. }
452 | Expr::Break { .. }
453 | Expr::Closure { .. }
454 | Expr::Return { .. }
455 | Expr::Yeet { .. }
456 | Expr::Yield { .. } => ExprPrecedence::Jump,
457
458 Expr::Continue { .. } => ExprPrecedence::Unambiguous,
459 }
460 }
461}
462
463#[derive(Debug, Clone, Copy, PartialEq, Eq)]
465pub enum LoopSource {
466 Loop,
468 While,
470 ForLoop,
472}
473
474#[derive(Debug, Clone, PartialEq, Eq)]
475pub struct OffsetOf {
476 pub container: TypeRefId,
477 pub fields: Box<[Name]>,
478}
479
480#[derive(Debug, Clone, PartialEq, Eq)]
481pub struct InlineAsm {
482 pub operands: Box<[(Option<Name>, AsmOperand)]>,
483 pub options: AsmOptions,
484 pub kind: InlineAsmKind,
485}
486
487#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
488pub enum InlineAsmKind {
489 Asm,
491 GlobalAsm,
493 NakedAsm,
495}
496
497#[derive(Clone, Copy, PartialEq, Eq, Hash)]
498pub struct AsmOptions(u16);
499bitflags::bitflags! {
500 impl AsmOptions: u16 {
501 const PURE = 1 << 0;
502 const NOMEM = 1 << 1;
503 const READONLY = 1 << 2;
504 const PRESERVES_FLAGS = 1 << 3;
505 const NORETURN = 1 << 4;
506 const NOSTACK = 1 << 5;
507 const ATT_SYNTAX = 1 << 6;
508 const RAW = 1 << 7;
509 const MAY_UNWIND = 1 << 8;
510 }
511}
512
513impl AsmOptions {
514 pub const COUNT: usize = Self::all().bits().count_ones() as usize;
515
516 pub const GLOBAL_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW);
517 pub const NAKED_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW).union(Self::NORETURN);
518
519 pub fn human_readable_names(&self) -> Vec<&'static str> {
520 let mut options = vec![];
521
522 if self.contains(AsmOptions::PURE) {
523 options.push("pure");
524 }
525 if self.contains(AsmOptions::NOMEM) {
526 options.push("nomem");
527 }
528 if self.contains(AsmOptions::READONLY) {
529 options.push("readonly");
530 }
531 if self.contains(AsmOptions::PRESERVES_FLAGS) {
532 options.push("preserves_flags");
533 }
534 if self.contains(AsmOptions::NORETURN) {
535 options.push("noreturn");
536 }
537 if self.contains(AsmOptions::NOSTACK) {
538 options.push("nostack");
539 }
540 if self.contains(AsmOptions::ATT_SYNTAX) {
541 options.push("att_syntax");
542 }
543 if self.contains(AsmOptions::RAW) {
544 options.push("raw");
545 }
546 if self.contains(AsmOptions::MAY_UNWIND) {
547 options.push("may_unwind");
548 }
549
550 options
551 }
552}
553
554impl std::fmt::Debug for AsmOptions {
555 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
556 bitflags::parser::to_writer(self, f)
557 }
558}
559
560#[derive(Clone, Debug, Eq, PartialEq, Hash)]
561pub enum AsmOperand {
562 In {
563 reg: InlineAsmRegOrRegClass,
564 expr: ExprId,
565 },
566 Out {
567 reg: InlineAsmRegOrRegClass,
568 expr: Option<ExprId>,
569 late: bool,
570 },
571 InOut {
572 reg: InlineAsmRegOrRegClass,
573 expr: ExprId,
574 late: bool,
575 },
576 SplitInOut {
577 reg: InlineAsmRegOrRegClass,
578 in_expr: ExprId,
579 out_expr: Option<ExprId>,
580 late: bool,
581 },
582 Label(ExprId),
583 Const(ExprId),
584 Sym(Path),
585}
586
587impl AsmOperand {
588 pub fn reg(&self) -> Option<&InlineAsmRegOrRegClass> {
589 match self {
590 Self::In { reg, .. }
591 | Self::Out { reg, .. }
592 | Self::InOut { reg, .. }
593 | Self::SplitInOut { reg, .. } => Some(reg),
594 Self::Const { .. } | Self::Sym { .. } | Self::Label { .. } => None,
595 }
596 }
597
598 pub fn is_clobber(&self) -> bool {
599 matches!(self, AsmOperand::Out { reg: InlineAsmRegOrRegClass::Reg(_), late: _, expr: None })
600 }
601}
602
603#[derive(Clone, Debug, Eq, PartialEq, Hash)]
604pub enum InlineAsmRegOrRegClass {
605 Reg(Symbol),
606 RegClass(Symbol),
607}
608
609#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
610pub enum CoroutineKind {
611 Async,
612 Gen,
613 AsyncGen,
614}
615
616#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
617pub enum ClosureKind {
618 Closure,
619 OldCoroutine(Movability),
620 Coroutine { kind: CoroutineKind, source: CoroutineSource },
621 CoroutineClosure(CoroutineKind),
622}
623
624#[derive(Clone, PartialEq, Eq, Hash, Debug, Copy)]
630pub enum CoroutineSource {
631 Block,
633
634 Closure,
636
637 Fn,
639}
640
641#[derive(Debug, Clone, Copy, PartialEq, Eq)]
642pub enum CaptureBy {
643 Value,
645 Ref,
647}
648
649#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
650pub enum Movability {
651 Static,
652 Movable,
653}
654
655#[derive(Debug, Clone, Eq, PartialEq)]
656pub enum Array {
657 ElementList { elements: Box<[ExprId]> },
658 Repeat { initializer: ExprId, repeat: ExprId },
659}
660
661#[derive(Debug, Clone, Eq, PartialEq)]
662pub struct MatchArm {
663 pub pat: PatId,
664 pub guard: Option<ExprId>,
665 pub expr: ExprId,
666}
667
668#[derive(Debug, Clone, Eq, PartialEq)]
669pub struct RecordLitField {
670 pub name: Name,
671 pub expr: ExprId,
672}
673
674#[derive(Debug, Clone, Eq, PartialEq)]
675pub enum Statement {
676 Let {
677 pat: PatId,
678 type_ref: Option<TypeRefId>,
679 initializer: Option<ExprId>,
680 else_branch: Option<ExprId>,
681 },
682 Expr {
683 expr: ExprId,
684 has_semi: bool,
685 },
686 Item(Item),
687}
688
689#[derive(Debug, Clone, PartialEq, Eq)]
690pub enum Item {
691 MacroDef(Box<MacroDefId>),
692 Other,
693}
694
695#[derive(Clone, PartialEq, Eq, Debug, Copy)]
699pub enum BindingAnnotation {
700 Unannotated,
706
707 Mutable,
709
710 Ref,
712
713 RefMut,
715}
716
717impl BindingAnnotation {
718 pub fn new(is_mutable: bool, is_ref: bool) -> Self {
719 match (is_mutable, is_ref) {
720 (true, true) => BindingAnnotation::RefMut,
721 (false, true) => BindingAnnotation::Ref,
722 (true, false) => BindingAnnotation::Mutable,
723 (false, false) => BindingAnnotation::Unannotated,
724 }
725 }
726}
727
728#[derive(Debug, Clone, Eq, PartialEq)]
729pub enum BindingProblems {
730 BoundMoreThanOnce,
732 BoundInconsistently,
734 NotBoundAcrossAll,
736}
737
738#[derive(Debug, Clone, Eq, PartialEq)]
739pub struct Binding {
740 pub name: Name,
741 pub mode: BindingAnnotation,
742 pub problems: Option<BindingProblems>,
743 pub hygiene: HygieneId,
746}
747
748#[derive(Debug, Clone, Eq, PartialEq)]
749pub struct RecordFieldPat {
750 pub name: Name,
751 pub pat: PatId,
752}
753
754#[derive(Debug, Clone, Eq, PartialEq)]
756pub enum Pat {
757 Missing,
758 Rest,
760 Wild,
761 Tuple {
762 args: Box<[PatId]>,
763 ellipsis: Option<u32>,
764 },
765 Or(Box<[PatId]>),
766 Record {
767 path: Path,
768 args: Box<[RecordFieldPat]>,
769 ellipsis: bool,
770 },
771 Range {
772 start: Option<ExprId>,
773 end: Option<ExprId>,
774 range_type: RangeOp,
775 },
776 Slice {
777 prefix: Box<[PatId]>,
778 slice: Option<PatId>,
779 suffix: Box<[PatId]>,
780 },
781 Path(Path),
782 Lit(ExprId),
783 Bind {
784 id: BindingId,
785 subpat: Option<PatId>,
786 },
787 TupleStruct {
788 path: Path,
789 args: Box<[PatId]>,
790 ellipsis: Option<u32>,
791 },
792 Ref {
793 pat: PatId,
794 mutability: Mutability,
795 },
796 Box {
797 inner: PatId,
798 },
799 Deref {
800 inner: PatId,
801 },
802 NotNull,
803 Expr(ExprId),
807}
808
809impl Pat {
810 pub fn walk_child_pats(&self, mut f: impl FnMut(PatId)) {
811 match self {
812 Pat::Range { .. }
813 | Pat::Lit(..)
814 | Pat::Path(..)
815 | Pat::Wild
816 | Pat::Missing
817 | Pat::Rest
818 | Pat::Expr(_)
819 | Pat::NotNull => {}
820 Pat::Bind { subpat, .. } => {
821 subpat.iter().copied().for_each(f);
822 }
823 Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => {
824 args.iter().copied().for_each(f);
825 }
826 Pat::Ref { pat, .. } => f(*pat),
827 Pat::Slice { prefix, slice, suffix } => {
828 let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter());
829 total_iter.copied().for_each(f);
830 }
831 Pat::Record { args, .. } => {
832 args.iter().map(|f| f.pat).for_each(f);
833 }
834 Pat::Box { inner } | Pat::Deref { inner } => f(*inner),
835 }
836 }
837}