sway_ir/
metadata.rs

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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
use sway_types::SourceId;

/// 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.
use crate::context::Context;

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

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum Metadatum {
    Integer(u64),
    Index(MetadataIndex),
    String(String),
    SourceId(SourceId),
    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 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 MetadataIndex {
    pub fn new_integer(context: &mut Context, int: u64) -> Self {
        MetadataIndex(context.metadata.insert(Metadatum::Integer(int)))
    }

    pub fn new_index(context: &mut Context, idx: MetadataIndex) -> Self {
        MetadataIndex(context.metadata.insert(Metadatum::Index(idx)))
    }

    pub fn new_source_id(context: &mut Context, id: SourceId) -> Self {
        MetadataIndex(context.metadata.insert(Metadatum::SourceId(id)))
    }

    pub fn new_string<S: Into<String>>(context: &mut Context, s: S) -> Self {
        MetadataIndex(context.metadata.insert(Metadatum::String(s.into())))
    }

    pub fn new_struct<S: Into<String>>(
        context: &mut Context,
        tag: S,
        fields: Vec<Metadatum>,
    ) -> Self {
        MetadataIndex(
            context
                .metadata
                .insert(Metadatum::Struct(tag.into(), fields)),
        )
    }

    pub fn new_list(context: &mut Context, els: Vec<MetadataIndex>) -> Self {
        MetadataIndex(context.metadata.insert(Metadatum::List(els)))
    }

    pub fn get_content<'a>(&self, context: &'a Context) -> &'a Metadatum {
        &context.metadata[self.0]
    }
}

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_source_id(&self) -> Option<&SourceId> {
        if let Metadatum::SourceId(id) = self {
            Some(id)
        } 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,
        }
    }

    pub fn unwrap_list(&self) -> Option<&[MetadataIndex]> {
        if let Metadatum::List(els) = self {
            Some(els)
        } else {
            None
        }
    }
}