1use shape_ast::ast::Span;
8use std::fmt;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
14pub struct SlotId(pub u16);
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18pub struct FieldIdx(pub u16);
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
22pub struct BasicBlockId(pub u32);
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
27pub struct Point(pub u32);
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
31pub struct LoanId(pub u32);
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
35pub enum ProjectionStep {
36 Field(FieldIdx),
37 Index,
41}
42
43impl fmt::Display for SlotId {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 write!(f, "_{}", self.0)
46 }
47}
48
49impl fmt::Display for BasicBlockId {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 write!(f, "bb{}", self.0)
52 }
53}
54
55impl fmt::Display for Point {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 write!(f, "p{}", self.0)
58 }
59}
60
61impl fmt::Display for LoanId {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 write!(f, "L{}", self.0)
64 }
65}
66
67#[derive(Debug, Clone, PartialEq, Eq, Hash)]
72pub enum Place {
73 Local(SlotId),
75 Field(Box<Place>, FieldIdx),
77 Index(Box<Place>, Box<Operand>),
80 Deref(Box<Place>),
82}
83
84impl Place {
85 pub fn root_local(&self) -> SlotId {
87 match self {
88 Place::Local(slot) => *slot,
89 Place::Field(base, _) | Place::Index(base, _) | Place::Deref(base) => base.root_local(),
90 }
91 }
92
93 pub fn is_prefix_of(&self, other: &Place) -> bool {
96 if self == other {
97 return true;
98 }
99 match other {
100 Place::Local(_) => false,
101 Place::Field(base, _) | Place::Index(base, _) | Place::Deref(base) => {
102 self.is_prefix_of(base)
103 }
104 }
105 }
106
107 pub fn conflicts_with(&self, other: &Place) -> bool {
112 if self.root_local() != other.root_local() {
114 return false;
115 }
116 self.is_prefix_of(other) || other.is_prefix_of(self) || self.overlaps(other)
118 }
119
120 fn overlaps(&self, other: &Place) -> bool {
121 match (self, other) {
122 (Place::Local(a), Place::Local(b)) => a == b,
123 (Place::Field(base_a, field_a), Place::Field(base_b, field_b)) => {
125 if base_a == base_b {
126 field_a == field_b
127 } else {
128 base_a.overlaps(base_b)
129 }
130 }
131 (Place::Index(base_a, _), Place::Index(base_b, _)) => base_a.overlaps(base_b),
133 _ => self.is_prefix_of(other) || other.is_prefix_of(self),
134 }
135 }
136
137 pub fn projection_steps(&self) -> Vec<ProjectionStep> {
139 let mut steps = Vec::new();
140 self.collect_projection_steps(&mut steps);
141 steps
142 }
143
144 fn collect_projection_steps(&self, steps: &mut Vec<ProjectionStep>) {
145 match self {
146 Place::Local(_) => {}
147 Place::Field(base, field) => {
148 base.collect_projection_steps(steps);
149 steps.push(ProjectionStep::Field(*field));
150 }
151 Place::Index(base, _) => {
152 base.collect_projection_steps(steps);
153 steps.push(ProjectionStep::Index);
154 }
155 Place::Deref(base) => base.collect_projection_steps(steps),
156 }
157 }
158}
159
160impl fmt::Display for Place {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 match self {
163 Place::Local(slot) => write!(f, "{}", slot),
164 Place::Field(base, field) => write!(f, "{}.{}", base, field.0),
165 Place::Index(base, idx) => write!(f, "{}[{}]", base, idx),
166 Place::Deref(base) => write!(f, "*{}", base),
167 }
168 }
169}
170
171#[derive(Debug, Clone, PartialEq, Eq, Hash)]
175pub enum Operand {
176 Copy(Place),
178 Move(Place),
180 MoveExplicit(Place),
182 Constant(MirConstant),
184}
185
186impl fmt::Display for Operand {
187 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188 match self {
189 Operand::Copy(p) => write!(f, "copy {}", p),
190 Operand::Move(p) => write!(f, "move {}", p),
191 Operand::MoveExplicit(p) => write!(f, "move! {}", p),
192 Operand::Constant(c) => write!(f, "{}", c),
193 }
194 }
195}
196
197#[derive(Debug, Clone, PartialEq, Eq, Hash)]
199pub enum MirConstant {
200 Int(i64),
201 Bool(bool),
202 None,
203 StringId(u32),
205 Float(u64),
207 Function(String),
209 Method(String),
211}
212
213impl fmt::Display for MirConstant {
214 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215 match self {
216 MirConstant::Int(v) => write!(f, "{}", v),
217 MirConstant::Bool(v) => write!(f, "{}", v),
218 MirConstant::None => write!(f, "none"),
219 MirConstant::StringId(id) => write!(f, "str#{}", id),
220 MirConstant::Float(bits) => write!(f, "{}", f64::from_bits(*bits)),
221 MirConstant::Function(name) => write!(f, "fn:{}", name),
222 MirConstant::Method(name) => write!(f, "method:{}", name),
223 }
224 }
225}
226
227#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
231pub enum BorrowKind {
232 Shared,
234 Exclusive,
236}
237
238impl fmt::Display for BorrowKind {
239 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
240 match self {
241 BorrowKind::Shared => write!(f, "&"),
242 BorrowKind::Exclusive => write!(f, "&mut"),
243 }
244 }
245}
246
247#[derive(Debug, Clone, PartialEq)]
249pub enum Rvalue {
250 Use(Operand),
252 Borrow(BorrowKind, Place),
254 BinaryOp(BinOp, Operand, Operand),
256 UnaryOp(UnOp, Operand),
258 Aggregate(Vec<Operand>),
261 Clone(Operand),
263}
264
265#[derive(Debug, Clone, Copy, PartialEq, Eq)]
267pub enum BinOp {
268 Add,
269 Sub,
270 Mul,
271 Div,
272 Mod,
273 Eq,
274 Ne,
275 Lt,
276 Le,
277 Gt,
278 Ge,
279 And,
280 Or,
281}
282
283#[derive(Debug, Clone, Copy, PartialEq, Eq)]
285pub enum UnOp {
286 Neg,
287 Not,
288}
289
290#[derive(Debug, Clone, Copy, PartialEq, Eq)]
294pub enum TaskBoundaryKind {
295 Detached,
297 Structured,
299}
300
301#[derive(Debug, Clone, PartialEq)]
305pub struct MirStatement {
306 pub kind: StatementKind,
307 pub span: Span,
308 pub point: Point,
310}
311
312#[derive(Debug, Clone, PartialEq)]
313pub enum StatementKind {
314 Assign(Place, Rvalue),
316 Drop(Place),
319 TaskBoundary(Vec<Operand>, TaskBoundaryKind),
323 ClosureCapture {
326 closure_slot: SlotId,
327 operands: Vec<Operand>,
328 },
329 ArrayStore {
332 container_slot: SlotId,
333 operands: Vec<Operand>,
334 },
335 ObjectStore {
338 container_slot: SlotId,
339 operands: Vec<Operand>,
340 },
341 EnumStore {
344 container_slot: SlotId,
345 operands: Vec<Operand>,
346 },
347 Nop,
349}
350
351#[derive(Debug, Clone, PartialEq)]
355pub struct Terminator {
356 pub kind: TerminatorKind,
357 pub span: Span,
358}
359
360#[derive(Debug, Clone, PartialEq)]
361pub enum TerminatorKind {
362 Goto(BasicBlockId),
364 SwitchBool {
366 operand: Operand,
367 true_bb: BasicBlockId,
368 false_bb: BasicBlockId,
369 },
370 Call {
372 func: Operand,
373 args: Vec<Operand>,
374 destination: Place,
376 next: BasicBlockId,
378 },
379 Return,
381 Unreachable,
383}
384
385#[derive(Debug, Clone)]
389pub struct BasicBlock {
390 pub id: BasicBlockId,
391 pub statements: Vec<MirStatement>,
392 pub terminator: Terminator,
393}
394
395#[derive(Debug, Clone)]
399pub struct MirFunction {
400 pub name: String,
401 pub blocks: Vec<BasicBlock>,
403 pub num_locals: u16,
405 pub param_slots: Vec<SlotId>,
407 pub param_reference_kinds: Vec<Option<BorrowKind>>,
409 pub local_types: Vec<LocalTypeInfo>,
411 pub span: Span,
413}
414
415#[derive(Debug, Clone, PartialEq, Eq)]
417pub enum LocalTypeInfo {
418 Copy,
420 NonCopy,
422 Unknown,
424}
425
426impl MirFunction {
427 pub fn entry_block(&self) -> BasicBlockId {
429 BasicBlockId(0)
430 }
431
432 pub fn iter_blocks(&self) -> impl Iterator<Item = &BasicBlock> {
434 self.blocks.iter()
435 }
436
437 pub fn block(&self, id: BasicBlockId) -> &BasicBlock {
439 &self.blocks[id.0 as usize]
440 }
441
442 pub fn all_points(&self) -> Vec<(Point, BasicBlockId, usize)> {
445 let mut points = Vec::new();
446 for block in &self.blocks {
447 for (i, stmt) in block.statements.iter().enumerate() {
448 points.push((stmt.point, block.id, i));
449 }
450 }
451 points
452 }
453}
454
455#[cfg(test)]
456mod tests {
457 use super::*;
458
459 #[test]
460 fn test_place_root_local() {
461 let p = Place::Field(Box::new(Place::Local(SlotId(0))), FieldIdx(1));
462 assert_eq!(p.root_local(), SlotId(0));
463 }
464
465 #[test]
466 fn test_place_prefix() {
467 let x = Place::Local(SlotId(0));
468 let xa = Place::Field(Box::new(Place::Local(SlotId(0))), FieldIdx(0));
469 assert!(x.is_prefix_of(&xa));
470 assert!(!xa.is_prefix_of(&x));
471 }
472
473 #[test]
474 fn test_disjoint_fields_no_conflict() {
475 let xa = Place::Field(Box::new(Place::Local(SlotId(0))), FieldIdx(0));
476 let xb = Place::Field(Box::new(Place::Local(SlotId(0))), FieldIdx(1));
477 assert!(!xa.overlaps(&xb));
479 }
480
481 #[test]
482 fn test_same_field_conflicts() {
483 let xa1 = Place::Field(Box::new(Place::Local(SlotId(0))), FieldIdx(0));
484 let xa2 = Place::Field(Box::new(Place::Local(SlotId(0))), FieldIdx(0));
485 assert!(xa1.conflicts_with(&xa2));
486 }
487
488 #[test]
489 fn test_different_locals_no_conflict() {
490 let x = Place::Local(SlotId(0));
491 let y = Place::Local(SlotId(1));
492 assert!(!x.conflicts_with(&y));
493 }
494
495 #[test]
496 fn test_parent_child_conflict() {
497 let x = Place::Local(SlotId(0));
498 let xa = Place::Field(Box::new(Place::Local(SlotId(0))), FieldIdx(0));
499 assert!(x.conflicts_with(&xa));
500 assert!(xa.conflicts_with(&x));
501 }
502}