diff --git a/sway-ast/src/intrinsics.rs b/sway-ast/src/intrinsics.rs index 182d47fa3d9..30dea17436d 100644 --- a/sway-ast/src/intrinsics.rs +++ b/sway-ast/src/intrinsics.rs @@ -43,6 +43,7 @@ pub enum Intrinsic { EncodeBufferAsRawSlice, // let slice: raw_slice = __encode_buffer_as_raw_slice(buffer) Slice, // let ref_to_slice = __slice::(item: T, inclusive_start_index, exclusive_end_index) ElemAt, // let elem: &T = __elem_at::(item: T, index) + Transmute, // let dst: B = __transmute::(src) } impl fmt::Display for Intrinsic { @@ -89,6 +90,7 @@ impl fmt::Display for Intrinsic { Intrinsic::EncodeBufferAsRawSlice => "encode_buffer_as_raw_slice", Intrinsic::Slice => "slice", Intrinsic::ElemAt => "elem_at", + Intrinsic::Transmute => "transmute", }; write!(f, "{s}") } @@ -139,6 +141,7 @@ impl Intrinsic { "__encode_buffer_as_raw_slice" => EncodeBufferAsRawSlice, "__slice" => Slice, "__elem_at" => ElemAt, + "__transmute" => Transmute, _ => return None, }) } diff --git a/sway-core/src/ir_generation/const_eval.rs b/sway-core/src/ir_generation/const_eval.rs index 258d63cb6d2..9a986c54656 100644 --- a/sway-core/src/ir_generation/const_eval.rs +++ b/sway-core/src/ir_generation/const_eval.rs @@ -1,4 +1,7 @@ -use std::ops::{BitAnd, BitOr, BitXor, Not, Rem}; +use std::{ + io::Read, + ops::{BitAnd, BitOr, BitXor, Not, Rem}, +}; use crate::{ engine_threading::*, @@ -1363,6 +1366,136 @@ fn const_eval_intrinsic( }), } } + Intrinsic::Transmute => { + let src_type = &intrinsic.type_arguments[0]; + let src_ir_type = convert_resolved_type_id( + lookup.engines.te(), + lookup.engines.de(), + lookup.context, + src_type.type_id, + &src_type.span, + ) + .unwrap(); + + let dst_type = &intrinsic.type_arguments[1]; + let dst_ir_type = convert_resolved_type_id( + lookup.engines.te(), + lookup.engines.de(), + lookup.context, + dst_type.type_id, + &dst_type.span, + ) + .unwrap(); + + // check IR sizes match + let src_ir_type_in_bytes = src_ir_type.size(lookup.context).in_bytes(); + let dst_ir_type_in_bytes = dst_ir_type.size(lookup.context).in_bytes(); + if src_ir_type_in_bytes != dst_ir_type_in_bytes { + return Err(ConstEvalError::CompileError); + } + + fn append_bytes( + ctx: &Context<'_>, + bytes: &mut Vec, + t: &Type, + value: &ConstantValue, + ) -> Result<(), ConstEvalError> { + match t.get_content(ctx) { + TypeContent::Array(item_type, size) => match value { + ConstantValue::Array(items) => { + assert!(*size as usize == items.len()); + for item in items { + append_bytes(ctx, bytes, item_type, &item.value)?; + } + } + _ => unreachable!(), + }, + TypeContent::Uint(8) => match value { + ConstantValue::Uint(v) => { + bytes.extend((*v as u8).to_be_bytes()); + } + _ => unreachable!(), + }, + TypeContent::Uint(16) => match value { + ConstantValue::Uint(v) => { + bytes.extend([0u8, 0u8, 0u8, 0u8, 0u8, 0u8]); + bytes.extend((*v as u16).to_be_bytes()); + } + _ => unreachable!(), + }, + TypeContent::Uint(32) => match value { + ConstantValue::Uint(v) => { + bytes.extend([0u8, 0u8, 0u8, 0u8]); + bytes.extend((*v as u32).to_be_bytes()); + } + _ => unreachable!(), + }, + TypeContent::Uint(64) => match value { + ConstantValue::Uint(v) => { + bytes.extend((*v).to_be_bytes()); + } + _ => unreachable!(), + }, + _ => return Err(ConstEvalError::CompileError), + } + Ok(()) + } + + fn transmute_bytes( + ctx: &Context<'_>, + bytes: &mut std::io::Cursor>, + t: &Type, + ) -> Result { + Ok(match t.get_content(ctx) { + TypeContent::Uint(8) => { + let mut buffer = [0u8]; + let _ = bytes.read_exact(&mut buffer); + Constant { + ty: Type::get_uint8(ctx), + value: ConstantValue::Uint(buffer[0] as u64), + } + } + TypeContent::Uint(16) => { + let mut buffer = [0u8; 8]; // u16 = u64 at runtime + let _ = bytes.read_exact(&mut buffer); + let buffer = [buffer[6], buffer[7]]; + Constant { + ty: Type::get_uint16(ctx), + value: ConstantValue::Uint(u16::from_be_bytes(buffer) as u64), + } + } + TypeContent::Uint(32) => { + let mut buffer = [0u8; 8]; // u32 = u64 at runtime + let _ = bytes.read_exact(&mut buffer); + let buffer = [buffer[4], buffer[5], buffer[6], buffer[7]]; + Constant { + ty: Type::get_uint32(ctx), + value: ConstantValue::Uint(u32::from_be_bytes(buffer) as u64), + } + } + TypeContent::Uint(64) => { + let mut buffer = [0u8; 8]; + let _ = bytes.read_exact(&mut buffer); + Constant { + ty: Type::get_uint64(ctx), + value: ConstantValue::Uint(u64::from_be_bytes(buffer)), + } + } + _ => return Err(ConstEvalError::CompileError), + }) + } + + let mut runtime_bytes = vec![]; + append_bytes( + lookup.context, + &mut runtime_bytes, + &src_ir_type, + &args[0].value, + )?; + let mut cursor = std::io::Cursor::new(runtime_bytes); + let c = transmute_bytes(lookup.context, &mut cursor, &dst_ir_type)?; + Ok(Some(c)) + } } } diff --git a/sway-core/src/ir_generation/function.rs b/sway-core/src/ir_generation/function.rs index 54fd1d31f75..f3a7064568e 100644 --- a/sway-core/src/ir_generation/function.rs +++ b/sway-core/src/ir_generation/function.rs @@ -660,9 +660,13 @@ impl<'eng> FnCompiler<'eng> { span_md_idx, ) } - ty::TyExpressionVariant::IntrinsicFunction(kind) => { - self.compile_intrinsic_function(context, md_mgr, kind, ast_expr.span.clone()) - } + ty::TyExpressionVariant::IntrinsicFunction(kind) => self.compile_intrinsic_function( + context, + md_mgr, + kind, + ast_expr.span.clone(), + ast_expr.return_type, + ), ty::TyExpressionVariant::AbiName(_) => { let val = Value::new_constant(context, Constant::new_unit(context)); Ok(TerminatorValue::new(val, context)) @@ -869,6 +873,7 @@ impl<'eng> FnCompiler<'eng> { span: _, }: &ty::TyIntrinsicFunctionKind, span: Span, + return_type: TypeId, ) -> Result { fn store_key_in_local_mem( compiler: &mut FnCompiler, @@ -2174,9 +2179,61 @@ impl<'eng> FnCompiler<'eng> { } Intrinsic::Slice => self.compile_intrinsic_slice(arguments, context, md_mgr), Intrinsic::ElemAt => self.compile_intrinsic_elem_at(arguments, context, md_mgr), + Intrinsic::Transmute => { + self.compile_intrinsic_transmute(arguments, return_type, context, md_mgr, &span) + } } } + fn compile_intrinsic_transmute( + &mut self, + arguments: &[ty::TyExpression], + return_type: TypeId, + context: &mut Context, + md_mgr: &mut MetadataManager, + span: &Span, + ) -> Result { + assert!(arguments.len() == 1); + + let te = self.engines.te(); + let de = self.engines.de(); + + let return_type_ir_type = convert_resolved_type_id(te, de, context, return_type, span)?; + let return_type_ir_type_ptr = Type::new_ptr(context, return_type_ir_type); + + let first_argument_expr = &arguments[0]; + let first_argument_value = return_on_termination_or_extract!( + self.compile_expression_to_value(context, md_mgr, first_argument_expr)? + ); + let first_argument_type = first_argument_value + .get_type(context) + .expect("transmute first argument type not found"); + let first_argument_ptr = save_to_local_return_ptr(self, context, first_argument_value)?; + + // check IR sizes match + let first_arg_size = first_argument_type.size(context).in_bytes(); + let return_type_size = return_type_ir_type.size(context).in_bytes(); + if first_arg_size != return_type_size { + return Err(CompileError::Internal( + "Types size do not match", + span.clone(), + )); + } + + let u64 = Type::get_uint64(context); + let first_argument_ptr = self + .current_block + .append(context) + .ptr_to_int(first_argument_ptr, u64); + let first_argument_ptr = self + .current_block + .append(context) + .int_to_ptr(first_argument_ptr, return_type_ir_type_ptr); + + let final_value = self.current_block.append(context).load(first_argument_ptr); + Ok(TerminatorValue::new(final_value, context)) + } + fn ptr_to_first_element( &mut self, context: &mut Context, diff --git a/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs b/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs index 3ed6c9de234..421509fbc81 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs @@ -106,8 +106,118 @@ impl ty::TyIntrinsicFunctionKind { type_check_slice(handler, ctx, kind, arguments, type_arguments, span) } Intrinsic::ElemAt => type_check_elem_at(arguments, handler, kind, span, ctx), + Intrinsic::Transmute => { + type_check_transmute(arguments, handler, kind, type_arguments, span, ctx) + } + } + } +} + +fn type_check_transmute( + arguments: &[Expression], + handler: &Handler, + kind: Intrinsic, + type_arguments: &[TypeArgument], + span: Span, + mut ctx: TypeCheckContext, +) -> Result<(TyIntrinsicFunctionKind, TypeId), ErrorEmitted> { + if arguments.len() != 1 { + return Err(handler.emit_err(CompileError::IntrinsicIncorrectNumArgs { + name: kind.to_string(), + expected: 1, + span, + })); + } + + let engines = ctx.engines(); + + // Both type arguments needs to be explicitly defined + if type_arguments.len() != 2 { + return Err(handler.emit_err(CompileError::IntrinsicIncorrectNumTArgs { + name: kind.to_string(), + expected: 2, + span, + })); + } + + let src_type = ctx + .resolve_type( + handler, + type_arguments[0].type_id, + &type_arguments[0].span, + EnforceTypeArguments::Yes, + None, + ) + .unwrap_or_else(|err| engines.te().id_of_error_recovery(err)); + let return_type = ctx + .resolve_type( + handler, + type_arguments[1].type_id, + &type_arguments[1].span, + EnforceTypeArguments::Yes, + None, + ) + .unwrap_or_else(|err| engines.te().id_of_error_recovery(err)); + + // Forbid ref and ptr types + fn forbid_ref_ptr_types( + engines: &Engines, + handler: &Handler, + t: TypeId, + span: &Span, + ) -> Result<(), ErrorEmitted> { + let types = t.extract_any_including_self( + engines, + &|t| { + matches!( + t, + TypeInfo::StringSlice + | TypeInfo::RawUntypedPtr + | TypeInfo::RawUntypedSlice + | TypeInfo::Ptr(_) + | TypeInfo::Slice(_) + | TypeInfo::Ref { .. } + ) + }, + vec![], + 0, + ); + if !types.is_empty() { + Err(handler.emit_err(CompileError::TypeNotAllowed { + reason: sway_error::error::TypeNotAllowedReason::NotAllowedInTransmute, + span: span.clone(), + })) + } else { + Ok(()) } } + + forbid_ref_ptr_types(engines, handler, src_type, &type_arguments[0].span)?; + forbid_ref_ptr_types(engines, handler, return_type, &type_arguments[1].span)?; + + // check first argument + let arg_type = engines.te().get(src_type); + let first_argument_typed_expr = { + let ctx = ctx + .by_ref() + .with_help_text("") + .with_type_annotation(engines.te().insert( + engines, + (*arg_type).clone(), + type_arguments[0].span.source_id(), + )); + ty::TyExpression::type_check(handler, ctx, &arguments[0]).unwrap() + }; + + Ok(( + TyIntrinsicFunctionKind { + kind, + arguments: vec![first_argument_typed_expr], + type_arguments: type_arguments.to_vec(), + span, + }, + return_type, + )) } fn type_check_elem_at( diff --git a/sway-core/src/semantic_analysis/cei_pattern_analysis.rs b/sway-core/src/semantic_analysis/cei_pattern_analysis.rs index 1dbad596ed4..1f40ad03ac7 100644 --- a/sway-core/src/semantic_analysis/cei_pattern_analysis.rs +++ b/sway-core/src/semantic_analysis/cei_pattern_analysis.rs @@ -649,7 +649,8 @@ fn effects_of_intrinsic(intr: &sway_ast::Intrinsic) -> HashSet { | EncodeBufferAppend | EncodeBufferAsRawSlice | Slice - | ElemAt => HashSet::new(), + | ElemAt + | Transmute => HashSet::new(), } } diff --git a/sway-error/src/error.rs b/sway-error/src/error.rs index 3998426f92e..fb129fa9903 100644 --- a/sway-error/src/error.rs +++ b/sway-error/src/error.rs @@ -2812,6 +2812,9 @@ pub enum TypeNotAllowedReason { #[error("slices or types containing slices on `const` are not allowed.")] SliceInConst, + + #[error("references, pointers, slices, string slices or types containing any of these are not allowed.")] + NotAllowedInTransmute, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/.gitignore b/test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/.gitignore new file mode 100644 index 00000000000..77d3844f58c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/.gitignore @@ -0,0 +1,2 @@ +out +target diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/Forc.lock new file mode 100644 index 00000000000..f9932679777 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/Forc.lock @@ -0,0 +1,8 @@ +[[package]] +name = "core" +source = "path+from-root-E4EB5F90E61EC58F" + +[[package]] +name = "transmute" +source = "member" +dependencies = ["core"] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/Forc.toml new file mode 100644 index 00000000000..457f84ef26a --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +license = "Apache-2.0" +entry = "main.sw" +name = "transmute" + +[dependencies] +core = { path = "../../../../../../../../sway-lib-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/debug/transmute-abi.json b/test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/debug/transmute-abi.json new file mode 100644 index 00000000000..c42f6cefac2 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/debug/transmute-abi.json @@ -0,0 +1,28 @@ +{ + "programType": "script", + "specVersion": "1", + "encodingVersion": "1", + "concreteTypes": [ + { + "type": "u64", + "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ], + "metadataTypes": [], + "functions": [ + { + "inputs": [], + "name": "main", + "output": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0", + "attributes": null + } + ], + "loggedTypes": [ + { + "logId": "1515152261580153489", + "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ], + "messagesTypes": [], + "configurables": [] +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/debug/transmute-bin-hash b/test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/debug/transmute-bin-hash new file mode 100644 index 00000000000..86a358e416a --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/debug/transmute-bin-hash @@ -0,0 +1 @@ +0x82d76771fa3e7e2cc3c0f9904b45673c9066384f3077cd97e20892c965a096db \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/debug/transmute.bin b/test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/debug/transmute.bin new file mode 100644 index 00000000000..4c9d69d93cb Binary files /dev/null and b/test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/debug/transmute.bin differ diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/snapshot.toml b/test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/snapshot.toml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/src/main.sw new file mode 100644 index 00000000000..6873ab62973 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/src/main.sw @@ -0,0 +1,16 @@ +script; + +fn main() { + // Missing type arguments + let _ = __transmute(1u64); + let _ = __transmute::(1u64); + + // Wrong source type + let _ = __transmute::(1u32); + + // Different sizes + let _ = __transmute::(1u64); + + // Invalid types + let _ = __transmute::<&u64, &u8>(&1u64); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/stdout.snap b/test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/stdout.snap new file mode 100644 index 00000000000..70f758a10e0 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/stdout.snap @@ -0,0 +1,62 @@ +--- +source: test/tests/tests.rs +snapshot_kind: text +--- +> forc build --path test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute +exit status: 1 +output: + Building test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute + Compiling library core (sway-lib-core) + Compiling script transmute (test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute) +error + --> test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/src/main.sw:5:13 + | +3 | fn main() { +4 | // Missing type arguments +5 | let _ = __transmute(1u64); + | ^^^^^^^^^^^^^^^^^ Call to "transmute" expects 2 type arguments +6 | let _ = __transmute::(1u64); +7 | + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/src/main.sw:6:13 + | +4 | // Missing type arguments +5 | let _ = __transmute(1u64); +6 | let _ = __transmute::(1u64); + | ^^^^^^^^^^^^^^^^^^^^^^^^ Call to "transmute" expects 2 type arguments +7 | +8 | // Wrong source type + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/src/main.sw:9:36 + | + 7 | + 8 | // Wrong source type + 9 | let _ = __transmute::(1u32); + | ^^^^ Mismatched types. +expected: u64 +found: u32. + +10 | +11 | // Different sizes + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/language/intrinsics/transmute/src/main.sw:15:27 + | +13 | +14 | // Invalid types +15 | let _ = __transmute::<&u64, &u8>(&1u64); + | ^^^^ references, pointers, slices, string slices or types containing any of these are not allowed. +16 | } + | +____ + + Aborting due to 4 errors. +error: Failed to compile transmute diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/transmute/.gitignore b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/transmute/.gitignore new file mode 100644 index 00000000000..77d3844f58c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/transmute/.gitignore @@ -0,0 +1,2 @@ +out +target diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/transmute/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/transmute/Forc.lock new file mode 100644 index 00000000000..f9932679777 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/transmute/Forc.lock @@ -0,0 +1,8 @@ +[[package]] +name = "core" +source = "path+from-root-E4EB5F90E61EC58F" + +[[package]] +name = "transmute" +source = "member" +dependencies = ["core"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/transmute/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/transmute/Forc.toml new file mode 100644 index 00000000000..457f84ef26a --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/transmute/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +license = "Apache-2.0" +entry = "main.sw" +name = "transmute" + +[dependencies] +core = { path = "../../../../../../../../sway-lib-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/transmute/json_abi_oracle_new_encoding.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/transmute/json_abi_oracle_new_encoding.json new file mode 100644 index 00000000000..7ebeae3ddd5 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/transmute/json_abi_oracle_new_encoding.json @@ -0,0 +1,28 @@ +{ + "concreteTypes": [ + { + "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0", + "type": "u64" + } + ], + "configurables": [], + "encodingVersion": "1", + "functions": [ + { + "attributes": null, + "inputs": [], + "name": "main", + "output": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ], + "loggedTypes": [ + { + "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0", + "logId": "1515152261580153489" + } + ], + "messagesTypes": [], + "metadataTypes": [], + "programType": "script", + "specVersion": "1" +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/transmute/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/transmute/src/main.sw new file mode 100644 index 00000000000..9bad6776dcc --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/transmute/src/main.sw @@ -0,0 +1,121 @@ +script; + +fn assert(v: bool) { + if !v { + __revert(0); + } +} + +enum SomeEnum { + A: u64, + B: u64 +} + +pub struct SomeStruct { + #[allow(dead_code)] + tag: u64, + #[allow(dead_code)] + value: u64 +} + +fn const_transmute() { + // u16 needs 8 bytes as u64 + const U8ARRAY_U16 = __transmute::<[u8; 8], u16>([0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8]); + assert(U8ARRAY_U16 == 0x0102u16); + + // u32 needs 8 bytes as u64 + const U8ARRAY_U32 = __transmute::<[u8; 8], u32>([0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8, 4u8]); + assert(U8ARRAY_U32 == 0x01020304u32); + + const U8ARRAY_U64 = __transmute::<[u8; 8], u64>([1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8]); + assert(U8ARRAY_U64 == 0x0102030405060708u64); + + // u32 <-> u64 + const U32_U64 = __transmute::(1u32); + assert(U32_U64 == 0x0000000000000001u64); + + const U64_U32 = __transmute::(1u64); + assert(U64_U32 == 0x00000001u32); +} + +fn main() { + const_transmute(); + + // Check transmute work as nop + let u8_u8 = __transmute::(1); + assert(u8_u8 == 1); + + let u16_u16 = __transmute::(1); + assert(u16_u16 == 1); + + let u32_u32 = __transmute::(1); + assert(u32_u32 == 1); + + let u64_u64 = __transmute::(1); + assert(u64_u64 == 1); + + // Check transmute arrays + let u8array_u8 = __transmute::<[u8; 1], u8>([1u8]); + assert(u8array_u8 == 1); + + // u16 needs 8 bytes as u64 + let u8array_u16 = __transmute::<[u8; 8], u64>([0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8]); + assert(u8array_u16 == 1); + + // u32 needs 8 bytes as u64 + let u8array_u32 = __transmute::<[u8; 8], u32>([0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8]); + assert(u8array_u32 == 1); + + let u8array_u64 = __transmute::<[u8; 8], u64>([0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8]); + assert(u8array_u64 == 1); + + // u32 <-> u64 + let u32_u64 = __transmute::(1u32); + assert(u32_u64 == 0x0000000000000001u64); + + let u64_u32 = __transmute::(1u64); + assert(u64_u32 == 0x00000001u32); + + // check u256 and b256 are transmutable + let u256_b256 = __transmute::(u256::max()); + assert(u256_b256 == b256::max()); + + // b256 to arrays of u64 and back + let b256_u64array = __transmute::(b256::max()); + assert(b256_u64array[0] == u64::max()); + assert(b256_u64array[1] == u64::max()); + assert(b256_u64array[2] == u64::max()); + assert(b256_u64array[3] == u64::max()); + let u64array_b256 = __transmute::<[u64; 4], b256>(b256_u64array); + assert(u64array_b256 == b256::max()); + + // Check tuples + let b256_tuple_u64 = __transmute::(b256::max()); + assert(b256_tuple_u64.0 == u64::max()); + assert(b256_tuple_u64.1 == u64::max()); + assert(b256_tuple_u64.2 == u64::max()); + assert(b256_tuple_u64.3 == u64::max()); + let tuple_u64_b256 = __transmute::<(u64, u64, u64, u64), b256>(b256_tuple_u64); + assert(tuple_u64_b256 == b256::max()); + + // u16 is actually as big as a u64 + // even inside "structs" + let tuple_u8_u6_u8 = __transmute::<(u8, u16, u8), (u8, u64, u8)>((1, 2, 3)); + assert(tuple_u8_u6_u8.0 == 1); + assert(tuple_u8_u6_u8.1 == 2); + assert(tuple_u8_u6_u8.2 == 3); + + // Check struct to enum + let some_struct: SomeStruct = SomeStruct { tag: 0, value: 1 }; + let some_enum = __transmute::(some_struct); + match some_enum { + SomeEnum::A(v) => assert(v == 1), + _ => {} + }; + + // check enum to struct + let some_enum = SomeEnum::B(1); + let some_struct = __transmute::(some_enum); + assert(some_struct.tag == 1); + assert(some_struct.value == 1); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/transmute/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/transmute/test.toml new file mode 100644 index 00000000000..f656259dc91 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/transmute/test.toml @@ -0,0 +1,4 @@ +category = "run" +expected_result = { action = "return", value = 0 } +expected_result_new_encoding = { action = "return_data", value = "" } +