1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
///! Associated metadata attached mostly to values.
///!
///! Each value (instruction, function argument or constant) has associated metadata which helps
///! describe properties which aren't required for code generation, but help with other
///! introspective tools (e.g., the debugger) or compiler error messages.
///!
///! The metadata themselves are opaque to `sway-ir` and are represented with simple value types;
///! integers, strings, symbols (tags) and lists.

#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
pub struct MetadataIndex(pub generational_arena::Index);

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum Metadatum {
    Integer(u64),
    Index(MetadataIndex),
    String(String),
    Struct(String, Vec<Metadatum>),
    List(Vec<MetadataIndex>),
}

/// Combine two metadata indices into one.
///
/// When multiple indices are attached to an IR value or function they must go in a list.  It is
/// rare for `MetadataIndex` to exist outside of an `Option` though, so we may want to combine two
/// optional indices when we might end up with only one or the other, or maybe even None.
///
/// This function conveniently has all the logic to return the simplest combination of two
/// `Option<MetadataIndex>`s.
pub fn combine(
    context: &mut crate::context::Context,
    md_idx_a: &Option<MetadataIndex>,
    md_idx_b: &Option<MetadataIndex>,
) -> Option<MetadataIndex> {
    match (md_idx_a, md_idx_b) {
        (None, None) => None,
        (Some(_), None) => *md_idx_a,
        (None, Some(_)) => *md_idx_b,
        (Some(idx_a), Some(idx_b)) => {
            // Rather than potentially making lists of lists, if either are already list we can
            // merge them together.
            let mut new_list = Vec::new();
            if let Metadatum::List(lst_a) = &context.metadata[idx_a.0] {
                new_list.append(&mut lst_a.clone());
            } else {
                new_list.push(*idx_a);
            }
            if let Metadatum::List(lst_b) = &context.metadata[idx_b.0] {
                new_list.append(&mut lst_b.clone());
            } else {
                new_list.push(*idx_b);
            }
            Some(MetadataIndex(
                context.metadata.insert(Metadatum::List(new_list)),
            ))
        }
    }
}

impl Metadatum {
    pub fn unwrap_integer(&self) -> Option<u64> {
        if let Metadatum::Integer(n) = self {
            Some(*n)
        } else {
            None
        }
    }

    pub fn unwrap_index(&self) -> Option<MetadataIndex> {
        if let Metadatum::Index(idx) = self {
            Some(*idx)
        } else {
            None
        }
    }

    pub fn unwrap_string(&self) -> Option<&str> {
        if let Metadatum::String(s) = self {
            Some(s)
        } else {
            None
        }
    }

    pub fn unwrap_struct<'a>(&'a self, tag: &str, num_fields: usize) -> Option<&'a [Metadatum]> {
        match self {
            Metadatum::Struct(t, fs) if t == tag && fs.len() == num_fields => Some(fs),
            _otherwise => None,
        }
    }
}