| author | |
| committer | |
| log | d4f668b2416aed2cae1ae498ede418e6d5a5bce8 |
| tree | eb2f9308d19995bd0d2f9877ad32e8ffb06ce623 |
| parent | 3e126102b7313026ff94f314053c3fa3f791ce75 |
| parent | a77d89afe3766b69476488601bf82744fc92d334 |
| signature |
AstGen: avoid access to capture defined in an inner scope from a continue expression10 files changed, 42 insertions(+), 55 deletions(-)
src/AstGen.zig+37-33| ... | ... | @@ -5910,8 +5910,8 @@ fn whileExpr( |
| 5910 | 5910 | defer loop_scope.unstack(); |
| 5911 | 5911 | defer loop_scope.labeled_breaks.deinit(astgen.gpa); |
| 5912 | 5912 | |
| 5913 | var continue_scope = parent_gz.makeSubBlock(&loop_scope.base); | |
| 5914 | defer continue_scope.unstack(); | |
| 5913 | var cond_scope = parent_gz.makeSubBlock(&loop_scope.base); | |
| 5914 | defer cond_scope.unstack(); | |
| 5915 | 5915 | |
| 5916 | 5916 | const payload_is_ref = if (while_full.payload_token) |payload_token| |
| 5917 | 5917 | token_tags[payload_token] == .asterisk |
| ... | ... | @@ -5925,22 +5925,22 @@ fn whileExpr( |
| 5925 | 5925 | } = c: { |
| 5926 | 5926 | if (while_full.error_token) |_| { |
| 5927 | 5927 | const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none }; |
| 5928 | const err_union = try expr(&continue_scope, &continue_scope.base, cond_ri, while_full.ast.cond_expr); | |
| 5928 | const err_union = try expr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr); | |
| 5929 | 5929 | const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err; |
| 5930 | 5930 | break :c .{ |
| 5931 | 5931 | .inst = err_union, |
| 5932 | .bool_bit = try continue_scope.addUnNode(tag, err_union, while_full.ast.then_expr), | |
| 5932 | .bool_bit = try cond_scope.addUnNode(tag, err_union, while_full.ast.then_expr), | |
| 5933 | 5933 | }; |
| 5934 | 5934 | } else if (while_full.payload_token) |_| { |
| 5935 | 5935 | const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none }; |
| 5936 | const optional = try expr(&continue_scope, &continue_scope.base, cond_ri, while_full.ast.cond_expr); | |
| 5936 | const optional = try expr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr); | |
| 5937 | 5937 | const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null; |
| 5938 | 5938 | break :c .{ |
| 5939 | 5939 | .inst = optional, |
| 5940 | .bool_bit = try continue_scope.addUnNode(tag, optional, while_full.ast.then_expr), | |
| 5940 | .bool_bit = try cond_scope.addUnNode(tag, optional, while_full.ast.then_expr), | |
| 5941 | 5941 | }; |
| 5942 | 5942 | } else { |
| 5943 | const cond = try expr(&continue_scope, &continue_scope.base, bool_ri, while_full.ast.cond_expr); | |
| 5943 | const cond = try expr(&cond_scope, &cond_scope.base, bool_ri, while_full.ast.cond_expr); | |
| 5944 | 5944 | break :c .{ |
| 5945 | 5945 | .inst = cond, |
| 5946 | 5946 | .bool_bit = cond, |
| ... | ... | @@ -5949,16 +5949,16 @@ fn whileExpr( |
| 5949 | 5949 | }; |
| 5950 | 5950 | |
| 5951 | 5951 | const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr; |
| 5952 | const condbr = try continue_scope.addCondBr(condbr_tag, node); | |
| 5952 | const condbr = try cond_scope.addCondBr(condbr_tag, node); | |
| 5953 | 5953 | const block_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .block; |
| 5954 | 5954 | const cond_block = try loop_scope.makeBlockInst(block_tag, node); |
| 5955 | try continue_scope.setBlockBody(cond_block); | |
| 5956 | // continue_scope unstacked now, can add new instructions to loop_scope | |
| 5955 | try cond_scope.setBlockBody(cond_block); | |
| 5956 | // cond_scope unstacked now, can add new instructions to loop_scope | |
| 5957 | 5957 | try loop_scope.instructions.append(astgen.gpa, cond_block); |
| 5958 | 5958 | |
| 5959 | 5959 | // make scope now but don't stack on parent_gz until loop_scope |
| 5960 | 5960 | // gets unstacked after cont_expr is emitted and added below |
| 5961 | var then_scope = parent_gz.makeSubBlock(&continue_scope.base); | |
| 5961 | var then_scope = parent_gz.makeSubBlock(&cond_scope.base); | |
| 5962 | 5962 | then_scope.instructions_top = GenZir.unstacked_top; |
| 5963 | 5963 | defer then_scope.unstack(); |
| 5964 | 5964 | |
| ... | ... | @@ -6026,24 +6026,17 @@ fn whileExpr( |
| 6026 | 6026 | } |
| 6027 | 6027 | }; |
| 6028 | 6028 | |
| 6029 | // This code could be improved to avoid emitting the continue expr when there | |
| 6030 | // are no jumps to it. This happens when the last statement of a while body is noreturn | |
| 6031 | // and there are no `continue` statements. | |
| 6032 | // Tracking issue: https://github.com/ziglang/zig/issues/9185 | |
| 6033 | try then_scope.addDbgBlockBegin(); | |
| 6034 | if (dbg_var_name) |some| { | |
| 6035 | try then_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst); | |
| 6036 | } | |
| 6037 | if (while_full.ast.cont_expr != 0) { | |
| 6038 | _ = try unusedResultExpr(&loop_scope, then_sub_scope, while_full.ast.cont_expr); | |
| 6039 | } | |
| 6040 | try then_scope.addDbgBlockEnd(); | |
| 6029 | var continue_scope = parent_gz.makeSubBlock(then_sub_scope); | |
| 6030 | continue_scope.instructions_top = GenZir.unstacked_top; | |
| 6031 | defer continue_scope.unstack(); | |
| 6032 | const continue_block = try then_scope.makeBlockInst(block_tag, node); | |
| 6033 | ||
| 6041 | 6034 | const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat; |
| 6042 | 6035 | _ = try loop_scope.addNode(repeat_tag, node); |
| 6043 | 6036 | |
| 6044 | 6037 | try loop_scope.setBlockBody(loop_block); |
| 6045 | 6038 | loop_scope.break_block = loop_block; |
| 6046 | loop_scope.continue_block = cond_block; | |
| 6039 | loop_scope.continue_block = continue_block; | |
| 6047 | 6040 | if (while_full.label_token) |label_token| { |
| 6048 | 6041 | loop_scope.label = @as(?GenZir.Label, GenZir.Label{ |
| 6049 | 6042 | .token = label_token, |
| ... | ... | @@ -6054,18 +6047,30 @@ fn whileExpr( |
| 6054 | 6047 | // done adding instructions to loop_scope, can now stack then_scope |
| 6055 | 6048 | then_scope.instructions_top = then_scope.instructions.items.len; |
| 6056 | 6049 | |
| 6057 | if (payload_inst != 0) try then_scope.instructions.append(astgen.gpa, payload_inst); | |
| 6058 | 6050 | try then_scope.addDbgBlockBegin(); |
| 6059 | if (dbg_var_name) |some| { | |
| 6060 | try then_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst); | |
| 6051 | if (payload_inst != 0) try then_scope.instructions.append(astgen.gpa, payload_inst); | |
| 6052 | if (dbg_var_name) |name| try then_scope.addDbgVar(.dbg_var_val, name, dbg_var_inst); | |
| 6053 | try then_scope.instructions.append(astgen.gpa, continue_block); | |
| 6054 | // This code could be improved to avoid emitting the continue expr when there | |
| 6055 | // are no jumps to it. This happens when the last statement of a while body is noreturn | |
| 6056 | // and there are no `continue` statements. | |
| 6057 | // Tracking issue: https://github.com/ziglang/zig/issues/9185 | |
| 6058 | if (while_full.ast.cont_expr != 0) { | |
| 6059 | _ = try unusedResultExpr(&then_scope, then_sub_scope, while_full.ast.cont_expr); | |
| 6061 | 6060 | } |
| 6062 | const then_result = try expr(&then_scope, then_sub_scope, .{ .rl = .none }, while_full.ast.then_expr); | |
| 6063 | _ = try addEnsureResult(&then_scope, then_result, while_full.ast.then_expr); | |
| 6061 | try then_scope.addDbgBlockEnd(); | |
| 6064 | 6062 | |
| 6063 | continue_scope.instructions_top = continue_scope.instructions.items.len; | |
| 6064 | _ = try unusedResultExpr(&continue_scope, &continue_scope.base, while_full.ast.then_expr); | |
| 6065 | 6065 | try checkUsed(parent_gz, &then_scope.base, then_sub_scope); |
| 6066 | try then_scope.addDbgBlockEnd(); | |
| 6066 | const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break"; | |
| 6067 | if (!continue_scope.endsWithNoReturn()) { | |
| 6068 | const break_inst = try continue_scope.makeBreak(break_tag, continue_block, .void_value); | |
| 6069 | try then_scope.instructions.append(astgen.gpa, break_inst); | |
| 6070 | } | |
| 6071 | try continue_scope.setBlockBody(continue_block); | |
| 6067 | 6072 | |
| 6068 | var else_scope = parent_gz.makeSubBlock(&continue_scope.base); | |
| 6073 | var else_scope = parent_gz.makeSubBlock(&cond_scope.base); | |
| 6069 | 6074 | defer else_scope.unstack(); |
| 6070 | 6075 | |
| 6071 | 6076 | const else_node = while_full.ast.else_expr; |
| ... | ... | @@ -6128,7 +6133,6 @@ fn whileExpr( |
| 6128 | 6133 | try astgen.appendErrorTok(some.token, "unused while loop label", .{}); |
| 6129 | 6134 | } |
| 6130 | 6135 | } |
| 6131 | const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break"; | |
| 6132 | 6136 | const result = try finishThenElseBlock( |
| 6133 | 6137 | parent_gz, |
| 6134 | 6138 | ri, |
| ... | ... | @@ -6138,7 +6142,7 @@ fn whileExpr( |
| 6138 | 6142 | &else_scope, |
| 6139 | 6143 | condbr, |
| 6140 | 6144 | cond.bool_bit, |
| 6141 | then_result, | |
| 6145 | .void_value, | |
| 6142 | 6146 | else_info.result, |
| 6143 | 6147 | loop_block, |
| 6144 | 6148 | cond_block, |
src/Sema.zig+5-6| ... | ... | @@ -71,8 +71,8 @@ preallocated_new_func: ?*Module.Fn = null, |
| 71 | 71 | /// TODO: after upgrading to use InternPool change the key here to be an |
| 72 | 72 | /// InternPool value index. |
| 73 | 73 | types_to_resolve: std.ArrayListUnmanaged(Air.Inst.Ref) = .{}, |
| 74 | /// These are lazily created runtime blocks from inline_block instructions. | |
| 75 | /// They are created when an inline_break passes through a runtime condition, because | |
| 74 | /// These are lazily created runtime blocks from block_inline instructions. | |
| 75 | /// They are created when an break_inline passes through a runtime condition, because | |
| 76 | 76 | /// Sema must convert comptime control flow to runtime control flow, which means |
| 77 | 77 | /// breaking from a block. |
| 78 | 78 | post_hoc_blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, *LabeledBlock) = .{}, |
| ... | ... | @@ -147,7 +147,7 @@ pub const Block = struct { |
| 147 | 147 | /// for the one that will be the same for all Block instances. |
| 148 | 148 | src_decl: Decl.Index, |
| 149 | 149 | /// Non zero if a non-inline loop or a runtime conditional have been encountered. |
| 150 | /// Stores to to comptime variables are only allowed when var.runtime_index <= runtime_index. | |
| 150 | /// Stores to comptime variables are only allowed when var.runtime_index <= runtime_index. | |
| 151 | 151 | runtime_index: Value.RuntimeIndex = .zero, |
| 152 | 152 | inline_block: Zir.Inst.Index = 0, |
| 153 | 153 | |
| ... | ... | @@ -1391,9 +1391,8 @@ fn analyzeBodyInner( |
| 1391 | 1391 | // If this block contains a function prototype, we need to reset the |
| 1392 | 1392 | // current list of parameters and restore it later. |
| 1393 | 1393 | // Note: this probably needs to be resolved in a more general manner. |
| 1394 | if (tags[inline_body[inline_body.len - 1]] == .repeat_inline) { | |
| 1395 | child_block.inline_block = inline_body[0]; | |
| 1396 | } else child_block.inline_block = block.inline_block; | |
| 1394 | child_block.inline_block = | |
| 1395 | if (tags[inline_body[inline_body.len - 1]] == .repeat_inline) inline_body[0] else inst; | |
| 1397 | 1396 | |
| 1398 | 1397 | var label: Block.Label = .{ |
| 1399 | 1398 | .zir_block = inst, |
test/behavior/basic.zig-1| ... | ... | @@ -646,7 +646,6 @@ test "multiline string literal is null terminated" { |
| 646 | 646 | } |
| 647 | 647 | |
| 648 | 648 | test "string escapes" { |
| 649 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 650 | 649 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 651 | 650 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 652 | 651 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
test/behavior/bit_shifting.zig-1| ... | ... | @@ -62,7 +62,6 @@ fn ShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, compt |
| 62 | 62 | |
| 63 | 63 | test "sharded table" { |
| 64 | 64 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 65 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 66 | 65 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 67 | 66 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 68 | 67 | // realistic 16-way sharding |
test/behavior/bugs/6456.zig-1| ... | ... | @@ -11,7 +11,6 @@ const text = |
| 11 | 11 | ; |
| 12 | 12 | |
| 13 | 13 | test "issue 6456" { |
| 14 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 15 | 14 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 16 | 15 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 17 | 16 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
test/behavior/optional.zig-1| ... | ... | @@ -429,7 +429,6 @@ test "alignment of wrapping an optional payload" { |
| 429 | 429 | |
| 430 | 430 | test "Optional slice size is optimized" { |
| 431 | 431 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; |
| 432 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 433 | 432 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 434 | 433 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 435 | 434 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
test/behavior/pointers.zig-1| ... | ... | @@ -483,7 +483,6 @@ test "pointer to constant decl preserves alignment" { |
| 483 | 483 | test "ptrCast comptime known slice to C pointer" { |
| 484 | 484 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 485 | 485 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 486 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 487 | 486 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 488 | 487 | |
| 489 | 488 | const s: [:0]const u8 = "foo"; |
test/behavior/slice.zig-1| ... | ... | @@ -702,7 +702,6 @@ test "slice field ptr var" { |
| 702 | 702 | test "global slice field access" { |
| 703 | 703 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 704 | 704 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 705 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 706 | 705 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 707 | 706 | |
| 708 | 707 | const S = struct { |
test/behavior/translate_c_macros.zig-2| ... | ... | @@ -123,7 +123,6 @@ test "large integer macro" { |
| 123 | 123 | test "string literal macro with embedded tab character" { |
| 124 | 124 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 125 | 125 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 126 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 127 | 126 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 128 | 127 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 129 | 128 | |
| ... | ... | @@ -133,7 +132,6 @@ test "string literal macro with embedded tab character" { |
| 133 | 132 | test "string and char literals that are not UTF-8 encoded. Issue #12784" { |
| 134 | 133 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 135 | 134 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 136 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 137 | 135 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 138 | 136 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 139 | 137 |
test/behavior/typename.zig-8| ... | ... | @@ -18,7 +18,6 @@ test "anon fn param" { |
| 18 | 18 | return error.SkipZigTest; |
| 19 | 19 | } |
| 20 | 20 | |
| 21 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 22 | 21 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 23 | 22 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 24 | 23 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | ... | @@ -50,7 +49,6 @@ test "anon field init" { |
| 50 | 49 | return error.SkipZigTest; |
| 51 | 50 | } |
| 52 | 51 | |
| 53 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 54 | 52 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 55 | 53 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 56 | 54 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | ... | @@ -76,7 +74,6 @@ test "anon field init" { |
| 76 | 74 | } |
| 77 | 75 | |
| 78 | 76 | test "basic" { |
| 79 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 80 | 77 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 81 | 78 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 82 | 79 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | ... | @@ -92,7 +89,6 @@ test "top level decl" { |
| 92 | 89 | return error.SkipZigTest; |
| 93 | 90 | } |
| 94 | 91 | |
| 95 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 96 | 92 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 97 | 93 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 98 | 94 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | ... | @@ -142,7 +138,6 @@ const B = struct { |
| 142 | 138 | }; |
| 143 | 139 | |
| 144 | 140 | test "fn param" { |
| 145 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 146 | 141 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 147 | 142 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 148 | 143 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | ... | @@ -218,7 +213,6 @@ test "local variable" { |
| 218 | 213 | return error.SkipZigTest; |
| 219 | 214 | } |
| 220 | 215 | |
| 221 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 222 | 216 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 223 | 217 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 224 | 218 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | ... | @@ -238,7 +232,6 @@ test "local variable" { |
| 238 | 232 | |
| 239 | 233 | test "comptime parameters not converted to anytype in function type" { |
| 240 | 234 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; |
| 241 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 242 | 235 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 243 | 236 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 244 | 237 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | ... | @@ -254,7 +247,6 @@ test "anon name strategy used in sub expression" { |
| 254 | 247 | return error.SkipZigTest; |
| 255 | 248 | } |
| 256 | 249 | |
| 257 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 258 | 250 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 259 | 251 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 260 | 252 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |