sway_ir/
verify.rs

1//! Code to validate the IR in a [`Context`].
2//!
3//! During creation, deserialization and optimization the IR should be verified to be in a
4//! consistent valid state, using the functions in this module.
5
6use itertools::Itertools;
7
8use crate::{
9    context::Context,
10    error::IrError,
11    function::Function,
12    instruction::{FuelVmInstruction, InstOp, Predicate},
13    irtype::Type,
14    metadata::{MetadataIndex, Metadatum},
15    printer,
16    value::{Value, ValueDatum},
17    variable::LocalVar,
18    AnalysisResult, AnalysisResultT, AnalysisResults, BinaryOpKind, Block, BlockArgument,
19    BranchToWithArgs, Doc, GlobalVar, Module, Pass, PassMutability, ScopedPass, TypeOption,
20    UnaryOpKind,
21};
22
23pub struct ModuleVerifierResult;
24impl AnalysisResultT for ModuleVerifierResult {}
25
26/// Verify module
27pub fn module_verifier(
28    context: &Context,
29    _analyses: &AnalysisResults,
30    module: Module,
31) -> Result<AnalysisResult, IrError> {
32    context.verify_module(module)?;
33    Ok(Box::new(ModuleVerifierResult))
34}
35
36pub const MODULE_VERIFIER_NAME: &str = "module-verifier";
37
38pub fn create_module_verifier_pass() -> Pass {
39    Pass {
40        name: MODULE_VERIFIER_NAME,
41        descr: "Verify module",
42        deps: vec![],
43        runner: ScopedPass::ModulePass(PassMutability::Analysis(module_verifier)),
44    }
45}
46
47impl Context<'_> {
48    /// Verify the contents of this [`Context`] is valid.
49    pub fn verify(self) -> Result<Self, IrError> {
50        for (module, _) in &self.modules {
51            let module = Module(module);
52            self.verify_module(module)?;
53        }
54        Ok(self)
55    }
56
57    fn verify_module(&self, module: Module) -> Result<(), IrError> {
58        for function in module.function_iter(self) {
59            self.verify_function(module, function)?;
60        }
61
62        // Check that globals have initializers if they are not mutable.
63        for global in &self.modules[module.0].global_variables {
64            if !global.1.is_mutable(self) && global.1.get_initializer(self).is_none() {
65                let global_name = module.lookup_global_variable_name(self, global.1);
66                return Err(IrError::VerifyGlobalMissingInitializer(
67                    global_name.unwrap_or_else(|| "<unknown>".to_owned()),
68                ));
69            }
70        }
71        Ok(())
72    }
73
74    fn verify_function(&self, cur_module: Module, function: Function) -> Result<(), IrError> {
75        if function.get_module(self) != cur_module {
76            return Err(IrError::InconsistentParent(
77                function.get_name(self).into(),
78                format!("Module_Index_{:?}", cur_module.0),
79                format!("Module_Index_{:?}", function.get_module(self).0),
80            ));
81        }
82
83        let entry_block = function.get_entry_block(self);
84
85        if entry_block.num_predecessors(self) != 0 {
86            return Err(IrError::VerifyEntryBlockHasPredecessors(
87                function.get_name(self).to_string(),
88                entry_block
89                    .pred_iter(self)
90                    .map(|block| block.get_label(self))
91                    .collect(),
92            ));
93        }
94
95        // Ensure that the entry block arguments are same as function arguments.
96        if function.num_args(self) != entry_block.num_args(self) {
97            return Err(IrError::VerifyBlockArgMalformed);
98        }
99        for ((_, func_arg), block_arg) in function.args_iter(self).zip(entry_block.arg_iter(self)) {
100            if func_arg != block_arg {
101                return Err(IrError::VerifyBlockArgMalformed);
102            }
103        }
104
105        // Check that locals have initializers if they aren't mutable.
106        // TODO: This check is disabled because we incorrect create
107        //       immutable locals without initializers at many places.
108        // for local in &self.functions[function.0].local_storage {
109        //     if !local.1.is_mutable(self) && local.1.get_initializer(self).is_none() {
110        //         return Err(IrError::VerifyLocalMissingInitializer(
111        //             local.0.to_string(),
112        //             function.get_name(self).to_string(),
113        //         ));
114        //     }
115        // }
116
117        for block in function.block_iter(self) {
118            self.verify_block(cur_module, function, block)?;
119        }
120        self.verify_metadata(function.get_metadata(self))?;
121        Ok(())
122    }
123
124    fn verify_block(
125        &self,
126        cur_module: Module,
127        cur_function: Function,
128        cur_block: Block,
129    ) -> Result<(), IrError> {
130        if cur_block.get_function(self) != cur_function {
131            return Err(IrError::InconsistentParent(
132                cur_block.get_label(self),
133                cur_function.get_name(self).into(),
134                cur_block.get_function(self).get_name(self).into(),
135            ));
136        }
137
138        if cur_block.num_instructions(self) <= 1 && cur_block.num_predecessors(self) == 0 {
139            // Empty unreferenced blocks are a harmless artefact.
140            return Ok(());
141        }
142
143        for (arg_idx, arg_val) in cur_block.arg_iter(self).enumerate() {
144            match self.values[arg_val.0].value {
145                ValueDatum::Argument(BlockArgument { idx, .. }) if idx == arg_idx => (),
146                _ => return Err(IrError::VerifyBlockArgMalformed),
147            }
148        }
149
150        let r = InstructionVerifier {
151            context: self,
152            cur_module,
153            cur_function,
154            cur_block,
155        }
156        .verify_instructions();
157
158        // Help to understand the verification failure
159        // If the error knows the problematic value, prints everything with the error highlighted,
160        // if not, print only the block to help pinpoint the issue
161        if let Err(error) = &r {
162            println!(
163                "Verification failed at {}::{}",
164                cur_function.get_name(self),
165                cur_block.get_label(self)
166            );
167
168            let block = if let Some(problematic_value) = error.get_problematic_value() {
169                printer::context_print(self, &|current_value: &Value, doc: Doc| {
170                    if *current_value == *problematic_value {
171                        doc.append(Doc::text_line(format!("\x1b[0;31m^ {}\x1b[0m", error)))
172                    } else {
173                        doc
174                    }
175                })
176            } else {
177                printer::block_print(self, cur_function, cur_block, &|_, doc| doc)
178            };
179
180            println!("{}", block);
181        }
182
183        r?;
184
185        let (last_is_term, num_terms) =
186            cur_block
187                .instruction_iter(self)
188                .fold((false, 0), |(_, n), ins| {
189                    if ins.is_terminator(self) {
190                        (true, n + 1)
191                    } else {
192                        (false, n)
193                    }
194                });
195        if !last_is_term {
196            Err(IrError::MissingTerminator(
197                cur_block.get_label(self).clone(),
198            ))
199        } else if num_terms != 1 {
200            Err(IrError::MisplacedTerminator(
201                cur_block.get_label(self).clone(),
202            ))
203        } else {
204            Ok(())
205        }
206    }
207
208    fn verify_metadata(&self, md_idx: Option<MetadataIndex>) -> Result<(), IrError> {
209        // For now we check only that struct tags are valid identifiers.
210        if let Some(md_idx) = md_idx {
211            match &self.metadata[md_idx.0] {
212                Metadatum::List(md_idcs) => {
213                    for md_idx in md_idcs {
214                        self.verify_metadata(Some(*md_idx))?;
215                    }
216                }
217                Metadatum::Struct(tag, ..) => {
218                    // We could import Regex to match it, but it's a simple identifier style pattern:
219                    // alpha start char, alphanumeric for the rest, or underscore anywhere.
220                    if tag.is_empty() {
221                        return Err(IrError::InvalidMetadatum(
222                            "Struct has empty tag.".to_owned(),
223                        ));
224                    }
225                    let mut chs = tag.chars();
226                    let ch0 = chs.next().unwrap();
227                    if !(ch0.is_ascii_alphabetic() || ch0 == '_')
228                        || chs.any(|ch| !(ch.is_ascii_alphanumeric() || ch == '_'))
229                    {
230                        return Err(IrError::InvalidMetadatum(format!(
231                            "Invalid struct tag: '{tag}'."
232                        )));
233                    }
234                }
235                _otherwise => (),
236            }
237        }
238        Ok(())
239    }
240}
241
242struct InstructionVerifier<'a, 'eng> {
243    context: &'a Context<'eng>,
244    cur_module: Module,
245    cur_function: Function,
246    cur_block: Block,
247}
248
249impl InstructionVerifier<'_, '_> {
250    fn verify_instructions(&self) -> Result<(), IrError> {
251        for ins in self.cur_block.instruction_iter(self.context) {
252            let value_content = &self.context.values[ins.0];
253            let ValueDatum::Instruction(instruction) = &value_content.value else {
254                unreachable!("The value must be an instruction, because it is retrieved via block instruction iterator.")
255            };
256
257            if instruction.parent != self.cur_block {
258                return Err(IrError::InconsistentParent(
259                    format!("Instr_{:?}", ins.0),
260                    self.cur_block.get_label(self.context),
261                    instruction.parent.get_label(self.context),
262                ));
263            }
264
265            match &instruction.op {
266                InstOp::AsmBlock(..) => (),
267                InstOp::BitCast(value, ty) => self.verify_bitcast(value, ty)?,
268                InstOp::UnaryOp { op, arg } => self.verify_unary_op(op, arg)?,
269                InstOp::BinaryOp { op, arg1, arg2 } => self.verify_binary_op(op, arg1, arg2)?,
270                InstOp::Branch(block) => self.verify_br(block)?,
271                InstOp::Call(func, args) => self.verify_call(func, args)?,
272                InstOp::CastPtr(val, ty) => self.verify_cast_ptr(val, ty)?,
273                InstOp::Cmp(pred, lhs_value, rhs_value) => {
274                    self.verify_cmp(pred, lhs_value, rhs_value)?
275                }
276                InstOp::ConditionalBranch {
277                    cond_value,
278                    true_block,
279                    false_block,
280                } => self.verify_cbr(cond_value, true_block, false_block)?,
281                InstOp::ContractCall {
282                    params,
283                    coins,
284                    asset_id,
285                    gas,
286                    ..
287                } => self.verify_contract_call(params, coins, asset_id, gas)?,
288                // XXX move the fuelvm verification into a module
289                InstOp::FuelVm(fuel_vm_instr) => match fuel_vm_instr {
290                    FuelVmInstruction::Gtf { index, tx_field_id } => {
291                        self.verify_gtf(index, tx_field_id)?
292                    }
293                    FuelVmInstruction::Log {
294                        log_val,
295                        log_ty,
296                        log_id,
297                    } => self.verify_log(log_val, log_ty, log_id)?,
298                    FuelVmInstruction::ReadRegister(_) => (),
299                    FuelVmInstruction::JmpMem => (),
300                    FuelVmInstruction::Revert(val) => self.verify_revert(val)?,
301                    FuelVmInstruction::Smo {
302                        recipient,
303                        message,
304                        message_size,
305                        coins,
306                    } => self.verify_smo(recipient, message, message_size, coins)?,
307                    FuelVmInstruction::StateClear {
308                        key,
309                        number_of_slots,
310                    } => self.verify_state_clear(key, number_of_slots)?,
311                    FuelVmInstruction::StateLoadWord(key) => self.verify_state_load_word(key)?,
312                    FuelVmInstruction::StateLoadQuadWord {
313                        load_val: dst_val,
314                        key,
315                        number_of_slots,
316                    }
317                    | FuelVmInstruction::StateStoreQuadWord {
318                        stored_val: dst_val,
319                        key,
320                        number_of_slots,
321                    } => self.verify_state_access_quad(dst_val, key, number_of_slots)?,
322                    FuelVmInstruction::StateStoreWord {
323                        stored_val: dst_val,
324                        key,
325                    } => self.verify_state_store_word(dst_val, key)?,
326                    FuelVmInstruction::WideUnaryOp { op, result, arg } => {
327                        self.verify_wide_unary_op(op, result, arg)?
328                    }
329                    FuelVmInstruction::WideBinaryOp {
330                        op,
331                        result,
332                        arg1,
333                        arg2,
334                    } => self.verify_wide_binary_op(op, result, arg1, arg2)?,
335                    FuelVmInstruction::WideModularOp {
336                        op,
337                        result,
338                        arg1,
339                        arg2,
340                        arg3,
341                    } => self.verify_wide_modular_op(op, result, arg1, arg2, arg3)?,
342                    FuelVmInstruction::WideCmpOp { op, arg1, arg2 } => {
343                        self.verify_wide_cmp(op, arg1, arg2)?
344                    }
345                    FuelVmInstruction::Retd { .. } => (),
346                },
347                InstOp::GetElemPtr {
348                    base,
349                    elem_ptr_ty,
350                    indices,
351                } => self.verify_get_elem_ptr(&ins, base, elem_ptr_ty, indices)?,
352                InstOp::GetLocal(local_var) => self.verify_get_local(local_var)?,
353                InstOp::GetGlobal(global_var) => self.verify_get_global(global_var)?,
354                InstOp::GetConfig(_, name) => self.verify_get_config(self.cur_module, name)?,
355                InstOp::IntToPtr(value, ty) => self.verify_int_to_ptr(value, ty)?,
356                InstOp::Load(ptr) => self.verify_load(ptr)?,
357                InstOp::MemCopyBytes {
358                    dst_val_ptr,
359                    src_val_ptr,
360                    byte_len,
361                } => self.verify_mem_copy_bytes(dst_val_ptr, src_val_ptr, byte_len)?,
362                InstOp::MemCopyVal {
363                    dst_val_ptr,
364                    src_val_ptr,
365                } => self.verify_mem_copy_val(dst_val_ptr, src_val_ptr)?,
366                InstOp::Nop => (),
367                InstOp::PtrToInt(val, ty) => self.verify_ptr_to_int(val, ty)?,
368                InstOp::Ret(val, ty) => self.verify_ret(val, ty)?,
369                InstOp::Store {
370                    dst_val_ptr,
371                    stored_val,
372                } => self.verify_store(&ins, dst_val_ptr, stored_val)?,
373            };
374
375            // Verify the instruction metadata too.
376            self.context.verify_metadata(value_content.metadata)?;
377        }
378
379        Ok(())
380    }
381
382    fn verify_bitcast(&self, value: &Value, ty: &Type) -> Result<(), IrError> {
383        // The bitsize of bools and unit is 1 which obviously won't match a typical uint.  LLVM
384        // would use `trunc` or `zext` to make types match sizes before casting.  Until we have
385        // similar we'll just make sure the sizes are <= 64 bits.
386        let val_ty = value
387            .get_type(self.context)
388            .ok_or(IrError::VerifyBitcastUnknownSourceType)?;
389        if self.type_bit_size(&val_ty).is_some_and(|sz| sz > 64)
390            || self.type_bit_size(ty).is_some_and(|sz| sz > 64)
391        {
392            Err(IrError::VerifyBitcastBetweenInvalidTypes(
393                val_ty.as_string(self.context),
394                ty.as_string(self.context),
395            ))
396        } else {
397            Ok(())
398        }
399    }
400
401    fn verify_unary_op(&self, op: &UnaryOpKind, arg: &Value) -> Result<(), IrError> {
402        let arg_ty = arg
403            .get_type(self.context)
404            .ok_or(IrError::VerifyUnaryOpIncorrectArgType)?;
405        match op {
406            UnaryOpKind::Not => {
407                if !arg_ty.is_uint(self.context) && !arg_ty.is_b256(self.context) {
408                    return Err(IrError::VerifyUnaryOpIncorrectArgType);
409                }
410            }
411        }
412
413        Ok(())
414    }
415
416    fn verify_wide_cmp(&self, _: &Predicate, arg1: &Value, arg2: &Value) -> Result<(), IrError> {
417        let arg1_ty = arg1
418            .get_type(self.context)
419            .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
420        let arg2_ty = arg2
421            .get_type(self.context)
422            .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
423
424        if arg1_ty.is_ptr(self.context) && arg2_ty.is_ptr(self.context) {
425            Ok(())
426        } else {
427            Err(IrError::VerifyBinaryOpIncorrectArgType)
428        }
429    }
430
431    fn verify_wide_modular_op(
432        &self,
433        _op: &BinaryOpKind,
434        result: &Value,
435        arg1: &Value,
436        arg2: &Value,
437        arg3: &Value,
438    ) -> Result<(), IrError> {
439        let result_ty = result
440            .get_type(self.context)
441            .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
442        let arg1_ty = arg1
443            .get_type(self.context)
444            .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
445        let arg2_ty = arg2
446            .get_type(self.context)
447            .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
448        let arg3_ty = arg3
449            .get_type(self.context)
450            .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
451
452        if !arg1_ty.is_ptr(self.context)
453            || !arg2_ty.is_ptr(self.context)
454            || !arg3_ty.is_ptr(self.context)
455            || !result_ty.is_ptr(self.context)
456        {
457            return Err(IrError::VerifyBinaryOpIncorrectArgType);
458        }
459
460        Ok(())
461    }
462
463    fn verify_wide_binary_op(
464        &self,
465        op: &BinaryOpKind,
466        result: &Value,
467        arg1: &Value,
468        arg2: &Value,
469    ) -> Result<(), IrError> {
470        let result_ty = result
471            .get_type(self.context)
472            .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
473        let arg1_ty = arg1
474            .get_type(self.context)
475            .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
476        let arg2_ty = arg2
477            .get_type(self.context)
478            .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
479
480        match op {
481            // Shifts rhs are 64 bits
482            BinaryOpKind::Lsh | BinaryOpKind::Rsh => {
483                if !arg1_ty.is_ptr(self.context)
484                    || !arg2_ty.is_uint64(self.context)
485                    || !result_ty.is_ptr(self.context)
486                {
487                    return Err(IrError::VerifyBinaryOpIncorrectArgType);
488                }
489            }
490            BinaryOpKind::Add
491            | BinaryOpKind::Sub
492            | BinaryOpKind::Mul
493            | BinaryOpKind::Div
494            | BinaryOpKind::And
495            | BinaryOpKind::Or
496            | BinaryOpKind::Xor
497            | BinaryOpKind::Mod => {
498                if !arg1_ty.is_ptr(self.context)
499                    || !arg2_ty.is_ptr(self.context)
500                    || !result_ty.is_ptr(self.context)
501                {
502                    return Err(IrError::VerifyBinaryOpIncorrectArgType);
503                }
504            }
505        }
506
507        Ok(())
508    }
509
510    fn verify_wide_unary_op(
511        &self,
512        _op: &UnaryOpKind,
513        result: &Value,
514        arg: &Value,
515    ) -> Result<(), IrError> {
516        let result_ty = result
517            .get_type(self.context)
518            .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
519        let arg_ty = arg
520            .get_type(self.context)
521            .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
522
523        if !arg_ty.is_ptr(self.context) || !result_ty.is_ptr(self.context) {
524            return Err(IrError::VerifyBinaryOpIncorrectArgType);
525        }
526
527        Ok(())
528    }
529
530    fn verify_binary_op(
531        &self,
532        op: &BinaryOpKind,
533        arg1: &Value,
534        arg2: &Value,
535    ) -> Result<(), IrError> {
536        let arg1_ty = arg1
537            .get_type(self.context)
538            .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
539        let arg2_ty = arg2
540            .get_type(self.context)
541            .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
542
543        match op {
544            // Shifts can have the rhs with different type
545            BinaryOpKind::Lsh | BinaryOpKind::Rsh => {
546                let is_lhs_ok = arg1_ty.is_uint(self.context) || arg1_ty.is_b256(self.context);
547                if !is_lhs_ok || !arg2_ty.is_uint(self.context) {
548                    return Err(IrError::VerifyBinaryOpIncorrectArgType);
549                }
550            }
551            BinaryOpKind::Add => {
552                if !(arg1_ty.eq(self.context, &arg2_ty) && arg1_ty.is_uint(self.context)
553                    || arg1_ty.is_ptr(self.context) && arg2_ty.is_uint64(self.context))
554                {
555                    return Err(IrError::VerifyBinaryOpIncorrectArgType);
556                }
557            }
558            BinaryOpKind::Sub | BinaryOpKind::Mul | BinaryOpKind::Div | BinaryOpKind::Mod => {
559                if !arg1_ty.eq(self.context, &arg2_ty) || !arg1_ty.is_uint(self.context) {
560                    return Err(IrError::VerifyBinaryOpIncorrectArgType);
561                }
562            }
563            BinaryOpKind::And | BinaryOpKind::Or | BinaryOpKind::Xor => {
564                if !arg1_ty.eq(self.context, &arg2_ty)
565                    || !(arg1_ty.is_uint(self.context) || arg1_ty.is_b256(self.context))
566                {
567                    return Err(IrError::VerifyBinaryOpIncorrectArgType);
568                }
569            }
570        }
571
572        Ok(())
573    }
574
575    fn verify_br(&self, dest_block: &BranchToWithArgs) -> Result<(), IrError> {
576        if !self
577            .cur_function
578            .block_iter(self.context)
579            .contains(&dest_block.block)
580        {
581            Err(IrError::VerifyBranchToMissingBlock(
582                self.context.blocks[dest_block.block.0].label.clone(),
583            ))
584        } else {
585            self.verify_dest_args(dest_block)
586        }
587    }
588
589    fn verify_call(&self, callee: &Function, args: &[Value]) -> Result<(), IrError> {
590        let callee_content = &self.context.functions[callee.0];
591        if !self.cur_module.function_iter(self.context).contains(callee) {
592            return Err(IrError::VerifyCallToMissingFunction(
593                callee_content.name.clone(),
594            ));
595        }
596
597        let callee_arg_types = callee_content
598            .arguments
599            .iter()
600            .map(|(_, arg_val)| {
601                if let ValueDatum::Argument(BlockArgument { ty, .. }) =
602                    &self.context.values[arg_val.0].value
603                {
604                    Ok(*ty)
605                } else {
606                    Err(IrError::VerifyArgumentValueIsNotArgument(
607                        callee_content.name.clone(),
608                    ))
609                }
610            })
611            .collect::<Result<Vec<Type>, IrError>>()?;
612
613        for (opt_caller_arg_type, callee_arg_type) in args
614            .iter()
615            .map(|val| val.get_type(self.context))
616            .zip(callee_arg_types.iter())
617        {
618            if opt_caller_arg_type.is_none() {
619                return Err(IrError::VerifyUntypedValuePassedToFunction);
620            }
621
622            let caller_arg_type = opt_caller_arg_type.as_ref().unwrap();
623            if !caller_arg_type.eq(self.context, callee_arg_type) {
624                return Err(IrError::VerifyCallArgTypeMismatch(
625                    callee_content.name.clone(),
626                    caller_arg_type.as_string(self.context),
627                    callee_arg_type.as_string(self.context),
628                ));
629            }
630        }
631
632        Ok(())
633    }
634
635    fn verify_cast_ptr(&self, val: &Value, ty: &Type) -> Result<(), IrError> {
636        let _ = self.get_ptr_type(val, IrError::VerifyPtrCastFromNonPointer)?;
637        if !ty.is_ptr(self.context) {
638            Err(IrError::VerifyPtrCastToNonPointer(
639                ty.as_string(self.context),
640            ))
641        } else {
642            Ok(())
643        }
644    }
645
646    fn verify_dest_args(&self, dest: &BranchToWithArgs) -> Result<(), IrError> {
647        if dest.block.num_args(self.context) != dest.args.len() {
648            return Err(IrError::VerifyBranchParamsMismatch);
649        }
650        for (arg_idx, dest_param) in dest.block.arg_iter(self.context).enumerate() {
651            match dest.args.get(arg_idx) {
652                Some(actual)
653                    if dest_param
654                        .get_type(self.context)
655                        .unwrap()
656                        .eq(self.context, &actual.get_type(self.context).unwrap()) => {}
657                _ =>
658                // TODO: https://github.com/FuelLabs/sway/pull/2880
659                {
660                    // return Err(IrError::VerifyBranchParamsMismatch)
661                }
662            }
663        }
664        Ok(())
665    }
666
667    fn verify_cbr(
668        &self,
669        cond_val: &Value,
670        true_block: &BranchToWithArgs,
671        false_block: &BranchToWithArgs,
672    ) -> Result<(), IrError> {
673        if !cond_val
674            .get_type(self.context)
675            .is(Type::is_bool, self.context)
676        {
677            Err(IrError::VerifyConditionExprNotABool)
678        } else if !self
679            .cur_function
680            .block_iter(self.context)
681            .contains(&true_block.block)
682        {
683            Err(IrError::VerifyBranchToMissingBlock(
684                self.context.blocks[true_block.block.0].label.clone(),
685            ))
686        } else if !self
687            .cur_function
688            .block_iter(self.context)
689            .contains(&false_block.block)
690        {
691            Err(IrError::VerifyBranchToMissingBlock(
692                self.context.blocks[false_block.block.0].label.clone(),
693            ))
694        } else {
695            self.verify_dest_args(true_block)
696                .and_then(|()| self.verify_dest_args(false_block))
697        }
698    }
699
700    fn verify_cmp(
701        &self,
702        _pred: &Predicate,
703        lhs_value: &Value,
704        rhs_value: &Value,
705    ) -> Result<(), IrError> {
706        // Comparisons must be between integers at this stage.
707        match (
708            lhs_value.get_type(self.context),
709            rhs_value.get_type(self.context),
710        ) {
711            (Some(lhs_ty), Some(rhs_ty)) => {
712                if !lhs_ty.eq(self.context, &rhs_ty) {
713                    Err(IrError::VerifyCmpTypeMismatch(
714                        lhs_ty.as_string(self.context),
715                        rhs_ty.as_string(self.context),
716                    ))
717                } else if lhs_ty.is_bool(self.context)
718                    || lhs_ty.is_uint(self.context)
719                    || lhs_ty.is_b256(self.context)
720                {
721                    Ok(())
722                } else {
723                    Err(IrError::VerifyCmpBadTypes(
724                        lhs_ty.as_string(self.context),
725                        rhs_ty.as_string(self.context),
726                    ))
727                }
728            }
729            _otherwise => Err(IrError::VerifyCmpUnknownTypes),
730        }
731    }
732
733    fn verify_contract_call(
734        &self,
735        params: &Value,
736        coins: &Value,
737        asset_id: &Value,
738        gas: &Value,
739    ) -> Result<(), IrError> {
740        if !self.context.experimental.new_encoding {
741            // - The params must be a struct with the B256 address, u64 selector and u64 address to
742            //   user args.
743            // - The coins and gas must be u64s.
744            // - The asset_id must be a B256
745            let fields = params
746                .get_type(self.context)
747                .and_then(|ty| ty.get_pointee_type(self.context))
748                .map_or_else(std::vec::Vec::new, |ty| ty.get_field_types(self.context));
749            if fields.len() != 3
750                || !fields[0].is_b256(self.context)
751                || !fields[1].is_uint64(self.context)
752                || !fields[2].is_uint64(self.context)
753            {
754                Err(IrError::VerifyContractCallBadTypes("params".to_owned()))
755            } else {
756                Ok(())
757            }
758            .and_then(|_| {
759                if coins
760                    .get_type(self.context)
761                    .is(Type::is_uint64, self.context)
762                {
763                    Ok(())
764                } else {
765                    Err(IrError::VerifyContractCallBadTypes("coins".to_owned()))
766                }
767            })
768            .and_then(|_| {
769                if asset_id
770                    .get_type(self.context)
771                    .and_then(|ty| ty.get_pointee_type(self.context))
772                    .is(Type::is_b256, self.context)
773                {
774                    Ok(())
775                } else {
776                    Err(IrError::VerifyContractCallBadTypes("asset_id".to_owned()))
777                }
778            })
779            .and_then(|_| {
780                if gas.get_type(self.context).is(Type::is_uint64, self.context) {
781                    Ok(())
782                } else {
783                    Err(IrError::VerifyContractCallBadTypes("gas".to_owned()))
784                }
785            })
786        } else {
787            Ok(())
788        }
789    }
790
791    fn verify_get_elem_ptr(
792        &self,
793        ins: &Value,
794        base: &Value,
795        elem_ptr_ty: &Type,
796        indices: &[Value],
797    ) -> Result<(), IrError> {
798        let base_ty =
799            self.get_ptr_type(base, |s| IrError::VerifyGepFromNonPointer(s, Some(*ins)))?;
800        if !base_ty.is_aggregate(self.context) {
801            return Err(IrError::VerifyGepOnNonAggregate);
802        }
803
804        let Some(elem_inner_ty) = elem_ptr_ty.get_pointee_type(self.context) else {
805            return Err(IrError::VerifyGepElementTypeNonPointer);
806        };
807
808        if indices.is_empty() {
809            return Err(IrError::VerifyGepInconsistentTypes(
810                "Empty Indices".into(),
811                Some(*base),
812            ));
813        }
814
815        let index_ty = base_ty.get_value_indexed_type(self.context, indices);
816
817        if self.opt_ty_not_eq(&Some(elem_inner_ty), &index_ty) {
818            return Err(IrError::VerifyGepInconsistentTypes(
819                format!(
820                    "Element type \"{}\" versus index type {:?}",
821                    elem_inner_ty.as_string(self.context),
822                    index_ty.map(|x| x.as_string(self.context))
823                ),
824                Some(*ins),
825            ));
826        }
827
828        Ok(())
829    }
830
831    fn verify_get_local(&self, local_var: &LocalVar) -> Result<(), IrError> {
832        if !self.context.functions[self.cur_function.0]
833            .local_storage
834            .values()
835            .any(|var| var == local_var)
836        {
837            Err(IrError::VerifyGetNonExistentPointer)
838        } else {
839            Ok(())
840        }
841    }
842
843    fn verify_get_global(&self, global_var: &GlobalVar) -> Result<(), IrError> {
844        if !self.context.modules[self.cur_module.0]
845            .global_variables
846            .values()
847            .any(|var| var == global_var)
848        {
849            Err(IrError::VerifyGetNonExistentPointer)
850        } else {
851            Ok(())
852        }
853    }
854
855    fn verify_get_config(&self, module: Module, name: &str) -> Result<(), IrError> {
856        if !self.context.modules[module.0].configs.contains_key(name) {
857            Err(IrError::VerifyGetNonExistentPointer)
858        } else {
859            Ok(())
860        }
861    }
862
863    fn verify_gtf(&self, index: &Value, _tx_field_id: &u64) -> Result<(), IrError> {
864        // We should perhaps verify that _tx_field_id fits in a twelve bit immediate
865        if !index.get_type(self.context).is(Type::is_uint, self.context) {
866            Err(IrError::VerifyInvalidGtfIndexType)
867        } else {
868            Ok(())
869        }
870    }
871
872    fn verify_int_to_ptr(&self, value: &Value, ty: &Type) -> Result<(), IrError> {
873        // We want the source value to be an integer and the destination type to be a pointer.
874        let val_ty = value
875            .get_type(self.context)
876            .ok_or(IrError::VerifyIntToPtrUnknownSourceType)?;
877        if !val_ty.is_uint(self.context) {
878            return Err(IrError::VerifyIntToPtrFromNonIntegerType(
879                val_ty.as_string(self.context),
880            ));
881        }
882        if !ty.is_ptr(self.context) {
883            return Err(IrError::VerifyIntToPtrToNonPointer(
884                ty.as_string(self.context),
885            ));
886        }
887
888        Ok(())
889    }
890
891    fn verify_load(&self, src_val: &Value) -> Result<(), IrError> {
892        // Just confirm `src_val` is a pointer.
893        self.get_ptr_type(src_val, IrError::VerifyLoadFromNonPointer)
894            .map(|_| ())
895    }
896
897    fn verify_log(&self, log_val: &Value, log_ty: &Type, log_id: &Value) -> Result<(), IrError> {
898        if !log_id
899            .get_type(self.context)
900            .is(Type::is_uint64, self.context)
901        {
902            return Err(IrError::VerifyLogId);
903        }
904
905        if self.opt_ty_not_eq(&log_val.get_type(self.context), &Some(*log_ty)) {
906            return Err(IrError::VerifyLogMismatchedTypes);
907        }
908
909        Ok(())
910    }
911
912    fn verify_mem_copy_bytes(
913        &self,
914        dst_val_ptr: &Value,
915        src_val_ptr: &Value,
916        _byte_len: &u64,
917    ) -> Result<(), IrError> {
918        // Just confirm both values are pointers.
919        self.get_ptr_type(dst_val_ptr, IrError::VerifyMemcopyNonPointer)
920            .and_then(|_| self.get_ptr_type(src_val_ptr, IrError::VerifyMemcopyNonPointer))
921            .map(|_| ())
922    }
923
924    fn verify_mem_copy_val(&self, dst_val_ptr: &Value, src_val_ptr: &Value) -> Result<(), IrError> {
925        // Check both types are pointers and to the same type.
926        self.get_ptr_type(dst_val_ptr, IrError::VerifyMemcopyNonPointer)
927            .and_then(|dst_ty| {
928                self.get_ptr_type(src_val_ptr, IrError::VerifyMemcopyNonPointer)
929                    .map(|src_ty| (dst_ty, src_ty))
930            })
931            .and_then(|(dst_ty, src_ty)| {
932                dst_ty
933                    .eq(self.context, &src_ty)
934                    .then_some(())
935                    .ok_or_else(|| {
936                        IrError::VerifyMemcopyMismatchedTypes(
937                            dst_ty.as_string(self.context),
938                            src_ty.as_string(self.context),
939                        )
940                    })
941            })
942    }
943
944    fn verify_ptr_to_int(&self, _val: &Value, ty: &Type) -> Result<(), IrError> {
945        // XXX Casting pointers to integers is a low level operation which needs to be verified in
946        // the target specific verifier.  e.g., for Fuel it is assumed that b256s are 'reference
947        // types' and you can to a ptr_to_int on them, but for target agnostic IR this isn't true.
948        //
949        // let _ = self.get_ptr_type(val, IrError::VerifyPtrCastFromNonPointer)?;
950        if !ty.is_uint(self.context) {
951            Err(IrError::VerifyPtrToIntToNonInteger(
952                ty.as_string(self.context),
953            ))
954        } else {
955            Ok(())
956        }
957    }
958
959    fn verify_ret(&self, val: &Value, ty: &Type) -> Result<(), IrError> {
960        if !self
961            .cur_function
962            .get_return_type(self.context)
963            .eq(self.context, ty)
964            || self.opt_ty_not_eq(&val.get_type(self.context), &Some(*ty))
965        {
966            Err(IrError::VerifyReturnMismatchedTypes(
967                self.cur_function.get_name(self.context).to_string(),
968            ))
969        } else {
970            Ok(())
971        }
972    }
973
974    fn verify_revert(&self, val: &Value) -> Result<(), IrError> {
975        if !val.get_type(self.context).is(Type::is_uint64, self.context) {
976            Err(IrError::VerifyRevertCodeBadType)
977        } else {
978            Ok(())
979        }
980    }
981
982    fn verify_smo(
983        &self,
984        recipient: &Value,
985        message: &Value,
986        message_size: &Value,
987        coins: &Value,
988    ) -> Result<(), IrError> {
989        // Check that the first operand is a `b256` representing the recipient address.
990        let recipient = self.get_ptr_type(recipient, IrError::VerifySmoRecipientNonPointer)?;
991        if !recipient.is_b256(self.context) {
992            return Err(IrError::VerifySmoRecipientBadType);
993        }
994
995        // Check that the second operand is a struct with two fields
996        let struct_ty = self.get_ptr_type(message, IrError::VerifySmoMessageNonPointer)?;
997
998        if !struct_ty.is_struct(self.context) {
999            return Err(IrError::VerifySmoBadMessageType);
1000        }
1001        let fields = struct_ty.get_field_types(self.context);
1002        if fields.len() != 2 {
1003            return Err(IrError::VerifySmoBadMessageType);
1004        }
1005
1006        // Check that the second operand is a `u64` representing the message size.
1007        if !message_size
1008            .get_type(self.context)
1009            .is(Type::is_uint64, self.context)
1010        {
1011            return Err(IrError::VerifySmoMessageSize);
1012        }
1013
1014        // Check that the third operand is a `u64` representing the amount of coins being sent.
1015        if !coins
1016            .get_type(self.context)
1017            .is(Type::is_uint64, self.context)
1018        {
1019            return Err(IrError::VerifySmoCoins);
1020        }
1021
1022        Ok(())
1023    }
1024
1025    fn verify_state_clear(&self, key: &Value, number_of_slots: &Value) -> Result<(), IrError> {
1026        let key_type = self.get_ptr_type(key, IrError::VerifyStateKeyNonPointer)?;
1027        if !key_type.is_b256(self.context) {
1028            Err(IrError::VerifyStateKeyBadType)
1029        } else if !number_of_slots
1030            .get_type(self.context)
1031            .is(Type::is_uint, self.context)
1032        {
1033            Err(IrError::VerifyStateAccessNumOfSlots)
1034        } else {
1035            Ok(())
1036        }
1037    }
1038
1039    fn verify_state_access_quad(
1040        &self,
1041        dst_val: &Value,
1042        key: &Value,
1043        number_of_slots: &Value,
1044    ) -> Result<(), IrError> {
1045        let dst_ty = self.get_ptr_type(dst_val, IrError::VerifyStateAccessQuadNonPointer)?;
1046        if !dst_ty.is_b256(self.context) {
1047            return Err(IrError::VerifyStateDestBadType(
1048                dst_ty.as_string(self.context),
1049            ));
1050        }
1051        let key_type = self.get_ptr_type(key, IrError::VerifyStateKeyNonPointer)?;
1052        if !key_type.is_b256(self.context) {
1053            return Err(IrError::VerifyStateKeyBadType);
1054        }
1055        if !number_of_slots
1056            .get_type(self.context)
1057            .is(Type::is_uint, self.context)
1058        {
1059            return Err(IrError::VerifyStateAccessNumOfSlots);
1060        }
1061        Ok(())
1062    }
1063
1064    fn verify_state_load_word(&self, key: &Value) -> Result<(), IrError> {
1065        let key_type = self.get_ptr_type(key, IrError::VerifyStateKeyNonPointer)?;
1066        if !key_type.is_b256(self.context) {
1067            Err(IrError::VerifyStateKeyBadType)
1068        } else {
1069            Ok(())
1070        }
1071    }
1072
1073    fn verify_state_store_word(&self, dst_val: &Value, key: &Value) -> Result<(), IrError> {
1074        let key_type = self.get_ptr_type(key, IrError::VerifyStateKeyNonPointer)?;
1075        if !key_type.is_b256(self.context) {
1076            Err(IrError::VerifyStateKeyBadType)
1077        } else if !dst_val
1078            .get_type(self.context)
1079            .is(Type::is_uint, self.context)
1080        {
1081            Err(IrError::VerifyStateDestBadType(
1082                Type::get_uint64(self.context).as_string(self.context),
1083            ))
1084        } else {
1085            Ok(())
1086        }
1087    }
1088
1089    fn verify_store(
1090        &self,
1091        ins: &Value,
1092        dst_val: &Value,
1093        stored_val: &Value,
1094    ) -> Result<(), IrError> {
1095        let dst_ty = self.get_ptr_type(dst_val, IrError::VerifyStoreToNonPointer)?;
1096        let stored_ty = stored_val.get_type(self.context);
1097        if self.opt_ty_not_eq(&Some(dst_ty), &stored_ty) {
1098            Err(IrError::VerifyStoreMismatchedTypes(Some(*ins)))
1099        } else {
1100            Ok(())
1101        }
1102    }
1103
1104    //----------------------------------------------------------------------------------------------
1105
1106    // This is a really common operation above... calling `Value::get_type()` and then failing when
1107    // two don't match.
1108    fn opt_ty_not_eq(&self, l_ty: &Option<Type>, r_ty: &Option<Type>) -> bool {
1109        l_ty.is_none() || r_ty.is_none() || !l_ty.unwrap().eq(self.context, r_ty.as_ref().unwrap())
1110    }
1111
1112    fn get_ptr_type<F: FnOnce(String) -> IrError>(
1113        &self,
1114        val: &Value,
1115        errfn: F,
1116    ) -> Result<Type, IrError> {
1117        val.get_type(self.context)
1118            .ok_or_else(|| "unknown".to_owned())
1119            .and_then(|ptr_ty| {
1120                ptr_ty
1121                    .get_pointee_type(self.context)
1122                    .ok_or_else(|| ptr_ty.as_string(self.context))
1123            })
1124            .map_err(errfn)
1125    }
1126
1127    // Get the bit size for fixed atomic types, or None for other types.
1128    fn type_bit_size(&self, ty: &Type) -> Option<usize> {
1129        // Typically we don't want to make assumptions about the size of types in the IR.  This is
1130        // here until we reintroduce pointers and don't need to care about type sizes (and whether
1131        // they'd fit in a 64 bit register).
1132        if ty.is_unit(self.context) || ty.is_bool(self.context) {
1133            Some(1)
1134        } else if ty.is_uint(self.context) {
1135            Some(ty.get_uint_width(self.context).unwrap() as usize)
1136        } else if ty.is_b256(self.context) {
1137            Some(256)
1138        } else {
1139            None
1140        }
1141    }
1142}