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 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
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 { 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 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 (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 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 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 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 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 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
814fn const_eval_codeblock(
818 lookup: &mut LookupEnv,
819 known_consts: &mut MappedStack<Ident, Constant>,
820 codeblock: &ty::TyCodeBlock,
821) -> Result<Option<Constant>, ConstEvalError> {
822 let mut result: Result<Option<Constant>, ConstEvalError> = Ok(None);
824 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 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 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 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 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 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]; 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]; 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 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 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 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 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 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 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 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}