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 #[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 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 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 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 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 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 let te = lookup.engines.te();
269 assert!({
270 let unify_check = UnifyCheck::coercion(lookup.engines);
271 element_types
272 .iter()
273 .all(|tid| unify_check.check(*tid, elem_type))
274 });
275
276 let arr = create_array_aggregate(
277 te,
278 lookup.engines.de(),
279 lookup.context,
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
297fn 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 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 {
361 decl: const_decl, ..
362 } => {
363 let call_path = &const_decl.call_path;
364 let name = &call_path.suffix;
365
366 match known_consts.get(name) {
367 Some(cvs) => Some(*cvs),
369 None => {
370 (lookup.lookup)(lookup, call_path, &Some(*const_decl.clone()))
372 .ok()
373 .flatten()
374 .and_then(|v| v.get_constant(lookup.context).cloned())
375 }
376 }
377 }
378 ty::TyExpressionVariant::ConfigurableExpression { span, .. } => {
379 return Err(ConstEvalError::CannotBeEvaluatedToConst { span: span.clone() });
380 }
381 ty::TyExpressionVariant::VariableExpression {
382 name, call_path, ..
383 } => match known_consts.get(name) {
384 Some(cvs) => Some(*cvs),
386 None => {
387 let call_path = match call_path {
388 Some(call_path) => call_path.clone(),
389 None => CallPath::from(name.clone()),
390 };
391 (lookup.lookup)(lookup, &call_path, &None)
393 .ok()
394 .flatten()
395 .and_then(|v| v.get_constant(lookup.context).cloned())
396 }
397 },
398 ty::TyExpressionVariant::StructExpression {
399 fields,
400 instantiation_span,
401 ..
402 } => {
403 let (mut field_types, mut field_vals): (Vec<_>, Vec<_>) = (vec![], vec![]);
404
405 for field in fields {
406 let ty::TyStructExpressionField { name: _, value, .. } = field;
407 let eval_expr_opt = const_eval_typed_expr(lookup, known_consts, value)?;
408 if let Some(cv) = eval_expr_opt {
409 field_types.push(value.return_type);
410 field_vals.push(cv);
411 } else {
412 return Err(ConstEvalError::CannotBeEvaluatedToConst {
413 span: instantiation_span.clone(),
414 });
415 }
416 }
417
418 assert!(field_types.len() == fields.len());
419 assert!(field_vals.len() == fields.len());
420
421 get_struct_for_types(
422 lookup.engines.te(),
423 lookup.engines.de(),
424 lookup.context,
425 &field_types,
426 )
427 .map_or(None, |struct_ty| {
428 let c = ConstantContent::new_struct(
429 lookup.context,
430 struct_ty.get_field_types(lookup.context),
431 field_vals
432 .iter()
433 .map(|fv| fv.get_content(lookup.context).clone())
434 .collect(),
435 );
436 let c = Constant::unique(lookup.context, c);
437 Some(c)
438 })
439 }
440 ty::TyExpressionVariant::Tuple { fields } => {
441 let (mut field_types, mut field_vals): (Vec<_>, Vec<_>) = (vec![], vec![]);
442
443 for value in fields {
444 let eval_expr_opt = const_eval_typed_expr(lookup, known_consts, value)?;
445 if let Some(cv) = eval_expr_opt {
446 field_types.push(value.return_type);
447 field_vals.push(cv);
448 } else {
449 return Err(ConstEvalError::CannotBeEvaluatedToConst {
450 span: expr.span.clone(),
451 });
452 }
453 }
454
455 assert!(field_types.len() == fields.len());
456 assert!(field_vals.len() == fields.len());
457
458 create_tuple_aggregate(
459 lookup.engines.te(),
460 lookup.engines.de(),
461 lookup.context,
462 &field_types,
463 )
464 .map_or(None, |tuple_ty| {
465 let c = ConstantContent::new_struct(
466 lookup.context,
467 tuple_ty.get_field_types(lookup.context),
468 field_vals
469 .iter()
470 .map(|fv| fv.get_content(lookup.context).clone())
471 .collect(),
472 );
473 let c = Constant::unique(lookup.context, c);
474 Some(c)
475 })
476 }
477 ty::TyExpressionVariant::ArrayExplicit {
478 elem_type,
479 contents,
480 } => {
481 let (mut element_types, mut element_vals): (Vec<_>, Vec<_>) = (vec![], vec![]);
482
483 for value in contents {
484 let eval_expr_opt = const_eval_typed_expr(lookup, known_consts, value)?;
485 if let Some(cv) = eval_expr_opt {
486 element_types.push(value.return_type);
487 element_vals.push(cv);
488 } else {
489 return Err(ConstEvalError::CannotBeEvaluatedToConst {
490 span: expr.span.clone(),
491 });
492 }
493 }
494
495 assert!(element_types.len() == contents.len());
496 assert!(element_vals.len() == contents.len());
497
498 create_array_from_vec(lookup, *elem_type, element_types, element_vals)
499 }
500 ty::TyExpressionVariant::ArrayRepeat {
501 elem_type,
502 value,
503 length,
504 } => {
505 let constant = const_eval_typed_expr(lookup, known_consts, value)?.unwrap();
506 let length = const_eval_typed_expr(lookup, known_consts, length)?
507 .unwrap()
508 .get_content(lookup.context)
509 .as_uint()
510 .unwrap() as usize;
511 let element_vals = (0..length).map(|_| constant).collect::<Vec<_>>();
512 let element_types = (0..length).map(|_| value.return_type).collect::<Vec<_>>();
513
514 assert!(element_types.len() == length);
515 assert!(element_vals.len() == length);
516
517 create_array_from_vec(lookup, *elem_type, element_types, element_vals)
518 }
519 ty::TyExpressionVariant::EnumInstantiation {
520 enum_ref,
521 tag,
522 contents,
523 variant_instantiation_span,
524 ..
525 } => {
526 let enum_decl = lookup.engines.de().get_enum(enum_ref);
527 let aggregate = create_tagged_union_type(
528 lookup.engines.te(),
529 lookup.engines.de(),
530 lookup.context,
531 &enum_decl.variants,
532 );
533
534 if let Ok(enum_ty) = aggregate {
535 let tag_value = ConstantContent::new_uint(lookup.context, 64, *tag as u64);
536 let mut fields: Vec<ConstantContent> = vec![tag_value];
537
538 match contents {
539 None => fields.push(ConstantContent::new_unit(lookup.context)),
540 Some(subexpr) => match const_eval_typed_expr(lookup, known_consts, subexpr)? {
541 Some(constant) => fields.push(constant.get_content(lookup.context).clone()),
542 None => {
543 return Err(ConstEvalError::CannotBeEvaluatedToConst {
544 span: variant_instantiation_span.clone(),
545 });
546 }
547 },
548 }
549
550 let fields_tys = enum_ty.get_field_types(lookup.context);
551 let c = ConstantContent::new_struct(lookup.context, fields_tys, fields);
552 let c = Constant::unique(lookup.context, c);
553 Some(c)
554 } else {
555 return Err(ConstEvalError::CannotBeEvaluatedToConst {
556 span: expr.span.clone(),
557 });
558 }
559 }
560 ty::TyExpressionVariant::StructFieldAccess {
561 prefix,
562 field_to_access,
563 resolved_type_of_parent,
564 ..
565 } => match const_eval_typed_expr(lookup, known_consts, prefix)?
566 .map(|c| c.get_content(lookup.context).clone())
567 {
568 Some(ConstantContent {
569 value: ConstantValue::Struct(fields),
570 ..
571 }) => {
572 let field_kind = ty::ProjectionKind::StructField {
573 name: field_to_access.name.clone(),
574 field_to_access: Some(Box::new(field_to_access.clone())),
575 };
576 get_struct_name_field_index_and_type(
577 lookup.engines.te(),
578 lookup.engines.de(),
579 *resolved_type_of_parent,
580 field_kind,
581 )
582 .and_then(|(_struct_name, field_idx_and_type_opt)| {
583 field_idx_and_type_opt.map(|(field_idx, _field_type)| field_idx)
584 })
585 .and_then(|field_idx| {
586 fields
587 .get(field_idx as usize)
588 .cloned()
589 .map(|c| Constant::unique(lookup.context, c))
590 })
591 }
592 _ => {
593 return Err(ConstEvalError::CannotBeEvaluatedToConst {
594 span: expr.span.clone(),
595 });
596 }
597 },
598 ty::TyExpressionVariant::TupleElemAccess {
599 prefix,
600 elem_to_access_num,
601 ..
602 } => match const_eval_typed_expr(lookup, known_consts, prefix)?
603 .map(|c| c.get_content(lookup.context))
604 {
605 Some(ConstantContent {
606 value: ConstantValue::Struct(fields),
607 ..
608 }) => fields
609 .get(*elem_to_access_num)
610 .cloned()
611 .map(|c| Constant::unique(lookup.context, c)),
612 _ => {
613 return Err(ConstEvalError::CannotBeEvaluatedToConst {
614 span: expr.span.clone(),
615 });
616 }
617 },
618 ty::TyExpressionVariant::ImplicitReturn(e) => {
619 if let Ok(Some(constant)) = const_eval_typed_expr(lookup, known_consts, e) {
620 Some(constant)
621 } else {
622 return Err(ConstEvalError::CannotBeEvaluatedToConst {
623 span: e.span.clone(),
624 });
625 }
626 }
627 ty::TyExpressionVariant::Return(exp) => {
632 return Err(ConstEvalError::CannotBeEvaluatedToConst {
633 span: exp.span.clone(),
634 });
635 }
636 ty::TyExpressionVariant::MatchExp { desugared, .. } => {
637 const_eval_typed_expr(lookup, known_consts, desugared)?
638 }
639 ty::TyExpressionVariant::IntrinsicFunction(kind) => {
640 const_eval_intrinsic(lookup, known_consts, kind)?
641 }
642 ty::TyExpressionVariant::IfExp {
643 condition,
644 then,
645 r#else,
646 } => {
647 match const_eval_typed_expr(lookup, known_consts, condition)?
648 .map(|c| c.get_content(lookup.context))
649 {
650 Some(ConstantContent {
651 value: ConstantValue::Bool(cond),
652 ..
653 }) => {
654 if *cond {
655 const_eval_typed_expr(lookup, known_consts, then)?
656 } else if let Some(r#else) = r#else {
657 const_eval_typed_expr(lookup, known_consts, r#else)?
658 } else {
659 None
663 }
664 }
665 _ => {
666 return Err(ConstEvalError::CannotBeEvaluatedToConst {
667 span: expr.span.clone(),
668 });
669 }
670 }
671 }
672 ty::TyExpressionVariant::CodeBlock(codeblock) => {
673 const_eval_codeblock(lookup, known_consts, codeblock)?
674 }
675 ty::TyExpressionVariant::ArrayIndex { prefix, index } => {
676 let prefix = const_eval_typed_expr(lookup, known_consts, prefix)?
677 .map(|c| c.get_content(lookup.context).clone());
678 let index = const_eval_typed_expr(lookup, known_consts, index)?
679 .map(|c| c.get_content(lookup.context));
680 match (prefix, index) {
681 (
682 Some(ConstantContent {
683 value: ConstantValue::Array(items),
684 ..
685 }),
686 Some(ConstantContent {
687 value: ConstantValue::Uint(index),
688 ..
689 }),
690 ) => {
691 let count = items.len() as u64;
692 if *index < count {
693 let c = Constant::unique(lookup.context, items[*index as usize].clone());
694 Some(c)
695 } else {
696 return Err(ConstEvalError::CompileError);
697 }
698 }
699 _ => {
700 return Err(ConstEvalError::CannotBeEvaluatedToConst {
701 span: expr.span.clone(),
702 });
703 }
704 }
705 }
706 ty::TyExpressionVariant::Ref(_) => {
707 return Err(ConstEvalError::CompileError);
708 }
709 ty::TyExpressionVariant::Deref(expr) => {
711 let value = expr
712 .as_intrinsic()
713 .filter(|x| matches!(x.kind, Intrinsic::ElemAt))
714 .ok_or(ConstEvalError::CompileError)
715 .and_then(|kind| {
716 const_eval_intrinsic(lookup, known_consts, kind)
717 .map(|c| c.map(|c| c.get_content(lookup.context).clone()))
718 });
719 if let Ok(Some(ConstantContent {
720 value: ConstantValue::Reference(value),
721 ..
722 })) = value
723 {
724 let c = Constant::unique(lookup.context, *value.clone());
725 Some(c)
726 } else {
727 return Err(ConstEvalError::CompileError);
728 }
729 }
730 ty::TyExpressionVariant::EnumTag { exp } => {
731 let value = const_eval_typed_expr(lookup, known_consts, exp)?
732 .map(|x| x.get_content(lookup.context).value.clone());
733 if let Some(ConstantValue::Struct(fields)) = value {
734 Some(Constant::unique(lookup.context, fields[0].clone()))
735 } else {
736 return Err(ConstEvalError::CompileError);
737 }
738 }
739 ty::TyExpressionVariant::UnsafeDowncast { exp, .. } => {
740 let value = const_eval_typed_expr(lookup, known_consts, exp)?
741 .map(|x| x.get_content(lookup.context).value.clone());
742 if let Some(ConstantValue::Struct(fields)) = value {
743 Some(Constant::unique(lookup.context, fields[1].clone()))
744 } else {
745 return Err(ConstEvalError::CompileError);
746 }
747 }
748 ty::TyExpressionVariant::WhileLoop {
749 condition, body, ..
750 } => {
751 let mut limit = 1_000_000;
754
755 while limit >= 0 {
756 limit -= 1;
757
758 let condition = const_eval_typed_expr(lookup, known_consts, condition)?;
759 match condition.map(|x| x.get_content(lookup.context).value.clone()) {
760 Some(ConstantValue::Bool(true)) => {
761 let _ = const_eval_codeblock(lookup, known_consts, body)?;
763 }
764 _ => break,
765 }
766 }
767
768 None
769 }
770 ty::TyExpressionVariant::Reassignment(r) => {
771 let rhs = const_eval_typed_expr(lookup, known_consts, &r.rhs)?.unwrap();
772 match &r.lhs {
773 ty::TyReassignmentTarget::ElementAccess {
774 base_name, indices, ..
775 } => {
776 if !indices.is_empty() {
777 return Err(ConstEvalError::CannotBeEvaluatedToConst {
778 span: expr.span.clone(),
779 });
780 }
781 if let Some(lhs) = known_consts.get_mut(base_name) {
782 *lhs = rhs;
783 return Ok(None);
784 } else {
785 return Err(ConstEvalError::CannotBeEvaluatedToConst {
786 span: expr.span.clone(),
787 });
788 }
789 }
790 ty::TyReassignmentTarget::DerefAccess { .. } => {
791 return Err(ConstEvalError::CannotBeEvaluatedToConst {
792 span: expr.span.clone(),
793 });
794 }
795 }
796 }
797 ty::TyExpressionVariant::FunctionParameter
798 | ty::TyExpressionVariant::AsmExpression { .. }
799 | ty::TyExpressionVariant::LazyOperator { .. }
800 | ty::TyExpressionVariant::AbiCast { .. }
801 | ty::TyExpressionVariant::StorageAccess(_)
802 | ty::TyExpressionVariant::AbiName(_)
803 | ty::TyExpressionVariant::Break
804 | ty::TyExpressionVariant::Continue
805 | ty::TyExpressionVariant::ForLoop { .. } => {
806 return Err(ConstEvalError::CannotBeEvaluatedToConst {
807 span: expr.span.clone(),
808 });
809 }
810 })
811}
812
813fn const_eval_codeblock(
817 lookup: &mut LookupEnv,
818 known_consts: &mut MappedStack<Ident, Constant>,
819 codeblock: &ty::TyCodeBlock,
820) -> Result<Option<Constant>, ConstEvalError> {
821 let mut result: Result<Option<Constant>, ConstEvalError> = Ok(None);
823 let mut bindings: Vec<_> = vec![];
825
826 for ast_node in &codeblock.contents {
827 result = match &ast_node.content {
828 ty::TyAstNodeContent::Declaration(decl @ ty::TyDecl::VariableDecl(var_decl)) => {
829 if let Ok(Some(rhs)) = const_eval_typed_expr(lookup, known_consts, &var_decl.body) {
830 known_consts.push(var_decl.name.clone(), rhs);
831 bindings.push(var_decl.name.clone());
832 Ok(None)
833 } else {
834 Err(ConstEvalError::CannotBeEvaluatedToConst {
835 span: decl.span(lookup.engines).clone(),
836 })
837 }
838 }
839 ty::TyAstNodeContent::Declaration(ty::TyDecl::ConstantDecl(const_decl)) => {
840 let ty_const_decl = lookup.engines.de().get_constant(&const_decl.decl_id);
841 if let Some(constant) = ty_const_decl
842 .value
843 .clone()
844 .and_then(|expr| const_eval_typed_expr(lookup, known_consts, &expr).ok())
845 .flatten()
846 {
847 known_consts.push(ty_const_decl.name().clone(), constant);
848 bindings.push(ty_const_decl.name().clone());
849 Ok(None)
850 } else {
851 Err(ConstEvalError::CannotBeEvaluatedToConst {
852 span: ty_const_decl.span.clone(),
853 })
854 }
855 }
856 ty::TyAstNodeContent::Declaration(_) => Ok(None),
857 ty::TyAstNodeContent::Expression(e) => match e.expression {
858 ty::TyExpressionVariant::ImplicitReturn(_) => {
859 if let Ok(Some(constant)) = const_eval_typed_expr(lookup, known_consts, e) {
860 Ok(Some(constant))
861 } else {
862 Err(ConstEvalError::CannotBeEvaluatedToConst {
863 span: e.span.clone(),
864 })
865 }
866 }
867 _ => {
868 if const_eval_typed_expr(lookup, known_consts, e).is_err() {
869 Err(ConstEvalError::CannotBeEvaluatedToConst {
870 span: e.span.clone(),
871 })
872 } else {
873 Ok(None)
874 }
875 }
876 },
877 ty::TyAstNodeContent::SideEffect(_) => Err(ConstEvalError::CannotBeEvaluatedToConst {
878 span: ast_node.span.clone(),
879 }),
880 ty::TyAstNodeContent::Error(_, _) => Err(ConstEvalError::CannotBeEvaluatedToConst {
881 span: ast_node.span.clone(),
882 }),
883 };
884
885 if result.is_err() {
886 break;
887 }
888 }
889
890 for name in bindings {
892 known_consts.pop(&name)
893 }
894
895 result
896}
897
898fn as_encode_buffer<'a>(context: &'a Context, buffer: &Constant) -> Option<(&'a Vec<u8>, u64)> {
899 match &buffer.get_content(context).value {
900 ConstantValue::Struct(fields) => {
901 let slice = match &fields[0].value {
902 ConstantValue::RawUntypedSlice(bytes) => bytes,
903 _ => return None,
904 };
905 let len = match fields[1].value {
906 ConstantValue::Uint(v) => v,
907 _ => return None,
908 };
909 Some((slice, len))
910 }
911 _ => None,
912 }
913}
914
915fn to_encode_buffer(lookup: &mut LookupEnv, bytes: Vec<u8>, len: u64) -> Constant {
916 let c = ConstantContent {
917 ty: Type::new_struct(
918 lookup.context,
919 vec![
920 Type::get_slice(lookup.context),
921 Type::get_uint64(lookup.context),
922 ],
923 ),
924 value: ConstantValue::Struct(vec![
925 ConstantContent {
926 ty: Type::get_slice(lookup.context),
927 value: ConstantValue::RawUntypedSlice(bytes),
928 },
929 ConstantContent {
930 ty: Type::get_uint64(lookup.context),
931 value: ConstantValue::Uint(len),
932 },
933 ]),
934 };
935 Constant::unique(lookup.context, c)
936}
937
938fn const_eval_intrinsic(
939 lookup: &mut LookupEnv,
940 known_consts: &mut MappedStack<Ident, Constant>,
941 intrinsic: &TyIntrinsicFunctionKind,
942) -> Result<Option<Constant>, ConstEvalError> {
943 let mut args = vec![];
944 for arg in intrinsic.arguments.iter() {
945 if let Ok(Some(constant)) = const_eval_typed_expr(lookup, known_consts, arg) {
946 args.push(constant);
947 } else {
948 return Err(ConstEvalError::CannotBeEvaluatedToConst {
949 span: arg.span.clone(),
950 });
951 }
952 }
953
954 assert!(args.len() == intrinsic.arguments.len());
955
956 match intrinsic.kind {
957 Intrinsic::Add | Intrinsic::Sub | Intrinsic::Mul | Intrinsic::Div | Intrinsic::Mod => {
958 let ty = args[0].get_content(lookup.context).ty;
959 assert!(
960 args.len() == 2 && ty.eq(lookup.context, &args[1].get_content(lookup.context).ty)
961 );
962
963 use ConstantValue::*;
964 let c = match (
965 &args[0].get_content(lookup.context).value,
966 &args[1].get_content(lookup.context).value,
967 ) {
968 (Uint(arg1), Uint(ref arg2)) => {
969 let result = match intrinsic.kind {
971 Intrinsic::Add => arg1.checked_add(*arg2),
972 Intrinsic::Sub => arg1.checked_sub(*arg2),
973 Intrinsic::Mul => arg1.checked_mul(*arg2),
974 Intrinsic::Div => arg1.checked_div(*arg2),
975 Intrinsic::Mod => arg1.checked_rem(*arg2),
976 _ => unreachable!(),
977 };
978
979 match result {
980 Some(result) => Ok(Some(ConstantContent {
981 ty,
982 value: ConstantValue::Uint(result),
983 })),
984 None => Err(ConstEvalError::CannotBeEvaluatedToConst {
985 span: intrinsic.span.clone(),
986 }),
987 }
988 }
989 (U256(arg1), U256(arg2)) => {
990 let result = match intrinsic.kind {
991 Intrinsic::Add => arg1.checked_add(arg2),
992 Intrinsic::Sub => arg1.checked_sub(arg2),
993 Intrinsic::Mul => arg1.checked_mul(arg2),
994 Intrinsic::Div => arg1.checked_div(arg2),
995 Intrinsic::Mod => Some(arg1.rem(arg2)),
996 _ => unreachable!(),
997 };
998
999 match result {
1000 Some(result) => Ok(Some(ConstantContent {
1001 ty,
1002 value: ConstantValue::U256(result),
1003 })),
1004 None => Err(ConstEvalError::CannotBeEvaluatedToConst {
1005 span: intrinsic.span.clone(),
1006 }),
1007 }
1008 }
1009 _ => {
1010 panic!("Type checker allowed incorrect args to binary op");
1011 }
1012 };
1013 c.map(|c| c.map(|c| Constant::unique(lookup.context, c)))
1014 }
1015 Intrinsic::And | Intrinsic::Or | Intrinsic::Xor => {
1016 let ty = args[0].get_content(lookup.context).ty;
1017 assert!(
1018 args.len() == 2 && ty.eq(lookup.context, &args[1].get_content(lookup.context).ty)
1019 );
1020
1021 use ConstantValue::*;
1022 let c = match (
1023 &args[0].get_content(lookup.context).value,
1024 &args[1].get_content(lookup.context).value,
1025 ) {
1026 (Uint(arg1), Uint(ref arg2)) => {
1027 let result = match intrinsic.kind {
1029 Intrinsic::And => Some(arg1.bitand(arg2)),
1030 Intrinsic::Or => Some(arg1.bitor(*arg2)),
1031 Intrinsic::Xor => Some(arg1.bitxor(*arg2)),
1032 _ => unreachable!(),
1033 };
1034
1035 match result {
1036 Some(sum) => Ok(Some(ConstantContent {
1037 ty,
1038 value: ConstantValue::Uint(sum),
1039 })),
1040 None => Err(ConstEvalError::CannotBeEvaluatedToConst {
1041 span: intrinsic.span.clone(),
1042 }),
1043 }
1044 }
1045 (U256(arg1), U256(arg2)) => {
1046 let result = match intrinsic.kind {
1047 Intrinsic::And => Some(arg1.bitand(arg2)),
1048 Intrinsic::Or => Some(arg1.bitor(arg2)),
1049 Intrinsic::Xor => Some(arg1.bitxor(arg2)),
1050 _ => unreachable!(),
1051 };
1052
1053 match result {
1054 Some(sum) => Ok(Some(ConstantContent {
1055 ty,
1056 value: ConstantValue::U256(sum),
1057 })),
1058 None => Err(ConstEvalError::CannotBeEvaluatedToConst {
1059 span: intrinsic.span.clone(),
1060 }),
1061 }
1062 }
1063 (B256(arg1), B256(arg2)) => {
1064 let result = match intrinsic.kind {
1065 Intrinsic::And => Some(arg1.bitand(arg2)),
1066 Intrinsic::Or => Some(arg1.bitor(arg2)),
1067 Intrinsic::Xor => Some(arg1.bitxor(arg2)),
1068 _ => unreachable!(),
1069 };
1070
1071 match result {
1072 Some(result) => Ok(Some(ConstantContent {
1073 ty,
1074 value: ConstantValue::B256(result),
1075 })),
1076 None => Err(ConstEvalError::CannotBeEvaluatedToConst {
1077 span: intrinsic.span.clone(),
1078 }),
1079 }
1080 }
1081 _ => {
1082 panic!("Type checker allowed incorrect args to binary op");
1083 }
1084 };
1085 c.map(|c| c.map(|c| Constant::unique(lookup.context, c)))
1086 }
1087 Intrinsic::Lsh | Intrinsic::Rsh => {
1088 assert!(args.len() == 2);
1089 assert!(
1090 args[0]
1091 .get_content(lookup.context)
1092 .ty
1093 .is_uint(lookup.context)
1094 || args[0]
1095 .get_content(lookup.context)
1096 .ty
1097 .is_b256(lookup.context)
1098 );
1099 assert!(args[1]
1100 .get_content(lookup.context)
1101 .ty
1102 .is_uint64(lookup.context));
1103
1104 let ty = args[0].get_content(lookup.context).ty;
1105
1106 use ConstantValue::*;
1107 let c = match (
1108 &args[0].get_content(lookup.context).value,
1109 &args[1].get_content(lookup.context).value,
1110 ) {
1111 (Uint(arg1), Uint(ref arg2)) => {
1112 let result = match intrinsic.kind {
1113 Intrinsic::Lsh => u32::try_from(*arg2)
1114 .ok()
1115 .and_then(|arg2| arg1.checked_shl(arg2)),
1116 Intrinsic::Rsh => u32::try_from(*arg2)
1117 .ok()
1118 .and_then(|arg2| arg1.checked_shr(arg2)),
1119 _ => unreachable!(),
1120 };
1121
1122 match result {
1123 Some(sum) => Ok(Some(ConstantContent {
1124 ty,
1125 value: ConstantValue::Uint(sum),
1126 })),
1127 None => Err(ConstEvalError::CannotBeEvaluatedToConst {
1128 span: intrinsic.span.clone(),
1129 }),
1130 }
1131 }
1132 (U256(arg1), Uint(ref arg2)) => {
1133 let result = match intrinsic.kind {
1134 Intrinsic::Lsh => arg1.checked_shl(arg2),
1135 Intrinsic::Rsh => Some(arg1.shr(arg2)),
1136 _ => unreachable!(),
1137 };
1138
1139 match result {
1140 Some(value) => Ok(Some(ConstantContent {
1141 ty,
1142 value: ConstantValue::U256(value),
1143 })),
1144 None => Err(ConstEvalError::CannotBeEvaluatedToConst {
1145 span: intrinsic.span.clone(),
1146 }),
1147 }
1148 }
1149 (B256(arg1), Uint(ref arg2)) => {
1150 let result = match intrinsic.kind {
1151 Intrinsic::Lsh => arg1.checked_shl(arg2),
1152 Intrinsic::Rsh => Some(arg1.shr(arg2)),
1153 _ => unreachable!(),
1154 };
1155
1156 match result {
1157 Some(result) => Ok(Some(ConstantContent {
1158 ty,
1159 value: ConstantValue::B256(result),
1160 })),
1161 None => Err(ConstEvalError::CannotBeEvaluatedToConst {
1162 span: intrinsic.span.clone(),
1163 }),
1164 }
1165 }
1166 _ => {
1167 panic!("Type checker allowed incorrect args to binary op");
1168 }
1169 };
1170 c.map(|c| c.map(|c| Constant::unique(lookup.context, c)))
1171 }
1172 Intrinsic::SizeOfType => {
1173 let targ = &intrinsic.type_arguments[0];
1174 let ir_type = convert_resolved_type_id(
1175 lookup.engines.te(),
1176 lookup.engines.de(),
1177 lookup.context,
1178 targ.type_id(),
1179 &targ.span(),
1180 )
1181 .map_err(|_| ConstEvalError::CompileError)?;
1182 let c = ConstantContent {
1183 ty: Type::get_uint64(lookup.context),
1184 value: ConstantValue::Uint(ir_type.size(lookup.context).in_bytes()),
1185 };
1186
1187 Ok(Some(Constant::unique(lookup.context, c)))
1188 }
1189 Intrinsic::SizeOfVal => {
1190 let val = &intrinsic.arguments[0];
1191 let type_id = val.return_type;
1192 let ir_type = convert_resolved_type_id(
1193 lookup.engines.te(),
1194 lookup.engines.de(),
1195 lookup.context,
1196 type_id,
1197 &val.span,
1198 )
1199 .map_err(|_| ConstEvalError::CompileError)?;
1200 let c = ConstantContent {
1201 ty: Type::get_uint64(lookup.context),
1202 value: ConstantValue::Uint(ir_type.size(lookup.context).in_bytes()),
1203 };
1204 Ok(Some(Constant::unique(lookup.context, c)))
1205 }
1206 Intrinsic::SizeOfStr => {
1207 let targ = &intrinsic.type_arguments[0];
1208 let ir_type = convert_resolved_type_id(
1209 lookup.engines.te(),
1210 lookup.engines.de(),
1211 lookup.context,
1212 targ.type_id(),
1213 &targ.span(),
1214 )
1215 .map_err(|_| ConstEvalError::CompileError)?;
1216 let c = ConstantContent {
1217 ty: Type::get_uint64(lookup.context),
1218 value: ConstantValue::Uint(
1219 ir_type.get_string_len(lookup.context).unwrap_or_default(),
1220 ),
1221 };
1222 Ok(Some(Constant::unique(lookup.context, c)))
1223 }
1224 Intrinsic::AssertIsStrArray => {
1225 let targ = &intrinsic.type_arguments[0];
1226 let ir_type = convert_resolved_type_id(
1227 lookup.engines.te(),
1228 lookup.engines.de(),
1229 lookup.context,
1230 targ.type_id(),
1231 &targ.span(),
1232 )
1233 .map_err(|_| ConstEvalError::CompileError)?;
1234 match ir_type.get_content(lookup.context) {
1235 TypeContent::StringSlice | TypeContent::StringArray(_) => {
1236 let c = ConstantContent {
1237 ty: Type::get_unit(lookup.context),
1238 value: ConstantValue::Unit,
1239 };
1240 Ok(Some(Constant::unique(lookup.context, c)))
1241 }
1242 _ => Err(ConstEvalError::CompileError),
1243 }
1244 }
1245 Intrinsic::ToStrArray => {
1246 assert!(args.len() == 1);
1247 match &args[0].get_content(lookup.context).value {
1248 ConstantValue::String(s) => {
1249 let c = ConstantContent::new_string(lookup.context, s.to_vec());
1250 Ok(Some(Constant::unique(lookup.context, c)))
1251 }
1252 _ => {
1253 unreachable!("Type checker allowed non string value for ToStrArray")
1254 }
1255 }
1256 }
1257 Intrinsic::Eq => {
1258 assert!(args.len() == 2);
1259 let c = ConstantContent {
1260 ty: Type::get_bool(lookup.context),
1261 value: ConstantValue::Bool(args[0] == args[1]),
1262 };
1263 Ok(Some(Constant::unique(lookup.context, c)))
1264 }
1265 Intrinsic::Gt => match (
1266 &args[0].get_content(lookup.context).value,
1267 &args[1].get_content(lookup.context).value,
1268 ) {
1269 (ConstantValue::Uint(val1), ConstantValue::Uint(val2)) => {
1270 let c = ConstantContent {
1271 ty: Type::get_bool(lookup.context),
1272 value: ConstantValue::Bool(val1 > val2),
1273 };
1274 Ok(Some(Constant::unique(lookup.context, c)))
1275 }
1276 (ConstantValue::U256(val1), ConstantValue::U256(val2)) => {
1277 let c = ConstantContent {
1278 ty: Type::get_bool(lookup.context),
1279 value: ConstantValue::Bool(val1 > val2),
1280 };
1281 Ok(Some(Constant::unique(lookup.context, c)))
1282 }
1283 _ => {
1284 unreachable!("Type checker allowed non integer value for GreaterThan")
1285 }
1286 },
1287 Intrinsic::Lt => match (
1288 &args[0].get_content(lookup.context).value,
1289 &args[1].get_content(lookup.context).value,
1290 ) {
1291 (ConstantValue::Uint(val1), ConstantValue::Uint(val2)) => {
1292 let c = ConstantContent {
1293 ty: Type::get_bool(lookup.context),
1294 value: ConstantValue::Bool(val1 < val2),
1295 };
1296 Ok(Some(Constant::unique(lookup.context, c)))
1297 }
1298 (ConstantValue::U256(val1), ConstantValue::U256(val2)) => {
1299 let c = ConstantContent {
1300 ty: Type::get_bool(lookup.context),
1301 value: ConstantValue::Bool(val1 < val2),
1302 };
1303 Ok(Some(Constant::unique(lookup.context, c)))
1304 }
1305 _ => {
1306 unreachable!("Type checker allowed non integer value for LessThan")
1307 }
1308 },
1309 Intrinsic::AddrOf
1310 | Intrinsic::PtrAdd
1311 | Intrinsic::PtrSub
1312 | Intrinsic::IsReferenceType
1313 | Intrinsic::IsStrArray
1314 | Intrinsic::Gtf
1315 | Intrinsic::StateClear
1316 | Intrinsic::StateLoadWord
1317 | Intrinsic::StateStoreWord
1318 | Intrinsic::StateLoadQuad
1319 | Intrinsic::StateStoreQuad
1320 | Intrinsic::Log
1321 | Intrinsic::Revert
1322 | Intrinsic::JmpMem
1323 | Intrinsic::Smo => Err(ConstEvalError::CannotBeEvaluatedToConst {
1324 span: intrinsic.span.clone(),
1325 }),
1326 Intrinsic::Not => {
1327 assert!(args.len() == 1);
1331 assert!(
1332 args[0]
1333 .get_content(lookup.context)
1334 .ty
1335 .is_uint(lookup.context)
1336 || args[0]
1337 .get_content(lookup.context)
1338 .ty
1339 .is_b256(lookup.context)
1340 );
1341
1342 let Some(arg) = args.into_iter().next() else {
1343 unreachable!("Unexpected 'not' without any arguments");
1344 };
1345
1346 let c = match &arg.get_content(lookup.context).value {
1347 ConstantValue::Uint(n) => {
1348 let n = match arg
1349 .get_content(lookup.context)
1350 .ty
1351 .get_uint_width(lookup.context)
1352 {
1353 Some(8) => !(*n as u8) as u64,
1354 Some(16) => !(*n as u16) as u64,
1355 Some(32) => !(*n as u32) as u64,
1356 Some(64) => !n,
1357 _ => unreachable!("Invalid unsigned integer width"),
1358 };
1359 Ok(Some(ConstantContent {
1360 ty: arg.get_content(lookup.context).ty,
1361 value: ConstantValue::Uint(n),
1362 }))
1363 }
1364 ConstantValue::U256(n) => Ok(Some(ConstantContent {
1365 ty: arg.get_content(lookup.context).ty,
1366 value: ConstantValue::U256(n.not()),
1367 })),
1368 ConstantValue::B256(v) => Ok(Some(ConstantContent {
1369 ty: arg.get_content(lookup.context).ty,
1370 value: ConstantValue::B256(v.not()),
1371 })),
1372 _ => {
1373 unreachable!("Type checker allowed non integer value for Not");
1374 }
1375 };
1376 c.map(|c| c.map(|c| Constant::unique(lookup.context, c)))
1377 }
1378 Intrinsic::ContractCall | Intrinsic::ContractRet => {
1379 Err(ConstEvalError::CannotBeEvaluatedToConst {
1380 span: intrinsic.span.clone(),
1381 })
1382 }
1383 Intrinsic::EncodeBufferEmpty => Ok(Some(to_encode_buffer(lookup, vec![], 0))),
1384 Intrinsic::EncodeBufferAppend => {
1385 assert!(args.len() == 2);
1386
1387 let (slice, mut len) = as_encode_buffer(lookup.context, &args[0]).unwrap();
1388 let mut bytes = slice.clone();
1389
1390 use ConstantValue::*;
1391 match &args[1].get_content(lookup.context).value {
1392 Bool(v) => {
1393 bytes.extend(if *v { [1] } else { [0] });
1394 len += 1;
1395 Ok(Some(to_encode_buffer(lookup, bytes, len)))
1396 }
1397 Uint(v) => {
1398 match &*lookup.engines.te().get(intrinsic.arguments[1].return_type) {
1399 TypeInfo::UnsignedInteger(IntegerBits::Eight) => {
1400 bytes.extend((*v as u8).to_be_bytes());
1401 len += 1;
1402 }
1403 TypeInfo::UnsignedInteger(IntegerBits::Sixteen) => {
1404 bytes.extend((*v as u16).to_be_bytes());
1405 len += 2;
1406 }
1407 TypeInfo::UnsignedInteger(IntegerBits::ThirtyTwo) => {
1408 bytes.extend((*v as u32).to_be_bytes());
1409 len += 4;
1410 }
1411 TypeInfo::UnsignedInteger(IntegerBits::SixtyFour) => {
1412 bytes.extend(v.to_be_bytes());
1413 len += 8;
1414 }
1415 _ => {
1416 return Err(ConstEvalError::CannotBeEvaluatedToConst {
1417 span: intrinsic.span.clone(),
1418 });
1419 }
1420 };
1421 Ok(Some(to_encode_buffer(lookup, bytes, len)))
1422 }
1423 U256(v) => {
1424 bytes.extend(v.to_be_bytes());
1425 len += 32;
1426 Ok(Some(to_encode_buffer(lookup, bytes, len)))
1427 }
1428 B256(v) => {
1429 bytes.extend(v.to_be_bytes());
1430 len += 32;
1431 Ok(Some(to_encode_buffer(lookup, bytes, len)))
1432 }
1433 String(v) => {
1434 if let TypeInfo::StringSlice =
1435 &*lookup.engines.te().get(intrinsic.arguments[1].return_type)
1436 {
1437 let l = v.len() as u64;
1438 bytes.extend(l.to_be_bytes());
1439 len += 8;
1440 }
1441
1442 bytes.extend(v);
1443 len += v.len() as u64;
1444
1445 Ok(Some(to_encode_buffer(lookup, bytes, len)))
1446 }
1447 _ => Err(ConstEvalError::CannotBeEvaluatedToConst {
1448 span: intrinsic.span.clone(),
1449 }),
1450 }
1451 }
1452 Intrinsic::EncodeBufferAsRawSlice => {
1453 assert!(args.len() == 1);
1454
1455 let (slice, len) = as_encode_buffer(lookup.context, &args[0]).unwrap();
1456 let bytes = slice.clone();
1457
1458 let c = ConstantContent {
1459 ty: Type::get_slice(lookup.context),
1460 value: ConstantValue::RawUntypedSlice(bytes[0..(len as usize)].to_vec()),
1461 };
1462 Ok(Some(Constant::unique(lookup.context, c)))
1463 }
1464 Intrinsic::Slice => {
1465 let start = args[1]
1466 .get_content(lookup.context)
1467 .as_uint()
1468 .expect("Type check allowed non u64") as usize;
1469 let end = args[2]
1470 .get_content(lookup.context)
1471 .as_uint()
1472 .expect("Type check allowed non u64") as usize;
1473
1474 match &args[0].get_content(lookup.context).value {
1475 ConstantValue::Array(elements) => {
1476 let slice = elements
1477 .get(start..end)
1478 .ok_or(ConstEvalError::CompileError)?;
1479 let elem_type = args[0]
1480 .get_content(lookup.context)
1481 .ty
1482 .get_array_elem_type(lookup.context)
1483 .expect("unexpected non array");
1484 let s = slice.to_vec();
1485 let c = ConstantContent {
1486 ty: Type::get_typed_slice(lookup.context, elem_type),
1487 value: ConstantValue::Slice(s),
1488 };
1489 Ok(Some(Constant::unique(lookup.context, c)))
1490 }
1491 ConstantValue::Reference(r) => match &r.value {
1492 ConstantValue::Slice(elements) => {
1493 let slice = elements
1494 .get(start..end)
1495 .ok_or(ConstEvalError::CompileError)?;
1496 let elem_type = args[0]
1497 .get_content(lookup.context)
1498 .ty
1499 .get_typed_slice_elem_type(lookup.context)
1500 .expect("unexpected non slice");
1501 let s = slice.to_vec();
1502 let c = ConstantContent {
1503 ty: Type::get_typed_slice(lookup.context, elem_type),
1504 value: ConstantValue::Slice(s),
1505 };
1506 Ok(Some(Constant::unique(lookup.context, c)))
1507 }
1508 _ => Err(ConstEvalError::CannotBeEvaluatedToConst {
1509 span: intrinsic.span.clone(),
1510 }),
1511 },
1512 _ => Err(ConstEvalError::CannotBeEvaluatedToConst {
1513 span: intrinsic.span.clone(),
1514 }),
1515 }
1516 }
1517 Intrinsic::ElemAt => {
1518 let idx = args[1]
1519 .get_content(lookup.context)
1520 .as_uint()
1521 .expect("Type check allowed non u64") as usize;
1522
1523 match &args[0].get_content(lookup.context).value {
1524 ConstantValue::Reference(r) => match &r.value {
1525 ConstantValue::Slice(elements) => {
1526 let v = elements[idx].clone();
1527 let c = ConstantContent {
1528 ty: Type::new_ptr(lookup.context, v.ty),
1529 value: ConstantValue::Reference(Box::new(v)),
1530 };
1531 Ok(Some(Constant::unique(lookup.context, c)))
1532 }
1533 _ => Err(ConstEvalError::CannotBeEvaluatedToConst {
1534 span: intrinsic.span.clone(),
1535 }),
1536 },
1537 _ => Err(ConstEvalError::CannotBeEvaluatedToConst {
1538 span: intrinsic.span.clone(),
1539 }),
1540 }
1541 }
1542 Intrinsic::Transmute => {
1543 let src_type = &intrinsic.type_arguments[0];
1544 let src_ir_type = convert_resolved_type_id(
1545 lookup.engines.te(),
1546 lookup.engines.de(),
1547 lookup.context,
1548 src_type.type_id(),
1549 &src_type.span(),
1550 )
1551 .unwrap();
1552
1553 let dst_type = &intrinsic.type_arguments[1];
1554 let dst_ir_type = convert_resolved_type_id(
1555 lookup.engines.te(),
1556 lookup.engines.de(),
1557 lookup.context,
1558 dst_type.type_id(),
1559 &dst_type.span(),
1560 )
1561 .unwrap();
1562
1563 let src_ir_type_in_bytes = src_ir_type.size(lookup.context).in_bytes();
1565 let dst_ir_type_in_bytes = dst_ir_type.size(lookup.context).in_bytes();
1566 if src_ir_type_in_bytes != dst_ir_type_in_bytes {
1567 return Err(ConstEvalError::CompileError);
1568 }
1569
1570 fn append_bytes(
1571 ctx: &Context<'_>,
1572 bytes: &mut Vec<u8>,
1573 t: &Type,
1574 value: &ConstantValue,
1575 ) -> Result<(), ConstEvalError> {
1576 match t.get_content(ctx) {
1577 TypeContent::Struct(fields) => match value {
1578 ConstantValue::Struct(constants) => {
1579 for (field_type, field) in fields.iter().zip(constants.iter()) {
1580 append_bytes(ctx, bytes, field_type, &field.value)?;
1581 }
1582 }
1583 _ => unreachable!(),
1584 },
1585 TypeContent::Array(item_type, size) => match value {
1586 ConstantValue::Array(items) => {
1587 assert!(*size as usize == items.len());
1588 for item in items {
1589 append_bytes(ctx, bytes, item_type, &item.value)?;
1590 }
1591 }
1592 _ => unreachable!(),
1593 },
1594 TypeContent::Uint(8) => match value {
1595 ConstantValue::Uint(v) => {
1596 bytes.extend((*v as u8).to_be_bytes());
1597 }
1598 _ => unreachable!(),
1599 },
1600 TypeContent::Uint(16) => match value {
1601 ConstantValue::Uint(v) => {
1602 bytes.extend([0u8, 0u8, 0u8, 0u8, 0u8, 0u8]);
1603 bytes.extend((*v as u16).to_be_bytes());
1604 }
1605 _ => unreachable!(),
1606 },
1607 TypeContent::Uint(32) => match value {
1608 ConstantValue::Uint(v) => {
1609 bytes.extend([0u8, 0u8, 0u8, 0u8]);
1610 bytes.extend((*v as u32).to_be_bytes());
1611 }
1612 _ => unreachable!(),
1613 },
1614 TypeContent::Uint(64) => match value {
1615 ConstantValue::Uint(v) => {
1616 bytes.extend((*v).to_be_bytes());
1617 }
1618 _ => unreachable!(),
1619 },
1620 _ => return Err(ConstEvalError::CompileError),
1621 }
1622 Ok(())
1623 }
1624
1625 fn transmute_bytes(
1626 ctx: &Context<'_>,
1627 bytes: &mut std::io::Cursor<Vec<u8>>,
1628 t: &Type,
1629 ) -> Result<ConstantContent, ConstEvalError> {
1630 Ok(match t.get_content(ctx) {
1631 TypeContent::Uint(8) => {
1632 let mut buffer = [0u8];
1633 let _ = bytes.read_exact(&mut buffer);
1634 ConstantContent {
1635 ty: Type::get_uint8(ctx),
1636 value: ConstantValue::Uint(buffer[0] as u64),
1637 }
1638 }
1639 TypeContent::Uint(16) => {
1640 let mut buffer = [0u8; 8]; let _ = bytes.read_exact(&mut buffer);
1642 let buffer = [buffer[6], buffer[7]];
1643 ConstantContent {
1644 ty: Type::get_uint16(ctx),
1645 value: ConstantValue::Uint(u16::from_be_bytes(buffer) as u64),
1646 }
1647 }
1648 TypeContent::Uint(32) => {
1649 let mut buffer = [0u8; 8]; let _ = bytes.read_exact(&mut buffer);
1651 let buffer = [buffer[4], buffer[5], buffer[6], buffer[7]];
1652 ConstantContent {
1653 ty: Type::get_uint32(ctx),
1654 value: ConstantValue::Uint(u32::from_be_bytes(buffer) as u64),
1655 }
1656 }
1657 TypeContent::Uint(64) => {
1658 let mut buffer = [0u8; 8];
1659 let _ = bytes.read_exact(&mut buffer);
1660 ConstantContent {
1661 ty: Type::get_uint64(ctx),
1662 value: ConstantValue::Uint(u64::from_be_bytes(buffer)),
1663 }
1664 }
1665 _ => return Err(ConstEvalError::CompileError),
1666 })
1667 }
1668
1669 let mut runtime_bytes = vec![];
1670 append_bytes(
1671 lookup.context,
1672 &mut runtime_bytes,
1673 &src_ir_type,
1674 &args[0].get_content(lookup.context).value,
1675 )?;
1676 let mut cursor = std::io::Cursor::new(runtime_bytes);
1677 let c = transmute_bytes(lookup.context, &mut cursor, &dst_ir_type)?;
1678 Ok(Some(Constant::unique(lookup.context, c)))
1679 }
1680 Intrinsic::Dbg => {
1681 unreachable!("__dbg should not exist in the typed tree")
1682 }
1683 }
1684}
1685
1686#[cfg(test)]
1687mod tests {
1688 use super::*;
1689 use sway_error::handler::Handler;
1690 use sway_features::ExperimentalFeatures;
1691 use sway_ir::Kind;
1692 use sway_types::ProgramId;
1693
1694 fn assert_is_constant(is_constant: bool, prefix: &str, expr: &str) {
1708 let engines = Engines::default();
1709 let handler = Handler::default();
1710 let mut context = Context::new(engines.se(), ExperimentalFeatures::default());
1711 let mut md_mgr = MetadataManager::default();
1712 let core_lib = namespace::Package::new(
1713 sway_types::Ident::new_no_span("assert_is_constant_test".to_string()),
1714 None,
1715 ProgramId::new(0),
1716 false,
1717 );
1718
1719 let r = crate::compile_to_ast(
1720 &handler,
1721 &engines,
1722 format!("library; {prefix} fn f() -> u64 {{ {expr}; 0 }}")
1723 .as_str()
1724 .into(),
1725 core_lib,
1726 None,
1727 "test",
1728 None,
1729 ExperimentalFeatures::default(),
1730 );
1731
1732 let (errors, _warnings) = handler.consume();
1733
1734 if !errors.is_empty() {
1735 panic!("{:#?}", errors);
1736 }
1737
1738 let f = r.unwrap();
1739 let f = f.typed.unwrap();
1740
1741 let f = f
1742 .declarations
1743 .iter()
1744 .find_map(|x| match x {
1745 ty::TyDecl::FunctionDecl(x) => {
1746 if engines.de().get_function(&x.decl_id).name.as_str() == "f" {
1747 Some(x)
1748 } else {
1749 None
1750 }
1751 }
1752 _ => None,
1753 })
1754 .expect("An function named `f` was not found.");
1755
1756 let f = engines.de().get_function(&f.decl_id);
1757 let expr_under_test = f.body.contents.first().unwrap();
1758
1759 let expr_under_test = match &expr_under_test.content {
1760 ty::TyAstNodeContent::Expression(expr_under_test) => expr_under_test.clone(),
1761 ty::TyAstNodeContent::Declaration(crate::language::ty::TyDecl::ConstantDecl(decl)) => {
1762 let decl = engines.de().get_constant(&decl.decl_id);
1763 decl.value.clone().unwrap()
1764 }
1765 x => todo!("{x:?}"),
1766 };
1767
1768 let module = Module::new(&mut context, Kind::Library);
1769 let actual_constant = compile_constant_expression_to_constant(
1770 &engines,
1771 &mut context,
1772 &mut md_mgr,
1773 module,
1774 None,
1775 None,
1776 &expr_under_test,
1777 );
1778
1779 match (is_constant, actual_constant) {
1780 (true, Ok(_)) => {}
1781 (true, Err(err)) => {
1782 panic!("Expression cannot be converted to constant: {expr:?}\nPrefix: {prefix:?}\nExpr:{expr_under_test:#?}\nError: {err:#?}");
1783 }
1784 (false, Ok(constant)) => {
1785 panic!("Expression unexpectedly can be converted to constant: {expr:?}\nPrefix: {prefix:?}\nExpr:{expr_under_test:#?}\nConstant: {constant:#?}");
1786 }
1787 (false, Err(_)) => {}
1788 }
1789 }
1790
1791 #[test]
1792 fn const_eval_test() {
1793 assert_is_constant(true, "", "1");
1795 assert_is_constant(true, "", "true");
1796 assert_is_constant(true, "fn one() -> u64 { 1 }", "one()");
1797 assert_is_constant(true, "fn id(x: u64) -> u64 { x }", "id(1)");
1798 assert_is_constant(true, "enum Color { Blue: () }", "Color::Blue");
1799 assert_is_constant(true, "enum Color { Blue: u64 }", "Color::Blue(1)");
1800 assert_is_constant(true, "struct Person { age: u64 }", "Person { age: 1 }");
1801 assert_is_constant(true, "struct Person { age: u64 }", "Person { age: 1 }.age");
1802 assert_is_constant(
1803 true,
1804 "struct Person { age: u64 }",
1805 "Person { age: { let mut x = 0; x = 1; 1 } }",
1806 );
1807 assert_is_constant(true, "", "if true { 1 } else { 0 }");
1808 assert_is_constant(true, "", "(0,1).0");
1809 assert_is_constant(true, "", "[0,1][0]");
1810
1811 assert_is_constant(
1813 true,
1814 "",
1815 "0x0000000000000000000000000000000000000000000000000000000000000001u256",
1816 );
1817 assert_is_constant(
1818 true,
1819 "",
1820 "__add(0x0000000000000000000000000000000000000000000000000000000000000001u256, 0x0000000000000000000000000000000000000000000000000000000000000001u256)",
1821 );
1822 assert_is_constant(
1823 true,
1824 "",
1825 "__eq(0x0000000000000000000000000000000000000000000000000000000000000001u256, 0x0000000000000000000000000000000000000000000000000000000000000001u256)",
1826 );
1827 assert_is_constant(
1828 true,
1829 "",
1830 "__gt(0x0000000000000000000000000000000000000000000000000000000000000001u256, 0x0000000000000000000000000000000000000000000000000000000000000001u256)",
1831 );
1832 assert_is_constant(
1833 true,
1834 "",
1835 "__lt(0x0000000000000000000000000000000000000000000000000000000000000001u256, 0x0000000000000000000000000000000000000000000000000000000000000001u256)",
1836 );
1837 assert_is_constant(
1838 true,
1839 "",
1840 "__lsh(0x0000000000000000000000000000000000000000000000000000000000000001u256, 2)",
1841 );
1842 assert_is_constant(
1843 true,
1844 "",
1845 "__not(0x0000000000000000000000000000000000000000000000000000000000000001u256)",
1846 );
1847
1848 assert_is_constant(false, "", "{ return 1; }");
1850 assert_is_constant(false, "", "{ return 1; 1}");
1851 assert_is_constant(
1852 false,
1853 "enum Color { Blue: u64 }",
1854 "Color::Blue({ return 1; 1})",
1855 );
1856 assert_is_constant(
1857 false,
1858 "struct Person { age: u64 }",
1859 "Person { age: { return 1; 1} }",
1860 );
1861
1862 assert_is_constant(false, "fn id(x: u64) -> u64 { return x; }", "id(1)");
1864 assert_is_constant(false, "", "[0,1][2]");
1865 assert_is_constant(
1866 false,
1867 "enum Color { Blue: u64 }",
1868 "Color::Blue({return 1;})",
1869 );
1870
1871 assert_is_constant(true, "", "{ 1 }");
1873 assert_is_constant(true, "", "{ let a = 1; a }");
1874 assert_is_constant(true, "", "{ const a = 1; a }");
1875 assert_is_constant(true, "", "{ struct A {} 1 }");
1876 assert_is_constant(true, "fn id(x: u64) -> u64 { { let x = 2; }; x }", "id(1)");
1877
1878 assert_is_constant(false, "", "{ let a = 1; }");
1880 assert_is_constant(false, "", "{ const a = 1; }");
1881 assert_is_constant(false, "", "{ struct A {} }");
1882 assert_is_constant(false, "", "{ return 1; 1 }");
1883 assert_is_constant(false, "", "{ }");
1884 assert_is_constant(false, "fn id(x: u64) -> u64 { { return 1; }; x }", "id(1)");
1885 }
1886}