| 1 | import os |
| 2 | |
| 3 | const vroot = os.dir(@VEXE) |
| 4 | const test_vexe = os.quoted_path(@VEXE) |
| 5 | const autofree_labeled_continue_testdata = os.join_path(vroot, |
| 6 | 'vlib/v/tests/autofree_labeled_continue_scope_test.v') |
| 7 | |
| 8 | fn function_window(source string, signature string) string { |
| 9 | start := source.index(signature) or { return '' } |
| 10 | mut depth := 0 |
| 11 | for i := start; i < source.len; i++ { |
| 12 | if source[i] == `{` { |
| 13 | depth++ |
| 14 | } else if source[i] == `}` { |
| 15 | depth-- |
| 16 | if depth == 0 { |
| 17 | return source[start..i + 1] |
| 18 | } |
| 19 | } |
| 20 | } |
| 21 | return source[start..] |
| 22 | } |
| 23 | |
| 24 | fn test_labeled_loop_cleanup_codegen_order() { |
| 25 | cmd := '${test_vexe} -autofree -o - ${os.quoted_path(autofree_labeled_continue_testdata)}' |
| 26 | res := os.execute(cmd) |
| 27 | assert res.exit_code == 0, '${cmd}\n${res.output}' |
| 28 | generated := res.output |
| 29 | |
| 30 | fallthrough_fn := function_window(generated, |
| 31 | 'void main__labeled_fallthrough_cleanup_order(void) {') |
| 32 | inner_free_pos := fallthrough_fn.index('main__Tracked_free(&inner);') or { |
| 33 | assert false, fallthrough_fn |
| 34 | return |
| 35 | } |
| 36 | after_inner := fallthrough_fn[inner_free_pos..] |
| 37 | defer_pos := after_inner.index('main__push_event(7 + target.id - 1);') or { |
| 38 | assert false, fallthrough_fn |
| 39 | return |
| 40 | } |
| 41 | free_pos := after_inner.index('main__Tracked_free(&target);') or { |
| 42 | assert false, fallthrough_fn |
| 43 | return |
| 44 | } |
| 45 | assert defer_pos < free_pos |
| 46 | |
| 47 | labeled_break_fn := function_window(generated, 'void main__labeled_break_cleanup_order(void) {') |
| 48 | labeled_break_goto_pos := labeled_break_fn.index('goto break_outer__break;') or { |
| 49 | assert false, labeled_break_fn |
| 50 | return |
| 51 | } |
| 52 | labeled_break_inner_defer_pos := labeled_break_fn.index('main__push_event(9);') or { |
| 53 | assert false, labeled_break_fn |
| 54 | return |
| 55 | } |
| 56 | labeled_break_inner_free_pos := labeled_break_fn.index('main__Tracked_free(&inner);') or { |
| 57 | assert false, labeled_break_fn |
| 58 | return |
| 59 | } |
| 60 | labeled_break_middle_defer_pos := labeled_break_fn.index('main__push_event(8);') or { |
| 61 | assert false, labeled_break_fn |
| 62 | return |
| 63 | } |
| 64 | labeled_break_middle_free_pos := labeled_break_fn.index('main__Tracked_free(&middle);') or { |
| 65 | assert false, labeled_break_fn |
| 66 | return |
| 67 | } |
| 68 | labeled_break_target_defer_pos := labeled_break_fn.index('main__push_event(7);') or { |
| 69 | assert false, labeled_break_fn |
| 70 | return |
| 71 | } |
| 72 | labeled_break_target_free_pos := labeled_break_fn.index('main__Tracked_free(&target);') or { |
| 73 | assert false, labeled_break_fn |
| 74 | return |
| 75 | } |
| 76 | assert labeled_break_inner_defer_pos < labeled_break_inner_free_pos |
| 77 | assert labeled_break_inner_free_pos < labeled_break_middle_defer_pos |
| 78 | assert labeled_break_middle_defer_pos < labeled_break_middle_free_pos |
| 79 | assert labeled_break_middle_free_pos < labeled_break_target_defer_pos |
| 80 | assert labeled_break_target_defer_pos < labeled_break_target_free_pos |
| 81 | assert labeled_break_target_free_pos < labeled_break_goto_pos |
| 82 | |
| 83 | break_fn := function_window(generated, 'void main__for_c_labeled_break(void) {') |
| 84 | break_label_pos := break_fn.index('outer__break: {}') or { |
| 85 | assert false, break_fn |
| 86 | return |
| 87 | } |
| 88 | init_free_pos := break_fn.index('main__Tracked_free(&init);') or { |
| 89 | assert false, break_fn |
| 90 | return |
| 91 | } |
| 92 | assert break_label_pos < init_free_pos |
| 93 | |
| 94 | nested_continue_fn := function_window(generated, |
| 95 | 'void main__for_c_nested_continue_outer_init_cleanup(void) {') |
| 96 | nested_continue_body_free_pos := nested_continue_fn.index('main__Tracked_free(&body);') or { |
| 97 | assert false, nested_continue_fn |
| 98 | return |
| 99 | } |
| 100 | nested_continue_inner_free_pos := nested_continue_fn.index('main__Tracked_free(&inner_init);') or { |
| 101 | assert false, nested_continue_fn |
| 102 | return |
| 103 | } |
| 104 | nested_continue_goto_pos := nested_continue_fn.index('goto outer__continue_entry;') or { |
| 105 | assert false, nested_continue_fn |
| 106 | return |
| 107 | } |
| 108 | nested_continue_after_goto := nested_continue_fn[nested_continue_goto_pos..] |
| 109 | nested_continue_before_goto := nested_continue_fn[..nested_continue_goto_pos] |
| 110 | nested_continue_outer_free_after_goto_pos := nested_continue_after_goto.index('main__Tracked_free(&outer_init);') or { |
| 111 | assert false, nested_continue_fn |
| 112 | return |
| 113 | } |
| 114 | assert nested_continue_body_free_pos < nested_continue_inner_free_pos |
| 115 | assert nested_continue_inner_free_pos < nested_continue_goto_pos |
| 116 | assert !nested_continue_before_goto.contains('main__Tracked_free(&outer_init);') |
| 117 | assert nested_continue_outer_free_after_goto_pos > 0 |
| 118 | |
| 119 | nested_break_fn := function_window(generated, |
| 120 | 'void main__for_c_nested_break_outer_init_cleanup(void) {') |
| 121 | nested_break_body_free_pos := nested_break_fn.index('main__Tracked_free(&body);') or { |
| 122 | assert false, nested_break_fn |
| 123 | return |
| 124 | } |
| 125 | nested_break_inner_free_pos := nested_break_fn.index('main__Tracked_free(&inner_init);') or { |
| 126 | assert false, nested_break_fn |
| 127 | return |
| 128 | } |
| 129 | nested_break_goto_pos := nested_break_fn.index('goto break_outer__break;') or { |
| 130 | assert false, nested_break_fn |
| 131 | return |
| 132 | } |
| 133 | nested_break_after_goto := nested_break_fn[nested_break_goto_pos..] |
| 134 | nested_break_before_goto := nested_break_fn[..nested_break_goto_pos] |
| 135 | nested_break_outer_free_after_goto_pos := nested_break_after_goto.index('main__Tracked_free(&outer_init);') or { |
| 136 | assert false, nested_break_fn |
| 137 | return |
| 138 | } |
| 139 | assert nested_break_body_free_pos < nested_break_inner_free_pos |
| 140 | assert nested_break_inner_free_pos < nested_break_goto_pos |
| 141 | assert !nested_break_before_goto.contains('main__Tracked_free(&outer_init);') |
| 142 | assert nested_break_outer_free_after_goto_pos > 0 |
| 143 | |
| 144 | return_second_fn := function_window(generated, |
| 145 | 'main__Tracked main__for_c_multi_init_return_second(void) {') |
| 146 | return_pos := return_second_fn.index('return ') or { |
| 147 | assert false, return_second_fn |
| 148 | return |
| 149 | } |
| 150 | body_free_pos := return_second_fn.index('main__Tracked_free(&body);') or { |
| 151 | assert false, return_second_fn |
| 152 | return |
| 153 | } |
| 154 | first_free_pos := return_second_fn.index('main__Tracked_free(&first);') or { |
| 155 | assert false, return_second_fn |
| 156 | return |
| 157 | } |
| 158 | assert body_free_pos < return_pos |
| 159 | assert first_free_pos < return_pos |
| 160 | if second_free_pos := return_second_fn.index('main__Tracked_free(&second);') { |
| 161 | assert return_pos < second_free_pos |
| 162 | } |
| 163 | |
| 164 | return_field_fn := function_window(generated, |
| 165 | 'string main__for_c_return_init_field_string(void) {') |
| 166 | return_field_return_pos := return_field_fn.index('return ') or { |
| 167 | assert false, return_field_fn |
| 168 | return |
| 169 | } |
| 170 | return_field_return_path := return_field_fn[..return_field_return_pos] |
| 171 | assert !return_field_return_path.contains('main__Holder_free(&init);') |
| 172 | assert !return_field_return_path.contains('builtin__string_free(&(init.label));') |
| 173 | assert !return_field_fn.contains('builtin__string_free(&(init.other));') |
| 174 | |
| 175 | return_alias_field_fn := function_window(generated, |
| 176 | 'main__Label main__for_c_return_init_field_alias_string(void) {') |
| 177 | return_alias_field_return_pos := return_alias_field_fn.index('return ') or { |
| 178 | assert false, return_alias_field_fn |
| 179 | return |
| 180 | } |
| 181 | return_alias_field_return_path := return_alias_field_fn[..return_alias_field_return_pos] |
| 182 | assert !return_alias_field_return_path.contains('main__AliasHolder_free(&init);') |
| 183 | assert !return_alias_field_return_path.contains('builtin__string_free(&(init.label));') |
| 184 | assert !return_alias_field_fn.contains('builtin__string_free(&(init.other));') |
| 185 | |
| 186 | return_id_fn := function_window(generated, 'int main__for_c_return_init_field_id(void) {') |
| 187 | return_id_holder_free_pos := return_id_fn.index('main__Holder_free(&init);') or { |
| 188 | assert false, return_id_fn |
| 189 | return |
| 190 | } |
| 191 | return_id_return_pos := return_id_fn.index('return ') or { |
| 192 | assert false, return_id_fn |
| 193 | return |
| 194 | } |
| 195 | assert return_id_holder_free_pos < return_id_return_pos |
| 196 | |
| 197 | branch_body_fn := function_window(generated, |
| 198 | 'main__Tracked main__for_c_branch_return_body_after_init_path(bool cond) {') |
| 199 | body_decl_pos := branch_body_fn.index('main__Tracked body = main__tracked(5);') or { |
| 200 | assert false, branch_body_fn |
| 201 | return |
| 202 | } |
| 203 | body_return_path := branch_body_fn[body_decl_pos..] |
| 204 | branch_body_return_pos := body_return_path.index('return ') or { |
| 205 | assert false, branch_body_fn |
| 206 | return |
| 207 | } |
| 208 | branch_init_free_pos := body_return_path.index('main__Tracked_free(&init);') or { |
| 209 | assert false, branch_body_fn |
| 210 | return |
| 211 | } |
| 212 | assert branch_init_free_pos < branch_body_return_pos |
| 213 | if branch_body_free_pos := body_return_path.index('main__Tracked_free(&body);') { |
| 214 | assert branch_body_return_pos < branch_body_free_pos |
| 215 | } |
| 216 | } |
| 217 | |