1pub(crate) mod compile;
2pub mod const_eval;
3mod convert;
4mod function;
5mod lexical_map;
6mod purity;
7pub mod storage;
8mod types;
9
10use std::{
11 collections::HashMap,
12 hash::{DefaultHasher, Hasher},
13};
14
15use sway_error::error::CompileError;
16use sway_features::ExperimentalFeatures;
17use sway_ir::{
18 Backtrace, Context, Function, InstOp, InstructionInserter, IrError, Kind, Module, Type, Value,
19};
20use sway_types::{span::Span, Ident};
21
22pub(crate) use purity::{check_function_purity, PurityEnv};
23
24use crate::{
25 engine_threading::HashWithEngines,
26 ir_generation::function::FnCompiler,
27 language::ty::{self, TyCodeBlock, TyExpression, TyFunctionDecl, TyReassignmentTarget},
28 metadata::MetadataManager,
29 types::{LogId, MessageId},
30 Engines, PanicOccurrences, PanickingCallOccurrences, TypeId,
31};
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
34pub(crate) struct FnKey(u64);
35
36impl FnKey {
37 fn new(decl: &TyFunctionDecl, engines: &Engines) -> Self {
38 let mut hasher = DefaultHasher::default();
39 decl.hash(&mut hasher, engines);
40 let key = hasher.finish();
41
42 Self(key)
43 }
44}
45
46pub(crate) struct KeyedTyFunctionDecl<'a> {
48 key: FnKey,
49 decl: &'a TyFunctionDecl,
50}
51
52impl<'a> KeyedTyFunctionDecl<'a> {
53 fn new(decl: &'a TyFunctionDecl, engines: &'a Engines) -> Self {
54 Self {
55 key: FnKey::new(decl, engines),
56 decl,
57 }
58 }
59}
60
61#[derive(Default)]
65pub(crate) struct CompiledFunctionCache {
66 cache: HashMap<FnKey, Function>,
67}
68
69impl CompiledFunctionCache {
70 #[allow(clippy::too_many_arguments)]
71 fn get_compiled_function(
72 &mut self,
73 engines: &Engines,
74 context: &mut Context,
75 module: Module,
76 md_mgr: &mut MetadataManager,
77 keyed_decl: &KeyedTyFunctionDecl,
78 logged_types_map: &HashMap<TypeId, LogId>,
79 messages_types_map: &HashMap<TypeId, MessageId>,
80 panic_occurrences: &mut PanicOccurrences,
81 panicking_call_occurrences: &mut PanickingCallOccurrences,
82 panicking_fn_cache: &mut PanickingFunctionCache,
83 ) -> Result<Function, CompileError> {
84 let fn_key = keyed_decl.key;
85 let decl = keyed_decl.decl;
86
87 let new_callee = match self.cache.get(&fn_key) {
88 Some(func) => *func,
89 None => {
90 let name = Ident::new(Span::from_string(format!(
91 "{}_{}",
92 decl.name,
93 context.get_unique_symbol_id()
94 )));
95 let callee_fn_decl = ty::TyFunctionDecl {
96 type_parameters: Vec::new(),
97 name,
98 parameters: decl.parameters.clone(),
99 ..decl.clone()
100 };
101 let is_entry = false;
104 let is_original_entry = callee_fn_decl.is_main() || callee_fn_decl.is_test();
105 let new_func = compile::compile_function(
106 engines,
107 context,
108 md_mgr,
109 module,
110 &callee_fn_decl,
111 &decl.name,
112 FnCompiler::fn_abi_errors_display(decl, engines),
113 logged_types_map,
114 messages_types_map,
115 panic_occurrences,
116 panicking_call_occurrences,
117 panicking_fn_cache,
118 is_entry,
119 is_original_entry,
120 None,
121 self,
122 )
123 .map_err(|mut x| x.pop().unwrap())?
124 .unwrap();
125
126 self.cache.insert(fn_key, new_func);
127
128 new_func
129 }
130 };
131
132 Ok(new_callee)
133 }
134}
135
136#[derive(Default)]
137pub(crate) struct PanickingFunctionCache {
138 cache: HashMap<FnKey, bool>,
139}
140
141impl PanickingFunctionCache {
142 pub(crate) fn can_panic(
166 &mut self,
167 keyed_decl: &KeyedTyFunctionDecl,
168 engines: &Engines,
169 ) -> bool {
170 let fn_key = keyed_decl.key;
171 let decl = keyed_decl.decl;
172
173 if !decl.is_default() {
175 return false;
176 }
177
178 match self.cache.get(&fn_key) {
179 Some(can_panic) => *can_panic,
180 None => {
181 let can_panic = self.can_code_block_panic(&decl.body, engines);
182 self.cache.insert(fn_key, can_panic);
183 can_panic
184 }
185 }
186 }
187
188 fn can_code_block_panic(&mut self, body: &TyCodeBlock, engines: &Engines) -> bool {
189 for node in body.contents.iter() {
190 use ty::TyAstNodeContent::*;
191 match &node.content {
192 Declaration(ty_decl) => {
193 if let ty::TyDecl::VariableDecl(var_decl) = ty_decl {
194 if self.can_expression_panic(&var_decl.body, engines) {
195 return true;
196 }
197 }
198 }
199 Expression(expr) => {
200 if self.can_expression_panic(expr, engines) {
201 return true;
202 }
203 }
204 SideEffect(_) | Error(_, _) => {}
205 }
206 }
207
208 false
209 }
210
211 fn can_expression_panic(&mut self, expr: &TyExpression, engines: &Engines) -> bool {
212 use ty::TyExpressionVariant::*;
213 match &expr.expression {
214 Panic(_) => true,
216
217 FunctionApplication { fn_ref, .. } => {
219 let decl = engines.de().get_function(fn_ref.id());
220 let keyed_decl = KeyedTyFunctionDecl::new(&decl, engines);
221 self.can_panic(&keyed_decl, engines)
223 }
224
225 MatchExp {
227 desugared: expr, ..
228 }
229 | StructFieldAccess { prefix: expr, .. }
230 | TupleElemAccess { prefix: expr, .. }
231 | AbiCast { address: expr, .. }
232 | EnumTag { exp: expr }
233 | UnsafeDowncast { exp: expr, .. }
234 | ForLoop { desugared: expr }
235 | ImplicitReturn(expr)
236 | Return(expr)
237 | Ref(expr)
238 | Deref(expr) => self.can_expression_panic(expr, engines),
239
240 LazyOperator { lhs, rhs, .. } => {
242 self.can_expression_panic(lhs, engines) || self.can_expression_panic(rhs, engines)
243 }
244 Tuple { fields } => fields
245 .iter()
246 .any(|field| self.can_expression_panic(field, engines)),
247 ArrayExplicit { contents, .. } => contents
248 .iter()
249 .any(|elem| self.can_expression_panic(elem, engines)),
250 ArrayRepeat { value, length, .. } => {
251 self.can_expression_panic(value, engines)
252 || self.can_expression_panic(length, engines)
253 }
254 ArrayIndex { prefix, index } => {
255 self.can_expression_panic(prefix, engines)
256 || self.can_expression_panic(index, engines)
257 }
258 StructExpression { fields, .. } => fields
259 .iter()
260 .any(|field| self.can_expression_panic(&field.value, engines)),
261 IfExp {
262 condition,
263 then,
264 r#else,
265 } => {
266 self.can_expression_panic(condition, engines)
267 || self.can_expression_panic(then, engines)
268 || r#else
269 .as_ref()
270 .map_or(false, |r#else| self.can_expression_panic(r#else, engines))
271 }
272 AsmExpression { registers, .. } => registers.iter().any(|reg| {
273 reg.initializer
274 .as_ref()
275 .is_some_and(|init| self.can_expression_panic(init, engines))
276 }),
277 EnumInstantiation { contents, .. } => contents
278 .as_ref()
279 .is_some_and(|contents| self.can_expression_panic(contents, engines)),
280 WhileLoop { condition, body } => {
281 self.can_expression_panic(condition, engines)
282 || self.can_code_block_panic(body, engines)
283 }
284 Reassignment(reassignment) => match &reassignment.lhs {
285 TyReassignmentTarget::ElementAccess { indices, .. } => {
286 indices.iter().any(|index| match index {
287 ty::ProjectionKind::StructField { .. }
288 | ty::ProjectionKind::TupleField { .. } => false,
289 ty::ProjectionKind::ArrayIndex { index, .. } => {
290 self.can_expression_panic(index, engines)
291 }
292 })
293 }
294 TyReassignmentTarget::DerefAccess { exp, indices } => {
295 self.can_expression_panic(exp, engines)
296 || indices.iter().any(|index| match index {
297 ty::ProjectionKind::StructField { .. }
298 | ty::ProjectionKind::TupleField { .. } => false,
299 ty::ProjectionKind::ArrayIndex { index, .. } => {
300 self.can_expression_panic(index, engines)
301 }
302 })
303 }
304 },
305
306 CodeBlock(block) => self.can_code_block_panic(block, engines),
307
308 Literal(_)
310 | ConstantExpression { .. }
311 | ConfigurableExpression { .. }
312 | ConstGenericExpression { .. }
313 | VariableExpression { .. }
314 | FunctionParameter
315 | StorageAccess(_)
316 | IntrinsicFunction(_)
317 | AbiName(_)
318 | Break
319 | Continue => false,
320 }
321 }
322}
323
324pub fn compile_program<'a>(
325 program: &ty::TyProgram,
326 panic_occurrences: &'a mut PanicOccurrences,
327 panicking_call_occurrences: &'a mut PanickingCallOccurrences,
328 include_tests: bool,
329 engines: &'a Engines,
330 experimental: ExperimentalFeatures,
331 backtrace: Backtrace,
332) -> Result<Context<'a>, Vec<CompileError>> {
333 let declaration_engine = engines.de();
334
335 let test_fns = match include_tests {
336 true => program.test_fns(declaration_engine).collect(),
337 false => vec![],
338 };
339
340 let ty::TyProgram {
341 kind,
342 namespace,
343 logged_types,
344 messages_types,
345 declarations,
346 ..
347 } = program;
348
349 let logged_types = logged_types
350 .iter()
351 .map(|(log_id, type_id)| (*type_id, *log_id))
352 .collect();
353
354 let messages_types = messages_types
355 .iter()
356 .map(|(message_id, type_id)| (*type_id, *message_id))
357 .collect();
358
359 let mut ctx = Context::new(engines.se(), experimental, backtrace);
360 ctx.program_kind = match kind {
361 ty::TyProgramKind::Script { .. } => Kind::Script,
362 ty::TyProgramKind::Predicate { .. } => Kind::Predicate,
363 ty::TyProgramKind::Contract { .. } => Kind::Contract,
364 ty::TyProgramKind::Library { .. } => Kind::Library,
365 };
366
367 let mut compiled_fn_cache = CompiledFunctionCache::default();
368 let mut panicking_fn_cache = PanickingFunctionCache::default();
369
370 match kind {
371 ty::TyProgramKind::Script { entry_function, .. } => compile::compile_script(
374 engines,
375 &mut ctx,
376 entry_function,
377 namespace,
378 &logged_types,
379 &messages_types,
380 panic_occurrences,
381 panicking_call_occurrences,
382 &mut panicking_fn_cache,
383 &test_fns,
384 &mut compiled_fn_cache,
385 ),
386 ty::TyProgramKind::Predicate { entry_function, .. } => compile::compile_predicate(
387 engines,
388 &mut ctx,
389 entry_function,
390 namespace,
391 &logged_types,
392 &messages_types,
393 panic_occurrences,
394 panicking_call_occurrences,
395 &mut panicking_fn_cache,
396 &test_fns,
397 &mut compiled_fn_cache,
398 ),
399 ty::TyProgramKind::Contract {
400 entry_function,
401 abi_entries,
402 } => compile::compile_contract(
403 &mut ctx,
404 entry_function.as_ref(),
405 abi_entries,
406 namespace,
407 declarations,
408 &logged_types,
409 &messages_types,
410 panic_occurrences,
411 panicking_call_occurrences,
412 &mut panicking_fn_cache,
413 &test_fns,
414 engines,
415 &mut compiled_fn_cache,
416 ),
417 ty::TyProgramKind::Library { .. } => compile::compile_library(
418 engines,
419 &mut ctx,
420 namespace,
421 &logged_types,
422 &messages_types,
423 panic_occurrences,
424 panicking_call_occurrences,
425 &mut panicking_fn_cache,
426 &test_fns,
427 &mut compiled_fn_cache,
428 ),
429 }?;
430
431 type_correction(&mut ctx).map_err(|ir_error: sway_ir::IrError| {
432 vec![CompileError::InternalOwned(
433 ir_error.to_string(),
434 Span::dummy(),
435 )]
436 })?;
437
438 ctx.verify().map_err(|ir_error: sway_ir::IrError| {
439 vec![CompileError::InternalOwned(
440 ir_error.to_string(),
441 Span::dummy(),
442 )]
443 })?;
444 Ok(ctx)
445}
446
447fn type_correction(ctx: &mut Context) -> Result<(), IrError> {
448 struct TypeCorrection {
449 actual_ty: sway_ir::Type,
450 expected_ty: sway_ir::Type,
451 use_instr: sway_ir::Value,
452 use_idx: usize,
453 }
454 fn is_copy_type(ty: &Type, context: &Context) -> bool {
456 ty.is_unit(context)
457 || ty.is_never(context)
458 || ty.is_bool(context)
459 || ty.is_ptr(context)
460 || ty.get_uint_width(context).map(|x| x < 256).unwrap_or(false)
461 }
462
463 let mut instrs_to_fix = Vec::new();
464 for module in ctx.module_iter() {
465 for function in module.function_iter(ctx) {
466 for (_block, instr) in function.instruction_iter(ctx).collect::<Vec<_>>() {
467 match &instr.get_instruction(ctx).unwrap().op {
468 InstOp::Call(callee, actual_params) => {
469 let formal_params: Vec<_> = callee.args_iter(ctx).collect();
470 for (param_idx, (actual_param, (_, formal_param))) in
471 actual_params.iter().zip(formal_params.iter()).enumerate()
472 {
473 let actual_ty = actual_param.get_type(ctx).unwrap();
474 let formal_ty = formal_param.get_type(ctx).unwrap();
475 if actual_ty != formal_ty {
476 instrs_to_fix.push(TypeCorrection {
477 actual_ty,
478 expected_ty: formal_ty,
479 use_instr: instr,
480 use_idx: param_idx,
481 });
482 }
483 }
484 }
485 InstOp::AsmBlock(_block, _args) => {
486 let op = &instr.get_instruction(ctx).unwrap().op;
488 let args = op
489 .get_operands()
490 .iter()
491 .enumerate()
492 .map(|(idx, init)| (idx, init.get_type(ctx).unwrap()))
493 .collect::<Vec<_>>();
494 for (arg_idx, arg_ty) in args {
495 if !is_copy_type(&arg_ty, ctx) {
496 instrs_to_fix.push(TypeCorrection {
497 actual_ty: arg_ty,
498 expected_ty: Type::new_typed_pointer(ctx, arg_ty),
499 use_instr: instr,
500 use_idx: arg_idx,
501 });
502 }
503 }
504 }
505 InstOp::GetElemPtr {
506 base,
507 elem_ptr_ty,
508 indices,
509 } => {
510 let base_ty = base.get_type(ctx).unwrap();
511 if let (Some(base_pointee_ty), Some(elem_inner_ty)) = (
512 base_ty.get_pointee_type(ctx),
513 elem_ptr_ty.get_pointee_type(ctx),
514 ) {
515 if let Some(base_pointee_pointee_ty) =
517 base_pointee_ty.get_pointee_type(ctx)
518 {
519 let indexed_ty =
521 base_pointee_pointee_ty.get_value_indexed_type(ctx, indices);
522 if indexed_ty.is_some_and(|ty| ty == elem_inner_ty) {
523 instrs_to_fix.push(TypeCorrection {
524 actual_ty: base_ty,
525 expected_ty: base_pointee_ty,
526 use_instr: instr,
527 use_idx: indices.len(),
528 });
529 }
530 }
531 } else {
532 let elem_ptr_ty = *elem_ptr_ty;
534 let indices = indices.clone(); let pointer_to_base = Type::new_typed_pointer(ctx, base_ty);
536 if pointer_to_base.get_value_indexed_type(ctx, &indices)
537 == Some(elem_ptr_ty)
538 {
539 instrs_to_fix.push(TypeCorrection {
540 actual_ty: base_ty,
541 expected_ty: pointer_to_base,
542 use_instr: instr,
543 use_idx: indices.len(),
544 });
545 }
546 }
547 }
548 InstOp::Store {
549 dst_val_ptr,
550 stored_val,
551 } => {
552 let dst_ty = dst_val_ptr.get_type(ctx).unwrap();
553 let stored_ty = stored_val.get_type(ctx).unwrap();
554 if let Some(dst_pointee_ty) = dst_ty.get_pointee_type(ctx) {
555 if let Some(dst_pointee_pointee_ty) =
557 dst_pointee_ty.get_pointee_type(ctx)
558 {
559 if dst_pointee_pointee_ty == stored_ty {
561 instrs_to_fix.push(TypeCorrection {
562 actual_ty: dst_ty,
563 expected_ty: dst_pointee_ty,
564 use_instr: instr,
565 use_idx: 0,
566 });
567 }
568 } else if let Some(stored_pointee_ty) = stored_ty.get_pointee_type(ctx)
569 {
570 if dst_pointee_ty == stored_pointee_ty {
573 instrs_to_fix.push(TypeCorrection {
574 actual_ty: stored_ty,
575 expected_ty: stored_pointee_ty,
576 use_instr: instr,
577 use_idx: 1,
578 });
579 }
580 }
581 } else {
582 let pointer_to_dst = Type::new_typed_pointer(ctx, dst_ty);
584 if pointer_to_dst == stored_ty {
585 instrs_to_fix.push(TypeCorrection {
586 actual_ty: dst_ty,
587 expected_ty: pointer_to_dst,
588 use_instr: instr,
589 use_idx: 0,
590 });
591 }
592 }
593 }
594 InstOp::Ret(ret_val, ret_ty) => {
595 if let Some(ret_val_pointee_ty) = ret_val
596 .get_type(ctx)
597 .and_then(|ret_val_ty| ret_val_ty.get_pointee_type(ctx))
598 {
599 if ret_val_pointee_ty == *ret_ty {
600 instrs_to_fix.push(TypeCorrection {
601 actual_ty: ret_val.get_type(ctx).unwrap(),
602 expected_ty: *ret_ty,
603 use_instr: instr,
604 use_idx: 0,
605 });
606 }
607 }
608 }
609 _ => (),
610 }
611 }
612 }
613 }
614
615 for TypeCorrection {
616 actual_ty,
617 expected_ty,
618 use_instr,
619 use_idx,
620 } in instrs_to_fix
621 {
622 let function = use_instr.get_instruction(ctx).unwrap().get_function(ctx);
623 if expected_ty
624 .get_pointee_type(ctx)
625 .is_some_and(|pointee| pointee == actual_ty)
626 {
627 let actual_use = use_instr.get_instruction(ctx).unwrap().op.get_operands()[use_idx];
631 if let Some(InstOp::Load(src_ptr)) = actual_use.get_instruction(ctx).map(|i| &i.op) {
632 let src_ptr = *src_ptr;
633 use_instr
634 .get_instruction_mut(ctx)
635 .unwrap()
636 .op
637 .set_operand(src_ptr, use_idx);
638 } else {
639 let parent_block = use_instr.get_instruction(ctx).unwrap().parent;
640 let new_local = function.new_unique_local_var(
641 ctx,
642 "type_fix".to_string(),
643 actual_ty,
644 None,
645 true,
646 );
647 let new_local =
648 Value::new_instruction(ctx, parent_block, InstOp::GetLocal(new_local));
649 let store = Value::new_instruction(
650 ctx,
651 parent_block,
652 InstOp::Store {
653 dst_val_ptr: new_local,
654 stored_val: actual_use,
655 },
656 );
657 let mut inserter = InstructionInserter::new(
658 ctx,
659 parent_block,
660 sway_ir::InsertionPosition::Before(use_instr),
661 );
662 inserter.insert_slice(&[new_local, store]);
663 use_instr
665 .get_instruction_mut(ctx)
666 .unwrap()
667 .op
668 .set_operand(new_local, use_idx);
669 }
670 } else if actual_ty
671 .get_pointee_type(ctx)
672 .is_some_and(|pointee| pointee == expected_ty)
673 {
674 let load = Value::new_instruction(
676 ctx,
677 use_instr.get_instruction(ctx).unwrap().parent,
678 InstOp::Load(use_instr.get_instruction(ctx).unwrap().op.get_operands()[use_idx]),
679 );
680 let mut inserter = InstructionInserter::new(
681 ctx,
682 use_instr.get_instruction(ctx).unwrap().parent,
683 sway_ir::InsertionPosition::Before(use_instr),
684 );
685 inserter.insert_slice(&[load]);
686 use_instr
688 .get_instruction_mut(ctx)
689 .unwrap()
690 .op
691 .set_operand(load, use_idx);
692 }
693 }
694 Ok(())
695}