| 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 v3_src = os.join_path(v3_dir, 'v3.v') |
| 8 | |
| 9 | fn test_optional_struct_selector_assignment_mutates_payload_storage() { |
| 10 | v3_bin := os.join_path(os.temp_dir(), 'v3_optional_struct_lvalue_test') |
| 11 | build := |
| 12 | os.execute('${vexe} -gc none -path "${vlib_dir}|@vlib|@vmodules" -o ${v3_bin} ${v3_src}') |
| 13 | assert build.exit_code == 0, build.output |
| 14 | |
| 15 | src := os.join_path(os.temp_dir(), 'v3_optional_struct_lvalue_input.v') |
| 16 | os.write_file(src, "struct AFoo { |
| 17 | mut: |
| 18 | name string |
| 19 | } |
| 20 | |
| 21 | struct Inner { |
| 22 | mut: |
| 23 | name string |
| 24 | } |
| 25 | |
| 26 | struct Holder { |
| 27 | mut: |
| 28 | opt ?Inner |
| 29 | } |
| 30 | |
| 31 | struct ResultHolder { |
| 32 | mut: |
| 33 | res !Inner |
| 34 | } |
| 35 | |
| 36 | struct Outer { |
| 37 | mut: |
| 38 | inner Inner |
| 39 | } |
| 40 | |
| 41 | fn (mut f AFoo) opt_string(arr ?[]int) ?string { |
| 42 | return arr?.len.str() |
| 43 | } |
| 44 | |
| 45 | fn guarded_failure() ?AFoo { |
| 46 | mut m := ?AFoo(none) |
| 47 | m?.name = 'bad' |
| 48 | return m |
| 49 | } |
| 50 | |
| 51 | fn fail_inner() !Inner { |
| 52 | return error('boom') |
| 53 | } |
| 54 | |
| 55 | fn mutate_result_source(mut h ResultHolder) ! { |
| 56 | h.res!.name = 'result' |
| 57 | } |
| 58 | |
| 59 | fn mutate_explicit_or_source(mut holder Holder) { |
| 60 | (holder.opt or { |
| 61 | assert false |
| 62 | return |
| 63 | }).name = 'ok' |
| 64 | } |
| 65 | |
| 66 | fn mutate_explicit_or_none() { |
| 67 | mut holder := Holder{ |
| 68 | opt: none |
| 69 | } |
| 70 | (holder.opt or { |
| 71 | assert err.msg() == '' |
| 72 | println('none') |
| 73 | return |
| 74 | }).name = 'bad' |
| 75 | assert false |
| 76 | } |
| 77 | |
| 78 | fn mutate_result_explicit_or_ok(mut h ResultHolder) { |
| 79 | (h.res or { |
| 80 | assert false |
| 81 | return |
| 82 | }).name = 'explicit-result' |
| 83 | } |
| 84 | |
| 85 | fn mutate_result_explicit_or_err(mut h ResultHolder) { |
| 86 | (h.res or { |
| 87 | assert err.msg() == 'boom' |
| 88 | println('boom') |
| 89 | return |
| 90 | }).name = 'bad' |
| 91 | assert false |
| 92 | } |
| 93 | |
| 94 | fn main() { |
| 95 | mut m := ?AFoo(AFoo{}) |
| 96 | assert m?.opt_string([1, 2, 3])? == '3' |
| 97 | m?.name = 'foo' |
| 98 | assert m?.name == 'foo' |
| 99 | mut holder := Holder{ |
| 100 | opt: Inner{} |
| 101 | } |
| 102 | holder.opt?.name = 'holder' |
| 103 | assert holder.opt?.name == 'holder' |
| 104 | mut outer := ?Outer(Outer{}) |
| 105 | outer?.inner.name = 'deep' |
| 106 | assert outer?.inner.name == 'deep' |
| 107 | mut result_ok := ResultHolder{ |
| 108 | res: Inner{} |
| 109 | } |
| 110 | mutate_result_source(mut result_ok)! |
| 111 | assert result_ok.res!.name == 'result' |
| 112 | mutate_result_explicit_or_ok(mut result_ok) |
| 113 | assert result_ok.res!.name == 'explicit-result' |
| 114 | mut result_bad := ResultHolder{ |
| 115 | res: fail_inner() |
| 116 | } |
| 117 | mut saw_result_err := false |
| 118 | mutate_result_source(mut result_bad) or { |
| 119 | assert err.msg() == 'boom' |
| 120 | saw_result_err = true |
| 121 | } |
| 122 | assert saw_result_err |
| 123 | mutate_result_explicit_or_err(mut result_bad) |
| 124 | mutate_explicit_or_source(mut holder) |
| 125 | assert holder.opt?.name == 'ok' |
| 126 | mutate_explicit_or_none() |
| 127 | guarded_failure() or { |
| 128 | assert err.msg() == '' |
| 129 | println('ok') |
| 130 | return |
| 131 | } |
| 132 | assert false |
| 133 | } |
| 134 | ") or { |
| 135 | panic(err) |
| 136 | } |
| 137 | |
| 138 | bin := os.join_path(os.temp_dir(), 'v3_optional_struct_lvalue_input') |
| 139 | compile := os.execute('${v3_bin} ${src} -b c -o ${bin}') |
| 140 | assert compile.exit_code == 0, compile.output |
| 141 | assert !compile.output.contains('C compilation failed'), compile.output |
| 142 | |
| 143 | c_code := os.read_file(bin + '.c') or { panic(err) } |
| 144 | assert c_code.contains('m.value.name ='), c_code |
| 145 | assert c_code.contains('holder.opt.value.name ='), c_code |
| 146 | assert c_code.contains('outer.value.inner.name ='), c_code |
| 147 | assert c_code.contains('holder->opt.value.name =') || c_code.contains('holder.opt.value.name ='), c_code |
| 148 | |
| 149 | assert c_code.contains('if (!m.ok)'), c_code |
| 150 | assert c_code.contains('if (!holder.opt.ok)'), c_code |
| 151 | assert c_code.contains('if (!outer.ok)'), c_code |
| 152 | assert c_code.contains('.err = h->res.err') || c_code.contains('.err = h.res.err'), c_code |
| 153 | assert c_code.contains('IError err = holder.opt.err') |
| 154 | || c_code.contains('IError err = holder->opt.err'), c_code |
| 155 | assert c_code.contains('IError err = h->res.err') || c_code.contains('IError err = h.res.err'), c_code |
| 156 | |
| 157 | mutate_result_start := c_code.index('\nOptional mutate_result_source(ResultHolder* h) {') or { |
| 158 | -1 |
| 159 | } |
| 160 | assert mutate_result_start >= 0, c_code |
| 161 | mutate_result_tail := c_code[mutate_result_start..] |
| 162 | mutate_result_guard := mutate_result_tail[..mutate_result_tail.index('h->res.value.name') or { |
| 163 | mutate_result_tail.len |
| 164 | }] |
| 165 | assert !mutate_result_guard.contains('return (Optional){.ok = false};'), mutate_result_guard |
| 166 | for line in c_code.split_into_lines() { |
| 167 | assert !(line.contains('__or_val_') && line.contains('.name =')), line |
| 168 | assert !(line.contains('__or_val_') && line.contains('.inner.name =')), line |
| 169 | } |
| 170 | |
| 171 | run := os.execute(bin) |
| 172 | assert run.exit_code == 0, run.output |
| 173 | assert run.output.trim_space() == 'boom\nnone\nok' |
| 174 | } |
| 175 | |