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_untyped_slice(context: &mut Context, bytes: Vec<u8>) -> Self {
124        ConstantContent {
125            ty: Type::new_untyped_slice(context),
126            value: ConstantValue::RawUntypedSlice(bytes),
127        }
128    }
129
130    pub fn new_array(context: &mut Context, elm_ty: Type, elems: Vec<ConstantContent>) -> Self {
131        ConstantContent {
132            ty: Type::new_array(context, elm_ty, elems.len() as u64),
133            value: ConstantValue::Array(elems),
134        }
135    }
136
137    pub fn new_struct(
138        context: &mut Context,
139        field_tys: Vec<Type>,
140        fields: Vec<ConstantContent>,
141    ) -> Self {
142        ConstantContent {
143            ty: Type::new_struct(context, field_tys),
144            value: ConstantValue::Struct(fields),
145        }
146    }
147
148    pub fn get_undef(ty: Type) -> Self {
149        ConstantContent {
150            ty,
151            value: ConstantValue::Undef,
152        }
153    }
154
155    pub fn get_unit(context: &mut Context) -> Value {
156        let new_const_contents = ConstantContent::new_unit(context);
157        let new_const = Constant::unique(context, new_const_contents);
158        Value::new_constant(context, new_const)
159    }
160
161    pub fn get_bool(context: &mut Context, value: bool) -> Value {
162        let new_const_contents = ConstantContent::new_bool(context, value);
163        let new_const = Constant::unique(context, new_const_contents);
164        Value::new_constant(context, new_const)
165    }
166
167    pub fn get_uint(context: &mut Context, nbits: u16, value: u64) -> Value {
168        let new_const_contents = ConstantContent::new_uint(context, nbits, value);
169        let new_const = Constant::unique(context, new_const_contents);
170        Value::new_constant(context, new_const)
171    }
172
173    pub fn get_uint256(context: &mut Context, value: U256) -> Value {
174        let new_const_contents = ConstantContent::new_uint256(context, value);
175        let new_const = Constant::unique(context, new_const_contents);
176        Value::new_constant(context, new_const)
177    }
178
179    pub fn get_b256(context: &mut Context, value: [u8; 32]) -> Value {
180        let new_const_contents = ConstantContent::new_b256(context, value);
181        let new_const = Constant::unique(context, new_const_contents);
182        Value::new_constant(context, new_const)
183    }
184
185    pub fn get_string(context: &mut Context, value: Vec<u8>) -> Value {
186        let new_const_contents = ConstantContent::new_string(context, value);
187        let new_const = Constant::unique(context, new_const_contents);
188        Value::new_constant(context, new_const)
189    }
190
191    pub fn get_untyped_slice(context: &mut Context, value: Vec<u8>) -> Value {
192        let new_const_contents = ConstantContent::new_untyped_slice(context, value);
193        let new_const = Constant::unique(context, new_const_contents);
194        Value::new_constant(context, new_const)
195    }
196
197    /// `value` must be created as an array constant first, using [`Constant::new_array()`].
198    pub fn get_array(context: &mut Context, value: ConstantContent) -> Value {
199        assert!(value.ty.is_array(context));
200        let new_const = Constant::unique(context, value);
201        Value::new_constant(context, new_const)
202    }
203
204    /// `value` must be created as a struct constant first, using [`Constant::new_struct()`].
205    pub fn get_struct(context: &mut Context, value: ConstantContent) -> Value {
206        assert!(value.ty.is_struct(context));
207        let new_const = Constant::unique(context, value);
208        Value::new_constant(context, new_const)
209    }
210
211    /// Returns the tag and the value of an enum constant if `self` is an enum constant,
212    /// otherwise `None`.
213    fn extract_enum_tag_and_value(
214        &self,
215        context: &Context,
216    ) -> Option<(&ConstantContent, &ConstantContent)> {
217        if !self.ty.is_enum(context) {
218            return None;
219        }
220
221        let elems = match &self.value {
222            ConstantValue::Struct(elems) if elems.len() == 2 => elems,
223            _ => return None, // This should never be the case. If we have an enum, it is a struct with exactly two elements.
224        };
225
226        Some((&elems[0], &elems[1]))
227    }
228
229    /// Returns enum tag and value as [Constant]s, together with their [Padding]s,
230    /// if `self` is an enum [Constant], otherwise `None`.
231    pub fn enum_tag_and_value_with_paddings(
232        &self,
233        context: &Context,
234    ) -> Option<(ConstantWithPadding, ConstantWithPadding)> {
235        if !self.ty.is_enum(context) {
236            return None;
237        }
238
239        let tag_and_value_with_paddings = self
240            .elements_of_aggregate_with_padding(context)
241            .expect("Enums are aggregates.");
242
243        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.");
244
245        let tag = tag_and_value_with_paddings[0].clone();
246        let value = tag_and_value_with_paddings[1].clone();
247
248        Some((tag, value))
249    }
250
251    /// Returns elements of an array with the expected padding for each array element
252    /// if `self` is an array [Constant], otherwise `None`.
253    pub fn array_elements_with_padding(
254        &self,
255        context: &Context,
256    ) -> Option<Vec<ConstantWithPadding>> {
257        if !self.ty.is_array(context) {
258            return None;
259        }
260
261        self.elements_of_aggregate_with_padding(context)
262    }
263
264    /// Returns fields of a struct with the expected padding for each field
265    /// if `self` is a struct [Constant], otherwise `None`.
266    pub fn struct_fields_with_padding(
267        &self,
268        context: &Context,
269    ) -> Option<Vec<ConstantWithPadding>> {
270        if !self.ty.is_struct(context) {
271            return None;
272        }
273
274        self.elements_of_aggregate_with_padding(context)
275    }
276
277    /// Returns elements of an aggregate constant with the expected padding for each element
278    /// if `self` is an aggregate (struct, enum, or array), otherwise `None`.
279    /// If the returned [Padding] is `None` the default [Padding] for the type
280    /// is expected.
281    /// If the aggregate constant is an enum, the returned [Vec] has exactly two elements,
282    /// the first being the tag and the second the value of the enum variant.
283    fn elements_of_aggregate_with_padding(
284        &self,
285        context: &Context,
286    ) -> Option<Vec<(&ConstantContent, Option<Padding>)>> {
287        // We need a special handling in case of enums.
288        if let Some((tag, value)) = self.extract_enum_tag_and_value(context) {
289            let tag_with_padding = (tag, None);
290
291            // Enum variants are left padded to the word boundary, and the size
292            // of each variant is the size of the union.
293            // We know we have an enum here, means exactly two fields in the struct
294            // second of which is the union.
295            let target_size = self.ty.get_field_types(context)[1]
296                .size(context)
297                .in_bytes_aligned() as usize;
298
299            let value_with_padding = (value, Some(Padding::Left { target_size }));
300
301            return Some(vec![tag_with_padding, value_with_padding]);
302        }
303
304        match &self.value {
305            // Individual array elements do not have additional padding.
306            ConstantValue::Array(elems) => Some(elems.iter().map(|el| (el, None)).collect()),
307            // Each struct field is right padded to the word boundary.
308            ConstantValue::Struct(elems) => Some(
309                elems
310                    .iter()
311                    .map(|el| {
312                        let target_size = el.ty.size(context).in_bytes_aligned() as usize;
313                        (el, Some(Padding::Right { target_size }))
314                    })
315                    .collect(),
316            ),
317            _ => None,
318        }
319    }
320
321    /// Compare two Constant values. Can't impl PartialOrder because of context.
322    pub fn eq(&self, context: &Context, other: &Self) -> bool {
323        self.ty.eq(context, &other.ty)
324            && match (&self.value, &other.value) {
325                // Two Undefs are *NOT* equal (PartialEq allows this).
326                (ConstantValue::Undef, _) | (_, ConstantValue::Undef) => false,
327                (ConstantValue::Unit, ConstantValue::Unit) => true,
328                (ConstantValue::Bool(l0), ConstantValue::Bool(r0)) => l0 == r0,
329                (ConstantValue::Uint(l0), ConstantValue::Uint(r0)) => l0 == r0,
330                (ConstantValue::U256(l0), ConstantValue::U256(r0)) => l0 == r0,
331                (ConstantValue::B256(l0), ConstantValue::B256(r0)) => l0 == r0,
332                (ConstantValue::String(l0), ConstantValue::String(r0)) => l0 == r0,
333                (ConstantValue::Array(l0), ConstantValue::Array(r0))
334                | (ConstantValue::Struct(l0), ConstantValue::Struct(r0)) => {
335                    l0.iter().zip(r0.iter()).all(|(l0, r0)| l0.eq(context, r0))
336                }
337                _ => false,
338            }
339    }
340
341    pub fn as_uint(&self) -> Option<u64> {
342        match &self.value {
343            ConstantValue::Uint(v) => Some(*v),
344            _ => None,
345        }
346    }
347
348    pub fn as_bool(&self) -> Option<bool> {
349        match &self.value {
350            ConstantValue::Bool(v) => Some(*v),
351            _ => None,
352        }
353    }
354
355    pub fn as_u256(&self) -> Option<U256> {
356        match &self.value {
357            ConstantValue::U256(v) => Some(v.clone()),
358            _ => None,
359        }
360    }
361
362    pub fn as_b256(&self) -> Option<B256> {
363        match &self.value {
364            ConstantValue::B256(v) => Some(v.clone()),
365            _ => None,
366        }
367    }
368
369    pub fn as_string(&self) -> Option<String> {
370        match &self.value {
371            ConstantValue::String(v) => Some(
372                String::from_utf8(v.clone())
373                    .expect("compilation ensures that the string slice is a valid UTF-8 sequence"),
374            ),
375            _ => None,
376        }
377    }
378}