sway_core/ir_generation/
const_eval.rs

1use std::{
2    hash::{DefaultHasher, Hash},
3    io::Read,
4    ops::{BitAnd, BitOr, BitXor, Not, Rem},
5};
6
7use crate::{
8    engine_threading::*,
9    ir_generation::function::{get_encoding_representation, get_memory_id},
10    language::{
11        ty::{self, TyConstantDecl, TyIntrinsicFunctionKind},
12        CallPath, Literal,
13    },
14    metadata::MetadataManager,
15    semantic_analysis::*,
16    TypeInfo, UnifyCheck,
17};
18
19use super::{
20    convert::{convert_literal_to_constant, convert_resolved_type_id},
21    function::FnCompiler,
22    types::*,
23};
24
25use sway_ast::Intrinsic;
26use sway_error::error::CompileError;
27use sway_ir::{
28    constant::{ConstantContent, ConstantValue},
29    context::Context,
30    module::Module,
31    value::Value,
32    Constant, GlobalVar, InstOp, Instruction, Type, TypeContent,
33};
34use sway_types::{ident::Ident, integer_bits::IntegerBits, span::Spanned, Named, Span};
35use sway_utils::mapped_stack::MappedStack;
36
37#[derive(Debug)]
38enum ConstEvalError {
39    CompileError,
40    CannotBeEvaluatedToConst {
41        // This is not used at the moment because we do not give detailed description of why a
42        // const eval failed.
43        // Nonetheless, this is used in tests to help debug.
44        #[allow(dead_code)]
45        span: Span,
46    },
47}
48
49pub(crate) struct LookupEnv<'a, 'eng> {
50    pub(crate) engines: &'a Engines,
51    pub(crate) context: &'a mut Context<'eng>,
52    pub(crate) md_mgr: &'a mut MetadataManager,
53    pub(crate) module: Module,
54    pub(crate) module_ns: Option<&'a namespace::Module>,
55    pub(crate) function_compiler: Option<&'a FnCompiler<'a>>,
56    #[allow(clippy::type_complexity)]
57    pub(crate) lookup: fn(
58        &mut LookupEnv,
59        &CallPath,
60        &Option<TyConstantDecl>,
61    ) -> Result<Option<Value>, CompileError>,
62}
63
64pub(crate) fn compile_const_decl(
65    env: &mut LookupEnv,
66    call_path: &CallPath,
67    const_decl: &Option<TyConstantDecl>,
68) -> Result<Option<Value>, CompileError> {
69    // Check if it's a processed local constant.
70    if let Some(fn_compiler) = env.function_compiler {
71        let mut found_local = false;
72        if let Some(local_var) =
73            fn_compiler.get_function_var(env.context, call_path.suffix.as_str())
74        {
75            found_local = true;
76            if let Some(constant) = local_var.get_initializer(env.context) {
77                return Ok(Some(Value::new_constant(env.context, *constant)));
78            }
79
80            // Check if a constant was stored to a local variable in the current block.
81            let mut stored_const_opt: Option<&Constant> = None;
82            for ins in fn_compiler.current_block.instruction_iter(env.context) {
83                if let Some(Instruction {
84                    op:
85                        InstOp::Store {
86                            dst_val_ptr: dst_val,
87                            stored_val,
88                        },
89                    ..
90                }) = ins.get_instruction(env.context)
91                {
92                    if let Some(Instruction {
93                        op: InstOp::GetLocal(store_dst_var),
94                        ..
95                    }) = dst_val.get_instruction(env.context)
96                    {
97                        if &local_var == store_dst_var {
98                            stored_const_opt = stored_val.get_constant(env.context);
99                        }
100                    }
101                }
102            }
103            if let Some(constant) = stored_const_opt {
104                return Ok(Some(Value::new_constant(env.context, *constant)));
105            }
106        }
107
108        if let Some(value) = fn_compiler.get_function_arg(env.context, call_path.suffix.as_str()) {
109            found_local = true;
110            if value.get_constant(env.context).is_some() {
111                return Ok(Some(value));
112            }
113        }
114
115        if found_local {
116            return Ok(None);
117        }
118    }
119
120    // Check if it's a processed global constant.
121    match (
122        env.module
123            .get_global_variable(env.context, &call_path.as_vec_string()),
124        env.module_ns,
125    ) {
126        (Some(global_var), _) => {
127            let constant = global_var
128                .get_initializer(env.context)
129                .expect("const decl without initializer, should've been detected way early");
130            Ok(Some(Value::new_constant(env.context, *constant)))
131        }
132        (None, Some(module_ns)) => {
133            // See if we it's a global const and whether we can compile it *now*.
134            let decl = module_ns.root_items().check_symbol(&call_path.suffix);
135            let const_decl = match const_decl {
136                Some(decl) => Some(decl),
137                None => None,
138            };
139
140            let const_decl = match decl {
141                Ok(decl) => match decl.expect_typed() {
142                    ty::TyDecl::ConstantDecl(ty::ConstantDecl { decl_id, .. }) => {
143                        Some((*env.engines.de().get_constant(&decl_id)).clone())
144                    }
145                    _otherwise => const_decl.cloned(),
146                },
147                Err(_) => const_decl.cloned(),
148            };
149
150            match const_decl {
151                Some(const_decl) => {
152                    let ty::TyConstantDecl {
153                        call_path, value, ..
154                    } = const_decl;
155
156                    let Some(value) = value else {
157                        return Ok(None);
158                    };
159
160                    let const_val = compile_constant_expression(
161                        env.engines,
162                        env.context,
163                        env.md_mgr,
164                        env.module,
165                        env.module_ns,
166                        env.function_compiler,
167                        &call_path,
168                        &value,
169                    )?;
170
171                    let const_val_c = *const_val
172                        .get_constant(env.context)
173                        .expect("Must have been compiled to a constant");
174
175                    let c_ty = const_val_c.get_content(env.context).ty;
176                    let const_global = GlobalVar::new(env.context, c_ty, Some(const_val_c), false);
177
178                    env.module.add_global_variable(
179                        env.context,
180                        call_path.as_vec_string().to_vec(),
181                        const_global,
182                    );
183
184                    Ok(Some(const_val))
185                }
186                None => Ok(None),
187            }
188        }
189        _ => Ok(None),
190    }
191}
192
193#[allow(clippy::too_many_arguments)]
194pub(super) fn compile_constant_expression(
195    engines: &Engines,
196    context: &mut Context,
197    md_mgr: &mut MetadataManager,
198    module: Module,
199    module_ns: Option<&namespace::Module>,
200    function_compiler: Option<&FnCompiler>,
201    _call_path: &CallPath,
202    const_expr: &ty::TyExpression,
203) -> Result<Value, CompileError> {
204    let span_id_idx = md_mgr.span_to_md(context, &const_expr.span);
205
206    let constant_evaluated = compile_constant_expression_to_constant(
207        engines,
208        context,
209        md_mgr,
210        module,
211        module_ns,
212        function_compiler,
213        const_expr,
214    )?;
215
216    Ok(Value::new_constant(context, constant_evaluated).add_metadatum(context, span_id_idx))
217}
218
219#[allow(clippy::too_many_arguments)]
220pub(crate) fn compile_constant_expression_to_constant(
221    engines: &Engines,
222    context: &mut Context,
223    md_mgr: &mut MetadataManager,
224    module: Module,
225    module_ns: Option<&namespace::Module>,
226    function_compiler: Option<&FnCompiler>,
227    const_expr: &ty::TyExpression,
228) -> Result<Constant, CompileError> {
229    let lookup = &mut LookupEnv {
230        engines,
231        context,
232        md_mgr,
233        module,
234        module_ns,
235        function_compiler,
236        lookup: compile_const_decl,
237    };
238
239    let err = match &const_expr.expression {
240        // Special case functions because the span in `const_expr` is to the inlined function
241        // definition, rather than the actual call site.
242        ty::TyExpressionVariant::FunctionApplication { call_path, .. } => {
243            let span = call_path.span();
244            let span = if span.is_dummy() {
245                const_expr.span.clone()
246            } else {
247                span
248            };
249            Err(CompileError::NonConstantDeclValue { span })
250        }
251        _otherwise => Err(CompileError::NonConstantDeclValue {
252            span: const_expr.span.clone(),
253        }),
254    };
255    let mut known_consts = MappedStack::<Ident, Constant>::new();
256
257    match const_eval_typed_expr(lookup, &mut known_consts, const_expr) {
258        Ok(Some(constant)) => Ok(constant),
259        Ok(None) => err,
260        Err(_) => err,
261    }
262}
263
264fn create_array_from_vec(
265    lookup: &mut LookupEnv,
266    elem_type: crate::TypeId,
267    element_types: Vec<crate::TypeId>,
268    element_vals: Vec<Constant>,
269) -> Option<Constant> {
270    assert!({
271        let unify_check = UnifyCheck::coercion(lookup.engines);
272        element_types
273            .iter()
274            .all(|tid| unify_check.check(*tid, elem_type))
275    });
276
277    let arr = create_array_aggregate(
278        lookup.engines,
279        lookup.context,
280        lookup.md_mgr,
281        lookup.module,
282        elem_type,
283        element_types.len().try_into().unwrap(),
284    )
285    .map_or(None, |array_ty| {
286        Some(ConstantContent::new_array(
287            lookup.context,
288            array_ty.get_array_elem_type(lookup.context).unwrap(),
289            element_vals
290                .iter()
291                .map(|f| f.get_content(lookup.context).clone())
292                .collect(),
293        ))
294    });
295
296    arr.map(|c| Constant::unique(lookup.context, c))
297}
298
299/// Given an environment mapping names to constants,
300/// attempt to evaluate a typed expression to a constant.
301fn const_eval_typed_expr(
302    lookup: &mut LookupEnv,
303    known_consts: &mut MappedStack<Ident, Constant>,
304    expr: &ty::TyExpression,
305) -> Result<Option<Constant>, ConstEvalError> {
306    if let TypeInfo::ErrorRecovery(_) = &*lookup.engines.te().get(expr.return_type) {
307        return Err(ConstEvalError::CannotBeEvaluatedToConst {
308            span: expr.span.clone(),
309        });
310    }
311
312    Ok(match &expr.expression {
313        ty::TyExpressionVariant::ConstGenericExpression { decl, .. } => {
314            assert!(decl.value.is_some());
315            const_eval_typed_expr(lookup, known_consts, decl.value.as_ref().unwrap())?
316        }
317        ty::TyExpressionVariant::Literal(Literal::Numeric(n)) => {
318            let implied_lit = match &*lookup.engines.te().get(expr.return_type) {
319                TypeInfo::UnsignedInteger(IntegerBits::Eight) => Literal::U8(*n as u8),
320                _ => Literal::U64(*n),
321            };
322            Some(convert_literal_to_constant(lookup.context, &implied_lit))
323        }
324        ty::TyExpressionVariant::Literal(l) => Some(convert_literal_to_constant(lookup.context, l)),
325        ty::TyExpressionVariant::FunctionApplication {
326            arguments,
327            fn_ref,
328            call_path,
329            ..
330        } => {
331            let mut actuals_const: Vec<_> = vec![];
332
333            for arg in arguments {
334                let (name, sub_expr) = arg;
335                let eval_expr_opt = const_eval_typed_expr(lookup, known_consts, sub_expr)?;
336                if let Some(sub_const) = eval_expr_opt {
337                    actuals_const.push((name, sub_const));
338                } else {
339                    // If all actual arguments don't evaluate a constant, bail out.
340                    // TODO: Explore if we could continue here and if it'll be useful.
341                    return Err(ConstEvalError::CannotBeEvaluatedToConst {
342                        span: call_path.span(),
343                    });
344                }
345            }
346
347            assert!(actuals_const.len() == arguments.len());
348
349            for (name, cval) in actuals_const.into_iter() {
350                known_consts.push(name.clone(), cval);
351            }
352
353            let function_decl = lookup.engines.de().get_function(fn_ref);
354            let res = const_eval_codeblock(lookup, known_consts, &function_decl.body);
355
356            for (name, _) in arguments {
357                known_consts.pop(name);
358            }
359
360            res?
361        }
362        ty::TyExpressionVariant::ConstantExpression { decl, .. } => {
363            let call_path = &decl.call_path;
364            let name = &call_path.suffix;
365            match known_consts.get(name) {
366                Some(constant) => Some(*constant),
367                None => (lookup.lookup)(lookup, call_path, &Some(*decl.clone()))
368                    .ok()
369                    .flatten()
370                    .and_then(|v| v.get_constant(lookup.context).cloned()),
371            }
372        }
373        ty::TyExpressionVariant::ConfigurableExpression { span, .. } => {
374            return Err(ConstEvalError::CannotBeEvaluatedToConst { span: span.clone() });
375        }
376        ty::TyExpressionVariant::VariableExpression {
377            name, call_path, ..
378        } => match known_consts.get(name) {
379            // 1. Check if name/call_path is in known_consts.
380            Some(cvs) => Some(*cvs),
381            None => {
382                let call_path = match call_path {
383                    Some(call_path) => call_path.clone(),
384                    None => CallPath::from(name.clone()),
385                };
386                // 2. Check if name is a global constant.
387                (lookup.lookup)(lookup, &call_path, &None)
388                    .ok()
389                    .flatten()
390                    .and_then(|v| v.get_constant(lookup.context).cloned())
391            }
392        },
393        ty::TyExpressionVariant::StructExpression {
394            fields,
395            instantiation_span,
396            ..
397        } => {
398            let (mut field_types, mut field_vals): (Vec<_>, Vec<_>) = (vec![], vec![]);
399
400            for field in fields {
401                let ty::TyStructExpressionField { name: _, value, .. } = field;
402                let eval_expr_opt = const_eval_typed_expr(lookup, known_consts, value)?;
403                if let Some(cv) = eval_expr_opt {
404                    field_types.push(value.return_type);
405                    field_vals.push(cv);
406                } else {
407                    return Err(ConstEvalError::CannotBeEvaluatedToConst {
408                        span: instantiation_span.clone(),
409                    });
410                }
411            }
412
413            assert!(field_types.len() == fields.len());
414            assert!(field_vals.len() == fields.len());
415
416            get_struct_for_types(
417                lookup.engines,
418                lookup.context,
419                lookup.md_mgr,
420                lookup.module,
421                &field_types,
422            )
423            .map_or(None, |struct_ty| {
424                let c = ConstantContent::new_struct(
425                    lookup.context,
426                    struct_ty.get_field_types(lookup.context),
427                    field_vals
428                        .iter()
429                        .map(|fv| fv.get_content(lookup.context).clone())
430                        .collect(),
431                );
432                let c = Constant::unique(lookup.context, c);
433                Some(c)
434            })
435        }
436        ty::TyExpressionVariant::Tuple { fields } => {
437            let (mut field_types, mut field_vals): (Vec<_>, Vec<_>) = (vec![], vec![]);
438
439            for value in fields {
440                let eval_expr_opt = const_eval_typed_expr(lookup, known_consts, value)?;
441                if let Some(cv) = eval_expr_opt {
442                    field_types.push(value.return_type);
443                    field_vals.push(cv);
444                } else {
445                    return Err(ConstEvalError::CannotBeEvaluatedToConst {
446                        span: expr.span.clone(),
447                    });
448                }
449            }
450
451            assert!(field_types.len() == fields.len());
452            assert!(field_vals.len() == fields.len());
453
454            create_tuple_aggregate(
455                lookup.engines,
456                lookup.context,
457                lookup.md_mgr,
458                lookup.module,
459                &field_types,
460            )
461            .map_or(None, |tuple_ty| {
462                let c = ConstantContent::new_struct(
463                    lookup.context,
464                    tuple_ty.get_field_types(lookup.context),
465                    field_vals
466                        .iter()
467                        .map(|fv| fv.get_content(lookup.context).clone())
468                        .collect(),
469                );
470                let c = Constant::unique(lookup.context, c);
471                Some(c)
472            })
473        }
474        ty::TyExpressionVariant::ArrayExplicit {
475            elem_type,
476            contents,
477        } => {
478            let (mut element_types, mut element_vals): (Vec<_>, Vec<_>) = (vec![], vec![]);
479
480            for value in contents {
481                let eval_expr_opt = const_eval_typed_expr(lookup, known_consts, value)?;
482                if let Some(cv) = eval_expr_opt {
483                    element_types.push(value.return_type);
484                    element_vals.push(cv);
485                } else {
486                    return Err(ConstEvalError::CannotBeEvaluatedToConst {
487                        span: expr.span.clone(),
488                    });
489                }
490            }
491
492            assert!(element_types.len() == contents.len());
493            assert!(element_vals.len() == contents.len());
494
495            create_array_from_vec(lookup, *elem_type, element_types, element_vals)
496        }
497        ty::TyExpressionVariant::ArrayRepeat {
498            elem_type,
499            value,
500            length,
501        } => {
502            let constant = const_eval_typed_expr(lookup, known_consts, value)?.unwrap();
503            let length = const_eval_typed_expr(lookup, known_consts, length)?
504                .unwrap()
505                .get_content(lookup.context)
506                .as_uint()
507                .unwrap() as usize;
508            let element_vals = (0..length).map(|_| constant).collect::<Vec<_>>();
509            let element_types = (0..length).map(|_| value.return_type).collect::<Vec<_>>();
510
511            assert!(element_types.len() == length);
512            assert!(element_vals.len() == length);
513
514            create_array_from_vec(lookup, *elem_type, element_types, element_vals)
515        }
516        ty::TyExpressionVariant::EnumInstantiation {
517            enum_ref,
518            tag,
519            contents,
520            variant_instantiation_span,
521            ..
522        } => {
523            let enum_decl = lookup.engines.de().get_enum(enum_ref);
524            let aggregate = create_tagged_union_type(
525                lookup.engines,
526                lookup.context,
527                lookup.md_mgr,
528                lookup.module,
529                &enum_decl.variants,
530            );
531
532            if let Ok(enum_ty) = aggregate {
533                let tag_value = ConstantContent::new_uint(lookup.context, 64, *tag as u64);
534                let mut fields: Vec<ConstantContent> = vec![tag_value];
535
536                match contents {
537                    None => fields.push(ConstantContent::new_unit(lookup.context)),
538                    Some(subexpr) => match const_eval_typed_expr(lookup, known_consts, subexpr)? {
539                        Some(constant) => fields.push(constant.get_content(lookup.context).clone()),
540                        None => {
541                            return Err(ConstEvalError::CannotBeEvaluatedToConst {
542                                span: variant_instantiation_span.clone(),
543                            });
544                        }
545                    },
546                }
547
548                let fields_tys = enum_ty.get_field_types(lookup.context);
549                let c = ConstantContent::new_struct(lookup.context, fields_tys, fields);
550                let c = Constant::unique(lookup.context, c);
551                Some(c)
552            } else {
553                return Err(ConstEvalError::CannotBeEvaluatedToConst {
554                    span: expr.span.clone(),
555                });
556            }
557        }
558        ty::TyExpressionVariant::StructFieldAccess {
559            prefix,
560            field_to_access,
561            resolved_type_of_parent,
562            ..
563        } => match const_eval_typed_expr(lookup, known_consts, prefix)?
564            .map(|c| c.get_content(lookup.context).clone())
565        {
566            Some(ConstantContent {
567                value: ConstantValue::Struct(fields),
568                ..
569            }) => {
570                let field_kind = ty::ProjectionKind::StructField {
571                    name: field_to_access.name.clone(),
572                    field_to_access: Some(Box::new(field_to_access.clone())),
573                };
574                get_struct_name_field_index_and_type(
575                    lookup.engines.te(),
576                    lookup.engines.de(),
577                    *resolved_type_of_parent,
578                    field_kind,
579                )
580                .and_then(|(_struct_name, field_idx_and_type_opt)| {
581                    field_idx_and_type_opt.map(|(field_idx, _field_type)| field_idx)
582                })
583                .and_then(|field_idx| {
584                    fields
585                        .get(field_idx as usize)
586                        .cloned()
587                        .map(|c| Constant::unique(lookup.context, c))
588                })
589            }
590            _ => {
591                return Err(ConstEvalError::CannotBeEvaluatedToConst {
592                    span: expr.span.clone(),
593                });
594            }
595        },
596        ty::TyExpressionVariant::TupleElemAccess {
597            prefix,
598            elem_to_access_num,
599            ..
600        } => match const_eval_typed_expr(lookup, known_consts, prefix)?
601            .map(|c| c.get_content(lookup.context))
602        {
603            Some(ConstantContent {
604                value: ConstantValue::Struct(fields),
605                ..
606            }) => fields
607                .get(*elem_to_access_num)
608                .cloned()
609                .map(|c| Constant::unique(lookup.context, c)),
610            _ => {
611                return Err(ConstEvalError::CannotBeEvaluatedToConst {
612                    span: expr.span.clone(),
613                });
614            }
615        },
616        ty::TyExpressionVariant::ImplicitReturn(e) => {
617            if let Ok(Some(constant)) = const_eval_typed_expr(lookup, known_consts, e) {
618                Some(constant)
619            } else {
620                return Err(ConstEvalError::CannotBeEvaluatedToConst {
621                    span: e.span.clone(),
622                });
623            }
624        }
625        // we could allow non-local control flow in pure functions, but it would
626        // require some more work and at this point it's not clear if it is too useful
627        // for constant initializers -- the user can always refactor their pure functions
628        // to not use the return statement
629        ty::TyExpressionVariant::Return(exp) => {
630            return Err(ConstEvalError::CannotBeEvaluatedToConst {
631                span: exp.span.clone(),
632            });
633        }
634        ty::TyExpressionVariant::Panic(exp) => {
635            return Err(ConstEvalError::CannotBeEvaluatedToConst {
636                span: exp.span.clone(),
637            });
638        }
639        ty::TyExpressionVariant::MatchExp { desugared, .. } => {
640            const_eval_typed_expr(lookup, known_consts, desugared)?
641        }
642        ty::TyExpressionVariant::IntrinsicFunction(kind) => {
643            const_eval_intrinsic(lookup, known_consts, kind)?
644        }
645        ty::TyExpressionVariant::IfExp {
646            condition,
647            then,
648            r#else,
649        } => {
650            match const_eval_typed_expr(lookup, known_consts, condition)?
651                .map(|c| c.get_content(lookup.context))
652            {
653                Some(ConstantContent {
654                    value: ConstantValue::Bool(cond),
655                    ..
656                }) => {
657                    if *cond {
658                        const_eval_typed_expr(lookup, known_consts, then)?
659                    } else if let Some(r#else) = r#else {
660                        const_eval_typed_expr(lookup, known_consts, r#else)?
661                    } else {
662                        // missing 'else' branch:
663                        // we probably don't really care about evaluating
664                        // const expressions of the unit type
665                        None
666                    }
667                }
668                _ => {
669                    return Err(ConstEvalError::CannotBeEvaluatedToConst {
670                        span: expr.span.clone(),
671                    });
672                }
673            }
674        }
675        ty::TyExpressionVariant::CodeBlock(codeblock) => {
676            const_eval_codeblock(lookup, known_consts, codeblock)?
677        }
678        ty::TyExpressionVariant::ArrayIndex { prefix, index } => {
679            let prefix = const_eval_typed_expr(lookup, known_consts, prefix)?
680                .map(|c| c.get_content(lookup.context).clone());
681            let index = const_eval_typed_expr(lookup, known_consts, index)?
682                .map(|c| c.get_content(lookup.context));
683            match (prefix, index) {
684                (
685                    Some(ConstantContent {
686                        value: ConstantValue::Array(items),
687                        ..
688                    }),
689                    Some(ConstantContent {
690                        value: ConstantValue::Uint(index),
691                        ..
692                    }),
693                ) => {
694                    let count = items.len() as u64;
695                    if *index < count {
696                        let c = Constant::unique(lookup.context, items[*index as usize].clone());
697                        Some(c)
698                    } else {
699                        return Err(ConstEvalError::CompileError);
700                    }
701                }
702                _ => {
703                    return Err(ConstEvalError::CannotBeEvaluatedToConst {
704                        span: expr.span.clone(),
705                    });
706                }
707            }
708        }
709        ty::TyExpressionVariant::Ref(_) => {
710            return Err(ConstEvalError::CompileError);
711        }
712        // We support *__elem_at(...)
713        ty::TyExpressionVariant::Deref(expr) => {
714            let value = expr
715                .as_intrinsic()
716                .filter(|x| matches!(x.kind, Intrinsic::ElemAt))
717                .ok_or(ConstEvalError::CompileError)
718                .and_then(|kind| {
719                    const_eval_intrinsic(lookup, known_consts, kind)
720                        .map(|c| c.map(|c| c.get_content(lookup.context).clone()))
721                });
722            if let Ok(Some(ConstantContent {
723                value: ConstantValue::Reference(value),
724                ..
725            })) = value
726            {
727                let c = Constant::unique(lookup.context, *value.clone());
728                Some(c)
729            } else {
730                return Err(ConstEvalError::CompileError);
731            }
732        }
733        ty::TyExpressionVariant::EnumTag { exp } => {
734            let value = const_eval_typed_expr(lookup, known_consts, exp)?
735                .map(|x| x.get_content(lookup.context).value.clone());
736            if let Some(ConstantValue::Struct(fields)) = value {
737                Some(Constant::unique(lookup.context, fields[0].clone()))
738            } else {
739                return Err(ConstEvalError::CompileError);
740            }
741        }
742        ty::TyExpressionVariant::UnsafeDowncast { exp, .. } => {
743            let value = const_eval_typed_expr(lookup, known_consts, exp)?
744                .map(|x| x.get_content(lookup.context).value.clone());
745            if let Some(ConstantValue::Struct(fields)) = value {
746                Some(Constant::unique(lookup.context, fields[1].clone()))
747            } else {
748                return Err(ConstEvalError::CompileError);
749            }
750        }
751        ty::TyExpressionVariant::WhileLoop {
752            condition, body, ..
753        } => {
754            // Arbitrary limit of iterations to avoid infinite loops like
755            // while true {}
756            let mut limit = 1_000_000;
757
758            while limit >= 0 {
759                limit -= 1;
760
761                let condition = const_eval_typed_expr(lookup, known_consts, condition)?;
762                match condition.map(|x| x.get_content(lookup.context).value.clone()) {
763                    Some(ConstantValue::Bool(true)) => {
764                        // Break and continue are not implemented, so there is need for flow control here
765                        let _ = const_eval_codeblock(lookup, known_consts, body)?;
766                    }
767                    _ => break,
768                }
769            }
770
771            None
772        }
773        ty::TyExpressionVariant::Reassignment(r) => {
774            let rhs = const_eval_typed_expr(lookup, known_consts, &r.rhs)?.unwrap();
775            match &r.lhs {
776                ty::TyReassignmentTarget::ElementAccess {
777                    base_name, indices, ..
778                } => {
779                    if !indices.is_empty() {
780                        return Err(ConstEvalError::CannotBeEvaluatedToConst {
781                            span: expr.span.clone(),
782                        });
783                    }
784                    if let Some(lhs) = known_consts.get_mut(base_name) {
785                        *lhs = rhs;
786                        return Ok(None);
787                    } else {
788                        return Err(ConstEvalError::CannotBeEvaluatedToConst {
789                            span: expr.span.clone(),
790                        });
791                    }
792                }
793                ty::TyReassignmentTarget::DerefAccess { .. } => {
794                    return Err(ConstEvalError::CannotBeEvaluatedToConst {
795                        span: expr.span.clone(),
796                    });
797                }
798            }
799        }
800        ty::TyExpressionVariant::FunctionParameter
801        | ty::TyExpressionVariant::AsmExpression { .. }
802        | ty::TyExpressionVariant::LazyOperator { .. }
803        | ty::TyExpressionVariant::AbiCast { .. }
804        | ty::TyExpressionVariant::StorageAccess(_)
805        | ty::TyExpressionVariant::AbiName(_)
806        | ty::TyExpressionVariant::Break
807        | ty::TyExpressionVariant::Continue
808        | ty::TyExpressionVariant::ForLoop { .. } => {
809            return Err(ConstEvalError::CannotBeEvaluatedToConst {
810                span: expr.span.clone(),
811            });
812        }
813    })
814}
815
816// the (constant) value of a codeblock is essentially it's last expression if there is one
817// or if it makes sense as the last expression, e.g. a dangling let-expression in a codeblock
818// would be an evaluation error
819fn const_eval_codeblock(
820    lookup: &mut LookupEnv,
821    known_consts: &mut MappedStack<Ident, Constant>,
822    codeblock: &ty::TyCodeBlock,
823) -> Result<Option<Constant>, ConstEvalError> {
824    // the current result
825    let mut result: Result<Option<Constant>, ConstEvalError> = Ok(None);
826    // keep track of new bindings for this codeblock
827    let mut bindings: Vec<_> = vec![];
828
829    for ast_node in &codeblock.contents {
830        result = match &ast_node.content {
831            ty::TyAstNodeContent::Declaration(decl @ ty::TyDecl::VariableDecl(var_decl)) => {
832                if let Ok(Some(rhs)) = const_eval_typed_expr(lookup, known_consts, &var_decl.body) {
833                    known_consts.push(var_decl.name.clone(), rhs);
834                    bindings.push(var_decl.name.clone());
835                    Ok(None)
836                } else {
837                    Err(ConstEvalError::CannotBeEvaluatedToConst {
838                        span: decl.span(lookup.engines).clone(),
839                    })
840                }
841            }
842            ty::TyAstNodeContent::Declaration(ty::TyDecl::ConstantDecl(const_decl)) => {
843                let ty_const_decl = lookup.engines.de().get_constant(&const_decl.decl_id);
844                if let Some(constant) = ty_const_decl
845                    .value
846                    .clone()
847                    .and_then(|expr| const_eval_typed_expr(lookup, known_consts, &expr).ok())
848                    .flatten()
849                {
850                    known_consts.push(ty_const_decl.name().clone(), constant);
851                    bindings.push(ty_const_decl.name().clone());
852                    Ok(None)
853                } else {
854                    Err(ConstEvalError::CannotBeEvaluatedToConst {
855                        span: ty_const_decl.span.clone(),
856                    })
857                }
858            }
859            ty::TyAstNodeContent::Declaration(_) => Ok(None),
860            ty::TyAstNodeContent::Expression(e) => match e.expression {
861                ty::TyExpressionVariant::ImplicitReturn(_) => {
862                    if let Ok(Some(constant)) = const_eval_typed_expr(lookup, known_consts, e) {
863                        Ok(Some(constant))
864                    } else {
865                        Err(ConstEvalError::CannotBeEvaluatedToConst {
866                            span: e.span.clone(),
867                        })
868                    }
869                }
870                _ => {
871                    if const_eval_typed_expr(lookup, known_consts, e).is_err() {
872                        Err(ConstEvalError::CannotBeEvaluatedToConst {
873                            span: e.span.clone(),
874                        })
875                    } else {
876                        Ok(None)
877                    }
878                }
879            },
880            ty::TyAstNodeContent::SideEffect(_) => Err(ConstEvalError::CannotBeEvaluatedToConst {
881                span: ast_node.span.clone(),
882            }),
883            ty::TyAstNodeContent::Error(_, _) => Err(ConstEvalError::CannotBeEvaluatedToConst {
884                span: ast_node.span.clone(),
885            }),
886        };
887
888        if result.is_err() {
889            break;
890        }
891    }
892
893    // remove introduced vars/consts from scope at the end of the codeblock
894    for name in bindings {
895        known_consts.pop(&name)
896    }
897
898    result
899}
900
901fn as_encode_buffer<'a>(context: &'a Context, buffer: &Constant) -> Option<(&'a Vec<u8>, u64)> {
902    match &buffer.get_content(context).value {
903        ConstantValue::Struct(fields) => {
904            let slice = match &fields[0].value {
905                ConstantValue::RawUntypedSlice(bytes) => bytes,
906                _ => return None,
907            };
908            let len = match fields[1].value {
909                ConstantValue::Uint(v) => v,
910                _ => return None,
911            };
912            Some((slice, len))
913        }
914        _ => None,
915    }
916}
917
918fn to_encode_buffer(lookup: &mut LookupEnv, bytes: Vec<u8>, len: u64) -> Constant {
919    let c = ConstantContent {
920        ty: Type::new_struct(
921            lookup.context,
922            vec![
923                Type::get_slice(lookup.context),
924                Type::get_uint64(lookup.context),
925            ],
926        ),
927        value: ConstantValue::Struct(vec![
928            ConstantContent {
929                ty: Type::get_slice(lookup.context),
930                value: ConstantValue::RawUntypedSlice(bytes),
931            },
932            ConstantContent {
933                ty: Type::get_uint64(lookup.context),
934                value: ConstantValue::Uint(len),
935            },
936        ]),
937    };
938    Constant::unique(lookup.context, c)
939}
940
941fn const_eval_intrinsic(
942    lookup: &mut LookupEnv,
943    known_consts: &mut MappedStack<Ident, Constant>,
944    intrinsic: &TyIntrinsicFunctionKind,
945) -> Result<Option<Constant>, ConstEvalError> {
946    let mut args = vec![];
947    for arg in intrinsic.arguments.iter() {
948        if let Ok(Some(constant)) = const_eval_typed_expr(lookup, known_consts, arg) {
949            args.push(constant);
950        } else {
951            return Err(ConstEvalError::CannotBeEvaluatedToConst {
952                span: arg.span.clone(),
953            });
954        }
955    }
956
957    assert!(args.len() == intrinsic.arguments.len());
958
959    match intrinsic.kind {
960        Intrinsic::Add | Intrinsic::Sub | Intrinsic::Mul | Intrinsic::Div | Intrinsic::Mod => {
961            let ty = args[0].get_content(lookup.context).ty;
962            assert!(
963                args.len() == 2 && ty.eq(lookup.context, &args[1].get_content(lookup.context).ty)
964            );
965
966            use ConstantValue::*;
967            let c = match (
968                &args[0].get_content(lookup.context).value,
969                &args[1].get_content(lookup.context).value,
970            ) {
971                (Uint(arg1), Uint(ref arg2)) => {
972                    // All arithmetic is done as if it were u64
973                    let result = match intrinsic.kind {
974                        Intrinsic::Add => arg1.checked_add(*arg2),
975                        Intrinsic::Sub => arg1.checked_sub(*arg2),
976                        Intrinsic::Mul => arg1.checked_mul(*arg2),
977                        Intrinsic::Div => arg1.checked_div(*arg2),
978                        Intrinsic::Mod => arg1.checked_rem(*arg2),
979                        _ => unreachable!(),
980                    };
981
982                    match result {
983                        Some(result) => Ok(Some(ConstantContent {
984                            ty,
985                            value: ConstantValue::Uint(result),
986                        })),
987                        None => Err(ConstEvalError::CannotBeEvaluatedToConst {
988                            span: intrinsic.span.clone(),
989                        }),
990                    }
991                }
992                (U256(arg1), U256(arg2)) => {
993                    let result = match intrinsic.kind {
994                        Intrinsic::Add => arg1.checked_add(arg2),
995                        Intrinsic::Sub => arg1.checked_sub(arg2),
996                        Intrinsic::Mul => arg1.checked_mul(arg2),
997                        Intrinsic::Div => arg1.checked_div(arg2),
998                        Intrinsic::Mod => Some(arg1.rem(arg2)),
999                        _ => unreachable!(),
1000                    };
1001
1002                    match result {
1003                        Some(result) => Ok(Some(ConstantContent {
1004                            ty,
1005                            value: ConstantValue::U256(result),
1006                        })),
1007                        None => Err(ConstEvalError::CannotBeEvaluatedToConst {
1008                            span: intrinsic.span.clone(),
1009                        }),
1010                    }
1011                }
1012                _ => {
1013                    panic!("Type checker allowed incorrect args to binary op");
1014                }
1015            };
1016            c.map(|c| c.map(|c| Constant::unique(lookup.context, c)))
1017        }
1018        Intrinsic::And | Intrinsic::Or | Intrinsic::Xor => {
1019            let ty = args[0].get_content(lookup.context).ty;
1020            assert!(
1021                args.len() == 2 && ty.eq(lookup.context, &args[1].get_content(lookup.context).ty)
1022            );
1023
1024            use ConstantValue::*;
1025            let c = match (
1026                &args[0].get_content(lookup.context).value,
1027                &args[1].get_content(lookup.context).value,
1028            ) {
1029                (Uint(arg1), Uint(ref arg2)) => {
1030                    // All arithmetic is done as if it were u64
1031                    let result = match intrinsic.kind {
1032                        Intrinsic::And => Some(arg1.bitand(arg2)),
1033                        Intrinsic::Or => Some(arg1.bitor(*arg2)),
1034                        Intrinsic::Xor => Some(arg1.bitxor(*arg2)),
1035                        _ => unreachable!(),
1036                    };
1037
1038                    match result {
1039                        Some(sum) => Ok(Some(ConstantContent {
1040                            ty,
1041                            value: ConstantValue::Uint(sum),
1042                        })),
1043                        None => Err(ConstEvalError::CannotBeEvaluatedToConst {
1044                            span: intrinsic.span.clone(),
1045                        }),
1046                    }
1047                }
1048                (U256(arg1), U256(arg2)) => {
1049                    let result = match intrinsic.kind {
1050                        Intrinsic::And => Some(arg1.bitand(arg2)),
1051                        Intrinsic::Or => Some(arg1.bitor(arg2)),
1052                        Intrinsic::Xor => Some(arg1.bitxor(arg2)),
1053                        _ => unreachable!(),
1054                    };
1055
1056                    match result {
1057                        Some(sum) => Ok(Some(ConstantContent {
1058                            ty,
1059                            value: ConstantValue::U256(sum),
1060                        })),
1061                        None => Err(ConstEvalError::CannotBeEvaluatedToConst {
1062                            span: intrinsic.span.clone(),
1063                        }),
1064                    }
1065                }
1066                (B256(arg1), B256(arg2)) => {
1067                    let result = match intrinsic.kind {
1068                        Intrinsic::And => Some(arg1.bitand(arg2)),
1069                        Intrinsic::Or => Some(arg1.bitor(arg2)),
1070                        Intrinsic::Xor => Some(arg1.bitxor(arg2)),
1071                        _ => unreachable!(),
1072                    };
1073
1074                    match result {
1075                        Some(result) => Ok(Some(ConstantContent {
1076                            ty,
1077                            value: ConstantValue::B256(result),
1078                        })),
1079                        None => Err(ConstEvalError::CannotBeEvaluatedToConst {
1080                            span: intrinsic.span.clone(),
1081                        }),
1082                    }
1083                }
1084                _ => {
1085                    panic!("Type checker allowed incorrect args to binary op");
1086                }
1087            };
1088            c.map(|c| c.map(|c| Constant::unique(lookup.context, c)))
1089        }
1090        Intrinsic::Lsh | Intrinsic::Rsh => {
1091            assert!(args.len() == 2);
1092            assert!(
1093                args[0]
1094                    .get_content(lookup.context)
1095                    .ty
1096                    .is_uint(lookup.context)
1097                    || args[0]
1098                        .get_content(lookup.context)
1099                        .ty
1100                        .is_b256(lookup.context)
1101            );
1102            assert!(args[1]
1103                .get_content(lookup.context)
1104                .ty
1105                .is_uint64(lookup.context));
1106
1107            let ty = args[0].get_content(lookup.context).ty;
1108
1109            use ConstantValue::*;
1110            let c = match (
1111                &args[0].get_content(lookup.context).value,
1112                &args[1].get_content(lookup.context).value,
1113            ) {
1114                (Uint(arg1), Uint(ref arg2)) => {
1115                    let result = match intrinsic.kind {
1116                        Intrinsic::Lsh => u32::try_from(*arg2)
1117                            .ok()
1118                            .and_then(|arg2| arg1.checked_shl(arg2)),
1119                        Intrinsic::Rsh => u32::try_from(*arg2)
1120                            .ok()
1121                            .and_then(|arg2| arg1.checked_shr(arg2)),
1122                        _ => unreachable!(),
1123                    };
1124
1125                    match result {
1126                        Some(sum) => Ok(Some(ConstantContent {
1127                            ty,
1128                            value: ConstantValue::Uint(sum),
1129                        })),
1130                        None => Err(ConstEvalError::CannotBeEvaluatedToConst {
1131                            span: intrinsic.span.clone(),
1132                        }),
1133                    }
1134                }
1135                (U256(arg1), Uint(ref arg2)) => {
1136                    let result = match intrinsic.kind {
1137                        Intrinsic::Lsh => arg1.checked_shl(arg2),
1138                        Intrinsic::Rsh => Some(arg1.shr(arg2)),
1139                        _ => unreachable!(),
1140                    };
1141
1142                    match result {
1143                        Some(value) => Ok(Some(ConstantContent {
1144                            ty,
1145                            value: ConstantValue::U256(value),
1146                        })),
1147                        None => Err(ConstEvalError::CannotBeEvaluatedToConst {
1148                            span: intrinsic.span.clone(),
1149                        }),
1150                    }
1151                }
1152                (B256(arg1), Uint(ref arg2)) => {
1153                    let result = match intrinsic.kind {
1154                        Intrinsic::Lsh => arg1.checked_shl(arg2),
1155                        Intrinsic::Rsh => Some(arg1.shr(arg2)),
1156                        _ => unreachable!(),
1157                    };
1158
1159                    match result {
1160                        Some(result) => Ok(Some(ConstantContent {
1161                            ty,
1162                            value: ConstantValue::B256(result),
1163                        })),
1164                        None => Err(ConstEvalError::CannotBeEvaluatedToConst {
1165                            span: intrinsic.span.clone(),
1166                        }),
1167                    }
1168                }
1169                _ => {
1170                    panic!("Type checker allowed incorrect args to binary op");
1171                }
1172            };
1173            c.map(|c| c.map(|c| Constant::unique(lookup.context, c)))
1174        }
1175        Intrinsic::SizeOfType => {
1176            let targ = &intrinsic.type_arguments[0];
1177            let ir_type = convert_resolved_type_id(
1178                lookup.engines,
1179                lookup.context,
1180                lookup.md_mgr,
1181                lookup.module,
1182                lookup.function_compiler,
1183                targ.type_id(),
1184                &targ.span(),
1185            )
1186            .map_err(|_| ConstEvalError::CompileError)?;
1187            let c = ConstantContent {
1188                ty: Type::get_uint64(lookup.context),
1189                value: ConstantValue::Uint(ir_type.size(lookup.context).in_bytes()),
1190            };
1191
1192            Ok(Some(Constant::unique(lookup.context, c)))
1193        }
1194        Intrinsic::SizeOfVal => {
1195            let val = &intrinsic.arguments[0];
1196            let type_id = val.return_type;
1197            let ir_type = convert_resolved_type_id(
1198                lookup.engines,
1199                lookup.context,
1200                lookup.md_mgr,
1201                lookup.module,
1202                lookup.function_compiler,
1203                type_id,
1204                &val.span,
1205            )
1206            .map_err(|_| ConstEvalError::CompileError)?;
1207            let c = ConstantContent {
1208                ty: Type::get_uint64(lookup.context),
1209                value: ConstantValue::Uint(ir_type.size(lookup.context).in_bytes()),
1210            };
1211            Ok(Some(Constant::unique(lookup.context, c)))
1212        }
1213        Intrinsic::SizeOfStr => {
1214            let targ = &intrinsic.type_arguments[0];
1215            let ir_type = convert_resolved_type_id(
1216                lookup.engines,
1217                lookup.context,
1218                lookup.md_mgr,
1219                lookup.module,
1220                lookup.function_compiler,
1221                targ.type_id(),
1222                &targ.span(),
1223            )
1224            .map_err(|_| ConstEvalError::CompileError)?;
1225            let c = ConstantContent {
1226                ty: Type::get_uint64(lookup.context),
1227                value: ConstantValue::Uint(
1228                    ir_type.get_string_len(lookup.context).unwrap_or_default(),
1229                ),
1230            };
1231            Ok(Some(Constant::unique(lookup.context, c)))
1232        }
1233        Intrinsic::AssertIsStrArray => {
1234            let targ = &intrinsic.type_arguments[0];
1235            let ir_type = convert_resolved_type_id(
1236                lookup.engines,
1237                lookup.context,
1238                lookup.md_mgr,
1239                lookup.module,
1240                lookup.function_compiler,
1241                targ.type_id(),
1242                &targ.span(),
1243            )
1244            .map_err(|_| ConstEvalError::CompileError)?;
1245            match ir_type.get_content(lookup.context) {
1246                TypeContent::StringSlice | TypeContent::StringArray(_) => {
1247                    let c = ConstantContent {
1248                        ty: Type::get_unit(lookup.context),
1249                        value: ConstantValue::Unit,
1250                    };
1251                    Ok(Some(Constant::unique(lookup.context, c)))
1252                }
1253                _ => Err(ConstEvalError::CompileError),
1254            }
1255        }
1256        Intrinsic::ToStrArray => {
1257            assert!(args.len() == 1);
1258            match &args[0].get_content(lookup.context).value {
1259                ConstantValue::String(s) => {
1260                    let c = ConstantContent::new_string(lookup.context, s.to_vec());
1261                    Ok(Some(Constant::unique(lookup.context, c)))
1262                }
1263                _ => {
1264                    unreachable!("Type checker allowed non string value for ToStrArray")
1265                }
1266            }
1267        }
1268        Intrinsic::Eq => {
1269            assert!(args.len() == 2);
1270            let c = ConstantContent {
1271                ty: Type::get_bool(lookup.context),
1272                value: ConstantValue::Bool(args[0] == args[1]),
1273            };
1274            Ok(Some(Constant::unique(lookup.context, c)))
1275        }
1276        Intrinsic::Gt => match (
1277            &args[0].get_content(lookup.context).value,
1278            &args[1].get_content(lookup.context).value,
1279        ) {
1280            (ConstantValue::Uint(val1), ConstantValue::Uint(val2)) => {
1281                let c = ConstantContent {
1282                    ty: Type::get_bool(lookup.context),
1283                    value: ConstantValue::Bool(val1 > val2),
1284                };
1285                Ok(Some(Constant::unique(lookup.context, c)))
1286            }
1287            (ConstantValue::U256(val1), ConstantValue::U256(val2)) => {
1288                let c = ConstantContent {
1289                    ty: Type::get_bool(lookup.context),
1290                    value: ConstantValue::Bool(val1 > val2),
1291                };
1292                Ok(Some(Constant::unique(lookup.context, c)))
1293            }
1294            _ => {
1295                unreachable!("Type checker allowed non integer value for GreaterThan")
1296            }
1297        },
1298        Intrinsic::Lt => match (
1299            &args[0].get_content(lookup.context).value,
1300            &args[1].get_content(lookup.context).value,
1301        ) {
1302            (ConstantValue::Uint(val1), ConstantValue::Uint(val2)) => {
1303                let c = ConstantContent {
1304                    ty: Type::get_bool(lookup.context),
1305                    value: ConstantValue::Bool(val1 < val2),
1306                };
1307                Ok(Some(Constant::unique(lookup.context, c)))
1308            }
1309            (ConstantValue::U256(val1), ConstantValue::U256(val2)) => {
1310                let c = ConstantContent {
1311                    ty: Type::get_bool(lookup.context),
1312                    value: ConstantValue::Bool(val1 < val2),
1313                };
1314                Ok(Some(Constant::unique(lookup.context, c)))
1315            }
1316            _ => {
1317                unreachable!("Type checker allowed non integer value for LessThan")
1318            }
1319        },
1320        Intrinsic::AddrOf
1321        | Intrinsic::PtrAdd
1322        | Intrinsic::PtrSub
1323        | Intrinsic::IsReferenceType
1324        | Intrinsic::IsStrArray
1325        | Intrinsic::Gtf
1326        | Intrinsic::StateClear
1327        | Intrinsic::StateLoadWord
1328        | Intrinsic::StateStoreWord
1329        | Intrinsic::StateLoadQuad
1330        | Intrinsic::StateStoreQuad
1331        | Intrinsic::Log
1332        | Intrinsic::Revert
1333        | Intrinsic::JmpMem
1334        | Intrinsic::Smo => Err(ConstEvalError::CannotBeEvaluatedToConst {
1335            span: intrinsic.span.clone(),
1336        }),
1337        Intrinsic::Not => {
1338            // `not` works only with uint/u256/b256 at the moment
1339            // `bool` ops::Not implementation uses `__eq`.
1340
1341            assert!(args.len() == 1);
1342            assert!(
1343                args[0]
1344                    .get_content(lookup.context)
1345                    .ty
1346                    .is_uint(lookup.context)
1347                    || args[0]
1348                        .get_content(lookup.context)
1349                        .ty
1350                        .is_b256(lookup.context)
1351            );
1352
1353            let Some(arg) = args.into_iter().next() else {
1354                unreachable!("Unexpected 'not' without any arguments");
1355            };
1356
1357            let c = match &arg.get_content(lookup.context).value {
1358                ConstantValue::Uint(n) => {
1359                    let n = match arg
1360                        .get_content(lookup.context)
1361                        .ty
1362                        .get_uint_width(lookup.context)
1363                    {
1364                        Some(8) => !(*n as u8) as u64,
1365                        Some(16) => !(*n as u16) as u64,
1366                        Some(32) => !(*n as u32) as u64,
1367                        Some(64) => !n,
1368                        _ => unreachable!("Invalid unsigned integer width"),
1369                    };
1370                    Ok(Some(ConstantContent {
1371                        ty: arg.get_content(lookup.context).ty,
1372                        value: ConstantValue::Uint(n),
1373                    }))
1374                }
1375                ConstantValue::U256(n) => Ok(Some(ConstantContent {
1376                    ty: arg.get_content(lookup.context).ty,
1377                    value: ConstantValue::U256(n.not()),
1378                })),
1379                ConstantValue::B256(v) => Ok(Some(ConstantContent {
1380                    ty: arg.get_content(lookup.context).ty,
1381                    value: ConstantValue::B256(v.not()),
1382                })),
1383                _ => {
1384                    unreachable!("Type checker allowed non integer value for Not");
1385                }
1386            };
1387            c.map(|c| c.map(|c| Constant::unique(lookup.context, c)))
1388        }
1389        Intrinsic::ContractCall | Intrinsic::ContractRet => {
1390            Err(ConstEvalError::CannotBeEvaluatedToConst {
1391                span: intrinsic.span.clone(),
1392            })
1393        }
1394        Intrinsic::EncodeBufferEmpty => Ok(Some(to_encode_buffer(lookup, vec![], 0))),
1395        Intrinsic::EncodeBufferAppend => {
1396            assert!(args.len() == 2);
1397
1398            let (slice, mut len) = as_encode_buffer(lookup.context, &args[0]).unwrap();
1399            let mut bytes = slice.clone();
1400
1401            use ConstantValue::*;
1402            match &args[1].get_content(lookup.context).value {
1403                Bool(v) => {
1404                    bytes.extend(if *v { [1] } else { [0] });
1405                    len += 1;
1406                    Ok(Some(to_encode_buffer(lookup, bytes, len)))
1407                }
1408                Uint(v) => {
1409                    match &*lookup.engines.te().get(intrinsic.arguments[1].return_type) {
1410                        TypeInfo::UnsignedInteger(IntegerBits::Eight) => {
1411                            bytes.extend((*v as u8).to_be_bytes());
1412                            len += 1;
1413                        }
1414                        TypeInfo::UnsignedInteger(IntegerBits::Sixteen) => {
1415                            bytes.extend((*v as u16).to_be_bytes());
1416                            len += 2;
1417                        }
1418                        TypeInfo::UnsignedInteger(IntegerBits::ThirtyTwo) => {
1419                            bytes.extend((*v as u32).to_be_bytes());
1420                            len += 4;
1421                        }
1422                        TypeInfo::UnsignedInteger(IntegerBits::SixtyFour) => {
1423                            bytes.extend(v.to_be_bytes());
1424                            len += 8;
1425                        }
1426                        _ => {
1427                            return Err(ConstEvalError::CannotBeEvaluatedToConst {
1428                                span: intrinsic.span.clone(),
1429                            });
1430                        }
1431                    };
1432                    Ok(Some(to_encode_buffer(lookup, bytes, len)))
1433                }
1434                U256(v) => {
1435                    bytes.extend(v.to_be_bytes());
1436                    len += 32;
1437                    Ok(Some(to_encode_buffer(lookup, bytes, len)))
1438                }
1439                B256(v) => {
1440                    bytes.extend(v.to_be_bytes());
1441                    len += 32;
1442                    Ok(Some(to_encode_buffer(lookup, bytes, len)))
1443                }
1444                String(v) => {
1445                    if let TypeInfo::StringSlice =
1446                        &*lookup.engines.te().get(intrinsic.arguments[1].return_type)
1447                    {
1448                        let l = v.len() as u64;
1449                        bytes.extend(l.to_be_bytes());
1450                        len += 8;
1451                    }
1452
1453                    bytes.extend(v);
1454                    len += v.len() as u64;
1455
1456                    Ok(Some(to_encode_buffer(lookup, bytes, len)))
1457                }
1458                _ => Err(ConstEvalError::CannotBeEvaluatedToConst {
1459                    span: intrinsic.span.clone(),
1460                }),
1461            }
1462        }
1463        Intrinsic::EncodeBufferAsRawSlice => {
1464            assert!(args.len() == 1);
1465
1466            let (slice, len) = as_encode_buffer(lookup.context, &args[0]).unwrap();
1467            let bytes = slice.clone();
1468
1469            let c = ConstantContent {
1470                ty: Type::get_slice(lookup.context),
1471                value: ConstantValue::RawUntypedSlice(bytes[0..(len as usize)].to_vec()),
1472            };
1473            Ok(Some(Constant::unique(lookup.context, c)))
1474        }
1475        Intrinsic::Slice => {
1476            let start = args[1]
1477                .get_content(lookup.context)
1478                .as_uint()
1479                .expect("Type check allowed non u64") as usize;
1480            let end = args[2]
1481                .get_content(lookup.context)
1482                .as_uint()
1483                .expect("Type check allowed non u64") as usize;
1484
1485            match &args[0].get_content(lookup.context).value {
1486                ConstantValue::Array(elements) => {
1487                    let slice = elements
1488                        .get(start..end)
1489                        .ok_or(ConstEvalError::CompileError)?;
1490                    let elem_type = args[0]
1491                        .get_content(lookup.context)
1492                        .ty
1493                        .get_array_elem_type(lookup.context)
1494                        .expect("unexpected non array");
1495                    let s = slice.to_vec();
1496                    let c = ConstantContent {
1497                        ty: Type::get_typed_slice(lookup.context, elem_type),
1498                        value: ConstantValue::Slice(s),
1499                    };
1500                    Ok(Some(Constant::unique(lookup.context, c)))
1501                }
1502                ConstantValue::Reference(r) => match &r.value {
1503                    ConstantValue::Slice(elements) => {
1504                        let slice = elements
1505                            .get(start..end)
1506                            .ok_or(ConstEvalError::CompileError)?;
1507                        let elem_type = args[0]
1508                            .get_content(lookup.context)
1509                            .ty
1510                            .get_typed_slice_elem_type(lookup.context)
1511                            .expect("unexpected non slice");
1512                        let s = slice.to_vec();
1513                        let c = ConstantContent {
1514                            ty: Type::get_typed_slice(lookup.context, elem_type),
1515                            value: ConstantValue::Slice(s),
1516                        };
1517                        Ok(Some(Constant::unique(lookup.context, c)))
1518                    }
1519                    _ => Err(ConstEvalError::CannotBeEvaluatedToConst {
1520                        span: intrinsic.span.clone(),
1521                    }),
1522                },
1523                _ => Err(ConstEvalError::CannotBeEvaluatedToConst {
1524                    span: intrinsic.span.clone(),
1525                }),
1526            }
1527        }
1528        Intrinsic::ElemAt => {
1529            let idx = args[1]
1530                .get_content(lookup.context)
1531                .as_uint()
1532                .expect("Type check allowed non u64") as usize;
1533
1534            match &args[0].get_content(lookup.context).value {
1535                ConstantValue::Reference(r) => match &r.value {
1536                    ConstantValue::Slice(elements) => {
1537                        let v = elements[idx].clone();
1538                        let c = ConstantContent {
1539                            ty: Type::new_typed_pointer(lookup.context, v.ty),
1540                            value: ConstantValue::Reference(Box::new(v)),
1541                        };
1542                        Ok(Some(Constant::unique(lookup.context, c)))
1543                    }
1544                    _ => Err(ConstEvalError::CannotBeEvaluatedToConst {
1545                        span: intrinsic.span.clone(),
1546                    }),
1547                },
1548                _ => Err(ConstEvalError::CannotBeEvaluatedToConst {
1549                    span: intrinsic.span.clone(),
1550                }),
1551            }
1552        }
1553        Intrinsic::Transmute => {
1554            let src_type = &intrinsic.type_arguments[0];
1555            let src_ir_type = convert_resolved_type_id(
1556                lookup.engines,
1557                lookup.context,
1558                lookup.md_mgr,
1559                lookup.module,
1560                lookup.function_compiler,
1561                src_type.type_id(),
1562                &src_type.span(),
1563            )
1564            .unwrap();
1565
1566            let dst_type = &intrinsic.type_arguments[1];
1567            let dst_ir_type = convert_resolved_type_id(
1568                lookup.engines,
1569                lookup.context,
1570                lookup.md_mgr,
1571                lookup.module,
1572                lookup.function_compiler,
1573                dst_type.type_id(),
1574                &dst_type.span(),
1575            )
1576            .unwrap();
1577
1578            // check IR sizes match
1579            let src_ir_type_in_bytes = src_ir_type.size(lookup.context).in_bytes();
1580            let dst_ir_type_in_bytes = dst_ir_type.size(lookup.context).in_bytes();
1581            if src_ir_type_in_bytes != dst_ir_type_in_bytes {
1582                return Err(ConstEvalError::CompileError);
1583            }
1584
1585            fn append_bytes(
1586                ctx: &Context<'_>,
1587                bytes: &mut Vec<u8>,
1588                t: &Type,
1589                value: &ConstantValue,
1590            ) -> Result<(), ConstEvalError> {
1591                match t.get_content(ctx) {
1592                    TypeContent::Struct(fields) => match value {
1593                        ConstantValue::Struct(constants) => {
1594                            for (field_type, field) in fields.iter().zip(constants.iter()) {
1595                                append_bytes(ctx, bytes, field_type, &field.value)?;
1596                            }
1597                        }
1598                        _ => unreachable!(),
1599                    },
1600                    TypeContent::Array(item_type, size) => match value {
1601                        ConstantValue::Array(items) => {
1602                            assert!(*size as usize == items.len());
1603                            for item in items {
1604                                append_bytes(ctx, bytes, item_type, &item.value)?;
1605                            }
1606                        }
1607                        _ => unreachable!(),
1608                    },
1609                    TypeContent::Uint(8) => match value {
1610                        ConstantValue::Uint(v) => {
1611                            bytes.extend((*v as u8).to_be_bytes());
1612                        }
1613                        _ => unreachable!(),
1614                    },
1615                    TypeContent::Uint(16) => match value {
1616                        ConstantValue::Uint(v) => {
1617                            bytes.extend([0u8, 0u8, 0u8, 0u8, 0u8, 0u8]);
1618                            bytes.extend((*v as u16).to_be_bytes());
1619                        }
1620                        _ => unreachable!(),
1621                    },
1622                    TypeContent::Uint(32) => match value {
1623                        ConstantValue::Uint(v) => {
1624                            bytes.extend([0u8, 0u8, 0u8, 0u8]);
1625                            bytes.extend((*v as u32).to_be_bytes());
1626                        }
1627                        _ => unreachable!(),
1628                    },
1629                    TypeContent::Uint(64) => match value {
1630                        ConstantValue::Uint(v) => {
1631                            bytes.extend((*v).to_be_bytes());
1632                        }
1633                        _ => unreachable!(),
1634                    },
1635                    _ => return Err(ConstEvalError::CompileError),
1636                }
1637                Ok(())
1638            }
1639
1640            fn transmute_bytes(
1641                ctx: &Context<'_>,
1642                bytes: &mut std::io::Cursor<Vec<u8>>,
1643                t: &Type,
1644            ) -> Result<ConstantContent, ConstEvalError> {
1645                Ok(match t.get_content(ctx) {
1646                    TypeContent::Uint(8) => {
1647                        let mut buffer = [0u8];
1648                        let _ = bytes.read_exact(&mut buffer);
1649                        ConstantContent {
1650                            ty: Type::get_uint8(ctx),
1651                            value: ConstantValue::Uint(buffer[0] as u64),
1652                        }
1653                    }
1654                    TypeContent::Uint(16) => {
1655                        let mut buffer = [0u8; 8]; // u16 = u64 at runtime
1656                        let _ = bytes.read_exact(&mut buffer);
1657                        let buffer = [buffer[6], buffer[7]];
1658                        ConstantContent {
1659                            ty: Type::get_uint16(ctx),
1660                            value: ConstantValue::Uint(u16::from_be_bytes(buffer) as u64),
1661                        }
1662                    }
1663                    TypeContent::Uint(32) => {
1664                        let mut buffer = [0u8; 8]; // u32 = u64 at runtime
1665                        let _ = bytes.read_exact(&mut buffer);
1666                        let buffer = [buffer[4], buffer[5], buffer[6], buffer[7]];
1667                        ConstantContent {
1668                            ty: Type::get_uint32(ctx),
1669                            value: ConstantValue::Uint(u32::from_be_bytes(buffer) as u64),
1670                        }
1671                    }
1672                    TypeContent::Uint(64) => {
1673                        let mut buffer = [0u8; 8];
1674                        let _ = bytes.read_exact(&mut buffer);
1675                        ConstantContent {
1676                            ty: Type::get_uint64(ctx),
1677                            value: ConstantValue::Uint(u64::from_be_bytes(buffer)),
1678                        }
1679                    }
1680                    _ => return Err(ConstEvalError::CompileError),
1681                })
1682            }
1683
1684            let mut runtime_bytes = vec![];
1685            append_bytes(
1686                lookup.context,
1687                &mut runtime_bytes,
1688                &src_ir_type,
1689                &args[0].get_content(lookup.context).value,
1690            )?;
1691            let mut cursor = std::io::Cursor::new(runtime_bytes);
1692            let c = transmute_bytes(lookup.context, &mut cursor, &dst_ir_type)?;
1693            Ok(Some(Constant::unique(lookup.context, c)))
1694        }
1695        Intrinsic::Dbg => {
1696            unreachable!("__dbg should not exist in the typed tree")
1697        }
1698        Intrinsic::RuntimeMemoryId => {
1699            assert!(intrinsic.type_arguments.len() == 1);
1700            assert!(intrinsic.arguments.is_empty());
1701
1702            let t = &intrinsic.type_arguments[0];
1703            let t = convert_resolved_type_id(
1704                lookup.engines,
1705                lookup.context,
1706                lookup.md_mgr,
1707                lookup.module,
1708                lookup.function_compiler,
1709                t.type_id(),
1710                &t.span(),
1711            )
1712            .unwrap();
1713
1714            let id = get_memory_id(lookup.context, t);
1715            let c = ConstantContent {
1716                ty: Type::get_uint64(lookup.context),
1717                value: ConstantValue::Uint(id),
1718            };
1719
1720            Ok(Some(Constant::unique(lookup.context, c)))
1721        }
1722        Intrinsic::EncodingMemoryId => {
1723            assert!(intrinsic.type_arguments.len() == 1);
1724            assert!(intrinsic.arguments.is_empty());
1725
1726            let t = intrinsic.type_arguments[0].as_type_argument().unwrap();
1727
1728            let r = get_encoding_representation(lookup.engines, t.type_id);
1729
1730            use std::hash::Hasher;
1731            let mut state = DefaultHasher::default();
1732            r.hash(&mut state);
1733            let id = state.finish();
1734
1735            let c = ConstantContent {
1736                ty: Type::get_uint64(lookup.context),
1737                value: ConstantValue::Uint(id),
1738            };
1739
1740            Ok(Some(Constant::unique(lookup.context, c)))
1741        }
1742    }
1743}
1744
1745#[cfg(test)]
1746mod tests {
1747    use super::*;
1748    use sway_error::handler::Handler;
1749    use sway_features::ExperimentalFeatures;
1750    use sway_ir::{Backtrace, Kind};
1751    use sway_types::ProgramId;
1752
1753    /// This function validates if an expression can be converted to [Constant].
1754    ///
1755    /// The flag `is_constant` is used to define if the expression should be convertible or not.
1756    /// `prefix` is any valid code at top level, useful to declare types.
1757    ///
1758    /// Example:
1759    ///
1760    /// ```rust,ignore
1761    /// assert_is_constant(true, "enum Color { Blue: u64 }", "Color::Blue(1)");
1762    /// assert_is_constant(false, "", "{return 1; 1}");
1763    /// ```
1764    ///
1765    /// It DOES NOT have access to the std lib, and constants, and other features that demand full compilation.
1766    fn assert_is_constant(is_constant: bool, prefix: &str, expr: &str) {
1767        let engines = Engines::default();
1768        let handler = Handler::default();
1769        let mut context = Context::new(
1770            engines.se(),
1771            ExperimentalFeatures::default(),
1772            Backtrace::default(),
1773        );
1774        let mut md_mgr = MetadataManager::default();
1775        let core_lib = namespace::Package::new(
1776            sway_types::Ident::new_no_span("assert_is_constant_test".to_string()),
1777            None,
1778            ProgramId::new(0),
1779            false,
1780        );
1781
1782        let r = crate::compile_to_ast(
1783            &handler,
1784            &engines,
1785            format!("library; {prefix} fn f() -> u64 {{ {expr}; 0 }}")
1786                .as_str()
1787                .into(),
1788            core_lib,
1789            None,
1790            "test",
1791            None,
1792            ExperimentalFeatures::default(),
1793        );
1794
1795        let (errors, _warnings, _infos) = handler.consume();
1796
1797        if !errors.is_empty() {
1798            panic!("{errors:#?}");
1799        }
1800
1801        let f = r.unwrap();
1802        let f = f.typed.unwrap();
1803
1804        let f = f
1805            .declarations
1806            .iter()
1807            .find_map(|x| match x {
1808                ty::TyDecl::FunctionDecl(x) => {
1809                    if engines.de().get_function(&x.decl_id).name.as_str() == "f" {
1810                        Some(x)
1811                    } else {
1812                        None
1813                    }
1814                }
1815                _ => None,
1816            })
1817            .expect("An function named `f` was not found.");
1818
1819        let f = engines.de().get_function(&f.decl_id);
1820        let expr_under_test = f.body.contents.first().unwrap();
1821
1822        let expr_under_test = match &expr_under_test.content {
1823            ty::TyAstNodeContent::Expression(expr_under_test) => expr_under_test.clone(),
1824            ty::TyAstNodeContent::Declaration(crate::language::ty::TyDecl::ConstantDecl(decl)) => {
1825                let decl = engines.de().get_constant(&decl.decl_id);
1826                decl.value.clone().unwrap()
1827            }
1828            x => todo!("{x:?}"),
1829        };
1830
1831        let module = Module::new(&mut context, Kind::Library);
1832        let actual_constant = compile_constant_expression_to_constant(
1833            &engines,
1834            &mut context,
1835            &mut md_mgr,
1836            module,
1837            None,
1838            None,
1839            &expr_under_test,
1840        );
1841
1842        match (is_constant, actual_constant) {
1843            (true, Ok(_)) => {}
1844            (true, Err(err)) => {
1845                panic!("Expression cannot be converted to constant: {expr:?}\nPrefix: {prefix:?}\nExpr:{expr_under_test:#?}\nError: {err:#?}");
1846            }
1847            (false, Ok(constant)) => {
1848                panic!("Expression unexpectedly can be converted to constant: {expr:?}\nPrefix: {prefix:?}\nExpr:{expr_under_test:#?}\nConstant: {constant:#?}");
1849            }
1850            (false, Err(_)) => {}
1851        }
1852    }
1853
1854    #[test]
1855    fn const_eval_test() {
1856        // Expressions that can be converted to constant
1857        assert_is_constant(true, "", "1");
1858        assert_is_constant(true, "", "true");
1859        assert_is_constant(true, "fn one() -> u64 { 1 }", "one()");
1860        assert_is_constant(true, "fn id(x: u64) -> u64 { x }", "id(1)");
1861        assert_is_constant(true, "enum Color { Blue: () }", "Color::Blue");
1862        assert_is_constant(true, "enum Color { Blue: u64 }", "Color::Blue(1)");
1863        assert_is_constant(true, "struct Person { age: u64 }", "Person { age: 1 }");
1864        assert_is_constant(true, "struct Person { age: u64 }", "Person { age: 1 }.age");
1865        assert_is_constant(
1866            true,
1867            "struct Person { age: u64 }",
1868            "Person { age: { let mut x = 0; x = 1; 1 } }",
1869        );
1870        assert_is_constant(true, "", "if true { 1 } else { 0 }");
1871        assert_is_constant(true, "", "(0,1).0");
1872        assert_is_constant(true, "", "[0,1][0]");
1873
1874        // u256
1875        assert_is_constant(
1876            true,
1877            "",
1878            "0x0000000000000000000000000000000000000000000000000000000000000001u256",
1879        );
1880        assert_is_constant(
1881            true,
1882            "",
1883            "__add(0x0000000000000000000000000000000000000000000000000000000000000001u256, 0x0000000000000000000000000000000000000000000000000000000000000001u256)",
1884        );
1885        assert_is_constant(
1886            true,
1887            "",
1888            "__eq(0x0000000000000000000000000000000000000000000000000000000000000001u256, 0x0000000000000000000000000000000000000000000000000000000000000001u256)",
1889        );
1890        assert_is_constant(
1891            true,
1892            "",
1893            "__gt(0x0000000000000000000000000000000000000000000000000000000000000001u256, 0x0000000000000000000000000000000000000000000000000000000000000001u256)",
1894        );
1895        assert_is_constant(
1896            true,
1897            "",
1898            "__lt(0x0000000000000000000000000000000000000000000000000000000000000001u256, 0x0000000000000000000000000000000000000000000000000000000000000001u256)",
1899        );
1900        assert_is_constant(
1901            true,
1902            "",
1903            "__lsh(0x0000000000000000000000000000000000000000000000000000000000000001u256, 2)",
1904        );
1905        assert_is_constant(
1906            true,
1907            "",
1908            "__not(0x0000000000000000000000000000000000000000000000000000000000000001u256)",
1909        );
1910
1911        // Expressions that cannot be converted to constant
1912        assert_is_constant(false, "", "{ return 1; }");
1913        assert_is_constant(false, "", "{ return 1; 1}");
1914        assert_is_constant(
1915            false,
1916            "enum Color { Blue: u64 }",
1917            "Color::Blue({ return 1; 1})",
1918        );
1919        assert_is_constant(
1920            false,
1921            "struct Person { age: u64 }",
1922            "Person { age: { return 1; 1} }",
1923        );
1924
1925        // At the moment this is not constant because of the "return"
1926        assert_is_constant(false, "fn id(x: u64) -> u64 { return x; }", "id(1)");
1927        assert_is_constant(false, "", "[0,1][2]");
1928        assert_is_constant(
1929            false,
1930            "enum Color { Blue: u64 }",
1931            "Color::Blue({return 1;})",
1932        );
1933
1934        // Code blocks that can be converted to constants
1935        assert_is_constant(true, "", "{ 1 }");
1936        assert_is_constant(true, "", "{ let a = 1; a }");
1937        assert_is_constant(true, "", "{ const a = 1; a }");
1938        assert_is_constant(true, "", "{ struct A {} 1 }");
1939        assert_is_constant(true, "fn id(x: u64) -> u64 { { let x = 2; }; x }", "id(1)");
1940
1941        // Code blocks that cannot be converted to constants
1942        assert_is_constant(false, "", "{ let a = 1; }");
1943        assert_is_constant(false, "", "{ const a = 1; }");
1944        assert_is_constant(false, "", "{ struct A {} }");
1945        assert_is_constant(false, "", "{ return 1; 1 }");
1946        assert_is_constant(false, "", "{ }");
1947        assert_is_constant(false, "fn id(x: u64) -> u64 { { return 1; }; x }", "id(1)");
1948    }
1949}