| 1 | import os |
| 2 | |
| 3 | const vexe = @VEXE |
| 4 | const tests_dir = os.dir(@FILE) |
| 5 | const v3_dir = os.dir(tests_dir) |
| 6 | const vlib_dir = os.dir(v3_dir) |
| 7 | const repo_dir = os.dir(vlib_dir) |
| 8 | const v3_src = os.join_path(v3_dir, 'v3.v') |
| 9 | const compiler_src_dir = os.join_path(repo_dir, 'cmd', 'v') |
| 10 | |
| 11 | fn tmp_test_path(name string) string { |
| 12 | return os.join_path(os.temp_dir(), 'v3_${name}_${os.getpid()}') |
| 13 | } |
| 14 | |
| 15 | fn build_v3() string { |
| 16 | v3_bin := tmp_test_path('post_merge_review_fixes_test') |
| 17 | build := |
| 18 | os.execute('${vexe} -gc none -path "${vlib_dir}|@vlib|@vmodules" -o ${v3_bin} ${v3_src}') |
| 19 | assert build.exit_code == 0, build.output |
| 20 | return v3_bin |
| 21 | } |
| 22 | |
| 23 | fn run_good(v3_bin string, name string, src string) string { |
| 24 | good_src := '${tmp_test_path(name)}.v' |
| 25 | os.write_file(good_src, src) or { panic(err) } |
| 26 | good_bin := tmp_test_path(name) |
| 27 | compile := os.execute('${v3_bin} ${good_src} -b c -o ${good_bin}') |
| 28 | assert compile.exit_code == 0, compile.output |
| 29 | assert !compile.output.contains('C compilation failed'), compile.output |
| 30 | run := os.execute(good_bin) |
| 31 | assert run.exit_code == 0, run.output |
| 32 | return run.output.trim_space() |
| 33 | } |
| 34 | |
| 35 | fn run_bad(v3_bin string, name string, src string, expected string) { |
| 36 | bad_src := '${tmp_test_path(name)}.v' |
| 37 | os.write_file(bad_src, src) or { panic(err) } |
| 38 | bad_bin := tmp_test_path(name) |
| 39 | compile := os.execute('${v3_bin} ${bad_src} -b c -o ${bad_bin}') |
| 40 | assert compile.exit_code != 0, compile.output |
| 41 | assert compile.output.contains(expected), compile.output |
| 42 | assert !compile.output.contains('C compilation failed'), compile.output |
| 43 | } |
| 44 | |
| 45 | fn gen_c(v3_bin string, name string, src string) string { |
| 46 | src_path := '${tmp_test_path(name)}.v' |
| 47 | os.write_file(src_path, src) or { panic(err) } |
| 48 | c_path := '${tmp_test_path(name)}.c' |
| 49 | os.rm(c_path) or {} |
| 50 | compile := os.execute('${v3_bin} ${src_path} -b c -o ${c_path}') |
| 51 | assert compile.exit_code == 0, compile.output |
| 52 | assert os.exists(c_path) |
| 53 | return os.read_file(c_path) or { panic(err) } |
| 54 | } |
| 55 | |
| 56 | fn write_project_file(root string, rel string, src string) { |
| 57 | path := os.join_path(root, rel) |
| 58 | os.mkdir_all(os.dir(path)) or { panic(err) } |
| 59 | os.write_file(path, src) or { panic(err) } |
| 60 | } |
| 61 | |
| 62 | fn run_good_project(v3_bin string, name string, files map[string]string, input string) string { |
| 63 | root := '${tmp_test_path(name)}_project' |
| 64 | if os.exists(root) { |
| 65 | os.rmdir_all(root) or { panic(err) } |
| 66 | } |
| 67 | os.mkdir_all(root) or { panic(err) } |
| 68 | for rel, src in files { |
| 69 | write_project_file(root, rel, src) |
| 70 | } |
| 71 | input_path := if input.len == 0 { root } else { os.join_path(root, input) } |
| 72 | good_bin := tmp_test_path(name) |
| 73 | compile := os.execute('${v3_bin} ${input_path} -b c -o ${good_bin}') |
| 74 | assert compile.exit_code == 0, compile.output |
| 75 | assert !compile.output.contains('C compilation failed'), compile.output |
| 76 | run := os.execute(good_bin) |
| 77 | assert run.exit_code == 0, run.output |
| 78 | return run.output.trim_space() |
| 79 | } |
| 80 | |
| 81 | fn test_compiler_vexe_env_uses_running_executable() { |
| 82 | v3_bin := build_v3() |
| 83 | c_out := os.join_path(os.temp_dir(), 'v3_review_vexe.c') |
| 84 | os.rm(c_out) or {} |
| 85 | gen := os.execute('${v3_bin} -o ${c_out} ${compiler_src_dir}') |
| 86 | assert gen.exit_code == 0, gen.output |
| 87 | assert os.exists(c_out) |
| 88 | c_source := os.read_file(c_out) or { panic(err) } |
| 89 | assert !c_source.contains('v3_vexe_target') |
| 90 | assert !c_source.contains('fopen(v3_src') |
| 91 | assert c_source.contains('snprintf(v3_checkout_vexe, sizeof(v3_checkout_vexe),') |
| 92 | assert c_source.contains('if (access(v3_checkout_vexe, F_OK) == 0) v3_vexe = v3_checkout_vexe;') |
| 93 | assert c_source.contains('const char* v3_vexe = v3_src_real_result != NULL ? v3_src_real : v3_arg0;') |
| 94 | assert c_source.contains('_putenv_s("VEXE", v3_vexe);') |
| 95 | assert c_source.contains('setenv("VEXE", v3_vexe, 1);') |
| 96 | } |
| 97 | |
| 98 | fn test_filelock_helpers_are_inlined_in_generated_c() { |
| 99 | v3_bin := build_v3() |
| 100 | c_source := gen_c(v3_bin, 'filelock_helpers_inline', |
| 101 | 'import os.filelock\n\nfn C.v_filelock_lock(i32, i32, i32, u64, u64) i32\nfn C.v_filelock_unlock(i32, u64, u64) i32\n\nfn main() {\n\t_ = filelock.LockMode.exclusive\n\t_ = C.v_filelock_lock(i32(-1), 1, 1, u64(0), u64(0))\n\t_ = C.v_filelock_unlock(i32(-1), u64(0), u64(0))\n}\n') |
| 102 | assert !c_source.contains('filelock_helpers.h') |
| 103 | assert c_source.contains('static inline int v_filelock_lock(') |
| 104 | assert c_source.contains('static inline int v_filelock_unlock(') |
| 105 | assert c_source.contains('#ifndef V_OS_FILELOCK_HELPERS_H') |
| 106 | assert !c_source.contains('v_filelock_status') |
| 107 | status_source := gen_c(v3_bin, 'filelock_custom_prefix_decl', |
| 108 | 'import os.filelock\n\nfn C.v_filelock_lock(i32, i32, i32, u64, u64) i32\nfn C.v_filelock_unlock(i32, u64, u64) i32\nfn C.v_filelock_status() int\n\nfn main() {\n\t_ = filelock.LockMode.exclusive\n\t_ = C.v_filelock_lock(i32(-1), 1, 1, u64(0), u64(0))\n\t_ = C.v_filelock_status()\n}\n') |
| 109 | assert status_source.contains('int v_filelock_status(') |
| 110 | out := run_good(v3_bin, 'filelock_user_names_not_helpers', |
| 111 | 'fn v_filelock_lock() int {\n\treturn 3\n}\n\nfn v_filelock_unlock() int {\n\treturn 4\n}\n\nfn main() {\n\tprintln(int_str(v_filelock_lock() + v_filelock_unlock()))\n}\n') |
| 112 | assert out == '7' |
| 113 | } |
| 114 | |
| 115 | fn test_assoc_return_runs_defers() { |
| 116 | v3_bin := build_v3() |
| 117 | out := run_good(v3_bin, 'assoc_return_runs_defers', |
| 118 | 'struct Point {\n\tx int\n\ty int\n}\n\n__global hit int\n\nfn make_point() Point {\n\tbase := Point{\n\t\tx: 1\n\t\ty: 2\n\t}\n\tdefer {\n\t\thit = 7\n\t}\n\treturn Point{\n\t\t...base\n\t\tx: 5\n\t}\n}\n\nfn main() {\n\tp := make_point()\n\tprintln(int_str(p.x))\n\tprintln(int_str(hit))\n}\n') |
| 119 | assert out == '5\n7' |
| 120 | } |
| 121 | |
| 122 | fn test_pointer_arithmetic_deref_keeps_pointer_type() { |
| 123 | v3_bin := build_v3() |
| 124 | out := run_good(v3_bin, 'pointer_arithmetic_deref', |
| 125 | 'fn main() {\n\tmut nums := [1, 2]!\n\tp := unsafe { &nums[0] }\n\tv := unsafe { *(p + 1) }\n\tprintln(int_str(v))\n}\n') |
| 126 | assert out == '2' |
| 127 | } |
| 128 | |
| 129 | fn test_array_alias_free_uses_array_builtin_inside_alias_method() { |
| 130 | v3_bin := build_v3() |
| 131 | out := run_good(v3_bin, 'array_alias_free_builtin', |
| 132 | 'import strings\n\nfn main() {\n\tmut b := strings.new_builder(4)\n\tb.write_string("ok")\n\tunsafe { b.free() }\n\tprintln("ok")\n}\n') |
| 133 | assert out == 'ok' |
| 134 | } |
| 135 | |
| 136 | fn test_channel_alias_close_method_wins_over_builtin() { |
| 137 | v3_bin := build_v3() |
| 138 | out := run_good(v3_bin, 'channel_alias_close_method_before_builtin', |
| 139 | 'type MyChan = chan int\n\nfn (c MyChan) close() int {\n\treturn 71\n}\n\nfn main() {\n\tch := MyChan(unsafe { nil })\n\tprintln(int_str(ch.close()))\n}\n') |
| 140 | assert out == '71' |
| 141 | pointer_c := gen_c(v3_bin, 'pointer_channel_close_lowers_to_runtime', |
| 142 | 'fn main() {\n\tmut ch := chan bool{cap: 1}\n\tp := &ch\n\tp.close()\n}\n') |
| 143 | assert pointer_c.contains('sync__Channel__close(*p,') |
| 144 | } |
| 145 | |
| 146 | fn test_qualified_enum_str_requires_exact_receiver() { |
| 147 | v3_bin := build_v3() |
| 148 | out := run_good_project(v3_bin, 'qualified_enum_str_exact_receiver', { |
| 149 | 'main.v': 'module main\n\nimport moda\nimport modb\n\nfn main() {\n\tprintln(moda.Color.red.str())\n\tprintln(modb.Color.blue.str())\n}\n' |
| 150 | 'moda/moda.v': 'module moda\n\npub enum Color {\n\tred\n}\n' |
| 151 | 'modb/modb.v': "module modb\n\npub enum Color {\n\tblue\n}\n\npub fn (c Color) str() string {\n\treturn 'custom'\n}\n" |
| 152 | }, 'main.v') |
| 153 | assert out == 'red\ncustom' |
| 154 | } |
| 155 | |
| 156 | fn test_array_builtin_method_fallback_keeps_return_type() { |
| 157 | v3_bin := build_v3() |
| 158 | out := run_good(v3_bin, 'array_builtin_method_fallback', |
| 159 | 'fn main() {\n\tmut nums := []int{}\n\tnums << 1\n\tnums << 2\n\tnums << 3\n\tptrs := unsafe { nums.pointers() }\n\tprintln(int_str(ptrs.len))\n}\n') |
| 160 | assert out == '3' |
| 161 | ptr_out := run_good(v3_bin, 'array_pointers_pointer_receiver', |
| 162 | 'fn main() {\n\tmut nums := []int{}\n\tnums << 1\n\tp := &nums\n\tptrs := unsafe { p.pointers() }\n\tprintln(int_str(ptrs.len))\n}\n') |
| 163 | assert ptr_out == '1' |
| 164 | reverse_out := run_good(v3_bin, 'array_reverse_pointer_receiver', |
| 165 | 'fn main() {\n\tmut nums := []int{}\n\tnums << 1\n\tnums << 2\n\tp := &nums\n\tp.reverse()\n\tprintln("ok")\n}\n') |
| 166 | assert reverse_out == 'ok' |
| 167 | exact_out := run_good(v3_bin, 'exact_array_receiver_method_before_builtin', |
| 168 | 'fn (a []int) pointers() []int {\n\treturn a\n}\n\nfn main() {\n\tnums := [9]\n\tptrs := nums.pointers()\n\tprintln(int_str(ptrs[0]))\n}\n') |
| 169 | assert exact_out == '9' |
| 170 | exact_clear_out := run_good(v3_bin, 'exact_array_clear_method_before_cgen', |
| 171 | 'fn (a []int) clear() int {\n\treturn 5\n}\n\nfn main() {\n\tmut nums := []int{}\n\tnums << 1\n\tprintln(int_str(nums.clear()))\n\tprintln(int_str(nums.len))\n}\n') |
| 172 | assert exact_clear_out == '5\n1' |
| 173 | exact_clone_out := run_good(v3_bin, 'exact_array_clone_method_before_builtin', |
| 174 | 'fn (a []int) clone() int {\n\treturn 12\n}\n\nfn main() {\n\tmut nums := []int{}\n\tnums << 1\n\tprintln(int_str(nums.clone()))\n}\n') |
| 175 | assert exact_clone_out == '12' |
| 176 | exact_reverse_out := run_good(v3_bin, 'exact_array_reverse_method_before_builtin', |
| 177 | 'fn (a []int) reverse() int {\n\treturn 13\n}\n\nfn main() {\n\tmut nums := []int{}\n\tnums << 1\n\tprintln(int_str(nums.reverse()))\n}\n') |
| 178 | assert exact_reverse_out == '13' |
| 179 | module_array_prefix_out := run_good_project(v3_bin, 'array_prefix_module_receiver_method', { |
| 180 | 'main.v': 'module main\n\nimport array_utils\n\nfn main() {\n\tprintln(array_utils.run())\n}\n' |
| 181 | 'array_utils/mod.v': 'module array_utils\n\nfn (a []int) reverse() int {\n\treturn 73\n}\n\npub fn run() string {\n\tmut nums := []int{}\n\tnums << 1\n\treturn int_str(nums.reverse())\n}\n' |
| 182 | }, 'main.v') |
| 183 | assert module_array_prefix_out == '73' |
| 184 | module_array_runtime_prefix_out := run_good_project(v3_bin, |
| 185 | 'array_runtime_prefix_module_receiver_method', { |
| 186 | 'main.v': 'module main\n\nimport array__utils\n\nfn main() {\n\tprintln(array__utils.run())\n}\n' |
| 187 | 'array__utils/mod.v': 'module array__utils\n\nfn (a []int) reverse() int {\n\treturn 83\n}\n\npub fn run() string {\n\tmut nums := []int{}\n\tnums << 1\n\treturn int_str(nums.reverse())\n}\n' |
| 188 | }, 'main.v') |
| 189 | assert module_array_runtime_prefix_out == '83' |
| 190 | module_array_move_out := run_good_project(v3_bin, 'module_array_move_receiver_method', { |
| 191 | 'main.v': 'module main\n\nimport thing\n\nfn main() {\n\tprintln(thing.run())\n}\n' |
| 192 | 'thing/mod.v': 'module thing\n\nfn (a []int) move() int {\n\treturn 91\n}\n\npub fn run() string {\n\tmut nums := []int{}\n\tnums << 1\n\treturn int_str(nums.move())\n}\n' |
| 193 | }, 'main.v') |
| 194 | assert module_array_move_out == '91' |
| 195 | exact_prepend_out := run_good(v3_bin, 'exact_array_prepend_method_before_builtin', |
| 196 | 'fn (a []int) prepend(x int) int {\n\treturn x + 1\n}\n\nfn main() {\n\tmut nums := []int{}\n\tnums << 1\n\tprintln(int_str(nums.prepend(4)))\n}\n') |
| 197 | assert exact_prepend_out == '5' |
| 198 | run_bad(v3_bin, 'exact_array_first_method_checked_before_builtin', |
| 199 | 'fn (a []int) first() string {\n\treturn "bad"\n}\n\nfn take_int(x int) {}\n\nfn main() {\n\tmut nums := []int{}\n\tnums << 1\n\ttake_int(nums.first())\n}\n', |
| 200 | 'cannot use `string` as argument 1 to `take_int`; expected `int`') |
| 201 | fixed_dynamic_out := run_good(v3_bin, 'fixed_array_dynamic_receiver_method_before_builtin', |
| 202 | 'fn (a []int) pointers() int {\n\treturn 41\n}\n\nfn main() {\n\tfixed := [3]int{}\n\tprintln(int_str(fixed.pointers()))\n}\n') |
| 203 | assert fixed_dynamic_out == '41' |
| 204 | nested_fixed_dynamic_out := run_good(v3_bin, 'nested_fixed_array_dynamic_receiver_method', |
| 205 | 'fn (a [][2]int) pointers() int {\n\treturn 82\n}\n\nfn main() {\n\tfixed := [3][2]int{}\n\tprintln(int_str(fixed.pointers()))\n}\n') |
| 206 | assert nested_fixed_dynamic_out == '82' |
| 207 | fixed_alias_shape_out := run_good(v3_bin, 'fixed_array_builtin_not_alias_method', |
| 208 | 'type F = [2]int\n\nfn (f F) pointers() int {\n\treturn 66\n}\n\nfn main() {\n\tmut fixed := [2]int{}\n\tptrs := unsafe { fixed.pointers() }\n\tprintln(int_str(ptrs.len))\n}\n') |
| 209 | assert fixed_alias_shape_out == '2' |
| 210 | plain_array_contains_out := run_good(v3_bin, 'plain_array_contains_not_alias_method', |
| 211 | 'type A = []int\n\nfn (a A) contains(x int) int {\n\treturn 0\n}\n\nfn main() {\n\tnums := [1, 2, 3]\n\tif nums.contains(2) {\n\t\tprintln("builtin")\n\t} else {\n\t\tprintln("alias")\n\t}\n\talias := A(nums)\n\tprintln(int_str(alias.contains(2)))\n}\n') |
| 212 | assert plain_array_contains_out == 'builtin\n0' |
| 213 | module_primitive_out := run_good_project(v3_bin, 'module_primitive_array_receiver_method', { |
| 214 | 'main.v': 'module main\n\nimport thing\n\nfn main() {\n\tprintln(thing.run())\n}\n' |
| 215 | 'thing/mod.v': 'module thing\n\nfn (a []int) pointers() int {\n\treturn 64\n}\n\npub fn run() string {\n\tmut nums := []int{}\n\tnums << 1\n\treturn int_str(nums.pointers())\n}\n' |
| 216 | }, 'main.v') |
| 217 | assert module_primitive_out == '64' |
| 218 | fixed_out := run_good(v3_bin, 'fixed_array_pointers_original_storage', |
| 219 | 'fn main() {\n\tmut fixed := [3]int{}\n\tfixed[0] = 1\n\tptrs := unsafe { fixed.pointers() }\n\tunsafe {\n\t\tp0 := &int(ptrs[0])\n\t\t*p0 = 9\n\t}\n\tprintln(int_str(fixed[0]))\n}\n') |
| 220 | assert fixed_out == '9' |
| 221 | fixed_expr_out := run_good(v3_bin, 'fixed_array_pointers_evaluates_receiver_once', |
| 222 | '__global calls int\n\nfn next() int {\n\tcalls = calls + 1\n\treturn 0\n}\n\nfn main() {\n\tmut rows := [1][2]int{}\n\trows[0][0] = 5\n\tptrs := unsafe { rows[next()].pointers() }\n\tunsafe {\n\t\tp0 := &int(ptrs[0])\n\t\t*p0 = 8\n\t}\n\tprintln(int_str(calls))\n\tprintln(int_str(rows[0][0]))\n}\n') |
| 223 | assert fixed_expr_out == '1\n8' |
| 224 | run_bad(v3_bin, 'fixed_array_pointers_rejects_rvalue_receiver', |
| 225 | 'fn make_fixed() [2]int {\n\treturn [7, 8]!\n}\n\nfn main() {\n\t_ := unsafe { make_fixed().pointers() }\n}\n', |
| 226 | 'fixed array receiver for `pointers` must be addressable') |
| 227 | run_bad(v3_bin, 'fixed_array_pointers_rejects_map_index_receiver', |
| 228 | 'fn main() {\n\tmut m := map[string][2]int{}\n\tm["x"] = [1, 2]!\n\t_ := unsafe { m["x"].pointers() }\n}\n', |
| 229 | 'fixed array receiver for `pointers` must be addressable') |
| 230 | fixed_len_expr_out := run_good(v3_bin, 'fixed_array_pointers_folds_len_expr', |
| 231 | 'const segs = 2\n\nfn main() {\n\tmut const_len := [segs + 1]int{}\n\tconst_ptrs := unsafe { const_len.pointers() }\n\tmut shift_len := [8 >>> 1]int{}\n\tshift_ptrs := unsafe { shift_len.pointers() }\n\tprintln(int_str(const_ptrs.len))\n\tprintln(int_str(shift_ptrs.len))\n}\n') |
| 232 | assert fixed_len_expr_out == '3\n4' |
| 233 | run_bad(v3_bin, 'fixed_array_pointers_rejects_extra_arg', |
| 234 | 'fn extra_arg() int {\n\treturn 1\n}\n\nfn main() {\n\tmut fixed := [3]int{}\n\t_ := unsafe { fixed.pointers(extra_arg()) }\n}\n', |
| 235 | 'argument count mismatch for `fixed.pointers`: expected 1, got 2') |
| 236 | } |
| 237 | |
| 238 | fn test_map_builtin_method_fallback_checks_arguments() { |
| 239 | v3_bin := build_v3() |
| 240 | run_bad(v3_bin, 'map_keys_rejects_extra_arg', |
| 241 | 'fn extra_arg() int {\n\treturn 1\n}\n\nfn main() {\n\tmut m := map[string]int{}\n\t_ := m.keys(extra_arg())\n}\n', |
| 242 | 'argument count mismatch for `m.keys`: expected 0, got 1') |
| 243 | run_bad(v3_bin, 'map_delete_rejects_bad_key_type', |
| 244 | 'fn main() {\n\tmut m := map[string]int{}\n\tm.delete(123)\n}\n', |
| 245 | 'cannot use `int` as argument 2 to `m.delete`; expected `string`') |
| 246 | run_bad(v3_bin, 'map_reserve_rejects_bad_count_type', |
| 247 | 'fn main() {\n\tmut m := map[string]int{}\n\tm.reserve("bad")\n}\n', |
| 248 | 'cannot use `string` as argument 2 to `m.reserve`; expected `u32`') |
| 249 | out := run_good(v3_bin, 'map_builtin_method_fallback', |
| 250 | 'fn main() {\n\tmut m := map[string]int{}\n\tm["abc"] = 42\n\tmut moved := m.move()\n\tprintln(int_str(m.len))\n\tmoved.clear()\n\tmoved.reserve(6)\n\tmoved.delete("x")\n\tkeys := moved.keys()\n\tvalues := moved.values()\n\tcloned := moved.clone()\n\tprintln(int_str(keys.len + values.len + cloned.len))\n}\n') |
| 251 | assert out == '0\n0' |
| 252 | pointer_out := run_good(v3_bin, 'map_move_pointer_receiver_returns_map', |
| 253 | 'fn take(m map[string]int) int {\n\treturn m.len\n}\n\nfn main() {\n\tmut m := map[string]int{}\n\tm["abc"] = 42\n\tp := &m\n\tprintln(int_str(take(p.move())))\n\tprintln(int_str(m.len))\n}\n') |
| 254 | assert pointer_out == '1\n0' |
| 255 | exact_out := run_good(v3_bin, 'exact_map_receiver_method_before_builtin', |
| 256 | 'fn (m map[string]int) keys() int {\n\treturn 77\n}\n\nfn main() {\n\tmut m := map[string]int{}\n\tm["x"] = 1\n\tn := m.keys()\n\tprintln(int_str(n))\n}\n') |
| 257 | assert exact_out == '77' |
| 258 | alias_rvalue_out := run_good(v3_bin, 'map_alias_rvalue_receiver_method_before_builtin', |
| 259 | 'type M = map[string]int\n\nfn (m M) delete(k string) int {\n\treturn 66\n}\n\nfn make_m() M {\n\tmut m := M(map[string]int{})\n\tm["x"] = 1\n\treturn m\n}\n\nfn main() {\n\tprintln(int_str(make_m().delete("x")))\n}\n') |
| 260 | assert alias_rvalue_out == '66' |
| 261 | plain_map_out := run_good(v3_bin, 'plain_map_builtin_not_alias_method', |
| 262 | 'type M = map[string]int\n\nfn (m M) keys() int {\n\treturn 66\n}\n\nfn main() {\n\tmut m := map[string]int{}\n\tm["x"] = 1\n\tkeys := m.keys()\n\tprintln(int_str(keys.len))\n}\n') |
| 263 | assert plain_map_out == '1' |
| 264 | module_map_runtime_prefix_out := run_good_project(v3_bin, |
| 265 | 'map_runtime_prefix_module_receiver_method', { |
| 266 | 'main.v': 'module main\n\nimport map__utils\n\nfn main() {\n\tprintln(map__utils.run())\n}\n' |
| 267 | 'map__utils/mod.v': 'module map__utils\n\nfn (m map[string]int) keys() int {\n\treturn 84\n}\n\npub fn run() string {\n\tmut m := map[string]int{}\n\tm["x"] = 1\n\treturn int_str(m.keys())\n}\n' |
| 268 | }, 'main.v') |
| 269 | assert module_map_runtime_prefix_out == '84' |
| 270 | module_map_out := run_good_project(v3_bin, 'map_module_receiver_method', { |
| 271 | 'main.v': 'module main\n\nimport map\n\nfn main() {\n\tprintln(map.run())\n}\n' |
| 272 | 'map/mod.v': 'module map\n\nfn (m map[string]int) keys() int {\n\treturn 85\n}\n\npub fn run() string {\n\tmut m := map[string]int{}\n\tm["x"] = 1\n\treturn int_str(m.keys())\n}\n' |
| 273 | }, 'main.v') |
| 274 | assert module_map_out == '85' |
| 275 | fixed_key_out := run_good(v3_bin, 'fixed_array_key_map_receiver_method_before_builtin', |
| 276 | 'fn (m map[[2]string]int) keys() int {\n\treturn 88\n}\n\nfn main() {\n\tmut m := map[[2]string]int{}\n\tkey := ["a", "b"]!\n\tm[key] = 1\n\tprintln(int_str(m.keys()))\n}\n') |
| 277 | assert fixed_key_out == '88' |
| 278 | nested_fixed_key_out := run_good(v3_bin, 'nested_fixed_array_key_map_receiver_method', |
| 279 | 'fn (m map[[3][2]int]int) keys() int {\n\treturn 99\n}\n\nfn main() {\n\tmut m := map[[3][2]int]int{}\n\tkey := [3][2]int{}\n\tm[key] = 1\n\tprintln(int_str(m.keys()))\n}\n') |
| 280 | assert nested_fixed_key_out == '99' |
| 281 | module_collection_out := run_good_project(v3_bin, 'module_collection_receiver_methods', { |
| 282 | 'main.v': 'module main\n\nimport thing\n\nfn main() {\n\tprintln(thing.run())\n}\n' |
| 283 | 'thing/mod.v': 'module thing\n\nstruct Foo {}\nstruct Key {}\n\nfn (m map[string]Foo) keys() int {\n\treturn 31\n}\n\nfn (a []Foo) pointers() int {\n\treturn 42\n}\n\nfn (m map[Key]int) keys() int {\n\treturn 53\n}\n\npub fn run() string {\n\tmut m := map[string]Foo{}\n\tm["x"] = Foo{}\n\titems := [Foo{}]\n\tkeyed := map[Key]int{}\n\treturn int_str(m.keys()) + "\\n" + int_str(items.pointers()) + "\\n" + int_str(keyed.keys())\n}\n' |
| 284 | }, 'main.v') |
| 285 | assert module_collection_out == '31\n42\n53' |
| 286 | } |
| 287 | |
| 288 | fn test_runtime_inits_run_before_module_init() { |
| 289 | v3_bin := build_v3() |
| 290 | out := run_good_project(v3_bin, 'runtime_inits_before_module_init', { |
| 291 | 'main.v': 'module main\n\nimport moda\n\nfn main() {\n\tprintln(int_str(moda.const_seen()))\n\tprintln(int_str(moda.global_seen()))\n}\n' |
| 292 | 'moda/moda.v': "module moda\n\nconst const_map = map[string]int{\n\t'const': 5\n}\n\n__global (\n\tglobal_map = map[string]int{\n\t\t'global': 7\n\t}\n\tseen_const int\n\tseen_global int\n)\n\nfn init() {\n\tseen_const = const_map['const']\n\tseen_global = global_map['global']\n}\n\npub fn const_seen() int {\n\treturn seen_const\n}\n\npub fn global_seen() int {\n\treturn seen_global\n}\n" |
| 293 | }, 'main.v') |
| 294 | assert out == '5\n7' |
| 295 | } |
| 296 | |
| 297 | fn test_string_index_type_is_u8() { |
| 298 | v3_bin := build_v3() |
| 299 | out := run_good(v3_bin, 'string_index_type_is_u8', |
| 300 | "fn main() {\n\ts := 'ABC'\n\tprintln(typeof(s[0]).name)\n\tprintln('\${s[2]}')\n}\n") |
| 301 | assert out == 'u8\n67' |
| 302 | } |
| 303 | |
| 304 | fn test_f32_map_and_fixed_array_stringification() { |
| 305 | v3_bin := build_v3() |
| 306 | out := run_good(v3_bin, 'f32_map_stringification', |
| 307 | "fn main() {\n\tm := {\n\t\t'a': f32(1.5)\n\t}\n\tprintln(m)\n\tfixed := [f32(1.5), f32(2.25)]!\n\tmf := {\n\t\t'x': fixed\n\t}\n\tprintln(mf)\n}\n") |
| 308 | assert out == "{'a': 1.5}\n{'x': [1.5, 2.25]}" |
| 309 | } |
| 310 | |
| 311 | fn test_u8_map_stringification_is_numeric() { |
| 312 | v3_bin := build_v3() |
| 313 | out := run_good(v3_bin, 'u8_map_stringification', |
| 314 | "fn main() {\n\tkeys := {\n\t\tu8(23): 'x'\n\t}\n\tvals := {\n\t\t'x': u8(23)\n\t}\n\tboth := {\n\t\tu8(65): u8(10)\n\t}\n\tprintln(keys)\n\tprintln(vals)\n\tprintln(both)\n}\n") |
| 315 | assert out == "{23: 'x'}\n{'x': 23}\n{65: 10}" |
| 316 | } |
| 317 | |
| 318 | fn test_map_equality_uses_semantic_value_comparison() { |
| 319 | v3_bin := build_v3() |
| 320 | out := run_good(v3_bin, 'map_semantic_value_equality', |
| 321 | "struct Item {\n\tname string\n\tparts []string\n}\n\nfn join(a string, b string) string {\n\treturn a + b\n}\n\nfn main() {\n\tleft := {\n\t\t'x': Item{\n\t\t\tname: 'hello'.clone()\n\t\t\tparts: ['ab'.clone()]\n\t\t}\n\t}\n\tright := {\n\t\t'x': Item{\n\t\t\tname: join('he', 'llo')\n\t\t\tparts: [join('a', 'b')]\n\t\t}\n\t}\n\tarr_left := {\n\t\t'y': ['cd'.clone()]\n\t}\n\tarr_right := {\n\t\t'y': [join('c', 'd')]\n\t}\n\tprintln(left == right)\n\tprintln(left != right)\n\tprintln(arr_left == arr_right)\n}\n") |
| 322 | assert out == 'true\nfalse\ntrue' |
| 323 | } |
| 324 | |
| 325 | fn test_array_equality_marks_struct_operator_used() { |
| 326 | v3_bin := build_v3() |
| 327 | out := run_good(v3_bin, 'array_eq_struct_operator_used', |
| 328 | 'struct Item {\n\tvalue int\n}\n\nfn (a Item) == (b Item) bool {\n\treturn a.value % 10 == b.value % 10\n}\n\nfn main() {\n\tleft := [Item{value: 12}]\n\tright := [Item{value: 2}]\n\tprintln(left == right)\n}\n') |
| 329 | assert out == 'true' |
| 330 | } |
| 331 | |
| 332 | fn test_zero_padded_interpolation_preserves_wide_integers() { |
| 333 | v3_bin := build_v3() |
| 334 | out := run_good(v3_bin, 'wide_zero_padded_interpolation', |
| 335 | "fn main() {\n\tbig := i64(5000000000)\n\tubig := u64(18446744073709551615)\n\tsmall := u64(42)\n\tprintln('\${big:012d}')\n\tprintln('\${ubig:020d}')\n\tprintln('\${small:08d}')\n}\n") |
| 336 | assert out == '005000000000\n18446744073709551615\n00000042' |
| 337 | } |
| 338 | |
| 339 | fn test_formatted_interpolation_rune_and_long_float() { |
| 340 | v3_bin := build_v3() |
| 341 | out := run_good(v3_bin, 'formatted_interpolation_rune_and_long_float', |
| 342 | "fn main() {\n\tr := '\${rune(0x20ac):c}'\n\tprintln(int_str(r.len))\n\tprintln(int_str(int(r[0])) + ',' + int_str(int(r[1])) + ',' + int_str(int(r[2])))\n\tlong := '\${1.0:.200f}'\n\tprintln(int_str(long.len))\n\tprintln(int_str(int(long[0])) + ',' + int_str(int(long[1])) + ',' + int_str(int(long[2])) + ',' + int_str(int(long[long.len - 1])))\n}\n") |
| 343 | assert out == '3\n226,130,172\n202\n49,46,48,48' |
| 344 | } |
| 345 | |