Skip to main content

melior/ir/
operation.rs

1//! Operations and operation builders.
2
3mod builder;
4mod printing_flags;
5mod result;
6
7pub use self::{
8    builder::OperationBuilder, printing_flags::OperationPrintingFlags, result::OperationResult,
9};
10use super::{Attribute, AttributeLike, BlockRef, Identifier, Location, RegionRef, Value};
11use crate::{
12    context::{Context, ContextRef},
13    utility::{print_callback, print_string_callback},
14    Error, StringRef,
15};
16use core::{
17    fmt,
18    mem::{forget, transmute},
19};
20use mlir_sys::{
21    mlirOperationClone, mlirOperationDestroy, mlirOperationDump, mlirOperationEqual,
22    mlirOperationGetAttribute, mlirOperationGetAttributeByName, mlirOperationGetBlock,
23    mlirOperationGetContext, mlirOperationGetLocation, mlirOperationGetName,
24    mlirOperationGetNextInBlock, mlirOperationGetNumAttributes, mlirOperationGetNumOperands,
25    mlirOperationGetNumRegions, mlirOperationGetNumResults, mlirOperationGetNumSuccessors,
26    mlirOperationGetOperand, mlirOperationGetParentOperation, mlirOperationGetRegion,
27    mlirOperationGetResult, mlirOperationGetSuccessor, mlirOperationPrint,
28    mlirOperationPrintWithFlags, mlirOperationRemoveAttributeByName, mlirOperationRemoveFromParent,
29    mlirOperationSetAttributeByName, mlirOperationVerify, MlirOperation,
30};
31use std::{
32    ffi::c_void,
33    fmt::{Debug, Display, Formatter},
34    marker::PhantomData,
35    ops::{Deref, DerefMut},
36};
37
38/// An operation.
39pub struct Operation<'c> {
40    raw: MlirOperation,
41    _context: PhantomData<&'c Context>,
42}
43
44impl<'c> Operation<'c> {
45    /// Returns a context.
46    pub fn context(&self) -> ContextRef<'c> {
47        unsafe { ContextRef::from_raw(mlirOperationGetContext(self.raw)) }
48    }
49
50    /// Returns a name.
51    pub fn name(&self) -> Identifier<'c> {
52        unsafe { Identifier::from_raw(mlirOperationGetName(self.raw)) }
53    }
54
55    /// Returns a block.
56    // TODO Store lifetime of block in operations, or create another type like
57    // `AppendedOperationRef`?
58    pub fn block(&self) -> Option<BlockRef<'c, '_>> {
59        unsafe { BlockRef::from_option_raw(mlirOperationGetBlock(self.raw)) }
60    }
61
62    /// Returns the number of operands.
63    pub fn operand_count(&self) -> usize {
64        unsafe { mlirOperationGetNumOperands(self.raw) as usize }
65    }
66
67    /// Returns the operand at a position.
68    pub fn operand(&self, index: usize) -> Result<Value<'c, '_>, Error> {
69        if index < self.operand_count() {
70            Ok(unsafe { Value::from_raw(mlirOperationGetOperand(self.raw, index as isize)) })
71        } else {
72            Err(Error::PositionOutOfBounds {
73                name: "operation operand",
74                value: self.to_string(),
75                index,
76            })
77        }
78    }
79
80    /// Returns all operands.
81    pub fn operands(&self) -> impl Iterator<Item = Value<'c, '_>> {
82        (0..self.operand_count()).map(|index| self.operand(index).expect("valid operand index"))
83    }
84
85    /// Returns the number of results.
86    pub fn result_count(&self) -> usize {
87        unsafe { mlirOperationGetNumResults(self.raw) as usize }
88    }
89
90    /// Returns a result at a position.
91    pub fn result(&self, index: usize) -> Result<OperationResult<'c, '_>, Error> {
92        if index < self.result_count() {
93            Ok(unsafe {
94                OperationResult::from_raw(mlirOperationGetResult(self.raw, index as isize))
95            })
96        } else {
97            Err(Error::PositionOutOfBounds {
98                name: "operation result",
99                value: self.to_string(),
100                index,
101            })
102        }
103    }
104
105    /// Returns all results.
106    pub fn results(&self) -> impl Iterator<Item = OperationResult<'c, '_>> {
107        (0..self.result_count()).map(|index| self.result(index).expect("valid result index"))
108    }
109
110    /// Returns the number of regions.
111    pub fn region_count(&self) -> usize {
112        unsafe { mlirOperationGetNumRegions(self.raw) as usize }
113    }
114
115    /// Returns a region at a position.
116    pub fn region(&self, index: usize) -> Result<RegionRef<'c, '_>, Error> {
117        if index < self.region_count() {
118            Ok(unsafe { RegionRef::from_raw(mlirOperationGetRegion(self.raw, index as isize)) })
119        } else {
120            Err(Error::PositionOutOfBounds {
121                name: "region",
122                value: self.to_string(),
123                index,
124            })
125        }
126    }
127
128    /// Returns all regions.
129    pub fn regions(&self) -> impl Iterator<Item = RegionRef<'c, '_>> {
130        (0..self.region_count()).map(|index| self.region(index).expect("valid result index"))
131    }
132
133    /// Gets the location of the operation.
134    pub fn location(&self) -> Location<'c> {
135        unsafe { Location::from_raw(mlirOperationGetLocation(self.raw)) }
136    }
137
138    /// Returns the number of successors.
139    pub fn successor_count(&self) -> usize {
140        unsafe { mlirOperationGetNumSuccessors(self.raw) as usize }
141    }
142
143    /// Returns a successor at a position.
144    pub fn successor(&self, index: usize) -> Result<BlockRef<'c, '_>, Error> {
145        if index < self.successor_count() {
146            Ok(unsafe { BlockRef::from_raw(mlirOperationGetSuccessor(self.raw, index as isize)) })
147        } else {
148            Err(Error::PositionOutOfBounds {
149                name: "successor",
150                value: self.to_string(),
151                index,
152            })
153        }
154    }
155
156    /// Returns all successors.
157    pub fn successors(&self) -> impl Iterator<Item = BlockRef<'c, '_>> {
158        (0..self.successor_count())
159            .map(|index| self.successor(index).expect("valid successor index"))
160    }
161
162    /// Returns the number of attributes.
163    pub fn attribute_count(&self) -> usize {
164        unsafe { mlirOperationGetNumAttributes(self.raw) as usize }
165    }
166
167    /// Returns a attribute at a position.
168    pub fn attribute_at(&self, index: usize) -> Result<(Identifier<'c>, Attribute<'c>), Error> {
169        if index < self.attribute_count() {
170            unsafe {
171                let named_attribute = mlirOperationGetAttribute(self.raw, index as isize);
172                Ok((
173                    Identifier::from_raw(named_attribute.name),
174                    Attribute::from_raw(named_attribute.attribute),
175                ))
176            }
177        } else {
178            Err(Error::PositionOutOfBounds {
179                name: "attribute",
180                value: self.to_string(),
181                index,
182            })
183        }
184    }
185
186    /// Returns all attributes.
187    pub fn attributes(&self) -> impl Iterator<Item = (Identifier<'c>, Attribute<'c>)> + '_ {
188        (0..self.attribute_count())
189            .map(|index| self.attribute_at(index).expect("valid attribute index"))
190    }
191
192    /// Returns a attribute with the given name.
193    pub fn attribute(&self, name: &str) -> Result<Attribute<'c>, Error> {
194        unsafe {
195            Attribute::from_option_raw(mlirOperationGetAttributeByName(
196                self.raw,
197                StringRef::new(name).to_raw(),
198            ))
199        }
200        .ok_or_else(|| Error::AttributeNotFound(name.into()))
201    }
202
203    /// Checks if the operation has a attribute with the given name.
204    pub fn has_attribute(&self, name: &str) -> bool {
205        self.attribute(name).is_ok()
206    }
207
208    /// Sets the attribute with the given name to the given attribute.
209    pub fn set_attribute(&mut self, name: &str, attribute: Attribute<'c>) {
210        unsafe {
211            mlirOperationSetAttributeByName(
212                self.raw,
213                StringRef::new(name).to_raw(),
214                attribute.to_raw(),
215            )
216        }
217    }
218
219    /// Removes the attribute with the given name.
220    pub fn remove_attribute(&mut self, name: &str) -> Result<(), Error> {
221        unsafe { mlirOperationRemoveAttributeByName(self.raw, StringRef::new(name).to_raw()) }
222            .then_some(())
223            .ok_or_else(|| Error::AttributeNotFound(name.into()))
224    }
225
226    /// Returns a reference to the next operation in the same block.
227    pub fn next_in_block(&self) -> Option<OperationRef<'c, '_>> {
228        unsafe { OperationRef::from_option_raw(mlirOperationGetNextInBlock(self.raw)) }
229    }
230
231    /// Returns a mutable reference to the next operation in the same block.
232    pub fn next_in_block_mut(&self) -> Option<OperationRefMut<'c, '_>> {
233        unsafe { OperationRefMut::from_option_raw(mlirOperationGetNextInBlock(self.raw)) }
234    }
235
236    /// Returns a reference to the previous operation in the same block.
237    pub fn previous_in_block(&self) -> Option<OperationRef<'c, '_>> {
238        todo!("mlirOperationGetPrevInBlock is not exposed in the C API")
239    }
240
241    /// Returns a reference to a parent operation.
242    pub fn parent_operation(&self) -> Option<OperationRef<'c, '_>> {
243        unsafe { OperationRef::from_option_raw(mlirOperationGetParentOperation(self.raw)) }
244    }
245
246    /// Removes itself from a parent block.
247    pub fn remove_from_parent(&mut self) {
248        unsafe { mlirOperationRemoveFromParent(self.raw) }
249    }
250
251    /// Verifies an operation.
252    pub fn verify(&self) -> bool {
253        unsafe { mlirOperationVerify(self.raw) }
254    }
255
256    /// Dumps an operation.
257    pub fn dump(&self) {
258        unsafe { mlirOperationDump(self.raw) }
259    }
260
261    /// Prints an operation with flags.
262    pub fn to_string_with_flags(&self, flags: OperationPrintingFlags) -> Result<String, Error> {
263        let mut data = (String::new(), Ok::<_, Error>(()));
264
265        unsafe {
266            mlirOperationPrintWithFlags(
267                self.raw,
268                flags.to_raw(),
269                Some(print_string_callback),
270                &mut data as *mut _ as *mut _,
271            );
272        }
273
274        data.1?;
275
276        Ok(data.0)
277    }
278
279    /// Creates an operation from a raw object.
280    ///
281    /// # Safety
282    ///
283    /// A raw object must be valid.
284    pub unsafe fn from_raw(raw: MlirOperation) -> Self {
285        Self {
286            raw,
287            _context: Default::default(),
288        }
289    }
290
291    /// Creates an optional operation from a raw object.
292    ///
293    /// # Safety
294    ///
295    /// A raw object must be valid.
296    pub unsafe fn from_option_raw(raw: MlirOperation) -> Option<Self> {
297        if raw.ptr.is_null() {
298            None
299        } else {
300            Some(Self::from_raw(raw))
301        }
302    }
303
304    /// Converts an operation into a raw object.
305    pub const fn into_raw(self) -> MlirOperation {
306        let operation = self.raw;
307
308        forget(self);
309
310        operation
311    }
312}
313
314impl Clone for Operation<'_> {
315    fn clone(&self) -> Self {
316        unsafe { Self::from_raw(mlirOperationClone(self.raw)) }
317    }
318}
319
320impl Drop for Operation<'_> {
321    fn drop(&mut self) {
322        unsafe { mlirOperationDestroy(self.raw) };
323    }
324}
325
326impl PartialEq for Operation<'_> {
327    fn eq(&self, other: &Self) -> bool {
328        unsafe { mlirOperationEqual(self.raw, other.raw) }
329    }
330}
331
332impl Eq for Operation<'_> {}
333
334impl Display for Operation<'_> {
335    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
336        let mut data = (formatter, Ok(()));
337
338        unsafe {
339            mlirOperationPrint(
340                self.raw,
341                Some(print_callback),
342                &mut data as *mut _ as *mut c_void,
343            );
344        }
345
346        data.1
347    }
348}
349
350impl Debug for Operation<'_> {
351    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
352        writeln!(formatter, "Operation(")?;
353        Display::fmt(self, formatter)?;
354        write!(formatter, ")")
355    }
356}
357
358/// A reference to an operation.
359#[derive(Clone, Copy)]
360pub struct OperationRef<'c, 'a> {
361    raw: MlirOperation,
362    _reference: PhantomData<&'a Operation<'c>>,
363}
364
365impl<'c, 'a> OperationRef<'c, 'a> {
366    /// Returns a result at a position.
367    pub fn result(self, index: usize) -> Result<OperationResult<'c, 'a>, Error> {
368        unsafe { self.to_ref() }.result(index)
369    }
370
371    /// Returns an operation.
372    ///
373    /// This function is different from `deref` because the correct lifetime is
374    /// kept for the return type.
375    ///
376    /// # Safety
377    ///
378    /// The returned reference is safe to use only in the lifetime scope of the
379    /// operation reference.
380    pub unsafe fn to_ref(&self) -> &'a Operation<'c> {
381        // As we can't deref OperationRef<'a> into `&'a Operation`, we forcibly cast its
382        // lifetime here to extend it from the lifetime of `ObjectRef<'a>` itself into
383        // `'a`.
384        transmute(self)
385    }
386
387    /// Converts an operation reference into a raw object.
388    pub const fn to_raw(self) -> MlirOperation {
389        self.raw
390    }
391
392    /// Creates an operation reference from a raw object.
393    ///
394    /// # Safety
395    ///
396    /// A raw object must be valid.
397    pub unsafe fn from_raw(raw: MlirOperation) -> Self {
398        Self {
399            raw,
400            _reference: Default::default(),
401        }
402    }
403
404    /// Creates an optional operation reference from a raw object.
405    ///
406    /// # Safety
407    ///
408    /// A raw object must be valid.
409    pub unsafe fn from_option_raw(raw: MlirOperation) -> Option<Self> {
410        if raw.ptr.is_null() {
411            None
412        } else {
413            Some(Self::from_raw(raw))
414        }
415    }
416}
417
418impl<'c> Deref for OperationRef<'c, '_> {
419    type Target = Operation<'c>;
420
421    fn deref(&self) -> &Self::Target {
422        unsafe { transmute(self) }
423    }
424}
425
426impl PartialEq for OperationRef<'_, '_> {
427    fn eq(&self, other: &Self) -> bool {
428        unsafe { mlirOperationEqual(self.raw, other.raw) }
429    }
430}
431
432impl Eq for OperationRef<'_, '_> {}
433
434impl Display for OperationRef<'_, '_> {
435    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
436        Display::fmt(self.deref(), formatter)
437    }
438}
439
440impl Debug for OperationRef<'_, '_> {
441    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
442        Debug::fmt(self.deref(), formatter)
443    }
444}
445
446/// A mutable reference to an operation.
447#[derive(Clone, Copy)]
448pub struct OperationRefMut<'c, 'a> {
449    raw: MlirOperation,
450    _reference: PhantomData<&'a Operation<'c>>,
451}
452
453impl OperationRefMut<'_, '_> {
454    /// Converts an operation reference into a raw object.
455    pub const fn to_raw(self) -> MlirOperation {
456        self.raw
457    }
458
459    /// Creates an operation reference from a raw object.
460    ///
461    /// # Safety
462    ///
463    /// A raw object must be valid.
464    pub unsafe fn from_raw(raw: MlirOperation) -> Self {
465        Self {
466            raw,
467            _reference: Default::default(),
468        }
469    }
470
471    /// Creates an optional operation reference from a raw object.
472    ///
473    /// # Safety
474    ///
475    /// A raw object must be valid.
476    pub unsafe fn from_option_raw(raw: MlirOperation) -> Option<Self> {
477        if raw.ptr.is_null() {
478            None
479        } else {
480            Some(Self::from_raw(raw))
481        }
482    }
483}
484
485impl<'c> Deref for OperationRefMut<'c, '_> {
486    type Target = Operation<'c>;
487
488    fn deref(&self) -> &Self::Target {
489        unsafe { transmute(self) }
490    }
491}
492
493impl DerefMut for OperationRefMut<'_, '_> {
494    fn deref_mut(&mut self) -> &mut Self::Target {
495        unsafe { transmute(self) }
496    }
497}
498
499impl PartialEq for OperationRefMut<'_, '_> {
500    fn eq(&self, other: &Self) -> bool {
501        unsafe { mlirOperationEqual(self.raw, other.raw) }
502    }
503}
504
505impl Eq for OperationRefMut<'_, '_> {}
506
507impl Display for OperationRefMut<'_, '_> {
508    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
509        Display::fmt(self.deref(), formatter)
510    }
511}
512
513impl Debug for OperationRefMut<'_, '_> {
514    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
515        Debug::fmt(self.deref(), formatter)
516    }
517}
518
519#[cfg(test)]
520mod tests {
521    use super::*;
522    use crate::{
523        context::Context,
524        ir::{attribute::StringAttribute, Block, Location, Region, Type},
525        test::create_test_context,
526    };
527    use pretty_assertions::assert_eq;
528
529    #[test]
530    fn new() {
531        let context = create_test_context();
532        context.set_allow_unregistered_dialects(true);
533        OperationBuilder::new("foo", Location::unknown(&context))
534            .build()
535            .unwrap();
536    }
537
538    #[test]
539    fn name() {
540        let context = Context::new();
541        context.set_allow_unregistered_dialects(true);
542
543        assert_eq!(
544            OperationBuilder::new("foo", Location::unknown(&context),)
545                .build()
546                .unwrap()
547                .name(),
548            Identifier::new(&context, "foo")
549        );
550    }
551
552    #[test]
553    fn block() {
554        let context = create_test_context();
555        context.set_allow_unregistered_dialects(true);
556        let block = Block::new(&[]);
557        let operation = block.append_operation(
558            OperationBuilder::new("foo", Location::unknown(&context))
559                .build()
560                .unwrap(),
561        );
562
563        assert_eq!(operation.block().as_deref(), Some(&block));
564    }
565
566    #[test]
567    fn block_none() {
568        let context = create_test_context();
569        context.set_allow_unregistered_dialects(true);
570        assert_eq!(
571            OperationBuilder::new("foo", Location::unknown(&context))
572                .build()
573                .unwrap()
574                .block(),
575            None
576        );
577    }
578
579    #[test]
580    fn result_error() {
581        let context = create_test_context();
582        context.set_allow_unregistered_dialects(true);
583        assert_eq!(
584            OperationBuilder::new("foo", Location::unknown(&context))
585                .build()
586                .unwrap()
587                .result(0)
588                .unwrap_err(),
589            Error::PositionOutOfBounds {
590                name: "operation result",
591                value: "\"foo\"() : () -> ()\n".into(),
592                index: 0
593            }
594        );
595    }
596
597    #[test]
598    fn region_none() {
599        let context = create_test_context();
600        context.set_allow_unregistered_dialects(true);
601        assert_eq!(
602            OperationBuilder::new("foo", Location::unknown(&context),)
603                .build()
604                .unwrap()
605                .region(0),
606            Err(Error::PositionOutOfBounds {
607                name: "region",
608                value: "\"foo\"() : () -> ()\n".into(),
609                index: 0
610            })
611        );
612    }
613
614    #[test]
615    fn operands() {
616        let context = create_test_context();
617        context.set_allow_unregistered_dialects(true);
618
619        let location = Location::unknown(&context);
620        let r#type = Type::index(&context);
621        let block = Block::new(&[(r#type, location)]);
622        let argument: Value = block.argument(0).unwrap().into();
623
624        let operands = vec![argument, argument, argument];
625        let operation = OperationBuilder::new("foo", Location::unknown(&context))
626            .add_operands(&operands)
627            .build()
628            .unwrap();
629
630        assert_eq!(
631            operation.operands().skip(1).collect::<Vec<_>>(),
632            vec![argument, argument]
633        );
634    }
635
636    #[test]
637    fn regions() {
638        let context = create_test_context();
639        context.set_allow_unregistered_dialects(true);
640
641        let operation = OperationBuilder::new("foo", Location::unknown(&context))
642            .add_regions([Region::new()])
643            .build()
644            .unwrap();
645
646        assert_eq!(
647            operation.regions().collect::<Vec<_>>(),
648            vec![operation.region(0).unwrap()]
649        );
650    }
651
652    #[test]
653    fn location() {
654        let context = create_test_context();
655        context.set_allow_unregistered_dialects(true);
656        let location = Location::new(&context, "test", 1, 1);
657
658        let operation = OperationBuilder::new("foo", location)
659            .add_regions([Region::new()])
660            .build()
661            .unwrap();
662
663        assert_eq!(operation.location(), location);
664    }
665
666    #[test]
667    fn attribute() {
668        let context = create_test_context();
669        context.set_allow_unregistered_dialects(true);
670
671        let mut operation = OperationBuilder::new("foo", Location::unknown(&context))
672            .add_attributes(&[(
673                Identifier::new(&context, "foo"),
674                StringAttribute::new(&context, "bar").into(),
675            )])
676            .build()
677            .unwrap();
678        assert!(operation.has_attribute("foo"));
679        assert_eq!(
680            operation.attribute("foo").map(|a| a.to_string()),
681            Ok("\"bar\"".into())
682        );
683        assert!(operation.remove_attribute("foo").is_ok());
684        assert!(operation.remove_attribute("foo").is_err());
685        operation.set_attribute("foo", StringAttribute::new(&context, "foo").into());
686        assert_eq!(
687            operation.attribute("foo").map(|a| a.to_string()),
688            Ok("\"foo\"".into())
689        );
690        assert_eq!(
691            operation.attributes().next(),
692            Some((
693                Identifier::new(&context, "foo"),
694                StringAttribute::new(&context, "foo").into()
695            ))
696        )
697    }
698
699    #[test]
700    fn clone() {
701        let context = create_test_context();
702        context.set_allow_unregistered_dialects(true);
703        let operation = OperationBuilder::new("foo", Location::unknown(&context))
704            .build()
705            .unwrap();
706
707        let _ = operation.clone();
708    }
709
710    #[test]
711    fn display() {
712        let context = create_test_context();
713        context.set_allow_unregistered_dialects(true);
714
715        assert_eq!(
716            OperationBuilder::new("foo", Location::unknown(&context),)
717                .build()
718                .unwrap()
719                .to_string(),
720            "\"foo\"() : () -> ()\n"
721        );
722    }
723
724    #[test]
725    fn debug() {
726        let context = create_test_context();
727        context.set_allow_unregistered_dialects(true);
728
729        assert_eq!(
730            format!(
731                "{:?}",
732                OperationBuilder::new("foo", Location::unknown(&context))
733                    .build()
734                    .unwrap()
735            ),
736            "Operation(\n\"foo\"() : () -> ()\n)"
737        );
738    }
739
740    #[test]
741    fn to_string_with_flags() {
742        let context = create_test_context();
743        context.set_allow_unregistered_dialects(true);
744
745        assert_eq!(
746            OperationBuilder::new("foo", Location::unknown(&context))
747                .build()
748                .unwrap()
749                .to_string_with_flags(
750                    OperationPrintingFlags::new()
751                        .elide_large_elements_attributes(100)
752                        .enable_debug_info(true, true)
753                        .print_generic_operation_form()
754                        .use_local_scope()
755                ),
756            Ok("\"foo\"() : () -> () [unknown]".into())
757        );
758    }
759
760    #[test]
761    fn remove_from_parent() {
762        let context = create_test_context();
763        context.set_allow_unregistered_dialects(true);
764
765        let location = Location::unknown(&context);
766        let mut block = Block::new(&[]);
767
768        let first_operation = block.append_operation(
769            OperationBuilder::new("foo", location)
770                .add_results(&[Type::index(&context)])
771                .build()
772                .unwrap(),
773        );
774        block.append_operation(
775            OperationBuilder::new("bar", location)
776                .add_operands(&[first_operation.result(0).unwrap().into()])
777                .build()
778                .unwrap(),
779        );
780        block.first_operation_mut().unwrap().remove_from_parent();
781
782        assert_eq!(block.first_operation().unwrap().next_in_block(), None);
783        assert_eq!(
784            block.first_operation().unwrap().to_string(),
785            "\"bar\"(<<UNKNOWN SSA VALUE>>) : (index) -> ()"
786        );
787    }
788
789    #[test]
790    fn parent_operation() {
791        let context = create_test_context();
792        context.set_allow_unregistered_dialects(true);
793
794        let location = Location::unknown(&context);
795        let block = Block::new(&[]);
796
797        let operation = block.append_operation(
798            OperationBuilder::new("foo", location)
799                .add_results(&[Type::index(&context)])
800                .add_regions([{
801                    let region = Region::new();
802
803                    let block = Block::new(&[]);
804                    block.append_operation(OperationBuilder::new("bar", location).build().unwrap());
805
806                    region.append_block(block);
807                    region
808                }])
809                .build()
810                .unwrap(),
811        );
812
813        assert_eq!(operation.parent_operation(), None);
814        assert_eq!(
815            &operation
816                .region(0)
817                .unwrap()
818                .first_block()
819                .unwrap()
820                .first_operation()
821                .unwrap()
822                .parent_operation()
823                .unwrap(),
824            &operation
825        );
826    }
827}