sway_ir/
metadata.rs

1use sway_types::SourceId;
2
3/// Associated metadata attached mostly to values.
4///
5/// Each value (instruction, function argument or constant) has associated metadata which helps
6/// describe properties which aren't required for code generation, but help with other
7/// introspective tools (e.g., the debugger) or compiler error messages.
8///
9/// The metadata themselves are opaque to `sway-ir` and are represented with simple value types;
10/// integers, strings, symbols (tags) and lists.
11use crate::context::Context;
12
13#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
14pub struct MetadataIndex(pub slotmap::DefaultKey);
15
16#[derive(Clone, Debug, Eq, PartialEq, Hash)]
17pub enum Metadatum {
18    Integer(u64),
19    Index(MetadataIndex),
20    String(String),
21    SourceId(SourceId),
22    Struct(String, Vec<Metadatum>),
23    List(Vec<MetadataIndex>),
24}
25
26/// Combine two metadata indices into one.
27///
28/// When multiple indices are attached to an IR value or function they must go in a list.  It is
29/// rare for `MetadataIndex` to exist outside of an `Option` though, so we may want to combine two
30/// optional indices when we might end up with only one or the other, or maybe even None.
31///
32/// This function conveniently has all the logic to return the simplest combination of two
33/// `Option<MetadataIndex>`s.
34pub fn combine(
35    context: &mut Context,
36    md_idx_a: &Option<MetadataIndex>,
37    md_idx_b: &Option<MetadataIndex>,
38) -> Option<MetadataIndex> {
39    match (md_idx_a, md_idx_b) {
40        (None, None) => None,
41        (Some(_), None) => *md_idx_a,
42        (None, Some(_)) => *md_idx_b,
43        (Some(idx_a), Some(idx_b)) => {
44            // Rather than potentially making lists of lists, if either are already list we can
45            // merge them together.
46            let mut new_list = Vec::new();
47            if let Metadatum::List(lst_a) = &context.metadata[idx_a.0] {
48                new_list.append(&mut lst_a.clone());
49            } else {
50                new_list.push(*idx_a);
51            }
52            if let Metadatum::List(lst_b) = &context.metadata[idx_b.0] {
53                new_list.append(&mut lst_b.clone());
54            } else {
55                new_list.push(*idx_b);
56            }
57            Some(MetadataIndex(
58                context.metadata.insert(Metadatum::List(new_list)),
59            ))
60        }
61    }
62}
63
64impl MetadataIndex {
65    pub fn new_integer(context: &mut Context, int: u64) -> Self {
66        MetadataIndex(context.metadata.insert(Metadatum::Integer(int)))
67    }
68
69    pub fn new_index(context: &mut Context, idx: MetadataIndex) -> Self {
70        MetadataIndex(context.metadata.insert(Metadatum::Index(idx)))
71    }
72
73    pub fn new_source_id(context: &mut Context, id: SourceId) -> Self {
74        MetadataIndex(context.metadata.insert(Metadatum::SourceId(id)))
75    }
76
77    pub fn new_string<S: Into<String>>(context: &mut Context, s: S) -> Self {
78        MetadataIndex(context.metadata.insert(Metadatum::String(s.into())))
79    }
80
81    pub fn new_struct<S: Into<String>>(
82        context: &mut Context,
83        tag: S,
84        fields: Vec<Metadatum>,
85    ) -> Self {
86        MetadataIndex(
87            context
88                .metadata
89                .insert(Metadatum::Struct(tag.into(), fields)),
90        )
91    }
92
93    pub fn new_list(context: &mut Context, els: Vec<MetadataIndex>) -> Self {
94        MetadataIndex(context.metadata.insert(Metadatum::List(els)))
95    }
96
97    pub fn get_content<'a>(&self, context: &'a Context) -> &'a Metadatum {
98        &context.metadata[self.0]
99    }
100}
101
102impl Metadatum {
103    pub fn unwrap_integer(&self) -> Option<u64> {
104        if let Metadatum::Integer(n) = self {
105            Some(*n)
106        } else {
107            None
108        }
109    }
110
111    pub fn unwrap_index(&self) -> Option<MetadataIndex> {
112        if let Metadatum::Index(idx) = self {
113            Some(*idx)
114        } else {
115            None
116        }
117    }
118
119    pub fn unwrap_string(&self) -> Option<&str> {
120        if let Metadatum::String(s) = self {
121            Some(s)
122        } else {
123            None
124        }
125    }
126
127    pub fn unwrap_source_id(&self) -> Option<&SourceId> {
128        if let Metadatum::SourceId(id) = self {
129            Some(id)
130        } else {
131            None
132        }
133    }
134
135    pub fn unwrap_struct<'a>(&'a self, tag: &str, num_fields: usize) -> Option<&'a [Metadatum]> {
136        match self {
137            Metadatum::Struct(t, fs) if t == tag && fs.len() == num_fields => Some(fs),
138            _otherwise => None,
139        }
140    }
141
142    pub fn unwrap_list(&self) -> Option<&[MetadataIndex]> {
143        if let Metadatum::List(els) = self {
144            Some(els)
145        } else {
146            None
147        }
148    }
149}