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