1use 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#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, DebugWithContext)]
12pub struct Constant(#[in_context(values)] pub slotmap::DefaultKey);
13
14impl Constant {
15 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 context.constants_map.entry(hash).or_default();
22 let constants = context.constants_map.get(&hash).unwrap();
23 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 let constants = context.constants_map.get_mut(&hash).unwrap();
32 constants.push(constant);
33 constant
34 }
35
36 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#[derive(Debug, Clone, DebugWithContext, Hash)]
47pub struct ConstantContent {
48 pub ty: Type,
49 pub value: ConstantValue,
50}
51
52pub type B256 = U256;
53
54#[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
71type 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 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 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 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 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, };
212
213 Some((&elems[0], &elems[1]))
214 }
215
216 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 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 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 fn elements_of_aggregate_with_padding(
271 &self,
272 context: &Context,
273 ) -> Option<Vec<(&ConstantContent, Option<Padding>)>> {
274 if let Some((tag, value)) = self.extract_enum_tag_and_value(context) {
276 let tag_with_padding = (tag, None);
277
278 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 ConstantValue::Array(elems) => Some(elems.iter().map(|el| (el, None)).collect()),
294 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 pub fn eq(&self, context: &Context, other: &Self) -> bool {
310 self.ty.eq(context, &other.ty)
311 && match (&self.value, &other.value) {
312 (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}