| author | |
| committer | |
| log | 13ced07f23311bef859d07cdd25e0e4fa95ab76a |
| tree | 6800f8f5e311983d66accee7ef8bf5dd25112685 |
| parent | bf7c3e9355530680b066a573c1743f9d570fddc5 |
also start to form a plan for how inline while loops will work5 files changed, 158 insertions(+), 125 deletions(-)
src/Module.zig+9-20| ... | ... | @@ -700,14 +700,7 @@ pub const Scope = struct { |
| 700 | 700 | /// It is shared among all the blocks in an inline or comptime called |
| 701 | 701 | /// function. |
| 702 | 702 | pub const Inlining = struct { |
| 703 | /// Shared state among the entire inline/comptime call stack. | |
| 704 | shared: *Shared, | |
| 705 | 703 | merges: Merges, |
| 706 | ||
| 707 | pub const Shared = struct { | |
| 708 | caller: ?*Fn, | |
| 709 | branch_count: u32, | |
| 710 | }; | |
| 711 | 704 | }; |
| 712 | 705 | |
| 713 | 706 | pub const Merges = struct { |
| ... | ... | @@ -2015,6 +2008,7 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool { |
| 2015 | 2008 | .inst_map = try analysis_arena.allocator.alloc(*ir.Inst, code.instructions.len), |
| 2016 | 2009 | .owner_decl = decl, |
| 2017 | 2010 | .func = null, |
| 2011 | .owner_func = null, | |
| 2018 | 2012 | .param_inst_list = &.{}, |
| 2019 | 2013 | }; |
| 2020 | 2014 | var block_scope: Scope.Block = .{ |
| ... | ... | @@ -2236,6 +2230,7 @@ fn astgenAndSemaFn( |
| 2236 | 2230 | .inst_map = try fn_type_scope_arena.allocator.alloc(*ir.Inst, fn_type_code.instructions.len), |
| 2237 | 2231 | .owner_decl = decl, |
| 2238 | 2232 | .func = null, |
| 2233 | .owner_func = null, | |
| 2239 | 2234 | .param_inst_list = &.{}, |
| 2240 | 2235 | }; |
| 2241 | 2236 | var block_scope: Scope.Block = .{ |
| ... | ... | @@ -2544,6 +2539,7 @@ fn astgenAndSemaVarDecl( |
| 2544 | 2539 | .inst_map = try gen_scope_arena.allocator.alloc(*ir.Inst, code.instructions.len), |
| 2545 | 2540 | .owner_decl = decl, |
| 2546 | 2541 | .func = null, |
| 2542 | .owner_func = null, | |
| 2547 | 2543 | .param_inst_list = &.{}, |
| 2548 | 2544 | }; |
| 2549 | 2545 | var block_scope: Scope.Block = .{ |
| ... | ... | @@ -2608,6 +2604,7 @@ fn astgenAndSemaVarDecl( |
| 2608 | 2604 | .inst_map = try type_scope_arena.allocator.alloc(*ir.Inst, code.instructions.len), |
| 2609 | 2605 | .owner_decl = decl, |
| 2610 | 2606 | .func = null, |
| 2607 | .owner_func = null, | |
| 2611 | 2608 | .param_inst_list = &.{}, |
| 2612 | 2609 | }; |
| 2613 | 2610 | var block_scope: Scope.Block = .{ |
| ... | ... | @@ -3192,6 +3189,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void { |
| 3192 | 3189 | .inst_map = try mod.gpa.alloc(*ir.Inst, func.zir.instructions.len), |
| 3193 | 3190 | .owner_decl = decl, |
| 3194 | 3191 | .func = func, |
| 3192 | .owner_func = func, | |
| 3195 | 3193 | .param_inst_list = param_inst_list, |
| 3196 | 3194 | }; |
| 3197 | 3195 | defer mod.gpa.free(sema.inst_map); |
| ... | ... | @@ -3681,20 +3679,11 @@ pub fn failWithOwnedErrorMsg(mod: *Module, scope: *Scope, err_msg: *ErrorMsg) In |
| 3681 | 3679 | switch (scope.tag) { |
| 3682 | 3680 | .block => { |
| 3683 | 3681 | const block = scope.cast(Scope.Block).?; |
| 3684 | if (block.inlining) |inlining| { | |
| 3685 | if (inlining.shared.caller) |func| { | |
| 3686 | func.state = .sema_failure; | |
| 3687 | } else { | |
| 3688 | block.sema.owner_decl.analysis = .sema_failure; | |
| 3689 | block.sema.owner_decl.generation = mod.generation; | |
| 3690 | } | |
| 3682 | if (block.sema.owner_func) |func| { | |
| 3683 | func.state = .sema_failure; | |
| 3691 | 3684 | } else { |
| 3692 | if (block.sema.func) |func| { | |
| 3693 | func.state = .sema_failure; | |
| 3694 | } else { | |
| 3695 | block.sema.owner_decl.analysis = .sema_failure; | |
| 3696 | block.sema.owner_decl.generation = mod.generation; | |
| 3697 | } | |
| 3685 | block.sema.owner_decl.analysis = .sema_failure; | |
| 3686 | block.sema.owner_decl.generation = mod.generation; | |
| 3698 | 3687 | } |
| 3699 | 3688 | mod.failed_decls.putAssumeCapacityNoClobber(block.sema.owner_decl, err_msg); |
| 3700 | 3689 | }, |
src/Sema.zig+75-28| ... | ... | @@ -17,6 +17,12 @@ inst_map: []*Inst, |
| 17 | 17 | /// and `src_decl` of `Scope.Block` is the `Decl` of the callee. |
| 18 | 18 | /// This `Decl` owns the arena memory of this `Sema`. |
| 19 | 19 | owner_decl: *Decl, |
| 20 | /// For an inline or comptime function call, this will be the root parent function | |
| 21 | /// which contains the callsite. Corresponds to `owner_decl`. | |
| 22 | owner_func: ?*Module.Fn, | |
| 23 | /// The function this ZIR code is the body of, according to the source code. | |
| 24 | /// This starts out the same as `owner_func` and then diverges in the case of | |
| 25 | /// an inline or comptime function call. | |
| 20 | 26 | func: ?*Module.Fn, |
| 21 | 27 | /// For now, TZIR requires arg instructions to be the first N instructions in the |
| 22 | 28 | /// TZIR code. We store references here for the purpose of `resolveInst`. |
| ... | ... | @@ -26,6 +32,7 @@ func: ?*Module.Fn, |
| 26 | 32 | /// > param_count: u32 |
| 27 | 33 | param_inst_list: []const *ir.Inst, |
| 28 | 34 | branch_quota: u32 = 1000, |
| 35 | branch_count: u32 = 0, | |
| 29 | 36 | /// This field is updated when a new source location becomes active, so that |
| 30 | 37 | /// instructions which do not have explicitly mapped source locations still have |
| 31 | 38 | /// access to the source location set by the previous instruction which did |
| ... | ... | @@ -86,6 +93,7 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde |
| 86 | 93 | |
| 87 | 94 | const map = block.sema.inst_map; |
| 88 | 95 | const tags = block.sema.code.instructions.items(.tag); |
| 96 | const datas = block.sema.code.instructions.items(.data); | |
| 89 | 97 | |
| 90 | 98 | // We use a while(true) loop here to avoid a redundant way of breaking out of |
| 91 | 99 | // the loop. The only way to break out of the loop is with a `noreturn` |
| ... | ... | @@ -178,6 +186,7 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde |
| 178 | 186 | .is_non_null_ptr => try sema.zirIsNullPtr(block, inst, true), |
| 179 | 187 | .is_null => try sema.zirIsNull(block, inst, false), |
| 180 | 188 | .is_null_ptr => try sema.zirIsNullPtr(block, inst, false), |
| 189 | .loop => try sema.zirLoop(block, inst), | |
| 181 | 190 | .merge_error_sets => try sema.zirMergeErrorSets(block, inst), |
| 182 | 191 | .mod_rem => try sema.zirArithmetic(block, inst), |
| 183 | 192 | .mul => try sema.zirArithmetic(block, inst), |
| ... | ... | @@ -225,7 +234,7 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde |
| 225 | 234 | .ret_node => return sema.zirRetNode(block, inst), |
| 226 | 235 | .ret_tok => return sema.zirRetTok(block, inst, false), |
| 227 | 236 | .@"unreachable" => return sema.zirUnreachable(block, inst), |
| 228 | .loop => return sema.zirLoop(block, inst), | |
| 237 | .repeat => return sema.zirRepeat(block, inst), | |
| 229 | 238 | |
| 230 | 239 | // Instructions that we know can *never* be noreturn based solely on |
| 231 | 240 | // their tag. We avoid needlessly checking if they are noreturn and |
| ... | ... | @@ -276,6 +285,14 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde |
| 276 | 285 | try sema.zirResolveInferredAlloc(block, inst); |
| 277 | 286 | continue; |
| 278 | 287 | }, |
| 288 | ||
| 289 | // Special case: send comptime control flow back to the beginning of this block. | |
| 290 | .repeat_inline => { | |
| 291 | const src: LazySrcLoc = .{ .node_offset = datas[inst].node }; | |
| 292 | try sema.emitBackwardBranch(block, src); | |
| 293 | i = 0; | |
| 294 | continue; | |
| 295 | }, | |
| 279 | 296 | }; |
| 280 | 297 | if (map[inst].ty.isNoReturn()) |
| 281 | 298 | return always_noreturn; |
| ... | ... | @@ -764,14 +781,50 @@ fn zirCompileLog(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr |
| 764 | 781 | } |
| 765 | 782 | } |
| 766 | 783 | |
| 767 | fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref { | |
| 784 | fn zirRepeat(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref { | |
| 785 | const tracy = trace(@src()); | |
| 786 | defer tracy.end(); | |
| 787 | ||
| 788 | const src_node = sema.code.instructions.items(.data)[inst].node; | |
| 789 | const src: LazySrcLoc = .{ .node_offset = src_node }; | |
| 790 | try sema.requireRuntimeBlock(block, src); | |
| 791 | return always_noreturn; | |
| 792 | } | |
| 793 | ||
| 794 | fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | |
| 768 | 795 | const tracy = trace(@src()); |
| 769 | 796 | defer tracy.end(); |
| 770 | 797 | |
| 771 | 798 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 772 | 799 | const src = inst_data.src(); |
| 773 | const extra = sema.code.extraData(zir.Inst.MultiOp, inst_data.payload_index); | |
| 774 | const body = sema.code.extra[extra.end..][0..extra.data.operands_len]; | |
| 800 | const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index); | |
| 801 | const body = sema.code.extra[extra.end..][0..extra.data.body_len]; | |
| 802 | ||
| 803 | // TZIR expects a block outside the loop block too. | |
| 804 | const block_inst = try sema.arena.create(Inst.Block); | |
| 805 | block_inst.* = .{ | |
| 806 | .base = .{ | |
| 807 | .tag = Inst.Block.base_tag, | |
| 808 | .ty = undefined, | |
| 809 | .src = src, | |
| 810 | }, | |
| 811 | .body = undefined, | |
| 812 | }; | |
| 813 | ||
| 814 | var child_block = parent_block.makeSubBlock(); | |
| 815 | child_block.label = Scope.Block.Label{ | |
| 816 | .zir_block = inst, | |
| 817 | .merges = .{ | |
| 818 | .results = .{}, | |
| 819 | .br_list = .{}, | |
| 820 | .block_inst = block_inst, | |
| 821 | }, | |
| 822 | }; | |
| 823 | const merges = &child_block.label.?.merges; | |
| 824 | ||
| 825 | defer child_block.instructions.deinit(sema.gpa); | |
| 826 | defer merges.results.deinit(sema.gpa); | |
| 827 | defer merges.br_list.deinit(sema.gpa); | |
| 775 | 828 | |
| 776 | 829 | // Reserve space for a Loop instruction so that generated Break instructions can |
| 777 | 830 | // point to it, even if it doesn't end up getting used because the code ends up being |
| ... | ... | @@ -786,23 +839,17 @@ fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerE |
| 786 | 839 | .body = undefined, |
| 787 | 840 | }; |
| 788 | 841 | |
| 789 | var child_block: Scope.Block = .{ | |
| 790 | .parent = parent_block, | |
| 791 | .sema = sema, | |
| 792 | .src_decl = parent_block.src_decl, | |
| 793 | .instructions = .{}, | |
| 794 | .inlining = parent_block.inlining, | |
| 795 | .is_comptime = parent_block.is_comptime, | |
| 796 | }; | |
| 797 | defer child_block.instructions.deinit(sema.gpa); | |
| 842 | var loop_block = child_block.makeSubBlock(); | |
| 843 | defer loop_block.instructions.deinit(sema.gpa); | |
| 798 | 844 | |
| 799 | _ = try sema.analyzeBody(&child_block, body); | |
| 845 | _ = try sema.analyzeBody(&loop_block, body); | |
| 800 | 846 | |
| 801 | 847 | // Loop repetition is implied so the last instruction may or may not be a noreturn instruction. |
| 802 | 848 | |
| 803 | try parent_block.instructions.append(sema.gpa, &loop_inst.base); | |
| 804 | loop_inst.body = .{ .instructions = try sema.arena.dupe(*Inst, child_block.instructions.items) }; | |
| 805 | return always_noreturn; | |
| 849 | try child_block.instructions.append(sema.gpa, &loop_inst.base); | |
| 850 | loop_inst.body = .{ .instructions = try sema.arena.dupe(*Inst, loop_block.instructions.items) }; | |
| 851 | ||
| 852 | return sema.analyzeBlockBody(parent_block, &child_block, merges); | |
| 806 | 853 | } |
| 807 | 854 | |
| 808 | 855 | fn zirBlock( |
| ... | ... | @@ -1160,16 +1207,9 @@ fn analyzeCall( |
| 1160 | 1207 | }, |
| 1161 | 1208 | .body = undefined, |
| 1162 | 1209 | }; |
| 1163 | // If this is the top of the inline/comptime call stack, we use this data. | |
| 1164 | // Otherwise we pass on the shared data from the parent scope. | |
| 1165 | var shared_inlining: Scope.Block.Inlining.Shared = .{ | |
| 1166 | .branch_count = 0, | |
| 1167 | .caller = sema.func, | |
| 1168 | }; | |
| 1169 | 1210 | // This one is shared among sub-blocks within the same callee, but not |
| 1170 | 1211 | // shared among the entire inline/comptime call stack. |
| 1171 | 1212 | var inlining: Scope.Block.Inlining = .{ |
| 1172 | .shared = if (block.inlining) |inlining| inlining.shared else &shared_inlining, | |
| 1173 | 1213 | .merges = .{ |
| 1174 | 1214 | .results = .{}, |
| 1175 | 1215 | .br_list = .{}, |
| ... | ... | @@ -1183,8 +1223,11 @@ fn analyzeCall( |
| 1183 | 1223 | .code = module_fn.zir, |
| 1184 | 1224 | .inst_map = try sema.gpa.alloc(*ir.Inst, module_fn.zir.instructions.len), |
| 1185 | 1225 | .owner_decl = sema.owner_decl, |
| 1226 | .owner_func = sema.owner_func, | |
| 1186 | 1227 | .func = module_fn, |
| 1187 | 1228 | .param_inst_list = casted_args, |
| 1229 | .branch_quota = sema.branch_quota, | |
| 1230 | .branch_count = sema.branch_count, | |
| 1188 | 1231 | }; |
| 1189 | 1232 | defer sema.gpa.free(inline_sema.inst_map); |
| 1190 | 1233 | |
| ... | ... | @@ -1210,7 +1253,12 @@ fn analyzeCall( |
| 1210 | 1253 | // the block_inst above. |
| 1211 | 1254 | _ = try inline_sema.root(&child_block); |
| 1212 | 1255 | |
| 1213 | break :res try inline_sema.analyzeBlockBody(block, &child_block, merges); | |
| 1256 | const result = try inline_sema.analyzeBlockBody(block, &child_block, merges); | |
| 1257 | ||
| 1258 | sema.branch_quota = inline_sema.branch_quota; | |
| 1259 | sema.branch_count = inline_sema.branch_count; | |
| 1260 | ||
| 1261 | break :res result; | |
| 1214 | 1262 | } else res: { |
| 1215 | 1263 | try sema.requireRuntimeBlock(block, call_src); |
| 1216 | 1264 | break :res try block.addCall(call_src, ret_type, func, casted_args); |
| ... | ... | @@ -3169,9 +3217,8 @@ fn safetyPanic(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, panic_id: Pani |
| 3169 | 3217 | } |
| 3170 | 3218 | |
| 3171 | 3219 | fn emitBackwardBranch(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void { |
| 3172 | const shared = block.inlining.?.shared; | |
| 3173 | shared.branch_count += 1; | |
| 3174 | if (shared.branch_count > sema.branch_quota) { | |
| 3220 | sema.branch_count += 1; | |
| 3221 | if (sema.branch_count > sema.branch_quota) { | |
| 3175 | 3222 | // TODO show the "called from here" stack |
| 3176 | 3223 | return sema.mod.fail(&block.base, src, "evaluation exceeded {d} backwards branches", .{sema.branch_quota}); |
| 3177 | 3224 | } |
src/astgen.zig+35-62| ... | ... | @@ -1055,6 +1055,7 @@ fn blockExprStmts( |
| 1055 | 1055 | .bit_or, |
| 1056 | 1056 | .block, |
| 1057 | 1057 | .block_comptime, |
| 1058 | .loop, | |
| 1058 | 1059 | .bool_br_and, |
| 1059 | 1060 | .bool_br_or, |
| 1060 | 1061 | .bool_not, |
| ... | ... | @@ -1156,12 +1157,13 @@ fn blockExprStmts( |
| 1156 | 1157 | .ret_tok, |
| 1157 | 1158 | .ret_coerce, |
| 1158 | 1159 | .@"unreachable", |
| 1159 | .loop, | |
| 1160 | 1160 | .elided, |
| 1161 | 1161 | .store, |
| 1162 | 1162 | .store_to_block_ptr, |
| 1163 | 1163 | .store_to_inferred_ptr, |
| 1164 | 1164 | .resolve_inferred_alloc, |
| 1165 | .repeat, | |
| 1166 | .repeat_inline, | |
| 1165 | 1167 | => break :b true, |
| 1166 | 1168 | } |
| 1167 | 1169 | } else switch (maybe_unused_result) { |
| ... | ... | @@ -2145,20 +2147,16 @@ fn whileExpr( |
| 2145 | 2147 | node: ast.Node.Index, |
| 2146 | 2148 | while_full: ast.full.While, |
| 2147 | 2149 | ) InnerError!zir.Inst.Ref { |
| 2148 | if (true) @panic("TODO update for zir-memory-layout"); | |
| 2149 | 2150 | if (while_full.label_token) |label_token| { |
| 2150 | 2151 | try checkLabelRedefinition(mod, scope, label_token); |
| 2151 | 2152 | } |
| 2152 | if (while_full.inline_token) |inline_token| { | |
| 2153 | return mod.failTok(scope, inline_token, "TODO inline while", .{}); | |
| 2154 | } | |
| 2155 | ||
| 2156 | 2153 | const parent_gz = scope.getGenZir(); |
| 2154 | const loop_block = try parent_gz.addBlock(.loop, node); | |
| 2155 | try parent_gz.instructions.append(mod.gpa, loop_block); | |
| 2157 | 2156 | |
| 2158 | 2157 | var loop_scope: Scope.GenZir = .{ |
| 2159 | 2158 | .parent = scope, |
| 2160 | .decl = scope.ownerDecl().?, | |
| 2161 | .arena = scope.arena(), | |
| 2159 | .zir_code = parent_gz.zir_code, | |
| 2162 | 2160 | .force_comptime = parent_gz.force_comptime, |
| 2163 | 2161 | .instructions = .{}, |
| 2164 | 2162 | }; |
| ... | ... | @@ -2167,21 +2165,12 @@ fn whileExpr( |
| 2167 | 2165 | |
| 2168 | 2166 | var continue_scope: Scope.GenZir = .{ |
| 2169 | 2167 | .parent = &loop_scope.base, |
| 2170 | .decl = loop_scope.decl, | |
| 2171 | .arena = loop_scope.arena, | |
| 2168 | .zir_code = parent_gz.zir_code, | |
| 2172 | 2169 | .force_comptime = loop_scope.force_comptime, |
| 2173 | 2170 | .instructions = .{}, |
| 2174 | 2171 | }; |
| 2175 | 2172 | defer continue_scope.instructions.deinit(mod.gpa); |
| 2176 | 2173 | |
| 2177 | const tree = gz.tree(); | |
| 2178 | const main_tokens = tree.nodes.items(.main_token); | |
| 2179 | ||
| 2180 | const while_src = token_starts[while_full.ast.while_token]; | |
| 2181 | const void_type = try addZIRInstConst(mod, scope, while_src, .{ | |
| 2182 | .ty = Type.initTag(.type), | |
| 2183 | .val = Value.initTag(.void_type), | |
| 2184 | }); | |
| 2185 | 2174 | const cond = c: { |
| 2186 | 2175 | // TODO https://github.com/ziglang/zig/issues/7929 |
| 2187 | 2176 | if (while_full.error_token) |error_token| { |
| ... | ... | @@ -2189,59 +2178,41 @@ fn whileExpr( |
| 2189 | 2178 | } else if (while_full.payload_token) |payload_token| { |
| 2190 | 2179 | return mod.failTok(scope, payload_token, "TODO implement while optional", .{}); |
| 2191 | 2180 | } else { |
| 2192 | const bool_type = try addZIRInstConst(mod, &continue_scope.base, while_src, .{ | |
| 2193 | .ty = Type.initTag(.type), | |
| 2194 | .val = Value.initTag(.bool_type), | |
| 2195 | }); | |
| 2196 | break :c try expr(mod, &continue_scope.base, .{ .ty = bool_type }, while_full.ast.cond_expr); | |
| 2181 | const bool_type_rl: ResultLoc = .{ .ty = @enumToInt(zir.Const.bool_type) }; | |
| 2182 | break :c try expr(mod, &continue_scope.base, bool_type_rl, while_full.ast.cond_expr); | |
| 2197 | 2183 | } |
| 2198 | 2184 | }; |
| 2199 | 2185 | |
| 2200 | const condbr = try addZIRInstSpecial(mod, &continue_scope.base, while_src, zir.Inst.CondBr, .{ | |
| 2201 | .condition = cond, | |
| 2202 | .then_body = undefined, // populated below | |
| 2203 | .else_body = undefined, // populated below | |
| 2204 | }, .{}); | |
| 2205 | const cond_block = try addZIRInstBlock(mod, &loop_scope.base, while_src, .block, .{ | |
| 2206 | .instructions = try loop_scope.arena.dupe(zir.Inst.Ref, continue_scope.instructions.items), | |
| 2207 | }); | |
| 2186 | const condbr = try continue_scope.addCondBr(node); | |
| 2187 | const cond_block = try loop_scope.addBlock(.block, node); | |
| 2188 | try loop_scope.instructions.append(mod.gpa, cond_block); | |
| 2189 | try continue_scope.setBlockBody(cond_block); | |
| 2190 | ||
| 2208 | 2191 | // TODO avoid emitting the continue expr when there |
| 2209 | 2192 | // are no jumps to it. This happens when the last statement of a while body is noreturn |
| 2210 | 2193 | // and there are no `continue` statements. |
| 2211 | 2194 | // The "repeat" at the end of a loop body is implied. |
| 2212 | 2195 | if (while_full.ast.cont_expr != 0) { |
| 2213 | _ = try expr(mod, &loop_scope.base, .{ .ty = void_type }, while_full.ast.cont_expr); | |
| 2196 | const void_type_rl: ResultLoc = .{ .ty = @enumToInt(zir.Const.void_type) }; | |
| 2197 | _ = try expr(mod, &loop_scope.base, void_type_rl, while_full.ast.cont_expr); | |
| 2214 | 2198 | } |
| 2215 | const loop = try scope.arena().create(zir.Inst.Loop); | |
| 2216 | loop.* = .{ | |
| 2217 | .base = .{ | |
| 2218 | .tag = .loop, | |
| 2219 | .src = while_src, | |
| 2220 | }, | |
| 2221 | .positionals = .{ | |
| 2222 | .body = .{ | |
| 2223 | .instructions = try scope.arena().dupe(zir.Inst.Ref, loop_scope.instructions.items), | |
| 2224 | }, | |
| 2225 | }, | |
| 2226 | .kw_args = .{}, | |
| 2227 | }; | |
| 2228 | const while_block = try addZIRInstBlock(mod, scope, while_src, .block, .{ | |
| 2229 | .instructions = try scope.arena().dupe(zir.Inst.Ref, &[1]zir.Inst.Ref{&loop.base}), | |
| 2230 | }); | |
| 2231 | loop_scope.break_block = while_block; | |
| 2199 | const is_inline = while_full.inline_token != null; | |
| 2200 | const repeat_tag: zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat; | |
| 2201 | _ = try loop_scope.addNode(repeat_tag, node); | |
| 2202 | ||
| 2203 | try loop_scope.setBlockBody(loop_block); | |
| 2204 | loop_scope.break_block = loop_block; | |
| 2232 | 2205 | loop_scope.continue_block = cond_block; |
| 2233 | 2206 | if (while_full.label_token) |label_token| { |
| 2234 | 2207 | loop_scope.label = @as(?Scope.GenZir.Label, Scope.GenZir.Label{ |
| 2235 | 2208 | .token = label_token, |
| 2236 | .block_inst = while_block, | |
| 2209 | .block_inst = loop_block, | |
| 2237 | 2210 | }); |
| 2238 | 2211 | } |
| 2239 | 2212 | |
| 2240 | const then_src = token_starts[tree.lastToken(while_full.ast.then_expr)]; | |
| 2241 | 2213 | var then_scope: Scope.GenZir = .{ |
| 2242 | 2214 | .parent = &continue_scope.base, |
| 2243 | .decl = continue_scope.decl, | |
| 2244 | .arena = continue_scope.arena, | |
| 2215 | .zir_code = parent_gz.zir_code, | |
| 2245 | 2216 | .force_comptime = continue_scope.force_comptime, |
| 2246 | 2217 | .instructions = .{}, |
| 2247 | 2218 | }; |
| ... | ... | @@ -2254,29 +2225,31 @@ fn whileExpr( |
| 2254 | 2225 | |
| 2255 | 2226 | var else_scope: Scope.GenZir = .{ |
| 2256 | 2227 | .parent = &continue_scope.base, |
| 2257 | .decl = continue_scope.decl, | |
| 2258 | .arena = continue_scope.arena, | |
| 2228 | .zir_code = parent_gz.zir_code, | |
| 2259 | 2229 | .force_comptime = continue_scope.force_comptime, |
| 2260 | 2230 | .instructions = .{}, |
| 2261 | 2231 | }; |
| 2262 | 2232 | defer else_scope.instructions.deinit(mod.gpa); |
| 2263 | 2233 | |
| 2264 | 2234 | const else_node = while_full.ast.else_expr; |
| 2265 | const else_info: struct { src: usize, result: ?*zir.Inst } = if (else_node != 0) blk: { | |
| 2235 | const else_info: struct { | |
| 2236 | src: ast.Node.Index, | |
| 2237 | result: zir.Inst.Ref, | |
| 2238 | } = if (else_node != 0) blk: { | |
| 2266 | 2239 | loop_scope.break_count += 1; |
| 2267 | 2240 | const sub_scope = &else_scope.base; |
| 2268 | 2241 | break :blk .{ |
| 2269 | .src = token_starts[tree.lastToken(else_node)], | |
| 2242 | .src = else_node, | |
| 2270 | 2243 | .result = try expr(mod, sub_scope, loop_scope.break_result_loc, else_node), |
| 2271 | 2244 | }; |
| 2272 | 2245 | } else .{ |
| 2273 | .src = token_starts[tree.lastToken(while_full.ast.then_expr)], | |
| 2274 | .result = null, | |
| 2246 | .src = while_full.ast.then_expr, | |
| 2247 | .result = 0, | |
| 2275 | 2248 | }; |
| 2276 | 2249 | |
| 2277 | 2250 | if (loop_scope.label) |some| { |
| 2278 | 2251 | if (!some.used) { |
| 2279 | return mod.fail(scope, token_starts[some.token], "unused while loop label", .{}); | |
| 2252 | return mod.failTok(scope, some.token, "unused while loop label", .{}); | |
| 2280 | 2253 | } |
| 2281 | 2254 | } |
| 2282 | 2255 | return finishThenElseBlock( |
| ... | ... | @@ -2289,11 +2262,11 @@ fn whileExpr( |
| 2289 | 2262 | &else_scope, |
| 2290 | 2263 | condbr, |
| 2291 | 2264 | cond, |
| 2292 | then_src, | |
| 2265 | while_full.ast.then_expr, | |
| 2293 | 2266 | else_info.src, |
| 2294 | 2267 | then_result, |
| 2295 | 2268 | else_info.result, |
| 2296 | while_block, | |
| 2269 | loop_block, | |
| 2297 | 2270 | cond_block, |
| 2298 | 2271 | ); |
| 2299 | 2272 | } |
src/ir.zig+10-8| ... | ... | @@ -80,22 +80,24 @@ pub const Inst = struct { |
| 80 | 80 | condbr, |
| 81 | 81 | constant, |
| 82 | 82 | dbg_stmt, |
| 83 | // ?T => bool | |
| 83 | /// ?T => bool | |
| 84 | 84 | is_null, |
| 85 | // ?T => bool (inverted logic) | |
| 85 | /// ?T => bool (inverted logic) | |
| 86 | 86 | is_non_null, |
| 87 | // *?T => bool | |
| 87 | /// *?T => bool | |
| 88 | 88 | is_null_ptr, |
| 89 | // *?T => bool (inverted logic) | |
| 89 | /// *?T => bool (inverted logic) | |
| 90 | 90 | is_non_null_ptr, |
| 91 | // E!T => bool | |
| 91 | /// E!T => bool | |
| 92 | 92 | is_err, |
| 93 | // *E!T => bool | |
| 93 | /// *E!T => bool | |
| 94 | 94 | is_err_ptr, |
| 95 | 95 | bool_and, |
| 96 | 96 | bool_or, |
| 97 | 97 | /// Read a value from a pointer. |
| 98 | 98 | load, |
| 99 | /// A labeled block of code that loops forever. At the end of the body it is implied | |
| 100 | /// to repeat; no explicit "repeat" instruction terminates loop bodies. | |
| 99 | 101 | loop, |
| 100 | 102 | ptrtoint, |
| 101 | 103 | ref, |
| ... | ... | @@ -112,9 +114,9 @@ pub const Inst = struct { |
| 112 | 114 | not, |
| 113 | 115 | floatcast, |
| 114 | 116 | intcast, |
| 115 | // ?T => T | |
| 117 | /// ?T => T | |
| 116 | 118 | optional_payload, |
| 117 | // *?T => *T | |
| 119 | /// *?T => *T | |
| 118 | 120 | optional_payload_ptr, |
| 119 | 121 | wrap_optional, |
| 120 | 122 | /// E!T -> T |
src/zir.zig+29-7| ... | ... | @@ -649,11 +649,19 @@ pub const Inst = struct { |
| 649 | 649 | /// Return a boolean true if dereferenced pointer is an error |
| 650 | 650 | /// Uses the `un_tok` field. |
| 651 | 651 | is_err_ptr, |
| 652 | /// A labeled block of code that loops forever. At the end of the body it is implied | |
| 653 | /// to repeat; no explicit "repeat" instruction terminates loop bodies. | |
| 652 | /// A labeled block of code that loops forever. At the end of the body will have either | |
| 653 | /// a `repeat` instruction or a `repeat_inline` instruction. | |
| 654 | 654 | /// Uses the `pl_node` field. The AST node is either a for loop or while loop. |
| 655 | /// This ZIR instruction is needed because TZIR does not (yet?) match ZIR, and Sema | |
| 656 | /// needs to emit more than 1 TZIR block for this instruction. | |
| 655 | 657 | /// The payload is `Block`. |
| 656 | 658 | loop, |
| 659 | /// Sends runtime control flow back to the beginning of the current block. | |
| 660 | /// Uses the `node` field. | |
| 661 | repeat, | |
| 662 | /// Sends comptime control flow back to the beginning of the current block. | |
| 663 | /// Uses the `node` field. | |
| 664 | repeat_inline, | |
| 657 | 665 | /// Merge two error sets into one, `E1 || E2`. |
| 658 | 666 | merge_error_sets, |
| 659 | 667 | /// Ambiguously remainder division or modulus. If the computation would possibly have |
| ... | ... | @@ -736,6 +744,7 @@ pub const Inst = struct { |
| 736 | 744 | /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceSentinel`. |
| 737 | 745 | slice_sentinel, |
| 738 | 746 | /// Write a value to a pointer. For loading, see `deref`. |
| 747 | /// Uses the `bin` union field. | |
| 739 | 748 | store, |
| 740 | 749 | /// Same as `store` but the type of the value being stored will be used to infer |
| 741 | 750 | /// the block type. The LHS is the pointer to store to. |
| ... | ... | @@ -902,6 +911,7 @@ pub const Inst = struct { |
| 902 | 911 | .bit_or, |
| 903 | 912 | .block, |
| 904 | 913 | .block_comptime, |
| 914 | .loop, | |
| 905 | 915 | .bool_br_and, |
| 906 | 916 | .bool_br_or, |
| 907 | 917 | .bool_not, |
| ... | ... | @@ -1012,7 +1022,8 @@ pub const Inst = struct { |
| 1012 | 1022 | .ret_tok, |
| 1013 | 1023 | .ret_coerce, |
| 1014 | 1024 | .@"unreachable", |
| 1015 | .loop, | |
| 1025 | .repeat, | |
| 1026 | .repeat_inline, | |
| 1016 | 1027 | => true, |
| 1017 | 1028 | }; |
| 1018 | 1029 | } |
| ... | ... | @@ -1355,12 +1366,13 @@ const Writer = struct { |
| 1355 | 1366 | .bit_and, |
| 1356 | 1367 | .bit_or, |
| 1357 | 1368 | .as, |
| 1358 | .@"break", | |
| 1359 | 1369 | .coerce_result_ptr, |
| 1360 | 1370 | .elem_ptr, |
| 1361 | 1371 | .elem_val, |
| 1362 | 1372 | .intcast, |
| 1363 | 1373 | .merge_error_sets, |
| 1374 | .store, | |
| 1375 | .store_to_block_ptr, | |
| 1364 | 1376 | => try self.writeBin(stream, inst), |
| 1365 | 1377 | |
| 1366 | 1378 | .alloc, |
| ... | ... | @@ -1425,6 +1437,7 @@ const Writer = struct { |
| 1425 | 1437 | .elided => try stream.writeAll(")"), |
| 1426 | 1438 | .break_void_node => try self.writeBreakVoidNode(stream, inst), |
| 1427 | 1439 | .int_type => try self.writeIntType(stream, inst), |
| 1440 | .@"break" => try self.writeBreak(stream, inst), | |
| 1428 | 1441 | |
| 1429 | 1442 | .@"asm", |
| 1430 | 1443 | .asm_volatile, |
| ... | ... | @@ -1436,7 +1449,6 @@ const Writer = struct { |
| 1436 | 1449 | .field_ptr_named, |
| 1437 | 1450 | .field_val_named, |
| 1438 | 1451 | .floatcast, |
| 1439 | .loop, | |
| 1440 | 1452 | .slice_start, |
| 1441 | 1453 | .slice_end, |
| 1442 | 1454 | .slice_sentinel, |
| ... | ... | @@ -1473,6 +1485,7 @@ const Writer = struct { |
| 1473 | 1485 | |
| 1474 | 1486 | .block, |
| 1475 | 1487 | .block_comptime, |
| 1488 | .loop, | |
| 1476 | 1489 | => try self.writePlNodeBlock(stream, inst), |
| 1477 | 1490 | |
| 1478 | 1491 | .condbr => try self.writePlNodeCondBr(stream, inst), |
| ... | ... | @@ -1483,6 +1496,8 @@ const Writer = struct { |
| 1483 | 1496 | .dbg_stmt_node, |
| 1484 | 1497 | .ret_ptr, |
| 1485 | 1498 | .ret_type, |
| 1499 | .repeat, | |
| 1500 | .repeat_inline, | |
| 1486 | 1501 | => try self.writeNode(stream, inst), |
| 1487 | 1502 | |
| 1488 | 1503 | .decl_ref, |
| ... | ... | @@ -1506,8 +1521,6 @@ const Writer = struct { |
| 1506 | 1521 | .bitcast_result_ptr, |
| 1507 | 1522 | .error_union_type, |
| 1508 | 1523 | .error_set, |
| 1509 | .store, | |
| 1510 | .store_to_block_ptr, | |
| 1511 | 1524 | .store_to_inferred_ptr, |
| 1512 | 1525 | => try stream.writeAll("TODO)"), |
| 1513 | 1526 | } |
| ... | ... | @@ -1772,6 +1785,15 @@ const Writer = struct { |
| 1772 | 1785 | try self.writeSrc(stream, int_type.src()); |
| 1773 | 1786 | } |
| 1774 | 1787 | |
| 1788 | fn writeBreak(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 1789 | const inst_data = self.code.instructions.items(.data)[inst].@"break"; | |
| 1790 | ||
| 1791 | try self.writeInstIndex(stream, inst_data.block_inst); | |
| 1792 | try stream.writeAll(", "); | |
| 1793 | try self.writeInstRef(stream, inst_data.operand); | |
| 1794 | try stream.writeAll(")"); | |
| 1795 | } | |
| 1796 | ||
| 1775 | 1797 | fn writeUnreachable(self: *Writer, stream: anytype, inst: Inst.Index) !void { |
| 1776 | 1798 | const inst_data = self.code.instructions.items(.data)[inst].@"unreachable"; |
| 1777 | 1799 | const safety_str = if (inst_data.safety) "safe" else "unsafe"; |