sway_ir/
constant.rs

1//! [`Constant`] is a typed constant value.
2
3use std::hash::{Hash, Hasher};
4
5use crate::{context::Context, irtype::Type, pretty::DebugWithContext, value::Value, Padding};
6use rustc_hash::FxHasher;
7use sway_types::u256::U256;
8
9/// A wrapper around an [ECS](https://github.com/orlp/slotmap) handle into the
10/// [`Context`].
11#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, DebugWithContext)]
12pub struct Constant(#[in_context(values)] pub slotmap::DefaultKey);
13
14impl Constant {
15    /// Get or create a unique constant with given contents.
16    pub fn unique(context: &mut Context, constant: ConstantContent) -> Constant {
17        let mut hasher = FxHasher::default();
18        constant.hash(&mut hasher);
19        let hash = hasher.finish();
20        // Insert a new entry if it doesn't exist.
21        context.constants_map.entry(hash).or_default();
22        let constants = context.constants_map.get(&hash).unwrap();
23        // If the constant already exists, return it.
24        for c in constants.iter() {
25            if context.constants.get(c.0).unwrap().eq(context, &constant) {
26                return *c;
27            }
28        }
29        let constant = Constant(context.constants.insert(constant));
30        // Re-borrow the constants map (mutably this time) to insert the new constant.
31        let constants = context.constants_map.get_mut(&hash).unwrap();
32        constants.push(constant);
33        constant
34    }
35
36    /// Get the contents of a unique constant
37    pub fn get_content<'a>(&self, context: &'a Context) -> &'a ConstantContent {
38        context
39            .constants
40            .get(self.0)
41            .expect("Constants are global immutable data, they must live through the context")
42    }
43}
44
45/// A [`Type`] and constant value, including [`ConstantValue::Undef`] for uninitialized constants.
46#[derive(Debug, Clone, DebugWithContext, Hash)]
47pub struct ConstantContent {
48    pub ty: Type,
49    pub value: ConstantValue,
50}
51
52pub type B256 = U256;
53
54/// A constant representation of each of the supported [`Type`]s.
55#[derive(Debug, Clone, DebugWithContext, Hash)]
56pub enum ConstantValue {
57    Undef,
58    Unit,
59    Bool(bool),
60    Uint(u64),
61    U256(U256),
62    B256(B256),
63    String(Vec<u8>),
64    Array(Vec<ConstantContent>),
65    Slice(Vec<ConstantContent>),
66    Struct(Vec<ConstantContent>),
67    Reference(Box<ConstantContent>),
68    RawUntypedSlice(Vec<u8>),
69}
70
71/// A [Constant] with its required [Padding].
72/// If the [Padding] is `None` the default [Padding] for the
73/// [Constant] type is expected.
74type ConstantWithPadding<'a> = (&'a ConstantContent, Option<Padding>);
75
76impl ConstantContent {
77    pub fn new_unit(context: &Context) -> Self {
78        ConstantContent {
79            ty: Type::get_unit(context),
80            value: ConstantValue::Unit,
81        }
82    }
83
84    pub fn new_bool(context: &Context, b: bool) -> Self {
85        ConstantContent {
86            ty: Type::get_bool(context),
87            value: ConstantValue::Bool(b),
88        }
89    }
90
91    /// For numbers bigger than u64 see `new_uint256`.
92    pub fn new_uint(context: &mut Context, nbits: u16, n: u64) -> Self {
93        ConstantContent {
94            ty: Type::new_uint(context, nbits),
95            value: match nbits {
96                256 => ConstantValue::U256(n.into()),
97                _ => ConstantValue::Uint(n),
98            },
99        }
100    }
101
102    pub fn new_uint256(context: &mut Context, n: U256) -> Self {
103        ConstantContent {
104            ty: Type::new_uint(context, 256),
105            value: ConstantValue::U256(n),
106        }
107    }
108
109    pub fn new_b256(context: &Context, bytes: [u8; 32]) -> Self {
110        ConstantContent {
111            ty: Type::get_b256(context),
112            value: ConstantValue::B256(B256::from_be_bytes(&bytes)),
113        }
114    }
115
116    pub fn new_string(context: &mut Context, string: Vec<u8>) -> Self {
117        ConstantContent {
118            ty: Type::new_string_array(context, string.len() as u64),
119            value: ConstantValue::String(string),
120        }
121    }
122
123    pub fn new_array(context: &mut Context, elm_ty: Type, elems: Vec<ConstantContent>) -> Self {
124        ConstantContent {
125            ty: Type::new_array(context, elm_ty, elems.len() as u64),
126            value: ConstantValue::Array(elems),
127        }
128    }
129
130    pub fn new_struct(
131        context: &mut Context,
132        field_tys: Vec<Type>,
133        fields: Vec<ConstantContent>,
134    ) -> Self {
135        ConstantContent {
136            ty: Type::new_struct(context, field_tys),
137            value: ConstantValue::Struct(fields),
138        }
139    }
140
141    pub fn get_undef(ty: Type) -> Self {
142        ConstantContent {
143            ty,
144            value: ConstantValue::Undef,
145        }
146    }
147
148    pub fn get_unit(context: &mut Context) -> Value {
149        let new_const_contents = ConstantContent::new_unit(context);
150        let new_const = Constant::unique(context, new_const_contents);
151        Value::new_constant(context, new_const)
152    }
153
154    pub fn get_bool(context: &mut Context, value: bool) -> Value {
155        let new_const_contents = ConstantContent::new_bool(context, value);
156        let new_const = Constant::unique(context, new_const_contents);
157        Value::new_constant(context, new_const)
158    }
159
160    pub fn get_uint(context: &mut Context, nbits: u16, value: u64) -> Value {
161        let new_const_contents = ConstantContent::new_uint(context, nbits, value);
162        let new_const = Constant::unique(context, new_const_contents);
163        Value::new_constant(context, new_const)
164    }
165
166    pub fn get_uint256(context: &mut Context, value: U256) -> Value {
167        let new_const_contents = ConstantContent::new_uint256(context, value);
168        let new_const = Constant::unique(context, new_const_contents);
169        Value::new_constant(context, new_const)
170    }
171
172    pub fn get_b256(context: &mut Context, value: [u8; 32]) -> Value {
173        let new_const_contents = ConstantContent::new_b256(context, value);
174        let new_const = Constant::unique(context, new_const_contents);
175        Value::new_constant(context, new_const)
176    }
177
178    pub fn get_string(context: &mut Context, value: Vec<u8>) -> Value {
179        let new_const_contents = ConstantContent::new_string(context, value);
180        let new_const = Constant::unique(context, new_const_contents);
181        Value::new_constant(context, new_const)
182    }
183
184    /// `value` must be created as an array constant first, using [`Constant::new_array()`].
185    pub fn get_array(context: &mut Context, value: ConstantContent) -> Value {
186        assert!(value.ty.is_array(context));
187        let new_const = Constant::unique(context, value);
188        Value::new_constant(context, new_const)
189    }
190
191    /// `value` must be created as a struct constant first, using [`Constant::new_struct()`].
192    pub fn get_struct(context: &mut Context, value: ConstantContent) -> Value {
193        assert!(value.ty.is_struct(context));
194        let new_const = Constant::unique(context, value);
195        Value::new_constant(context, new_const)
196    }
197
198    /// Returns the tag and the value of an enum constant if `self` is an enum constant,
199    /// otherwise `None`.
200    fn extract_enum_tag_and_value(
201        &self,
202        context: &Context,
203    ) -> Option<(&ConstantContent, &ConstantContent)> {
204        if !self.ty.is_enum(context) {
205            return None;
206        }
207
208        let elems = match &self.value {
209            ConstantValue::Struct(elems) if elems.len() == 2 => elems,
210            _ => return None, // This should never be the case. If we have an enum, it is a struct with exactly two elements.
211        };
212
213        Some((&elems[0], &elems[1]))
214    }
215
216    /// Returns enum tag and value as [Constant]s, together with their [Padding]s,
217    /// if `self` is an enum [Constant], otherwise `None`.
218    pub fn enum_tag_and_value_with_paddings(
219        &self,
220        context: &Context,
221    ) -> Option<(ConstantWithPadding, ConstantWithPadding)> {
222        if !self.ty.is_enum(context) {
223            return None;
224        }
225
226        let tag_and_value_with_paddings = self
227            .elements_of_aggregate_with_padding(context)
228            .expect("Enums are aggregates.");
229
230        debug_assert!(tag_and_value_with_paddings.len() == 2, "In case of enums, `elements_of_aggregate_with_padding` must return exactly two elements, the tag and the value.");
231
232        let tag = tag_and_value_with_paddings[0].clone();
233        let value = tag_and_value_with_paddings[1].clone();
234
235        Some((tag, value))
236    }
237
238    /// Returns elements of an array with the expected padding for each array element
239    /// if `self` is an array [Constant], otherwise `None`.
240    pub fn array_elements_with_padding(
241        &self,
242        context: &Context,
243    ) -> Option<Vec<ConstantWithPadding>> {
244        if !self.ty.is_array(context) {
245            return None;
246        }
247
248        self.elements_of_aggregate_with_padding(context)
249    }
250
251    /// Returns fields of a struct with the expected padding for each field
252    /// if `self` is a struct [Constant], otherwise `None`.
253    pub fn struct_fields_with_padding(
254        &self,
255        context: &Context,
256    ) -> Option<Vec<ConstantWithPadding>> {
257        if !self.ty.is_struct(context) {
258            return None;
259        }
260
261        self.elements_of_aggregate_with_padding(context)
262    }
263
264    /// Returns elements of an aggregate constant with the expected padding for each element
265    /// if `self` is an aggregate (struct, enum, or array), otherwise `None`.
266    /// If the returned [Padding] is `None` the default [Padding] for the type
267    /// is expected.
268    /// If the aggregate constant is an enum, the returned [Vec] has exactly two elements,
269    /// the first being the tag and the second the value of the enum variant.
270    fn elements_of_aggregate_with_padding(
271        &self,
272        context: &Context,
273    ) -> Option<Vec<(&ConstantContent, Option<Padding>)>> {
274        // We need a special handling in case of enums.
275        if let Some((tag, value)) = self.extract_enum_tag_and_value(context) {
276            let tag_with_padding = (tag, None);
277
278            // Enum variants are left padded to the word boundary, and the size
279            // of each variant is the size of the union.
280            // We know we have an enum here, means exactly two fields in the struct
281            // second of which is the union.
282            let target_size = self.ty.get_field_types(context)[1]
283                .size(context)
284                .in_bytes_aligned() as usize;
285
286            let value_with_padding = (value, Some(Padding::Left { target_size }));
287
288            return Some(vec![tag_with_padding, value_with_padding]);
289        }
290
291        match &self.value {
292            // Individual array elements do not have additional padding.
293            ConstantValue::Array(elems) => Some(elems.iter().map(|el| (el, None)).collect()),
294            // Each struct field is right padded to the word boundary.
295            ConstantValue::Struct(elems) => Some(
296                elems
297                    .iter()
298                    .map(|el| {
299                        let target_size = el.ty.size(context).in_bytes_aligned() as usize;
300                        (el, Some(Padding::Right { target_size }))
301                    })
302                    .collect(),
303            ),
304            _ => None,
305        }
306    }
307
308    /// Compare two Constant values. Can't impl PartialOrder because of context.
309    pub fn eq(&self, context: &Context, other: &Self) -> bool {
310        self.ty.eq(context, &other.ty)
311            && match (&self.value, &other.value) {
312                // Two Undefs are *NOT* equal (PartialEq allows this).
313                (ConstantValue::Undef, _) | (_, ConstantValue::Undef) => false,
314                (ConstantValue::Unit, ConstantValue::Unit) => true,
315                (ConstantValue::Bool(l0), ConstantValue::Bool(r0)) => l0 == r0,
316                (ConstantValue::Uint(l0), ConstantValue::Uint(r0)) => l0 == r0,
317                (ConstantValue::U256(l0), ConstantValue::U256(r0)) => l0 == r0,
318                (ConstantValue::B256(l0), ConstantValue::B256(r0)) => l0 == r0,
319                (ConstantValue::String(l0), ConstantValue::String(r0)) => l0 == r0,
320                (ConstantValue::Array(l0), ConstantValue::Array(r0))
321                | (ConstantValue::Struct(l0), ConstantValue::Struct(r0)) => {
322                    l0.iter().zip(r0.iter()).all(|(l0, r0)| l0.eq(context, r0))
323                }
324                _ => false,
325            }
326    }
327
328    pub fn as_uint(&self) -> Option<u64> {
329        match &self.value {
330            ConstantValue::Uint(v) => Some(*v),
331            _ => None,
332        }
333    }
334}