diff --git a/a.txt b/a.txt new file mode 100644 index 00000000000..d268248d773 --- /dev/null +++ b/a.txt @@ -0,0 +1,784 @@ +Testing should_pass/language/slice/slice_intrinsics ... failed + Failed to compile slice_intrinsics + Compiling should_pass/language/slice/slice_intrinsics ... + Compiling library core (/home/xunilrj/github/sway/sway-lib-core) + Compiling script slice_intrinsics (/home/xunilrj/github/sway/test/src/e2e_vm_tests/test_programs/should_pass/language/slice/slice_intrinsics) + Backtrace [ + { fn: "sway_core::ir_generation::function::FnCompiler::compile_intrinsic_function" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_expression" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_expression_to_value" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_code_block::{{closure}}" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_code_block_to_value" }, + { fn: "sway_core::ir_generation::compile::compile_fn" }, + { fn: "sway_core::ir_generation::CompiledFunctionCache::ty_function_decl_to_unique_function" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_fn_call" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_expression" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_expression_to_value" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_code_block::{{closure}}" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_code_block_to_value" }, + { fn: "sway_core::ir_generation::compile::compile_fn" }, + { fn: "sway_core::ir_generation::CompiledFunctionCache::ty_function_decl_to_unique_function" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_fn_call" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_expression" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_code_block::{{closure}}" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_code_block_to_value" }, + { fn: "sway_core::ir_generation::compile::compile_fn" }, + { fn: "sway_core::ir_generation::compile::compile_entry_function" }, + { fn: "sway_core::ir_generation::compile::compile_script" }, + { fn: "sway_core::ir_generation::compile_program" }, + { fn: "sway_core::ast_to_asm" }, + { fn: "forc_pkg::pkg::compile" }, + { fn: "forc_pkg::pkg::build" }, + { fn: "forc_pkg::pkg::build_with_options" }, + { fn: "test::e2e_vm_tests::harness::compile_to_bytes::{{closure}}" }, + { fn: "test::e2e_vm_tests::TestContext::run::{{closure}}" }, + { fn: "test::e2e_vm_tests::run::{{closure}}" }, + { fn: " as core::future::future::Future>::poll" }, + { fn: "test::main::{{closure}}" }, + { fn: "tokio::runtime::context::runtime::enter_runtime" }, + { fn: "tokio::runtime::runtime::Runtime::block_on" }, + { fn: "test::main" }, + { fn: "std::sys_common::backtrace::__rust_begin_short_backtrace" }, + { fn: "std::rt::lang_start::{{closure}}" }, + { fn: "std::rt::lang_start_internal" }, + { fn: "main" }, + { fn: "__libc_start_main" }, + { fn: "_start" }, + ] + Backtrace [ + { fn: "sway_core::ir_generation::function::FnCompiler::compile_intrinsic_function" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_expression" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_deref_up_to_ptr" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_expression" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_expression_to_value" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_code_block::{{closure}}" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_code_block_to_value" }, + { fn: "sway_core::ir_generation::compile::compile_fn" }, + { fn: "sway_core::ir_generation::CompiledFunctionCache::ty_function_decl_to_unique_function" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_fn_call" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_expression" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_fn_call" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_expression" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_expression_to_value" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_code_block::{{closure}}" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_code_block_to_value" }, + { fn: "sway_core::ir_generation::compile::compile_fn" }, + { fn: "sway_core::ir_generation::CompiledFunctionCache::ty_function_decl_to_unique_function" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_fn_call" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_expression" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_code_block::{{closure}}" }, + { fn: "sway_core::ir_generation::function::FnCompiler::compile_code_block_to_value" }, + { fn: "sway_core::ir_generation::compile::compile_fn" }, + { fn: "sway_core::ir_generation::compile::compile_entry_function" }, + { fn: "sway_core::ir_generation::compile::compile_script" }, + { fn: "sway_core::ir_generation::compile_program" }, + { fn: "sway_core::ast_to_asm" }, + { fn: "forc_pkg::pkg::compile" }, + { fn: "forc_pkg::pkg::build" }, + { fn: "forc_pkg::pkg::build_with_options" }, + { fn: "test::e2e_vm_tests::harness::compile_to_bytes::{{closure}}" }, + { fn: "test::e2e_vm_tests::TestContext::run::{{closure}}" }, + { fn: "test::e2e_vm_tests::run::{{closure}}" }, + { fn: " as core::future::future::Future>::poll" }, + { fn: "test::main::{{closure}}" }, + { fn: "tokio::runtime::context::runtime::enter_runtime" }, + { fn: "tokio::runtime::runtime::Runtime::block_on" }, + { fn: "test::main" }, + { fn: "std::sys_common::backtrace::__rust_begin_short_backtrace" }, + { fn: "std::rt::lang_start::{{closure}}" }, + { fn: "std::rt::lang_start_internal" }, + { fn: "main" }, + { fn: "__libc_start_main" }, + { fn: "_start" }, + ] + Verification failed at push_2::entry + script { + pub entry fn __entry() -> slice, !1 { + local () result + + entry(): + v0 = call main_0(), !2 + v1 = get_local ptr (), result, !3 + store v0 to v1, !3 + v2 = get_local ptr (), result, !4 + v3 = load v2 + v4 = call encode_17(v3), !5 + ret slice v4 + } + + entry_orig fn main_0() -> (), !7 { + local mut { __slice[u64], u64 } v + + entry(): + v0 = call new_1(), !8 + v1 = get_local ptr { __slice[u64], u64 }, v, !9 + store v0 to v1, !9 + v2 = get_local ptr { __slice[u64], u64 }, v, !10 + v3 = const u64 1, !11 + v4 = call push_2(v2, v3), !12 + v5 = const u64 99, !13 + v6 = call encode_9(v5), !14 + v7 = const u64 1515152261580153489 + log slice v6, v7, !14 + v8 = get_local ptr { __slice[u64], u64 }, v, !15 + v9 = load v8 + v10 = const u64 0, !16 + v11 = call get_16(v9, v10), !17 + v12 = const u64 1, !18 + v13 = call assert_13(v11, v12), !19 + v14 = const unit () + ret () v14 + } + + pub fn new_1() -> { __slice[u64], u64 }, !20 { + local { u64, u64 } __anon_0 + local { __slice[u64], u64 } __anon_1 + local u64 ptr + + entry(): + v0 = asm() -> u64 hp, !21 { + } + v1 = get_local ptr u64, ptr, !22 + store v0 to v1, !22 + v2 = get_local ptr u64, ptr, !23 + v3 = load v2 + v4 = get_local ptr { u64, u64 }, __anon_0, !24 + v5 = const u64 0 + v6 = get_elem_ptr v4, ptr u64, v5, !24 + store v3 to v6, !24 + v7 = const u64 1 + v8 = get_elem_ptr v4, ptr u64, v7, !24 + v9 = const u64 0, !25 + store v9 to v8, !24 + v10 = asm(buf: v4) -> __slice[u64] buf, !26 { + } + v11 = get_local ptr { __slice[u64], u64 }, __anon_1, !27 + v12 = const u64 0 + v13 = get_elem_ptr v11, ptr __slice[u64], v12 + store v10 to v13, !27 + v14 = const u64 1 + v15 = get_elem_ptr v11, ptr u64, v14 + v16 = const u64 0, !28 + store v16 to v15, !27 + v17 = load v11 + ret { __slice[u64], u64 } v17 + } + + pub fn push_2(self !29: ptr { __slice[u64], u64 }, _item !30: u64) -> (), !31 { + local { u64, u64 } __anon_0 + local __slice[u64] __anon_1 + local u64 current_cap + local u64 new_cap + local u64 new_cap_in_bytes + local u64 new_item_idx + local u64 old_buf_ptr + local u64 old_cap_in_bytes + local u64 ptr + + entry(self: ptr { __slice[u64], u64 }, _item: u64): + v0 = const u64 1 + v1 = get_elem_ptr self, ptr u64, v0, !32 + v2 = get_local ptr u64, new_item_idx, !33 + store v1 to v2, !33 + ^ Verification failed: Store value and pointer type mismatch. + v3 = const u64 0 + v4 = get_elem_ptr self, ptr __slice[u64], v3, !34 + v5 = load v4 + v6 = call len_3(v5), !35 + v7 = get_local ptr u64, current_cap, !36 + store v6 to v7, !36 + v8 = get_local ptr u64, new_item_idx, !37 + v9 = load v8 + v10 = get_local ptr u64, current_cap, !38 + v11 = load v10 + v12 = call ge_4(v9, v11), !39 + cbr v12, block0(), block4(), !39 + + block0(): + v13 = get_local ptr u64, current_cap, !40 + v14 = load v13 + v15 = const u64 0, !41 + v16 = call eq_6(v14, v15), !42 + cbr v16, block1(), block2(), !42 + + block1(): + v17 = const u64 1, !43 + br block3(v17) + + block2(): + v18 = get_local ptr u64, current_cap, !44 + v19 = load v18 + v20 = const u64 2, !45 + v21 = call multiply_7(v19, v20), !46 + br block3(v21) + + block3(v22: u64): + v23 = get_local ptr u64, new_cap, !47 + store v22 to v23, !47 + v24 = get_local ptr u64, new_cap, !48 + v25 = load v24 + v26 = const u64 8 + v27 = call multiply_7(v25, v26), !49 + v28 = get_local ptr u64, new_cap_in_bytes, !50 + store v27 to v28, !50 + v29 = const u64 0 + v30 = get_elem_ptr self, ptr __slice[u64], v29, !34 + v31 = load v30 + v32 = call ptr_8(v31), !51 + v33 = get_local ptr u64, old_buf_ptr, !52 + store v32 to v33, !52 + v34 = get_local ptr u64, current_cap, !53 + v35 = load v34 + v36 = const u64 8 + v37 = call multiply_7(v35, v36), !54 + v38 = get_local ptr u64, old_cap_in_bytes, !55 + store v37 to v38, !55 + v39 = get_local ptr u64, new_cap_in_bytes, !56 + v40 = load v39 + v41 = get_local ptr u64, old_buf_ptr, !57 + v42 = load v41 + v43 = get_local ptr u64, old_cap_in_bytes, !58 + v44 = load v43 + v45 = asm(new_cap_in_bytes: v40, old_buf_ptr: v42, old_cap_in_bytes: v44) -> u64 hp, !59 { + log hp hp hp hp, !60 + aloc new_cap_in_bytes, !61 + log hp hp hp hp, !62 + mcp hp old_buf_ptr old_cap_in_bytes, !63 + } + v46 = get_local ptr u64, ptr, !64 + store v45 to v46, !64 + v47 = get_local ptr u64, ptr, !65 + v48 = load v47 + v49 = get_local ptr u64, new_cap, !66 + v50 = load v49 + v51 = get_local ptr { u64, u64 }, __anon_0, !67 + v52 = const u64 0 + v53 = get_elem_ptr v51, ptr u64, v52, !67 + store v48 to v53, !67 + v54 = const u64 1 + v55 = get_elem_ptr v51, ptr u64, v54, !67 + store v50 to v55, !67 + v56 = asm(buf: v51) -> __slice[u64] buf, !68 { + } + v57 = const u64 0 + v58 = get_elem_ptr self, ptr __slice[u64], v57, !69 + store v56 to v58, !69 + v59 = const unit () + br block5(v59) + + block4(): + v60 = const unit () + br block5(v60) + + block5(v61: ()): + v62 = const u64 5, !70 + v63 = call encode_9(v62), !71 + v64 = const u64 1515152261580153489 + log slice v63, v64, !71 + v65 = const u64 0 + v66 = get_elem_ptr self, ptr __slice[u64], v65, !34 + v67 = load v66 + v68 = get_local ptr __slice[u64], __anon_1 + store v67 to v68 + v69 = get_local ptr u64, new_item_idx, !72 + v70 = load v69 + v71 = const u64 8 + v72 = asm(ptr_to_slice: v68, idx: v70, item_len: v71, offset, ptr) -> ptr u64 ptr { + lw ptr ptr_to_slice i0 + mul offset idx item_len + add ptr ptr offset + log ptr ptr ptr ptr + } + v73 = load v72 + v74 = const unit () + ret () v74 + } + + pub fn len_3(self !74: __slice[u64]) -> u64, !75 { + local u64 _ + local { u64, u64 } __tuple_2 + local { u64, u64 } __tuple_2_ + local u64 len + + entry(self: __slice[u64]): + v0 = asm(s: self) -> { u64, u64 } s, !76 { + } + v1 = get_local ptr { u64, u64 }, __tuple_2, !77 + store v0 to v1, !77 + v2 = get_local ptr { u64, u64 }, __tuple_2, !77 + v3 = get_local ptr { u64, u64 }, __tuple_2_, !77 + store v2 to v3, !77 + v4 = get_local ptr { u64, u64 }, __tuple_2_, !77 + v5 = const u64 0 + v6 = get_elem_ptr v4, ptr u64, v5, !77 + v7 = get_local ptr u64, _, !77 + store v6 to v7, !77 + v8 = get_local ptr { u64, u64 }, __tuple_2_, !77 + v9 = const u64 1 + v10 = get_elem_ptr v8, ptr u64, v9, !77 + v11 = get_local ptr u64, len, !77 + store v10 to v11, !77 + v12 = get_local ptr u64, len, !78 + v13 = load v12 + ret u64 v13 + } + + pub fn ge_4(self !80: u64, other !81: u64) -> bool, !82 { + entry(self: u64, other: u64): + v0 = call gt_5(self, other), !83 + cbr v0, block1(v0), block0(), !84 + + block0(): + v1 = call eq_6(self, other), !85 + br block1(v1), !84 + + block1(v2: bool): + ret bool v2 + } + + pub fn gt_5(self !86: u64, other !87: u64) -> bool, !88 { + entry(self: u64, other: u64): + v0 = cmp gt self other + ret bool v0 + } + + pub fn eq_6(self !89: u64, other !90: u64) -> bool, !91 { + entry(self: u64, other: u64): + v0 = cmp eq self other + ret bool v0 + } + + pub fn multiply_7(self !92: u64, other !93: u64) -> u64, !94 { + entry(self: u64, other: u64): + v0 = mul self, other + ret u64 v0 + } + + pub fn ptr_8(self !95: __slice[u64]) -> u64, !96 { + local u64 _ + local { u64, u64 } __tuple_1 + local { u64, u64 } __tuple_1_ + local u64 ptr + + entry(self: __slice[u64]): + v0 = asm(s: self) -> { u64, u64 } s, !97 { + } + v1 = get_local ptr { u64, u64 }, __tuple_1, !98 + store v0 to v1, !98 + v2 = get_local ptr { u64, u64 }, __tuple_1, !98 + v3 = get_local ptr { u64, u64 }, __tuple_1_, !98 + store v2 to v3, !98 + v4 = get_local ptr { u64, u64 }, __tuple_1_, !98 + v5 = const u64 0 + v6 = get_elem_ptr v4, ptr u64, v5, !98 + v7 = get_local ptr u64, ptr, !98 + store v6 to v7, !98 + v8 = get_local ptr { u64, u64 }, __tuple_1_, !98 + v9 = const u64 1 + v10 = get_elem_ptr v8, ptr u64, v9, !98 + v11 = get_local ptr u64, _, !98 + store v10 to v11, !98 + v12 = get_local ptr u64, ptr, !99 + v13 = load v12 + ret u64 v13 + } + + pub fn encode_9(item !101: u64) -> slice, !102 { + local { { u64, u64, u64 } } buffer + + entry(item: u64): + v0 = call new_11(), !103 + v1 = call abi_encode_10(item, v0), !104 + v2 = get_local ptr { { u64, u64, u64 } }, buffer, !105 + store v1 to v2, !105 + v3 = get_local ptr { { u64, u64, u64 } }, buffer, !106 + v4 = load v3 + v5 = call as_raw_slice_12(v4), !107 + ret slice v5 + } + + pub fn abi_encode_10(self !108: u64, buffer !109: { { u64, u64, u64 } }) -> { { u64, u64, u64 } }, !110 { + local { u64, u64, u64 } __anon_0 + local { u64, u64, u64 } __anon_1 + local { { u64, u64, u64 } } __anon_2 + + entry(self: u64, buffer: { { u64, u64, u64 } }): + v0 = ptr_to_int buffer to u64 + v1 = int_to_ptr v0 to ptr { { u64, u64, u64 } } + v2 = const u64 0 + v3 = get_elem_ptr v1, ptr { u64, u64, u64 }, v2, !111 + v4 = load v3 + v5 = asm(buffer: v4) -> { u64, u64, u64 } buffer { + } + v6 = get_local ptr { u64, u64, u64 }, __anon_0 + store v5 to v6 + v7 = const u64 0 + v8 = get_elem_ptr v6, ptr u64, v7 + v9 = load v8 + v10 = int_to_ptr v9 to ptr u8 + v11 = const u64 1 + v12 = get_elem_ptr v6, ptr u64, v11 + v13 = load v12 + v14 = const u64 2 + v15 = get_elem_ptr v6, ptr u64, v14 + v16 = load v15 + v17 = const u64 8 + v18 = add v16, v17 + v19 = cmp gt v18 v13 + cbr v19, block1(), block2() + + block0(v20: ptr u8, v21: u64): + v22 = ptr_to_int v20 to u64 + v23 = add v22, v16 + v24 = int_to_ptr v23 to ptr u64 + store self to v24 + v25 = const u64 8 + v26 = add v16, v25 + v27 = ptr_to_int v20 to u64 + v28 = get_local ptr { u64, u64, u64 }, __anon_1 + v29 = const u64 0 + v30 = get_elem_ptr v28, ptr u64, v29 + store v27 to v30 + v31 = const u64 1 + v32 = get_elem_ptr v28, ptr u64, v31 + store v21 to v32 + v33 = const u64 2 + v34 = get_elem_ptr v28, ptr u64, v33 + store v26 to v34 + v35 = asm(buffer: v28) -> { u64, u64, u64 } buffer { + } + v36 = get_local ptr { { u64, u64, u64 } }, __anon_2, !112 + v37 = const u64 0 + v38 = get_elem_ptr v36, ptr { u64, u64, u64 }, v37 + store v35 to v38, !112 + v39 = load v36 + ret { { u64, u64, u64 } } v39 + + block1(): + v40 = const u64 2 + v41 = mul v13, v40 + v42 = asm(new_cap: v41, old_ptr: v10, len: v16) -> ptr u8 hp { + aloc new_cap + mcp hp old_ptr len + } + br block0(v42, v41) + + block2(): + br block0(v10, v13) + } + + pub fn new_11() -> { { u64, u64, u64 } }, !113 { + local { u64, u64, u64 } __anon_0 + local { { u64, u64, u64 } } __anon_1 + + entry(): + v0 = const u64 1024 + v1 = asm(cap: v0) -> u64 hp { + aloc cap + } + v2 = int_to_ptr v1 to ptr u8 + v3 = ptr_to_int v2 to u64 + v4 = get_local ptr { u64, u64, u64 }, __anon_0 + v5 = const u64 0 + v6 = get_elem_ptr v4, ptr u64, v5 + store v3 to v6 + v7 = const u64 1 + v8 = get_elem_ptr v4, ptr u64, v7 + store v0 to v8 + v9 = const u64 2 + v10 = get_elem_ptr v4, ptr u64, v9 + v11 = const u64 0 + store v11 to v10 + v12 = asm(buffer: v4) -> { u64, u64, u64 } buffer { + } + v13 = get_local ptr { { u64, u64, u64 } }, __anon_1, !114 + v14 = const u64 0 + v15 = get_elem_ptr v13, ptr { u64, u64, u64 }, v14 + store v12 to v15, !114 + v16 = load v13 + ret { { u64, u64, u64 } } v16 + } + + pub fn as_raw_slice_12(self !115: { { u64, u64, u64 } }) -> slice, !116 { + local { u64, u64, u64 } __anon_0 + local { u64, u64 } __anon_1 + + entry(self: { { u64, u64, u64 } }): + v0 = ptr_to_int self to u64 + v1 = int_to_ptr v0 to ptr { { u64, u64, u64 } } + v2 = const u64 0 + v3 = get_elem_ptr v1, ptr { u64, u64, u64 }, v2, !111 + v4 = load v3 + v5 = asm(buffer: v4) -> { u64, u64, u64 } buffer { + } + v6 = get_local ptr { u64, u64, u64 }, __anon_0 + store v5 to v6 + v7 = const u64 0 + v8 = get_elem_ptr v6, ptr u64, v7 + v9 = load v8 + v10 = int_to_ptr v9 to ptr u8 + v11 = const u64 1 + v12 = get_elem_ptr v6, ptr u64, v11 + v13 = load v12 + v14 = const u64 2 + v15 = get_elem_ptr v6, ptr u64, v14 + v16 = load v15 + v17 = ptr_to_int v10 to u64 + v18 = get_local ptr { u64, u64 }, __anon_1 + v19 = const u64 0 + v20 = get_elem_ptr v18, ptr u64, v19 + store v17 to v20 + v21 = const u64 1 + v22 = get_elem_ptr v18, ptr u64, v21 + store v16 to v22 + v23 = asm(s: v18) -> slice s { + } + ret slice v23 + } + + fn assert_13(l !117: u64, r !118: u64) -> (), !119 { + entry(l: u64, r: u64): + v0 = call neq_14(l, r), !120 + cbr v0, block0(), block1(), !120 + + block0(): + v1 = call encode_9(l), !121 + v2 = const u64 1515152261580153489 + log slice v1, v2, !121 + v3 = call encode_9(r), !122 + v4 = const u64 1515152261580153489 + log slice v3, v4, !122 + v5 = const u64 1, !123 + revert v5, !124 + + block1(): + v6 = const unit () + br block2(v6) + + block2(v7: ()): + v8 = const unit () + ret () v8 + } + + pub fn neq_14(self !125: u64, other !126: u64) -> bool, !127 { + entry(self: u64, other: u64): + v0 = call eq_6(self, other), !128 + v1 = call not_15(v0), !129 + ret bool v1 + } + + pub fn not_15(self !130: bool) -> bool, !131 { + entry(self: bool): + v0 = const bool false, !132 + v1 = cmp eq self v0 + ret bool v1 + } + + pub fn get_16(self !133: { __slice[u64], u64 }, index !134: u64) -> u64, !135 { + local __slice[u64] __anon_0 + + entry(self: { __slice[u64], u64 }, index: u64): + v0 = ptr_to_int self to u64 + v1 = int_to_ptr v0 to ptr { __slice[u64], u64 } + v2 = const u64 0 + v3 = get_elem_ptr v1, ptr __slice[u64], v2, !34 + v4 = load v3 + v5 = get_local ptr __slice[u64], __anon_0 + store v4 to v5 + v6 = const u64 8 + v7 = asm(ptr_to_slice: v5, idx: index, item_len: v6, offset, ptr) -> ptr u64 ptr { + lw ptr ptr_to_slice i0 + mul offset idx item_len + add ptr ptr offset + log ptr ptr ptr ptr + } + v8 = load v7 + v9 = int_to_ptr v8 to ptr u64, !136 + v10 = load v9 + ret u64 v10 + } + + pub fn encode_17(item !101: ()) -> slice, !102 { + local { { u64, u64, u64 } } buffer + + entry(item: ()): + v0 = call new_11(), !103 + v1 = call abi_encode_18(item, v0), !104 + v2 = get_local ptr { { u64, u64, u64 } }, buffer, !105 + store v1 to v2, !105 + v3 = get_local ptr { { u64, u64, u64 } }, buffer, !106 + v4 = load v3 + v5 = call as_raw_slice_12(v4), !107 + ret slice v5 + } + + pub fn abi_encode_18(self !137: (), buffer !138: { { u64, u64, u64 } }) -> { { u64, u64, u64 } }, !139 { + entry(self: (), buffer: { { u64, u64, u64 } }): + ret { { u64, u64, u64 } } buffer + } + } + + !0 = "" + !1 = span !0 0 123 + !2 = span !0 65 71 + !3 = span !0 48 72 + !4 = span !0 102 108 + !5 = span !0 89 109 + !6 = "/home/xunilrj/github/sway/test/src/e2e_vm_tests/test_programs/should_pass/language/slice/slice_intrinsics/src/main.sw" + !7 = span !6 1517 1624 + !8 = span !6 1556 1566 + !9 = span !6 1534 1567 + !10 = span !6 1572 1573 + !11 = span !6 1579 1580 + !12 = span !6 1572 1581 + !13 = span !6 1593 1595 + !14 = span !6 1587 1596 + !15 = span !6 1609 1610 + !16 = span !6 1615 1616 + !17 = span !6 1609 1617 + !18 = span !6 1619 1620 + !19 = span !6 1602 1621 + !20 = span !6 89 309 + !21 = span !6 130 171 + !22 = span !6 120 172 + !23 = span !6 215 218 + !24 = span !6 214 223 + !25 = span !6 221 222 + !26 = span !6 205 273 + !27 = span !6 181 303 + !28 = span !6 292 293 + !29 = span !6 335 339 + !30 = span !6 341 346 + !31 = span !6 315 1291 + !32 = span !6 55 63 + !33 = span !6 361 389 + !34 = span !6 33 49 + !35 = span !6 416 430 + !36 = span !6 398 431 + !37 = span !6 443 455 + !38 = span !6 459 470 + !39 = span !6 443 470 + !40 = span !6 502 513 + !41 = span !6 517 518 + !42 = span !6 502 518 + !43 = span !6 537 538 + !44 = span !6 576 587 + !45 = span !6 590 591 + !46 = span !6 576 591 + !47 = span !6 485 606 + !48 = span !6 642 649 + !49 = span !6 642 668 + !50 = span !6 619 669 + !51 = span !6 701 715 + !52 = span !6 683 716 + !53 = span !6 752 763 + !54 = span !6 752 782 + !55 = span !6 729 783 + !56 = span !6 829 845 + !57 = span !6 860 871 + !58 = span !6 891 907 + !59 = span !6 807 1110 + !60 = span !6 927 942 + !61 = span !6 960 981 + !62 = span !6 999 1014 + !63 = span !6 1032 1067 + !64 = span !6 797 1111 + !65 = span !6 1146 1149 + !66 = span !6 1151 1158 + !67 = span !6 1145 1159 + !68 = span !6 1136 1209 + !69 = span !6 1125 1209 + !70 = span !6 1236 1237 + !71 = span !6 1230 1238 + !72 = span !6 1271 1283 + !73 = "/home/xunilrj/github/sway/sway-lib-core/src/slice.sw" + !74 = span !73 197 201 + !75 = span !73 186 308 + !76 = span !73 235 289 + !77 = span !73 220 290 + !78 = span !73 299 302 + !79 = "/home/xunilrj/github/sway/sway-lib-core/src/ops.sw" + !80 = span !79 21821 21825 + !81 = span !79 21827 21832 + !82 = span !79 21815 21896 + !83 = span !79 21858 21872 + !84 = span !79 21858 21890 + !85 = span !79 21876 21890 + !86 = span !79 15138 15142 + !87 = span !79 15144 15149 + !88 = span !79 15132 15198 + !89 = span !79 12661 12665 + !90 = span !79 12667 12672 + !91 = span !79 12655 12721 + !92 = span !79 4997 5001 + !93 = span !79 5003 5008 + !94 = span !79 4985 5058 + !95 = span !73 66 70 + !96 = span !73 55 180 + !97 = span !73 108 161 + !98 = span !73 93 162 + !99 = span !73 171 174 + !100 = "/home/xunilrj/github/sway/sway-lib-core/src/codec.sw" + !101 = span !100 64661 64665 + !102 = span !100 64644 64785 + !103 = span !100 64742 64755 + !104 = span !100 64726 64756 + !105 = span !100 64713 64757 + !106 = span !100 64762 64768 + !107 = span !100 64762 64783 + !108 = span !100 4642 4646 + !109 = span !100 4648 4654 + !110 = span !100 4628 4773 + !111 = span !100 55 82 + !112 = span !100 4684 4767 + !113 = span !100 128 228 + !114 = span !100 159 222 + !115 = span !100 483 487 + !116 = span !100 467 559 + !117 = span !6 1394 1395 + !118 = span !6 1400 1401 + !119 = span !6 1381 1515 + !120 = span !6 1443 1449 + !121 = span !6 1460 1468 + !122 = span !6 1478 1486 + !123 = span !6 1505 1506 + !124 = span !6 1496 1507 + !125 = span !79 12285 12289 + !126 = span !79 12291 12296 + !127 = span !79 12278 12350 + !128 = span !79 12323 12337 + !129 = span !79 12322 12344 + !130 = span !79 9972 9976 + !131 = span !79 9965 10019 + !132 = span !79 10007 10012 + !133 = span !6 1308 1312 + !134 = span !6 1314 1319 + !135 = span !6 1297 1377 + !136 = span !6 1341 1371 + !137 = span !100 36453 36457 + !138 = span !100 36459 36465 + !139 = span !100 36439 36507 + + + Failed to compile slice_intrinsics + warning + --> /home/xunilrj/github/sway/test/src/e2e_vm_tests/test_programs/should_pass/language/slice/slice_intrinsics/src/main.sw:49:9 +  | + 47 | + 48 | __log(5); + 49 | __slice_elem(self.buf, new_item_idx); +  | ------------------------------------ This returns a value of type &mut T, which is not assigned to anything and is ignored. + 50 | } +  | + ____ + + error: Internal compiler error: Verification failed: Store value and pointer type mismatch. + Please file an issue on the repository and include the code that triggered this error. + ____ + +  Aborting due to 1 error. + +_________________________________ +Sway tests result: failed. 828 total, 0 passed; 1 failed; 7 disabled [test duration: 00:00:410] +Failing tests: + should_pass/language/slice/slice_intrinsics ... failed diff --git a/forc-plugins/forc-doc/src/render/item/type_anchor.rs b/forc-plugins/forc-doc/src/render/item/type_anchor.rs index 65b75ad8f39..514d7f6dcd2 100644 --- a/forc-plugins/forc-doc/src/render/item/type_anchor.rs +++ b/forc-plugins/forc-doc/src/render/item/type_anchor.rs @@ -33,6 +33,18 @@ pub(crate) fn render_type_anchor( : format!("; {}]", len.val()); }) } + TypeInfo::Slice(ty_arg) => { + let inner = render_type_anchor( + (*render_plan.engines.te().get(ty_arg.type_id)).clone(), + render_plan, + current_module_info, + )?; + Ok(box_html! { + : "__slice["; + : inner; + : "]"; + }) + } TypeInfo::Tuple(ty_args) => { let mut rendered_args: Vec<_> = Vec::new(); for ty_arg in ty_args { diff --git a/sway-ast/src/intrinsics.rs b/sway-ast/src/intrinsics.rs index 5200da142ae..1f9d98e62bc 100644 --- a/sway-ast/src/intrinsics.rs +++ b/sway-ast/src/intrinsics.rs @@ -41,6 +41,8 @@ pub enum Intrinsic { EncodeBufferEmpty, // let buffer: (raw_ptr, u64, u64) = __encode_buffer_empty() EncodeBufferAppend, // let buffer: (raw_ptr, u64, u64) = __encode_buffer_append(buffer, primitive data type) EncodeBufferAsRawSlice, // let slice: raw_slice = __encode_buffer_as_raw_slice(buffer) + Slice, // let ref_to_slice = __slice(array or ref to slice, inclusive_start_index, exclusive_end_index) + SliceElem, // let ref_to_item = __slice_elem(ref to slice, index) } impl fmt::Display for Intrinsic { @@ -85,6 +87,8 @@ impl fmt::Display for Intrinsic { Intrinsic::EncodeBufferEmpty => "encode_buffer_empty", Intrinsic::EncodeBufferAppend => "encode_buffer_append", Intrinsic::EncodeBufferAsRawSlice => "encode_buffer_as_raw_slice", + Intrinsic::Slice => "slice", + Intrinsic::SliceElem => "slice_elem", }; write!(f, "{s}") } @@ -133,6 +137,8 @@ impl Intrinsic { "__encode_buffer_empty" => EncodeBufferEmpty, "__encode_buffer_append" => EncodeBufferAppend, "__encode_buffer_as_raw_slice" => EncodeBufferAsRawSlice, + "__slice" => Slice, + "__slice_elem" => SliceElem, _ => return None, }) } diff --git a/sway-core/src/abi_generation/abi_str.rs b/sway-core/src/abi_generation/abi_str.rs index b52af834f0c..ed8e9ae3cb0 100644 --- a/sway-core/src/abi_generation/abi_str.rs +++ b/sway-core/src/abi_generation/abi_str.rs @@ -53,6 +53,14 @@ impl TypeId { }; format!("[{}; {}]", inner_type, count.val()) } + (TypeInfo::Slice(type_arg), TypeInfo::Slice(_)) => { + let inner_type = if ctx.abi_with_fully_specified_types { + type_engine.get(type_arg.type_id).abi_str(ctx, engines) + } else { + "_".to_string() + }; + format!("[{}]", inner_type) + } (TypeInfo::Custom { .. }, _) => { format!("generic {}", self_abi_str) } diff --git a/sway-core/src/abi_generation/evm_abi.rs b/sway-core/src/abi_generation/evm_abi.rs index 6cfb1040484..f899e0de209 100644 --- a/sway-core/src/abi_generation/evm_abi.rs +++ b/sway-core/src/abi_generation/evm_abi.rs @@ -49,6 +49,7 @@ fn get_type_str(type_id: &TypeId, engines: &Engines, resolved_type_id: TypeId) - assert_eq!(count.val(), resolved_count.val()); format!("[_; {}]", count.val()) } + (TypeInfo::Slice(_), TypeInfo::Slice(_)) => "__slice[_]".into(), (TypeInfo::Custom { .. }, _) => { format!("generic {}", abi_str(&type_engine.get(*type_id), engines)) } diff --git a/sway-core/src/abi_generation/fuel_abi.rs b/sway-core/src/abi_generation/fuel_abi.rs index f49e8eb6fea..f4d0205d8b0 100644 --- a/sway-core/src/abi_generation/fuel_abi.rs +++ b/sway-core/src/abi_generation/fuel_abi.rs @@ -406,6 +406,47 @@ impl TypeId { unreachable!(); } } + TypeInfo::Slice(..) => { + if let TypeInfo::Slice(elem_ty) = &*type_engine.get(resolved_type_id) { + // The `program_abi::TypeDeclaration`s needed for the slice element type + let elem_abi_ty = program_abi::TypeDeclaration { + type_id: elem_ty.initial_type_id.index(), + type_field: elem_ty.initial_type_id.get_abi_type_str( + &ctx.to_str_context(engines, false), + engines, + elem_ty.type_id, + ), + components: elem_ty.initial_type_id.get_abi_type_components( + ctx, + engines, + types, + elem_ty.type_id, + ), + type_parameters: elem_ty.initial_type_id.get_abi_type_parameters( + ctx, + engines, + types, + elem_ty.type_id, + ), + }; + types.push(elem_abi_ty); + + // Generate the JSON data for the array. This is basically a single + // `program_abi::TypeApplication` for the array element type + Some(vec![program_abi::TypeApplication { + name: "__slice_element".to_string(), + type_id: elem_ty.initial_type_id.index(), + type_arguments: elem_ty.initial_type_id.get_abi_type_arguments( + ctx, + engines, + types, + elem_ty.type_id, + ), + }]) + } else { + unreachable!(); + } + } TypeInfo::Tuple(_) => { if let TypeInfo::Tuple(fields) = &*type_engine.get(resolved_type_id) { // A list of all `program_abi::TypeDeclaration`s needed for the tuple fields diff --git a/sway-core/src/asm_generation/fuel/data_section.rs b/sway-core/src/asm_generation/fuel/data_section.rs index 10dc27f0da8..74949aad566 100644 --- a/sway-core/src/asm_generation/fuel/data_section.rs +++ b/sway-core/src/asm_generation/fuel/data_section.rs @@ -123,6 +123,7 @@ impl Entry { name, padding, ), + ConstantValue::Slice(_) => todo!(), ConstantValue::Struct(_) => Entry::new_collection( constant .struct_fields_with_padding(context) diff --git a/sway-core/src/asm_generation/miden_vm/miden_vm_asm_builder.rs b/sway-core/src/asm_generation/miden_vm/miden_vm_asm_builder.rs index ff0575219a0..87696ca002a 100644 --- a/sway-core/src/asm_generation/miden_vm/miden_vm_asm_builder.rs +++ b/sway-core/src/asm_generation/miden_vm/miden_vm_asm_builder.rs @@ -662,6 +662,7 @@ impl<'ir, 'eng> MidenVMAsmBuilder<'ir, 'eng> { B256(_) => todo!(), String(_) => todo!(), Array(_) => todo!(), + Slice(_) => todo!(), Struct(_) => todo!(), Reference(_) => todo!(), RawUntypedSlice(_) => todo!(), diff --git a/sway-core/src/control_flow_analysis/dead_code_analysis.rs b/sway-core/src/control_flow_analysis/dead_code_analysis.rs index 0098313cd58..60843dbf3dd 100644 --- a/sway-core/src/control_flow_analysis/dead_code_analysis.rs +++ b/sway-core/src/control_flow_analysis/dead_code_analysis.rs @@ -970,6 +970,9 @@ fn get_struct_type_info_from_type_id( TypeInfo::Array(type_arg, _) => { get_struct_type_info_from_type_id(type_engine, decl_engine, type_arg.type_id) } + TypeInfo::Slice(type_arg) => { + get_struct_type_info_from_type_id(type_engine, decl_engine, type_arg.type_id) + } _ => Ok(None), } } @@ -1792,35 +1795,28 @@ fn connect_expression<'eng: 'cfg, 'cfg>( elem_type: _, contents, } => { - let mut element_diverge = false; - let nodes = contents - .iter() - .map(|elem| { - if !element_diverge - && type_engine - .get(elem.return_type) - .is_uninhabited(engines.te(), engines.de()) - { - element_diverge = true - } - connect_expression( - engines, - &elem.expression, - graph, - leaves, - exit_node, - "", - tree_type, - elem.span.clone(), - options, - ) - }) - .collect::, _>>()?; - if element_diverge { - Ok(vec![]) - } else { - Ok(nodes.concat()) + let mut last = leaves.to_vec(); + + for elem in contents.iter() { + last = connect_expression( + engines, + &elem.expression, + graph, + last.as_slice(), + None, + "", + tree_type, + elem.span.clone(), + options, + )?; + + // If an element diverges, break the connections and return nothing + if last.is_empty() { + break; + } } + + Ok(last) } ArrayIndex { prefix, index } => { let prefix_idx = connect_expression( diff --git a/sway-core/src/ir_generation/compile.rs b/sway-core/src/ir_generation/compile.rs index 1e8d937b5a2..8d4228de9ef 100644 --- a/sway-core/src/ir_generation/compile.rs +++ b/sway-core/src/ir_generation/compile.rs @@ -11,7 +11,7 @@ use crate::{ use super::{ const_eval::{compile_const_decl, LookupEnv}, - convert::convert_resolved_typeid, + convert::convert_resolved_type_id, function::FnCompiler, CompiledFunctionCache, }; @@ -298,11 +298,11 @@ pub(crate) fn compile_configurables( { let decl = engines.de().get(decl_id); - let ty = convert_resolved_typeid( + let ty = convert_resolved_type_id( engines.te(), engines.de(), context, - &decl.type_ascription.type_id, + decl.type_ascription.type_id, &decl.type_ascription.span, ) .unwrap(); @@ -520,11 +520,11 @@ fn compile_fn( .iter() .map(|param| { // Convert to an IR type. - convert_resolved_typeid( + convert_resolved_type_id( type_engine, decl_engine, context, - ¶m.type_argument.type_id, + param.type_argument.type_id, ¶m.type_argument.span, ) .map(|ty| { @@ -544,11 +544,11 @@ fn compile_fn( .collect::, CompileError>>() .map_err(|err| vec![err])?; - let ret_type = convert_resolved_typeid( + let ret_type = convert_resolved_type_id( type_engine, decl_engine, context, - &return_type.type_id, + return_type.type_id, &return_type.span, ) .map_err(|err| vec![err])?; diff --git a/sway-core/src/ir_generation/const_eval.rs b/sway-core/src/ir_generation/const_eval.rs index 157d103589e..265ca09cddc 100644 --- a/sway-core/src/ir_generation/const_eval.rs +++ b/sway-core/src/ir_generation/const_eval.rs @@ -12,7 +12,7 @@ use crate::{ }; use super::{ - convert::{convert_literal_to_constant, convert_resolved_typeid}, + convert::{convert_literal_to_constant, convert_resolved_type_id}, function::FnCompiler, types::*, }; @@ -620,9 +620,24 @@ fn const_eval_typed_expr( } } } - ty::TyExpressionVariant::Ref(_) | ty::TyExpressionVariant::Deref(_) => { + ty::TyExpressionVariant::Ref(_) => { return Err(ConstEvalError::CompileError); } + ty::TyExpressionVariant::Deref(v) => match &v.expression { + ty::TyExpressionVariant::IntrinsicFunction(f) => match f.kind { + Intrinsic::SliceElem => { + let v = const_eval_intrinsic(lookup, known_consts, f)? + .ok_or(ConstEvalError::CompileError)?; + let v = match v.value { + ConstantValue::Reference(v) => v, + _ => todo!(), + }; + Some((*v).clone()) + } + _ => todo!(), + }, + _ => return Err(ConstEvalError::CompileError), + }, ty::TyExpressionVariant::EnumTag { exp } => { let value = const_eval_typed_expr(lookup, known_consts, exp)?.map(|x| x.value); if let Some(ConstantValue::Struct(fields)) = value { @@ -1036,11 +1051,11 @@ fn const_eval_intrinsic( } Intrinsic::SizeOfType => { let targ = &intrinsic.type_arguments[0]; - let ir_type = convert_resolved_typeid( + let ir_type = convert_resolved_type_id( lookup.engines.te(), lookup.engines.de(), lookup.context, - &targ.type_id, + targ.type_id, &targ.span, ) .map_err(|_| ConstEvalError::CompileError)?; @@ -1052,11 +1067,11 @@ fn const_eval_intrinsic( Intrinsic::SizeOfVal => { let val = &intrinsic.arguments[0]; let type_id = val.return_type; - let ir_type = convert_resolved_typeid( + let ir_type = convert_resolved_type_id( lookup.engines.te(), lookup.engines.de(), lookup.context, - &type_id, + type_id, &val.span, ) .map_err(|_| ConstEvalError::CompileError)?; @@ -1067,11 +1082,11 @@ fn const_eval_intrinsic( } Intrinsic::SizeOfStr => { let targ = &intrinsic.type_arguments[0]; - let ir_type = convert_resolved_typeid( + let ir_type = convert_resolved_type_id( lookup.engines.te(), lookup.engines.de(), lookup.context, - &targ.type_id, + targ.type_id, &targ.span, ) .map_err(|_| ConstEvalError::CompileError)?; @@ -1084,11 +1099,11 @@ fn const_eval_intrinsic( } Intrinsic::AssertIsStrArray => { let targ = &intrinsic.type_arguments[0]; - let ir_type = convert_resolved_typeid( + let ir_type = convert_resolved_type_id( lookup.engines.te(), lookup.engines.de(), lookup.context, - &targ.type_id, + targ.type_id, &targ.span, ) .map_err(|_| ConstEvalError::CompileError)?; @@ -1284,6 +1299,49 @@ fn const_eval_intrinsic( value: ConstantValue::RawUntypedSlice(bytes[0..(len as usize)].to_vec()), })) } + Intrinsic::Slice => { + let start = match &args[1].value { + ConstantValue::Uint(v) => *v, + _ => todo!(), + } as usize; + let end = match &args[2].value { + ConstantValue::Uint(v) => *v, + _ => todo!(), + } as usize; + + match &args[0].value { + ConstantValue::Array(v) => { + let elem_type = v[0].ty; + let slice = v[start..end].to_vec(); + Ok(Some(Constant { + ty: Type::get_typed_slice(lookup.context, elem_type), + value: ConstantValue::Slice(slice), + })) + } + ConstantValue::Reference(r) => match &r.value { + ConstantValue::Slice(_) => todo!(), + _ => todo!(), + }, + _ => todo!(), + } + } + Intrinsic::SliceElem => { + let idx = match &args[1].value { + ConstantValue::Uint(v) => *v, + _ => todo!(), + } as usize; + + match &args[0].value { + ConstantValue::Slice(s) => { + let v = s[idx].clone(); + Ok(Some(Constant { + ty: Type::new_ptr(lookup.context, v.ty), + value: ConstantValue::Reference(Box::new(v)), + })) + } + _ => todo!(), + } + } } } @@ -1322,6 +1380,7 @@ mod tests { None, )); + dbg!(2); let r = crate::compile_to_ast( &handler, &engines, @@ -1332,12 +1391,15 @@ mod tests { None, ); + dbg!(2); let (errors, _warnings) = handler.consume(); + dbg!(1); if !errors.is_empty() { panic!("{:#?}", errors); } + dbg!(1); let f = r.unwrap(); let f = f.typed.unwrap(); diff --git a/sway-core/src/ir_generation/convert.rs b/sway-core/src/ir_generation/convert.rs index ddacdeab37a..3aa22401338 100644 --- a/sway-core/src/ir_generation/convert.rs +++ b/sway-core/src/ir_generation/convert.rs @@ -52,38 +52,29 @@ pub(super) fn convert_literal_to_constant( } } -pub(super) fn convert_resolved_typeid( +pub(super) fn convert_resolved_type_id( type_engine: &TypeEngine, decl_engine: &DeclEngine, context: &mut Context, - ast_type: &TypeId, + ast_type: TypeId, span: &Span, ) -> Result { - // There's probably a better way to convert TypeError to String, but... we'll use something - // other than String eventually? IrError? - convert_resolved_type( - type_engine, - decl_engine, - context, - &type_engine - .to_typeinfo(*ast_type, span) - .map_err(|ty_err| CompileError::InternalOwned(format!("{ty_err:?}"), span.clone()))?, - span, - ) + let t = type_engine.get(ast_type); + convert_resolved_type_info(type_engine, decl_engine, context, &t, span) } pub(super) fn convert_resolved_typeid_no_span( type_engine: &TypeEngine, decl_engine: &DeclEngine, context: &mut Context, - ast_type: &TypeId, + ast_type: TypeId, ) -> Result { let msg = "unknown source location"; let span = crate::span::Span::from_string(msg.to_string()); - convert_resolved_typeid(type_engine, decl_engine, context, ast_type, &span) + convert_resolved_type_id(type_engine, decl_engine, context, ast_type, &span) } -fn convert_resolved_type( +fn convert_resolved_type_info( type_engine: &TypeEngine, decl_engine: &DeclEngine, context: &mut Context, @@ -93,10 +84,10 @@ fn convert_resolved_type( // A handy macro for rejecting unsupported types. macro_rules! reject_type { ($name_str:literal) => {{ - return Err(CompileError::Internal( - concat!($name_str, " type cannot be resolved in IR."), - span.clone(), - )); + return Err(CompileError::TypeMustBeKnownAtThisPoint { + span: span.clone(), + internal: $name_str.into(), + }); }}; } @@ -131,15 +122,16 @@ fn convert_resolved_type( &decl_engine.get_enum(decl_ref).variants, )?, TypeInfo::Array(elem_type, length) => { - let elem_type = convert_resolved_typeid( + let elem_type = convert_resolved_type_id( type_engine, decl_engine, context, - &elem_type.type_id, + elem_type.type_id, span, )?; Type::new_array(context, elem_type, length.val() as u64) } + TypeInfo::Tuple(fields) => { if fields.is_empty() { // XXX We've removed Unit from the core compiler, replaced with an empty Tuple. @@ -154,13 +146,32 @@ fn convert_resolved_type( TypeInfo::RawUntypedPtr => Type::get_uint64(context), TypeInfo::RawUntypedSlice => Type::get_slice(context), TypeInfo::Ptr(_) => Type::get_uint64(context), - TypeInfo::Slice(_) => Type::get_slice(context), TypeInfo::Alias { ty, .. } => { - convert_resolved_typeid(type_engine, decl_engine, context, &ty.type_id, span)? + convert_resolved_type_id(type_engine, decl_engine, context, ty.type_id, span)? + } + // refs to slice are actually fat pointers, + // all others refs are thin pointers. + TypeInfo::Ref { + referenced_type, .. + } => { + if let Some(slice_elem) = type_engine.get(referenced_type.type_id).as_slice() { + let elem_ir_type = convert_resolved_type_id( + type_engine, + decl_engine, + context, + slice_elem.type_id, + span, + )?; + Type::get_typed_slice(context, elem_ir_type) + } else { + Type::get_uint64(context) + } } - TypeInfo::Ref { .. } => Type::get_uint64(context), TypeInfo::Never => Type::get_never(context), + // Unsized types + TypeInfo::Slice(_) => reject_type!("unsized"), + // Unsupported types which shouldn't exist in the AST after type checking and // monomorphisation. TypeInfo::Custom { .. } => reject_type!("Custom"), diff --git a/sway-core/src/ir_generation/function.rs b/sway-core/src/ir_generation/function.rs index 184a4d88c05..0203d230340 100644 --- a/sway-core/src/ir_generation/function.rs +++ b/sway-core/src/ir_generation/function.rs @@ -104,6 +104,49 @@ pub(crate) struct FnCompiler<'eng> { messages_types_map: HashMap, } +fn to_constant(_s: &mut FnCompiler<'_>, context: &mut Context, value: u64) -> Value { + let needed_size = Constant::new_uint(context, 64, value); + Value::new_constant(context, needed_size) +} + +fn save_to_local_return_ptr( + s: &mut FnCompiler<'_>, + context: &mut Context, + value: Value, +) -> Result { + let temp_arg_name = s.lexical_map.insert_anon(); + + let value_type = value.get_type(context).unwrap(); + let local_var = s + .function + .new_local_var(context, temp_arg_name, value_type, None, false) + .map_err(|ir_error| CompileError::InternalOwned(ir_error.to_string(), Span::dummy()))?; + + let local_var_ptr = s.current_block.append(context).get_local(local_var); + let _ = s.current_block.append(context).store(local_var_ptr, value); + Ok(local_var_ptr) +} + +fn calc_addr_as_ptr( + current_block: &mut Block, + context: &mut Context, + ptr: Value, + len: Value, + ptr_to: Type, +) -> Value { + assert!(ptr.get_type(context).unwrap().is_ptr(context)); + assert!(len.get_type(context).unwrap().is_uint64(context)); + + let uint64 = Type::get_uint64(context); + let ptr = current_block.append(context).ptr_to_int(ptr, uint64); + let addr = current_block + .append(context) + .binary_op(BinaryOpKind::Add, ptr, len); + + let ptr_to = Type::new_ptr(context, ptr_to); + current_block.append(context).int_to_ptr(addr, ptr_to) +} + impl<'eng> FnCompiler<'eng> { #[allow(clippy::too_many_arguments)] pub(super) fn new( @@ -498,7 +541,7 @@ impl<'eng> FnCompiler<'eng> { ty::TyExpressionVariant::Array { elem_type, contents, - } => self.compile_array_expr(context, md_mgr, elem_type, contents, span_md_idx), + } => self.compile_array_expr(context, md_mgr, *elem_type, contents, span_md_idx), ty::TyExpressionVariant::ArrayIndex { prefix, index } => { self.compile_array_index(context, md_mgr, prefix, index, span_md_idx) } @@ -861,11 +904,11 @@ impl<'eng> FnCompiler<'eng> { Intrinsic::SizeOfVal => { let exp = &arguments[0]; // Compile the expression in case of side-effects but ignore its value. - let ir_type = convert_resolved_typeid( + let ir_type = convert_resolved_type_id( engines.te(), engines.de(), context, - &exp.return_type, + exp.return_type, &exp.span, )?; self.compile_expression_to_value(context, md_mgr, exp)?; @@ -874,11 +917,11 @@ impl<'eng> FnCompiler<'eng> { } Intrinsic::SizeOfType => { let targ = type_arguments[0].clone(); - let ir_type = convert_resolved_typeid( + let ir_type = convert_resolved_type_id( engines.te(), engines.de(), context, - &targ.type_id, + targ.type_id, &targ.span, )?; let val = Constant::get_uint(context, 64, ir_type.size(context).in_bytes()); @@ -886,11 +929,11 @@ impl<'eng> FnCompiler<'eng> { } Intrinsic::SizeOfStr => { let targ = type_arguments[0].clone(); - let ir_type = convert_resolved_typeid( + let ir_type = convert_resolved_type_id( engines.te(), engines.de(), context, - &targ.type_id, + targ.type_id, &targ.span, )?; let val = Constant::get_uint( @@ -917,11 +960,11 @@ impl<'eng> FnCompiler<'eng> { } Intrinsic::AssertIsStrArray => { let targ = type_arguments[0].clone(); - let ir_type = convert_resolved_typeid( + let ir_type = convert_resolved_type_id( engines.te(), engines.de(), context, - &targ.type_id, + targ.type_id, &targ.span, )?; match ir_type.get_content(context) { @@ -994,11 +1037,11 @@ impl<'eng> FnCompiler<'eng> { // Get the target type from the type argument provided let target_type = &type_arguments[0]; - let target_ir_type = convert_resolved_typeid( + let target_ir_type = convert_resolved_type_id( engines.te(), engines.de(), context, - &target_type.type_id, + target_type.type_id, &target_type.span, )?; @@ -1279,11 +1322,11 @@ impl<'eng> FnCompiler<'eng> { }; let len = type_arguments[0].clone(); - let ir_type = convert_resolved_typeid( + let ir_type = convert_resolved_type_id( engines.te(), engines.de(), context, - &len.type_id, + len.type_id, &len.span, )?; let len_value = Constant::get_uint(context, 64, ir_type.size(context).in_bytes()); @@ -1579,26 +1622,6 @@ impl<'eng> FnCompiler<'eng> { .binary_op(BinaryOpKind::Add, len, step) } - fn calc_addr_as_ptr( - current_block: &mut Block, - context: &mut Context, - ptr: Value, - len: Value, - ptr_to: Type, - ) -> Value { - assert!(ptr.get_type(context).unwrap().is_ptr(context)); - assert!(len.get_type(context).unwrap().is_uint64(context)); - - let uint64 = Type::get_uint64(context); - let ptr = current_block.append(context).ptr_to_int(ptr, uint64); - let addr = current_block - .append(context) - .binary_op(BinaryOpKind::Add, ptr, len); - - let ptr_to = Type::new_ptr(context, ptr_to); - current_block.append(context).int_to_ptr(addr, ptr_to) - } - fn append_with_store( current_block: &mut Block, context: &mut Context, @@ -1661,27 +1684,6 @@ impl<'eng> FnCompiler<'eng> { .binary_op(BinaryOpKind::Add, len, step) } - fn save_to_local_return_ptr( - s: &mut FnCompiler<'_>, - context: &mut Context, - value: Value, - ) -> Result { - let temp_arg_name = s.lexical_map.insert_anon(); - - let value_type = value.get_type(context).unwrap(); - let local_var = s - .function - .new_local_var(context, temp_arg_name, value_type, None, false) - .map_err(|ir_error| { - CompileError::InternalOwned(ir_error.to_string(), Span::dummy()) - })?; - - let local_var_ptr = s.current_block.append(context).get_local(local_var); - let _ = s.current_block.append(context).store(local_var_ptr, value); - - Ok(local_var_ptr) - } - fn append_with_memcpy( s: &mut FnCompiler<'_>, context: &mut Context, @@ -1847,15 +1849,6 @@ impl<'eng> FnCompiler<'eng> { (merge_block_ptr, merge_block_cap) } - fn to_constant( - _s: &mut FnCompiler<'_>, - context: &mut Context, - needed_size: u64, - ) -> Value { - let needed_size = Constant::new_uint(context, 64, needed_size); - Value::new_constant(context, needed_size) - } - // Grow the buffer if needed let (ptr, cap) = match &*item_type { TypeInfo::Boolean => { @@ -2168,9 +2161,229 @@ impl<'eng> FnCompiler<'eng> { Ok(TerminatorValue::new(buffer, context)) } + Intrinsic::Slice => self.compile_intrinsic_slice(arguments, context, md_mgr), + Intrinsic::SliceElem => self.compile_intrinsic_slice_elem(arguments, context, md_mgr), } } + fn compile_intrinsic_slice_elem( + &mut self, + arguments: &[ty::TyExpression], + context: &mut Context, + md_mgr: &mut MetadataManager, + ) -> Result { + assert!(arguments.len() == 2); + + let te = self.engines.te(); + + let ref_to_slice_expr = &arguments[0]; + let slice_type_id = te + .get(ref_to_slice_expr.return_type) + .as_reference() + .unwrap() + .1 + .type_id; + let elem_type_id = te.get(slice_type_id).as_slice().unwrap().type_id; + let elem_ir_type = convert_resolved_type_id( + self.engines.te(), + self.engines.de(), + context, + elem_type_id, + &ref_to_slice_expr.span.clone(), + )?; + let elem_ir_type_size = elem_ir_type.size(context); + let ref_to_slice_value = return_on_termination_or_extract!( + self.compile_expression_to_value(context, md_mgr, ref_to_slice_expr)? + ); + let ptr_to_ref_to_slice = save_to_local_return_ptr(self, context, ref_to_slice_value)?; + + let ptr_to_slice_arg = AsmArg { + name: Ident::new_no_span("ptr_to_slice".into()), + initializer: Some(ptr_to_ref_to_slice), + }; + + let elem_ir_type_size = to_constant(self, context, elem_ir_type_size.in_bytes()); + let elem_len_arg = AsmArg { + name: Ident::new_no_span("item_len".into()), + initializer: Some(elem_ir_type_size), + }; + + let idx = &arguments[1]; + let idx = return_on_termination_or_extract!( + self.compile_expression_to_value(context, md_mgr, idx)? + ); + let idx_arg = AsmArg { + name: Ident::new_no_span("idx".into()), + initializer: Some(idx), + }; + + let offset_out_arg = AsmArg { + name: Ident::new_no_span("offset".into()), + initializer: None, + }; + let ptr_out_arg = AsmArg { + name: Ident::new_no_span("ptr".into()), + initializer: None, + }; + + let ptr_to_elem_type = Type::get_uint64(context); + + let ptr = self.current_block.append(context).asm_block( + vec![ + ptr_to_slice_arg, + idx_arg, + elem_len_arg, + offset_out_arg, + ptr_out_arg, + ], + vec![ + AsmInstruction::lw_no_span("ptr", "ptr_to_slice", "i0"), + AsmInstruction::mul_no_span("offset", "idx", "item_len"), + AsmInstruction::add_no_span("ptr", "ptr", "offset"), + AsmInstruction::log_no_span("ptr", "offset", "idx", "item_len"), + ], + ptr_to_elem_type, + Some(Ident::new_no_span("ptr".into())), + ); + + Ok(TerminatorValue::new(ptr, context)) + } + + fn compile_intrinsic_slice( + &mut self, + arguments: &[ty::TyExpression], + context: &mut Context, + md_mgr: &mut MetadataManager, + ) -> Result { + assert!(arguments.len() == 3); + + let uint64 = Type::get_uint64(context); + let ptr_u64 = Type::new_ptr(context, uint64); + + let first_argument = &arguments[0]; + let elem_type_id = match &*self.engines.te().get(first_argument.return_type) { + TypeInfo::Array(t, _) => t.type_id, + TypeInfo::Slice(t) => t.type_id, + _ => unreachable!(), + }; + let elem_ir_type = convert_resolved_type_id( + self.engines.te(), + self.engines.de(), + context, + elem_type_id, + &first_argument.span.clone(), + )?; + let item_ir_type_size = elem_ir_type.size(context); + let first_argument_value = return_on_termination_or_extract!( + self.compile_expression_to_value(context, md_mgr, first_argument)? + ); + let ptr_to_first_argument = save_to_local_return_ptr(self, context, first_argument_value)?; + let ptr_to_elements = match &*self.engines.te().get(first_argument.return_type) { + TypeInfo::Array(_, _) => self + .current_block + .append(context) + .ptr_to_int(ptr_to_first_argument, uint64), + TypeInfo::Slice(_) => { + let slice_ptr = self + .current_block + .append(context) + .cast_ptr(ptr_to_first_argument, ptr_u64); + self.current_block.append(context).load(slice_ptr) + } + _ => unreachable!(), + }; + + let start = &arguments[1]; + let start = return_on_termination_or_extract!( + self.compile_expression_to_value(context, md_mgr, start)? + ); + + let end = &arguments[2]; + let end = return_on_termination_or_extract!( + self.compile_expression_to_value(context, md_mgr, end)? + ); + + //asm(array_ptr: array, start: start, end: end, item_len: __size_of::(), slice_ptr, slice_len) { + // lw array_ptr array_ptr i0; + // mul slice_ptr start item_len; + // add slice_ptr slice_ptr array_ptr; // byte offset + // sub slice_len end start; + // mul slice_len slice_len item_len; // length in bytes + // (slice_ptr, slice_len): __slice[T] + //}; + + let array_ptr_arg = AsmArg { + name: Ident::new_no_span("array_ptr".into()), + initializer: Some(ptr_to_elements), + }; + let start_arg = AsmArg { + name: Ident::new_no_span("start".into()), + initializer: Some(start), + }; + let end_arg = AsmArg { + name: Ident::new_no_span("end".into()), + initializer: Some(end), + }; + let item_len_arg = AsmArg { + name: Ident::new_no_span("item_len".into()), + initializer: Some(to_constant(self, context, item_ir_type_size.in_bytes())), + }; + + let slice_ptr_out_arg = AsmArg { + name: Ident::new_no_span("slice_ptr".into()), + initializer: None, + }; + let slice_len_out_arg = AsmArg { + name: Ident::new_no_span("slice_len".into()), + initializer: None, + }; + + let slice_ptr = self.current_block.append(context).asm_block( + vec![ + array_ptr_arg, + start_arg.clone(), + item_len_arg.clone(), + slice_ptr_out_arg, + ], + vec![ + AsmInstruction::mul_no_span("slice_ptr", "start", "item_len"), + AsmInstruction::add_no_span("slice_ptr", "slice_ptr", "array_ptr"), + ], + uint64, + Some(Ident::new_no_span("slice_ptr".into())), + ); + + let slice_len = self.current_block.append(context).asm_block( + vec![start_arg, end_arg, item_len_arg, slice_len_out_arg], + vec![ + AsmInstruction::sub_no_span("slice_len", "end", "start"), + AsmInstruction::mul_no_span("slice_len", "slice_len", "item_len"), + ], + uint64, + Some(Ident::new_no_span("slice_len".into())), + ); + + // compile the slice together + let return_type = Type::get_typed_slice(context, elem_ir_type); + let slice_as_tuple = self.compile_tuple_from_values( + context, + vec![slice_ptr, slice_len], + vec![uint64, uint64], + None, + )?; + let slice = self.current_block.append(context).asm_block( + vec![AsmArg { + name: Ident::new_no_span("s".into()), + initializer: Some(slice_as_tuple), + }], + vec![], + return_type, + Some(Ident::new_no_span("s".into())), + ); + + Ok(TerminatorValue::new(slice, context)) + } + fn compile_return( &mut self, context: &mut Context, @@ -2299,11 +2512,11 @@ impl<'eng> FnCompiler<'eng> { )), }?; - let referenced_ir_type = convert_resolved_typeid( + let referenced_ir_type = convert_resolved_type_id( self.engines.te(), self.engines.de(), context, - &referenced_ast_type, + referenced_ast_type, &ast_expr.span.clone(), )?; @@ -2629,7 +2842,7 @@ impl<'eng> FnCompiler<'eng> { self.engines.te(), self.engines.de(), context, - &ast_return_type, + ast_return_type, )?; let ret_is_copy_type = self .engines @@ -2778,7 +2991,7 @@ impl<'eng> FnCompiler<'eng> { self.engines.te(), self.engines.de(), context, - &return_type, + return_type, ) .unwrap_or_else(|_| Type::get_unit(context)); let merge_val_arg_idx = merge_block.new_arg(context, return_type); @@ -2806,11 +3019,11 @@ impl<'eng> FnCompiler<'eng> { variant: &ty::TyEnumVariant, ) -> Result { // Retrieve the type info for the enum. - let enum_type = match convert_resolved_typeid( + let enum_type = match convert_resolved_type_id( self.engines.te(), self.engines.de(), context, - &exp.return_type, + exp.return_type, &exp.span, )? { ty if ty.is_struct(context) => ty, @@ -3087,11 +3300,11 @@ impl<'eng> FnCompiler<'eng> { // accessed and isn't present in the environment. let init_val = self.compile_expression_to_value(context, md_mgr, body); - let return_type = convert_resolved_typeid( + let return_type = convert_resolved_type_id( self.engines.te(), self.engines.de(), context, - &body.return_type, + body.return_type, &body.span, )?; @@ -3166,11 +3379,11 @@ impl<'eng> FnCompiler<'eng> { .lexical_map .insert(call_path.suffix.as_str().to_owned()); - let return_type = convert_resolved_typeid( + let return_type = convert_resolved_type_id( self.engines.te(), self.engines.de(), context, - &value.return_type, + value.return_type, &value.span, )?; @@ -3371,7 +3584,7 @@ impl<'eng> FnCompiler<'eng> { &mut self, context: &mut Context, md_mgr: &mut MetadataManager, - elem_type: &TypeId, + elem_type: TypeId, contents: &[ty::TyExpression], span_md_idx: Option, ) -> Result { @@ -3617,7 +3830,7 @@ impl<'eng> FnCompiler<'eng> { self.engines.te(), self.engines.de(), context, - &struct_field.value.return_type, + struct_field.value.return_type, )?; field_types.push(field_type); } @@ -3696,11 +3909,11 @@ impl<'eng> FnCompiler<'eng> { ) })?; - let field_type = convert_resolved_typeid( + let field_type = convert_resolved_type_id( self.engines.te(), self.engines.de(), context, - &field_type_id, + field_type_id, &ast_field.span, )?; @@ -3855,7 +4068,7 @@ impl<'eng> FnCompiler<'eng> { self.engines.te(), self.engines.de(), context, - &field_expr.return_type, + field_expr.return_type, )?; init_values.push(init_value); init_types.push(init_type); @@ -3879,11 +4092,11 @@ impl<'eng> FnCompiler<'eng> { let tuple_value = return_on_termination_or_extract!( self.compile_expression_to_ptr(context, md_mgr, tuple)? ); - let tuple_type = convert_resolved_typeid( + let tuple_type = convert_resolved_type_id( self.engines.te(), self.engines.de(), context, - &tuple_type, + tuple_type, &span, )?; @@ -3930,7 +4143,7 @@ impl<'eng> FnCompiler<'eng> { self.engines.te(), self.engines.de(), context, - &base_type, + base_type, )?; // Do the actual work. This is a recursive function because we want to drill down @@ -4035,7 +4248,7 @@ impl<'eng> FnCompiler<'eng> { self.engines.te(), self.engines.de(), context, - &return_type, + return_type, )?; let val = self .current_block diff --git a/sway-core/src/ir_generation/types.rs b/sway-core/src/ir_generation/types.rs index 6d549c7f890..63487dd6b78 100644 --- a/sway-core/src/ir_generation/types.rs +++ b/sway-core/src/ir_generation/types.rs @@ -26,7 +26,7 @@ pub(super) fn create_tagged_union_type( type_engine, decl_engine, context, - &tev.type_argument.type_id, + tev.type_argument.type_id, ) }) .collect::, CompileError>>()?; @@ -51,7 +51,7 @@ pub(super) fn create_tuple_aggregate( ) -> Result { let field_types = fields .iter() - .map(|ty_id| convert_resolved_typeid_no_span(type_engine, decl_engine, context, ty_id)) + .map(|ty_id| convert_resolved_typeid_no_span(type_engine, decl_engine, context, *ty_id)) .collect::, CompileError>>()?; Ok(Type::new_struct(context, field_types)) @@ -65,7 +65,7 @@ pub(super) fn create_array_aggregate( count: u64, ) -> Result { let element_type = - convert_resolved_typeid_no_span(type_engine, decl_engine, context, &element_type_id)?; + convert_resolved_typeid_no_span(type_engine, decl_engine, context, element_type_id)?; Ok(Type::new_array(context, element_type, count)) } @@ -77,7 +77,7 @@ pub(super) fn get_struct_for_types( ) -> Result { let types = type_ids .iter() - .map(|ty_id| convert_resolved_typeid_no_span(type_engine, decl_engine, context, ty_id)) + .map(|ty_id| convert_resolved_typeid_no_span(type_engine, decl_engine, context, *ty_id)) .collect::, CompileError>>()?; Ok(Type::new_struct(context, types)) } diff --git a/sway-core/src/language/literal.rs b/sway-core/src/language/literal.rs index a50712d04ad..afedaad12d0 100644 --- a/sway-core/src/language/literal.rs +++ b/sway-core/src/language/literal.rs @@ -22,6 +22,19 @@ pub enum Literal { B256([u8; 32]), } +impl Literal { + pub fn cast_value_to_u64(&self) -> Option { + match self { + Literal::U8(v) => Some(*v as u64), + Literal::U16(v) => Some(*v as u64), + Literal::U32(v) => Some(*v as u64), + Literal::U64(v) => Some(*v), + Literal::Numeric(v) => Some(*v), + _ => None, + } + } +} + impl Hash for Literal { fn hash(&self, state: &mut H) { use Literal::*; diff --git a/sway-core/src/language/ty/expression/expression_variant.rs b/sway-core/src/language/ty/expression/expression_variant.rs index 12f68716031..4f5643a5020 100644 --- a/sway-core/src/language/ty/expression/expression_variant.rs +++ b/sway-core/src/language/ty/expression/expression_variant.rs @@ -172,6 +172,15 @@ pub enum TyExpressionVariant { Deref(Box), } +impl TyExpressionVariant { + pub fn as_literal(&self) -> Option<&Literal> { + match self { + TyExpressionVariant::Literal(v) => Some(v), + _ => None, + } + } +} + impl EqWithEngines for TyExpressionVariant {} impl PartialEqWithEngines for TyExpressionVariant { fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool { diff --git a/sway-core/src/language/ty/module.rs b/sway-core/src/language/ty/module.rs index 21bc45eb6cf..33078fb2bb6 100644 --- a/sway-core/src/language/ty/module.rs +++ b/sway-core/src/language/ty/module.rs @@ -4,9 +4,8 @@ use sway_error::handler::{ErrorEmitted, Handler}; use sway_types::Span; use crate::{ - decl_engine::{DeclEngine, DeclRef, DeclRefFunction}, - language::ModName, - language::{ty::*, HasModule, HasSubmodules}, + decl_engine::{DeclEngine, DeclEngineGet, DeclRef, DeclRefFunction}, + language::{ty::*, HasModule, HasSubmodules, ModName}, semantic_analysis::namespace, transform::{self, AllowDeprecatedState}, Engines, @@ -21,6 +20,59 @@ pub struct TyModule { pub attributes: transform::AttributesMap, } +impl TyModule { + /// Iter on all constants in this module, which means, globals constants and + /// local constants, but it does not enter into submodules. + pub fn iter_constants(&self, de: &DeclEngine) -> Vec { + fn inside_code_block(de: &DeclEngine, block: &TyCodeBlock) -> Vec { + block + .contents + .iter() + .flat_map(|node| inside_ast_node(de, node)) + .collect::>() + } + + fn inside_ast_node(de: &DeclEngine, node: &TyAstNode) -> Vec { + match &node.content { + TyAstNodeContent::Declaration(decl) => match decl { + TyDecl::ConstantDecl(decl) => { + vec![decl.clone()] + } + TyDecl::FunctionDecl(decl) => { + let decl = de.get(&decl.decl_id); + inside_code_block(de, &decl.body) + } + TyDecl::ImplSelfOrTrait(decl) => { + let decl = de.get(&decl.decl_id); + decl.items + .iter() + .flat_map(|item| match item { + TyTraitItem::Fn(decl) => { + let decl = de.get(decl.id()); + inside_code_block(de, &decl.body) + } + TyTraitItem::Constant(decl) => { + vec![ConstantDecl { + decl_id: *decl.id(), + }] + } + _ => vec![], + }) + .collect() + } + _ => vec![], + }, + _ => vec![], + } + } + + self.all_nodes + .iter() + .flat_map(|node| inside_ast_node(de, node)) + .collect::>() + } +} + #[derive(Clone, Debug)] pub struct TySubmodule { pub module: TyModule, diff --git a/sway-core/src/language/ty/program.rs b/sway-core/src/language/ty/program.rs index 7d585533f18..92d273f1526 100644 --- a/sway-core/src/language/ty/program.rs +++ b/sway-core/src/language/ty/program.rs @@ -72,7 +72,6 @@ impl TyProgram { let decl_engine = engines.de(); // Validate all submodules - let mut non_configurables_constants = vec![]; let mut configurables = vec![]; for (_, submodule) in &root.submodules { match Self::validate_root( @@ -114,13 +113,6 @@ impl TyProgram { declarations.push(TyDecl::FunctionDecl(FunctionDecl { decl_id: *decl_id })); } - TyAstNodeContent::Declaration(TyDecl::ConstantDecl(ConstantDecl { - decl_id, - .. - })) => { - let decl = (*decl_engine.get_constant(decl_id)).clone(); - non_configurables_constants.push(decl); - } TyAstNodeContent::Declaration(TyDecl::ConfigurableDecl(ConfigurableDecl { decl_id, .. @@ -404,6 +396,7 @@ impl TyProgram { &c.type_ascription, |t| match t { TypeInfo::StringSlice => Some(TypeNotAllowedReason::StringSliceInConfigurables), + TypeInfo::Slice(_) => Some(TypeNotAllowedReason::SliceInConst), _ => None, }, ) { @@ -411,16 +404,18 @@ impl TyProgram { } } - for c in non_configurables_constants.iter() { - if let Some(error) = get_type_not_allowed_error( - engines, - c.return_type, - &c.type_ascription, - |t| match t { - TypeInfo::StringSlice => Some(TypeNotAllowedReason::StringSliceInConst), - _ => None, - }, - ) { + // verify all constants + for decl in root.iter_constants(decl_engine).iter() { + let decl = decl_engine.get_constant(&decl.decl_id); + let e = + get_type_not_allowed_error(engines, decl.return_type, &decl.type_ascription, |t| { + match t { + TypeInfo::StringSlice => Some(TypeNotAllowedReason::StringSliceInConst), + TypeInfo::Slice(_) => Some(TypeNotAllowedReason::SliceInConst), + _ => None, + } + }); + if let Some(error) = e { handler.emit_err(error); } } diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index bdc7e683ceb..7508faab6ea 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -573,13 +573,17 @@ pub fn parsed_to_ast( } }; - // Skip collecting metadata if we triggered an optimised build from LSP. - let types_metadata = if !lsp_config.as_ref().is_some_and(|lsp| lsp.optimized_build) { + // Skip collecting type metadata and control-flow analysis + // if we triggered an optimised build from LSP. + + let is_lsp_optimized_build = lsp_config.as_ref().is_some_and(|lsp| lsp.optimized_build); + let types_metadata = if !is_lsp_optimized_build { // Collect information about the types used in this program let types_metadata_result = typed_program.collect_types_metadata( handler, &mut CollectTypesMetadataContext::new(engines, experimental, package_name.to_string()), ); + let types_metadata = match types_metadata_result { Ok(types_metadata) => types_metadata, Err(e) => { @@ -588,19 +592,17 @@ pub fn parsed_to_ast( } }; - typed_program - .logged_types - .extend(types_metadata.iter().filter_map(|m| match m { - TypeMetadata::LoggedType(log_id, type_id) => Some((*log_id, *type_id)), - _ => None, - })); + let logged_types = types_metadata.iter().filter_map(|m| match m { + TypeMetadata::LoggedType(log_id, type_id) => Some((*log_id, *type_id)), + _ => None, + }); + typed_program.logged_types.extend(logged_types); - typed_program - .messages_types - .extend(types_metadata.iter().filter_map(|m| match m { - TypeMetadata::MessageType(message_id, type_id) => Some((*message_id, *type_id)), - _ => None, - })); + let message_types = types_metadata.iter().filter_map(|m| match m { + TypeMetadata::MessageType(message_id, type_id) => Some((*message_id, *type_id)), + _ => None, + }); + typed_program.messages_types.extend(message_types); let (print_graph, print_graph_url_format) = match build_config { Some(cfg) => ( @@ -828,10 +830,13 @@ pub(crate) fn compile_ast_to_ir_to_asm( program: &ty::TyProgram, build_config: &BuildConfig, ) -> Result { - // The IR pipeline relies on type information being fully resolved. - // If type information is found to still be generic or unresolved inside of - // IR, this is considered an internal compiler error. To resolve this situation, - // we need to explicitly ensure all types are resolved before going into IR. + // IR generaterion requires type information to be fully resolved. + // + // If type information is found to still be generic inside of + // IR, this is considered an internal compiler error. + // + // But, there are genuine cases for types be unknown here, like `let a = []`. These should + // have friendly errors. // // We _could_ introduce a new type here that uses TypeInfo instead of TypeId and throw away // the engine, since we don't need inference for IR. That'd be a _lot_ of copy-pasted code, diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl.rs b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl.rs index 2731811f6a7..d0629249f51 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl.rs @@ -221,7 +221,7 @@ where }, _ => { let variant_type_name = Self::generate_type(engines, x.type_argument.type_id)?; - format!("{tag_value} => {enum_name}::{variant_name}(buffer.decode::<{variant_type}>()), \n", + format!("{tag_value} => {enum_name}::{variant_name}(buffer.decode::<{variant_type}>()), \n", tag_value = x.tag, enum_name = enum_name, variant_name = name, @@ -551,6 +551,12 @@ where count.val() ) } + TypeInfo::Slice(elem_ty) => { + format!( + "__slice[{}]", + Self::generate_type(engines, elem_ty.type_id)? + ) + } TypeInfo::RawUntypedPtr => "raw_ptr".into(), TypeInfo::RawUntypedSlice => "raw_slice".into(), TypeInfo::Alias { name, .. } => name.to_string(), @@ -847,7 +853,7 @@ where let code = if args_types == "()" { format!( "pub fn __entry() -> raw_slice {{ - let result: {return_type} = main(); + let result: {return_type} = main(); encode::<{return_type}>(result) }}" ) @@ -855,7 +861,7 @@ where format!( "pub fn __entry() -> raw_slice {{ let args: {args_types} = decode_script_data::<{args_types}>(); - let result: {return_type} = main({expanded_args}); + let result: {return_type} = main({expanded_args}); encode::<{return_type}>(result) }}" ) 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 9c324cf1289..4b642a06c5e 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 @@ -10,7 +10,8 @@ use crate::{ engine_threading::*, language::{ parsed::{Expression, ExpressionKind}, - ty, Literal, + ty::{self, TyIntrinsicFunctionKind}, + Literal, }, semantic_analysis::{type_check_context::EnforceTypeArguments, TypeCheckContext}, type_system::*, @@ -101,7 +102,242 @@ impl ty::TyIntrinsicFunctionKind { Intrinsic::EncodeBufferAsRawSlice => { type_check_encode_as_raw_slice(handler, ctx, kind, arguments, type_arguments, span) } + Intrinsic::Slice => { + type_check_slice(handler, ctx, kind, arguments, type_arguments, span) + } + Intrinsic::SliceElem => type_check_slice_elem(arguments, handler, kind, span, ctx), + } + } +} + +fn type_check_slice_elem( + arguments: &[Expression], + handler: &Handler, + kind: Intrinsic, + span: Span, + ctx: TypeCheckContext, +) -> Result<(TyIntrinsicFunctionKind, TypeId), ErrorEmitted> { + if arguments.len() != 2 { + return Err(handler.emit_err(CompileError::IntrinsicIncorrectNumArgs { + name: kind.to_string(), + expected: 2, + span, + })); + } + + let type_engine = ctx.engines.te(); + let engines = ctx.engines(); + + let mut ctx = ctx; + + // check first argument + let first_argument_span = arguments[0].span.clone(); + let first_argument_type = type_engine.insert(engines, TypeInfo::Unknown, None); + let first_argument_typed_expr = { + let ctx = ctx + .by_ref() + .with_help_text("") + .with_type_annotation(first_argument_type); + ty::TyExpression::type_check(handler, ctx, &arguments[0])? + }; + + let first_argument_ref_to_type = type_engine + .get(first_argument_type) + .as_reference() + .map(|(_, t)| type_engine.get(t.type_id)); + let Some(TypeInfo::Slice(elem_type)) = first_argument_ref_to_type.as_deref() else { + return Err(handler.emit_err(CompileError::IntrinsicUnsupportedArgType { + name: kind.to_string(), + span: first_argument_span, + hint: "".to_string(), + })); + }; + + // index argument + let index_type = type_engine.insert( + engines, + TypeInfo::UnsignedInteger(IntegerBits::SixtyFour), + None, + ); + let index_typed_expr = { + let ctx = ctx + .by_ref() + .with_help_text("") + .with_type_annotation(index_type); + ty::TyExpression::type_check(handler, ctx, &arguments[1])? + }; + + let return_type = type_engine.insert( + engines, + TypeInfo::Ref { + to_mutable_value: true, + referenced_type: TypeArgument { + type_id: elem_type.type_id, + initial_type_id: elem_type.type_id, + span: Span::dummy(), + call_path_tree: None, + }, + }, + None, + ); + + Ok(( + TyIntrinsicFunctionKind { + kind, + arguments: vec![first_argument_typed_expr, index_typed_expr], + type_arguments: vec![], + span, + }, + return_type, + )) +} + +fn type_check_slice( + handler: &Handler, + mut ctx: TypeCheckContext, + kind: sway_ast::Intrinsic, + arguments: &[Expression], + _type_arguments: &[TypeArgument], + span: Span, +) -> Result<(ty::TyIntrinsicFunctionKind, TypeId), ErrorEmitted> { + if arguments.len() != 3 { + return Err(handler.emit_err(CompileError::IntrinsicIncorrectNumArgs { + name: kind.to_string(), + expected: 3, + span, + })); + } + + let type_engine = ctx.engines.te(); + let engines = ctx.engines(); + + // start index argument + let start_type = type_engine.insert( + engines, + TypeInfo::UnsignedInteger(IntegerBits::SixtyFour), + None, + ); + let start_ty_expr = { + let ctx = ctx + .by_ref() + .with_help_text("") + .with_type_annotation(start_type); + ty::TyExpression::type_check(handler, ctx, &arguments[1])? + }; + + // end index argument + let end_type = type_engine.insert( + engines, + TypeInfo::UnsignedInteger(IntegerBits::SixtyFour), + None, + ); + let end_ty_expr = { + let ctx = ctx + .by_ref() + .with_help_text("") + .with_type_annotation(end_type); + ty::TyExpression::type_check(handler, ctx, &arguments[2])? + }; + + // check first argument + let array_span = arguments[0].span.clone(); + let array_type = type_engine.insert(engines, TypeInfo::Unknown, None); + let array_ty_expr = { + let ctx = ctx + .by_ref() + .with_help_text("") + .with_type_annotation(array_type); + ty::TyExpression::type_check(handler, ctx, &arguments[0])? + }; + + // statically check start and end, if possible + let start_literal = start_ty_expr + .expression + .as_literal() + .and_then(|x| x.cast_value_to_u64()); + + let end_literal = end_ty_expr + .expression + .as_literal() + .and_then(|x| x.cast_value_to_u64()); + + if let (Some(start), Some(end)) = (start_literal, end_literal) { + if start > end { + return Err( + handler.emit_err(CompileError::InvalidRangeEndGreaterThanStart { + start, + end, + span, + }), + ); + } + } + + fn create_ref_to_slice(engines: &Engines, elem_type_arg: TypeArgument) -> TypeId { + let type_engine = engines.te(); + let slice_type_id = + type_engine.insert(engines, TypeInfo::Slice(elem_type_arg.clone()), None); + let ref_to_slice_type = TypeInfo::Ref { + to_mutable_value: false, + referenced_type: TypeArgument { + type_id: slice_type_id, + initial_type_id: slice_type_id, + span: Span::dummy(), + call_path_tree: None, + }, + }; + type_engine.insert(engines, ref_to_slice_type, None) + } + + // We can slice arrays or other slices + match &*type_engine.get(array_type) { + TypeInfo::Array(elem_type_arg, array_len) => { + let array_len = array_len.val() as u64; + + if let Some(v) = start_literal { + if v > array_len { + return Err(handler.emit_err(CompileError::ArrayOutOfBounds { + index: v, + count: array_len, + span, + })); + } + } + + if let Some(v) = end_literal { + if v > array_len { + return Err(handler.emit_err(CompileError::ArrayOutOfBounds { + index: v, + count: array_len, + span, + })); + } + } + + Ok(( + TyIntrinsicFunctionKind { + kind, + arguments: vec![array_ty_expr, start_ty_expr, end_ty_expr], + type_arguments: vec![], + span, + }, + create_ref_to_slice(engines, elem_type_arg.clone()), + )) } + TypeInfo::Slice(elem_type_arg) => Ok(( + TyIntrinsicFunctionKind { + kind, + arguments: vec![array_ty_expr, start_ty_expr, end_ty_expr], + type_arguments: vec![], + span, + }, + create_ref_to_slice(engines, elem_type_arg.clone()), + )), + _ => Err(handler.emit_err(CompileError::IntrinsicUnsupportedArgType { + name: kind.to_string(), + span: array_span, + hint: "".to_string(), + })), } } diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs index e5f5ca57586..8a0c4f671c9 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs @@ -1805,20 +1805,20 @@ impl ty::TyExpression { let engines = ctx.engines(); if contents.is_empty() { - let never_type = type_engine.insert(engines, TypeInfo::Never, None); + let elem_type = type_engine.insert(engines, TypeInfo::Unknown, None); return Ok(ty::TyExpression { expression: ty::TyExpressionVariant::Array { - elem_type: never_type, + elem_type, contents: Vec::new(), }, return_type: type_engine.insert( engines, TypeInfo::Array( TypeArgument { - type_id: never_type, + type_id: elem_type, span: Span::dummy(), call_path_tree: None, - initial_type_id: never_type, + initial_type_id: elem_type, }, Length::new(0, Span::dummy()), ), diff --git a/sway-core/src/semantic_analysis/cei_pattern_analysis.rs b/sway-core/src/semantic_analysis/cei_pattern_analysis.rs index 808d114c23a..1d72f32cfb1 100644 --- a/sway-core/src/semantic_analysis/cei_pattern_analysis.rs +++ b/sway-core/src/semantic_analysis/cei_pattern_analysis.rs @@ -648,6 +648,8 @@ fn effects_of_intrinsic(intr: &sway_ast::Intrinsic) -> HashSet { | EncodeBufferEmpty | EncodeBufferAppend | EncodeBufferAsRawSlice => HashSet::new(), + Slice => todo!(), + SliceElem => todo!(), } } diff --git a/sway-core/src/semantic_analysis/node_dependencies.rs b/sway-core/src/semantic_analysis/node_dependencies.rs index 45d2858a1b8..9fe1e98b670 100644 --- a/sway-core/src/semantic_analysis/node_dependencies.rs +++ b/sway-core/src/semantic_analysis/node_dependencies.rs @@ -794,6 +794,7 @@ impl Dependencies { deps.gather_from_type_argument(engines, elem) }), TypeInfo::Array(elem_type, _) => self.gather_from_type_argument(engines, elem_type), + TypeInfo::Slice(elem_type) => self.gather_from_type_argument(engines, elem_type), TypeInfo::Struct(decl_ref) => self.gather_from_iter( decl_engine.get_struct(decl_ref).fields.iter(), |deps, field| deps.gather_from_type_argument(engines, &field.type_argument), diff --git a/sway-core/src/semantic_analysis/type_check_context.rs b/sway-core/src/semantic_analysis/type_check_context.rs index 621c8e8d838..dc1d4661e98 100644 --- a/sway-core/src/semantic_analysis/type_check_context.rs +++ b/sway-core/src/semantic_analysis/type_check_context.rs @@ -593,6 +593,28 @@ impl<'a> TypeCheckContext<'a> { elem_ty.span.source_id(), ) } + TypeInfo::Slice(mut elem_ty) => { + elem_ty.type_id = self + .resolve( + handler, + elem_ty.type_id, + span, + enforce_type_arguments, + None, + mod_path, + ) + .unwrap_or_else(|err| { + self.engines + .te() + .insert(self.engines, TypeInfo::ErrorRecovery(err), None) + }); + + self.engines.te().insert( + self.engines, + TypeInfo::Slice(elem_ty.clone()), + elem_ty.span.source_id(), + ) + } TypeInfo::Tuple(mut type_arguments) => { for type_argument in type_arguments.iter_mut() { type_argument.type_id = self diff --git a/sway-core/src/type_system/info.rs b/sway-core/src/type_system/info.rs index 793be444b13..eeb22e6a734 100644 --- a/sway-core/src/type_system/info.rs +++ b/sway-core/src/type_system/info.rs @@ -1049,7 +1049,7 @@ impl TypeInfo { TypeInfo::Tuple(fields) => fields .iter() .any(|field_type| id_uninhabited(field_type.type_id)), - TypeInfo::Array(elem_ty, length) => length.val() > 0 && id_uninhabited(elem_ty.type_id), + TypeInfo::Array(elem_ty, _) => id_uninhabited(elem_ty.type_id), TypeInfo::Ptr(ty) => id_uninhabited(ty.type_id), TypeInfo::Alias { name: _, ty } => id_uninhabited(ty.type_id), TypeInfo::Slice(ty) => id_uninhabited(ty.type_id), @@ -1175,6 +1175,16 @@ impl TypeInfo { matches!(self, TypeInfo::Ref { .. }) } + pub fn as_reference(&self) -> Option<(&bool, &TypeArgument)> { + match self { + TypeInfo::Ref { + to_mutable_value, + referenced_type, + } => Some((to_mutable_value, referenced_type)), + _ => None, + } + } + pub fn is_array(&self) -> bool { matches!(self, TypeInfo::Array(_, _)) } @@ -1191,6 +1201,18 @@ impl TypeInfo { matches!(self, TypeInfo::Tuple(_)) } + pub fn is_slice(&self) -> bool { + matches!(self, TypeInfo::Slice(_)) + } + + pub fn as_slice(&self) -> Option<&TypeArgument> { + if let TypeInfo::Slice(t) = self { + Some(t) + } else { + None + } + } + pub(crate) fn apply_type_arguments( self, handler: &Handler, @@ -1413,7 +1435,6 @@ impl TypeInfo { | TypeInfo::RawUntypedPtr | TypeInfo::RawUntypedSlice | TypeInfo::Ptr(_) - | TypeInfo::Slice(_) | TypeInfo::ErrorRecovery(_) | TypeInfo::TraitType { .. } | TypeInfo::Never => false, @@ -1423,6 +1444,7 @@ impl TypeInfo { | TypeInfo::Custom { .. } | TypeInfo::Tuple(_) | TypeInfo::Array(_, _) + | TypeInfo::Slice(_) | TypeInfo::Contract | TypeInfo::Storage { .. } | TypeInfo::Numeric diff --git a/sway-core/src/type_system/substitute/subst_map.rs b/sway-core/src/type_system/substitute/subst_map.rs index 3d917bc5f43..9eb13a8da3d 100644 --- a/sway-core/src/type_system/substitute/subst_map.rs +++ b/sway-core/src/type_system/substitute/subst_map.rs @@ -233,6 +233,14 @@ impl TypeSubstMap { vec![type_argument.type_id], ) } + (TypeInfo::Slice(type_parameter), TypeInfo::Slice(type_argument)) => { + TypeSubstMap::from_superset_and_subset_helper( + type_engine, + decl_engine, + vec![type_parameter.type_id], + vec![type_argument.type_id], + ) + } ( TypeInfo::Storage { fields: type_parameters, @@ -408,6 +416,15 @@ impl TypeSubstMap { ) }) } + TypeInfo::Slice(mut elem_ty) => { + let type_id = self.find_match(elem_ty.type_id, engines)?; + elem_ty.type_id = type_id; + Some(type_engine.insert( + engines, + TypeInfo::Slice(elem_ty.clone()), + elem_ty.span.source_id(), + )) + } TypeInfo::Tuple(fields) => { let mut need_to_create_new = false; let mut source_id = None; @@ -470,10 +487,6 @@ impl TypeSubstMap { ty.type_id = type_id; type_engine.insert(engines, TypeInfo::Ptr(ty.clone()), ty.span.source_id()) }), - TypeInfo::Slice(mut ty) => self.find_match(ty.type_id, engines).map(|type_id| { - ty.type_id = type_id; - type_engine.insert(engines, TypeInfo::Slice(ty.clone()), ty.span.source_id()) - }), TypeInfo::TraitType { .. } => iter_for_match(engines, self, &type_info), TypeInfo::Ref { to_mutable_value, diff --git a/sway-core/src/type_system/unify/unifier.rs b/sway-core/src/type_system/unify/unifier.rs index 0efc7883153..e94cb3b7384 100644 --- a/sway-core/src/type_system/unify/unifier.rs +++ b/sway-core/src/type_system/unify/unifier.rs @@ -93,8 +93,8 @@ impl<'a> Unifier<'a> { pub(crate) fn unify(&self, handler: &Handler, received: TypeId, expected: TypeId, span: &Span) { use TypeInfo::{ Alias, Array, Boolean, Contract, Enum, Never, Numeric, Placeholder, RawUntypedPtr, - RawUntypedSlice, Ref, StringArray, StringSlice, Struct, Tuple, Unknown, UnknownGeneric, - UnsignedInteger, B256, + RawUntypedSlice, Ref, Slice, StringArray, StringSlice, Struct, Tuple, Unknown, + UnknownGeneric, UnsignedInteger, B256, }; if received == expected { @@ -123,6 +123,9 @@ impl<'a> Unifier<'a> { (Array(re, rc), Array(ee, ec)) if rc.val() == ec.val() => { self.unify_type_arguments_in_parents(handler, received, expected, span, re, ee); } + (Slice(re), Slice(ee)) => { + self.unify_type_arguments_in_parents(handler, received, expected, span, re, ee); + } (Struct(r_decl_ref), Struct(e_decl_ref)) => { let r_decl = self.engines.de().get_struct(r_decl_ref); let e_decl = self.engines.de().get_struct(e_decl_ref); diff --git a/sway-core/src/type_system/unify/unify_check.rs b/sway-core/src/type_system/unify/unify_check.rs index 24f73a077c1..26a0777bdee 100644 --- a/sway-core/src/type_system/unify/unify_check.rs +++ b/sway-core/src/type_system/unify/unify_check.rs @@ -224,7 +224,8 @@ impl<'a> UnifyCheck<'a> { fn check_inner(&self, left: TypeId, right: TypeId) -> bool { use TypeInfo::{ Alias, Array, ContractCaller, Custom, Enum, ErrorRecovery, Never, Numeric, Placeholder, - Ref, StringArray, StringSlice, Struct, Tuple, Unknown, UnknownGeneric, UnsignedInteger, + Ref, Slice, StringArray, StringSlice, Struct, Tuple, Unknown, UnknownGeneric, + UnsignedInteger, }; use UnifyCheckMode::{ Coercion, ConstraintSubset, NonDynamicEquality, NonGenericConstraintSubset, @@ -242,10 +243,15 @@ impl<'a> UnifyCheck<'a> { (Never, Never) => { return true; } + (Array(l0, l1), Array(r0, r1)) => { return self.check_inner(l0.type_id, r0.type_id) && l1.val() == r1.val(); } + (Slice(l0), Slice(r0)) => { + return self.check_inner(l0.type_id, r0.type_id); + } + (Tuple(l_types), Tuple(r_types)) => { let l_types = l_types.iter().map(|x| x.type_id).collect::>(); let r_types = r_types.iter().map(|x| x.type_id).collect::>(); diff --git a/sway-error/src/error.rs b/sway-error/src/error.rs index 45c42c222dd..f0bb9c26a84 100644 --- a/sway-error/src/error.rs +++ b/sway-error/src/error.rs @@ -655,8 +655,12 @@ pub enum CompileError { ContractStorageFromExternalContext { span: Span }, #[error("The {opcode} opcode cannot be used in a predicate.")] InvalidOpcodeFromPredicate { opcode: String, span: Span }, - #[error("Array index out of bounds; the length is {count} but the index is {index}.")] + #[error("Index out of bounds; the length is {count} but the index is {index}.")] ArrayOutOfBounds { index: u64, count: u64, span: Span }, + #[error( + "Invalid range; the range end at index {end} is smaller than its start at index {start}" + )] + InvalidRangeEndGreaterThanStart { start: u64, end: u64, span: Span }, #[error("Tuple index {index} is out of bounds. The tuple has {count} element{}.", plural_s(*count))] TupleIndexOutOfBounds { index: usize, @@ -1005,6 +1009,8 @@ pub enum CompileError { EncodingUnsupportedType { span: Span }, #[error("Configurables need a function named \"abi_decode_in_place\" to be in scope.")] ConfigurableMissingAbiDecodeInPlace { span: Span }, + #[error("Type must be known at this point")] + TypeMustBeKnownAtThisPoint { span: Span, internal: String }, } impl std::convert::From for CompileError { @@ -1220,6 +1226,8 @@ impl Spanned for CompileError { CannotBeEvaluatedToConfigurableSizeUnknown { span } => span.clone(), EncodingUnsupportedType { span } => span.clone(), ConfigurableMissingAbiDecodeInPlace { span } => span.clone(), + InvalidRangeEndGreaterThanStart { span, .. } => span.clone(), + TypeMustBeKnownAtThisPoint { span, .. } => span.clone(), } } } @@ -2668,6 +2676,9 @@ pub enum TypeNotAllowedReason { #[error("`str` or a type containing `str` on `const` is not allowed.")] StringSliceInConst, + + #[error("slices or types containing slices on `const` are not allowed.")] + SliceInConst, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/sway-ir/src/asm.rs b/sway-ir/src/asm.rs index 8a74df16ecf..b6f4cd657c9 100644 --- a/sway-ir/src/asm.rs +++ b/sway-ir/src/asm.rs @@ -43,6 +43,82 @@ pub struct AsmInstruction { pub metadata: Option, } +impl AsmInstruction { + pub fn log_no_span( + ra: impl Into, + rb: impl Into, + rc: impl Into, + rd: impl Into, + ) -> Self { + AsmInstruction { + op_name: Ident::new(sway_types::Span::from_string("log".into())), + args: vec![ + Ident::new_no_span(ra.into()), + Ident::new_no_span(rb.into()), + Ident::new_no_span(rc.into()), + Ident::new_no_span(rd.into()), + ], + immediate: None, + metadata: None, + } + } + + pub fn lw_no_span( + dst: impl Into, + src: impl Into, + offset: impl Into, + ) -> Self { + AsmInstruction { + op_name: Ident::new(sway_types::Span::from_string("lw".into())), + args: vec![ + Ident::new_no_span(dst.into()), + Ident::new_no_span(src.into()), + ], + immediate: Some(Ident::new_no_span(offset.into())), + metadata: None, + } + } + + pub fn mul_no_span(dst: impl Into, a: impl Into, b: impl Into) -> Self { + AsmInstruction { + op_name: Ident::new(sway_types::Span::from_string("mul".into())), + args: vec![ + Ident::new_no_span(dst.into()), + Ident::new_no_span(a.into()), + Ident::new_no_span(b.into()), + ], + immediate: None, + metadata: None, + } + } + + pub fn add_no_span(dst: impl Into, a: impl Into, b: impl Into) -> Self { + AsmInstruction { + op_name: Ident::new(sway_types::Span::from_string("add".into())), + args: vec![ + Ident::new_no_span(dst.into()), + Ident::new_no_span(a.into()), + Ident::new_no_span(b.into()), + ], + immediate: None, + metadata: None, + } + } + + pub fn sub_no_span(dst: impl Into, a: impl Into, b: impl Into) -> Self { + AsmInstruction { + op_name: Ident::new(sway_types::Span::from_string("sub".into())), + args: vec![ + Ident::new_no_span(dst.into()), + Ident::new_no_span(a.into()), + Ident::new_no_span(b.into()), + ], + immediate: None, + metadata: None, + } + } +} + impl AsmBlock { /// Create a new [`AsmBlock`] in the passed context and return its handle. pub fn new( diff --git a/sway-ir/src/constant.rs b/sway-ir/src/constant.rs index 9aaae3c4218..fc4d33a3125 100644 --- a/sway-ir/src/constant.rs +++ b/sway-ir/src/constant.rs @@ -23,6 +23,7 @@ pub enum ConstantValue { B256(B256), String(Vec), Array(Vec), + Slice(Vec), Struct(Vec), Reference(Box), RawUntypedSlice(Vec), diff --git a/sway-ir/src/irtype.rs b/sway-ir/src/irtype.rs index 3bca2ace003..af2a3cdf1cc 100644 --- a/sway-ir/src/irtype.rs +++ b/sway-ir/src/irtype.rs @@ -39,6 +39,7 @@ pub enum TypeContent { Struct(Vec), Slice, Pointer(Type), + TypedSlice(Type), } impl Type { @@ -162,6 +163,11 @@ impl Type { Self::get_type(context, &TypeContent::Slice).expect("create_basic_types not called") } + /// Get typed slice type + pub fn get_typed_slice(context: &mut Context, item_ty: Type) -> Type { + Self::get_or_create_unique_type(context, TypeContent::TypedSlice(item_ty)) + } + /// Return a string representation of type, used for printing. pub fn as_string(&self, context: &Context) -> String { let sep_types_str = |agg_content: &Vec, sep: &str| { @@ -190,6 +196,7 @@ impl Type { format!("{{ {} }}", sep_types_str(agg, ", ")) } TypeContent::Slice => "slice".into(), + TypeContent::TypedSlice(ty) => format!("__slice[{}]", ty.as_string(context)), TypeContent::Pointer(ty) => format!("ptr {}", ty.as_string(context)), } } @@ -209,6 +216,9 @@ impl Type { (TypeContent::Array(l, llen), TypeContent::Array(r, rlen)) => { llen == rlen && l.eq(context, r) } + + (TypeContent::TypedSlice(l), TypeContent::TypedSlice(r)) => l.eq(context, r), + (TypeContent::Struct(l), TypeContent::Struct(r)) | (TypeContent::Union(l), TypeContent::Union(r)) => { l.len() == r.len() && l.iter().zip(r.iter()).all(|(l, r)| l.eq(context, r)) @@ -542,6 +552,7 @@ impl Type { TypeContent::Uint(256) => TypeSize::new(32), TypeContent::Uint(_) => unreachable!(), TypeContent::Slice => TypeSize::new(16), + TypeContent::TypedSlice(..) => TypeSize::new(16), TypeContent::B256 => TypeSize::new(32), TypeContent::StringSlice => TypeSize::new(16), TypeContent::StringArray(n) => { diff --git a/sway-ir/src/optimize/sroa.rs b/sway-ir/src/optimize/sroa.rs index 31550709dfb..70c8160362c 100644 --- a/sway-ir/src/optimize/sroa.rs +++ b/sway-ir/src/optimize/sroa.rs @@ -404,6 +404,7 @@ fn is_processable_aggregate(context: &Context, ty: Type) -> bool { fields.iter().all(|ty| check_sub_types(context, *ty)) } crate::TypeContent::Slice => false, + crate::TypeContent::TypedSlice(..) => false, crate::TypeContent::Pointer(_) => true, crate::TypeContent::StringSlice => false, crate::TypeContent::StringArray(_) => false, diff --git a/sway-ir/src/printer.rs b/sway-ir/src/printer.rs index 19b90c92861..bcb957b2999 100644 --- a/sway-ir/src/printer.rs +++ b/sway-ir/src/printer.rs @@ -1188,6 +1188,15 @@ impl Constant { .collect::>() .join(", ") ), + ConstantValue::Slice(elems) => format!( + "__slice[{}] [{}]", + self.ty.as_string(context), + elems + .iter() + .map(|elem| elem.as_lit_string(context)) + .collect::>() + .join(", ") + ), ConstantValue::Struct(fields) => format!( "{} {{ {} }}", self.ty.as_string(context), diff --git a/sway-lib-core/src/codec.sw b/sway-lib-core/src/codec.sw index 9ab99efc188..59a70ece81c 100644 --- a/sway-lib-core/src/codec.sw +++ b/sway-lib-core/src/codec.sw @@ -697,6 +697,17 @@ impl AbiEncode for raw_slice { } } +impl AbiEncode for &__slice[T] +where + T: AbiEncode, +{ + fn abi_encode(self, buffer: Buffer) -> Buffer { + Buffer { + buffer: __encode_buffer_append(buffer.buffer, self), + } + } +} + impl AbiEncode for [T; 0] where T: AbiEncode, @@ -2533,7 +2544,7 @@ where } } -// Decode +// Decode pub trait AbiDecode { fn abi_decode(ref mut buffer: BufferReader) -> Self; diff --git a/sway-lib-core/src/lib.sw b/sway-lib-core/src/lib.sw index 9bec5cc9bd9..4437bdd85cb 100644 --- a/sway-lib-core/src/lib.sw +++ b/sway-lib-core/src/lib.sw @@ -3,6 +3,7 @@ library; pub mod primitives; pub mod raw_ptr; pub mod raw_slice; +pub mod slice; pub mod r#str; pub mod ops; pub mod primitive_conversions; diff --git a/sway-lib-core/src/ops.sw b/sway-lib-core/src/ops.sw index 1229ca8e649..aaa46315bfc 100644 --- a/sway-lib-core/src/ops.sw +++ b/sway-lib-core/src/ops.sw @@ -1,6 +1,7 @@ library; use ::primitives::*; +use ::slice::*; /// Trait for the addition of two values. pub trait Add { @@ -1257,3 +1258,31 @@ pub fn ok_str_eq() { assert("" != "a"); assert("a" != "b"); } + +//impl Eq for __slice[T] +//where +// T: Eq +//{ +// fn eq(self, other: Self) -> bool { +// let self_qty = self.len(); +// let other_qty = other.len(); +// +// if self_qty != other_qty { +// return false +// } +// +// let mut i = 0u64; +// while i < self_qty { +// let self_elem = __slice_elem(self, i); +// let other_elem = __slice_elem(other, i); +// +// if self_elem != other_elem { +// return false; +// } +// +// i += 1; +// } +// +// true +// } +//} diff --git a/sway-lib-core/src/slice.sw b/sway-lib-core/src/slice.sw new file mode 100644 index 00000000000..c2a19246696 --- /dev/null +++ b/sway-lib-core/src/slice.sw @@ -0,0 +1,19 @@ +library; + +use ::raw_ptr::*; + +impl &__slice[T] { + pub fn ptr(self) -> raw_ptr { + let (ptr, _) = asm(s: self) { + s: (raw_ptr, u64) + }; + ptr + } + + pub fn len(self) -> u64 { + let (_, len) = asm(s: self) { + s: (raw_ptr, u64) + }; + len + } +} diff --git a/sway-lsp/src/core/token.rs b/sway-lsp/src/core/token.rs index 51a84347d83..3e657a0cff9 100644 --- a/sway-lsp/src/core/token.rs +++ b/sway-lsp/src/core/token.rs @@ -285,6 +285,10 @@ pub fn type_info_to_symbol_kind( let type_info = type_engine.get(elem_ty.type_id); type_info_to_symbol_kind(type_engine, &type_info, Some(&elem_ty.span())) } + TypeInfo::Slice(elem_ty) => { + let type_info = type_engine.get(elem_ty.type_id); + type_info_to_symbol_kind(type_engine, &type_info, Some(&elem_ty.span())) + } _ => SymbolKind::Unknown, } } diff --git a/sway-lsp/src/traverse/parsed_tree.rs b/sway-lsp/src/traverse/parsed_tree.rs index 39ba771f879..869afce1925 100644 --- a/sway-lsp/src/traverse/parsed_tree.rs +++ b/sway-lsp/src/traverse/parsed_tree.rs @@ -1041,6 +1041,9 @@ impl Parse for TypeArgument { ); type_arg.parse(ctx); } + TypeInfo::Slice(type_arg) => { + type_arg.parse(ctx); + } TypeInfo::Tuple(type_arguments) => { adaptive_iter(type_arguments, |type_arg| type_arg.parse(ctx)); } diff --git a/sway-lsp/src/traverse/typed_tree.rs b/sway-lsp/src/traverse/typed_tree.rs index acfa4bf9de2..3c6098cc43d 100644 --- a/sway-lsp/src/traverse/typed_tree.rs +++ b/sway-lsp/src/traverse/typed_tree.rs @@ -1270,6 +1270,9 @@ fn collect_type_id( TypeInfo::Array(type_arg, ..) => { collect_type_argument(ctx, type_arg); } + TypeInfo::Slice(type_arg, ..) => { + collect_type_argument(ctx, type_arg); + } TypeInfo::Tuple(type_arguments) => { adaptive_iter(type_arguments, |type_arg| { collect_type_argument(ctx, type_arg); diff --git a/test/src/e2e_vm_tests/mod.rs b/test/src/e2e_vm_tests/mod.rs index b5ed9a43040..4c8664fbe40 100644 --- a/test/src/e2e_vm_tests/mod.rs +++ b/test/src/e2e_vm_tests/mod.rs @@ -12,6 +12,7 @@ use core::fmt; use forc_pkg::BuildProfile; use forc_test::decode_log_data; use fuel_vm::fuel_tx; +use fuel_vm::fuel_types::canonical::Input; use fuel_vm::prelude::*; use regex::Regex; use std::collections::HashSet; @@ -113,6 +114,8 @@ struct TestContext { } fn print_receipts(output: &mut String, receipts: &[Receipt]) { + let mut text_log = String::new(); + use std::fmt::Write; let _ = writeln!(output, " {}", "Receipts".green().bold()); for (i, receipt) in receipts.iter().enumerate() { @@ -129,6 +132,28 @@ fn print_receipts(output: &mut String, receipts: &[Receipt]) { is, data, } => { + // Small hack to allow log from tests. + if *ra == u64::MAX { + match rb { + 0 => { + let mut data = data.as_deref().unwrap(); + data.skip(8).unwrap(); + let s = std::str::from_utf8(data).unwrap(); + + text_log.push_str(s); + } + 1 => { + let data = data.as_deref().unwrap(); + let s = u64::from_be_bytes(data.try_into().unwrap()); + + text_log.push_str(&format!("{}", s)); + } + 2 => { + text_log.push('\n'); + } + _ => {} + } + } let _ = write!(output, " LogData\n ID: {id:?}\n RA: {ra:?}\n RB: {rb:?}\n Ptr: {ptr:?}\n Len: {len:?}\n Digest: {digest:?}\n PC: {pc:?}\n IS: {is:?}\n Data: {data:?}\n"); } Receipt::ReturnData { @@ -238,6 +263,14 @@ fn print_receipts(output: &mut String, receipts: &[Receipt]) { } } } + + if !text_log.is_empty() { + let _ = writeln!(output, " {}", "Text Logs".green().bold()); + + for l in text_log.lines() { + let _ = writeln!(output, "{l}"); + } + } } impl TestContext { diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/array_oob/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/array_oob/test.toml index f4fb799b640..059ddb65458 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/array_oob/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/array_oob/test.toml @@ -1,3 +1,3 @@ category = "fail" -# check: $()Array index out of bounds; the length is 3 but the index is 4. +# check: $()Index out of bounds; the length is 3 but the index is 4. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/array_oob_global_const_index/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/array_oob_global_const_index/test.toml index f4fb799b640..059ddb65458 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/array_oob_global_const_index/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/array_oob_global_const_index/test.toml @@ -1,3 +1,3 @@ category = "fail" -# check: $()Array index out of bounds; the length is 3 but the index is 4. +# check: $()Index out of bounds; the length is 3 but the index is 4. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/array_oob_variable_index/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/array_oob_variable_index/test.toml index f4fb799b640..059ddb65458 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/array_oob_variable_index/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/array_oob_variable_index/test.toml @@ -1,3 +1,3 @@ category = "fail" -# check: $()Array index out of bounds; the length is 3 but the index is 4. +# check: $()Index out of bounds; the length is 3 but the index is 4. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/slice/slice_intrinsics/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/slice/slice_intrinsics/Forc.lock new file mode 100644 index 00000000000..c5bc5415952 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/slice/slice_intrinsics/Forc.lock @@ -0,0 +1,8 @@ +[[package]] +name = "core" +source = "path+from-root-53D6DC514F06A250" + +[[package]] +name = "slice_intrinsics" +source = "member" +dependencies = ["core"] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/slice/slice_intrinsics/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/slice/slice_intrinsics/Forc.toml new file mode 100644 index 00000000000..011fe1ab735 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/slice/slice_intrinsics/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "slice_intrinsics" + +[dependencies] +core = { path = "../../../../../../../sway-lib-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/slice/slice_intrinsics/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_fail/slice/slice_intrinsics/json_abi_oracle.json new file mode 100644 index 00000000000..ad50b55d54c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/slice/slice_intrinsics/json_abi_oracle.json @@ -0,0 +1,25 @@ +{ + "configurables": [], + "functions": [ + { + "attributes": null, + "inputs": [], + "name": "main", + "output": { + "name": "", + "type": 0, + "typeArguments": null + } + } + ], + "loggedTypes": [], + "messagesTypes": [], + "types": [ + { + "components": null, + "type": "u64", + "typeId": 0, + "typeParameters": null + } + ] +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/slice/slice_intrinsics/json_abi_oracle_new_encoding.json b/test/src/e2e_vm_tests/test_programs/should_fail/slice/slice_intrinsics/json_abi_oracle_new_encoding.json new file mode 100644 index 00000000000..e60dda965d4 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/slice/slice_intrinsics/json_abi_oracle_new_encoding.json @@ -0,0 +1,26 @@ +{ + "configurables": [], + "encoding": "1", + "functions": [ + { + "attributes": null, + "inputs": [], + "name": "main", + "output": { + "name": "", + "type": 0, + "typeArguments": null + } + } + ], + "loggedTypes": [], + "messagesTypes": [], + "types": [ + { + "components": [], + "type": "()", + "typeId": 0, + "typeParameters": null + } + ] +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/slice/slice_intrinsics/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/slice/slice_intrinsics/src/main.sw new file mode 100644 index 00000000000..b2bb36b32ab --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/slice/slice_intrinsics/src/main.sw @@ -0,0 +1,28 @@ +script; + +// slice cannot be consts +const GLOBAL_ARRAY: [u64; 5] = [1, 2, 3, 4, 5]; +const GLOBAL_SLICE: &__slice[u64] = __slice(GLOBAL_ARRAY, 0, 5); + +fn main() { + // slice cannot be consts + const LOCAL_ARRAY: [u64; 5] = [1, 2, 3, 4, 5]; + const LOCAL_SLICE: &__slice[u64] = __slice(LOCAL_ARRAY, 0, 5); + + // Wrong start index + let a: [u64; 5] = [1, 2, 3, 4, 5]; + let _ = __slice(a, 6, 7); + + // Wrong end index + let a: [u64; 5] = [1, 2, 3, 4, 5]; + let _ = __slice(a, 0, 6); + + // Wrong first argument + __slice(0, 0, 0); + + // Wrong start index + __slice(0, "", 0); + + // Wrong end index + __slice(0, 0, ""); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/slice/slice_intrinsics/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/slice/slice_intrinsics/test.toml new file mode 100644 index 00000000000..3bbe64aee42 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/slice/slice_intrinsics/test.toml @@ -0,0 +1,26 @@ +category = "fail" + +# check: let _ = __slice(a, 6, 7) +# nextln: $()Index out of bounds; the length is 5 but the index is 6. + +# check: let _ = __slice(a, 0, 6) +# nextln: $()Index out of bounds; the length is 5 but the index is 6. + +# check: __slice(0, 0, 0) +# nextln: $()Unsupported argument type to intrinsic "slice" + +# check: __slice(0, "", 0) +# nextln: $()Mismatched types +# nextln: $()expected: u64 +# nextln: $()found: str + +# check: __slice(0, 0, "") +# nextln: $()Mismatched types +# nextln: $()expected: u64 +# nextln: $()found: str + +# check: &__slice[u64] = __slice(GLOBAL_ARRAY, 0, 5) +# nextln: $()slices or types containing slices on `const` are not allowed + +# check: &__slice[u64] = __slice(LOCAL_ARRAY, 0, 5) +# nextln: $()slices or types containing slices on `const` are not allowed diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/slice/slice_intrinsics/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/slice/slice_intrinsics/Forc.lock new file mode 100644 index 00000000000..a7de4b3565f --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/slice/slice_intrinsics/Forc.lock @@ -0,0 +1,16 @@ +[[package]] +name = "core" +source = "path+from-root-53D6DC514F06A250" + +[[package]] +name = "slice_intrinsics" +source = "member" +dependencies = [ + "core", + "utils", +] + +[[package]] +name = "utils" +source = "path+from-root-53D6DC514F06A250" +dependencies = ["core"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/slice/slice_intrinsics/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/slice/slice_intrinsics/Forc.toml new file mode 100644 index 00000000000..fe47736bbcf --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/slice/slice_intrinsics/Forc.toml @@ -0,0 +1,9 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "slice_intrinsics" + +[dependencies] +core = { path = "../../../../../../../../sway-lib-core" } +utils = { path = "../../../../../../../../test/src/e2e_vm_tests/utils" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/slice/slice_intrinsics/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/slice/slice_intrinsics/json_abi_oracle.json new file mode 100644 index 00000000000..ad50b55d54c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/slice/slice_intrinsics/json_abi_oracle.json @@ -0,0 +1,25 @@ +{ + "configurables": [], + "functions": [ + { + "attributes": null, + "inputs": [], + "name": "main", + "output": { + "name": "", + "type": 0, + "typeArguments": null + } + } + ], + "loggedTypes": [], + "messagesTypes": [], + "types": [ + { + "components": null, + "type": "u64", + "typeId": 0, + "typeParameters": null + } + ] +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/slice/slice_intrinsics/json_abi_oracle_new_encoding.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/slice/slice_intrinsics/json_abi_oracle_new_encoding.json new file mode 100644 index 00000000000..17f3d600e0d --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/slice/slice_intrinsics/json_abi_oracle_new_encoding.json @@ -0,0 +1,41 @@ +{ + "configurables": [], + "encoding": "1", + "functions": [ + { + "attributes": null, + "inputs": [], + "name": "main", + "output": { + "name": "", + "type": 0, + "typeArguments": null + } + } + ], + "loggedTypes": [ + { + "logId": "1515152261580153489", + "loggedType": { + "name": "", + "type": 1, + "typeArguments": null + } + } + ], + "messagesTypes": [], + "types": [ + { + "components": [], + "type": "()", + "typeId": 0, + "typeParameters": null + }, + { + "components": null, + "type": "u64", + "typeId": 1, + "typeParameters": null + } + ] +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/slice/slice_intrinsics/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/slice/slice_intrinsics/src/main.sw new file mode 100644 index 00000000000..43f9edd086d --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/slice/slice_intrinsics/src/main.sw @@ -0,0 +1,151 @@ +script; + +use utils::*; + +fn alloc_slice(len: u64) -> &__slice[T] { + let size_in_bytes = len * __size_of::(); + let ptr = asm(size_in_bytes: size_in_bytes) { + aloc size_in_bytes; + hp: raw_ptr + }; + asm(buf: (ptr, len)) { + buf: &__slice[T] + } +} + +fn realloc_slice(old: &__slice[T], len: u64) -> &__slice[T] { + let old_ptr = old.ptr(); + let old_len_in_bytes = old.len() * __size_of::(); + + let new_len_in_bytes = len * __size_of::(); + let new_ptr = asm(new_len_in_bytes: new_len_in_bytes, old_ptr: old_ptr, old_len_in_bytes: old_len_in_bytes) { + aloc new_len_in_bytes; + mcp hp old_ptr old_len_in_bytes; + hp: raw_ptr + }; + + asm(buf: (new_ptr, len)) { + buf: &__slice[T] + } +} + +pub struct Vec { + buf: &__slice[T], + len: u64, +} + +impl Dbg for Vec { + fn dbg(self) { + ( + "Vec { buf: (ptr: ", + asm(v: self.buf.ptr()) { v: u64 }, + ", len: ", + self.buf.len(), + "), len: ", + self.len, + ")" + ).dbg(); + } +} + + +impl Vec { + pub fn new() -> Self { + Self { + buf: alloc_slice::(0), + len: 0 + } + } + + pub fn push(ref mut self, item: T) { + "Vec::push(...)".dbgln(); + (" ", self).dbgln(); + + let new_item_idx = self.len; + let current_cap = self.buf.len(); + if new_item_idx >= current_cap { + let new_cap = if current_cap == 0 { + 1 + } else { + current_cap * 2 + }; + self.buf = realloc_slice(self.buf, new_cap); + (" After realloc: ", self).dbgln(); + } + + let v: &mut T = __slice_elem(self.buf, new_item_idx); + + let buffer_addr = asm(v: self.buf.ptr()) { v: u64 }; + let elem_addr = asm(v: v) { v: u64 }; + (" elem ", new_item_idx, " at ", elem_addr, " buffer offset (in bytes): ", elem_addr - buffer_addr).dbgln(); + *v = item; + + self.len += 1; + + (" ", self).dbgln(); + } + + pub fn get(self, index: u64) -> T { + ("Vec::get(", index, ")").dbgln(); + (" ", self).dbgln(); + + let item: &mut T = __slice_elem(self.buf, index); + + let buffer_addr = asm(v: self.buf.ptr()) { v: u64 }; + let elem_addr = asm(v: item) { v: u64 }; + (" element ", index, " at ", elem_addr, " buffer offset (in bytes): ", elem_addr - buffer_addr).dbgln(); + + *item + } +} + +fn assert(l: T, r: T) +where + T: Eq + AbiEncode +{ + if l != r { + __log(l); + __log(r); + __revert(1) + } +} + +fn main() { + // slice arrays + let a: [u64; 5] = [1, 2, 3, 4, 5]; + let s = __slice(a, 0, 5); + assert(1, *__slice_elem(s, 0)); + assert(2, *__slice_elem(s, 1)); + assert(3, *__slice_elem(s, 2)); + assert(4, *__slice_elem(s, 3)); + assert(5, *__slice_elem(s, 4)); + + // slice another slice + let s = __slice(a, 1, 4); + assert(2, *__slice_elem(s, 0)); + assert(3, *__slice_elem(s, 1)); + assert(4, *__slice_elem(s, 2)); + + // Vec impl using slices + let mut v: Vec = Vec::new(); + v.push(1); + assert(v.get(0), 1); + + v.push(2); + v.push(3); + assert(v.get(0), 1); + assert(v.get(1), 2); + assert(v.get(2), 3); + + v.push(4); + v.push(5); + v.push(6); + v.push(7); + assert(v.get(0), 1); + assert(v.get(1), 2); + assert(v.get(2), 3); + assert(v.get(3), 4); + assert(v.get(4), 5); + assert(v.get(5), 6); + assert(v.get(6), 7); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/slice/slice_intrinsics/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/slice/slice_intrinsics/test.toml new file mode 100644 index 00000000000..dae3eb53b08 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/slice/slice_intrinsics/test.toml @@ -0,0 +1,7 @@ +category = "run" + +category_new_encoding = "run" +script_data_new_encoding = "" +expected_result_new_encoding = { action = "return_data", value = "" } + +validate_abi = true diff --git a/test/src/e2e_vm_tests/utils/.gitignore b/test/src/e2e_vm_tests/utils/.gitignore new file mode 100644 index 00000000000..77d3844f58c --- /dev/null +++ b/test/src/e2e_vm_tests/utils/.gitignore @@ -0,0 +1,2 @@ +out +target diff --git a/test/src/e2e_vm_tests/utils/Forc.toml b/test/src/e2e_vm_tests/utils/Forc.toml new file mode 100644 index 00000000000..10a6cda5587 --- /dev/null +++ b/test/src/e2e_vm_tests/utils/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "utils" + +[dependencies] +core = { path = "../../../../sway-lib-core" } diff --git a/test/src/e2e_vm_tests/utils/src/main.sw b/test/src/e2e_vm_tests/utils/src/main.sw new file mode 100644 index 00000000000..4dcfbf52f28 --- /dev/null +++ b/test/src/e2e_vm_tests/utils/src/main.sw @@ -0,0 +1,146 @@ +library; + +/// This is only useful for the e2e harness setup, because +/// no one else knows how to "decode" this into meaningful +// textual logs. +pub trait Dbg { + fn dbg(self); +} { + fn dbgln(self) { + self.dbg(); + asm(ra: u64::max(), rb: 2) { + logd ra rb zero zero; + } + } +} + +impl Dbg for str { + fn dbg(self) { + let encoded = encode(self); + asm(ra: u64::max(), ptr: encoded.ptr(), len: encoded.len::()) { + logd ra zero ptr len; + } + } +} + + +impl Dbg for u64 { + fn dbg(self) { + let encoded = encode(self); + asm(ra: u64::max(), ptr: encoded.ptr(), len: encoded.len::()) { + logd ra one ptr len; + } + } +} + +pub struct NewLine {} + +pub fn new_line() -> NewLine { + NewLine { } +} + +impl Dbg for NewLine { + fn dbg(self) { + asm(ra: u64::max(), rb: 2) { + logd ra rb zero zero; + } + } +} + +impl Dbg for (A, B) +where + A: Dbg, + B: Dbg, +{ + fn dbg(self) { + self.0.dbg(); + self.1.dbg(); + } +} + +impl Dbg for (A, B, C) +where + A: Dbg, + B: Dbg, + C: Dbg, +{ + #[allow(dead_code)] + fn dbg(self) { + self.0.dbg(); + self.1.dbg(); + self.2.dbg(); + } +} + +impl Dbg for (A, B, C, D) +where + A: Dbg, + B: Dbg, + C: Dbg, + D: Dbg +{ + fn dbg(self) { + self.0.dbg(); + self.1.dbg(); + self.2.dbg(); + self.3.dbg(); + } +} + + +impl Dbg for (A, B, C, D, E) +where + A: Dbg, + B: Dbg, + C: Dbg, + D: Dbg, + E: Dbg, +{ + fn dbg(self) { + self.0.dbg(); + self.1.dbg(); + self.2.dbg(); + self.3.dbg(); + self.4.dbg(); + } +} + +impl Dbg for (A, B, C, D, E, F) +where + A: Dbg, + B: Dbg, + C: Dbg, + D: Dbg, + E: Dbg, + F: Dbg, +{ + fn dbg(self) { + self.0.dbg(); + self.1.dbg(); + self.2.dbg(); + self.3.dbg(); + self.4.dbg(); + self.5.dbg(); + } +} + +impl Dbg for (A, B, C, D, E, F, G) +where + A: Dbg, + B: Dbg, + C: Dbg, + D: Dbg, + E: Dbg, + F: Dbg, + G: Dbg +{ + fn dbg(self) { + self.0.dbg(); + self.1.dbg(); + self.2.dbg(); + self.3.dbg(); + self.4.dbg(); + self.5.dbg(); + self.6.dbg(); + } +}