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::{Context, Function, InstOp, InstructionInserter, IrError, Kind, Module, Type, Value};
18use sway_types::{span::Span, Ident};
19
20pub(crate) use purity::{check_function_purity, PurityEnv};
21
22use crate::{
23 engine_threading::HashWithEngines,
24 language::ty,
25 metadata::MetadataManager,
26 types::{LogId, MessageId},
27 Engines, PanicOccurrences, TypeId,
28};
29
30type FnKey = u64;
31
32#[derive(Default)]
36pub(crate) struct CompiledFunctionCache {
37 recreated_fns: HashMap<FnKey, Function>,
38}
39
40impl CompiledFunctionCache {
41 #[allow(clippy::too_many_arguments)]
42 fn ty_function_decl_to_unique_function(
43 &mut self,
44 engines: &Engines,
45 context: &mut Context,
46 module: Module,
47 md_mgr: &mut MetadataManager,
48 decl: &ty::TyFunctionDecl,
49 logged_types_map: &HashMap<TypeId, LogId>,
50 messages_types_map: &HashMap<TypeId, MessageId>,
51 panic_occurrences: &mut PanicOccurrences,
52 ) -> Result<Function, CompileError> {
53 let mut hasher = DefaultHasher::default();
71 decl.hash(&mut hasher, engines);
72 let fn_key = hasher.finish();
73
74 let (fn_key, item) = (Some(fn_key), self.recreated_fns.get(&fn_key).copied());
75 let new_callee = match item {
76 Some(func) => func,
77 None => {
78 let name = Ident::new(Span::from_string(format!(
79 "{}_{}",
80 decl.name,
81 context.get_unique_symbol_id()
82 )));
83 let callee_fn_decl = ty::TyFunctionDecl {
84 type_parameters: Vec::new(),
85 name,
86 parameters: decl.parameters.clone(),
87 ..decl.clone()
88 };
89 let is_entry = false;
92 let is_original_entry = callee_fn_decl.is_main() || callee_fn_decl.is_test();
93 let new_func = compile::compile_function(
94 engines,
95 context,
96 md_mgr,
97 module,
98 &callee_fn_decl,
99 &decl.name,
100 logged_types_map,
101 messages_types_map,
102 panic_occurrences,
103 is_entry,
104 is_original_entry,
105 None,
106 self,
107 )
108 .map_err(|mut x| x.pop().unwrap())?
109 .unwrap();
110
111 if let Some(fn_key) = fn_key {
112 self.recreated_fns.insert(fn_key, new_func);
113 }
114
115 new_func
116 }
117 };
118
119 Ok(new_callee)
120 }
121}
122
123pub fn compile_program<'a>(
124 program: &ty::TyProgram,
125 panic_occurrences: &'a mut PanicOccurrences,
126 include_tests: bool,
127 engines: &'a Engines,
128 experimental: ExperimentalFeatures,
129) -> Result<Context<'a>, Vec<CompileError>> {
130 let declaration_engine = engines.de();
131
132 let test_fns = match include_tests {
133 true => program.test_fns(declaration_engine).collect(),
134 false => vec![],
135 };
136
137 let ty::TyProgram {
138 kind,
139 namespace,
140 logged_types,
141 messages_types,
142 declarations,
143 ..
144 } = program;
145
146 let logged_types = logged_types
147 .iter()
148 .map(|(log_id, type_id)| (*type_id, *log_id))
149 .collect();
150
151 let messages_types = messages_types
152 .iter()
153 .map(|(message_id, type_id)| (*type_id, *message_id))
154 .collect();
155
156 let mut ctx = Context::new(engines.se(), experimental);
157 ctx.program_kind = match kind {
158 ty::TyProgramKind::Script { .. } => Kind::Script,
159 ty::TyProgramKind::Predicate { .. } => Kind::Predicate,
160 ty::TyProgramKind::Contract { .. } => Kind::Contract,
161 ty::TyProgramKind::Library { .. } => Kind::Library,
162 };
163
164 let mut cache = CompiledFunctionCache::default();
165
166 match kind {
167 ty::TyProgramKind::Script { entry_function, .. } => compile::compile_script(
170 engines,
171 &mut ctx,
172 entry_function,
173 namespace,
174 &logged_types,
175 &messages_types,
176 panic_occurrences,
177 &test_fns,
178 &mut cache,
179 ),
180 ty::TyProgramKind::Predicate { entry_function, .. } => compile::compile_predicate(
181 engines,
182 &mut ctx,
183 entry_function,
184 namespace,
185 &logged_types,
186 &messages_types,
187 panic_occurrences,
188 &test_fns,
189 &mut cache,
190 ),
191 ty::TyProgramKind::Contract {
192 entry_function,
193 abi_entries,
194 } => compile::compile_contract(
195 &mut ctx,
196 entry_function.as_ref(),
197 abi_entries,
198 namespace,
199 declarations,
200 &logged_types,
201 &messages_types,
202 panic_occurrences,
203 &test_fns,
204 engines,
205 &mut cache,
206 ),
207 ty::TyProgramKind::Library { .. } => compile::compile_library(
208 engines,
209 &mut ctx,
210 namespace,
211 &logged_types,
212 &messages_types,
213 panic_occurrences,
214 &test_fns,
215 &mut cache,
216 ),
217 }?;
218
219 type_correction(&mut ctx).map_err(|ir_error: sway_ir::IrError| {
220 vec![CompileError::InternalOwned(
221 ir_error.to_string(),
222 Span::dummy(),
223 )]
224 })?;
225
226 ctx.verify().map_err(|ir_error: sway_ir::IrError| {
227 vec![CompileError::InternalOwned(
228 ir_error.to_string(),
229 Span::dummy(),
230 )]
231 })
232}
233
234fn type_correction(ctx: &mut Context) -> Result<(), IrError> {
235 struct TypeCorrection {
236 actual_ty: sway_ir::Type,
237 expected_ty: sway_ir::Type,
238 use_instr: sway_ir::Value,
239 use_idx: usize,
240 }
241 fn is_copy_type(ty: &Type, context: &Context) -> bool {
243 ty.is_unit(context)
244 || ty.is_never(context)
245 || ty.is_bool(context)
246 || ty.is_ptr(context)
247 || ty.get_uint_width(context).map(|x| x < 256).unwrap_or(false)
248 }
249
250 let mut instrs_to_fix = Vec::new();
251 for module in ctx.module_iter() {
252 for function in module.function_iter(ctx) {
253 for (_block, instr) in function.instruction_iter(ctx).collect::<Vec<_>>() {
254 match &instr.get_instruction(ctx).unwrap().op {
255 InstOp::Call(callee, actual_params) => {
256 let formal_params: Vec<_> = callee.args_iter(ctx).collect();
257 for (param_idx, (actual_param, (_, formal_param))) in
258 actual_params.iter().zip(formal_params.iter()).enumerate()
259 {
260 let actual_ty = actual_param.get_type(ctx).unwrap();
261 let formal_ty = formal_param.get_type(ctx).unwrap();
262 if actual_ty != formal_ty {
263 instrs_to_fix.push(TypeCorrection {
264 actual_ty,
265 expected_ty: formal_ty,
266 use_instr: instr,
267 use_idx: param_idx,
268 });
269 }
270 }
271 }
272 InstOp::AsmBlock(_block, _args) => {
273 let op = &instr.get_instruction(ctx).unwrap().op;
275 let args = op
276 .get_operands()
277 .iter()
278 .enumerate()
279 .map(|(idx, init)| (idx, init.get_type(ctx).unwrap()))
280 .collect::<Vec<_>>();
281 for (arg_idx, arg_ty) in args {
282 if !is_copy_type(&arg_ty, ctx) {
283 instrs_to_fix.push(TypeCorrection {
284 actual_ty: arg_ty,
285 expected_ty: Type::new_ptr(ctx, arg_ty),
286 use_instr: instr,
287 use_idx: arg_idx,
288 });
289 }
290 }
291 }
292 InstOp::GetElemPtr {
293 base,
294 elem_ptr_ty,
295 indices,
296 } => {
297 let base_ty = base.get_type(ctx).unwrap();
298 if let (Some(base_pointee_ty), Some(elem_inner_ty)) = (
299 base_ty.get_pointee_type(ctx),
300 elem_ptr_ty.get_pointee_type(ctx),
301 ) {
302 if let Some(base_pointee_pointee_ty) =
304 base_pointee_ty.get_pointee_type(ctx)
305 {
306 let indexed_ty =
308 base_pointee_pointee_ty.get_value_indexed_type(ctx, indices);
309 if indexed_ty.is_some_and(|ty| ty == elem_inner_ty) {
310 instrs_to_fix.push(TypeCorrection {
311 actual_ty: base_ty,
312 expected_ty: base_pointee_ty,
313 use_instr: instr,
314 use_idx: indices.len(),
315 });
316 }
317 }
318 } else {
319 let elem_ptr_ty = *elem_ptr_ty;
321 let indices = indices.clone(); let pointer_to_base = Type::new_ptr(ctx, base_ty);
323 if pointer_to_base.get_value_indexed_type(ctx, &indices)
324 == Some(elem_ptr_ty)
325 {
326 instrs_to_fix.push(TypeCorrection {
327 actual_ty: base_ty,
328 expected_ty: pointer_to_base,
329 use_instr: instr,
330 use_idx: indices.len(),
331 });
332 }
333 }
334 }
335 InstOp::Store {
336 dst_val_ptr,
337 stored_val,
338 } => {
339 let dst_ty = dst_val_ptr.get_type(ctx).unwrap();
340 let stored_ty = stored_val.get_type(ctx).unwrap();
341 if let Some(dst_pointee_ty) = dst_ty.get_pointee_type(ctx) {
342 if let Some(dst_pointee_pointee_ty) =
344 dst_pointee_ty.get_pointee_type(ctx)
345 {
346 if dst_pointee_pointee_ty == stored_ty {
348 instrs_to_fix.push(TypeCorrection {
349 actual_ty: dst_ty,
350 expected_ty: dst_pointee_ty,
351 use_instr: instr,
352 use_idx: 0,
353 });
354 }
355 } else if let Some(stored_pointee_ty) = stored_ty.get_pointee_type(ctx)
356 {
357 if dst_pointee_ty == stored_pointee_ty {
360 instrs_to_fix.push(TypeCorrection {
361 actual_ty: stored_ty,
362 expected_ty: stored_pointee_ty,
363 use_instr: instr,
364 use_idx: 1,
365 });
366 }
367 }
368 } else {
369 let pointer_to_dst = Type::new_ptr(ctx, dst_ty);
371 if pointer_to_dst == stored_ty {
372 instrs_to_fix.push(TypeCorrection {
373 actual_ty: dst_ty,
374 expected_ty: pointer_to_dst,
375 use_instr: instr,
376 use_idx: 0,
377 });
378 }
379 }
380 }
381 InstOp::Ret(ret_val, ret_ty) => {
382 if let Some(ret_val_pointee_ty) = ret_val
383 .get_type(ctx)
384 .and_then(|ret_val_ty| ret_val_ty.get_pointee_type(ctx))
385 {
386 if ret_val_pointee_ty == *ret_ty {
387 instrs_to_fix.push(TypeCorrection {
388 actual_ty: ret_val.get_type(ctx).unwrap(),
389 expected_ty: *ret_ty,
390 use_instr: instr,
391 use_idx: 0,
392 });
393 }
394 }
395 }
396 _ => (),
397 }
398 }
399 }
400 }
401
402 for TypeCorrection {
403 actual_ty,
404 expected_ty,
405 use_instr,
406 use_idx,
407 } in instrs_to_fix
408 {
409 let function = use_instr.get_instruction(ctx).unwrap().get_function(ctx);
410 if expected_ty
411 .get_pointee_type(ctx)
412 .is_some_and(|pointee| pointee == actual_ty)
413 {
414 let actual_use = use_instr.get_instruction(ctx).unwrap().op.get_operands()[use_idx];
418 if let Some(InstOp::Load(src_ptr)) = actual_use.get_instruction(ctx).map(|i| &i.op) {
419 let src_ptr = *src_ptr;
420 use_instr
421 .get_instruction_mut(ctx)
422 .unwrap()
423 .op
424 .set_operand(src_ptr, use_idx);
425 } else {
426 let parent_block = use_instr.get_instruction(ctx).unwrap().parent;
427 let new_local = function.new_unique_local_var(
428 ctx,
429 "type_fix".to_string(),
430 actual_ty,
431 None,
432 true,
433 );
434 let new_local =
435 Value::new_instruction(ctx, parent_block, InstOp::GetLocal(new_local));
436 let store = Value::new_instruction(
437 ctx,
438 parent_block,
439 InstOp::Store {
440 dst_val_ptr: new_local,
441 stored_val: actual_use,
442 },
443 );
444 let mut inserter = InstructionInserter::new(
445 ctx,
446 parent_block,
447 sway_ir::InsertionPosition::Before(use_instr),
448 );
449 inserter.insert_slice(&[new_local, store]);
450 use_instr
452 .get_instruction_mut(ctx)
453 .unwrap()
454 .op
455 .set_operand(new_local, use_idx);
456 }
457 } else if actual_ty
458 .get_pointee_type(ctx)
459 .is_some_and(|pointee| pointee == expected_ty)
460 {
461 let load = Value::new_instruction(
463 ctx,
464 use_instr.get_instruction(ctx).unwrap().parent,
465 InstOp::Load(use_instr.get_instruction(ctx).unwrap().op.get_operands()[use_idx]),
466 );
467 let mut inserter = InstructionInserter::new(
468 ctx,
469 use_instr.get_instruction(ctx).unwrap().parent,
470 sway_ir::InsertionPosition::Before(use_instr),
471 );
472 inserter.insert_slice(&[load]);
473 use_instr
475 .get_instruction_mut(ctx)
476 .unwrap()
477 .op
478 .set_operand(load, use_idx);
479 }
480 }
481 Ok(())
482}