Skip to main content

pliron/
attribute.rs

1//! Attributes are non-SSA data stored in [Operation](crate::operation::Operation)s.
2//!
3//! See [MLIR Attributes](https://mlir.llvm.org/docs/LangRef/#attributes).
4//! Unlike in MLIR, we do not unique attributes, and hence they are mutable.
5//! These are similar in concept to [Properties](https://discourse.llvm.org/t/rfc-introducing-mlir-operation-properties/67846).
6//! Attribute objects are boxed and not wrapped with [Ptr](crate::context::Ptr).
7//! They are heavy (i.e., not just a pointer, handle or reference),
8//! making clones potentially expensive.
9//!
10//! The [def_attribute](pliron::derive::def_attribute) proc macro from the
11//! pliron-derive create can be used to implement [Attribute] for a rust type.
12//!
13//! Common semantics, API and behaviour of [Attribute]s are
14//! abstracted into interfaces. Interfaces in pliron capture MLIR
15//! functionality of both [Traits](https://mlir.llvm.org/docs/Traits/)
16//! and [Interfaces](https://mlir.llvm.org/docs/Interfaces/).
17//! Interfaces must all implement an associated function named `verify` with
18//! the type [AttrInterfaceVerifier].
19//!
20//! Interfaces are rust Trait definitions annotated with the attribute macro
21//! [attr_interface](pliron::derive::attr_interface). The attribute ensures that any
22//! verifiers of super-interfaces are run prior to the verifier of this interface.
23//! Note: Super-interface verifiers *may* run multiple times for the same attribute.
24//!
25//! [Attribute]s that implement an interface must annotate the implementation with
26//! [attr_interface_impl](pliron::derive::attr_interface_impl) macro to ensure that
27//! the interface verifier is automatically called during verification
28//! and that a `&dyn Attribute` object can be [cast](attr_cast) into an interface object,
29//! (or that it can be checked if the interface is [implemented](attr_impls))
30//! with ease.
31//!
32//! Use [verify_attr] to verify an [Attribute] object.
33//! This function verifies all interfaces implemented by the attribute, and then the attribute itself.
34//! The attribute's verifier must explicitly invoke verifiers on any sub-objects it contains.
35//!
36//! [AttrObj]s can be downcasted to their concrete types using
37//! [downcast_rs](https://docs.rs/downcast-rs/latest/downcast_rs/#example-without-generics).
38
39use std::{
40    fmt::{Debug, Display},
41    hash::Hash,
42    ops::Deref,
43    sync::LazyLock,
44};
45
46use combine::{Parser, parser, token};
47use downcast_rs::{Downcast, impl_downcast};
48use dyn_clone::DynClone;
49use rustc_hash::FxHashMap;
50
51use crate::{
52    builtin::attr_interfaces::OutlinedAttr,
53    common_traits::Verify,
54    context::{Context, collect_deduped_interface_verifiers},
55    dialect::{Dialect, DialectName},
56    identifier::Identifier,
57    impl_printable_for_display, input_err,
58    irfmt::{
59        parsers::{attr_parser, delimited_list_parser, spaced},
60        printers::iter_with_sep,
61    },
62    location::Located,
63    parsable::{Parsable, ParseResult, StateStream},
64    printable::{self, Printable},
65    result::Result,
66};
67
68/// Convenience type to easily print and parse key-value pairs in an [AttributeDict].
69#[derive(Clone)]
70struct AttributeDictKeyVal<'a> {
71    key: &'a Identifier,
72    val: &'a AttrObj,
73}
74
75impl<'a> Printable for AttributeDictKeyVal<'a> {
76    fn fmt(
77        &self,
78        ctx: &Context,
79        _state: &printable::State,
80        f: &mut std::fmt::Formatter<'_>,
81    ) -> std::fmt::Result {
82        write!(f, "{}: {}", self.key, self.val.disp(ctx))
83    }
84}
85
86impl<'b> Parsable for AttributeDictKeyVal<'b> {
87    type Arg = ();
88
89    type Parsed = (Identifier, AttrObj);
90
91    fn parse<'a>(
92        state_stream: &mut StateStream<'a>,
93        _arg: Self::Arg,
94    ) -> ParseResult<'a, Self::Parsed> {
95        (Identifier::parser(()), spaced(token(':')), attr_parser())
96            .map(|(key, _, val)| (key, val))
97            .parse_stream(state_stream)
98            .into_result()
99    }
100}
101
102impl Printable for AttributeDict {
103    fn fmt(
104        &self,
105        ctx: &Context,
106        _state: &printable::State,
107        f: &mut std::fmt::Formatter<'_>,
108    ) -> std::fmt::Result {
109        write!(
110            f,
111            "[{}]",
112            iter_with_sep(
113                self.0
114                    .iter()
115                    .map(|(key, val)| AttributeDictKeyVal { key, val }),
116                printable::ListSeparator::CharSpace(','),
117            )
118            .disp(ctx)
119        )
120    }
121}
122
123impl Parsable for AttributeDict {
124    type Arg = ();
125    type Parsed = Self;
126
127    fn parse<'a>(
128        state_stream: &mut StateStream<'a>,
129        _arg: Self::Arg,
130    ) -> ParseResult<'a, Self::Parsed> {
131        delimited_list_parser('[', ']', ',', AttributeDictKeyVal::parser(()))
132            .map(|key_vals| AttributeDict(key_vals.into_iter().collect()))
133            .parse_stream(state_stream)
134            .into_result()
135    }
136}
137
138/// A dictionary of attributes, mapping keys to attribute objects.
139#[derive(Default, Debug, Clone, PartialEq, Eq)]
140pub struct AttributeDict(pub FxHashMap<Identifier, AttrObj>);
141
142impl AttributeDict {
143    /// Get reference to attribute value that is mapped to key `k`.
144    pub fn get<T: Attribute>(&self, k: &Identifier) -> Option<&T> {
145        self.0.get(k).and_then(|ao| ao.downcast_ref::<T>())
146    }
147
148    /// Get mutable reference to attribute value that is mapped to key `k`.
149    pub fn get_mut<T: Attribute>(&mut self, k: &Identifier) -> Option<&mut T> {
150        self.0.get_mut(k).and_then(|ao| ao.downcast_mut::<T>())
151    }
152
153    /// Reference to the attribute value (that is mapped to key `k`) as an interface reference.
154    pub fn get_as<T: ?Sized + AttrInterfaceMarker + 'static>(&self, k: &Identifier) -> Option<&T> {
155        self.0.get(k).and_then(|ao| attr_cast::<T>(&**ao))
156    }
157
158    /// Set the attribute value for key `k`.
159    pub fn set<T: Attribute>(&mut self, k: Identifier, v: T) {
160        self.0.insert(k, Box::new(v));
161    }
162
163    /// Clone, but skip [Outlined](OutlinedAttr) attributes.
164    pub fn clone_skip_outlined(&self) -> Self {
165        self.0
166            .iter()
167            .filter_map(|(k, v)| {
168                if attr_impls::<dyn OutlinedAttr>(&**v) {
169                    None
170                } else {
171                    Some((k.clone(), dyn_clone::clone_box(&**v)))
172                }
173            })
174            .collect::<FxHashMap<Identifier, AttrObj>>()
175            .into()
176    }
177}
178
179impl From<FxHashMap<Identifier, AttrObj>> for AttributeDict {
180    fn from(value: FxHashMap<Identifier, AttrObj>) -> Self {
181        AttributeDict(value)
182    }
183}
184
185/// Basic functionality that every attribute in the IR must implement.
186///
187/// See [module](crate::attribute) documentation for more information.
188pub trait Attribute: Printable + Verify + Downcast + Sync + Send + DynClone + Debug {
189    /// Is self equal to an other Attribute?
190    fn eq_attr(&self, other: &dyn Attribute) -> bool;
191
192    /// Get an [Attribute]'s static name. This is *not* per instantnce.
193    /// It is mostly useful for printing and parsing the attribute.
194    fn get_attr_id(&self) -> AttrId;
195
196    /// Same as [get_attr_id](Self::get_attr_id), but without the self reference.
197    fn get_attr_id_static() -> AttrId
198    where
199        Self: Sized;
200
201    #[doc(hidden)]
202    /// Verify all interfaces implemented by this attribute.
203    fn verify_interfaces(&self, ctx: &Context) -> Result<()>;
204
205    /// Register this attribute's [AttrId] in the dialect it belongs to.
206    fn register<A: Attribute>(ctx: &mut Context)
207    where
208        Self: Sized + Parsable<Arg = (), Parsed = A>,
209    {
210        let attr_parser: AttrParserFn = Box::new(|&()| {
211            combine::parser(move |parsable_state: &mut StateStream<'_>| {
212                Self::parse(parsable_state, ())
213                    .map(|(attr, r)| -> (AttrObj, _) { (Box::new(attr), r) })
214            })
215            .boxed()
216        });
217        let attrid = Self::get_attr_id_static();
218        Dialect::register(ctx, &attrid.dialect).add_attr(attrid.clone(), attr_parser);
219    }
220}
221impl_downcast!(Attribute);
222dyn_clone::clone_trait_object!(Attribute);
223
224/// [Attribute] objects are boxed and stored in the IR.
225pub type AttrObj = Box<dyn Attribute>;
226
227/// A storable function pointer to parse a specific [Attribute].
228/// The [Attribute]'s [Dialect] maps an [AttrId] to such a parser.
229pub(crate) type AttrParserFn = Box<
230    for<'a> fn(
231        &'a (),
232    ) -> Box<dyn Parser<StateStream<'a>, Output = AttrObj, PartialState = ()> + 'a>,
233>;
234
235impl PartialEq for AttrObj {
236    fn eq(&self, other: &Self) -> bool {
237        (**self).eq_attr(&**other)
238    }
239}
240
241impl<T: Attribute> From<T> for AttrObj {
242    fn from(value: T) -> Self {
243        Box::new(value)
244    }
245}
246
247impl Eq for AttrObj {}
248
249impl Printable for AttrObj {
250    fn fmt(
251        &self,
252        ctx: &Context,
253        state: &printable::State,
254        f: &mut core::fmt::Formatter<'_>,
255    ) -> core::fmt::Result {
256        write!(f, "{} ", self.get_attr_id())?;
257        Printable::fmt(self.deref(), ctx, state, f)
258    }
259}
260
261impl Parsable for AttrObj {
262    type Arg = ();
263    type Parsed = AttrObj;
264
265    fn parse<'a>(
266        state_stream: &mut StateStream<'a>,
267        _arg: Self::Arg,
268    ) -> ParseResult<'a, Self::Parsed> {
269        let loc = state_stream.loc();
270        let attr_id_parser = spaced(AttrId::parser(()));
271
272        let mut attr_parser = attr_id_parser.then(move |attr_id: AttrId| {
273            let loc = loc.clone();
274            combine::parser(move |parsable_state: &mut StateStream<'a>| {
275                let state = &parsable_state.state;
276                let dialect = state
277                    .ctx
278                    .dialects
279                    .get(&attr_id.dialect)
280                    .expect("Dialect name parsed but dialect isn't registered");
281                let Some(attr_parser) = dialect.attributes.get(&attr_id) else {
282                    input_err!(
283                        loc.clone(),
284                        "Unregistered attribute {}",
285                        attr_id.disp(state.ctx)
286                    )?
287                };
288                attr_parser(&()).parse_stream(parsable_state).into_result()
289            })
290        });
291
292        attr_parser.parse_stream(state_stream).into_result()
293    }
294}
295
296/// Verify an [Attribute] object.
297/// 1. Verify all interfaces implemented by this attribute.
298/// 2. Verify the attribute itself.
299pub fn verify_attr(attr: &dyn Attribute, ctx: &Context) -> Result<()> {
300    // Verify all interfaces implemented by this attribute.
301    attr.verify_interfaces(ctx)?;
302
303    // Verify the attribute itself.
304    Verify::verify(attr, ctx)
305}
306
307impl Verify for AttrObj {
308    fn verify(&self, ctx: &Context) -> Result<()> {
309        verify_attr(self.as_ref(), ctx)
310    }
311}
312
313/// Marker trait for attribute interface trait objects.
314///
315/// This is auto-implemented by the `#[attr_interface]` macro for `dyn Interface`
316/// objects and is used to restrict [attr_cast] and [attr_impls] to interface casts.
317#[diagnostic::on_unimplemented(
318    message = "`{Self}` not an attribute interface.",
319    label = "If `{Self}` is a trait, annotate it with #[attr_interface] to be able to cast to it from a `&dyn Attribute`",
320    note = "If you want to cast to a concrete `Attribute`, use `downcast_ref` instead."
321)]
322pub trait AttrInterfaceMarker {}
323
324/// Cast reference to an [Attribute] object to an interface reference.
325///
326/// Right usage: cast to an interface trait object.
327/// ```
328/// use pliron::attribute::{Attribute, attr_cast};
329/// use pliron::builtin::attr_interfaces::TypedAttrInterface;
330///
331/// fn right_cast(attr: &dyn Attribute) {
332///     let _ = attr_cast::<dyn TypedAttrInterface>(attr);
333/// }
334/// ```
335///
336/// Casting to concrete [Attribute] types are intentionally rejected.
337/// ```compile_fail
338/// use pliron::attribute::{Attribute, attr_cast};
339/// use pliron::builtin::attributes::IntegerAttr;
340///
341/// fn wrong_cast(attr: &dyn Attribute) {
342///     let _ = attr_cast::<IntegerAttr>(attr);
343/// }
344/// ```
345/// Use [downcast_rs](https://docs.rs/downcast-rs/latest/downcast_rs/#example-without-generics)
346/// to cast to concrete [Attribute] types.
347pub fn attr_cast<T: ?Sized + AttrInterfaceMarker + 'static>(attr: &dyn Attribute) -> Option<&T> {
348    crate::utils::trait_cast::any_to_trait::<T>(attr.as_any())
349}
350
351/// Does this [Attribute] object implement interface `T`?
352///
353/// Right usage: query using an interface trait object.
354/// ```
355/// use pliron::attribute::{Attribute, attr_impls};
356/// use pliron::builtin::attr_interfaces::TypedAttrInterface;
357///
358/// fn right_query(attr: &dyn Attribute) {
359///     let _ = attr_impls::<dyn TypedAttrInterface>(attr);
360/// }
361/// ```
362///
363/// Querying with a concrete [Attribute] type is intentionally rejected.
364/// ```compile_fail
365/// use pliron::attribute::{Attribute, attr_impls};
366/// use pliron::builtin::attributes::IntegerAttr;
367///
368/// fn wrong_query(attr: &dyn Attribute) {
369///     let _ = attr_impls::<IntegerAttr>(attr);
370/// }
371/// ```
372pub fn attr_impls<T: ?Sized + AttrInterfaceMarker + 'static>(attr: &dyn Attribute) -> bool {
373    attr_cast::<T>(attr).is_some()
374}
375
376#[derive(Clone, Hash, PartialEq, Eq)]
377/// An [Attribute]'s name (not including it's dialect).
378pub struct AttrName(String);
379
380impl AttrName {
381    /// Create a new AttrName.
382    pub fn new(name: &str) -> AttrName {
383        AttrName(name.to_string())
384    }
385}
386
387impl_printable_for_display!(AttrName);
388
389impl Display for AttrName {
390    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
391        write!(f, "{}", self.0)
392    }
393}
394
395impl Parsable for AttrName {
396    type Arg = ();
397    type Parsed = AttrName;
398
399    fn parse<'a>(
400        state_stream: &mut crate::parsable::StateStream<'a>,
401        _arg: Self::Arg,
402    ) -> ParseResult<'a, Self::Parsed>
403    where
404        Self: Sized,
405    {
406        Identifier::parser(())
407            .map(|name| AttrName::new(&name))
408            .parse_stream(state_stream)
409            .into()
410    }
411}
412
413impl Deref for AttrName {
414    type Target = String;
415
416    fn deref(&self) -> &Self::Target {
417        &self.0
418    }
419}
420/// A combination of a Attr's name and its dialect.
421#[derive(Clone, Hash, PartialEq, Eq)]
422pub struct AttrId {
423    pub dialect: DialectName,
424    pub name: AttrName,
425}
426
427impl_printable_for_display!(AttrId);
428
429impl Display for AttrId {
430    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
431        write!(f, "{}.{}", self.dialect, self.name)
432    }
433}
434
435impl Parsable for AttrId {
436    type Arg = ();
437    type Parsed = AttrId;
438
439    // Parses (but does not validate) a TypeId.
440    fn parse<'a>(
441        state_stream: &mut StateStream<'a>,
442        _arg: Self::Arg,
443    ) -> ParseResult<'a, Self::Parsed>
444    where
445        Self: Sized,
446    {
447        let mut parser = DialectName::parser(())
448            .skip(parser::char::char('.'))
449            .and(AttrName::parser(()))
450            .map(|(dialect, name)| AttrId { dialect, name });
451        parser.parse_stream(state_stream).into()
452    }
453}
454
455/// Every attribute interface must have a function named `verify` with this type.
456pub type AttrInterfaceVerifier = fn(&dyn Attribute, &Context) -> Result<()>;
457/// Function returns the list of super verifiers, followed by a self verifier, for an interface.
458pub type AttrInterfaceAllVerifiers = fn() -> Vec<AttrInterfaceVerifier>;
459
460#[doc(hidden)]
461/// An [Attribute] paired with an interface it implements
462/// (specifically the verifiers (including super verifiers) for that interface).
463type AttrInterfaceVerifierInfo = (std::any::TypeId, AttrInterfaceAllVerifiers);
464
465#[cfg(not(target_family = "wasm"))]
466pub mod statics {
467    use super::*;
468
469    #[::pliron::linkme::distributed_slice]
470    pub static ATTR_INTERFACE_VERIFIERS: [LazyLock<AttrInterfaceVerifierInfo>] = [..];
471
472    pub fn get_attr_interface_verifiers()
473    -> impl Iterator<Item = &'static LazyLock<AttrInterfaceVerifierInfo>> {
474        ATTR_INTERFACE_VERIFIERS.iter()
475    }
476}
477
478#[cfg(target_family = "wasm")]
479pub mod statics {
480    use super::*;
481    use crate::utils::inventory::LazyLockWrapper;
482
483    ::pliron::inventory::collect!(LazyLockWrapper<AttrInterfaceVerifierInfo>);
484
485    pub fn get_attr_interface_verifiers()
486    -> impl Iterator<Item = &'static LazyLock<AttrInterfaceVerifierInfo>> {
487        ::pliron::inventory::iter::<LazyLockWrapper<AttrInterfaceVerifierInfo>>().map(|llw| llw.0)
488    }
489}
490
491pub use statics::*;
492
493#[doc(hidden)]
494/// A map from every [Attribute] to its ordered (as per interface deps) list of interface verifiers.
495/// An interface's super-interfaces are to be verified before it itself is.
496pub static ATTR_INTERFACE_VERIFIERS_MAP: LazyLock<
497    FxHashMap<std::any::TypeId, Vec<AttrInterfaceVerifier>>,
498> = LazyLock::new(|| collect_deduped_interface_verifiers(get_attr_interface_verifiers()));