sway_core/ir_generation/
const_eval.rs

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