Skip to main content

midenc_hir/
program_point.rs

1use core::fmt;
2
3use crate::{
4    Block, BlockRef, EntityListCursor, EntityListCursorMut, EntityMut, EntityRef, Operation,
5    OperationRef, Spanned,
6    entity::{EntityProjection, EntityProjectionMut},
7};
8
9/// [ProgramPoint] represents a specific location in the execution of a program.
10///
11/// A program point consists of two parts:
12///
13/// * An anchor, either a block or operation
14/// * A position, i.e. the direction relative to the anchor to which the program point refers
15///
16/// A program point can be reified as a cursor within a block, such that an operation inserted at
17/// the cursor will be placed at the specified position relative to the anchor.
18#[derive(Default, Copy, Clone)]
19pub enum ProgramPoint {
20    /// A program point which refers to nothing, and is always invalid if used
21    #[default]
22    Invalid,
23    /// A program point referring to the entry or exit of a block
24    Block {
25        /// The block this program point refers to
26        block: BlockRef,
27        /// The placement of the cursor relative to `block`
28        position: Position,
29    },
30    /// A program point referring to the entry or exit of an operation
31    Op {
32        /// The operation this program point refers to
33        op: OperationRef,
34        /// The placement of the cursor relative to `op`
35        position: Position,
36    },
37}
38
39/// Represents the placement of inserted items relative to a [ProgramPoint]
40#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
41pub enum Position {
42    /// New items will be inserted before the current program point
43    Before,
44    /// New items will be inserted after the current program point
45    After,
46}
47
48impl<T> From<EntityRef<'_, T>> for ProgramPoint
49where
50    for<'a> ProgramPoint: From<&'a T>,
51{
52    #[inline]
53    fn from(entity: EntityRef<'_, T>) -> Self {
54        Self::from(&*entity)
55    }
56}
57
58impl<T> From<EntityMut<'_, T>> for ProgramPoint
59where
60    for<'a> ProgramPoint: From<&'a T>,
61{
62    #[inline]
63    fn from(entity: EntityMut<'_, T>) -> Self {
64        Self::from(&*entity)
65    }
66}
67
68/// Construct a ProgramPoint referring to the point at entry to `op`
69impl From<&Operation> for ProgramPoint {
70    #[inline]
71    fn from(op: &Operation) -> Self {
72        Self::from(op.as_operation_ref())
73    }
74}
75
76/// Construct a ProgramPoint referring to the point at entry to `op`
77impl From<OperationRef> for ProgramPoint {
78    #[inline]
79    fn from(op: OperationRef) -> Self {
80        Self::Op {
81            op,
82            position: Position::Before,
83        }
84    }
85}
86
87/// Construct a ProgramPoint referring to the point at entry to `block`
88impl From<&Block> for ProgramPoint {
89    #[inline]
90    fn from(block: &Block) -> Self {
91        Self::at_start_of(block.as_block_ref())
92    }
93}
94
95/// Construct a ProgramPoint referring to the point at entry to `block`
96impl From<BlockRef> for ProgramPoint {
97    #[inline]
98    fn from(block: BlockRef) -> Self {
99        Self::Block {
100            block,
101            position: Position::Before,
102        }
103    }
104}
105
106#[doc(hidden)]
107#[derive(Copy, Clone)]
108pub struct BlockPoint {
109    block: BlockRef,
110    point: Position,
111}
112impl From<BlockPoint> for ProgramPoint {
113    fn from(point: BlockPoint) -> Self {
114        ProgramPoint::Block {
115            block: point.block,
116            position: point.point,
117        }
118    }
119}
120impl From<BlockRef> for BlockPoint {
121    fn from(block: BlockRef) -> Self {
122        Self {
123            block,
124            point: Position::Before,
125        }
126    }
127}
128impl From<&Block> for BlockPoint {
129    fn from(block: &Block) -> Self {
130        Self {
131            block: block.as_block_ref(),
132            point: Position::Before,
133        }
134    }
135}
136
137impl ProgramPoint {
138    /// Create a [ProgramPoint] at entry to `entity`, i.e. "before"
139    #[inline]
140    pub fn before(entity: impl Into<ProgramPoint>) -> Self {
141        entity.into()
142    }
143
144    /// Create a [ProgramPoint] at exit from `entity`, i.e. "after"
145    pub fn after(entity: impl Into<ProgramPoint>) -> Self {
146        let mut pp = entity.into();
147        match &mut pp {
148            Self::Invalid => (),
149            Self::Op {
150                position: point, ..
151            }
152            | Self::Block {
153                position: point, ..
154            } => {
155                *point = Position::After;
156            }
157        }
158        pp
159    }
160
161    /// Create a [ProgramPoint] at entry to `block`, i.e. "before"
162    pub fn at_start_of(block: impl Into<BlockPoint>) -> Self {
163        let BlockPoint { block, .. } = block.into();
164        Self::Block {
165            block,
166            position: Position::Before,
167        }
168    }
169
170    /// Create a [ProgramPoint] at exit from `block`, i.e. "after"
171    pub fn at_end_of(block: impl Into<BlockPoint>) -> Self {
172        let BlockPoint { block, .. } = block.into();
173        Self::Block {
174            block,
175            position: Position::After,
176        }
177    }
178
179    /// Returns true if this program point is at the start of the containing block
180    pub fn is_at_block_start(&self) -> bool {
181        self.operation().is_some_and(|op| {
182            op.parent().is_some() && op.prev().is_none() && self.placement() == Position::Before
183        }) || matches!(self, Self::Block { position: Position::Before, block, .. } if block.borrow().body().is_empty())
184    }
185
186    /// Returns true if this program point is at the end of the containing block
187    pub fn is_at_block_end(&self) -> bool {
188        self.operation().is_some_and(|op| {
189            op.parent().is_some() && op.next().is_none() && self.placement() == Position::After
190        }) || matches!(self, Self::Block { position: Position::After, block, .. } if block.borrow().body().is_empty())
191    }
192
193    /// Returns the block of the program point anchor.
194    ///
195    /// Returns `None`, if the program point is either invalid, or pointing to an orphaned operation
196    pub fn block(&self) -> Option<BlockRef> {
197        match self {
198            Self::Invalid => None,
199            Self::Block { block, .. } => Some(*block),
200            Self::Op { op, .. } => op.parent(),
201        }
202    }
203
204    /// Returns the program point anchor as an operation.
205    ///
206    /// Returns `None` if the program point is either invalid, or not pointing to a specific op
207    pub fn operation(&self) -> Option<OperationRef> {
208        match self {
209            Self::Invalid => None,
210            Self::Block {
211                position: Position::Before,
212                block,
213                ..
214            } => block.borrow().body().front().as_pointer(),
215            Self::Block {
216                position: Position::After,
217                block,
218                ..
219            } => block.borrow().body().back().as_pointer(),
220            Self::Op { op, .. } => Some(*op),
221        }
222    }
223
224    /// Returns the operation after [Self::operation], relative to this program point.
225    ///
226    /// If the current program point is in an orphaned operation, this will return the current op.
227    ///
228    /// Returns `None` if the program point is either invalid, or not pointing to a specific op
229    #[track_caller]
230    pub fn next_operation(&self) -> Option<OperationRef> {
231        assert!(!self.is_at_block_end());
232        match self {
233            Self::Op {
234                position: Position::After,
235                op,
236                ..
237            } if op.parent().is_some() => op.next(),
238            Self::Op { op, .. } => Some(*op),
239            Self::Block {
240                position: Position::Before,
241                block,
242            } => block.borrow().front(),
243            Self::Block { .. } | Self::Invalid => None,
244        }
245    }
246
247    /// Returns the operation preceding [Self::operation], relative to this program point.
248    ///
249    /// If the current program point is in an orphaned operation, this will return the current op.
250    ///
251    /// Returns `None` if the program point is either invalid, or not pointing to a specific op
252    #[track_caller]
253    pub fn prev_operation(&self) -> Option<OperationRef> {
254        assert!(!self.is_at_block_start());
255        match self {
256            Self::Op {
257                position: Position::Before,
258                op,
259                ..
260            } if op.parent().is_some() => op.prev(),
261            Self::Op { op, .. } => Some(*op),
262            Self::Block {
263                position: Position::After,
264                block,
265            } => block.borrow().back(),
266            Self::Block { .. } | Self::Invalid => None,
267        }
268    }
269
270    /// Returns true if this program point refers to a valid program point
271    #[inline]
272    pub fn is_valid(&self) -> bool {
273        !self.is_unset()
274    }
275
276    /// Returns true if this program point is invalid/unset
277    #[inline]
278    pub fn is_unset(&self) -> bool {
279        matches!(self, Self::Invalid)
280    }
281
282    /// The positioning relative to the program point anchor
283    pub fn placement(&self) -> Position {
284        match self {
285            Self::Invalid => Position::After,
286            Self::Block {
287                position: point, ..
288            }
289            | Self::Op {
290                position: point, ..
291            } => *point,
292        }
293    }
294
295    /// Obtain an immutable cursor in the block corresponding to this program point.
296    ///
297    /// The resulting cursor can have `as_pointer` or `get` called on it to get the operation to
298    /// which this point is relative. The intuition around where the cursor is placed for a given
299    /// program point can be understood as answering the question of "where does the cursor need
300    /// to be, such that if I inserted an op at that cursor, that the insertion would be placed at
301    /// the referenced program point (semantically before or after an operation or block). The
302    /// specific rules are as follows:
303    ///
304    /// * If "before" a block, the resulting cursor is the null cursor for the containing block,
305    ///   since an insertion at the null cursor will be placed at the start of the block.
306    /// * If "after" a block, the cursor is placed on the last operation in the block, as insertion
307    ///   will place the inserted op at the end of the block
308    /// * If "before" an operation, the cursor is placed on the operation immediately preceding
309    ///   `self`, or a null cursor is returned. In both cases, an insertion at the returned cursor
310    ///   would be placed immediately before `self`
311    /// * If "after" an operation, the cursor is placed on the operation in `self`, so that
312    ///   insertion will place the inserted op immediately after `self`.
313    ///
314    /// NOTE: The block to which this program point refers will be borrowed for the lifetime of the
315    /// returned [EntityProjection].
316    pub fn cursor<'a, 'b: 'a, 'c: 'b>(
317        &'c self,
318    ) -> Option<EntityProjection<'b, EntityListCursor<'a, Operation>>> {
319        match self {
320            Self::Invalid => None,
321            Self::Block {
322                block,
323                position: point,
324            } => Some(EntityRef::project(block.borrow(), |block| match point {
325                Position::Before => block.body().front(),
326                Position::After => block.body().back(),
327            })),
328            Self::Op {
329                op,
330                position: point,
331            } => {
332                let block = op.parent()?;
333                Some(EntityRef::project(block.borrow(), |block| match point {
334                    Position::Before => {
335                        if let Some(placement) = op.prev() {
336                            unsafe { block.body().cursor_from_ptr(placement) }
337                        } else {
338                            block.body().cursor()
339                        }
340                    }
341                    Position::After => unsafe { block.body().cursor_from_ptr(*op) },
342                }))
343            }
344        }
345    }
346
347    /// Same as [Self::cursor], but obtains a mutable cursor instead.
348    ///
349    /// NOTE: The block to which this program point refers will be borrowed mutably for the lifetime
350    /// of the returned [EntityProjectionMut].
351    pub fn cursor_mut<'a, 'b: 'a, 'c: 'b>(
352        &'c mut self,
353    ) -> Option<EntityProjectionMut<'b, EntityListCursorMut<'a, Operation>>> {
354        match self {
355            Self::Invalid => None,
356            Self::Block {
357                block,
358                position: point,
359            } => Some(EntityMut::project(block.borrow_mut(), |block| match point {
360                Position::Before => block.body_mut().cursor_mut(),
361                Position::After => block.body_mut().back_mut(),
362            })),
363            Self::Op {
364                op,
365                position: point,
366            } => {
367                let mut block = op.parent()?;
368                Some(EntityMut::project(block.borrow_mut(), |block| match point {
369                    Position::Before => {
370                        if let Some(placement) = op.prev() {
371                            unsafe { block.body_mut().cursor_mut_from_ptr(placement) }
372                        } else {
373                            block.body_mut().cursor_mut()
374                        }
375                    }
376                    Position::After => unsafe { block.body_mut().cursor_mut_from_ptr(*op) },
377                }))
378            }
379        }
380    }
381}
382
383impl Eq for ProgramPoint {}
384
385impl PartialEq for ProgramPoint {
386    fn eq(&self, other: &Self) -> bool {
387        match (self, other) {
388            (Self::Invalid, Self::Invalid) => true,
389            (Self::Invalid, _) | (_, Self::Invalid) => false,
390            (
391                Self::Block {
392                    block: x,
393                    position: xp,
394                },
395                Self::Block {
396                    block: y,
397                    position: yp,
398                },
399            ) => x == y && xp == yp,
400            (
401                Self::Op {
402                    op: x,
403                    position: xp,
404                    ..
405                },
406                Self::Op {
407                    op: y,
408                    position: yp,
409                    ..
410                },
411            ) => x == y && xp == yp,
412            (..) => false,
413        }
414    }
415}
416
417impl core::hash::Hash for ProgramPoint {
418    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
419        core::mem::discriminant(self).hash(state);
420        match self {
421            Self::Invalid => (),
422            Self::Block {
423                block,
424                position: point,
425            } => {
426                core::ptr::hash(BlockRef::as_ptr(block), state);
427                point.hash(state);
428            }
429            Self::Op {
430                op,
431                position: point,
432                ..
433            } => {
434                core::ptr::hash(OperationRef::as_ptr(op), state);
435                point.hash(state);
436            }
437        }
438    }
439}
440
441impl Spanned for ProgramPoint {
442    fn span(&self) -> crate::SourceSpan {
443        use crate::SourceSpan;
444
445        match self {
446            Self::Invalid => SourceSpan::UNKNOWN,
447            Self::Block {
448                block,
449                position: point,
450            } => match point {
451                Position::Before => {
452                    block.borrow().body().front().get().map(|op| op.span()).unwrap_or_default()
453                }
454                Position::After => {
455                    block.borrow().body().back().get().map(|op| op.span()).unwrap_or_default()
456                }
457            },
458            Self::Op { op, .. } => op.borrow().span(),
459        }
460    }
461}
462
463impl fmt::Display for ProgramPoint {
464    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
465        use crate::EntityWithId;
466        match self {
467            Self::Invalid => f.write_str("<invalid>"),
468            Self::Block {
469                block,
470                position: point,
471            } => match point {
472                Position::Before => write!(f, "start({})", &block.borrow().id()),
473                Position::After => write!(f, "end({})", &block.borrow().id()),
474            },
475            Self::Op {
476                op,
477                position: point,
478            } => {
479                use crate::formatter::{const_text, display};
480                let block = op
481                    .parent()
482                    .map(|blk| display(blk.borrow().id()))
483                    .unwrap_or_else(|| const_text("null"));
484                match point {
485                    Position::Before => {
486                        write!(f, "before({} in {block})", &op.borrow().name())
487                    }
488                    Position::After => {
489                        write!(f, "after({} in {block})", &op.borrow().name())
490                    }
491                }
492            }
493        }
494    }
495}
496
497impl fmt::Debug for ProgramPoint {
498    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
499        use crate::EntityWithId;
500        match self {
501            Self::Invalid => f.write_str("Invalid"),
502            Self::Block {
503                block,
504                position: point,
505            } => f
506                .debug_struct("Block")
507                .field("block", &block.borrow().id())
508                .field("point", point)
509                .finish(),
510            Self::Op {
511                op,
512                position: point,
513            } => f
514                .debug_struct("Op")
515                .field("block", &op.parent().map(|blk| blk.borrow().id()))
516                .field("point", point)
517                .field("op", &op.borrow())
518                .finish(),
519        }
520    }
521}