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