Skip to main content

melior/ir/
attribute.rs

1//! Attributes.
2
3#[macro_use]
4mod r#macro;
5mod array;
6mod attribute_like;
7mod bool;
8mod dense_elements;
9mod dense_i32_array;
10mod dense_i64_array;
11mod flat_symbol_ref;
12mod float;
13mod integer;
14mod string;
15mod r#type;
16
17pub use self::{
18    array::ArrayAttribute, attribute_like::AttributeLike, bool::BoolAttribute,
19    dense_elements::DenseElementsAttribute, dense_i32_array::DenseI32ArrayAttribute,
20    dense_i64_array::DenseI64ArrayAttribute, flat_symbol_ref::FlatSymbolRefAttribute,
21    float::FloatAttribute, integer::IntegerAttribute, r#type::TypeAttribute,
22    string::StringAttribute,
23};
24use crate::{context::Context, string_ref::StringRef, utility::print_callback};
25use mlir_sys::{
26    mlirAttributeEqual, mlirAttributeGetNull, mlirAttributeParseGet, mlirAttributePrint,
27    mlirUnitAttrGet, MlirAttribute,
28};
29use std::{
30    ffi::c_void,
31    fmt::{self, Debug, Display, Formatter},
32    marker::PhantomData,
33};
34
35/// An attribute.
36// Attributes are always values but their internal storage is owned by contexts.
37#[derive(Clone, Copy)]
38pub struct Attribute<'c> {
39    raw: MlirAttribute,
40    _context: PhantomData<&'c Context>,
41}
42
43impl<'c> Attribute<'c> {
44    /// Parses an attribute.
45    pub fn parse(context: &'c Context, source: &str) -> Option<Self> {
46        unsafe {
47            Self::from_option_raw(mlirAttributeParseGet(
48                context.to_raw(),
49                StringRef::new(source).to_raw(),
50            ))
51        }
52    }
53
54    /// Creates a unit attribute.
55    pub fn unit(context: &'c Context) -> Self {
56        unsafe { Self::from_raw(mlirUnitAttrGet(context.to_raw())) }
57    }
58
59    pub(crate) unsafe fn null() -> Self {
60        unsafe { Self::from_raw(mlirAttributeGetNull()) }
61    }
62
63    /// Creates an attribute from a raw object.
64    ///
65    /// # Safety
66    ///
67    /// A raw object must be valid.
68    pub unsafe fn from_raw(raw: MlirAttribute) -> Self {
69        Self {
70            raw,
71            _context: Default::default(),
72        }
73    }
74
75    /// Creates an optional attribute from a raw object.
76    ///
77    /// # Safety
78    ///
79    /// A raw object must be valid.
80    pub unsafe fn from_option_raw(raw: MlirAttribute) -> Option<Self> {
81        if raw.ptr.is_null() {
82            None
83        } else {
84            Some(Self::from_raw(raw))
85        }
86    }
87}
88
89impl<'c> AttributeLike<'c> for Attribute<'c> {
90    fn to_raw(&self) -> MlirAttribute {
91        self.raw
92    }
93}
94
95impl PartialEq for Attribute<'_> {
96    fn eq(&self, other: &Self) -> bool {
97        unsafe { mlirAttributeEqual(self.raw, other.raw) }
98    }
99}
100
101impl Eq for Attribute<'_> {}
102
103impl Display for Attribute<'_> {
104    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
105        let mut data = (formatter, Ok(()));
106
107        unsafe {
108            mlirAttributePrint(
109                self.raw,
110                Some(print_callback),
111                &mut data as *mut _ as *mut c_void,
112            );
113        }
114
115        data.1
116    }
117}
118
119impl Debug for Attribute<'_> {
120    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
121        Display::fmt(self, formatter)
122    }
123}
124
125from_subtypes!(
126    Attribute,
127    ArrayAttribute,
128    BoolAttribute,
129    DenseElementsAttribute,
130    DenseI32ArrayAttribute,
131    DenseI64ArrayAttribute,
132    FlatSymbolRefAttribute,
133    FloatAttribute,
134    IntegerAttribute,
135    StringAttribute,
136    TypeAttribute,
137);
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142    use crate::{
143        ir::{Type, TypeLike},
144        test::create_test_context,
145    };
146
147    #[test]
148    fn parse() {
149        let context = create_test_context();
150        for attribute in ["unit", "i32", r#""foo""#] {
151            assert!(Attribute::parse(&context, attribute).is_some());
152        }
153    }
154
155    #[test]
156    fn parse_none() {
157        // Note: this test will print a warning if LLVM was compiled with asserts.
158        // `<mlir_parser_buffer>:1:1: error: expected attribute value
159        // z
160        // ^`
161        assert!(Attribute::parse(&Context::new(), "z").is_none());
162    }
163
164    #[test]
165    fn context() {
166        let context = create_test_context();
167        Attribute::parse(&context, "unit").unwrap().context();
168    }
169
170    #[test]
171    fn r#type() {
172        let context = Context::new();
173
174        assert_eq!(
175            Attribute::parse(&context, "unit").unwrap().r#type(),
176            Type::none(&context)
177        );
178    }
179
180    // TODO Fix this.
181    #[ignore]
182    #[test]
183    fn type_id() {
184        let context = Context::new();
185
186        assert_eq!(
187            Attribute::parse(&context, "42 : index").unwrap().type_id(),
188            Type::index(&context).id()
189        );
190    }
191
192    #[test]
193    fn is_array() {
194        let context = create_test_context();
195        assert!(Attribute::parse(&context, "[]").unwrap().is_array());
196    }
197
198    #[test]
199    fn is_bool() {
200        let context = create_test_context();
201        assert!(Attribute::parse(&context, "false").unwrap().is_bool());
202    }
203
204    #[test]
205    fn is_dense_elements() {
206        let context = create_test_context();
207        assert!(Attribute::parse(&context, "dense<10> : tensor<2xi8>")
208            .unwrap()
209            .is_dense_elements());
210    }
211
212    #[test]
213    fn is_dense_int_elements() {
214        let context = create_test_context();
215        assert!(Attribute::parse(&context, "dense<42> : tensor<42xi8>")
216            .unwrap()
217            .is_dense_int_elements());
218    }
219
220    #[test]
221    fn is_dense_fp_elements() {
222        let context = create_test_context();
223        assert!(Attribute::parse(&context, "dense<42.0> : tensor<42xf32>")
224            .unwrap()
225            .is_dense_fp_elements());
226    }
227
228    #[test]
229    fn is_elements() {
230        let context = create_test_context();
231        assert!(Attribute::parse(
232            &context,
233            "sparse<[[0, 0], [1, 2]], [1, 5]> : tensor<3x4xi32>"
234        )
235        .unwrap()
236        .is_elements());
237    }
238
239    #[test]
240    fn is_integer() {
241        let context = create_test_context();
242        assert!(Attribute::parse(&context, "42").unwrap().is_integer());
243    }
244
245    #[test]
246    fn is_integer_set() {
247        let context = create_test_context();
248        assert!(
249            Attribute::parse(&context, "affine_set<(d0) : (d0 - 2 >= 0)>")
250                .unwrap()
251                .is_integer_set()
252        );
253    }
254
255    // TODO Fix this.
256    #[ignore]
257    #[test]
258    fn is_opaque() {
259        let context = create_test_context();
260        assert!(Attribute::parse(&context, "#foo<\"bar\">")
261            .unwrap()
262            .is_opaque());
263    }
264
265    #[test]
266    fn is_sparse_elements() {
267        let context = create_test_context();
268        assert!(Attribute::parse(
269            &context,
270            "sparse<[[0, 0], [1, 2]], [1, 5]> : tensor<3x4xi32>"
271        )
272        .unwrap()
273        .is_sparse_elements());
274    }
275
276    #[test]
277    fn is_string() {
278        let context = create_test_context();
279        assert!(Attribute::parse(&context, "\"foo\"").unwrap().is_string());
280    }
281
282    #[test]
283    fn is_type() {
284        let context = create_test_context();
285        assert!(Attribute::parse(&context, "index").unwrap().is_type());
286    }
287
288    #[test]
289    fn is_unit() {
290        let context = create_test_context();
291        assert!(Attribute::parse(&context, "unit").unwrap().is_unit());
292    }
293
294    #[test]
295    fn is_symbol() {
296        let context = create_test_context();
297        assert!(Attribute::parse(&context, "@foo").unwrap().is_symbol_ref());
298    }
299
300    #[test]
301    fn equal() {
302        let context = create_test_context();
303        let attribute = Attribute::parse(&context, "unit").unwrap();
304
305        assert_eq!(attribute, attribute);
306    }
307
308    #[test]
309    fn not_equal() {
310        let context = create_test_context();
311
312        assert_ne!(
313            Attribute::parse(&context, "unit").unwrap(),
314            Attribute::parse(&context, "42").unwrap()
315        );
316    }
317
318    #[test]
319    fn display() {
320        let context = create_test_context();
321        assert_eq!(
322            Attribute::parse(&context, "unit").unwrap().to_string(),
323            "unit"
324        );
325    }
326}