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