| 1 | import os |
| 2 | |
| 3 | const vexe = @VEXE |
| 4 | |
| 5 | fn test_skip_unused_prunes_unused_generic_fn_instantiations() { |
| 6 | tmp_dir := os.join_path(os.vtmp_dir(), 'v_issue_26019') |
| 7 | os.mkdir_all(tmp_dir) or { panic(err) } |
| 8 | defer { |
| 9 | os.rmdir_all(tmp_dir) or {} |
| 10 | } |
| 11 | source_path := os.join_path(tmp_dir, 'issue_26019.v') |
| 12 | source := [ |
| 13 | 'module main', |
| 14 | '', |
| 15 | 'fn new[T]() map[u8]T {', |
| 16 | '\tx := map[u8]T{}', |
| 17 | '\treturn x', |
| 18 | '}', |
| 19 | '', |
| 20 | 'fn not_used_fn() map[u8]int {', |
| 21 | '\tx := new[int]()', |
| 22 | '\treturn x', |
| 23 | '}', |
| 24 | '', |
| 25 | 'fn main() {', |
| 26 | '\tx := new[u32]()', |
| 27 | '\tdump(x)', |
| 28 | '}', |
| 29 | ].join('\n') |
| 30 | os.write_file(source_path, source) or { panic(err) } |
| 31 | res := os.execute('${os.quoted_path(vexe)} -o - ${os.quoted_path(source_path)}') |
| 32 | if res.exit_code != 0 { |
| 33 | panic(res.output) |
| 34 | } |
| 35 | assert res.output.contains('VV_LOC Map_u8_u32 main__new_T_u32(void)') |
| 36 | assert !res.output.contains('VV_LOC Map_u8_int main__new_T_int(void)') |
| 37 | } |
| 38 | |
| 39 | fn test_skip_unused_keeps_generic_offsetof_struct_instantiations() { |
| 40 | tmp_dir := os.join_path(os.vtmp_dir(), 'v_issue_27283_generic_offsetof') |
| 41 | os.mkdir_all(tmp_dir) or { panic(err) } |
| 42 | defer { |
| 43 | os.rmdir_all(tmp_dir) or {} |
| 44 | } |
| 45 | source_path := os.join_path(tmp_dir, 'issue_27283_generic_offsetof.v') |
| 46 | binary_path := os.join_path(tmp_dir, 'issue_27283_generic_offsetof') |
| 47 | source := [ |
| 48 | 'module main', |
| 49 | '', |
| 50 | 'struct Box[T] {', |
| 51 | '\ta int', |
| 52 | '\tb T', |
| 53 | '}', |
| 54 | '', |
| 55 | 'fn off[T]() u32 {', |
| 56 | '\treturn __offsetof(Box[T], b)', |
| 57 | '}', |
| 58 | '', |
| 59 | 'fn main() {', |
| 60 | '\tprintln(off[int]())', |
| 61 | '\tprintln(off[string]())', |
| 62 | '}', |
| 63 | ].join('\n') |
| 64 | os.write_file(source_path, source) or { panic(err) } |
| 65 | res := |
| 66 | os.execute('${os.quoted_path(vexe)} -skip-unused -o ${os.quoted_path(binary_path)} ${os.quoted_path(source_path)}') |
| 67 | if res.exit_code != 0 { |
| 68 | panic(res.output) |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | fn test_skip_unused_does_not_emit_impl_methods_for_interface_extensions() { |
| 73 | tmp_dir := os.join_path(os.vtmp_dir(), 'v_skip_unused_interface_extension_collision') |
| 74 | os.mkdir_all(tmp_dir) or { panic(err) } |
| 75 | defer { |
| 76 | os.rmdir_all(tmp_dir) or {} |
| 77 | } |
| 78 | source_path := os.join_path(tmp_dir, 'interface_extension_collision.v') |
| 79 | source := [ |
| 80 | 'module main', |
| 81 | '', |
| 82 | 'import crypto.internal.subtle', |
| 83 | '', |
| 84 | 'interface Elem {}', |
| 85 | '', |
| 86 | 'struct Thing {}', |
| 87 | '', |
| 88 | 'struct Holder {', |
| 89 | '\titems []Elem', |
| 90 | '}', |
| 91 | '', |
| 92 | 'fn (el Elem) equal(_ Elem) bool {', |
| 93 | '\treturn true', |
| 94 | '}', |
| 95 | '', |
| 96 | 'fn (t Thing) equal(_ Thing) bool {', |
| 97 | '\treturn subtle.constant_time_compare([u8(1)], [u8(1)]) == 1', |
| 98 | '}', |
| 99 | '', |
| 100 | 'fn (h Holder) ok() bool {', |
| 101 | '\tfor i, item in h.items {', |
| 102 | '\t\tfor j, obj in h.items {', |
| 103 | '\t\t\tif i == j {', |
| 104 | '\t\t\t\treturn item.equal(obj)', |
| 105 | '\t\t\t}', |
| 106 | '\t\t}', |
| 107 | '\t}', |
| 108 | '\treturn false', |
| 109 | '}', |
| 110 | '', |
| 111 | 'fn main() {', |
| 112 | '\th := Holder{items: [Elem(Thing{}), Elem(Thing{})]}', |
| 113 | '\tprintln(h.ok())', |
| 114 | '}', |
| 115 | ].join('\n') |
| 116 | os.write_file(source_path, source) or { panic(err) } |
| 117 | res := os.execute('${os.quoted_path(vexe)} -o - ${os.quoted_path(source_path)}') |
| 118 | if res.exit_code != 0 { |
| 119 | panic(res.output) |
| 120 | } |
| 121 | assert res.output.contains('VV_LOC bool main__Elem_equal(') |
| 122 | assert !res.output.contains('main__Thing_equal(') |
| 123 | } |
| 124 | |
| 125 | fn test_skip_unused_keeps_json2_embedded_struct_decode_helpers() { |
| 126 | tmp_dir := os.join_path(os.vtmp_dir(), 'v_issue_26928') |
| 127 | os.mkdir_all(tmp_dir) or { panic(err) } |
| 128 | defer { |
| 129 | os.rmdir_all(tmp_dir) or {} |
| 130 | } |
| 131 | source_path := os.join_path(tmp_dir, 'issue_26928.v') |
| 132 | source := [ |
| 133 | 'module main', |
| 134 | '', |
| 135 | 'import time', |
| 136 | 'import x.json2', |
| 137 | '', |
| 138 | 'struct Meta {', |
| 139 | '\tcreated_at ?time.Time', |
| 140 | '}', |
| 141 | '', |
| 142 | 'struct Req {', |
| 143 | '\tMeta', |
| 144 | '\tname string', |
| 145 | '}', |
| 146 | '', |
| 147 | 'fn main() {', |
| 148 | '\t_ := json2.decode[Req](\'{"name":"x"}\') or { panic(err) }', |
| 149 | '}', |
| 150 | ].join('\n') |
| 151 | os.write_file(source_path, source) or { panic(err) } |
| 152 | res := os.execute('${os.quoted_path(vexe)} -w -o - ${os.quoted_path(source_path)}') |
| 153 | if res.exit_code != 0 { |
| 154 | panic(res.output) |
| 155 | } |
| 156 | assert res.output.contains('x__json2__decode_struct_key_T_main__Req') |
| 157 | assert res.output.contains('x__json2__check_required_struct_fields_T_main__Req') |
| 158 | // the `?time.Time` payload of the embedded `Meta` is decoded through a generic |
| 159 | // instantiation reachable only via comptime `$for field` codegen; skip-unused |
| 160 | // must keep it. Assert the payload decoder itself rather than a specific json2 |
| 161 | // helper name, so the test does not break when those helpers are refactored. |
| 162 | assert res.output.contains('x__json2__Decoder_decode_value_T_time__Time') |
| 163 | } |
| 164 | |
| 165 | fn test_skip_unused_marks_dependencies_inside_generic_anon_fns() { |
| 166 | tmp_dir := os.join_path(os.vtmp_dir(), 'v_generic_anon_fn_dependencies') |
| 167 | os.mkdir_all(tmp_dir) or { panic(err) } |
| 168 | defer { |
| 169 | os.rmdir_all(tmp_dir) or {} |
| 170 | } |
| 171 | source_path := os.join_path(tmp_dir, 'generic_anon_fn_dependencies.v') |
| 172 | binary_path := os.join_path(tmp_dir, 'generic_anon_fn_dependencies') |
| 173 | source := [ |
| 174 | 'module main', |
| 175 | '', |
| 176 | 'struct Holder[T] {', |
| 177 | '\tdata []T', |
| 178 | '}', |
| 179 | '', |
| 180 | 'fn (h &Holder[T]) nmap[T](others []&Holder[T], f fn ([]T) T) T {', |
| 181 | '\treturn f([h.data[0], others[0].data[0]])', |
| 182 | '}', |
| 183 | '', |
| 184 | 'fn (a &Holder[T]) subtract[T](b &Holder[T]) T {', |
| 185 | '\treturn a.nmap([b], fn [T] (xs []T) T {', |
| 186 | '\t\tx := xs[0]', |
| 187 | '\t\ty := xs[1]', |
| 188 | '\t\t$if T is string {', |
| 189 | "\t\t\treturn x.replace(y, '')", |
| 190 | '\t\t} $else {', |
| 191 | '\t\t\treturn x - y', |
| 192 | '\t\t}', |
| 193 | '\t})', |
| 194 | '}', |
| 195 | '', |
| 196 | 'fn unused_string() {', |
| 197 | "\ta := &Holder[string]{data: ['abc']}", |
| 198 | "\tb := &Holder[string]{data: ['b']}", |
| 199 | '\tprintln(a.subtract(b))', |
| 200 | '}', |
| 201 | '', |
| 202 | 'fn main() {', |
| 203 | '\ta := &Holder[int]{data: [1]}', |
| 204 | '\tb := &Holder[int]{data: [2]}', |
| 205 | '\tprintln(a.subtract(b))', |
| 206 | '}', |
| 207 | ].join('\n') |
| 208 | os.write_file(source_path, source) or { panic(err) } |
| 209 | res := |
| 210 | os.execute('${os.quoted_path(vexe)} -d no_backtrace -o ${os.quoted_path(binary_path)} ${os.quoted_path(source_path)}') |
| 211 | if res.exit_code != 0 { |
| 212 | panic(res.output) |
| 213 | } |
| 214 | } |
| 215 | |
| 216 | fn test_skip_unused_keeps_generic_next_for_for_in_when_direct_calls_use_other_types() { |
| 217 | // Regression for https://github.com/vlang/v/issues/27147 . |
| 218 | // A `for in` over a generic iterator instantiation (e.g. `Range[big.Integer]`) |
| 219 | // must emit the matching `next` instantiation, even when other code calls |
| 220 | // methods on a different instantiation of the same generic struct directly |
| 221 | // (e.g. `iter.next()` on a `Range[int]`). |
| 222 | tmp_dir := os.join_path(os.vtmp_dir(), 'v_issue_27147') |
| 223 | os.mkdir_all(tmp_dir) or { panic(err) } |
| 224 | defer { |
| 225 | os.rmdir_all(tmp_dir) or {} |
| 226 | } |
| 227 | source_path := os.join_path(tmp_dir, 'issue_27147.v') |
| 228 | binary_path := os.join_path(tmp_dir, 'issue_27147') |
| 229 | source := [ |
| 230 | 'module main', |
| 231 | '', |
| 232 | 'import math.big', |
| 233 | '', |
| 234 | 'struct Range[T] {', |
| 235 | '\tstart T', |
| 236 | '\tend T', |
| 237 | '\tstep T', |
| 238 | 'mut:', |
| 239 | '\tcur T', |
| 240 | '}', |
| 241 | '', |
| 242 | 'pub fn (mut r Range[T]) next() ?T {', |
| 243 | '\tif r.cur > r.end {', |
| 244 | '\t\treturn none', |
| 245 | '\t}', |
| 246 | '\tdefer { r.cur += r.step }', |
| 247 | '\treturn r.cur', |
| 248 | '}', |
| 249 | '', |
| 250 | 'pub fn (mut r Range[T]) reset() {', |
| 251 | '\tr.cur = r.start', |
| 252 | '}', |
| 253 | '', |
| 254 | 'pub fn range[T](start T, end T, step T) Range[T] {', |
| 255 | '\treturn Range[T]{start: start, end: end, step: step, cur: start}', |
| 256 | '}', |
| 257 | '', |
| 258 | 'fn main() {', |
| 259 | '\tmut iter := range(0, 5, 1)', |
| 260 | '\titer.reset()', |
| 261 | '\titer.next()', |
| 262 | '\tend := big.integer_from_int(3)', |
| 263 | '\tfor i in range[big.Integer](big.zero_int, end, big.one_int) {', |
| 264 | '\t\tprintln(i)', |
| 265 | '\t}', |
| 266 | '}', |
| 267 | ].join('\n') |
| 268 | os.write_file(source_path, source) or { panic(err) } |
| 269 | res := |
| 270 | os.execute('${os.quoted_path(vexe)} -o ${os.quoted_path(binary_path)} ${os.quoted_path(source_path)}') |
| 271 | if res.exit_code != 0 { |
| 272 | panic(res.output) |
| 273 | } |
| 274 | } |
| 275 | |