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)]
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
325#[derive(Debug, Clone, PartialEq, Eq)]
326pub struct OffsetOf {
327 pub container: TypeRefId,
328 pub fields: Box<[Name]>,
329}
330
331#[derive(Debug, Clone, PartialEq, Eq)]
332pub struct InlineAsm {
333 pub operands: Box<[(Option<Name>, AsmOperand)]>,
334 pub options: AsmOptions,
335 pub kind: InlineAsmKind,
336}
337
338#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
339pub enum InlineAsmKind {
340 Asm,
342 GlobalAsm,
344 NakedAsm,
346}
347
348#[derive(Clone, Copy, PartialEq, Eq, Hash)]
349pub struct AsmOptions(u16);
350bitflags::bitflags! {
351 impl AsmOptions: u16 {
352 const PURE = 1 << 0;
353 const NOMEM = 1 << 1;
354 const READONLY = 1 << 2;
355 const PRESERVES_FLAGS = 1 << 3;
356 const NORETURN = 1 << 4;
357 const NOSTACK = 1 << 5;
358 const ATT_SYNTAX = 1 << 6;
359 const RAW = 1 << 7;
360 const MAY_UNWIND = 1 << 8;
361 }
362}
363
364impl AsmOptions {
365 pub const COUNT: usize = Self::all().bits().count_ones() as usize;
366
367 pub const GLOBAL_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW);
368 pub const NAKED_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW).union(Self::NORETURN);
369
370 pub fn human_readable_names(&self) -> Vec<&'static str> {
371 let mut options = vec![];
372
373 if self.contains(AsmOptions::PURE) {
374 options.push("pure");
375 }
376 if self.contains(AsmOptions::NOMEM) {
377 options.push("nomem");
378 }
379 if self.contains(AsmOptions::READONLY) {
380 options.push("readonly");
381 }
382 if self.contains(AsmOptions::PRESERVES_FLAGS) {
383 options.push("preserves_flags");
384 }
385 if self.contains(AsmOptions::NORETURN) {
386 options.push("noreturn");
387 }
388 if self.contains(AsmOptions::NOSTACK) {
389 options.push("nostack");
390 }
391 if self.contains(AsmOptions::ATT_SYNTAX) {
392 options.push("att_syntax");
393 }
394 if self.contains(AsmOptions::RAW) {
395 options.push("raw");
396 }
397 if self.contains(AsmOptions::MAY_UNWIND) {
398 options.push("may_unwind");
399 }
400
401 options
402 }
403}
404
405impl std::fmt::Debug for AsmOptions {
406 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
407 bitflags::parser::to_writer(self, f)
408 }
409}
410
411#[derive(Clone, Debug, Eq, PartialEq, Hash)]
412pub enum AsmOperand {
413 In {
414 reg: InlineAsmRegOrRegClass,
415 expr: ExprId,
416 },
417 Out {
418 reg: InlineAsmRegOrRegClass,
419 expr: Option<ExprId>,
420 late: bool,
421 },
422 InOut {
423 reg: InlineAsmRegOrRegClass,
424 expr: ExprId,
425 late: bool,
426 },
427 SplitInOut {
428 reg: InlineAsmRegOrRegClass,
429 in_expr: ExprId,
430 out_expr: Option<ExprId>,
431 late: bool,
432 },
433 Label(ExprId),
434 Const(ExprId),
435 Sym(Path),
436}
437
438impl AsmOperand {
439 pub fn reg(&self) -> Option<&InlineAsmRegOrRegClass> {
440 match self {
441 Self::In { reg, .. }
442 | Self::Out { reg, .. }
443 | Self::InOut { reg, .. }
444 | Self::SplitInOut { reg, .. } => Some(reg),
445 Self::Const { .. } | Self::Sym { .. } | Self::Label { .. } => None,
446 }
447 }
448
449 pub fn is_clobber(&self) -> bool {
450 matches!(self, AsmOperand::Out { reg: InlineAsmRegOrRegClass::Reg(_), late: _, expr: None })
451 }
452}
453
454#[derive(Clone, Debug, Eq, PartialEq, Hash)]
455pub enum InlineAsmRegOrRegClass {
456 Reg(Symbol),
457 RegClass(Symbol),
458}
459
460#[derive(Debug, Clone, Copy, PartialEq, Eq)]
461pub enum ClosureKind {
462 Closure,
463 Coroutine(Movability),
464 Async,
465}
466
467#[derive(Debug, Clone, Copy, PartialEq, Eq)]
468pub enum CaptureBy {
469 Value,
471 Ref,
473}
474
475#[derive(Debug, Clone, Copy, PartialEq, Eq)]
476pub enum Movability {
477 Static,
478 Movable,
479}
480
481#[derive(Debug, Clone, Eq, PartialEq)]
482pub enum Array {
483 ElementList { elements: Box<[ExprId]> },
484 Repeat { initializer: ExprId, repeat: ExprId },
485}
486
487#[derive(Debug, Clone, Eq, PartialEq)]
488pub struct MatchArm {
489 pub pat: PatId,
490 pub guard: Option<ExprId>,
491 pub expr: ExprId,
492}
493
494#[derive(Debug, Clone, Eq, PartialEq)]
495pub struct RecordLitField {
496 pub name: Name,
497 pub expr: ExprId,
498}
499
500#[derive(Debug, Clone, Eq, PartialEq)]
501pub enum Statement {
502 Let {
503 pat: PatId,
504 type_ref: Option<TypeRefId>,
505 initializer: Option<ExprId>,
506 else_branch: Option<ExprId>,
507 },
508 Expr {
509 expr: ExprId,
510 has_semi: bool,
511 },
512 Item(Item),
513}
514
515#[derive(Debug, Clone, PartialEq, Eq)]
516pub enum Item {
517 MacroDef(Box<MacroDefId>),
518 Other,
519}
520
521#[derive(Clone, PartialEq, Eq, Debug, Copy)]
525pub enum BindingAnnotation {
526 Unannotated,
532
533 Mutable,
535
536 Ref,
538
539 RefMut,
541}
542
543impl BindingAnnotation {
544 pub fn new(is_mutable: bool, is_ref: bool) -> Self {
545 match (is_mutable, is_ref) {
546 (true, true) => BindingAnnotation::RefMut,
547 (false, true) => BindingAnnotation::Ref,
548 (true, false) => BindingAnnotation::Mutable,
549 (false, false) => BindingAnnotation::Unannotated,
550 }
551 }
552}
553
554#[derive(Debug, Clone, Eq, PartialEq)]
555pub enum BindingProblems {
556 BoundMoreThanOnce,
558 BoundInconsistently,
560 NotBoundAcrossAll,
562}
563
564#[derive(Debug, Clone, Eq, PartialEq)]
565pub struct Binding {
566 pub name: Name,
567 pub mode: BindingAnnotation,
568 pub problems: Option<BindingProblems>,
569 pub hygiene: HygieneId,
572}
573
574#[derive(Debug, Clone, Eq, PartialEq)]
575pub struct RecordFieldPat {
576 pub name: Name,
577 pub pat: PatId,
578}
579
580#[derive(Debug, Clone, Eq, PartialEq)]
582pub enum Pat {
583 Missing,
584 Wild,
585 Tuple {
586 args: Box<[PatId]>,
587 ellipsis: Option<u32>,
588 },
589 Or(Box<[PatId]>),
590 Record {
591 path: Option<Box<Path>>,
592 args: Box<[RecordFieldPat]>,
593 ellipsis: bool,
594 },
595 Range {
596 start: Option<ExprId>,
597 end: Option<ExprId>,
598 },
599 Slice {
600 prefix: Box<[PatId]>,
601 slice: Option<PatId>,
602 suffix: Box<[PatId]>,
603 },
604 Path(Path),
606 Lit(ExprId),
607 Bind {
608 id: BindingId,
609 subpat: Option<PatId>,
610 },
611 TupleStruct {
612 path: Option<Box<Path>>,
613 args: Box<[PatId]>,
614 ellipsis: Option<u32>,
615 },
616 Ref {
617 pat: PatId,
618 mutability: Mutability,
619 },
620 Box {
621 inner: PatId,
622 },
623 ConstBlock(ExprId),
624 Expr(ExprId),
628}
629
630impl Pat {
631 pub fn walk_child_pats(&self, mut f: impl FnMut(PatId)) {
632 match self {
633 Pat::Range { .. }
634 | Pat::Lit(..)
635 | Pat::Path(..)
636 | Pat::ConstBlock(..)
637 | Pat::Wild
638 | Pat::Missing
639 | Pat::Expr(_) => {}
640 Pat::Bind { subpat, .. } => {
641 subpat.iter().copied().for_each(f);
642 }
643 Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => {
644 args.iter().copied().for_each(f);
645 }
646 Pat::Ref { pat, .. } => f(*pat),
647 Pat::Slice { prefix, slice, suffix } => {
648 let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter());
649 total_iter.copied().for_each(f);
650 }
651 Pat::Record { args, .. } => {
652 args.iter().map(|f| f.pat).for_each(f);
653 }
654 Pat::Box { inner } => f(*inner),
655 }
656 }
657}