| ... | ... | @@ -2162,92 +2162,101 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn |
| 2162 | 2162 | |
| 2163 | 2163 | // Look for the label in the scope. |
| 2164 | 2164 | var scope = parent_scope; |
| 2165 | | while (true) { |
| 2166 | | switch (scope.tag) { |
| 2167 | | .gen_zir => { |
| 2168 | | const block_gz = scope.cast(GenZir).?; |
| 2165 | find_scope: switch (scope.tag) { |
| 2166 | .gen_zir => { |
| 2167 | const gen_zir = scope.cast(GenZir).?; |
| 2169 | 2168 | |
| 2170 | | if (block_gz.cur_defer_node.unwrap()) |cur_defer_node| { |
| 2171 | | // We are breaking out of a `defer` block. |
| 2172 | | return astgen.failNodeNotes(node, "cannot break out of defer expression", .{}, &.{ |
| 2173 | | try astgen.errNoteNode( |
| 2174 | | cur_defer_node, |
| 2175 | | "defer expression here", |
| 2176 | | .{}, |
| 2177 | | ), |
| 2178 | | }); |
| 2179 | | } |
| 2169 | if (gen_zir.cur_defer_node.unwrap()) |cur_defer_node| { |
| 2170 | // We are breaking out of a `defer` block. |
| 2171 | return astgen.failNodeNotes(node, "cannot break out of defer expression", .{}, &.{ |
| 2172 | try astgen.errNoteNode( |
| 2173 | cur_defer_node, |
| 2174 | "defer expression here", |
| 2175 | .{}, |
| 2176 | ), |
| 2177 | }); |
| 2178 | } |
| 2180 | 2179 | |
| 2181 | | const block_inst = blk: { |
| 2182 | | if (opt_break_label.unwrap()) |break_label| { |
| 2183 | | if (block_gz.label) |*label| { |
| 2184 | | if (try astgen.tokenIdentEql(label.token, break_label)) { |
| 2185 | | label.used = true; |
| 2186 | | break :blk label.block_inst; |
| 2187 | | } |
| 2188 | | } |
| 2189 | | } else if (block_gz.break_block.unwrap()) |i| { |
| 2190 | | break :blk i; |
| 2180 | if (opt_break_label.unwrap()) |break_label| labeled: { |
| 2181 | if (gen_zir.label) |*label| { |
| 2182 | if (try astgen.tokenIdentEql(label.token, break_label)) { |
| 2183 | label.used = true; |
| 2184 | break :labeled; |
| 2191 | 2185 | } |
| 2192 | | // If not the target, start over with the parent |
| 2193 | | scope = block_gz.parent; |
| 2194 | | continue; |
| 2195 | | }; |
| 2196 | | // If we made it here, this block is the target of the break expr |
| 2197 | | |
| 2198 | | const break_tag: Zir.Inst.Tag = if (block_gz.is_inline) |
| 2199 | | .break_inline |
| 2200 | | else |
| 2201 | | .@"break"; |
| 2202 | | |
| 2203 | | const rhs = opt_rhs.unwrap() orelse { |
| 2204 | | _ = try rvalue(parent_gz, block_gz.break_result_info, .void_value, node); |
| 2205 | | |
| 2206 | | try genDefers(parent_gz, scope, parent_scope, .normal_only); |
| 2207 | | |
| 2208 | | // As our last action before the break, "pop" the error trace if needed |
| 2209 | | if (!block_gz.is_comptime) |
| 2210 | | _ = try parent_gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, node); |
| 2186 | } |
| 2187 | // gz without or with different label, continue to parent scopes. |
| 2188 | scope = gen_zir.parent; |
| 2189 | continue :find_scope scope.tag; |
| 2190 | } else if (!gen_zir.allow_unlabeled_control_flow) { |
| 2191 | // This `break` is unlabeled and the gz we've found doesn't allow |
| 2192 | // unlabeled control flow. Continue to parent scopes. |
| 2193 | scope = gen_zir.parent; |
| 2194 | continue :find_scope scope.tag; |
| 2195 | } |
| 2211 | 2196 | |
| 2212 | | _ = try parent_gz.addBreak(break_tag, block_inst, .void_value); |
| 2213 | | return Zir.Inst.Ref.unreachable_value; |
| 2214 | | }; |
| 2197 | const break_tag: Zir.Inst.Tag = if (gen_zir.is_inline) |
| 2198 | .break_inline |
| 2199 | else |
| 2200 | .@"break"; |
| 2215 | 2201 | |
| 2216 | | const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_info, rhs, node); |
| 2202 | if (opt_rhs.unwrap()) |rhs| { |
| 2203 | // We have a `break` operand. |
| 2204 | const operand = try reachableExpr(parent_gz, parent_scope, gen_zir.break_result_info, rhs, node); |
| 2217 | 2205 | |
| 2218 | 2206 | try genDefers(parent_gz, scope, parent_scope, .normal_only); |
| 2219 | 2207 | |
| 2220 | 2208 | // As our last action before the break, "pop" the error trace if needed |
| 2221 | | if (!block_gz.is_comptime) |
| 2222 | | try restoreErrRetIndex(parent_gz, .{ .block = block_inst }, block_gz.break_result_info, rhs, operand); |
| 2223 | | |
| 2224 | | switch (block_gz.break_result_info.rl) { |
| 2209 | if (!gen_zir.is_comptime) { |
| 2210 | try restoreErrRetIndex(parent_gz, .{ .block = gen_zir.break_target }, gen_zir.break_result_info, rhs, operand); |
| 2211 | } |
| 2212 | switch (gen_zir.break_result_info.rl) { |
| 2225 | 2213 | .ptr => { |
| 2226 | 2214 | // In this case we don't have any mechanism to intercept it; |
| 2227 | 2215 | // we assume the result location is written, and we break with void. |
| 2228 | | _ = try parent_gz.addBreak(break_tag, block_inst, .void_value); |
| 2216 | _ = try parent_gz.addBreak(break_tag, gen_zir.break_target, .void_value); |
| 2229 | 2217 | }, |
| 2230 | 2218 | .discard => { |
| 2231 | | _ = try parent_gz.addBreak(break_tag, block_inst, .void_value); |
| 2219 | _ = try parent_gz.addBreak(break_tag, gen_zir.break_target, .void_value); |
| 2232 | 2220 | }, |
| 2233 | 2221 | else => { |
| 2234 | | _ = try parent_gz.addBreakWithSrcNode(break_tag, block_inst, operand, rhs); |
| 2222 | _ = try parent_gz.addBreakWithSrcNode(break_tag, gen_zir.break_target, operand, rhs); |
| 2235 | 2223 | }, |
| 2236 | 2224 | } |
| 2237 | | return Zir.Inst.Ref.unreachable_value; |
| 2238 | | }, |
| 2239 | | .local_val => scope = scope.cast(Scope.LocalVal).?.parent, |
| 2240 | | .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, |
| 2241 | | .namespace => break, |
| 2242 | | .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent, |
| 2243 | | .top => unreachable, |
| 2244 | | } |
| 2245 | | } |
| 2246 | | if (opt_break_label.unwrap()) |break_label| { |
| 2247 | | const label_name = try astgen.identifierTokenString(break_label); |
| 2248 | | return astgen.failTok(break_label, "label not found: '{s}'", .{label_name}); |
| 2249 | | } else { |
| 2250 | | return astgen.failNode(node, "break expression outside loop", .{}); |
| 2225 | return .unreachable_value; |
| 2226 | } else { |
| 2227 | _ = try rvalue(parent_gz, gen_zir.break_result_info, .void_value, node); |
| 2228 | |
| 2229 | try genDefers(parent_gz, scope, parent_scope, .normal_only); |
| 2230 | |
| 2231 | // As our last action before the break, "pop" the error trace if needed |
| 2232 | if (!gen_zir.is_comptime) |
| 2233 | _ = try parent_gz.addRestoreErrRetIndex(.{ .block = gen_zir.break_target }, .always, node); |
| 2234 | |
| 2235 | _ = try parent_gz.addBreak(break_tag, gen_zir.break_target, .void_value); |
| 2236 | return .unreachable_value; |
| 2237 | } |
| 2238 | }, |
| 2239 | .local_val => { |
| 2240 | scope = scope.cast(Scope.LocalVal).?.parent; |
| 2241 | continue :find_scope scope.tag; |
| 2242 | }, |
| 2243 | .local_ptr => { |
| 2244 | scope = scope.cast(Scope.LocalPtr).?.parent; |
| 2245 | continue :find_scope scope.tag; |
| 2246 | }, |
| 2247 | .defer_normal, .defer_error => { |
| 2248 | scope = scope.cast(Scope.Defer).?.parent; |
| 2249 | continue :find_scope scope.tag; |
| 2250 | }, |
| 2251 | .namespace => { |
| 2252 | if (opt_break_label.unwrap()) |break_label| { |
| 2253 | const label_name = try astgen.identifierTokenString(break_label); |
| 2254 | return astgen.failTok(break_label, "label not found: '{s}'", .{label_name}); |
| 2255 | } else { |
| 2256 | return astgen.failNode(node, "break expression outside loop", .{}); |
| 2257 | } |
| 2258 | }, |
| 2259 | .top => unreachable, |
| 2251 | 2260 | } |
| 2252 | 2261 | } |
| 2253 | 2262 | |
| ... | ... | @@ -2262,100 +2271,116 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) |
| 2262 | 2271 | |
| 2263 | 2272 | // Look for the label in the scope. |
| 2264 | 2273 | var scope = parent_scope; |
| 2265 | | while (true) { |
| 2266 | | switch (scope.tag) { |
| 2267 | | .gen_zir => { |
| 2268 | | const gen_zir = scope.cast(GenZir).?; |
| 2274 | find_scope: switch (scope.tag) { |
| 2275 | .gen_zir => { |
| 2276 | const gen_zir = scope.cast(GenZir).?; |
| 2269 | 2277 | |
| 2270 | | if (gen_zir.cur_defer_node.unwrap()) |cur_defer_node| { |
| 2271 | | return astgen.failNodeNotes(node, "cannot continue out of defer expression", .{}, &.{ |
| 2272 | | try astgen.errNoteNode( |
| 2273 | | cur_defer_node, |
| 2274 | | "defer expression here", |
| 2275 | | .{}, |
| 2276 | | ), |
| 2277 | | }); |
| 2278 | | } |
| 2279 | | const continue_block = gen_zir.continue_block.unwrap() orelse { |
| 2280 | | scope = gen_zir.parent; |
| 2281 | | continue; |
| 2282 | | }; |
| 2283 | | if (opt_break_label.unwrap()) |break_label| blk: { |
| 2284 | | if (gen_zir.label) |*label| { |
| 2285 | | if (try astgen.tokenIdentEql(label.token, break_label)) { |
| 2286 | | const maybe_switch_tag = astgen.instructions.items(.tag)[@intFromEnum(label.block_inst)]; |
| 2287 | | if (opt_rhs != .none) switch (maybe_switch_tag) { |
| 2288 | | .switch_block, .switch_block_ref => {}, |
| 2289 | | else => return astgen.failNode(node, "cannot continue loop with operand", .{}), |
| 2290 | | } else switch (maybe_switch_tag) { |
| 2291 | | .switch_block, .switch_block_ref => return astgen.failNode(node, "cannot continue switch without operand", .{}), |
| 2292 | | else => {}, |
| 2293 | | } |
| 2294 | | |
| 2295 | | label.used = true; |
| 2296 | | label.used_for_continue = true; |
| 2297 | | break :blk; |
| 2278 | if (gen_zir.cur_defer_node.unwrap()) |cur_defer_node| { |
| 2279 | return astgen.failNodeNotes(node, "cannot continue out of defer expression", .{}, &.{ |
| 2280 | try astgen.errNoteNode( |
| 2281 | cur_defer_node, |
| 2282 | "defer expression here", |
| 2283 | .{}, |
| 2284 | ), |
| 2285 | }); |
| 2286 | } |
| 2287 | |
| 2288 | if (opt_break_label.unwrap()) |break_label| labeled: { |
| 2289 | if (gen_zir.label) |*label| { |
| 2290 | if (try astgen.tokenIdentEql(label.token, break_label)) { |
| 2291 | switch (gen_zir.continue_target) { |
| 2292 | .none => { |
| 2293 | return astgen.failNode(node, "continue cannot target labeled block", .{}); |
| 2294 | }, |
| 2295 | .@"break" => if (opt_rhs != .none) { |
| 2296 | return astgen.failNode(node, "cannot continue loop with operand", .{}); |
| 2297 | }, |
| 2298 | .switch_continue => if (opt_rhs == .none) { |
| 2299 | return astgen.failNode(node, "cannot continue switch without operand", .{}); |
| 2300 | }, |
| 2298 | 2301 | } |
| 2299 | | } |
| 2300 | | // found continue but either it has a different label, or no label |
| 2301 | | scope = gen_zir.parent; |
| 2302 | | continue; |
| 2303 | | } else if (gen_zir.label) |label| { |
| 2304 | | // This `continue` is unlabeled. If the gz we've found corresponds to a labeled |
| 2305 | | // `switch`, ignore it and continue to parent scopes. |
| 2306 | | switch (astgen.instructions.items(.tag)[@intFromEnum(label.block_inst)]) { |
| 2307 | | .switch_block, .switch_block_ref => { |
| 2308 | | scope = gen_zir.parent; |
| 2309 | | continue; |
| 2310 | | }, |
| 2311 | | else => {}, |
| 2302 | label.used = true; |
| 2303 | label.used_for_continue = true; |
| 2304 | break :labeled; |
| 2312 | 2305 | } |
| 2313 | 2306 | } |
| 2307 | // gz without or with different label, continue to parent scopes. |
| 2308 | scope = gen_zir.parent; |
| 2309 | continue :find_scope scope.tag; |
| 2310 | } else if (gen_zir.allow_unlabeled_control_flow) { |
| 2311 | // This `continue` is unlabeled. If the gz we've found doesn't |
| 2312 | // provide a `continue` target or corresponds to a labeled |
| 2313 | // `switch`, ignore it and continue to parent scopes. |
| 2314 | switch (gen_zir.continue_target) { |
| 2315 | .none, .switch_continue => { |
| 2316 | scope = gen_zir.parent; |
| 2317 | continue :find_scope scope.tag; |
| 2318 | }, |
| 2319 | .@"break" => {}, |
| 2320 | } |
| 2321 | } else { |
| 2322 | // We don't have a break label and the gz we found doesn't allow |
| 2323 | // unlabeled control flow, so we continue to its parent scopes. |
| 2324 | scope = gen_zir.parent; |
| 2325 | continue :find_scope scope.tag; |
| 2326 | } |
| 2314 | 2327 | |
| 2315 | | if (opt_rhs.unwrap()) |rhs| { |
| 2316 | | // We need to figure out the result info to use. |
| 2317 | | // The type should match |
| 2318 | | const operand = try reachableExpr(parent_gz, parent_scope, gen_zir.continue_result_info, rhs, node); |
| 2319 | | |
| 2328 | switch (gen_zir.continue_target) { |
| 2329 | .none => unreachable, // should have failed or continued to parent scopes by now |
| 2330 | .@"break" => |block| { |
| 2320 | 2331 | try genDefers(parent_gz, scope, parent_scope, .normal_only); |
| 2321 | 2332 | |
| 2322 | | // As our last action before the continue, "pop" the error trace if needed |
| 2323 | | if (!gen_zir.is_comptime) |
| 2324 | | _ = try parent_gz.addRestoreErrRetIndex(.{ .block = continue_block }, .always, node); |
| 2325 | | |
| 2326 | | _ = try parent_gz.addBreakWithSrcNode(.switch_continue, continue_block, operand, rhs); |
| 2327 | | return Zir.Inst.Ref.unreachable_value; |
| 2328 | | } |
| 2329 | | |
| 2330 | | try genDefers(parent_gz, scope, parent_scope, .normal_only); |
| 2333 | const break_tag: Zir.Inst.Tag = if (gen_zir.is_inline) |
| 2334 | .break_inline |
| 2335 | else |
| 2336 | .@"break"; |
| 2337 | if (break_tag == .break_inline) { |
| 2338 | _ = try parent_gz.addUnNode(.check_comptime_control_flow, block.toRef(), node); |
| 2339 | } |
| 2331 | 2340 | |
| 2332 | | const break_tag: Zir.Inst.Tag = if (gen_zir.is_inline) |
| 2333 | | .break_inline |
| 2334 | | else |
| 2335 | | .@"break"; |
| 2336 | | if (break_tag == .break_inline) { |
| 2337 | | _ = try parent_gz.addUnNode(.check_comptime_control_flow, continue_block.toRef(), node); |
| 2338 | | } |
| 2341 | // As our last action before the continue, "pop" the error trace if needed |
| 2342 | if (!gen_zir.is_comptime) { |
| 2343 | _ = try parent_gz.addRestoreErrRetIndex(.{ .block = block }, .always, node); |
| 2344 | } |
| 2345 | _ = try parent_gz.addBreak(break_tag, block, .void_value); |
| 2346 | return .unreachable_value; |
| 2347 | }, |
| 2348 | .switch_continue => |switch_block| { |
| 2349 | const rhs = opt_rhs.unwrap().?; // checked above |
| 2350 | const operand = try reachableExpr(parent_gz, parent_scope, gen_zir.continue_result_info, rhs, node); |
| 2339 | 2351 | |
| 2340 | | // As our last action before the continue, "pop" the error trace if needed |
| 2341 | | if (!gen_zir.is_comptime) |
| 2342 | | _ = try parent_gz.addRestoreErrRetIndex(.{ .block = continue_block }, .always, node); |
| 2352 | try genDefers(parent_gz, scope, parent_scope, .normal_only); |
| 2343 | 2353 | |
| 2344 | | _ = try parent_gz.addBreak(break_tag, continue_block, .void_value); |
| 2345 | | return Zir.Inst.Ref.unreachable_value; |
| 2346 | | }, |
| 2347 | | .local_val => scope = scope.cast(Scope.LocalVal).?.parent, |
| 2348 | | .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, |
| 2349 | | .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent, |
| 2350 | | .namespace => break, |
| 2351 | | .top => unreachable, |
| 2352 | | } |
| 2353 | | } |
| 2354 | | if (opt_break_label.unwrap()) |break_label| { |
| 2355 | | const label_name = try astgen.identifierTokenString(break_label); |
| 2356 | | return astgen.failTok(break_label, "label not found: '{s}'", .{label_name}); |
| 2357 | | } else { |
| 2358 | | return astgen.failNode(node, "continue expression outside loop", .{}); |
| 2354 | // As our last action before the continue, "pop" the error trace if needed |
| 2355 | if (!gen_zir.is_comptime) { |
| 2356 | _ = try parent_gz.addRestoreErrRetIndex(.{ .block = switch_block }, .always, node); |
| 2357 | } |
| 2358 | _ = try parent_gz.addBreakWithSrcNode(.switch_continue, switch_block, operand, rhs); |
| 2359 | return .unreachable_value; |
| 2360 | }, |
| 2361 | } |
| 2362 | }, |
| 2363 | .local_val => { |
| 2364 | scope = scope.cast(Scope.LocalVal).?.parent; |
| 2365 | continue :find_scope scope.tag; |
| 2366 | }, |
| 2367 | .local_ptr => { |
| 2368 | scope = scope.cast(Scope.LocalPtr).?.parent; |
| 2369 | continue :find_scope scope.tag; |
| 2370 | }, |
| 2371 | .defer_normal, .defer_error => { |
| 2372 | scope = scope.cast(Scope.Defer).?.parent; |
| 2373 | continue :find_scope scope.tag; |
| 2374 | }, |
| 2375 | .namespace => { |
| 2376 | if (opt_break_label.unwrap()) |break_label| { |
| 2377 | const label_name = try astgen.identifierTokenString(break_label); |
| 2378 | return astgen.failTok(break_label, "label not found: '{s}'", .{label_name}); |
| 2379 | } else { |
| 2380 | return astgen.failNode(node, "continue expression outside loop", .{}); |
| 2381 | } |
| 2382 | }, |
| 2383 | .top => unreachable, |
| 2359 | 2384 | } |
| 2360 | 2385 | } |
| 2361 | 2386 | |
| ... | ... | @@ -2509,10 +2534,9 @@ fn labeledBlockExpr( |
| 2509 | 2534 | try gz.instructions.append(astgen.gpa, block_inst); |
| 2510 | 2535 | var block_scope = gz.makeSubBlock(parent_scope); |
| 2511 | 2536 | block_scope.is_inline = force_comptime; |
| 2512 | | block_scope.label = GenZir.Label{ |
| 2513 | | .token = label_token, |
| 2514 | | .block_inst = block_inst, |
| 2515 | | }; |
| 2537 | block_scope.label = .{ .token = label_token }; |
| 2538 | block_scope.break_target = block_inst; |
| 2539 | block_scope.continue_target = .none; |
| 2516 | 2540 | block_scope.setBreakResultInfo(block_ri); |
| 2517 | 2541 | if (force_comptime) block_scope.is_comptime = true; |
| 2518 | 2542 | defer block_scope.unstack(); |
| ... | ... | @@ -6574,7 +6598,6 @@ fn whileExpr( |
| 6574 | 6598 | |
| 6575 | 6599 | var loop_scope = parent_gz.makeSubBlock(scope); |
| 6576 | 6600 | loop_scope.is_inline = is_inline; |
| 6577 | | loop_scope.setBreakResultInfo(block_ri); |
| 6578 | 6601 | defer loop_scope.unstack(); |
| 6579 | 6602 | |
| 6580 | 6603 | var cond_scope = parent_gz.makeSubBlock(&loop_scope.base); |
| ... | ... | @@ -6707,14 +6730,13 @@ fn whileExpr( |
| 6707 | 6730 | _ = try loop_scope.addNode(repeat_tag, node); |
| 6708 | 6731 | |
| 6709 | 6732 | try loop_scope.setBlockBody(loop_block); |
| 6710 | | loop_scope.break_block = loop_block.toOptional(); |
| 6711 | | loop_scope.continue_block = continue_block.toOptional(); |
| 6712 | 6733 | if (while_full.label_token) |label_token| { |
| 6713 | | loop_scope.label = .{ |
| 6714 | | .token = label_token, |
| 6715 | | .block_inst = loop_block, |
| 6716 | | }; |
| 6734 | loop_scope.label = .{ .token = label_token }; |
| 6717 | 6735 | } |
| 6736 | loop_scope.allow_unlabeled_control_flow = true; |
| 6737 | loop_scope.break_target = loop_block; |
| 6738 | loop_scope.continue_target = .{ .@"break" = continue_block }; |
| 6739 | loop_scope.setBreakResultInfo(block_ri); |
| 6718 | 6740 | |
| 6719 | 6741 | // done adding instructions to loop_scope, can now stack then_scope |
| 6720 | 6742 | then_scope.instructions_top = then_scope.instructions.items.len; |
| ... | ... | @@ -6787,10 +6809,12 @@ fn whileExpr( |
| 6787 | 6809 | break :s &else_scope.base; |
| 6788 | 6810 | } |
| 6789 | 6811 | }; |
| 6790 | | // Remove the continue block and break block so that `continue` and `break` |
| 6791 | | // control flow apply to outer loops; not this one. |
| 6792 | | loop_scope.continue_block = .none; |
| 6793 | | loop_scope.break_block = .none; |
| 6812 | // Remove label and forbid unlabeled control flow to this scope so that |
| 6813 | // `continue` and `break` control flow apply to outer loops; not this one. |
| 6814 | loop_scope.label = null; |
| 6815 | loop_scope.allow_unlabeled_control_flow = false; |
| 6816 | loop_scope.continue_target = undefined; |
| 6817 | loop_scope.break_target = undefined; |
| 6794 | 6818 | const else_result = try fullBodyExpr(&else_scope, sub_scope, loop_scope.break_result_info, else_node, .allow_branch_hint); |
| 6795 | 6819 | if (is_statement) { |
| 6796 | 6820 | _ = try addEnsureResult(&else_scope, else_result, else_node); |
| ... | ... | @@ -6979,14 +7003,12 @@ fn forExpr( |
| 6979 | 7003 | const cond_block = try loop_scope.makeBlockInst(block_tag, node); |
| 6980 | 7004 | try cond_scope.setBlockBody(cond_block); |
| 6981 | 7005 | |
| 6982 | | loop_scope.break_block = loop_block.toOptional(); |
| 6983 | | loop_scope.continue_block = cond_block.toOptional(); |
| 6984 | 7006 | if (for_full.label_token) |label_token| { |
| 6985 | | loop_scope.label = .{ |
| 6986 | | .token = label_token, |
| 6987 | | .block_inst = loop_block, |
| 6988 | | }; |
| 7007 | loop_scope.label = .{ .token = label_token }; |
| 6989 | 7008 | } |
| 7009 | loop_scope.allow_unlabeled_control_flow = true; |
| 7010 | loop_scope.break_target = loop_block; |
| 7011 | loop_scope.continue_target = .{ .@"break" = cond_block }; |
| 6990 | 7012 | |
| 6991 | 7013 | const then_node = for_full.ast.then_expr; |
| 6992 | 7014 | var then_scope = parent_gz.makeSubBlock(&cond_scope.base); |
| ... | ... | @@ -7077,10 +7099,12 @@ fn forExpr( |
| 7077 | 7099 | |
| 7078 | 7100 | if (for_full.ast.else_expr.unwrap()) |else_node| { |
| 7079 | 7101 | const sub_scope = &else_scope.base; |
| 7080 | | // Remove the continue block and break block so that `continue` and `break` |
| 7081 | | // control flow apply to outer loops; not this one. |
| 7082 | | loop_scope.continue_block = .none; |
| 7083 | | loop_scope.break_block = .none; |
| 7102 | // Remove label and forbid unlabeled control flow to this scope so that |
| 7103 | // `continue` and `break` control flow apply to outer loops; not this one. |
| 7104 | loop_scope.label = null; |
| 7105 | loop_scope.allow_unlabeled_control_flow = false; |
| 7106 | loop_scope.continue_target = undefined; |
| 7107 | loop_scope.break_target = undefined; |
| 7084 | 7108 | const else_result = try fullBodyExpr(&else_scope, sub_scope, loop_scope.break_result_info, else_node, .allow_branch_hint); |
| 7085 | 7109 | if (is_statement) { |
| 7086 | 7110 | _ = try addEnsureResult(&else_scope, else_result, else_node); |
| ... | ... | @@ -7818,7 +7842,9 @@ fn switchExpr( |
| 7818 | 7842 | const switch_block = try parent_gz.makeBlockInst(switch_tag, node); |
| 7819 | 7843 | |
| 7820 | 7844 | if (switch_full.label_token) |label_token| { |
| 7821 | | block_scope.continue_block = switch_block.toOptional(); |
| 7845 | block_scope.label = .{ .token = label_token }; |
| 7846 | block_scope.break_target = switch_block; |
| 7847 | block_scope.continue_target = .{ .switch_continue = switch_block }; |
| 7822 | 7848 | block_scope.continue_result_info = .{ |
| 7823 | 7849 | .rl = if (any_payload_is_ref) |
| 7824 | 7850 | .{ .ref_coerced_ty = raw_operand_ty_ref } |
| ... | ... | @@ -7826,12 +7852,7 @@ fn switchExpr( |
| 7826 | 7852 | .{ .coerced_ty = raw_operand_ty_ref }, |
| 7827 | 7853 | }; |
| 7828 | 7854 | |
| 7829 | | block_scope.label = .{ |
| 7830 | | .token = label_token, |
| 7831 | | .block_inst = switch_block, |
| 7832 | | }; |
| 7833 | | // `break` can target this via `label.block_inst` |
| 7834 | | // `break_result_info` already set by `setBreakResultInfo` |
| 7855 | // `break_result_info` already set by `setBreakResultInfo` above. |
| 7835 | 7856 | } |
| 7836 | 7857 | |
| 7837 | 7858 | // We re-use this same scope for all cases, including the special prong, if any. |
| ... | ... | @@ -11916,8 +11937,8 @@ const GenZir = struct { |
| 11916 | 11937 | /// whenever we know Sema will analyze the current block with `is_comptime`, |
| 11917 | 11938 | /// for instance when we're within a `struct_decl` or a `block_comptime`. |
| 11918 | 11939 | is_comptime: bool, |
| 11919 | | /// Whether we're in an expression within a `@TypeOf` operand. In this case, closure of runtime |
| 11920 | | /// variables is permitted where it is usually not. |
| 11940 | /// Whether we're in an expression within a `@TypeOf` operand. In this case, |
| 11941 | /// closure of runtime variables is permitted where it is usually not. |
| 11921 | 11942 | is_typeof: bool = false, |
| 11922 | 11943 | /// This is set to true for a `GenZir` of a `block_inline`, indicating that |
| 11923 | 11944 | /// exits from this block should use `break_inline` rather than `break`. |
| ... | ... | @@ -11938,10 +11959,27 @@ const GenZir = struct { |
| 11938 | 11959 | /// if use is strictly nested. This saves prior size of list for unstacking. |
| 11939 | 11960 | instructions_top: usize, |
| 11940 | 11961 | label: ?Label = null, |
| 11941 | | break_block: Zir.Inst.OptionalIndex = .none, |
| 11942 | | continue_block: Zir.Inst.OptionalIndex = .none, |
| 11962 | /// If `true`, unlabeled `break` and `continue` exprs can target this `GenZir`. |
| 11963 | allow_unlabeled_control_flow: bool = false, |
| 11964 | /// If `label` is `null` and `unlabeled_control_flow_target` is `false`, |
| 11965 | /// this is unused and may be `undefined`. |
| 11966 | /// Otherwise, this is the target for a `break` instruction when a `break` |
| 11967 | /// targets this `GenZir`. |
| 11968 | break_target: Zir.Inst.Index = undefined, |
| 11969 | /// If `label` is `null` and `unlabeled_control_flow_target` is `false`, |
| 11970 | /// this is unused and may be `undefined`. |
| 11971 | continue_target: union(enum) { |
| 11972 | /// A `continue` cannot target this `GenZir`; emit an error. |
| 11973 | none, |
| 11974 | /// Emit a `break` instruction targeting this block. |
| 11975 | @"break": Zir.Inst.Index, |
| 11976 | /// Emit a `switch_continue` instruction targeting this `switch_block`. |
| 11977 | switch_continue: Zir.Inst.Index, |
| 11978 | } = undefined, |
| 11943 | 11979 | /// Only valid when setBreakResultInfo is called. |
| 11944 | 11980 | break_result_info: AstGen.ResultInfo = undefined, |
| 11981 | /// If `continue_target` is *not* `switch_continue`, this is unused and may |
| 11982 | /// be `undefined`. |
| 11945 | 11983 | continue_result_info: AstGen.ResultInfo = undefined, |
| 11946 | 11984 | |
| 11947 | 11985 | suspend_node: Ast.Node.OptionalIndex = .none, |
| ... | ... | @@ -12008,7 +12046,6 @@ const GenZir = struct { |
| 12008 | 12046 | |
| 12009 | 12047 | const Label = struct { |
| 12010 | 12048 | token: Ast.TokenIndex, |
| 12011 | | block_inst: Zir.Inst.Index, |
| 12012 | 12049 | used: bool = false, |
| 12013 | 12050 | used_for_continue: bool = false, |
| 12014 | 12051 | }; |