Skip to main content

melior_macro/dialect/operation/
attribute.rs

1use crate::dialect::{
2    error::Error,
3    operation::operation_field::OperationField,
4    utility::{generate_result_type, sanitize_snake_case_identifier},
5};
6use proc_macro2::{Span, TokenStream};
7use quote::quote;
8use std::collections::HashMap;
9use std::sync::LazyLock;
10use syn::{parse_quote, Ident, Type};
11use tblgen::{error::TableGenError, Record};
12
13macro_rules! prefixed_string {
14    ($prefix:literal, $name:ident) => {
15        concat!($prefix, stringify!($name))
16    };
17}
18
19macro_rules! mlir_attribute {
20    ($name:ident) => {
21        prefixed_string!("::mlir::", $name)
22    };
23}
24
25macro_rules! melior_attribute {
26    ($name:ident) => {
27        prefixed_string!("::melior::ir::attribute::", $name)
28    };
29}
30
31static ATTRIBUTE_TYPES: LazyLock<HashMap<&'static str, &'static str>> = LazyLock::new(|| {
32    let mut map = HashMap::new();
33
34    macro_rules! initialize_attributes {
35        ($($mlir:ident => $melior:ident),* $(,)*) => {
36            $(
37                map.insert(
38                    mlir_attribute!($mlir),
39                    melior_attribute!($melior),
40                );
41            )*
42        };
43    }
44
45    initialize_attributes!(
46        ArrayAttr => ArrayAttribute,
47        Attribute => Attribute,
48        DenseElementsAttr => DenseElementsAttribute,
49        DenseI32ArrayAttr => DenseI32ArrayAttribute,
50        FlatSymbolRefAttr => FlatSymbolRefAttribute,
51        FloatAttr => FloatAttribute,
52        IntegerAttr => IntegerAttribute,
53        StringAttr => StringAttribute,
54        TypeAttr => TypeAttribute,
55    );
56
57    map
58});
59
60#[derive(Debug)]
61pub struct Attribute<'a> {
62    name: &'a str,
63    singular_identifier: Ident,
64    storage_type_string: String,
65    set_identifier: Ident,
66    remove_identifier: Ident,
67    storage_type: Type,
68    optional: bool,
69    default: bool,
70}
71
72impl<'a> Attribute<'a> {
73    pub fn new(name: &'a str, record: Record<'a>) -> Result<Self, Error> {
74        let storage_type_string = record.string_value("storageType")?;
75
76        Ok(Self {
77            name,
78            singular_identifier: sanitize_snake_case_identifier(name)?,
79            set_identifier: sanitize_snake_case_identifier(&format!("set_{name}"))?,
80            remove_identifier: sanitize_snake_case_identifier(&format!("remove_{name}"))?,
81            storage_type: syn::parse_str(
82                ATTRIBUTE_TYPES
83                    .get(storage_type_string.trim())
84                    .copied()
85                    .unwrap_or(melior_attribute!(Attribute)),
86            )?,
87            storage_type_string,
88            optional: record.bit_value("isOptional")?,
89            default: match record.string_value("defaultValue") {
90                Ok(value) => !value.is_empty(),
91                Err(error) => {
92                    // `defaultValue` can be uninitialized.
93                    if !matches!(error.error(), TableGenError::InitConversion { .. }) {
94                        return Err(error.into());
95                    }
96
97                    false
98                }
99            },
100        })
101    }
102
103    pub const fn set_identifier(&self) -> &Ident {
104        &self.set_identifier
105    }
106
107    pub const fn remove_identifier(&self) -> &Ident {
108        &self.remove_identifier
109    }
110
111    pub fn is_unit(&self) -> bool {
112        self.storage_type_string == mlir_attribute!(UnitAttr)
113    }
114}
115
116impl OperationField for Attribute<'_> {
117    fn name(&self) -> &str {
118        self.name
119    }
120
121    fn singular_identifier(&self) -> &Ident {
122        &self.singular_identifier
123    }
124
125    fn plural_kind_identifier(&self) -> Ident {
126        Ident::new("attributes", Span::call_site())
127    }
128
129    fn parameter_type(&self) -> Type {
130        if self.is_unit() {
131            parse_quote!(bool)
132        } else {
133            let r#type = &self.storage_type;
134            parse_quote!(#r#type<'c>)
135        }
136    }
137
138    fn return_type(&self) -> Type {
139        if self.is_unit() {
140            parse_quote!(bool)
141        } else {
142            generate_result_type(self.parameter_type())
143        }
144    }
145
146    fn is_optional(&self) -> bool {
147        self.optional || self.default
148    }
149
150    fn add_arguments(&self, name: &Ident) -> TokenStream {
151        let name_string = &self.name;
152
153        quote! {
154            &[(
155                ::melior::ir::Identifier::new(self.context, #name_string),
156                #name.into(),
157            )]
158        }
159    }
160}